From 45018fdd274f963c435b7a4125e59341ea50bb73 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 24 Nov 2021 11:43:48 +0400 Subject: [PATCH] Various Improvements --- .../Sources/AccountContext.swift | 4 +- .../Sources/ChatListSearchContainerNode.swift | 51 +++++++++++++++++++ ...tListSearchMessageSelectionPanelNode.swift | 30 +++++++---- .../Display/Source/HighlightableButton.swift | 2 +- .../Display/Source/Nodes/ButtonNode.swift | 49 +++++++++++------- .../TelegramUI/Sources/ChatController.swift | 33 ++++++++++++ .../ChatInterfaceStateContextMenus.swift | 9 +++- .../ChatMessageSelectionInputPanelNode.swift | 24 ++++++--- .../ChatPanelInterfaceInteraction.swift | 4 ++ .../Sources/ChatRecentActionsController.swift | 4 +- .../Sources/PeerInfo/PeerInfoScreen.swift | 46 +++++++++++++++-- .../Sources/PeerSelectionControllerNode.swift | 4 +- 12 files changed, 216 insertions(+), 44 deletions(-) diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index b6eeed0ce3..3771943896 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -147,11 +147,13 @@ public struct ChatAvailableMessageActions { public var options: ChatAvailableMessageActionOptions public var banAuthor: Peer? public var disableDelete: Bool + public var isCopyProtected: Bool - public init(options: ChatAvailableMessageActionOptions, banAuthor: Peer?, disableDelete: Bool) { + public init(options: ChatAvailableMessageActionOptions, banAuthor: Peer?, disableDelete: Bool, isCopyProtected: Bool) { self.options = options self.banAuthor = banAuthor self.disableDelete = disableDelete + self.isCopyProtected = isCopyProtected } } diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index d2a89ef126..70e638cf20 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -115,6 +115,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo private var selectedFilterKeyPromise = Promise() private var transitionFraction: CGFloat = 0.0 + private weak var copyProtectionTooltipController: TooltipController? + private var didSetReady: Bool = false private let _ready = Promise() public override func ready() -> Signal { @@ -417,6 +419,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo self.presentationDataDisposable?.dispose() self.suggestedFiltersDisposable.dispose() self.shareStatusDisposable?.dispose() + + self.copyProtectionTooltipController?.dismiss() } private func updateState(_ f: (ChatListSearchContainerNodeSearchState) -> ChatListSearchContainerNodeSearchState) { @@ -588,6 +592,53 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo return } strongSelf.forwardMessages(messageIds: nil) + }, displayCopyProtectionTip: { [weak self] node, save in + guard let strongSelf = self, let messageIds = strongSelf.stateValue.selectedMessageIds, !messageIds.isEmpty else { + return + } + let _ = (strongSelf.context.account.postbox.transaction { transaction -> [EngineMessage] in + var messages: [EngineMessage] = [] + for id in messageIds { + if let message = transaction.getMessage(id) { + messages.append(EngineMessage(message)) + } + } + return messages + } + |> deliverOnMainQueue).start(next: { messages in + if let strongSelf = self, !messages.isEmpty { + var isChannel = false + for message in messages { + if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = channel.info { + isChannel = true + break + } + } + + let text: String + if save { + text = isChannel ? strongSelf.presentationData.strings.Conversation_CopyProtectionSavingDisabledChannel : strongSelf.presentationData.strings.Conversation_CopyProtectionSavingDisabledGroup + } else { + text = isChannel ? strongSelf.presentationData.strings.Conversation_CopyProtectionForwardingDisabledChannel : strongSelf.presentationData.strings.Conversation_CopyProtectionForwardingDisabledGroup + } + + strongSelf.copyProtectionTooltipController?.dismiss() + let tooltipController = TooltipController(content: .text(text), baseFontSize: strongSelf.presentationData.listsFontSize.baseDisplaySize, dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true) + strongSelf.copyProtectionTooltipController = tooltipController + tooltipController.dismissed = { [weak tooltipController] _ in + if let strongSelf = self, let tooltipController = tooltipController, strongSelf.copyProtectionTooltipController === tooltipController { + strongSelf.copyProtectionTooltipController = nil + } + } + strongSelf.present?(tooltipController, TooltipControllerPresentationArguments(sourceNodeAndRect: { + if let strongSelf = self { + let rect = node.view.convert(node.view.bounds, to: strongSelf.view).offsetBy(dx: 0.0, dy: 3.0) + return (strongSelf, rect) + } + return nil + })) + } + }) }) selectionPanelNode.chatAvailableMessageActions = { [weak self] messageIds -> Signal in guard let strongSelf = self else { diff --git a/submodules/ChatListUI/Sources/ChatListSearchMessageSelectionPanelNode.swift b/submodules/ChatListUI/Sources/ChatListSearchMessageSelectionPanelNode.swift index 372a7fa020..438625d3b2 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchMessageSelectionPanelNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchMessageSelectionPanelNode.swift @@ -17,6 +17,7 @@ final class ChatListSearchMessageSelectionPanelNode: ASDisplayNode { private let deleteMessages: () -> Void private let shareMessages: () -> Void private let forwardMessages: () -> Void + private let displayCopyProtectionTip: (ASDisplayNode, Bool) -> Void private let separatorNode: ASDisplayNode private let backgroundNode: NavigationBackgroundNode @@ -60,11 +61,12 @@ final class ChatListSearchMessageSelectionPanelNode: ASDisplayNode { } } - init(context: AccountContext, deleteMessages: @escaping () -> Void, shareMessages: @escaping () -> Void, forwardMessages: @escaping () -> Void) { + init(context: AccountContext, deleteMessages: @escaping () -> Void, shareMessages: @escaping () -> Void, forwardMessages: @escaping () -> Void, displayCopyProtectionTip: @escaping (ASDisplayNode, Bool) -> Void) { self.context = context self.deleteMessages = deleteMessages self.shareMessages = shareMessages self.forwardMessages = forwardMessages + self.displayCopyProtectionTip = displayCopyProtectionTip let presentationData = context.sharedContext.currentPresentationData.with { $0 } self.theme = presentationData.theme @@ -102,8 +104,8 @@ final class ChatListSearchMessageSelectionPanelNode: ASDisplayNode { self.addSubnode(self.separatorNode) self.deleteButton.isEnabled = false - self.forwardButton.isEnabled = false - self.shareButton.isEnabled = false + self.forwardButton.isImplicitlyDisabled = true + self.shareButton.isImplicitlyDisabled = true self.deleteButton.addTarget(self, action: #selector(self.deleteButtonPressed), forControlEvents: .touchUpInside) self.forwardButton.addTarget(self, action: #selector(self.forwardButtonPressed), forControlEvents: .touchUpInside) @@ -144,15 +146,15 @@ final class ChatListSearchMessageSelectionPanelNode: ASDisplayNode { if let actions = self.actions { self.deleteButton.isEnabled = false - self.forwardButton.isEnabled = actions.options.contains(.forward) - self.shareButton.isEnabled = false + self.forwardButton.isImplicitlyDisabled = !actions.options.contains(.forward) + self.deleteButton.isEnabled = !actions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty - self.shareButton.isEnabled = !actions.options.intersection([.forward]).isEmpty + self.shareButton.isImplicitlyDisabled = actions.options.intersection([.forward]).isEmpty } else { self.deleteButton.isEnabled = false - self.forwardButton.isEnabled = false - self.shareButton.isEnabled = false + self.forwardButton.isImplicitlyDisabled = true + self.shareButton.isImplicitlyDisabled = true } self.deleteButton.frame = CGRect(origin: CGPoint(x: leftInset, y: 0.0), size: CGSize(width: 57.0, height: panelHeight)) @@ -173,10 +175,18 @@ final class ChatListSearchMessageSelectionPanelNode: ASDisplayNode { } @objc func forwardButtonPressed() { - self.forwardMessages() + if let actions = self.actions, actions.isCopyProtected { + self.displayCopyProtectionTip(self.forwardButton, false) + } else { + self.forwardMessages() + } } @objc func shareButtonPressed() { - self.shareMessages() + if let actions = self.actions, actions.isCopyProtected { + self.displayCopyProtectionTip(self.shareButton, true) + } else { + self.shareMessages() + } } } diff --git a/submodules/Display/Source/HighlightableButton.swift b/submodules/Display/Source/HighlightableButton.swift index a80b134702..35caa00566 100644 --- a/submodules/Display/Source/HighlightableButton.swift +++ b/submodules/Display/Source/HighlightableButton.swift @@ -89,7 +89,7 @@ open class HighlightableButtonNode: HighlightTrackingButtonNode { super.init(pointerStyle: pointerStyle) self.highligthedChanged = { [weak self] highlighted in - if let strongSelf = self { + if let strongSelf = self, !strongSelf.isImplicitlyDisabled { if highlighted { strongSelf.layer.removeAnimation(forKey: "opacity") strongSelf.alpha = 0.4 diff --git a/submodules/Display/Source/Nodes/ButtonNode.swift b/submodules/Display/Source/Nodes/ButtonNode.swift index 8741e95cad..e3045884de 100644 --- a/submodules/Display/Source/Nodes/ButtonNode.swift +++ b/submodules/Display/Source/Nodes/ButtonNode.swift @@ -298,8 +298,9 @@ open class ASButtonNode: ASControlNode { override open var isHighlighted: Bool { didSet { - if self.isHighlighted != oldValue { - if self.isHighlighted { + if self.isHighlighted != oldValue && !self.isImplicitlyDisabled { + let isHighlighted = self.isHighlighted + if isHighlighted { if self.highlightedTitleNode.attributedText != nil { self.highlightedTitleNode.isHidden = false self.titleNode.isHidden = true @@ -349,28 +350,42 @@ open class ASButtonNode: ASControlNode { } } + open var isImplicitlyDisabled: Bool = false { + didSet { + if self.isImplicitlyDisabled != oldValue { + self.updateIsEnabled() + } + } + } + override open var isEnabled: Bool { didSet { if self.isEnabled != oldValue { - if self.isEnabled || self.disabledTitleNode.attributedText == nil { - self.titleNode.isHidden = false - self.disabledTitleNode.isHidden = true - } else { - self.titleNode.isHidden = true - self.disabledTitleNode.isHidden = false - } - - if self.isEnabled || self.disabledImageNode.image == nil { - self.imageNode.isHidden = false - self.disabledImageNode.isHidden = true - } else { - self.imageNode.isHidden = true - self.disabledImageNode.isHidden = false - } + self.updateIsEnabled() } } } + private func updateIsEnabled() { + let isEnabled = self.isEnabled && !self.isImplicitlyDisabled + + if isEnabled || self.disabledTitleNode.attributedText == nil { + self.titleNode.isHidden = false + self.disabledTitleNode.isHidden = true + } else { + self.titleNode.isHidden = true + self.disabledTitleNode.isHidden = false + } + + if isEnabled || self.disabledImageNode.image == nil { + self.imageNode.isHidden = false + self.disabledImageNode.isHidden = true + } else { + self.imageNode.isHidden = true + self.disabledImageNode.isHidden = false + } + } + override open func layout() { let size = self.bounds.size diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index ad63769d24..e6704eefdc 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -411,6 +411,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private weak var mediaRestrictedTooltipController: TooltipController? private var mediaRestrictedTooltipControllerMode = true private weak var checksTooltipController: TooltipController? + private weak var copyProtectionTooltipController: TooltipController? private var currentMessageTooltipScreens: [(TooltipScreen, ListViewItemNode)] = [] @@ -7685,6 +7686,37 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) }, presentChatRequestAdminInfo: { [weak self] in self?.presentChatRequestAdminInfo() + }, displayCopyProtectionTip: { [weak self] node, save in + if let strongSelf = self, let peer = strongSelf.presentationInterfaceState.renderedPeer?.peer { + let isChannel: Bool + if let channel = peer as? TelegramChannel, case .broadcast = channel.info { + isChannel = true + } else { + isChannel = false + } + let text: String + if save { + text = isChannel ? strongSelf.presentationInterfaceState.strings.Conversation_CopyProtectionSavingDisabledChannel : strongSelf.presentationInterfaceState.strings.Conversation_CopyProtectionSavingDisabledGroup + } else { + text = isChannel ? strongSelf.presentationInterfaceState.strings.Conversation_CopyProtectionForwardingDisabledChannel : strongSelf.presentationInterfaceState.strings.Conversation_CopyProtectionForwardingDisabledGroup + } + + strongSelf.copyProtectionTooltipController?.dismiss() + let tooltipController = TooltipController(content: .text(text), baseFontSize: strongSelf.presentationData.listsFontSize.baseDisplaySize, dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true) + strongSelf.copyProtectionTooltipController = tooltipController + tooltipController.dismissed = { [weak tooltipController] _ in + if let strongSelf = self, let tooltipController = tooltipController, strongSelf.copyProtectionTooltipController === tooltipController { + strongSelf.copyProtectionTooltipController = nil + } + } + strongSelf.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceNodeAndRect: { + if let strongSelf = self { + let rect = node.view.convert(node.view.bounds, to: strongSelf.chatDisplayNode.view).offsetBy(dx: 0.0, dy: 3.0) + return (strongSelf.chatDisplayNode, rect) + } + return nil + })) + } }, statuses: ChatPanelInterfaceInteractionStatuses(editingMessage: self.editingMessage.get(), startingBot: self.startingBot.get(), unblockingPeer: self.unblockingPeer.get(), searching: self.searching.get(), loadingMessage: self.loadingMessage.get(), inlineSearch: self.performingInlineSearch.get())) do { @@ -13548,6 +13580,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.mediaRecordingModeTooltipController?.dismiss() self.mediaRestrictedTooltipController?.dismiss() self.checksTooltipController?.dismiss() + self.copyProtectionTooltipController?.dismiss() self.window?.forEachController({ controller in if let controller = controller as? UndoOverlayController { diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 2c32f6c887..a05d3d95fa 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -1285,6 +1285,7 @@ func chatAvailableMessageActionsImpl(postbox: Postbox, accountPeerId: PeerId, me var hadPersonalIncoming = false var hadBanPeerId = false var disableDelete = false + var isCopyProtected = false func getPeer(_ peerId: PeerId) -> Peer? { if let peer = transaction.getPeer(peerId) { @@ -1306,12 +1307,16 @@ func chatAvailableMessageActionsImpl(postbox: Postbox, accountPeerId: PeerId, me } } + for id in messageIds { let isScheduled = id.namespace == Namespaces.Message.ScheduledCloud if optionsMap[id] == nil { optionsMap[id] = [] } if let message = getMessage(id) { + if message.isCopyProtected() { + isCopyProtected = true + } for media in message.media { if let file = media as? TelegramMediaFile, file.isSticker { for case let .Sticker(_, packReference, _) in file.attributes { @@ -1501,9 +1506,9 @@ func chatAvailableMessageActionsImpl(postbox: Postbox, accountPeerId: PeerId, me if hadPersonalIncoming && optionsMap.values.contains(where: { $0.contains(.deleteGlobally) }) && !reducedOptions.contains(.deleteGlobally) { reducedOptions.insert(.unsendPersonal) } - return ChatAvailableMessageActions(options: reducedOptions, banAuthor: banPeer, disableDelete: disableDelete) + return ChatAvailableMessageActions(options: reducedOptions, banAuthor: banPeer, disableDelete: disableDelete, isCopyProtected: isCopyProtected) } else { - return ChatAvailableMessageActions(options: [], banAuthor: nil, disableDelete: false) + return ChatAvailableMessageActions(options: [], banAuthor: nil, disableDelete: false, isCopyProtected: isCopyProtected) } } } diff --git a/submodules/TelegramUI/Sources/ChatMessageSelectionInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatMessageSelectionInputPanelNode.swift index 17ad4b6c85..e01f11d8c1 100644 --- a/submodules/TelegramUI/Sources/ChatMessageSelectionInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageSelectionInputPanelNode.swift @@ -93,8 +93,8 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { self.addSubnode(self.shareButton) self.addSubnode(self.separatorNode) - self.forwardButton.isEnabled = false - self.shareButton.isEnabled = false + self.forwardButton.isImplicitlyDisabled = true + self.shareButton.isImplicitlyDisabled = true self.deleteButton.addTarget(self, action: #selector(self.deleteButtonPressed), forControlEvents: .touchUpInside) self.reportButton.addTarget(self, action: #selector(self.reportButtonPressed), forControlEvents: .touchUpInside) @@ -132,11 +132,19 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { } @objc func forwardButtonPressed() { - self.interfaceInteraction?.forwardSelectedMessages() + if let actions = self.actions, actions.isCopyProtected { + self.interfaceInteraction?.displayCopyProtectionTip(self.forwardButton, false) + } else { + self.interfaceInteraction?.forwardSelectedMessages() + } } @objc func shareButtonPressed() { - self.interfaceInteraction?.shareSelectedMessages() + if let actions = self.actions, actions.isCopyProtected { + self.interfaceInteraction?.displayCopyProtectionTip(self.shareButton, true) + } else { + self.interfaceInteraction?.shareSelectedMessages() + } } override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, additionalSideInsets: UIEdgeInsets, maxHeight: CGFloat, isSecondary: Bool, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { @@ -150,14 +158,14 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { if let actions = self.actions { self.deleteButton.isEnabled = false self.reportButton.isEnabled = false - self.forwardButton.isEnabled = actions.options.contains(.forward) + self.forwardButton.isImplicitlyDisabled = !actions.options.contains(.forward) if self.peerMedia { self.deleteButton.isEnabled = !actions.options.intersection([.deleteLocally, .deleteGlobally]).isEmpty } else { self.deleteButton.isEnabled = !actions.disableDelete } - self.shareButton.isEnabled = !actions.options.intersection([.forward]).isEmpty + self.shareButton.isImplicitlyDisabled = actions.options.intersection([.forward]).isEmpty self.reportButton.isEnabled = !actions.options.intersection([.report]).isEmpty if self.peerMedia { @@ -171,8 +179,8 @@ final class ChatMessageSelectionInputPanelNode: ChatInputPanelNode { self.deleteButton.isHidden = self.peerMedia self.reportButton.isEnabled = false self.reportButton.isHidden = true - self.forwardButton.isEnabled = false - self.shareButton.isEnabled = false + self.forwardButton.isImplicitlyDisabled = true + self.shareButton.isImplicitlyDisabled = true } if self.reportButton.isHidden || (self.peerMedia && self.deleteButton.isHidden && self.reportButton.isHidden) { diff --git a/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift b/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift index 344ea345df..74d282cc2a 100644 --- a/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift +++ b/submodules/TelegramUI/Sources/ChatPanelInterfaceInteraction.swift @@ -136,6 +136,7 @@ final class ChatPanelInterfaceInteraction { let openInviteRequests: () -> Void let openSendAsPeer: (ASDisplayNode, ContextGesture?) -> Void let presentChatRequestAdminInfo: () -> Void + let displayCopyProtectionTip: (ASDisplayNode, Bool) -> Void let statuses: ChatPanelInterfaceInteractionStatuses? init( @@ -226,6 +227,7 @@ final class ChatPanelInterfaceInteraction { openInviteRequests: @escaping () -> Void, openSendAsPeer: @escaping (ASDisplayNode, ContextGesture?) -> Void, presentChatRequestAdminInfo: @escaping () -> Void, + displayCopyProtectionTip: @escaping (ASDisplayNode, Bool) -> Void, statuses: ChatPanelInterfaceInteractionStatuses? ) { self.setupReplyMessage = setupReplyMessage @@ -315,6 +317,7 @@ final class ChatPanelInterfaceInteraction { self.openInviteRequests = openInviteRequests self.openSendAsPeer = openSendAsPeer self.presentChatRequestAdminInfo = presentChatRequestAdminInfo + self.displayCopyProtectionTip = displayCopyProtectionTip self.statuses = statuses } @@ -411,6 +414,7 @@ final class ChatPanelInterfaceInteraction { }, openInviteRequests: { }, openSendAsPeer: { _, _ in }, presentChatRequestAdminInfo: { + }, displayCopyProtectionTip: { _, _ in }, statuses: nil) } } diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsController.swift b/submodules/TelegramUI/Sources/ChatRecentActionsController.swift index 93ad77cccd..b05e92e7ac 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsController.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsController.swift @@ -148,7 +148,9 @@ final class ChatRecentActionsController: TelegramBaseController { }, updateShowSendAsPeers: { _ in }, openInviteRequests: { }, openSendAsPeer: { _, _ in - }, presentChatRequestAdminInfo: {}, statuses: nil) + }, presentChatRequestAdminInfo: { + }, displayCopyProtectionTip: { _, _ in + }, statuses: nil) self.navigationItem.titleView = self.titleView diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index b3b3b8b101..4ff7bf17c0 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -222,18 +222,20 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode { private let shareMessages: () -> Void private let forwardMessages: () -> Void private let reportMessages: () -> Void + private let displayCopyProtectionTip: (ASDisplayNode, Bool) -> Void let selectionPanel: ChatMessageSelectionInputPanelNode let separatorNode: ASDisplayNode let backgroundNode: NavigationBackgroundNode - init(context: AccountContext, presentationData: PresentationData, peerId: PeerId, deleteMessages: @escaping () -> Void, shareMessages: @escaping () -> Void, forwardMessages: @escaping () -> Void, reportMessages: @escaping () -> Void) { + init(context: AccountContext, presentationData: PresentationData, peerId: PeerId, deleteMessages: @escaping () -> Void, shareMessages: @escaping () -> Void, forwardMessages: @escaping () -> Void, reportMessages: @escaping () -> Void, displayCopyProtectionTip: @escaping (ASDisplayNode, Bool) -> Void) { self.context = context self.peerId = peerId self.deleteMessages = deleteMessages self.shareMessages = shareMessages self.forwardMessages = forwardMessages self.reportMessages = reportMessages + self.displayCopyProtectionTip = displayCopyProtectionTip let presentationData = presentationData @@ -337,7 +339,10 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode { }, updateShowSendAsPeers: { _ in }, openInviteRequests: { }, openSendAsPeer: { _, _ in - }, presentChatRequestAdminInfo: {}, statuses: nil) + }, presentChatRequestAdminInfo: { + }, displayCopyProtectionTip: { node, save in + displayCopyProtectionTip(node, save) + }, statuses: nil) self.selectionPanel.interfaceInteraction = interfaceInteraction @@ -1507,6 +1512,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate private let tipsPeerDisposable = MetaDisposable() private let cachedFaq = Promise(nil) + private weak var copyProtectionTooltipController: TooltipController? + private let _ready = Promise() var ready: Promise { return self._ready @@ -2877,6 +2884,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate self.shareStatusDisposable?.dispose() self.customStatusDisposable?.dispose() self.refreshMessageTagStatsDisposable?.dispose() + + self.copyProtectionTooltipController?.dismiss() } override func didLoad() { @@ -6477,6 +6486,37 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate }, push: { c in self?.controller?.push(c) }, completion: { _, _ in }), in: .window(.root)) + }, displayCopyProtectionTip: { [weak self] node, save in + if let strongSelf = self, let peer = strongSelf.data?.peer { + let isChannel: Bool + if let channel = peer as? TelegramChannel, case .broadcast = channel.info { + isChannel = true + } else { + isChannel = false + } + let text: String + if save { + text = isChannel ? strongSelf.presentationData.strings.Conversation_CopyProtectionSavingDisabledChannel : strongSelf.presentationData.strings.Conversation_CopyProtectionSavingDisabledGroup + } else { + text = isChannel ? strongSelf.presentationData.strings.Conversation_CopyProtectionForwardingDisabledChannel : strongSelf.presentationData.strings.Conversation_CopyProtectionForwardingDisabledGroup + } + + strongSelf.copyProtectionTooltipController?.dismiss() + let tooltipController = TooltipController(content: .text(text), baseFontSize: strongSelf.presentationData.listsFontSize.baseDisplaySize, dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true) + strongSelf.copyProtectionTooltipController = tooltipController + tooltipController.dismissed = { [weak tooltipController] _ in + if let strongSelf = self, let tooltipController = tooltipController, strongSelf.copyProtectionTooltipController === tooltipController { + strongSelf.copyProtectionTooltipController = nil + } + } + strongSelf.controller?.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceNodeAndRect: { + if let strongSelf = self { + let rect = node.view.convert(node.view.bounds, to: strongSelf.view).offsetBy(dx: 0.0, dy: 3.0) + return (strongSelf, rect) + } + return nil + })) + } }) self.paneContainerNode.selectionPanelNode = selectionPanelNode self.paneContainerNode.addSubnode(selectionPanelNode) @@ -6856,7 +6896,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen { private let activeSessionsContextAndCount = Promise<(ActiveSessionsContext, Int, WebSessionsContext)?>(nil) private var tabBarItemDisposable: Disposable? - + fileprivate var controllerNode: PeerInfoScreenNode { return self.displayNode as! PeerInfoScreenNode } diff --git a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift index dd05c779ae..718dd9b4f0 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift @@ -304,7 +304,9 @@ final class PeerSelectionControllerNode: ASDisplayNode { }, updateShowSendAsPeers: { _ in }, openInviteRequests: { }, openSendAsPeer: { _, _ in - }, presentChatRequestAdminInfo: {}, statuses: nil) + }, presentChatRequestAdminInfo: { + }, displayCopyProtectionTip: { _, _ in + }, statuses: nil) self.readyValue.set(self.chatListNode.ready) }