diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 9877cd2e03..ccb603d087 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -2490,10 +2490,21 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if let componentView = self.headerContentView.view as? ChatListHeaderComponent.View { if let transitionView = componentView.storyPeerListView()?.transitionViewForItem(peerId: peerId) { + let localRect = transitionView.convert(transitionView.bounds, to: self.view) + + Queue.mainQueue().after(0.2 * UIView.animationDurationFactor, { [weak self] in + HapticFeedback().impact() + self?.animateRipple(centerLocation: localRect.center) + }) + return StoryContainerScreen.TransitionOut( destinationView: transitionView, destinationRect: transitionView.bounds, - destinationCornerRadius: transitionView.bounds.height * 0.5 + destinationCornerRadius: transitionView.bounds.height * 0.5, + completed: { [weak self] in + let _ = self + //self?.animateRipple(centerLocation: localRect.center) + } ) } } diff --git a/submodules/ComponentFlow/Source/Base/Transition.swift b/submodules/ComponentFlow/Source/Base/Transition.swift index 4777b81033..f5ef3ae11e 100644 --- a/submodules/ComponentFlow/Source/Base/Transition.swift +++ b/submodules/ComponentFlow/Source/Base/Transition.swift @@ -6,7 +6,7 @@ import Display @_silgen_name("UIAnimationDragCoefficient") func UIAnimationDragCoefficient() -> Float #endif -private extension UIView { +public extension UIView { static var animationDurationFactor: Double { #if targetEnvironment(simulator) return Double(UIAnimationDragCoefficient()) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift index 2a3cdec1d8..d150f3b2d1 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AdMessages.swift @@ -579,6 +579,24 @@ private class AdMessagesHistoryContextImpl { } self.maskAsSeenDisposables.set(signal.start(), forKey: opaqueId) } + + func markAction(opaqueId: Data) { + let account = self.account + let signal: Signal = account.postbox.transaction { transaction -> Api.InputChannel? in + return transaction.getPeer(self.peerId).flatMap(apiInputChannel) + } + |> mapToSignal { inputChannel -> Signal in + guard let inputChannel = inputChannel else { + return .complete() + } + return account.network.request(Api.functions.channels.clickSponsoredMessage(channel: inputChannel, randomId: Buffer(data: opaqueId))) + |> `catch` { _ -> Signal in + return .single(.boolFalse) + } + |> ignoreValues + } + let _ = signal.start() + } } public class AdMessagesHistoryContext { @@ -612,4 +630,10 @@ public class AdMessagesHistoryContext { impl.markAsSeen(opaqueId: opaqueId) } } + + public func markAction(opaqueId: Data) { + self.impl.with { impl in + impl.markAction(opaqueId: opaqueId) + } + } } diff --git a/submodules/TelegramUI/Components/FullScreenEffectView/BUILD b/submodules/TelegramUI/Components/FullScreenEffectView/BUILD index aff39e9fd4..75349b1053 100644 --- a/submodules/TelegramUI/Components/FullScreenEffectView/BUILD +++ b/submodules/TelegramUI/Components/FullScreenEffectView/BUILD @@ -58,6 +58,7 @@ swift_library( "-warnings-as-errors", ], deps = [ + "//submodules/ComponentFlow", ], data = [ ":FullScreenEffectViewBundle", diff --git a/submodules/TelegramUI/Components/FullScreenEffectView/Sources/RippleEffectView.swift b/submodules/TelegramUI/Components/FullScreenEffectView/Sources/RippleEffectView.swift index 9dd60d0b42..b8f93536ac 100644 --- a/submodules/TelegramUI/Components/FullScreenEffectView/Sources/RippleEffectView.swift +++ b/submodules/TelegramUI/Components/FullScreenEffectView/Sources/RippleEffectView.swift @@ -2,6 +2,7 @@ import Foundation import Metal import MetalKit import simd +import ComponentFlow public final class RippleEffectView: MTKView { private let centerLocation: CGPoint @@ -154,7 +155,7 @@ public final class RippleEffectView: MTKView { let relativeTime: Double let timestamp = CACurrentMediaTime() if let startTime = self.startTime { - relativeTime = timestamp - startTime + relativeTime = (timestamp - startTime) * (1.0 / UIView.animationDurationFactor) } else { self.startTime = timestamp relativeTime = 0.0 diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift index 37664ed4ca..b5e94aa619 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContainerScreen.swift @@ -115,6 +115,8 @@ private final class StoryContainerScreenComponent: Component { private weak var state: EmptyComponentState? private var environment: ViewControllerComponentContainer.Environment? + private let backgroundLayer: SimpleLayer + private var focusedItemSet: AnyHashable? private var itemSets: [StoryContentItemSlice] = [] private var visibleItemSetViews: [AnyHashable: ItemSetView] = [:] @@ -122,10 +124,14 @@ private final class StoryContainerScreenComponent: Component { private var itemSetPanState: ItemSetPanState? private var dismissPanState: ItemSetPanState? + private var isAnimatingOut: Bool = false + private var didAnimateOut: Bool = false + override init(frame: CGRect) { - super.init(frame: frame) + self.backgroundLayer = SimpleLayer() + self.backgroundLayer.backgroundColor = UIColor.black.cgColor - self.backgroundColor = .black + super.init(frame: frame) let horizontalPanRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: #selector(self.panGesture(_:)), allowedDirections: { [weak self] point in guard let self, let focusedItemSet = self.focusedItemSet, let itemSetView = self.visibleItemSetViews[focusedItemSet], let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View else { @@ -320,7 +326,7 @@ private final class StoryContainerScreenComponent: Component { func animateIn() { if let transitionIn = self.component?.transitionIn, transitionIn.sourceView != nil { - self.layer.animate(from: UIColor.black.withAlphaComponent(0.0).cgColor, to: self.layer.backgroundColor ?? UIColor.black.cgColor, keyPath: "backgroundColor", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.28) + self.backgroundLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.28, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue) if let transitionIn = self.component?.transitionIn, let focusedItemSet = self.focusedItemSet, let itemSetView = self.visibleItemSetViews[focusedItemSet] { if let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View { @@ -336,17 +342,26 @@ private final class StoryContainerScreenComponent: Component { } func animateOut(completion: @escaping () -> Void) { + self.isAnimatingOut = true + self.state?.updated(transition: .immediate) + if let component = self.component, let focusedItemSet = self.focusedItemSet, let peerId = focusedItemSet.base as? EnginePeer.Id, let itemSetView = self.visibleItemSetViews[focusedItemSet], let itemSetComponentView = itemSetView.view.view as? StoryItemSetContainerComponent.View, let transitionOut = component.transitionOut(peerId) { - let currentBackgroundColor = self.layer.presentation()?.backgroundColor ?? self.layer.backgroundColor - self.layer.animate(from: currentBackgroundColor ?? UIColor.black.cgColor, to: UIColor.black.withAlphaComponent(0.0).cgColor, keyPath: "backgroundColor", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.25, removeOnCompletion: false) + let transition = Transition(animation: .curve(duration: 0.25, curve: .easeInOut)) + transition.setAlpha(layer: self.backgroundLayer, alpha: 0.0) - itemSetComponentView.animateOut(transitionOut: transitionOut, completion: completion) + let transitionOutCompleted = transitionOut.completed + itemSetComponentView.animateOut(transitionOut: transitionOut, completion: { + completion() + transitionOutCompleted() + }) } else { self.layer.allowsGroupOpacity = true self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in completion() }) } + + self.didAnimateOut = true } private func updatePreloads() { @@ -381,6 +396,10 @@ private final class StoryContainerScreenComponent: Component { } func update(component: StoryContainerScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + if self.didAnimateOut { + return availableSize + } + let isFirstTime = self.component == nil let environment = environment[ViewControllerComponentContainer.Environment.self].value @@ -405,6 +424,9 @@ private final class StoryContainerScreenComponent: Component { if self.dismissPanState != nil { isProgressPaused = true } + if self.isAnimatingOut { + isProgressPaused = true + } var dismissPanOffset: CGFloat = 0.0 var dismissPanScale: CGFloat = 1.0 @@ -415,7 +437,7 @@ private final class StoryContainerScreenComponent: Component { dismissAlphaScale = 1.0 * (1.0 - dismissPanState.fraction) + 0.2 * dismissPanState.fraction } - transition.setBackgroundColor(view: self, color: UIColor.black.withAlphaComponent(max(0.5, dismissAlphaScale))) + transition.setAlpha(layer: self.backgroundLayer, alpha: max(0.5, dismissAlphaScale)) var contentDerivedBottomInset: CGFloat = environment.safeInsets.bottom @@ -727,15 +749,18 @@ public class StoryContainerScreen: ViewControllerComponentContainer { public weak var destinationView: UIView? public let destinationRect: CGRect public let destinationCornerRadius: CGFloat + public let completed: () -> Void public init( destinationView: UIView, destinationRect: CGRect, - destinationCornerRadius: CGFloat + destinationCornerRadius: CGFloat, + completed: @escaping () -> Void ) { self.destinationView = destinationView self.destinationRect = destinationRect self.destinationCornerRadius = destinationCornerRadius + self.completed = completed } } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 1c23744af1..8f21701c98 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -4329,6 +4329,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } + self.chatDisplayNode.historyNode.adMessagesContext?.markAction(opaqueId: adAttribute.opaqueId) + switch adAttribute.target { case let .peer(id, messageId, startParam): if case let .peer(currentPeerId) = self.chatLocation, currentPeerId == id { diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index d44e2ac7fd..26b5fad9f5 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -636,7 +636,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { var openNextChannelToRead: ((EnginePeer, TelegramEngine.NextUnreadChannelLocation) -> Void)? private var contentInsetAnimator: DisplayLinkAnimator? - private let adMessagesContext: AdMessagesHistoryContext? + let adMessagesContext: AdMessagesHistoryContext? private var adMessagesDisposable: Disposable? private var preloadAdPeerId: PeerId? private let preloadAdPeerDisposable = MetaDisposable()