diff --git a/Telegram-iOS/en.lproj/Localizable.strings b/Telegram-iOS/en.lproj/Localizable.strings index 4472d3069f..75172eb086 100644 --- a/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram-iOS/en.lproj/Localizable.strings @@ -4638,12 +4638,12 @@ Any member of this group will be able to see messages in the channel."; "Conversation.SendMessage.SetReminder" = "Set a Reminder"; -"Conversation.SelectedMessages_1" = "%@ Message Selected"; -"Conversation.SelectedMessages_2" = "%@ Messages Selected"; -"Conversation.SelectedMessages_3_10" = "%@ Messages Selected"; -"Conversation.SelectedMessages_any" = "%@ Messages Selected"; -"Conversation.SelectedMessages_many" = "%@ Messages Selected"; -"Conversation.SelectedMessages_0" = "%@ Messages Selected"; +"Conversation.SelectedMessages_1" = "%@ Selected"; +"Conversation.SelectedMessages_2" = "%@ Selected"; +"Conversation.SelectedMessages_3_10" = "%@ Selected"; +"Conversation.SelectedMessages_any" = "%@ Selected"; +"Conversation.SelectedMessages_many" = "%@ Selected"; +"Conversation.SelectedMessages_0" = "%@ Selected"; "AccentColor.Title" = "Accent Color"; diff --git a/submodules/TelegramUI/TelegramUI/ChatController.swift b/submodules/TelegramUI/TelegramUI/ChatController.swift index 7fa50734eb..530125999a 100644 --- a/submodules/TelegramUI/TelegramUI/ChatController.swift +++ b/submodules/TelegramUI/TelegramUI/ChatController.swift @@ -265,6 +265,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G private var raiseToListen: RaiseToListenManager? private var voicePlaylistDidEndTimestamp: Double = 0.0 + private weak var searchResultsTooltipController: TooltipController? private weak var messageTooltipController: TooltipController? private weak var videoUnmuteTooltipController: TooltipController? private weak var silentPostTooltipController: TooltipController? @@ -1522,9 +1523,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let strongSelf = self { strongSelf.context.sharedContext.applicationBindings.openAppStorePage() } - }, displayMessageTooltip: { [weak self] messageId, text, sourceNode, sourceFrame in + }, displayMessageTooltip: { [weak self] messageId, text, node, nodeRect in if let strongSelf = self { - if let sourceNode = sourceNode { + if let node = node { strongSelf.messageTooltipController?.dismiss() let tooltipController = TooltipController(content: .text(text), dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true) strongSelf.messageTooltipController = tooltipController @@ -1535,9 +1536,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } strongSelf.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceNodeAndRect: { if let strongSelf = self { - var rect = sourceNode.view.convert(sourceNode.view.bounds, to: strongSelf.chatDisplayNode.view) - if let sourceFrame = sourceFrame { - rect = CGRect(origin: rect.origin.offsetBy(dx: sourceFrame.minX, dy: sourceFrame.minY - sourceNode.bounds.minY), size: sourceFrame.size) + var rect = node.view.convert(node.view.bounds, to: strongSelf.chatDisplayNode.view) + if let nodeRect = nodeRect { + rect = CGRect(origin: rect.origin.offsetBy(dx: nodeRect.minX, dy: nodeRect.minY - node.bounds.minY), size: nodeRect.size) } return (strongSelf.chatDisplayNode, rect) } @@ -4088,6 +4089,25 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let strongSelf = self { strongSelf.openScheduledMessages() } + }, displaySearchResultsTooltip: { [weak self] node, nodeRect in + if let strongSelf = self { + strongSelf.searchResultsTooltipController?.dismiss() + let tooltipController = TooltipController(content: .text(strongSelf.presentationData.strings.ChatSearch_ResultsTooltip), dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true) + strongSelf.searchResultsTooltipController = tooltipController + tooltipController.dismissed = { [weak tooltipController] _ in + if let strongSelf = self, let tooltipController = tooltipController, strongSelf.searchResultsTooltipController === tooltipController { + strongSelf.searchResultsTooltipController = nil + } + } + strongSelf.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceNodeAndRect: { + if let strongSelf = self { + var rect = node.view.convert(node.view.bounds, to: strongSelf.chatDisplayNode.view) + rect = CGRect(origin: rect.origin.offsetBy(dx: nodeRect.minX, dy: nodeRect.minY - node.bounds.minY), size: nodeRect.size) + 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())) switch self.chatLocation { @@ -7817,6 +7837,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } private func dismissAllTooltips() { + self.searchResultsTooltipController?.dismiss() self.messageTooltipController?.dismiss() self.videoUnmuteTooltipController?.dismiss() self.silentPostTooltipController?.dismiss() diff --git a/submodules/TelegramUI/TelegramUI/ChatPanelInterfaceInteraction.swift b/submodules/TelegramUI/TelegramUI/ChatPanelInterfaceInteraction.swift index 47663594cf..6c185b84de 100644 --- a/submodules/TelegramUI/TelegramUI/ChatPanelInterfaceInteraction.swift +++ b/submodules/TelegramUI/TelegramUI/ChatPanelInterfaceInteraction.swift @@ -112,9 +112,10 @@ final class ChatPanelInterfaceInteraction { let displaySlowmodeTooltip: (ASDisplayNode, CGRect) -> Void let displaySendMessageOptions: () -> Void let openScheduledMessages: () -> Void + let displaySearchResultsTooltip: (ASDisplayNode, CGRect) -> Void let statuses: ChatPanelInterfaceInteractionStatuses? - init(setupReplyMessage: @escaping (MessageId, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, setupEditMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, beginMessageSelection: @escaping ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void, deleteSelectedMessages: @escaping () -> Void, reportSelectedMessages: @escaping () -> Void, reportMessages: @escaping ([Message], ContextController?) -> Void, deleteMessages: @escaping ([Message], ContextController?, @escaping (ContextMenuActionResult) -> Void) -> Void, forwardSelectedMessages: @escaping () -> Void, forwardCurrentForwardMessages: @escaping () -> Void, forwardMessages: @escaping ([Message]) -> Void, shareSelectedMessages: @escaping () -> Void, updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void, updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, openStickers: @escaping () -> Void, editMessage: @escaping () -> Void, beginMessageSearch: @escaping (ChatSearchDomain, String) -> Void, dismissMessageSearch: @escaping () -> Void, updateMessageSearch: @escaping (String) -> Void, openSearchResults: @escaping () -> Void, navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void, openCalendarSearch: @escaping () -> Void, toggleMembersSearch: @escaping (Bool) -> Void, navigateToMessage: @escaping (MessageId) -> Void, navigateToChat: @escaping (PeerId) -> Void, openPeerInfo: @escaping () -> Void, togglePeerNotifications: @escaping () -> Void, sendContextResult: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, sendBotCommand: @escaping (Peer, String) -> Void, sendBotStart: @escaping (String?) -> Void, botSwitchChatWithPayload: @escaping (PeerId, String) -> Void, beginMediaRecording: @escaping (Bool) -> Void, finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void, stopMediaRecording: @escaping () -> Void, lockMediaRecording: @escaping () -> Void, deleteRecordedMedia: @escaping () -> Void, sendRecordedMedia: @escaping () -> Void, displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void, displayVideoUnmuteTip: @escaping (CGPoint?) -> Void, switchMediaRecordingMode: @escaping () -> Void, setupMessageAutoremoveTimeout: @escaping () -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId) -> Void, unpinMessage: @escaping () -> Void, shareAccountContact: @escaping () -> Void, reportPeer: @escaping () -> Void, presentPeerContact: @escaping () -> Void, dismissReportPeer: @escaping () -> Void, deleteChat: @escaping () -> Void, beginCall: @escaping () -> Void, toggleMessageStickerStarred: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigateFeed: @escaping () -> Void, openGrouping: @escaping () -> Void, toggleSilentPost: @escaping () -> Void, requestUnvoteInMessage: @escaping (MessageId) -> Void, requestStopPollInMessage: @escaping (MessageId) -> Void, updateInputLanguage: @escaping ((String?) -> String?) -> Void, unarchiveChat: @escaping () -> Void, openLinkEditing: @escaping () -> Void, reportPeerIrrelevantGeoLocation: @escaping () -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, displaySendMessageOptions: @escaping () -> Void, openScheduledMessages: @escaping () -> Void, statuses: ChatPanelInterfaceInteractionStatuses?) { + init(setupReplyMessage: @escaping (MessageId, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, setupEditMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, beginMessageSelection: @escaping ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void, deleteSelectedMessages: @escaping () -> Void, reportSelectedMessages: @escaping () -> Void, reportMessages: @escaping ([Message], ContextController?) -> Void, deleteMessages: @escaping ([Message], ContextController?, @escaping (ContextMenuActionResult) -> Void) -> Void, forwardSelectedMessages: @escaping () -> Void, forwardCurrentForwardMessages: @escaping () -> Void, forwardMessages: @escaping ([Message]) -> Void, shareSelectedMessages: @escaping () -> Void, updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void, updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, openStickers: @escaping () -> Void, editMessage: @escaping () -> Void, beginMessageSearch: @escaping (ChatSearchDomain, String) -> Void, dismissMessageSearch: @escaping () -> Void, updateMessageSearch: @escaping (String) -> Void, openSearchResults: @escaping () -> Void, navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void, openCalendarSearch: @escaping () -> Void, toggleMembersSearch: @escaping (Bool) -> Void, navigateToMessage: @escaping (MessageId) -> Void, navigateToChat: @escaping (PeerId) -> Void, openPeerInfo: @escaping () -> Void, togglePeerNotifications: @escaping () -> Void, sendContextResult: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, sendBotCommand: @escaping (Peer, String) -> Void, sendBotStart: @escaping (String?) -> Void, botSwitchChatWithPayload: @escaping (PeerId, String) -> Void, beginMediaRecording: @escaping (Bool) -> Void, finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void, stopMediaRecording: @escaping () -> Void, lockMediaRecording: @escaping () -> Void, deleteRecordedMedia: @escaping () -> Void, sendRecordedMedia: @escaping () -> Void, displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void, displayVideoUnmuteTip: @escaping (CGPoint?) -> Void, switchMediaRecordingMode: @escaping () -> Void, setupMessageAutoremoveTimeout: @escaping () -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId) -> Void, unpinMessage: @escaping () -> Void, shareAccountContact: @escaping () -> Void, reportPeer: @escaping () -> Void, presentPeerContact: @escaping () -> Void, dismissReportPeer: @escaping () -> Void, deleteChat: @escaping () -> Void, beginCall: @escaping () -> Void, toggleMessageStickerStarred: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigateFeed: @escaping () -> Void, openGrouping: @escaping () -> Void, toggleSilentPost: @escaping () -> Void, requestUnvoteInMessage: @escaping (MessageId) -> Void, requestStopPollInMessage: @escaping (MessageId) -> Void, updateInputLanguage: @escaping ((String?) -> String?) -> Void, unarchiveChat: @escaping () -> Void, openLinkEditing: @escaping () -> Void, reportPeerIrrelevantGeoLocation: @escaping () -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, displaySendMessageOptions: @escaping () -> Void, openScheduledMessages: @escaping () -> Void, displaySearchResultsTooltip: @escaping (ASDisplayNode, CGRect) -> Void, statuses: ChatPanelInterfaceInteractionStatuses?) { self.setupReplyMessage = setupReplyMessage self.setupEditMessage = setupEditMessage self.beginMessageSelection = beginMessageSelection @@ -181,6 +182,7 @@ final class ChatPanelInterfaceInteraction { self.displaySlowmodeTooltip = displaySlowmodeTooltip self.displaySendMessageOptions = displaySendMessageOptions self.openScheduledMessages = openScheduledMessages + self.displaySearchResultsTooltip = displaySearchResultsTooltip self.statuses = statuses } } diff --git a/submodules/TelegramUI/TelegramUI/ChatRecentActionsController.swift b/submodules/TelegramUI/TelegramUI/ChatRecentActionsController.swift index a10a9b21dc..55f4195536 100644 --- a/submodules/TelegramUI/TelegramUI/ChatRecentActionsController.swift +++ b/submodules/TelegramUI/TelegramUI/ChatRecentActionsController.swift @@ -113,6 +113,7 @@ final class ChatRecentActionsController: TelegramBaseController { }, displaySlowmodeTooltip: { _, _ in }, displaySendMessageOptions: { }, openScheduledMessages: { + }, displaySearchResultsTooltip: { _, _ in }, statuses: nil) self.navigationItem.titleView = self.titleView diff --git a/submodules/TelegramUI/TelegramUI/ChatSearchInputPanelNode.swift b/submodules/TelegramUI/TelegramUI/ChatSearchInputPanelNode.swift index 68badf64fd..dedab1ef79 100644 --- a/submodules/TelegramUI/TelegramUI/ChatSearchInputPanelNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatSearchInputPanelNode.swift @@ -5,6 +5,7 @@ import Display import TelegramCore import Postbox import SwiftSignalKit +import TelegramNotices import TelegramPresentationData import ActivityIndicator @@ -24,6 +25,8 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode { private let activityDisposable = MetaDisposable() private var displayActivity = false + private var needsSearchResultsTooltip = true + private var validLayout: (CGFloat, CGFloat, CGFloat, CGFloat, LayoutMetrics)? override var interfaceInteraction: ChatPanelInterfaceInteraction? { @@ -79,6 +82,26 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode { @objc func upPressed() { self.interfaceInteraction?.navigateMessageSearch(.earlier) + + guard self.needsSearchResultsTooltip, let context = self.context else { + return + } + + let _ = (ApplicationSpecificNotice.getChatMessageSearchResultsTip(accountManager: context.sharedContext.accountManager) + |> deliverOnMainQueue).start(next: { [weak self] counter in + guard let strongSelf = self else { + return + } + + if counter >= 3 { + strongSelf.needsSearchResultsTooltip = false + } else if arc4random_uniform(4) == 1 { + strongSelf.needsSearchResultsTooltip = false + + let _ = ApplicationSpecificNotice.incrementChatMessageSearchResultsTip(accountManager: context.sharedContext.accountManager).start() + strongSelf.interfaceInteraction?.displaySearchResultsTooltip(strongSelf.resultsButton, strongSelf.resultsButton.bounds) + } + }) } @objc func downPressed() { @@ -95,6 +118,10 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode { @objc func resultsPressed() { self.interfaceInteraction?.openSearchResults() + + if let context = self.context { + let _ = ApplicationSpecificNotice.incrementChatMessageSearchResultsTip(accountManager: context.sharedContext.accountManager, count: 4).start() + } } override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, maxHeight: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState, metrics: LayoutMetrics) -> CGFloat { diff --git a/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift b/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift index 19b172fa6f..5c4e6caad4 100644 --- a/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift +++ b/submodules/TelegramUI/TelegramUI/PeerMediaCollectionController.swift @@ -519,6 +519,7 @@ public class PeerMediaCollectionController: TelegramBaseController { }, displaySlowmodeTooltip: { _, _ in }, displaySendMessageOptions: { }, openScheduledMessages: { + }, displaySearchResultsTooltip: { _, _ in }, statuses: nil) self.updateInterfaceState(animated: false, { return $0 })