diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index 75ffce2a03..a66a47da4d 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -1045,6 +1045,8 @@ public protocol ChatController: ViewController { func updateIsScrollingLockedAtTop(isScrollingLockedAtTop: Bool) func playShakeAnimation() + + func removeAd(opaqueId: Data) } public protocol ChatMessagePreviewItemNode: AnyObject { diff --git a/submodules/AccountContext/Sources/GalleryController.swift b/submodules/AccountContext/Sources/GalleryController.swift index 33b8a49c8f..3342beb0a8 100644 --- a/submodules/AccountContext/Sources/GalleryController.swift +++ b/submodules/AccountContext/Sources/GalleryController.swift @@ -17,18 +17,32 @@ public final class GalleryControllerActionInteraction { public let openPeer: (EnginePeer) -> Void public let openHashtag: (String?, String) -> Void public let openBotCommand: (String) -> Void + public let openAd: (MessageId) -> Void public let addContact: (String) -> Void public let storeMediaPlaybackState: (MessageId, Double?, Double) -> Void public let editMedia: (MessageId, [UIView], @escaping () -> Void) -> Void public let updateCanReadHistory: (Bool) -> Void - public init(openUrl: @escaping (String, Bool) -> Void, openUrlIn: @escaping (String) -> Void, openPeerMention: @escaping (String) -> Void, openPeer: @escaping (EnginePeer) -> Void, openHashtag: @escaping (String?, String) -> Void, openBotCommand: @escaping (String) -> Void, addContact: @escaping (String) -> Void, storeMediaPlaybackState: @escaping (MessageId, Double?, Double) -> Void, editMedia: @escaping (MessageId, [UIView], @escaping () -> Void) -> Void, updateCanReadHistory: @escaping (Bool) -> Void) { + public init( + openUrl: @escaping (String, Bool) -> Void, + openUrlIn: @escaping (String) -> Void, + openPeerMention: @escaping (String) -> Void, + openPeer: @escaping (EnginePeer) -> Void, + openHashtag: @escaping (String?, String) -> Void, + openBotCommand: @escaping (String) -> Void, + openAd: @escaping (MessageId) -> Void, + addContact: @escaping (String) -> Void, + storeMediaPlaybackState: @escaping (MessageId, Double?, Double) -> Void, + editMedia: @escaping (MessageId, [UIView], @escaping () -> Void) -> Void, + updateCanReadHistory: @escaping (Bool) -> Void) + { self.openUrl = openUrl self.openUrlIn = openUrlIn self.openPeerMention = openPeerMention self.openPeer = openPeer self.openHashtag = openHashtag self.openBotCommand = openBotCommand + self.openAd = openAd self.addContact = addContact self.storeMediaPlaybackState = storeMediaPlaybackState self.editMedia = editMedia diff --git a/submodules/AdUI/Sources/AdInfoScreen.swift b/submodules/AdUI/Sources/AdInfoScreen.swift index 4bdbf3a484..9a3309f9b8 100644 --- a/submodules/AdUI/Sources/AdInfoScreen.swift +++ b/submodules/AdUI/Sources/AdInfoScreen.swift @@ -69,7 +69,7 @@ public final class AdInfoScreen: ViewController { self.controller = controller self.context = context - self.presentationData = context.sharedContext.currentPresentationData.with { $0 } + self.presentationData = controller.presentationData self.titleNode = ImmediateTextNode() self.titleNode.maximumNumberOfLines = 1 @@ -211,11 +211,16 @@ public final class AdInfoScreen: ViewController { } private let context: AccountContext - private var presentationData: PresentationData + fileprivate var presentationData: PresentationData - public init(context: AccountContext) { + public init(context: AccountContext, forceDark: Bool = false) { self.context = context - self.presentationData = context.sharedContext.currentPresentationData.with { $0 } + + var presentationData = context.sharedContext.currentPresentationData.with { $0 } + if forceDark { + presentationData = presentationData.withUpdated(theme: defaultDarkColorPresentationTheme) + } + self.presentationData = presentationData super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData)) diff --git a/submodules/Display/Source/PresentationContext.swift b/submodules/Display/Source/PresentationContext.swift index 804cabd50a..cfcf9bdcf0 100644 --- a/submodules/Display/Source/PresentationContext.swift +++ b/submodules/Display/Source/PresentationContext.swift @@ -18,7 +18,7 @@ public enum PresentationContextType { public final class PresentationContext { private var _view: UIView? - var view: UIView? { + public var view: UIView? { get { return self._view } set(value) { @@ -123,6 +123,9 @@ public final class PresentationContext { return (containerLayout, CGRect(origin: CGPoint(), size: containerLayout.size)) } + public init() { + } + public func present(_ controller: ContainableController, on level: PresentationSurfaceLevel, blockInteraction: Bool = false, completion: @escaping () -> Void) { let controllerReady = controller.ready.get() |> filter({ $0 }) diff --git a/submodules/GalleryUI/BUILD b/submodules/GalleryUI/BUILD index eb4b4c1367..601f15eb8f 100644 --- a/submodules/GalleryUI/BUILD +++ b/submodules/GalleryUI/BUILD @@ -49,6 +49,9 @@ swift_library( "//submodules/TooltipUI", "//submodules/TelegramNotices", "//submodules/Pasteboard", + "//submodules/AdUI", + "//submodules/TelegramUI/Components/Ads/AdsInfoScreen", + "//submodules/TelegramUI/Components/Ads/AdsReportScreen", ], visibility = [ "//visibility:public", diff --git a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift index 7105ccdb9b..f9b7d2065d 100644 --- a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift +++ b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift @@ -29,6 +29,7 @@ import Pasteboard import Speak import TranslateUI import TelegramNotices +import SolidRoundedButtonNode private let deleteImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionTrash"), color: .white) private let actionImage = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Accessory Panels/MessageSelectionForward"), color: .white) @@ -145,6 +146,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll private let textNode: ImmediateTextNodeWithEntities private var spoilerTextNode: ImmediateTextNodeWithEntities? private var dustNode: InvisibleInkDustNode? + private var buttonNode: SolidRoundedButtonNode? private var textSelectionNode: TextSelectionNode? @@ -164,7 +166,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll private var currentMessageText: NSAttributedString? private var currentAuthorNameText: String? private var currentDateText: String? - + private var currentMessage: Message? private var currentWebPageAndMedia: (TelegramMediaWebpage, Media)? private let messageContextDisposable = MetaDisposable() @@ -193,6 +195,13 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll var performAction: ((GalleryControllerInteractionTapAction) -> Void)? var openActionOptions: ((GalleryControllerInteractionTapAction, Message) -> Void)? + private var isAd: Bool { + if self.currentMessage?.adAttribute != nil { + return true + } + return false + } + var content: ChatItemGalleryFooterContent = .info { didSet { if self.content != oldValue { @@ -208,8 +217,9 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll self.currentIsPaused = true self.authorNameNode.isHidden = true self.dateNode.isHidden = true - self.hasSeekControls = seekable - if status == .Local { + self.hasSeekControls = seekable && !self.isAd + + if status == .Local && !self.isAd { self.playbackControlButton.isHidden = false self.playPauseIconNode.enqueueState(.play, animated: true) } else { @@ -236,16 +246,20 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll self.currentIsPaused = paused self.authorNameNode.isHidden = true self.dateNode.isHidden = true - self.hasSeekControls = seekable - self.playbackControlButton.isHidden = false - let icon: PlayPauseIconNodeState - if let wasPlaying = self.wasPlaying { - icon = wasPlaying ? .pause : .play + if !self.isAd { + self.playbackControlButton.isHidden = false + let icon: PlayPauseIconNodeState + if let wasPlaying = self.wasPlaying { + icon = wasPlaying ? .pause : .play + } else { + icon = paused ? .play : .pause + } + self.playPauseIconNode.enqueueState(icon, animated: true) + self.hasSeekControls = seekable } else { - icon = paused ? .play : .pause + self.hasSeekControls = false } - self.playPauseIconNode.enqueueState(icon, animated: true) self.statusButtonNode.isHidden = true self.statusNode.isHidden = true } @@ -829,15 +843,10 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll if Namespaces.Message.allNonRegular.contains(message.id.namespace) || message.timestamp == 0 { displayInfo = false } - if let _ = message.adAttribute { - displayInfo = false - } - + var canFullscreen = false var canDelete: Bool var canShare = !message.containsSecretMedia && !Namespaces.Message.allNonRegular.contains(message.id.namespace) && message.adAttribute == nil - - var canFullscreen = false - + var canEdit = false var isImage = false var isVideo = false @@ -924,6 +933,11 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll canDelete = false } + if let _ = message.adAttribute { + displayInfo = false + canFullscreen = false + } + var authorNameText: String? if let forwardInfo = message.forwardInfo, forwardInfo.flags.contains(.isImported), let authorSignature = forwardInfo.authorSignature { authorNameText = authorSignature @@ -1044,6 +1058,22 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll self.actionButton.isHidden = !canShare self.editButton.isHidden = !canEdit + if let adAttribute = message.adAttribute { + if self.buttonNode == nil { + let buttonNode = SolidRoundedButtonNode(title: adAttribute.buttonText, theme: SolidRoundedButtonTheme(backgroundColor: UIColor(rgb: 0xffffff, alpha: 0.15), foregroundColor: UIColor(rgb: 0xffffff)), height: 50.0, cornerRadius: 11.0) + buttonNode.pressed = { [weak self] in + guard let self else { + return + } + self.performAction?(.ad(message.id)) + } + self.contentNode.addSubnode(buttonNode) + self.buttonNode = buttonNode + } + } else if let buttonNode = self.buttonNode { + buttonNode.removeFromSupernode() + } + self.requestLayout?(.immediate) } } @@ -1192,6 +1222,17 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll self.scrollWrapperNode.layer.mask?.frame = self.scrollWrapperNode.bounds self.scrollWrapperNode.layer.mask?.removeAllAnimations() } + + if let buttonNode = self.buttonNode { + let buttonHeight = buttonNode.updateLayout(width: constrainSize.width, transition: transition) + transition.updateFrame(node: buttonNode, frame: CGRect(origin: CGPoint(x: sideInset, y: scrollWrapperNodeFrame.maxY + 8.0), size: CGSize(width: constrainSize.width, height: buttonHeight))) + + if let _ = self.scrubberView { + panelHeight += 68.0 + } else { + panelHeight += 22.0 + } + } } textFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset + textOffset), size: textSize) @@ -1227,6 +1268,10 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll } } + if let _ = self.buttonNode { + panelHeight -= 44.0 + } + let scrubberFrame = CGRect(origin: CGPoint(x: leftInset, y: scrubberY), size: CGSize(width: width - leftInset - rightInset, height: 34.0)) scrubberView.updateLayout(size: size, leftInset: leftInset, rightInset: rightInset, transition: .immediate) transition.updateBounds(layer: scrubberView.layer, bounds: CGRect(origin: CGPoint(), size: scrubberFrame.size)) @@ -1325,6 +1370,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll self.forwardButton.alpha = self.hasSeekControls ? 1.0 : 0.0 self.statusNode.alpha = 1.0 self.playbackControlButton.alpha = 1.0 + self.buttonNode?.alpha = 1.0 self.scrollWrapperNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) } @@ -1349,6 +1395,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, ASScroll self.forwardButton.alpha = 0.0 self.statusNode.alpha = 0.0 self.playbackControlButton.alpha = 0.0 + self.buttonNode?.alpha = 0.0 self.scrollWrapperNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, completion: { _ in completion() }) diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index 4ce047d5ce..5c65e2a77d 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -487,6 +487,7 @@ public enum GalleryControllerInteractionTapAction { case botCommand(String) case hashtag(String?, String) case timecode(Double, String) + case ad(MessageId) } public enum GalleryControllerItemNodeAction { @@ -962,6 +963,8 @@ public class GalleryController: ViewController, StandalonePresentableController, strongSelf.actionInteraction?.openHashtag(peerName, hashtag) case let .timecode(timecode, _): strongSelf.galleryNode.pager.centralItemNode()?.processAction(.timecode(timecode)) + case let .ad(messageId): + strongSelf.actionInteraction?.openAd(messageId) } } } @@ -1217,6 +1220,8 @@ public class GalleryController: ViewController, StandalonePresentableController, ]) ]) strongSelf.present(actionSheet, in: .window(.root)) + case .ad: + break } } } diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index 2fa3300c5f..1a39e933c7 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -22,6 +22,9 @@ import UndoUI import ContextUI import SaveToCameraRoll import Pasteboard +import AdUI +import AdsInfoScreen +import AdsReportScreen enum ChatMediaGalleryThumbnail: Equatable { case image(ImageMediaReference) @@ -545,40 +548,38 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { if adAttribute.canReport { actions.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_ContextMenu_AboutAd, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor) - }, iconSource: nil, action: { _, f in + }, iconSource: nil, action: { [weak self] _, f in f(.dismissWithoutContent) - -// controllerInteraction.navigationController()?.pushViewController(AdsInfoScreen(context: context)) + if let navigationController = self?.baseNavigationController() as? NavigationController { + navigationController.pushViewController(AdsInfoScreen(context: context)) + } }))) actions.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_ContextMenu_ReportAd, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.actionSheet.primaryTextColor) - }, iconSource: nil, action: { _, f in + }, iconSource: nil, action: { [weak self] _, f in f(.default) let _ = (context.engine.messages.reportAdMessage(peerId: message.id.peerId, opaqueId: adAttribute.opaqueId, option: nil) - |> deliverOnMainQueue).start(next: { result in + |> deliverOnMainQueue).start(next: { [weak self] result in if case let .options(title, options) = result { - let _ = title - let _ = options -// controllerInteraction.navigationController()?.pushViewController( -// AdsReportScreen( -// context: context, -// peerId: message.id.peerId, -// opaqueId: adAttribute.opaqueId, -// title: title, -// options: options, -// completed: { [weak interfaceInteraction] in -// guard let interfaceInteraction else { -// return -// } -// guard let chatController = interfaceInteraction.chatController() as? ChatControllerImpl else { -// return -// } -// chatController.removeAd(opaqueId: adAttribute.opaqueId) -// } -// ) -// ) + if let navigationController = self?.baseNavigationController() as? NavigationController { + navigationController.pushViewController( + AdsReportScreen( + context: context, + peerId: message.id.peerId, + opaqueId: adAttribute.opaqueId, + title: title, + options: options, + forceDark: true, + completed: { + if let navigationController = self?.baseNavigationController() as? NavigationController, let chatController = navigationController.viewControllers.last as? ChatController { + chatController.removeAd(opaqueId: adAttribute.opaqueId) + } + } + ) + ) + } } }) }))) @@ -587,40 +588,53 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { actions.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_ContextMenu_RemoveAd, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.primaryTextColor) - }, iconSource: nil, action: { c, _ in + }, iconSource: nil, action: { [weak self] c, _ in c?.dismiss(completion: { -// controllerInteraction.openNoAdsDemo() + var replaceImpl: ((ViewController) -> Void)? + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .noAds, forceDark: true, action: { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .ads, forceDark: true, dismissed: nil) + replaceImpl?(controller) + }, dismissed: nil) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + if let navigationController = self?.baseNavigationController() as? NavigationController { + navigationController.pushViewController(controller) + } }) }))) } else { actions.append(.action(ContextMenuActionItem(text: presentationData.strings.SponsoredMessageMenu_Info, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor) - }, iconSource: nil, action: { _, f in + }, iconSource: nil, action: { [weak self] _, f in f(.dismissWithoutContent) -// controllerInteraction.navigationController()?.pushViewController(AdInfoScreen(context: context)) + if let navigationController = self?.baseNavigationController() as? NavigationController { + navigationController.pushViewController(AdInfoScreen(context: context, forceDark: true)) + } }))) let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) if !context.isPremium && !premiumConfiguration.isPremiumDisabled { actions.append(.action(ContextMenuActionItem(text: presentationData.strings.SponsoredMessageMenu_Hide, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.primaryTextColor) - }, iconSource: nil, action: { c, _ in + }, iconSource: nil, action: { [weak self] c, _ in c?.dismiss(completion: { var replaceImpl: ((ViewController) -> Void)? - let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .noAds, forceDark: false, action: { - let controller = context.sharedContext.makePremiumIntroController(context: context, source: .ads, forceDark: false, dismissed: nil) + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .noAds, forceDark: true, action: { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .ads, forceDark: true, dismissed: nil) replaceImpl?(controller) }, dismissed: nil) replaceImpl = { [weak controller] c in controller?.replace(with: c) } -// controllerInteraction.navigationController()?.pushViewController(controller) + if let navigationController = self?.baseNavigationController() as? NavigationController { + navigationController.pushViewController(controller) + } }) }))) } if !message.text.isEmpty { - actions.append(.separator) actions.append(.action(ContextMenuActionItem(text: presentationData.strings.Conversation_ContextMenuCopy, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.actionSheet.primaryTextColor) diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index a66dc812bd..074f443cda 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -24,6 +24,9 @@ import AVKit import TextFormat import SliderContextItem import Pasteboard +import AdUI +import AdsInfoScreen +import AdsReportScreen public enum UniversalVideoGalleryItemContentInfo { case message(Message, Int?) @@ -1477,7 +1480,11 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { if let _ = message.paidContent, message.id.namespace == Namespaces.Message.Local { hasMoreButton = false } - + + if let _ = message.adAttribute { + hasMoreButton = true + } + if hasMoreButton { let moreMenuItem = UIBarButtonItem(customDisplayNode: self.moreBarButton)! moreMenuItem.accessibilityLabel = self.presentationData.strings.Common_More @@ -2527,40 +2534,38 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { if adAttribute.canReport { actions.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_ContextMenu_AboutAd, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor) - }, iconSource: nil, action: { _, f in + }, iconSource: nil, action: { [weak self] _, f in f(.dismissWithoutContent) - -// controllerInteraction.navigationController()?.pushViewController(AdsInfoScreen(context: context)) + if let navigationController = self?.baseNavigationController() as? NavigationController { + navigationController.pushViewController(AdsInfoScreen(context: context, forceDark: true)) + } }))) actions.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_ContextMenu_ReportAd, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.actionSheet.primaryTextColor) - }, iconSource: nil, action: { _, f in + }, iconSource: nil, action: { [weak self] _, f in f(.default) let _ = (context.engine.messages.reportAdMessage(peerId: message.id.peerId, opaqueId: adAttribute.opaqueId, option: nil) - |> deliverOnMainQueue).start(next: { result in + |> deliverOnMainQueue).start(next: { [weak self] result in if case let .options(title, options) = result { - let _ = title - let _ = options -// controllerInteraction.navigationController()?.pushViewController( -// AdsReportScreen( -// context: context, -// peerId: message.id.peerId, -// opaqueId: adAttribute.opaqueId, -// title: title, -// options: options, -// completed: { [weak interfaceInteraction] in -// guard let interfaceInteraction else { -// return -// } -// guard let chatController = interfaceInteraction.chatController() as? ChatControllerImpl else { -// return -// } -// chatController.removeAd(opaqueId: adAttribute.opaqueId) -// } -// ) -// ) + if let navigationController = self?.baseNavigationController() as? NavigationController { + navigationController.pushViewController( + AdsReportScreen( + context: context, + peerId: message.id.peerId, + opaqueId: adAttribute.opaqueId, + title: title, + options: options, + forceDark: true, + completed: { + if let navigationController = self?.baseNavigationController() as? NavigationController, let chatController = navigationController.viewControllers.last as? ChatController { + chatController.removeAd(opaqueId: adAttribute.opaqueId) + } + } + ) + ) + } } }) }))) @@ -2569,34 +2574,48 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { actions.append(.action(ContextMenuActionItem(text: presentationData.strings.Chat_ContextMenu_RemoveAd, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.primaryTextColor) - }, iconSource: nil, action: { c, _ in + }, iconSource: nil, action: { [weak self] c, _ in c?.dismiss(completion: { -// controllerInteraction.openNoAdsDemo() + var replaceImpl: ((ViewController) -> Void)? + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .noAds, forceDark: true, action: { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .ads, forceDark: true, dismissed: nil) + replaceImpl?(controller) + }, dismissed: nil) + replaceImpl = { [weak controller] c in + controller?.replace(with: c) + } + if let navigationController = self?.baseNavigationController() as? NavigationController { + navigationController.pushViewController(controller) + } }) }))) } else { actions.append(.action(ContextMenuActionItem(text: presentationData.strings.SponsoredMessageMenu_Info, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor) - }, iconSource: nil, action: { _, f in + }, iconSource: nil, action: { [weak self] _, f in f(.dismissWithoutContent) -// controllerInteraction.navigationController()?.pushViewController(AdInfoScreen(context: context)) + if let navigationController = self?.baseNavigationController() as? NavigationController { + navigationController.pushViewController(AdInfoScreen(context: context, forceDark: true)) + } }))) let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) if !context.isPremium && !premiumConfiguration.isPremiumDisabled { actions.append(.action(ContextMenuActionItem(text: presentationData.strings.SponsoredMessageMenu_Hide, textColor: .primary, textLayout: .twoLinesMax, textFont: .custom(font: Font.regular(presentationData.listsFontSize.baseDisplaySize - 1.0), height: nil, verticalOffset: nil), badge: nil, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Clear"), color: theme.actionSheet.primaryTextColor) - }, iconSource: nil, action: { c, _ in + }, iconSource: nil, action: { [weak self] c, _ in c?.dismiss(completion: { var replaceImpl: ((ViewController) -> Void)? - let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .noAds, forceDark: false, action: { - let controller = context.sharedContext.makePremiumIntroController(context: context, source: .ads, forceDark: false, dismissed: nil) + let controller = context.sharedContext.makePremiumDemoController(context: context, subject: .noAds, forceDark: true, action: { + let controller = context.sharedContext.makePremiumIntroController(context: context, source: .ads, forceDark: true, dismissed: nil) replaceImpl?(controller) }, dismissed: nil) replaceImpl = { [weak controller] c in controller?.replace(with: c) } -// controllerInteraction.navigationController()?.pushViewController(controller) + if let navigationController = self?.baseNavigationController() as? NavigationController { + navigationController.pushViewController(controller) + } }) }))) } diff --git a/submodules/TelegramUI/Components/Ads/AdsInfoScreen/Sources/AdsInfoScreen.swift b/submodules/TelegramUI/Components/Ads/AdsInfoScreen/Sources/AdsInfoScreen.swift index f55bdaa96c..25856d1013 100644 --- a/submodules/TelegramUI/Components/Ads/AdsInfoScreen/Sources/AdsInfoScreen.swift +++ b/submodules/TelegramUI/Components/Ads/AdsInfoScreen/Sources/AdsInfoScreen.swift @@ -488,7 +488,8 @@ public final class AdsInfoScreen: ViewControllerComponentContainer { private let context: AccountContext public init( - context: AccountContext + context: AccountContext, + forceDark: Bool = false ) { self.context = context @@ -503,7 +504,7 @@ public final class AdsInfoScreen: ViewControllerComponentContainer { ), navigationBarAppearance: .none, statusBarStyle: .ignore, - theme: .default + theme: forceDark ? .dark : .default ) self.navigationPresentation = .modal diff --git a/submodules/TelegramUI/Components/Ads/AdsReportScreen/Sources/AdsReportScreen.swift b/submodules/TelegramUI/Components/Ads/AdsReportScreen/Sources/AdsReportScreen.swift index 49dccb428e..86ee3fe125 100644 --- a/submodules/TelegramUI/Components/Ads/AdsReportScreen/Sources/AdsReportScreen.swift +++ b/submodules/TelegramUI/Components/Ads/AdsReportScreen/Sources/AdsReportScreen.swift @@ -573,6 +573,7 @@ public final class AdsReportScreen: ViewControllerComponentContainer { opaqueId: Data, title: String, options: [ReportAdMessageResult.Option], + forceDark: Bool = false, completed: @escaping () -> Void ) { self.context = context @@ -593,7 +594,7 @@ public final class AdsReportScreen: ViewControllerComponentContainer { ), navigationBarAppearance: .none, statusBarStyle: .ignore, - theme: .default + theme: forceDark ? .dark : .default ) self.navigationPresentation = .flatModal diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 2f6e0d90d3..a3eee1731e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -5957,7 +5957,16 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } override public func makeProgress() -> Promise? { - return self.unlockButtonNode?.makeProgress() + if let unlockButtonNode = self.unlockButtonNode { + return unlockButtonNode.makeProgress() + } else { + for contentNode in self.contentNodes { + if let webpageContentNode = contentNode as? ChatMessageWebpageBubbleContentNode { + return webpageContentNode.contentNode.makeProgress() + } + } + } + return nil } override public func targetReactionView(value: MessageReaction.Reaction) -> UIView? { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 65c7f1afdb..0918f63098 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -5113,6 +5113,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro strongSelf.openHashtag(hashtag, peerName: peerName) } }, openBotCommand: { _ in + }, openAd: { _ in }, addContact: { [weak self] phoneNumber in if let strongSelf = self { strongSelf.context.sharedContext.openAddContact(context: strongSelf.context, firstName: "", lastName: "", phoneNumber: phoneNumber, label: defaultContactLabel, present: { [weak self] controller, arguments in diff --git a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift index 7b6df7adf7..de6cf2aa6a 100644 --- a/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift +++ b/submodules/TelegramUI/Components/StorageUsageScreen/Sources/StorageUsageScreen.swift @@ -2805,6 +2805,7 @@ final class StorageUsageScreenComponent: Component { } let _ = self }, openBotCommand: { _ in + }, openAd: { _ in }, addContact: { _ in }, storeMediaPlaybackState: { [weak self] messageId, timestamp, playbackRate in guard let self else { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index c371ebb090..4fec59a09b 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -294,6 +294,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let galleryHiddenMesageAndMediaDisposable = MetaDisposable() let temporaryHiddenGalleryMediaDisposable = MetaDisposable() + + let galleryPresentationContext = PresentationContext() let chatBackgroundNode: WallpaperBackgroundNode public private(set) var controllerInteraction: ChatControllerInteraction? @@ -1274,7 +1276,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return context.sharedContext.openChatMessage(OpenChatMessageParams(context: context, updatedPresentationData: strongSelf.updatedPresentationData, chatLocation: openChatLocation, chatFilterTag: chatFilterTag, chatLocationContextHolder: strongSelf.chatLocationContextHolder, message: message, mediaIndex: params.mediaIndex, standalone: standalone, reverseMessageGalleryOrder: false, mode: mode, navigationController: strongSelf.effectiveNavigationController, dismissInput: { self?.chatDisplayNode.dismissInput() }, present: { c, a in - self?.present(c, in: .window(.root), with: a, blockInteraction: true) + if c is GalleryController { + c.presentationArguments = a + self?.galleryPresentationContext.present(c, on: PresentationSurfaceLevel(rawValue: 0), blockInteraction: true, completion: {}) + } else { + self?.present(c, in: .window(.root), with: a, blockInteraction: true) + } }, transitionNode: { messageId, media, adjustRect in var selectedNode: (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? if let strongSelf = self { @@ -1369,6 +1376,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let strongSelf = self { strongSelf.controllerInteraction?.sendBotCommand(nil, command) } + }, openAd: { [weak self] messageId in + if let strongSelf = self { + strongSelf.controllerInteraction?.activateAdAction(messageId, nil) + } }, addContact: { [weak self] phoneNumber in if let strongSelf = self { strongSelf.controllerInteraction?.addContact(phoneNumber) @@ -3857,6 +3868,16 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let self, let message = self.chatDisplayNode.historyNode.messageInCurrentHistoryView(messageId), let adAttribute = message.adAttribute else { return } + + var progress = progress + if progress == nil { + self.chatDisplayNode.historyNode.forEachVisibleMessageItemNode { itemView in + if itemView.item?.message.id == messageId { + progress = itemView.makeProgress() + } + } + } + self.chatDisplayNode.historyNode.adMessagesContext?.markAction(opaqueId: adAttribute.opaqueId) self.controllerInteraction?.openUrl(ChatControllerInteraction.OpenUrl(url: adAttribute.url, concealed: false, external: true, progress: progress)) }, openRequestedPeerSelection: { [weak self] messageId, peerType, buttonId, maxQuantity in @@ -7178,6 +7199,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G override public func loadDisplayNode() { self.loadDisplayNodeImpl() + self.galleryPresentationContext.view = self.view } override public func viewWillAppear(_ animated: Bool) { diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index a9bee54363..649d48a508 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -218,7 +218,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { private var didInitializeInputMediaNodeDataPromise: Bool = false private var inputMediaNodeDataDisposable: Disposable? private var inputMediaNodeStateContext = ChatEntityKeyboardInputNode.StateContext() - + let navigateButtons: ChatHistoryNavigationButtons private var ignoreUpdateHeight = false @@ -2058,6 +2058,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { childrenLayout.intrinsicInsets = UIEdgeInsets(top: listInsets.top, left: listInsets.left, bottom: listInsets.bottom, right: listInsets.right) } self.controller?.presentationContext.containerLayoutUpdated(childrenLayout, transition: transition) + self.controller?.galleryPresentationContext.containerLayoutUpdated(layout, transition: transition) listViewTransaction(ListViewUpdateSizeAndInsets(size: contentBounds.size, insets: listInsets, scrollIndicatorInsets: listScrollIndicatorInsets, duration: duration, curve: curve, ensureTopInsetForOverlayHighlightedItems: ensureTopInsetForOverlayHighlightedItems), additionalScrollDistance, scrollToTop, { [weak self] in if let strongSelf = self { diff --git a/submodules/TelegramUI/Sources/ChatControllerRemoveAd.swift b/submodules/TelegramUI/Sources/ChatControllerRemoveAd.swift index c8e4710f9d..5d7e3c0f88 100644 --- a/submodules/TelegramUI/Sources/ChatControllerRemoveAd.swift +++ b/submodules/TelegramUI/Sources/ChatControllerRemoveAd.swift @@ -9,7 +9,7 @@ import TelegramPresentationData import PresentationDataUtils import ChatMessageItemView -extension ChatControllerImpl { +public extension ChatControllerImpl { func removeAd(opaqueId: Data) { var foundItemNode: ChatMessageItemView? self.chatDisplayNode.historyNode.forEachItemNode { itemNode in diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index b31c40bc8c..e22e66a5b0 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -594,7 +594,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor) }, iconSource: nil, action: { _, f in f(.dismissWithoutContent) - controllerInteraction.navigationController()?.pushViewController(AdInfoScreen(context: context)) + controllerInteraction.navigationController()?.pushViewController(AdInfoScreen(context: context, forceDark: true)) }))) let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 7f4a28979a..6ecaf5fdfe 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -2156,7 +2156,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { } return controller default: - return PremiumDemoScreen(context: context, subject: mappedSubject, action: action) + return PremiumDemoScreen(context: context, subject: mappedSubject, forceDark: forceDark, action: action) } }