diff --git a/submodules/ChatListUI/Sources/ChatContextMenus.swift b/submodules/ChatListUI/Sources/ChatContextMenus.swift index 672ec628ea..23bc17ed13 100644 --- a/submodules/ChatListUI/Sources/ChatContextMenus.swift +++ b/submodules/ChatListUI/Sources/ChatContextMenus.swift @@ -323,9 +323,7 @@ func chatContextMenuItems(context: AccountContext, peerId: PeerId, promoInfo: Ch var replaceImpl: ((ViewController) -> Void)? let controller = PremiumLimitScreen(context: context, subject: .pins, action: { - let premiumScreen = PremiumIntroScreen(context: context, action: { - - }) + let premiumScreen = PremiumIntroScreen(context: context) replaceImpl?(premiumScreen) }) chatListController?.push(controller) diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index ad293b8e21..c91b6a73df 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -1342,9 +1342,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController if filter.data.includePeers.peers.count >= limit { var replaceImpl: ((ViewController) -> Void)? let controller = PremiumLimitScreen(context: context, subject: .chatsInFolder, action: { - let controller = PremiumIntroScreen(context: context, action: { - - }) + let controller = PremiumIntroScreen(context: context) replaceImpl?(controller) }) replaceImpl = { [weak controller] c in diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift index 0abd8a75e6..0ae2c4f341 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetListController.swift @@ -292,9 +292,7 @@ public func chatListFilterPresetListController(context: AccountContext, mode: Ch if filters.count >= limit { var replaceImpl: ((ViewController) -> Void)? let controller = PremiumLimitScreen(context: context, subject: .folders, action: { - let controller = PremiumIntroScreen(context: context, action: { - - }) + let controller = PremiumIntroScreen(context: context) replaceImpl?(controller) }) replaceImpl = { [weak controller] c in diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index a89b0b66c4..e77cf7a025 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -845,9 +845,7 @@ public final class ChatListNode: ListView { case .limitExceeded: var replaceImpl: ((ViewController) -> Void)? let controller = PremiumLimitScreen(context: context, subject: .pins, action: { - let premiumScreen = PremiumIntroScreen(context: context, action: { - - }) + let premiumScreen = PremiumIntroScreen(context: context) replaceImpl?(premiumScreen) }) replaceImpl = { [weak controller] c in diff --git a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift index eba1682f2d..6f2fbef360 100644 --- a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift @@ -22,6 +22,7 @@ import InviteLinksUI import ContextUI import UndoUI import QrCodeUI +import PremiumUI private final class ChannelVisibilityControllerArguments { let context: AccountContext @@ -1628,7 +1629,10 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta } if hasNamesToRevoke && selectedType == .publicChannel { - footerItem = IncreaseLimitFooterItem(theme: presentationData.theme, title: presentationData.strings.Premium_IncreaseLimit, colorful: true, action: {}) + footerItem = IncreaseLimitFooterItem(theme: presentationData.theme, title: presentationData.strings.Premium_IncreaseLimit, colorful: true, action: { + let controller = PremiumIntroScreen(context: context) + pushControllerImpl?(controller) + }) } if let hadNamesToRevoke = hadNamesToRevoke { diff --git a/submodules/PeerInfoUI/Sources/IncreaseLimitHeaderItem.swift b/submodules/PeerInfoUI/Sources/IncreaseLimitHeaderItem.swift index dc25f7f8b6..0e7cf8b938 100644 --- a/submodules/PeerInfoUI/Sources/IncreaseLimitHeaderItem.swift +++ b/submodules/PeerInfoUI/Sources/IncreaseLimitHeaderItem.swift @@ -145,7 +145,7 @@ class IncreaseLimitHeaderItemNode: ListViewItemNode { let size = strongSelf.hostView.update( transition: .immediate, component: AnyComponent(PremiumLimitDisplayComponent( - inactiveColor: UIColor(rgb: 0xE9E9EA), + inactiveColor: UIColor(rgb: 0xe3e3e9), activeColors: [ UIColor(rgb: 0x0077ff), UIColor(rgb: 0x6b93ff), diff --git a/submodules/PeerInfoUI/Sources/OldChannelsController.swift b/submodules/PeerInfoUI/Sources/OldChannelsController.swift index e09d89e53b..32ae339284 100644 --- a/submodules/PeerInfoUI/Sources/OldChannelsController.swift +++ b/submodules/PeerInfoUI/Sources/OldChannelsController.swift @@ -12,6 +12,7 @@ import AccountContext import ContactsPeerItem import SearchUI import SolidRoundedButtonNode +import PremiumUI func localizedOldChannelDate(peer: InactiveChannel, strings: PresentationStrings) -> String { let timestamp = peer.lastActivityDate @@ -216,6 +217,7 @@ public func oldChannelsController(context: AccountContext, updatedPresentationDa } var dismissImpl: (() -> Void)? + var pushImpl: ((ViewController) -> Void)? var setDisplayNavigationBarImpl: ((Bool) -> Void)? var ensurePeerVisibleImpl: ((PeerId) -> Void)? @@ -321,7 +323,12 @@ public func oldChannelsController(context: AccountContext, updatedPresentationDa colorful = true } let footerItem = IncreaseLimitFooterItem(theme: presentationData.theme, title: buttonText, colorful: colorful, action: { - leaveActionImpl?() + if state.selectedPeers.count > 0 { + leaveActionImpl?() + } else { + let controller = PremiumIntroScreen(context: context) + pushImpl?(controller) + } }) let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: oldChannelsEntries(presentationData: presentationData, state: state, limit: limits.maxChannelsCount, premiumLimit: premiumLimits.maxChannelsCount, peers: peers, intent: intent), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, footerItem: footerItem, initialScrollToItem: ListViewScrollToItem(index: 0, position: .top(-navigationBarSearchContentHeight), animated: false, curve: .Default(duration: 0.0), directionHint: .Up), crossfadeState: peersAreEmptyUpdated, animateChanges: false) @@ -364,6 +371,9 @@ public func oldChannelsController(context: AccountContext, updatedPresentationDa dismissImpl = { [weak controller] in controller?.dismiss() } + pushImpl = { [weak controller] c in + controller?.push(c) + } setDisplayNavigationBarImpl = { [weak controller] display in controller?.setDisplayNavigationBar(display, transition: .animated(duration: 0.5, curve: .spring)) } diff --git a/submodules/PremiumUI/Resources/star.scn b/submodules/PremiumUI/Resources/star.scn index 926ae31829..3fcbdaa255 100644 Binary files a/submodules/PremiumUI/Resources/star.scn and b/submodules/PremiumUI/Resources/star.scn differ diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index 4cbacbdcf5..b326b49c5c 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -23,8 +23,14 @@ private func rad2deg(_ number: Float) -> Float { } private class StarComponent: Component { + let isVisible: Bool + + init(isVisible: Bool) { + self.isVisible = isVisible + } + static func ==(lhs: StarComponent, rhs: StarComponent) -> Bool { - return true + return lhs.isVisible == rhs.isVisible } final class View: UIView, SCNSceneRendererDelegate, ComponentTaggedView { @@ -149,7 +155,7 @@ private class StarComponent: Component { smallAngle = true } - self.playAppearanceAnimation(velocity: velocity.x, smallAngle: smallAngle, explode: !smallAngle && velocity.x > 600) + self.playAppearanceAnimation(velocity: velocity.x, smallAngle: smallAngle, explode: !smallAngle && abs(velocity.x) > 600) node.rotation = SCNVector4(x: 0.0, y: 1.0, z: 0.0, w: 0.0) default: break @@ -237,12 +243,14 @@ private class StarComponent: Component { particleSystem?.particleColorVariation = SCNVector4(0.15, 0.2, 0.35, 0.3) particleSystem?.particleVelocity = 2.2 particleSystem?.birthRate = 4.5 + particleSystem?.particleLifeSpan = 2.0 node.physicsField?.isActive = true Queue.mainQueue().after(1.0) { node.physicsField?.isActive = false particles.particleSystems?.first?.birthRate = 1.2 particleSystem?.particleVelocity = 1.65 + particleSystem?.particleLifeSpan = 4.0 } } @@ -459,15 +467,18 @@ private final class ScrollComponent: Component { public let content: AnyComponent<(ChildEnvironment, ScrollChildEnvironment)> public let contentInsets: UIEdgeInsets public let contentOffsetUpdated: (_ top: CGFloat, _ bottom: CGFloat) -> Void + public let contentOffsetWillCommit: (UnsafeMutablePointer) -> Void public init( content: AnyComponent<(ChildEnvironment, ScrollChildEnvironment)>, contentInsets: UIEdgeInsets, - contentOffsetUpdated: @escaping (_ top: CGFloat, _ bottom: CGFloat) -> Void + contentOffsetUpdated: @escaping (_ top: CGFloat, _ bottom: CGFloat) -> Void, + contentOffsetWillCommit: @escaping (UnsafeMutablePointer) -> Void ) { self.content = content self.contentInsets = contentInsets self.contentOffsetUpdated = contentOffsetUpdated + self.contentOffsetWillCommit = contentOffsetWillCommit } public static func ==(lhs: ScrollComponent, rhs: ScrollComponent) -> Bool { @@ -504,12 +515,18 @@ private final class ScrollComponent: Component { guard let component = self.component, !self.ignoreDidScroll else { return } - let topOffset = scrollView.contentOffset.y let bottomOffset = max(0.0, scrollView.contentSize.height - scrollView.contentOffset.y - scrollView.frame.height) component.contentOffsetUpdated(topOffset, bottomOffset) } + public func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { + guard let component = self.component, !self.ignoreDidScroll else { + return + } + component.contentOffsetWillCommit(targetContentOffset) + } + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -1043,11 +1060,9 @@ private final class PremiumIntroScreenComponent: CombinedComponent { typealias EnvironmentType = ViewControllerComponentContainer.Environment let context: AccountContext - let action: () -> Void - init(context: AccountContext, action: @escaping () -> Void) { + init(context: AccountContext) { self.context = context - self.action = action } static func ==(lhs: PremiumIntroScreenComponent, rhs: PremiumIntroScreenComponent) -> Bool { @@ -1083,9 +1098,14 @@ private final class PremiumIntroScreenComponent: CombinedComponent { let background = background.update(component: Rectangle(color: environment.theme.list.blocksBackgroundColor), environment: {}, availableSize: context.availableSize, transition: context.transition) + var starIsVisible = true + if let topContentOffset = state.topContentOffset, topContentOffset >= 123.0 { + starIsVisible = false + } + let star = star.update( - component: StarComponent(), - availableSize: CGSize(width: min(390.0, context.availableSize.width), height: 180.0), + component: StarComponent(isVisible: starIsVisible), + availableSize: CGSize(width: min(390.0, context.availableSize.width), height: 220.0), transition: context.transition ) @@ -1132,7 +1152,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent { height: 50.0, cornerRadius: 10.0, gloss: true, - action: context.component.action + action: {} ), availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - environment.safeInsets.left - environment.safeInsets.right, height: 50.0), transition: context.transition) @@ -1165,6 +1185,13 @@ private final class PremiumIntroScreenComponent: CombinedComponent { state?.topContentOffset = topContentOffset state?.bottomContentOffset = bottomContentOffset state?.updated(transition: .immediate) + }, + contentOffsetWillCommit: { targetContentOffset in + if targetContentOffset.pointee.y < 100.0 { + targetContentOffset.pointee = CGPoint(x: 0.0, y: 0.0) + } else if targetContentOffset.pointee.y < 123.0 { + targetContentOffset.pointee = CGPoint(x: 0.0, y: 123.0) + } } ), environment: { environment }, @@ -1184,19 +1211,21 @@ private final class PremiumIntroScreenComponent: CombinedComponent { let titleOffset: CGFloat let titleScale: CGFloat let titleOffsetDelta = 160.0 - environment.navigationHeight / 2.0 - + if let topContentOffset = state.topContentOffset { - topPanelAlpha = min(30.0, max(0.0, topContentOffset - 80.0)) / 30.0 + topPanelAlpha = min(20.0, max(0.0, topContentOffset - 95.0)) / 20.0 + let topContentOffset = topContentOffset + max(0.0, min(1.0, topContentOffset / titleOffsetDelta)) * 10.0 titleOffset = topContentOffset - titleScale = 1.0 - max(0.0, min(1.0, titleOffset / titleOffsetDelta)) * 0.36 + let fraction = max(0.0, min(1.0, titleOffset / titleOffsetDelta)) + titleScale = 1.0 - fraction * 0.36 } else { topPanelAlpha = 0.0 - titleOffset = 0.0 titleScale = 1.0 + titleOffset = 0.0 } context.add(star - .position(CGPoint(x: context.availableSize.width / 2.0, y: star.size.height / 2.0 - 10.0 - titleOffset * titleScale)) + .position(CGPoint(x: context.availableSize.width / 2.0, y: star.size.height / 2.0 - 30.0 - titleOffset * titleScale)) .scale(titleScale) ) @@ -1240,7 +1269,6 @@ private final class PremiumIntroScreenComponent: CombinedComponent { public final class PremiumIntroScreen: ViewControllerComponentContainer { private let context: AccountContext - private let action: () -> Void private var didSetReady = false private let _ready = Promise() @@ -1248,11 +1276,10 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer { return self._ready } - public init(context: AccountContext, action: @escaping () -> Void) { + public init(context: AccountContext) { self.context = context - self.action = action - super.init(context: context, component: PremiumIntroScreenComponent(context: context, action: action), navigationBarAppearance: .transparent) + super.init(context: context, component: PremiumIntroScreenComponent(context: context), navigationBarAppearance: .transparent) let presentationData = context.sharedContext.currentPresentationData.with { $0 } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index a71513f605..995c3849d7 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -7949,9 +7949,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.presentInGlobalOverlay(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: stickerFile, title: strongSelf.presentationData.strings.Premium_MaxFavedStickersTitle("5").string, text: strongSelf.presentationData.strings.Premium_MaxFavedStickersText("10").string), elevatedLayout: true, action: { [weak self] action in if let strongSelf = self { if case .info = action { - let controller = PremiumIntroScreen(context: strongSelf.context, action: { - - }) + let controller = PremiumIntroScreen(context: strongSelf.context) strongSelf.push(controller) return true }