diff --git a/submodules/AttachmentUI/Sources/AttachmentController.swift b/submodules/AttachmentUI/Sources/AttachmentController.swift index 32ad15d7cf..07271caf62 100644 --- a/submodules/AttachmentUI/Sources/AttachmentController.swift +++ b/submodules/AttachmentUI/Sources/AttachmentController.swift @@ -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) diff --git a/submodules/Display/Source/Navigation/NavigationController.swift b/submodules/Display/Source/Navigation/NavigationController.swift index 6e92565928..299cf98e7c 100644 --- a/submodules/Display/Source/Navigation/NavigationController.swift +++ b/submodules/Display/Source/Navigation/NavigationController.swift @@ -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 { diff --git a/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift b/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift index dc991546e0..add3b3da13 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerSelectedListNode.swift @@ -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)) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageStarsMediaInfoNode/Sources/ChatMessageStarsMediaInfoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageStarsMediaInfoNode/Sources/ChatMessageStarsMediaInfoNode.swift index 173a1a3f13..25836a51b2 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageStarsMediaInfoNode/Sources/ChatMessageStarsMediaInfoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageStarsMediaInfoNode/Sources/ChatMessageStarsMediaInfoNode.swift @@ -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 { diff --git a/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedContainer.swift b/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedContainer.swift index 2ce18364d9..d066796100 100644 --- a/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedContainer.swift +++ b/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedContainer.swift @@ -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: []) - }) -}() diff --git a/submodules/TelegramUI/Components/MinimizedContainer/Sources/HeaderNode.swift b/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedHeaderNode.swift similarity index 87% rename from submodules/TelegramUI/Components/MinimizedContainer/Sources/HeaderNode.swift rename to submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedHeaderNode.swift index f8e5c204ae..7c3681def0 100644 --- a/submodules/TelegramUI/Components/MinimizedContainer/Sources/HeaderNode.swift +++ b/submodules/TelegramUI/Components/MinimizedContainer/Sources/MinimizedHeaderNode.swift @@ -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))) } diff --git a/submodules/TelegramUI/Components/MinimizedContainer/Sources/Utils.swift b/submodules/TelegramUI/Components/MinimizedContainer/Sources/Utils.swift index 47e004f47a..ee5f726e16 100644 --- a/submodules/TelegramUI/Components/MinimizedContainer/Sources/Utils.swift +++ b/submodules/TelegramUI/Components/MinimizedContainer/Sources/Utils.swift @@ -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: []) + }) +}() diff --git a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift index 7ff4843dfd..05869fb375 100644 --- a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift @@ -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? diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 843ec2e6e8..69b5cdc63b 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -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? @@ -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) } }