diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 9e9013d0b9..b2d0efc509 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -10992,3 +10992,5 @@ Sorry for the inconvenience."; "Premium.MessageTags" = "Tags for Messages"; "Premium.MessageTagsInfo" = "Organize your Saved Messages with tags for quicker access."; "Premium.MessageTags.Proceed" = "About Telegram Premium"; + +"Chat.SavedMessagesTabInfoText" = "Messages you send to **Saved Messages**"; diff --git a/submodules/TelegramNotices/Sources/Notices.swift b/submodules/TelegramNotices/Sources/Notices.swift index 61ce0ec8af..bea3e90919 100644 --- a/submodules/TelegramNotices/Sources/Notices.swift +++ b/submodules/TelegramNotices/Sources/Notices.swift @@ -196,6 +196,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 { case incomingVideoMessagePlayOnceTip = 62 case outgoingVideoMessagePlayOnceTip = 63 case dismissedMessageTagsBadge = 64 + case savedMessageTagLabelSuggestion = 65 var key: ValueBoxKey { let v = ValueBoxKey(length: 4) @@ -509,6 +510,10 @@ private struct ApplicationSpecificNoticeKeys { static func dismissedMessageTagsBadge() -> NoticeEntryKey { return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.dismissedMessageTagsBadge.key) } + + static func savedMessageTagLabelSuggestion() -> NoticeEntryKey { + return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.savedMessageTagLabelSuggestion.key) + } } public struct ApplicationSpecificNotice { @@ -2106,4 +2111,31 @@ public struct ApplicationSpecificNotice { } |> take(1) } + + public static func getSavedMessageTagLabelSuggestion(accountManager: AccountManager) -> Signal { + return accountManager.transaction { transaction -> Int32 in + if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.savedMessageTagLabelSuggestion())?.get(ApplicationSpecificCounterNotice.self) { + return value.value + } else { + return 0 + } + } + } + + public static func incrementSavedMessageTagLabelSuggestion(accountManager: AccountManager, count: Int = 1) -> Signal { + return accountManager.transaction { transaction -> Int in + var currentValue: Int32 = 0 + if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.savedMessageTagLabelSuggestion())?.get(ApplicationSpecificCounterNotice.self) { + currentValue = value.value + } + let previousValue = currentValue + currentValue += Int32(count) + + if let entry = CodableEntry(ApplicationSpecificCounterNotice(value: currentValue)) { + transaction.setNotice(ApplicationSpecificNoticeKeys.savedMessageTagLabelSuggestion(), entry) + } + + return Int(previousValue) + } + } } diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift index f87578ea2f..03d14c86d3 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageDateAndStatusNode/Sources/ChatMessageDateAndStatusNode.swift @@ -394,7 +394,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { selectedForeground: themeColors.reactionActiveForeground.argb, extractedBackground: arguments.presentationData.theme.theme.contextMenu.backgroundColor.argb, extractedForeground: arguments.presentationData.theme.theme.contextMenu.primaryColor.argb, - extractedSelectedForeground: arguments.presentationData.theme.theme.contextMenu.primaryColor.argb, + extractedSelectedForeground: arguments.presentationData.theme.theme.overallDarkAppearance ? themeColors.reactionActiveForeground.argb : arguments.presentationData.theme.theme.list.itemCheckColors.foregroundColor.argb, deselectedMediaPlaceholder: themeColors.reactionInactiveMediaPlaceholder.argb, selectedMediaPlaceholder: themeColors.reactionActiveMediaPlaceholder.argb ) @@ -408,7 +408,7 @@ public class ChatMessageDateAndStatusNode: ASDisplayNode { selectedForeground: themeColors.reactionActiveForeground.argb, extractedBackground: arguments.presentationData.theme.theme.contextMenu.backgroundColor.argb, extractedForeground: arguments.presentationData.theme.theme.contextMenu.primaryColor.argb, - extractedSelectedForeground: arguments.presentationData.theme.theme.list.itemCheckColors.foregroundColor.argb, + extractedSelectedForeground: arguments.presentationData.theme.theme.overallDarkAppearance ? themeColors.reactionActiveForeground.argb : arguments.presentationData.theme.theme.list.itemCheckColors.foregroundColor.argb, deselectedMediaPlaceholder: themeColors.reactionInactiveMediaPlaceholder.argb, selectedMediaPlaceholder: themeColors.reactionActiveMediaPlaceholder.argb ) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift index 240d315d2b..c3895c87ee 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageReactionsFooterContentNode/Sources/ChatMessageReactionsFooterContentNode.swift @@ -79,7 +79,7 @@ public final class MessageReactionButtonsNode: ASDisplayNode { selectedForeground: themeColors.reactionActiveForeground.argb, extractedBackground: presentationData.theme.theme.contextMenu.backgroundColor.argb, extractedForeground: presentationData.theme.theme.contextMenu.primaryColor.argb, - extractedSelectedForeground: presentationData.theme.theme.contextMenu.primaryColor.argb, + extractedSelectedForeground: presentationData.theme.theme.overallDarkAppearance ? themeColors.reactionActiveForeground.argb : presentationData.theme.theme.list.itemCheckColors.foregroundColor.argb, deselectedMediaPlaceholder: themeColors.reactionInactiveMediaPlaceholder.argb, selectedMediaPlaceholder: themeColors.reactionActiveMediaPlaceholder.argb ) @@ -92,7 +92,7 @@ public final class MessageReactionButtonsNode: ASDisplayNode { selectedForeground: themeColors.reactionActiveForeground.argb, extractedBackground: presentationData.theme.theme.contextMenu.backgroundColor.argb, extractedForeground: presentationData.theme.theme.contextMenu.primaryColor.argb, - extractedSelectedForeground: presentationData.theme.theme.list.itemCheckColors.foregroundColor.argb, + extractedSelectedForeground: presentationData.theme.theme.overallDarkAppearance ? themeColors.reactionActiveForeground.argb : presentationData.theme.theme.list.itemCheckColors.foregroundColor.argb, deselectedMediaPlaceholder: themeColors.reactionInactiveMediaPlaceholder.argb, selectedMediaPlaceholder: themeColors.reactionActiveMediaPlaceholder.argb ) @@ -110,7 +110,7 @@ public final class MessageReactionButtonsNode: ASDisplayNode { selectedForeground: themeColors.reactionActiveForeground.argb, extractedBackground: presentationData.theme.theme.contextMenu.backgroundColor.argb, extractedForeground: presentationData.theme.theme.contextMenu.primaryColor.argb, - extractedSelectedForeground: presentationData.theme.theme.list.itemCheckColors.foregroundColor.argb, + extractedSelectedForeground: presentationData.theme.theme.contextMenu.primaryColor.argb, deselectedMediaPlaceholder: themeColors.reactionInactiveMediaPlaceholder.argb, selectedMediaPlaceholder: themeColors.reactionActiveMediaPlaceholder.argb ) diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift index b31889c890..aa8ff5b3be 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageContextMenu.swift @@ -16,6 +16,7 @@ import TextNodeWithEntities import PremiumUI import TooltipUI import TopMessageReactions +import TelegramNotices extension ChatControllerImpl { func openMessageContextMenu(message: Message, selectAll: Bool, node: ASDisplayNode, frame: CGRect, anyRecognizer: UIGestureRecognizer?, location: CGPoint?) -> Void { @@ -396,13 +397,31 @@ extension ChatControllerImpl { standaloneReactionAnimation.frame = self.chatDisplayNode.bounds self.chatDisplayNode.addSubnode(standaloneReactionAnimation) }, completion: { [weak self, weak itemNode, weak targetView] in - guard let self, let itemNode = itemNode, let targetView = targetView else { + guard let self, let itemNode, let targetView else { return } - let _ = self - let _ = itemNode - let _ = targetView + let _ = (ApplicationSpecificNotice.getSavedMessageTagLabelSuggestion(accountManager: self.context.sharedContext.accountManager) + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { [weak self, weak targetView, weak itemNode] value in + guard let self, let targetView, let itemNode else { + return + } + if value >= 3 { + return + } + + let _ = itemNode + + //TODO:localize + let rect = self.chatDisplayNode.view.convert(targetView.bounds, from: targetView).insetBy(dx: -8.0, dy: -8.0) + let tooltipScreen = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: "Tap and hold to add a name to your tag"), location: .point(rect, .bottom), displayDuration: .custom(5.0), shouldDismissOnTouch: { point, _ in + return .ignore + }) + self.present(tooltipScreen, in: .current) + + let _ = ApplicationSpecificNotice.incrementSavedMessageTagLabelSuggestion(accountManager: self.context.sharedContext.accountManager).startStandalone() + }) }) } else { controller.dismiss() diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 552294d9a5..8739bbec51 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -1901,6 +1901,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { let current = listInsets listInsets.top = current.bottom listInsets.bottom = current.top + listInsets.top += 8.0 } var displayTopDimNode = false diff --git a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift index d7cd4e20db..2f1bedf637 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift @@ -1,4 +1,5 @@ import Foundation +import UIKit import Postbox import TelegramCore import TemporaryCachedPeerDataManager @@ -7,6 +8,9 @@ import AccountContext import TelegramPresentationData import ChatHistoryEntry import ChatMessageItemCommon +import TextFormat +import Markdown +import Display func chatHistoryEntriesForView( location: ChatLocation, @@ -15,6 +19,7 @@ func chatHistoryEntriesForView( includeEmptyEntry: Bool, includeChatInfoEntry: Bool, includeSearchEntry: Bool, + includeEmbeddedSavedChatInfo: Bool, reverse: Bool, groupMessages: Bool, reverseGroupedMessages: Bool, @@ -454,6 +459,56 @@ func chatHistoryEntriesForView( } } } + if includeEmbeddedSavedChatInfo, let peerId = location.peerId { + if !view.isLoading && view.laterId == nil { + let string = presentationData.strings.Chat_SavedMessagesTabInfoText + let formattedString = parseMarkdownIntoAttributedString( + string, + attributes: MarkdownAttributes( + body: MarkdownAttributeSet(font: Font.regular(15.0), textColor: .black), + bold: MarkdownAttributeSet(font: Font.regular(15.0), textColor: .white), + link: MarkdownAttributeSet(font: Font.regular(15.0), textColor: .black), + linkAttribute: { url in + return ("URL", url) + } + ) + ) + var entities: [MessageTextEntity] = [] + formattedString.enumerateAttribute(.foregroundColor, in: NSRange(location: 0, length: formattedString.length), options: [], using: { value, range, _ in + if let value = value as? UIColor, value == .white { + entities.append(MessageTextEntity(range: range.lowerBound ..< range.upperBound, type: .Bold)) + } + }) + + let message = Message( + stableId: UInt32.max - 1001, + stableVersion: 0, + id: MessageId(peerId: peerId, namespace: Namespaces.Message.Local, id: 123), + globallyUniqueId: nil, + groupingKey: nil, + groupInfo: nil, + threadId: nil, + timestamp: Int32.max - 1, + flags: [.Incoming], + tags: [], + globalTags: [], + localTags: [], + customTags: [], + forwardInfo: nil, + author: nil, + text: "", + attributes: [], + media: [TelegramMediaAction(action: .customText(text: formattedString.string, entities: entities, additionalAttributes: nil))], + peers: SimpleDictionary(), + associatedMessages: SimpleDictionary(), + associatedMessageIds: [], + associatedMedia: [:], + associatedThreadInfo: nil, + associatedStories: [:] + ) + entries.append(.MessageEntry(message, presentationData, false, nil, .none, ChatMessageEntryAttributes(rank: nil, isContact: false, contentTypeHint: .generic, updatingMedia: nil, isPlaying: false, isCentered: false, authorStoryStats: nil))) + } + } if reverse { return entries.reversed() diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index f108e88d3d..210a8d2175 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -1697,6 +1697,11 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageIdAndType?.0, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, savedMessageTags: savedMessageTags, defaultReaction: defaultReaction, isPremium: isPremium, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, accountPeer: accountPeer, topicAuthorId: topicAuthorId, hasBots: chatHasBots, translateToLanguage: translateToLanguage, maxReadStoryId: maxReadStoryId, recommendedChannels: recommendedChannels, audioTranscriptionTrial: audioTranscriptionTrial, chatThemes: chatThemes, deviceContactsNumbers: deviceContactsNumbers, isInline: !rotated) + var includeEmbeddedSavedChatInfo = false + if case let .replyThread(message) = chatLocation, message.peerId == context.account.peerId, !rotated { + includeEmbeddedSavedChatInfo = true + } + let filteredEntries = chatHistoryEntriesForView( location: chatLocation, view: view, @@ -1704,6 +1709,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto includeEmptyEntry: mode == .bubbles && tag == nil, includeChatInfoEntry: mode == .bubbles, includeSearchEntry: includeSearchEntry && tag != nil, + includeEmbeddedSavedChatInfo: includeEmbeddedSavedChatInfo, reverse: reverse, groupMessages: mode == .bubbles, reverseGroupedMessages: reverseGroups,