mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various fixes
This commit is contained in:
parent
29e31bdf8b
commit
022d626000
@ -1061,35 +1061,30 @@ public class AttachmentController: ViewController {
|
||||
return self.buttons.contains(.standalone)
|
||||
}
|
||||
|
||||
private var snapshotView: UIView?
|
||||
public override var isMinimized: Bool {
|
||||
didSet {
|
||||
guard self.isMinimized != oldValue else {
|
||||
return
|
||||
}
|
||||
if self.isMinimized {
|
||||
if self.snapshotView == nil, let lastController = self.node.container.container.controllers.last, let snapshotView = lastController.view.snapshotView(afterScreenUpdates: false) {
|
||||
snapshotView.isUserInteractionEnabled = false
|
||||
self.snapshotView = snapshotView
|
||||
lastController.view.addSubview(snapshotView)
|
||||
}
|
||||
} else {
|
||||
if let snapshotView = self.snapshotView {
|
||||
self.snapshotView = nil
|
||||
Queue.mainQueue().after(0.5) {
|
||||
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
||||
snapshotView.removeFromSuperview()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !self.node.isDismissing {
|
||||
let transition: ContainedViewLayoutTransition = self.isMinimized ? .immediate : .animated(duration: 0.2, curve: .easeInOut)
|
||||
transition.updateAlpha(node: self.node.dim, alpha: self.isMinimized ? 0.0 : 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
// private var snapshotView: UIView?
|
||||
// public override var isMinimized: Bool {
|
||||
// didSet {
|
||||
// guard self.isMinimized != oldValue else {
|
||||
// return
|
||||
// }
|
||||
// if self.isMinimized {
|
||||
// if self.snapshotView == nil, let lastController = self.node.container.container.controllers.last, let snapshotView = lastController.view.snapshotView(afterScreenUpdates: false) {
|
||||
// snapshotView.isUserInteractionEnabled = false
|
||||
// self.snapshotView = snapshotView
|
||||
// lastController.view.addSubview(snapshotView)
|
||||
// }
|
||||
// } else {
|
||||
// if let snapshotView = self.snapshotView {
|
||||
// self.snapshotView = nil
|
||||
// Queue.mainQueue().after(0.15) {
|
||||
// snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
||||
// snapshotView.removeFromSuperview()
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
public func updateSelectionCount(_ count: Int) {
|
||||
self.node.updateSelectionCount(count, animated: false)
|
||||
|
@ -1567,6 +1567,7 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
|
||||
self.updateContainersNonReentrant(transition: transition)
|
||||
}
|
||||
viewController.isMinimized = true
|
||||
self.filterController(viewController, animated: true)
|
||||
minimizedContainer?.addController(viewController, transition: transition)
|
||||
}
|
||||
@ -1587,6 +1588,9 @@ open class NavigationController: UINavigationController, ContainableController,
|
||||
var viewControllers = self.viewControllers
|
||||
viewControllers.append(viewController)
|
||||
self.setViewControllers(viewControllers, animated: false)
|
||||
|
||||
viewController.isMinimized = false
|
||||
|
||||
self.isMaximizing = false
|
||||
|
||||
if dismissed, let minimizedContainer = self.minimizedContainer {
|
||||
|
@ -723,15 +723,7 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
|
||||
transition.updateTransformScale(layer: backgroundNode.layer, scale: 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
for (_, priceNode) in strongSelf.priceNodes {
|
||||
priceNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: 0.1)
|
||||
if strongSelf.isExternalPreview {
|
||||
ComponentTransition.immediate.setScale(layer: priceNode.layer, scale: 0.001)
|
||||
transition.updateTransformScale(layer: priceNode.layer, scale: 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (identifier, itemNode) in strongSelf.itemNodes {
|
||||
if !strongSelf.isObscuredExternalPreview, let (transitionView, _, _) = strongSelf.getTransitionView(identifier) {
|
||||
itemNode.animateFrom(transitionView, transition: transition)
|
||||
@ -746,6 +738,15 @@ final class MediaPickerSelectedListNode: ASDisplayNode, ASScrollViewDelegate, AS
|
||||
}
|
||||
}
|
||||
|
||||
for (_, priceNode) in strongSelf.priceNodes {
|
||||
strongSelf.scrollNode.addSubnode(priceNode)
|
||||
priceNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: 0.1)
|
||||
if strongSelf.isExternalPreview {
|
||||
ComponentTransition.immediate.setScale(layer: priceNode.layer, scale: 0.001)
|
||||
transition.updateTransformScale(layer: priceNode.layer, scale: 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
if let topNode = strongSelf.messageNodes?.first, !topNode.alpha.isZero {
|
||||
topNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: 0.1)
|
||||
transition.animatePositionAdditive(layer: topNode.layer, offset: CGPoint(x: 0.0, y: -30.0))
|
||||
|
@ -234,7 +234,7 @@ public class ChatMessageStarsMediaInfoNode: ASDisplayNode {
|
||||
let textFont = Font.regular(fontSize)
|
||||
|
||||
let text: NSMutableAttributedString
|
||||
if let peer = arguments.message.peers[arguments.message.id.peerId] as? TelegramChannel, peer.flags.contains(.isCreator) || peer.adminRights != nil {
|
||||
if let peer = arguments.message.peers[arguments.message.id.peerId] as? TelegramChannel, peer.flags.contains(.isCreator) || peer.adminRights != nil, arguments.message.forwardInfo == nil {
|
||||
let amountString = presentationStringsFormattedNumber(Int32(arguments.media.amount), arguments.presentationData.dateTimeFormat.groupingSeparator)
|
||||
text = NSMutableAttributedString(string: "⭐️\(amountString)", font: textFont, textColor: .white)
|
||||
} else {
|
||||
|
@ -48,6 +48,8 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
private let dimCoverNode: ASDisplayNode
|
||||
private let shadowNode: ASImageNode
|
||||
|
||||
private var controllerView: UIView?
|
||||
|
||||
var tapped: (() -> Void)?
|
||||
var highlighted: ((Bool) -> Void)?
|
||||
var closeTapped: (() -> Void)?
|
||||
@ -58,9 +60,8 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
transition.updateAlpha(node: self.dimCoverNode, alpha: self.isCovered ? 0.25 : 0.0)
|
||||
}
|
||||
}
|
||||
var isExpanded = false
|
||||
|
||||
private var validLayout: (CGSize, UIEdgeInsets)?
|
||||
private var validLayout: (CGSize, UIEdgeInsets, Bool)?
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, item: Item) {
|
||||
self.theme = theme
|
||||
@ -94,9 +95,15 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
applySmoothRoundedCorners(self.containerNode.layer)
|
||||
|
||||
self.shadowNode.image = shadowImage
|
||||
|
||||
|
||||
self.addSubnode(self.containerNode)
|
||||
self.containerNode.addSubnode(self.item.controller.displayNode)
|
||||
if let snapshotView = self.item.controller.displayNode.view.snapshotView(afterScreenUpdates: false) {
|
||||
self.controllerView = snapshotView
|
||||
self.containerNode.view.addSubview(snapshotView)
|
||||
} else {
|
||||
self.controllerView = self.item.controller.displayNode.view
|
||||
self.containerNode.view.addSubview(self.item.controller.displayNode.view)
|
||||
}
|
||||
self.addSubnode(self.headerNode)
|
||||
self.addSubnode(self.dimCoverNode)
|
||||
self.addSubnode(self.shadowNode)
|
||||
@ -152,7 +159,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
}
|
||||
|
||||
@objc func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||
guard let (_, insets) = self.validLayout else {
|
||||
guard let (_, insets, _) = self.validLayout else {
|
||||
return
|
||||
}
|
||||
switch recognizer.state {
|
||||
@ -174,8 +181,8 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
}
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, insets)
|
||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, isExpanded: Bool, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, insets, isExpanded)
|
||||
|
||||
var topInset = insets.top
|
||||
if size.width < size.height {
|
||||
@ -187,17 +194,22 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
self.shadowNode.frame = CGRect(origin: .zero, size: CGSize(width: size.width, height: size.height - topInset))
|
||||
|
||||
var navigationHeight: CGFloat = minimizedNavigationHeight
|
||||
if !self.isExpanded {
|
||||
if !isExpanded {
|
||||
navigationHeight += insets.bottom
|
||||
}
|
||||
|
||||
let headerFrame = CGRect(origin: .zero, size: CGSize(width: size.width, height: navigationHeight))
|
||||
self.headerNode.update(size: size, insets: insets, transition: transition)
|
||||
self.headerNode.update(size: size, insets: insets, isExpanded: isExpanded, transition: transition)
|
||||
transition.updateFrame(node: self.headerNode, frame: headerFrame)
|
||||
transition.updateFrame(node: self.dimCoverNode, frame: CGRect(origin: .zero, size: size))
|
||||
|
||||
if let controllerView = self.controllerView {
|
||||
let controllerFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - controllerView.bounds.size.width) / 2.0), y: 0.0), size: controllerView.bounds.size)
|
||||
transition.updateFrame(view: controllerView, frame: controllerFrame)
|
||||
}
|
||||
|
||||
if !self.isDismissed {
|
||||
transition.updateAlpha(node: self.shadowNode, alpha: self.isExpanded ? 1.0 : 0.0)
|
||||
transition.updateAlpha(node: self.shadowNode, alpha: isExpanded ? 1.0 : 0.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -225,6 +237,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
private var dismissingItemOffset: CGFloat?
|
||||
|
||||
private var currentTransition: Transition?
|
||||
private var isApplyingTransition = false
|
||||
private var validLayout: ContainerViewLayout?
|
||||
|
||||
public init(context: AccountContext, navigationController: NavigationController) {
|
||||
@ -299,7 +312,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
let insets = layout.insets(options: [.statusBar])
|
||||
let itemCount = self.items.count
|
||||
let spacing = interitemSpacing(itemCount: itemCount, boundingSize: self.scrollView.bounds.size, insets: insets)
|
||||
return max(0, min(Int(floor((y - additionalInsetTop) / spacing)), itemCount - 1))
|
||||
return max(0, min(Int(floor((y - additionalInsetTop - insets.top) / spacing)), itemCount - 1))
|
||||
}
|
||||
|
||||
public override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
@ -463,7 +476,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
}
|
||||
|
||||
self.validLayout = layout
|
||||
|
||||
|
||||
let bounds = CGRect(origin: .zero, size: layout.size)
|
||||
|
||||
containerTransition.updateFrame(view: self.blurView, frame: bounds)
|
||||
@ -494,6 +507,10 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
transition.animatePosition(layer: self.bottomEdgeView.layer, from: self.bottomEdgeView.layer.position.offsetBy(dx: 0.0, dy: minimizedNavigationHeight + minimizedTopMargin), to: self.bottomEdgeView.layer.position)
|
||||
}
|
||||
|
||||
if self.isApplyingTransition {
|
||||
return
|
||||
}
|
||||
|
||||
let insets = layout.insets(options: [.statusBar])
|
||||
let itemInsets = UIEdgeInsets(top: insets.top, left: layout.safeInsets.left, bottom: insets.bottom, right: layout.safeInsets.right)
|
||||
var topInset = insets.top
|
||||
@ -569,7 +586,6 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
} else {
|
||||
itemNode.layer.zPosition = 0.0
|
||||
}
|
||||
itemNode.isExpanded = self.isExpanded
|
||||
|
||||
if self.isExpanded {
|
||||
let currentItemFrame = frameForIndex(index: index, size: layout.size, insets: itemInsets, itemCount: self.items.count, boundingSize: layout.size)
|
||||
@ -652,7 +668,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
}
|
||||
|
||||
itemNode.bounds = CGRect(origin: .zero, size: itemFrame.size)
|
||||
itemNode.updateLayout(size: layout.size, insets: itemInsets, transition: itemTransition)
|
||||
itemNode.updateLayout(size: itemFrame.size, insets: itemInsets, isExpanded: self.isExpanded, transition: itemTransition)
|
||||
|
||||
if index == self.items.count - 1 && !self.isExpanded {
|
||||
itemNode.setTitleControllers(self.items.map { $0.controller })
|
||||
@ -677,6 +693,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
self.scrollView.isScrollEnabled = self.isExpanded
|
||||
|
||||
if let currentTransition = self.currentTransition {
|
||||
self.isApplyingTransition = true
|
||||
switch self.currentTransition {
|
||||
case let .minimize(itemId):
|
||||
guard let itemNode = self.itemNodes[itemId] else {
|
||||
@ -692,7 +709,6 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
dimView.removeFromSuperview()
|
||||
})
|
||||
|
||||
|
||||
itemNode.animateIn()
|
||||
|
||||
var initialOffset = insets.top + itemNode.item.controller.minimizedTopEdgeOffset
|
||||
@ -704,6 +720,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
}
|
||||
|
||||
transition.animatePosition(node: itemNode, from: CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0 + initialOffset), completion: { _ in
|
||||
self.isApplyingTransition = false
|
||||
if self.currentTransition == currentTransition {
|
||||
self.currentTransition = nil
|
||||
}
|
||||
@ -724,6 +741,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
itemNode.animateOut()
|
||||
transition.updateTransform(node: itemNode, transform: CATransform3DIdentity)
|
||||
transition.updatePosition(node: itemNode, position: CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0 + topInset + self.scrollView.contentOffset.y), completion: { _ in
|
||||
self.isApplyingTransition = false
|
||||
if self.currentTransition == currentTransition {
|
||||
self.currentTransition = nil
|
||||
}
|
||||
@ -750,6 +768,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
itemNode.animateOut()
|
||||
transition.updateTransform(node: itemNode, transform: CATransform3DIdentity)
|
||||
transition.updatePosition(node: itemNode, position: CGPoint(x: layout.size.width / 2.0, y: layout.size.height / 2.0 + topInset + self.scrollView.contentOffset.y), completion: { _ in
|
||||
self.isApplyingTransition = false
|
||||
if self.currentTransition == currentTransition {
|
||||
self.currentTransition = nil
|
||||
}
|
||||
@ -766,6 +785,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
transition.updatePosition(node: dismissedItemNode, position: CGPoint(x: -layout.size.width, y: dismissedItemNode.position.y))
|
||||
} else {
|
||||
transition.updatePosition(node: dismissedItemNode, position: CGPoint(x: -layout.size.width, y: dismissedItemNode.position.y), completion: { _ in
|
||||
self.isApplyingTransition = false
|
||||
if self.currentTransition == currentTransition {
|
||||
self.currentTransition = nil
|
||||
}
|
||||
@ -778,6 +798,7 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
case .dismissAll:
|
||||
let dismissOffset = collapsedHeight(layout: layout)
|
||||
transition.updatePosition(layer: self.bottomEdgeView.layer, position: self.bottomEdgeView.layer.position.offsetBy(dx: 0.0, dy: dismissOffset), completion: { _ in
|
||||
self.isApplyingTransition = false
|
||||
if self.currentTransition == currentTransition {
|
||||
self.currentTransition = nil
|
||||
}
|
||||
@ -794,133 +815,3 @@ public class MinimizedContainerImpl: ASDisplayNode, MinimizedContainer, ASScroll
|
||||
return minimizedNavigationHeight + minimizedTopMargin + layout.intrinsicInsets.bottom
|
||||
}
|
||||
}
|
||||
|
||||
private let maxInteritemSpacing: CGFloat = 240.0
|
||||
private let additionalInsetTop: CGFloat = 16.0
|
||||
private let additionalInsetBottom: CGFloat = 0.0
|
||||
private let zOffset: CGFloat = -60.0
|
||||
|
||||
private let perspectiveCorrection: CGFloat = -1.0 / 1000.0
|
||||
private let maxRotationAngle: CGFloat = -CGFloat.pi / 2.2
|
||||
|
||||
private func angle(for origin: CGFloat, itemCount: Int, scrollBounds: CGRect, contentHeight: CGFloat?, insets: UIEdgeInsets) -> CGFloat {
|
||||
var rotationAngle = rotationAngleAt0(itemCount: itemCount)
|
||||
|
||||
var contentOffset = scrollBounds.origin.y
|
||||
if contentOffset < 0.0 {
|
||||
contentOffset *= 2.0
|
||||
}
|
||||
|
||||
var yOnScreen = origin - contentOffset - additionalInsetTop - insets.top
|
||||
if yOnScreen < 0 {
|
||||
yOnScreen = 0
|
||||
} else if yOnScreen > scrollBounds.height {
|
||||
yOnScreen = scrollBounds.height
|
||||
}
|
||||
|
||||
let maxRotationVariance = maxRotationAngle - rotationAngleAt0(itemCount: itemCount)
|
||||
rotationAngle += (maxRotationVariance / scrollBounds.height) * yOnScreen
|
||||
|
||||
return rotationAngle
|
||||
}
|
||||
|
||||
private func final3dTransform(for origin: CGFloat, size: CGSize, contentHeight: CGFloat?, itemCount: Int, forcedAngle: CGFloat? = nil, additionalAngle: CGFloat? = nil, scrollBounds: CGRect, insets: UIEdgeInsets) -> CATransform3D {
|
||||
var transform = CATransform3DIdentity
|
||||
transform.m34 = perspectiveCorrection
|
||||
|
||||
let rotationAngle = forcedAngle ?? angle(for: origin, itemCount: itemCount, scrollBounds: scrollBounds, contentHeight: contentHeight, insets: insets)
|
||||
var effectiveRotationAngle = rotationAngle
|
||||
if let additionalAngle = additionalAngle {
|
||||
effectiveRotationAngle += additionalAngle
|
||||
}
|
||||
|
||||
let r = size.height / 2.0 + abs(zOffset / sin(rotationAngle))
|
||||
|
||||
let zTranslation = r * sin(rotationAngle)
|
||||
let yTranslation: CGFloat = r * (1 - cos(rotationAngle))
|
||||
|
||||
let zTranslateTransform = CATransform3DTranslate(transform, 0.0, -yTranslation, zTranslation)
|
||||
|
||||
let rotateTransform = CATransform3DRotate(zTranslateTransform, effectiveRotationAngle, 1.0, 0.0, 0.0)
|
||||
|
||||
return rotateTransform
|
||||
}
|
||||
|
||||
private func interitemSpacing(itemCount: Int, boundingSize: CGSize, insets: UIEdgeInsets) -> CGFloat {
|
||||
var interitemSpacing = maxInteritemSpacing
|
||||
if itemCount > 0 {
|
||||
interitemSpacing = (boundingSize.height - additionalInsetTop - additionalInsetBottom - insets.top) / CGFloat(min(itemCount, 5))
|
||||
}
|
||||
return interitemSpacing
|
||||
}
|
||||
|
||||
private func frameForIndex(index: Int, size: CGSize, insets: UIEdgeInsets, itemCount: Int, boundingSize: CGSize) -> CGRect {
|
||||
let spacing = interitemSpacing(itemCount: itemCount, boundingSize: boundingSize, insets: insets)
|
||||
let y = additionalInsetTop + insets.top + spacing * CGFloat(index)
|
||||
let origin = CGPoint(x: insets.left, y: y)
|
||||
|
||||
return CGRect(origin: origin, size: CGSize(width: size.width - insets.left - insets.right, height: size.height))
|
||||
}
|
||||
|
||||
private func rotationAngleAt0(itemCount: Int) -> CGFloat {
|
||||
let multiplier: CGFloat = min(CGFloat(itemCount), 5.0) - 1.0
|
||||
return -CGFloat.pi / 7.0 - CGFloat.pi / 7.0 * multiplier / 4.0
|
||||
}
|
||||
|
||||
private class BlurView: UIVisualEffectView {
|
||||
private func setup() {
|
||||
for subview in self.subviews {
|
||||
if subview.description.contains("VisualEffectSubview") {
|
||||
subview.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
if let sublayer = self.layer.sublayers?[0], let filters = sublayer.filters {
|
||||
sublayer.backgroundColor = nil
|
||||
sublayer.isOpaque = false
|
||||
let allowedKeys: [String] = [
|
||||
"gaussianBlur",
|
||||
"colorSaturate"
|
||||
]
|
||||
sublayer.filters = filters.filter { filter in
|
||||
guard let filter = filter as? NSObject else {
|
||||
return true
|
||||
}
|
||||
let filterName = String(describing: filter)
|
||||
if !allowedKeys.contains(filterName) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override var effect: UIVisualEffect? {
|
||||
get {
|
||||
return super.effect
|
||||
}
|
||||
set {
|
||||
super.effect = newValue
|
||||
self.setup()
|
||||
}
|
||||
}
|
||||
|
||||
override func didAddSubview(_ subview: UIView) {
|
||||
super.didAddSubview(subview)
|
||||
self.setup()
|
||||
}
|
||||
}
|
||||
|
||||
private let shadowImage: UIImage? = {
|
||||
return generateImage(CGSize(width: 1.0, height: 480.0), rotatedContext: { size, context in
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
context.clear(bounds)
|
||||
|
||||
let gradientColors = [UIColor.black.withAlphaComponent(0.0).cgColor, UIColor.black.withAlphaComponent(0.55).cgColor, UIColor.black.withAlphaComponent(0.55).cgColor] as CFArray
|
||||
|
||||
var locations: [CGFloat] = [0.0, 0.65, 1.0]
|
||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
|
||||
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: bounds.height), options: [])
|
||||
})
|
||||
}()
|
||||
|
@ -57,8 +57,8 @@ final class MinimizedHeaderNode: ASDisplayNode {
|
||||
|
||||
var title: String? {
|
||||
didSet {
|
||||
if let (size, insets) = self.validLayout {
|
||||
self.update(size: size, insets: insets, transition: .immediate)
|
||||
if let (size, insets, isExpanded) = self.validLayout {
|
||||
self.update(size: size, insets: insets, isExpanded: isExpanded, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -66,7 +66,7 @@ final class MinimizedHeaderNode: ASDisplayNode {
|
||||
var requestClose: () -> Void = {}
|
||||
var requestMaximize: () -> Void = {}
|
||||
|
||||
private var validLayout: (CGSize, UIEdgeInsets)?
|
||||
private var validLayout: (CGSize, UIEdgeInsets, Bool)?
|
||||
|
||||
init(theme: NavigationControllerTheme, strings: PresentationStrings) {
|
||||
self.theme = theme
|
||||
@ -123,19 +123,22 @@ final class MinimizedHeaderNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
func update(size: CGSize, insets: UIEdgeInsets, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, insets)
|
||||
func update(size: CGSize, insets: UIEdgeInsets, isExpanded: Bool, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, insets, isExpanded)
|
||||
|
||||
let headerHeight: CGFloat = 44.0
|
||||
let titleSideInset: CGFloat = 56.0 + insets.left
|
||||
var titleSideInset: CGFloat = 56.0
|
||||
if !isExpanded {
|
||||
titleSideInset += insets.left
|
||||
}
|
||||
|
||||
self.minimizedTitleNode.attributedText = NSAttributedString(string: title ?? "", font: Font.bold(17.0), textColor: self.theme.navigationBar.primaryTextColor)
|
||||
self.minimizedTitleNode.attributedText = NSAttributedString(string: self.title ?? "", font: Font.bold(17.0), textColor: self.theme.navigationBar.primaryTextColor)
|
||||
|
||||
let titleSize = self.minimizedTitleNode.updateLayout(CGSize(width: size.width - titleSideInset * 2.0, height: headerHeight))
|
||||
let titleFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - titleSize.width) / 2.0), y: floorToScreenPixels((headerHeight - titleSize.height) / 2.0)), size: titleSize)
|
||||
self.minimizedTitleNode.bounds = CGRect(origin: .zero, size: titleFrame.size)
|
||||
transition.updatePosition(node: self.minimizedTitleNode, position: titleFrame.center)
|
||||
transition.updateFrame(node: self.minimizedCloseButton, frame: CGRect(origin: CGPoint(x: insets.left, y: 0.0), size: CGSize(width: 44.0, height: 44.0)))
|
||||
transition.updateFrame(node: self.minimizedCloseButton, frame: CGRect(origin: CGPoint(x: isExpanded ? 0.0 : insets.left, y: 0.0), size: CGSize(width: 44.0, height: 44.0)))
|
||||
|
||||
transition.updateFrame(node: self.minimizedBackgroundNode, frame: CGRect(origin: .zero, size: CGSize(width: size.width, height: 243.0)))
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
|
||||
extension CATransform3D {
|
||||
func interpolate(with other: CATransform3D, fraction: CGFloat) -> CATransform3D {
|
||||
@ -50,3 +51,133 @@ extension CGRect {
|
||||
return CGRect(origin: self.origin.interpolate(with: other.origin, fraction: fraction), size: self.size.interpolate(with: other.size, fraction: fraction))
|
||||
}
|
||||
}
|
||||
|
||||
private let maxInteritemSpacing: CGFloat = 240.0
|
||||
let additionalInsetTop: CGFloat = 16.0
|
||||
private let additionalInsetBottom: CGFloat = 0.0
|
||||
private let zOffset: CGFloat = -60.0
|
||||
|
||||
private let perspectiveCorrection: CGFloat = -1.0 / 1000.0
|
||||
private let maxRotationAngle: CGFloat = -CGFloat.pi / 2.2
|
||||
|
||||
func angle(for origin: CGFloat, itemCount: Int, scrollBounds: CGRect, contentHeight: CGFloat?, insets: UIEdgeInsets) -> CGFloat {
|
||||
var rotationAngle = rotationAngleAt0(itemCount: itemCount)
|
||||
|
||||
var contentOffset = scrollBounds.origin.y
|
||||
if contentOffset < 0.0 {
|
||||
contentOffset *= 2.0
|
||||
}
|
||||
|
||||
var yOnScreen = origin - contentOffset - additionalInsetTop - insets.top
|
||||
if yOnScreen < 0 {
|
||||
yOnScreen = 0
|
||||
} else if yOnScreen > scrollBounds.height {
|
||||
yOnScreen = scrollBounds.height
|
||||
}
|
||||
|
||||
let maxRotationVariance = maxRotationAngle - rotationAngleAt0(itemCount: itemCount)
|
||||
rotationAngle += (maxRotationVariance / scrollBounds.height) * yOnScreen
|
||||
|
||||
return rotationAngle
|
||||
}
|
||||
|
||||
func final3dTransform(for origin: CGFloat, size: CGSize, contentHeight: CGFloat?, itemCount: Int, forcedAngle: CGFloat? = nil, additionalAngle: CGFloat? = nil, scrollBounds: CGRect, insets: UIEdgeInsets) -> CATransform3D {
|
||||
var transform = CATransform3DIdentity
|
||||
transform.m34 = perspectiveCorrection
|
||||
|
||||
let rotationAngle = forcedAngle ?? angle(for: origin, itemCount: itemCount, scrollBounds: scrollBounds, contentHeight: contentHeight, insets: insets)
|
||||
var effectiveRotationAngle = rotationAngle
|
||||
if let additionalAngle = additionalAngle {
|
||||
effectiveRotationAngle += additionalAngle
|
||||
}
|
||||
|
||||
let r = size.height / 2.0 + abs(zOffset / sin(rotationAngle))
|
||||
|
||||
let zTranslation = r * sin(rotationAngle)
|
||||
let yTranslation: CGFloat = r * (1 - cos(rotationAngle))
|
||||
|
||||
let zTranslateTransform = CATransform3DTranslate(transform, 0.0, -yTranslation, zTranslation)
|
||||
|
||||
let rotateTransform = CATransform3DRotate(zTranslateTransform, effectiveRotationAngle, 1.0, 0.0, 0.0)
|
||||
|
||||
return rotateTransform
|
||||
}
|
||||
|
||||
func interitemSpacing(itemCount: Int, boundingSize: CGSize, insets: UIEdgeInsets) -> CGFloat {
|
||||
var interitemSpacing = maxInteritemSpacing
|
||||
if itemCount > 0 {
|
||||
interitemSpacing = (boundingSize.height - additionalInsetTop - additionalInsetBottom - insets.top) / CGFloat(min(itemCount, 5))
|
||||
}
|
||||
return interitemSpacing
|
||||
}
|
||||
|
||||
func frameForIndex(index: Int, size: CGSize, insets: UIEdgeInsets, itemCount: Int, boundingSize: CGSize) -> CGRect {
|
||||
let spacing = interitemSpacing(itemCount: itemCount, boundingSize: boundingSize, insets: insets)
|
||||
let y = additionalInsetTop + insets.top + spacing * CGFloat(index)
|
||||
let origin = CGPoint(x: insets.left, y: y)
|
||||
|
||||
return CGRect(origin: origin, size: CGSize(width: size.width - insets.left - insets.right, height: size.height))
|
||||
}
|
||||
|
||||
func rotationAngleAt0(itemCount: Int) -> CGFloat {
|
||||
let multiplier: CGFloat = min(CGFloat(itemCount), 5.0) - 1.0
|
||||
return -CGFloat.pi / 7.0 - CGFloat.pi / 7.0 * multiplier / 4.0
|
||||
}
|
||||
|
||||
final class BlurView: UIVisualEffectView {
|
||||
private func setup() {
|
||||
for subview in self.subviews {
|
||||
if subview.description.contains("VisualEffectSubview") {
|
||||
subview.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
if let sublayer = self.layer.sublayers?[0], let filters = sublayer.filters {
|
||||
sublayer.backgroundColor = nil
|
||||
sublayer.isOpaque = false
|
||||
let allowedKeys: [String] = [
|
||||
"gaussianBlur",
|
||||
"colorSaturate"
|
||||
]
|
||||
sublayer.filters = filters.filter { filter in
|
||||
guard let filter = filter as? NSObject else {
|
||||
return true
|
||||
}
|
||||
let filterName = String(describing: filter)
|
||||
if !allowedKeys.contains(filterName) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override var effect: UIVisualEffect? {
|
||||
get {
|
||||
return super.effect
|
||||
}
|
||||
set {
|
||||
super.effect = newValue
|
||||
self.setup()
|
||||
}
|
||||
}
|
||||
|
||||
override func didAddSubview(_ subview: UIView) {
|
||||
super.didAddSubview(subview)
|
||||
self.setup()
|
||||
}
|
||||
}
|
||||
|
||||
let shadowImage: UIImage? = {
|
||||
return generateImage(CGSize(width: 1.0, height: 480.0), rotatedContext: { size, context in
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
context.clear(bounds)
|
||||
|
||||
let gradientColors = [UIColor.black.withAlphaComponent(0.0).cgColor, UIColor.black.withAlphaComponent(0.55).cgColor, UIColor.black.withAlphaComponent(0.55).cgColor] as CFArray
|
||||
|
||||
var locations: [CGFloat] = [0.0, 0.65, 1.0]
|
||||
let colorSpace = CGColorSpaceCreateDeviceRGB()
|
||||
let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)!
|
||||
context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: bounds.height), options: [])
|
||||
})
|
||||
}()
|
||||
|
@ -519,6 +519,8 @@ public final class StarsWithdrawScreen: ViewControllerComponentContainer {
|
||||
}
|
||||
}
|
||||
|
||||
private let invalidAmountCharacters = CharacterSet.decimalDigits.inverted
|
||||
|
||||
private final class AmountFieldComponent: Component {
|
||||
typealias EnvironmentType = Empty
|
||||
|
||||
@ -623,7 +625,16 @@ private final class AmountFieldComponent: Component {
|
||||
}
|
||||
|
||||
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||
let newText = ((textField.text ?? "") as NSString).replacingCharacters(in: range, with: string)
|
||||
if string.rangeOfCharacter(from: invalidAmountCharacters) != nil {
|
||||
return false
|
||||
}
|
||||
var newText = ((textField.text ?? "") as NSString).replacingCharacters(in: range, with: string)
|
||||
if newText == "0" || (newText.count > 1 && newText.hasPrefix("0")) {
|
||||
newText.removeFirst()
|
||||
textField.text = newText
|
||||
self.textChanged(self.textField)
|
||||
return false
|
||||
}
|
||||
|
||||
if let component = self.component {
|
||||
let amount: Int64?
|
||||
|
@ -2134,6 +2134,7 @@ func chatAvailableMessageActionsImpl(engine: TelegramEngine, accountPeerId: Peer
|
||||
var disableDelete = false
|
||||
var isCopyProtected = false
|
||||
var isShareProtected = false
|
||||
var isExternalShareProtected = false
|
||||
|
||||
var setTag = false
|
||||
var commonTags: Set<MessageReaction.Reaction>?
|
||||
@ -2191,7 +2192,7 @@ func chatAvailableMessageActionsImpl(engine: TelegramEngine, accountPeerId: Peer
|
||||
if let invoice = media as? TelegramMediaInvoice, let _ = invoice.extendedMedia {
|
||||
isShareProtected = true
|
||||
} else if let _ = media as? TelegramMediaPaidContent {
|
||||
isShareProtected = true
|
||||
isExternalShareProtected = true
|
||||
} else if let file = media as? TelegramMediaFile, file.isSticker {
|
||||
for case let .Sticker(_, packReference, _) in file.attributes {
|
||||
if let _ = packReference {
|
||||
@ -2394,7 +2395,7 @@ func chatAvailableMessageActionsImpl(engine: TelegramEngine, accountPeerId: Peer
|
||||
}
|
||||
}
|
||||
|
||||
if !isShareProtected {
|
||||
if !isShareProtected && !isExternalShareProtected {
|
||||
optionsMap[id]!.insert(.externalShare)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user