diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 5c42773db3..5d7e5dbc9b 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -13006,6 +13006,9 @@ Sorry for the inconvenience."; "Gift.View.Convert.Stars_1" = "%@ Star"; "Gift.View.Convert.Stars_any" = "%@ Stars"; +"Gift.View.NameHidden" = "Only you can see the senders's name."; +"Gift.View.NameAndMessageHidden" = "Only you can see the senders's name and message."; + "Gift.View.DisplayedInfo" = "The gift is visible on your Page. [View >]()"; "Gift.View.HiddenInfo" = "This gift is hidden. Only you can see it."; diff --git a/submodules/AccountContext/Sources/ChatController.swift b/submodules/AccountContext/Sources/ChatController.swift index e25e234b27..05ab49c8b2 100644 --- a/submodules/AccountContext/Sources/ChatController.swift +++ b/submodules/AccountContext/Sources/ChatController.swift @@ -63,7 +63,6 @@ public final class ChatMessageItemAssociatedData: Equatable { public let isStandalone: Bool public let isInline: Bool public let showSensitiveContent: Bool - public let starGifts: [Int64: TelegramMediaFile] public init( automaticDownloadPeerType: MediaAutoDownloadPeerType, @@ -97,8 +96,7 @@ public final class ChatMessageItemAssociatedData: Equatable { deviceContactsNumbers: Set = Set(), isStandalone: Bool = false, isInline: Bool = false, - showSensitiveContent: Bool = false, - starGifts: [Int64: TelegramMediaFile] = [:] + showSensitiveContent: Bool = false ) { self.automaticDownloadPeerType = automaticDownloadPeerType self.automaticDownloadPeerId = automaticDownloadPeerId @@ -132,7 +130,6 @@ public final class ChatMessageItemAssociatedData: Equatable { self.isStandalone = isStandalone self.isInline = isInline self.showSensitiveContent = showSensitiveContent - self.starGifts = starGifts } public static func == (lhs: ChatMessageItemAssociatedData, rhs: ChatMessageItemAssociatedData) -> Bool { @@ -226,9 +223,6 @@ public final class ChatMessageItemAssociatedData: Equatable { if lhs.showSensitiveContent != rhs.showSensitiveContent { return false } - if lhs.starGifts != rhs.starGifts { - return false - } return true } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift index 349401e213..85a0117c97 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift @@ -288,6 +288,8 @@ func _internal_updateStarGiftAddedToProfile(account: Account, messageId: EngineM } } +private var cachedAccountGifts: [EnginePeer.Id: [ProfileGiftsContext.State.StarGift]] = [:] + private final class ProfileGiftsContextImpl { private let queue: Queue private let account: Account @@ -320,7 +322,11 @@ private final class ProfileGiftsContextImpl { } func loadMore() { - if case let .ready(true, nextOffset) = self.dataState { + if case let .ready(true, initialNextOffset) = self.dataState { + if self.gifts.isEmpty, self.peerId == self.account.peerId, let cachedGifts = cachedAccountGifts[self.peerId] { + self.gifts = cachedGifts + } + self.dataState = .loading self.pushState() @@ -335,7 +341,7 @@ private final class ProfileGiftsContextImpl { guard let inputUser else { return .single(([], 0, nil)) } - return network.request(Api.functions.payments.getUserStarGifts(userId: inputUser, offset: nextOffset ?? "", limit: 32)) + return network.request(Api.functions.payments.getUserStarGifts(userId: inputUser, offset: initialNextOffset ?? "", limit: 32)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) @@ -362,8 +368,13 @@ private final class ProfileGiftsContextImpl { guard let strongSelf = self else { return } - for gift in gifts { - strongSelf.gifts.append(gift) + if initialNextOffset == nil, strongSelf.peerId == strongSelf.account.peerId { + cachedAccountGifts[strongSelf.peerId] = gifts + strongSelf.gifts = gifts + } else { + for gift in gifts { + strongSelf.gifts.append(gift) + } } let updatedCount = max(Int32(strongSelf.gifts.count), count) diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index 49171ab6e8..3396ef3132 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -91,17 +91,16 @@ private final class GiftViewSheetContent: CombinedComponent { if let arguments = subject.arguments { var peerIds: [EnginePeer.Id] = [arguments.peerId, context.account.peerId] - if let fromPeerId = arguments.fromPeerId { + if let fromPeerId = arguments.fromPeerId, !peerIds.contains(fromPeerId) { peerIds.append(fromPeerId) } - self.disposable = combineLatest(queue: Queue.mainQueue(), context.engine.data.get(EngineDataMap( peerIds.map { peerId -> TelegramEngine.EngineData.Item.Peer.Peer in return TelegramEngine.EngineData.Item.Peer.Peer(id: peerId) } )), - context.engine.payments.cachedStarGifts() + .single(nil) |> then(context.engine.payments.cachedStarGifts()) ).startStrict(next: { [weak self] peers, starGifts in if let strongSelf = self { var peersMap: [EnginePeer.Id: EnginePeer] = [:] @@ -142,6 +141,7 @@ private final class GiftViewSheetContent: CombinedComponent { let animation = Child(GiftAnimationComponent.self) let title = Child(MultilineTextComponent.self) let description = Child(MultilineTextComponent.self) + let hiddenText = Child(MultilineTextComponent.self) let table = Child(TableComponent.self) let additionalText = Child(MultilineTextComponent.self) let button = Child(SolidRoundedButtonComponent.self) @@ -192,6 +192,7 @@ private final class GiftViewSheetContent: CombinedComponent { var giftId: Int64 = 0 var date: Int32? var soldOut = false + var nameHidden = false if case let .soldOutGift(gift) = component.subject { animationFile = gift.file stars = gift.price @@ -214,6 +215,7 @@ private final class GiftViewSheetContent: CombinedComponent { giftId = arguments.gift.id date = arguments.date titleString = incoming ? strings.Gift_View_ReceivedTitle : strings.Gift_View_Title + nameHidden = arguments.nameHidden } else { animationFile = nil stars = 0 @@ -270,7 +272,101 @@ private final class GiftViewSheetContent: CombinedComponent { availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude), transition: .immediate ) - + context.add(title + .position(CGPoint(x: context.availableSize.width / 2.0, y: 177.0)) + ) + + var originY: CGFloat = 0.0 + if let animationFile { + let animation = animation.update( + component: GiftAnimationComponent( + context: component.context, + theme: environment.theme, + file: animationFile + ), + availableSize: CGSize(width: 128.0, height: 128.0), + transition: .immediate + ) + context.add(animation + .position(CGPoint(x: context.availableSize.width / 2.0, y: animation.size.height / 2.0 + 25.0)) + ) + originY += animation.size.height + } + originY += 80.0 + if soldOut { + originY -= 12.0 + } + + let linkColor = theme.actionSheet.controlAccentColor + if !descriptionText.isEmpty { + if state.cachedChevronImage == nil || state.cachedChevronImage?.1 !== environment.theme { + state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Settings/TextArrowRight"), color: linkColor)!, theme) + } + + let textFont = soldOut ? Font.medium(15.0) : Font.regular(15.0) + let textColor = soldOut ? theme.list.itemDestructiveColor : theme.list.itemPrimaryTextColor + let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: textFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in + return (TelegramTextAttributes.URL, contents) + }) + let attributedString = parseMarkdownIntoAttributedString(descriptionText, attributes: markdownAttributes, textAlignment: .center).mutableCopy() as! NSMutableAttributedString + if let range = attributedString.string.range(of: ">"), let chevronImage = state.cachedChevronImage?.0 { + attributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: attributedString.string)) + } + let description = description.update( + component: MultilineTextComponent( + text: .plain(attributedString), + horizontalAlignment: .center, + maximumNumberOfLines: 5, + lineSpacing: 0.2, + highlightColor: linkColor.withAlphaComponent(0.1), + highlightInset: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: -8.0), + highlightAction: { attributes in + if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] { + return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL) + } else { + return nil + } + }, + tapAction: { _, _ in + component.openStarsIntro() + } + ), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude), + transition: .immediate + ) + context.add(description + .position(CGPoint(x: context.availableSize.width / 2.0, y: originY + description.size.height / 2.0)) + ) + originY += description.size.height + 21.0 + if soldOut { + originY -= 7.0 + } + } else { + originY += 21.0 + } + + if nameHidden { + let textFont = Font.regular(13.0) + let textColor = theme.list.itemSecondaryTextColor + + let hiddenText = hiddenText.update( + component: MultilineTextComponent( + text: .plain(NSAttributedString(string: text != nil ? strings.Gift_View_NameAndMessageHidden : strings.Gift_View_NameHidden, font: textFont, textColor: textColor)), + horizontalAlignment: .center, + maximumNumberOfLines: 2, + lineSpacing: 0.2 + ), + availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude), + transition: .immediate + ) + context.add(hiddenText + .position(CGPoint(x: context.availableSize.width / 2.0, y: originY)) + ) + + originY += hiddenText.size.height + originY += 11.0 + } + let tableFont = Font.regular(15.0) let tableBoldFont = Font.semibold(15.0) let tableItalicFont = Font.italic(15.0) @@ -489,82 +585,6 @@ private final class GiftViewSheetContent: CombinedComponent { availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: .greatestFiniteMagnitude), transition: .immediate ) - - let linkColor = theme.actionSheet.controlAccentColor - - context.add(title - .position(CGPoint(x: context.availableSize.width / 2.0, y: 177.0)) - ) - - var originY: CGFloat = 0.0 - if let animationFile { - let animation = animation.update( - component: GiftAnimationComponent( - context: component.context, - theme: environment.theme, - file: animationFile - ), - availableSize: CGSize(width: 128.0, height: 128.0), - transition: .immediate - ) - context.add(animation - .position(CGPoint(x: context.availableSize.width / 2.0, y: animation.size.height / 2.0 + 25.0)) - ) - originY += animation.size.height - } - originY += 80.0 - - if soldOut { - originY -= 12.0 - } - - if !descriptionText.isEmpty { - if state.cachedChevronImage == nil || state.cachedChevronImage?.1 !== environment.theme { - state.cachedChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Settings/TextArrowRight"), color: linkColor)!, theme) - } - - let textFont = soldOut ? Font.medium(15.0) : Font.regular(15.0) - let textColor = soldOut ? theme.list.itemDestructiveColor : theme.list.itemPrimaryTextColor - let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: textFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: linkColor), linkAttribute: { contents in - return (TelegramTextAttributes.URL, contents) - }) - let attributedString = parseMarkdownIntoAttributedString(descriptionText, attributes: markdownAttributes, textAlignment: .center).mutableCopy() as! NSMutableAttributedString - if let range = attributedString.string.range(of: ">"), let chevronImage = state.cachedChevronImage?.0 { - attributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: attributedString.string)) - } - let description = description.update( - component: MultilineTextComponent( - text: .plain(attributedString), - horizontalAlignment: .center, - maximumNumberOfLines: 5, - lineSpacing: 0.2, - highlightColor: linkColor.withAlphaComponent(0.1), - highlightInset: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: -8.0), - highlightAction: { attributes in - if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] { - return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL) - } else { - return nil - } - }, - tapAction: { _, _ in - component.openStarsIntro() - } - ), - availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude), - transition: .immediate - ) - context.add(description - .position(CGPoint(x: context.availableSize.width / 2.0, y: originY + description.size.height / 2.0)) - ) - originY += description.size.height + 21.0 - if soldOut { - originY -= 7.0 - } - } else { - originY += 21.0 - } - context.add(table .position(CGPoint(x: context.availableSize.width / 2.0, y: originY + table.size.height / 2.0)) ) diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 77cef9dfe3..5d53b73751 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -351,8 +351,7 @@ private func extractAssociatedData( chatThemes: [TelegramTheme], deviceContactsNumbers: Set, isInline: Bool, - showSensitiveContent: Bool, - starGifts: [Int64 : TelegramMediaFile] + showSensitiveContent: Bool ) -> ChatMessageItemAssociatedData { var automaticDownloadPeerId: EnginePeer.Id? var automaticMediaDownloadPeerType: MediaAutoDownloadPeerType = .channel @@ -407,7 +406,7 @@ private func extractAssociatedData( automaticDownloadPeerId = message.peerId } - return ChatMessageItemAssociatedData(automaticDownloadPeerType: automaticMediaDownloadPeerType, automaticDownloadPeerId: automaticDownloadPeerId, automaticDownloadNetworkType: automaticDownloadNetworkType, preferredStoryHighQuality: preferredStoryHighQuality, isRecentActions: false, subject: subject, contactsPeerIds: contactsPeerIds, channelDiscussionGroup: channelDiscussionGroup, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, currentlyPlayingMessageId: currentlyPlayingMessageId, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: savedMessageTags, defaultReaction: defaultReaction, isPremium: isPremium, accountPeer: accountPeer, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, topicAuthorId: topicAuthorId, hasBots: hasBots, translateToLanguage: translateToLanguage, maxReadStoryId: maxReadStoryId, recommendedChannels: recommendedChannels, audioTranscriptionTrial: audioTranscriptionTrial, chatThemes: chatThemes, deviceContactsNumbers: deviceContactsNumbers, isInline: isInline, showSensitiveContent: showSensitiveContent, starGifts: starGifts) + return ChatMessageItemAssociatedData(automaticDownloadPeerType: automaticMediaDownloadPeerType, automaticDownloadPeerId: automaticDownloadPeerId, automaticDownloadNetworkType: automaticDownloadNetworkType, preferredStoryHighQuality: preferredStoryHighQuality, isRecentActions: false, subject: subject, contactsPeerIds: contactsPeerIds, channelDiscussionGroup: channelDiscussionGroup, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, currentlyPlayingMessageId: currentlyPlayingMessageId, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, savedMessageTags: savedMessageTags, defaultReaction: defaultReaction, isPremium: isPremium, accountPeer: accountPeer, alwaysDisplayTranscribeButton: alwaysDisplayTranscribeButton, topicAuthorId: topicAuthorId, hasBots: hasBots, translateToLanguage: translateToLanguage, maxReadStoryId: maxReadStoryId, recommendedChannels: recommendedChannels, audioTranscriptionTrial: audioTranscriptionTrial, chatThemes: chatThemes, deviceContactsNumbers: deviceContactsNumbers, isInline: isInline, showSensitiveContent: showSensitiveContent) } private extension ChatHistoryLocationInput { @@ -1615,17 +1614,6 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto } |> distinctUntilChanged - let starGifts: Signal<[Int64 : TelegramMediaFile], NoError> = context.engine.payments.cachedStarGifts() - |> map { gifts in - var files: [Int64 : TelegramMediaFile] = [:] - if let gifts { - for gift in gifts { - files[gift.id] = gift.file - } - } - return files - } - let messageViewQueue = Queue.mainQueue() let historyViewTransitionDisposable = combineLatest(queue: messageViewQueue, historyViewUpdate, @@ -1652,9 +1640,8 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto audioTranscriptionTrial, chatThemes, deviceContactsNumbers, - contentSettings, - starGifts - ).startStrict(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, preferredStoryHighQuality, animatedEmojiStickers, additionalAnimatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState, availableReactions, availableMessageEffects, savedMessageTags, defaultReaction, accountPeer, suggestAudioTranscription, promises, topicAuthorId, translationState, maxReadStoryId, recommendedChannels, audioTranscriptionTrial, chatThemes, deviceContactsNumbers, contentSettings, starGifts in + contentSettings + ).startStrict(next: { [weak self] update, chatPresentationData, selectedMessages, updatingMedia, networkType, preferredStoryHighQuality, animatedEmojiStickers, additionalAnimatedEmojiStickers, customChannelDiscussionReadState, customThreadOutgoingReadState, availableReactions, availableMessageEffects, savedMessageTags, defaultReaction, accountPeer, suggestAudioTranscription, promises, topicAuthorId, translationState, maxReadStoryId, recommendedChannels, audioTranscriptionTrial, chatThemes, deviceContactsNumbers, contentSettings in let (historyAppearsCleared, pendingUnpinnedAllMessages, pendingRemovedMessages, currentlyPlayingMessageIdAndType, scrollToMessageId, chatHasBots, allAdMessages) = promises func applyHole() { @@ -1868,7 +1855,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto translateToLanguage = normalizeTranslationLanguage(languageCode) } - let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, preferredStoryHighQuality: preferredStoryHighQuality, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageIdAndType?.0, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, 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, showSensitiveContent: contentSettings.ignoreContentRestrictionReasons.contains("sensitive"), starGifts: starGifts) + let associatedData = extractAssociatedData(chatLocation: chatLocation, view: view, automaticDownloadNetworkType: networkType, preferredStoryHighQuality: preferredStoryHighQuality, animatedEmojiStickers: animatedEmojiStickers, additionalAnimatedEmojiStickers: additionalAnimatedEmojiStickers, subject: subject, currentlyPlayingMessageId: currentlyPlayingMessageIdAndType?.0, isCopyProtectionEnabled: isCopyProtectionEnabled, availableReactions: availableReactions, availableMessageEffects: availableMessageEffects, 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, showSensitiveContent: contentSettings.ignoreContentRestrictionReasons.contains("sensitive")) var includeEmbeddedSavedChatInfo = false if case let .replyThread(message) = chatLocation, message.peerId == context.account.peerId, !rotated {