From ab470b6be7248ec3ba79c9753145de484717947e Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Tue, 17 Jun 2025 15:22:39 +0400 Subject: [PATCH] [WIP] Post suggestion --- .../Sources/BrowserBookmarksScreen.swift | 1 + .../Sources/ChatInterfaceState.swift | 4 +- .../ChatEmptyNode/Sources/ChatEmptyNode.swift | 2 +- .../ChatMessageActionBubbleContentNode/BUILD | 1 + .../ChatMessageActionBubbleContentNode.swift | 97 +++++++++++++++---- .../Sources/ChatMessageBubbleItemNode.swift | 12 ++- .../ChatRecentActionsControllerNode.swift | 1 + .../ChatSendAudioMessageContextPreview.swift | 1 + .../Sources/ChatControllerInteraction.swift | 4 + .../Sources/PeerInfoScreen.swift | 1 + .../Sources/StarsWithdrawalScreen.swift | 4 +- .../Chat/ChatControllerLoadDisplayNode.swift | 1 + .../Chat/ChatMessageActionOptions.swift | 1 + .../TelegramUI/Sources/ChatController.swift | 41 +++++--- .../Sources/ChatControllerNode.swift | 7 +- .../ChatInterfaceStateInputPanels.swift | 2 +- .../OverlayAudioPlayerControllerNode.swift | 1 + .../Sources/SharedAccountContext.swift | 1 + 18 files changed, 140 insertions(+), 42 deletions(-) diff --git a/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift b/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift index acf349f8f9..1c6ebd41a8 100644 --- a/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift +++ b/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift @@ -178,6 +178,7 @@ public final class BrowserBookmarksScreen: ViewController { }, updateChatLocationThread: { _, _ in }, requestToggleTodoMessageItem: { _, _, _ in }, displayTodoToggleUnavailable: { _ in + }, openStarsPurchase: { _ in }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil)) diff --git a/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift b/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift index fcf8790e59..3391e3e865 100644 --- a/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift +++ b/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift @@ -496,10 +496,12 @@ public final class ChatInterfaceState: Codable, Equatable { } public struct PostSuggestionState: Codable, Equatable { + public var editingOriginalMessageId: MessageId? public var price: Int64 public var timestamp: Int32? - public init(price: Int64, timestamp: Int32?) { + public init(editingOriginalMessageId: MessageId?, price: Int64, timestamp: Int32?) { + self.editingOriginalMessageId = editingOriginalMessageId self.price = price self.timestamp = timestamp } diff --git a/submodules/TelegramUI/Components/Chat/ChatEmptyNode/Sources/ChatEmptyNode.swift b/submodules/TelegramUI/Components/Chat/ChatEmptyNode/Sources/ChatEmptyNode.swift index b7f2eba602..bfee82ed6b 100644 --- a/submodules/TelegramUI/Components/Chat/ChatEmptyNode/Sources/ChatEmptyNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatEmptyNode/Sources/ChatEmptyNode.swift @@ -1348,7 +1348,7 @@ public final class ChatEmptyNodePremiumRequiredChatContent: ASDisplayNode, ChatE text: .plain(NSAttributedString(string: actionText, font: Font.semibold(15.0), textColor: serviceColor.primaryText)) )), environment: {}, - containerSize: CGSize(width: 200.0, height: 100.0) + containerSize: CGSize(width: 250.0, height: 100.0) ) } else { self.buttonTitle.view?.removeFromSuperview() diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageActionBubbleContentNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageActionBubbleContentNode/BUILD index ce4a9344c2..e613fa4d19 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageActionBubbleContentNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatMessageActionBubbleContentNode/BUILD @@ -34,6 +34,7 @@ swift_library( "//submodules/Markdown", "//submodules/ComponentFlow", "//submodules/ReactionSelectionNode", + "//submodules/Components/MultilineTextComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageActionBubbleContentNode/Sources/ChatMessageActionBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageActionBubbleContentNode/Sources/ChatMessageActionBubbleContentNode.swift index 0476a126f6..6166e8bbcf 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageActionBubbleContentNode/Sources/ChatMessageActionBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageActionBubbleContentNode/Sources/ChatMessageActionBubbleContentNode.swift @@ -24,6 +24,7 @@ import ChatMessageItemCommon import Markdown import ComponentFlow import ReactionSelectionNode +import MultilineTextComponent private func attributedServiceMessageString(theme: ChatPresentationThemeData, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, message: Message, messageCount: Int? = nil, accountPeerId: PeerId, forForumOverview: Bool) -> NSAttributedString? { return universalServiceMessageString(presentationData: (theme.theme, theme.wallpaper), strings: strings, nameDisplayOrder: nameDisplayOrder, dateTimeFormat: dateTimeFormat, message: EngineMessage(message), messageCount: messageCount, accountPeerId: accountPeerId, forChatList: false, forForumOverview: forForumOverview) @@ -40,7 +41,7 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode { public let backgroundMaskNode: ASImageNode public var linkHighlightingNode: LinkHighlightingNode? - private var buyStarsTitle: ComponentView? + private var buyStarsTitle: TextNode? private var buyStarsButton: HighlightTrackingButton? private var buttonStarsNode: PremiumStarsNode? @@ -162,9 +163,16 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode { return mediaHidden } + @objc private func buyStarsPressed() { + if let item = self.item { + item.controllerInteraction.openStarsPurchase(nil) + } + } + override public func asyncLayoutContent() -> (_ item: ChatMessageBubbleContentItem, _ layoutConstants: ChatMessageItemLayoutConstants, _ preparePosition: ChatMessageBubblePreparePosition, _ messageSelection: Bool?, _ constrainedSize: CGSize, _ avatarInset: CGFloat) -> (ChatMessageBubbleContentProperties, unboundSize: CGSize?, maxWidth: CGFloat, layout: (CGSize, ChatMessageBubbleContentPosition) -> (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation, Bool, ListViewItemApply?) -> Void))) { let makeTitleLayout = TextNode.asyncLayout(self.titleNode) let makeLabelLayout = TextNodeWithEntities.asyncLayout(self.labelNode) + let makeBuyStarsTitleLayout = TextNode.asyncLayout(self.buyStarsTitle) let cachedMaskBackgroundImage = self.cachedMaskBackgroundImage @@ -322,6 +330,7 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode { } let rawString: String + var smallFont = false switch suggestedPost { case .approved: rawString = "🤝 Agreement Reached!" @@ -336,6 +345,7 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode { } case .lowBalance: rawString = "⚠️ **Transaction failed** because the user didn't have enough Stars." + smallFont = true } } else { switch reason { @@ -347,13 +357,15 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode { } case .lowBalance: rawString = "⚠️ **Transaction failed** because you didn't have enough Stars." + smallFont = true } } } + let baseFontSize: CGFloat = smallFont ? 13.0 : 15.0 let titleString = parseMarkdownIntoAttributedString(rawString, attributes: MarkdownAttributes( - body: MarkdownAttributeSet(font: Font.regular(15.0), textColor: primaryTextColor), - bold: MarkdownAttributeSet(font: Font.bold(15.0), textColor: primaryTextColor), - link: MarkdownAttributeSet(font: Font.semibold(15.0), textColor: primaryTextColor), + body: MarkdownAttributeSet(font: Font.regular(baseFontSize), textColor: primaryTextColor), + bold: MarkdownAttributeSet(font: Font.bold(baseFontSize), textColor: primaryTextColor), + link: MarkdownAttributeSet(font: Font.semibold(baseFontSize), textColor: primaryTextColor), linkAttribute: { url in return ("URL", url) } @@ -429,6 +441,26 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode { backgroundSize.height += 4.0 } + var hasBuyStarsButton = false + if item.message.effectivelyIncoming(item.context.account.peerId), let suggestedPost, case let .rejected(reason, _) = suggestedPost, case .lowBalance = reason { + hasBuyStarsButton = true + } + + var buyStarsTitleLayoutAndApply: (TextNodeLayout, () -> TextNode)? + var buyStarsButtonSize: CGSize? + if hasBuyStarsButton { + //TODO:localize + let serviceColor = serviceMessageColorComponents(theme: item.presentationData.theme.theme, wallpaper: item.presentationData.theme.wallpaper) + let buyStarsTitleLayoutAndApplyValue = makeBuyStarsTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: "Buy Stars", font: Font.semibold(15.0), textColor: serviceColor.primaryText), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: constrainedSize.width - 32.0, height: CGFloat.greatestFiniteMagnitude), alignment: textAlignment, cutout: nil, insets: UIEdgeInsets())) + buyStarsTitleLayoutAndApply = buyStarsTitleLayoutAndApplyValue + + let buyStarsButtonSizeValue = CGSize(width: buyStarsTitleLayoutAndApplyValue.0.size.width + 20.0 * 2.0, height: buyStarsTitleLayoutAndApplyValue.0.size.height + 8.0 * 2.0) + buyStarsButtonSize = buyStarsButtonSizeValue + + backgroundSize.width = max(backgroundSize.width, buyStarsButtonSizeValue.width + 8.0 * 2.0) + backgroundSize.height += 15.0 + buyStarsButtonSizeValue.height + } + return (backgroundSize.width, { boundingWidth in return (CGSize(width: boundingWidth, height: backgroundSize.height + contentOuterInsets.top + contentOuterInsets.bottom), { [weak self] animation, synchronousLoads, _ in if let strongSelf = self { @@ -540,40 +572,66 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode { animation.animator.updatePosition(layer: titleNode.layer, position: titleFrame.origin, completion: nil) titleNode.bounds = CGRect(origin: CGPoint(), size: titleFrame.size) } - } else { labelFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((boundingWidth - labelLayout.size.width) / 2.0) - 1.0, y: image != nil ? 2.0 : floorToScreenPixels((backgroundSize.height - labelLayout.size.height) / 2.0) - 1.0), size: labelLayout.size) contentFrame = labelFrame } - if item.message.effectivelyIncoming(item.context.account.peerId), let suggestedPost, case let .rejected(reason, _) = suggestedPost, case .lowBalance = reason { - let buyStarsTitle: ComponentView? - if let current = strongSelf.buyStarsTitle { - buyStarsTitle = current - } else { - buyStarsTitle = ComponentView() - strongSelf.buyStarsTitle = buyStarsTitle - } - - let buyStarsButton: HighlightTrackingButton? + if hasBuyStarsButton, let (buyStarsTitleLayout, buyStarsTitleApply) = buyStarsTitleLayoutAndApply, let buyStarsButtonSize { + let buyStarsButton: HighlightTrackingButton if let current = strongSelf.buyStarsButton { buyStarsButton = current } else { buyStarsButton = HighlightTrackingButton() + buyStarsButton.clipsToBounds = true strongSelf.buyStarsButton = buyStarsButton + strongSelf.view.addSubview(buyStarsButton) + buyStarsButton.highligthedChanged = { [weak buyStarsButton] highlighted in + guard let buyStarsButton else { + return + } + if highlighted { + buyStarsButton.layer.removeAnimation(forKey: "opacity") + buyStarsButton.alpha = 0.6 + } else { + buyStarsButton.alpha = 1.0 + buyStarsButton.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + } + } + buyStarsButton.addTarget(strongSelf, action: #selector(strongSelf.buyStarsPressed), for: .touchUpInside) } - let buttonStarsNode: PremiumStarsNode? + let buttonStarsNode: PremiumStarsNode if let current = strongSelf.buttonStarsNode { buttonStarsNode = current } else { buttonStarsNode = PremiumStarsNode() + buttonStarsNode.isUserInteractionEnabled = false strongSelf.buttonStarsNode = buttonStarsNode + buyStarsButton.addSubview(buttonStarsNode.view) } + + let buyStarsTitle = buyStarsTitleApply() + if buyStarsTitle !== strongSelf.buyStarsTitle { + buyStarsTitle.isUserInteractionEnabled = false + strongSelf.buyStarsTitle?.view.removeFromSuperview() + } + strongSelf.buyStarsTitle = buyStarsTitle + buyStarsButton.addSubview(buyStarsTitle.view) + + let buttonTitleSize = buyStarsTitleLayout.size + + let buttonFrame = CGRect(origin: CGPoint(x: contentFrame.minX + floor((contentFrame.width - buyStarsButtonSize.width) * 0.5), y: labelFrame.minY - 2.0), size: buyStarsButtonSize) + buyStarsButton.frame = buttonFrame + buyStarsButton.layer.cornerRadius = buttonFrame.height * 0.5 + buyStarsTitle.frame = CGRect(origin: CGPoint(x: floor((buyStarsButtonSize.width - buttonTitleSize.width) * 0.5), y: floor((buyStarsButtonSize.height - buttonTitleSize.height) * 0.5)), size: buttonTitleSize) + + buyStarsButton.backgroundColor = item.presentationData.theme.theme.overallDarkAppearance ? UIColor(rgb: 0xffffff, alpha: 0.12) : UIColor(rgb: 0x000000, alpha: 0.12) + buttonStarsNode.frame = CGRect(origin: CGPoint(), size: buyStarsButtonSize) } else { if let buyStarsTitle = strongSelf.buyStarsTitle { strongSelf.buyStarsTitle = nil - buyStarsTitle.view?.removeFromSuperview() + buyStarsTitle.view.removeFromSuperview() } if let buyStarsButton = strongSelf.buyStarsButton { strongSelf.buyStarsButton = nil @@ -871,9 +929,12 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode { return ChatMessageBubbleContentTapAction(content: .hashtag(hashtag.peerName, hashtag.hashtag)) } } - if let imageNode = imageNode, imageNode.frame.contains(point) { + if let imageNode = self.imageNode, imageNode.frame.contains(point) { return ChatMessageBubbleContentTapAction(content: .openMessage) } + if let buyStarsButton = self.buyStarsButton, buyStarsButton.frame.contains(point) { + return ChatMessageBubbleContentTapAction(content: .ignore) + } if let backgroundNode = self.backgroundNode, backgroundNode.frame.contains(point) { if let item = self.item, item.message.media.contains(where: { $0 is TelegramMediaStory }) { diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 83840b9272..93983169f4 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -1059,6 +1059,12 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI self.backgroundWallpaperNode.animateFrom(sourceView: textInput.backgroundView, transition: transition) self.backgroundNode.animateFrom(sourceView: textInput.backgroundView, transition: transition) + + if let suggestedPostInfoNode = self.suggestedPostInfoNode { + transition.horizontal.animatePositionAdditive(layer: suggestedPostInfoNode.layer, offset: CGPoint(x: -widthDifference, y: 0.0)) + transition.horizontal.animateTransformScale(view: suggestedPostInfoNode.view, from: 0.001) + suggestedPostInfoNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) + } for contentNode in self.contentNodes { if let contentNode = contentNode as? ChatMessageTextBubbleContentNode { @@ -3526,11 +3532,9 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI strongSelf.suggestedPostInfoNode?.removeFromSupernode() strongSelf.suggestedPostInfoNode = suggestedPostInfoNode strongSelf.mainContextSourceNode.contentNode.addSubnode(suggestedPostInfoNode) - - let suggestedPostInfoFrame = CGRect(origin: CGPoint(x: floor((params.width - suggestedPostInfoSize.width) * 0.5), y: 4.0), size: suggestedPostInfoSize) - suggestedPostInfoNode.frame = suggestedPostInfoFrame - //animation.animator.updateFrame(layer: suggestedPostInfoNode.layer, frame: suggestedPostInfoFrame, completion: nil) } + let suggestedPostInfoFrame = CGRect(origin: CGPoint(x: floor((params.width - suggestedPostInfoSize.width) * 0.5), y: 4.0), size: suggestedPostInfoSize) + suggestedPostInfoNode.frame = suggestedPostInfoFrame } else if let suggestedPostInfoNode = strongSelf.suggestedPostInfoNode { strongSelf.suggestedPostInfoNode = nil suggestedPostInfoNode.removeFromSupernode() diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift index 3b6716dca2..3901808232 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift @@ -649,6 +649,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { }, updateChatLocationThread: { _, _ in }, requestToggleTodoMessageItem: { _, _, _ in }, displayTodoToggleUnavailable: { _ in + }, openStarsPurchase: { _ in }, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: self.backgroundNode)) self.controllerInteraction = controllerInteraction diff --git a/submodules/TelegramUI/Components/Chat/ChatSendAudioMessageContextPreview/Sources/ChatSendAudioMessageContextPreview.swift b/submodules/TelegramUI/Components/Chat/ChatSendAudioMessageContextPreview/Sources/ChatSendAudioMessageContextPreview.swift index cc30bf2a7b..2afa28bc4c 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSendAudioMessageContextPreview/Sources/ChatSendAudioMessageContextPreview.swift +++ b/submodules/TelegramUI/Components/Chat/ChatSendAudioMessageContextPreview/Sources/ChatSendAudioMessageContextPreview.swift @@ -504,6 +504,7 @@ public final class ChatSendGroupMediaMessageContextPreview: UIView, ChatSendMess }, updateChatLocationThread: { _, _ in }, requestToggleTodoMessageItem: { _, _, _ in }, displayTodoToggleUnavailable: { _ in + }, openStarsPurchase: { _ in }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: self.context, backgroundNode: self.wallpaperBackgroundNode)) diff --git a/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift b/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift index 98aa86bcde..05b3f420db 100644 --- a/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift +++ b/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift @@ -288,6 +288,8 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol public let updateChatLocationThread: (Int64?, ChatControllerAnimateInnerChatSwitchDirection?) -> Void public let requestToggleTodoMessageItem: (MessageId, Int32, Bool) -> Void public let displayTodoToggleUnavailable: (MessageId) -> Void + public let openStarsPurchase: (Int64?) -> Void + public var canPlayMedia: Bool = false public var hiddenMedia: [MessageId: [Media]] = [:] public var expandedTranslationMessageStableIds: Set = Set() @@ -452,6 +454,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol updateChatLocationThread: @escaping (Int64?, ChatControllerAnimateInnerChatSwitchDirection?) -> Void, requestToggleTodoMessageItem: @escaping (MessageId, Int32, Bool) -> Void, displayTodoToggleUnavailable: @escaping (MessageId) -> Void, + openStarsPurchase: @escaping (Int64?) -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings, @@ -573,6 +576,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol self.updateChatLocationThread = updateChatLocationThread self.requestToggleTodoMessageItem = requestToggleTodoMessageItem self.displayTodoToggleUnavailable = displayTodoToggleUnavailable + self.openStarsPurchase = openStarsPurchase self.automaticMediaDownloadSettings = automaticMediaDownloadSettings diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index f0b52601ef..a879d791f5 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -3869,6 +3869,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }, updateChatLocationThread: { _, _ in }, requestToggleTodoMessageItem: { _, _, _ in }, displayTodoToggleUnavailable: { _ in + }, openStarsPurchase: { _ in }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil)) self.hiddenMediaDisposable = context.sharedContext.mediaManager.galleryHiddenMediaManager.hiddenIds().startStrict(next: { [weak self] ids in diff --git a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift index 84952e5109..158edaf5ea 100644 --- a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift @@ -508,9 +508,9 @@ private final class SheetContent: CombinedComponent { switch mode { case .sender: if let amount = state.amount { - buttonString = "Offer # \(presentationStringsFormattedNumber(amount, environment.dateTimeFormat.groupingSeparator))" + buttonString = "Set # \(presentationStringsFormattedNumber(amount, environment.dateTimeFormat.groupingSeparator))" } else { - buttonString = "Offer" + buttonString = "Set" } case .admin: buttonString = "Update Terms" diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift index b4614d6302..15a70128ed 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift @@ -4140,6 +4140,7 @@ extension ChatControllerImpl { state = state.updatedInterfaceState { interfaceState in var interfaceState = interfaceState interfaceState = interfaceState.withUpdatedPostSuggestionState(ChatInterfaceState.PostSuggestionState( + editingOriginalMessageId: nil, price: 0, timestamp: nil )) diff --git a/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift b/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift index 0c013ef3d7..85a0215d36 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift @@ -1001,6 +1001,7 @@ extension ChatControllerImpl { state = state.updatedInterfaceState { interfaceState in var interfaceState = interfaceState interfaceState = interfaceState.withUpdatedPostSuggestionState(ChatInterfaceState.PostSuggestionState( + editingOriginalMessageId: interfaceState.postSuggestionState?.editingOriginalMessageId, price: price, timestamp: timestamp )) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 5558b76b64..c50c8d7f5f 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -2358,20 +2358,28 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let _ = strongSelf.context.engine.messages.monoforumPerformSuggestedPostAction(id: message.id, action: .approve(timestamp: timestamp)).startStandalone() } case 2: - strongSelf.push(strongSelf.context.sharedContext.makeStarsWithdrawalScreen( - context: strongSelf.context, - subject: .postSuggestionModification( - current: StarsAmount(value: attribute.amount, nanos: 0), - timestamp: attribute.timestamp, - completion: { [weak strongSelf] price, timestamp in - guard let strongSelf else { - return - } - - let _ = strongSelf.context.engine.messages.monoforumPerformSuggestedPostAction(id: message.id, action: .proposeChanges(amount: price, timestamp: timestamp)).startStandalone() - } - ) - )) + var entities: [MessageTextEntity] = [] + for attribute in message.attributes { + if let attribute = attribute as? TextEntitiesMessageAttribute { + entities = attribute.entities + break + } + } + let inputText = chatInputStateStringWithAppliedEntities(message.text, entities: entities) + + strongSelf.updateChatPresentationInterfaceState(interactive: true, { state in + var state = state + state = state.updatedInterfaceState { interfaceState in + var interfaceState = interfaceState + interfaceState = interfaceState.withUpdatedPostSuggestionState(ChatInterfaceState.PostSuggestionState( + editingOriginalMessageId: message.id, + price: attribute.amount, + timestamp: attribute.timestamp + )).withUpdatedComposeInputState(ChatTextInputState(inputText: inputText)) + return interfaceState + } + return state + }) default: break } @@ -4946,6 +4954,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G ) self.present(controller, in: .current) } + }, openStarsPurchase: { [weak self] amount in + self?.interfaceInteraction?.openStarsPurchase(amount) }, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: self.stickerSettings, presentationContext: ChatPresentationContext(context: context, backgroundNode: self.chatBackgroundNode)) controllerInteraction.enableFullTranslucency = context.sharedContext.energyUsageSettings.fullTranslucency @@ -7917,6 +7927,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case .customChatContents: break } + if let postSuggestionState = self.presentationInterfaceState.interfaceState.postSuggestionState, let editingOriginalMessageId = postSuggestionState.editingOriginalMessageId { + defaultReplyMessageSubject = EngineMessageReplySubject(messageId: editingOriginalMessageId, quote: nil) + } return messages.map { message in var message = message diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 036ba91e12..a7ea404462 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -1716,7 +1716,12 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { } else if let _ = accessoryPanelNode as? SuggestPostAccessoryPanelNode { strongSelf.requestUpdateChatInterfaceState(.animated(duration: 0.4, curve: .spring), false, { state in var state = state - state = state.withUpdatedPostSuggestionState(nil) + if let postSuggestionState = state.postSuggestionState { + state = state.withUpdatedPostSuggestionState(nil) + if postSuggestionState.editingOriginalMessageId != nil { + state = state.withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))) + } + } return state }) } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift index aeb763de8a..2e3a1c482f 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateInputPanels.swift @@ -231,7 +231,7 @@ func inputPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceState if channel.flags.contains(.isMonoforum) { if let linkedMonoforumId = channel.linkedMonoforumId, let mainChannel = chatPresentationInterfaceState.renderedPeer?.peers[linkedMonoforumId] as? TelegramChannel, mainChannel.hasPermission(.manageDirect), case .peer = chatPresentationInterfaceState.chatLocation { - if chatPresentationInterfaceState.interfaceState.editMessage != nil { + if chatPresentationInterfaceState.interfaceState.editMessage != nil || chatPresentationInterfaceState.interfaceState.postSuggestionState != nil { displayInputTextPanel = true } else if chatPresentationInterfaceState.interfaceState.replyMessageSubject == nil { displayInputTextPanel = false diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift index fc0892252c..9edb749228 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift @@ -196,6 +196,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu }, updateChatLocationThread: { _, _ in }, requestToggleTodoMessageItem: { _, _, _ in }, displayTodoToggleUnavailable: { _ in + }, openStarsPurchase: { _ in }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil)) self.dimNode = ASDisplayNode() diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 37b6b1ad6f..21e7bbf9ab 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -2402,6 +2402,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { }, updateChatLocationThread: { _, _ in }, requestToggleTodoMessageItem: { _, _, _ in }, displayTodoToggleUnavailable: { _ in + }, openStarsPurchase: { _ in }, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: backgroundNode as? WallpaperBackgroundNode))