[WIP] Post suggestions

This commit is contained in:
Isaac 2025-06-17 18:09:45 +04:00
parent 55c7b6551f
commit 6dc45113d2
6 changed files with 106 additions and 24 deletions

View file

@ -342,6 +342,7 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
rawString = "❌ You rejected the message with the comment:"
} else {
rawString = "❌ You rejected the message."
smallFont = true
}
case .lowBalance:
rawString = "⚠️ **Transaction failed** because the user didn't have enough Stars."
@ -354,6 +355,7 @@ public class ChatMessageActionBubbleContentNode: ChatMessageBubbleContentNode {
rawString = "❌ **\(channelName)** rejected your message with the comment:"
} else {
rawString = "❌ **\(channelName)** rejected your message."
smallFont = true
}
case .lowBalance:
rawString = "⚠️ **Transaction failed** because you didn't have enough Stars."

View file

@ -547,9 +547,9 @@ private final class SheetContent: CombinedComponent {
switch mode {
case .sender:
if let amount = state.amount {
buttonString = "Set # \(presentationStringsFormattedNumber(amount, environment.dateTimeFormat.groupingSeparator))"
buttonString = "Offer # \(presentationStringsFormattedNumber(amount, environment.dateTimeFormat.groupingSeparator))"
} else {
buttonString = "Set # Free"
buttonString = "Offer for Free"
}
case .admin:
buttonString = "Update Terms"

View file

@ -2358,27 +2358,54 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let _ = strongSelf.context.engine.messages.monoforumPerformSuggestedPostAction(id: message.id, action: .approve(timestamp: timestamp)).startStandalone()
}
case 2:
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 entities: [MessageTextEntity] = []
for attribute in message.attributes {
if let attribute = attribute as? TextEntitiesMessageAttribute {
entities = attribute.entities
break
}
}
var inputTextMaxLength: Int32 = 4096
var webpageUrl: String?
for media in message.media {
if media is TelegramMediaImage || media is TelegramMediaFile {
inputTextMaxLength = strongSelf.context.userLimits.maxCaptionLength
} else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content {
webpageUrl = content.url
}
}
let inputText = chatInputStateStringWithAppliedEntities(message.text, entities: entities)
var disableUrlPreviews: [String] = []
if webpageUrl == nil {
disableUrlPreviews = detectUrls(inputText)
}
var updated = state.updatedInterfaceState { interfaceState in
return interfaceState.withUpdatedEditMessage(ChatEditMessageState(messageId: messageId, inputState: ChatTextInputState(inputText: inputText), disableUrlPreviews: disableUrlPreviews, inputTextMaxLength: inputTextMaxLength, mediaCaptionIsAbove: nil))
}
let (updatedState, updatedPreviewQueryState) = updatedChatEditInterfaceMessageState(context: strongSelf.context, state: updated, message: message)
updated = updatedState
strongSelf.editingUrlPreviewQueryState?.1.dispose()
strongSelf.editingUrlPreviewQueryState = updatedPreviewQueryState
updated = updated.updatedInputMode({ _ in
return .text
})
updated = updated.updatedShowCommands(false)
updated = updated.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
return updated
})
default:
break
@ -7421,7 +7448,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
return state
})
self.interfaceInteraction?.editMessage()
if self.presentationInterfaceState.interfaceState.postSuggestionState == nil {
self.interfaceInteraction?.editMessage()
}
}
}

View file

@ -1719,7 +1719,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
if let postSuggestionState = state.postSuggestionState {
state = state.withUpdatedPostSuggestionState(nil)
if postSuggestionState.editingOriginalMessageId != nil {
state = state.withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: "")))
state = state.withUpdatedEditMessage(nil)
}
}
return state
@ -4476,7 +4476,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
effectivePresentationInterfaceState = effectivePresentationInterfaceState.updatedInterfaceState { $0.withUpdatedEffectiveInputState(textInputPanelNode.inputTextState) }
}
if let _ = effectivePresentationInterfaceState.interfaceState.editMessage {
if let _ = effectivePresentationInterfaceState.interfaceState.editMessage, effectivePresentationInterfaceState.interfaceState.postSuggestionState == nil {
self.interfaceInteraction?.editMessage()
} else {
var isScheduledMessages = false
@ -4493,7 +4493,13 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
var messages: [EnqueueMessage] = []
let effectiveInputText = expandedInputStateAttributedString(effectivePresentationInterfaceState.interfaceState.composeInputState.inputText)
let effectiveInputText: NSAttributedString
if effectivePresentationInterfaceState.interfaceState.editMessage != nil && effectivePresentationInterfaceState.interfaceState.postSuggestionState != nil {
effectiveInputText = expandedInputStateAttributedString(effectivePresentationInterfaceState.interfaceState.effectiveInputState.inputText)
} else {
effectiveInputText = expandedInputStateAttributedString(effectivePresentationInterfaceState.interfaceState.composeInputState.inputText)
}
let peerSpecificEmojiPack = (self.controller?.contentData?.state.peerView?.cachedData as? CachedChannelData)?.emojiPack
@ -4609,6 +4615,30 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
} else {
let inputText = convertMarkdownToAttributes(effectiveInputText)
var mediaReference: AnyMediaReference?
var webpage: TelegramMediaWebpage?
if let urlPreview = self.chatPresentationInterfaceState.urlPreview {
if self.chatPresentationInterfaceState.interfaceState.composeDisableUrlPreviews.contains(urlPreview.url) {
} else {
webpage = urlPreview.webPage
}
}
mediaReference = webpage.flatMap(AnyMediaReference.standalone)
if let postSuggestionState = effectivePresentationInterfaceState.interfaceState.postSuggestionState, let editingOriginalMessageId = postSuggestionState.editingOriginalMessageId {
if let editMessageState = effectivePresentationInterfaceState.editMessageState, let mediaReferenceValue = editMessageState.mediaReference {
mediaReference = mediaReferenceValue
} else {
if let message = self.historyNode.messageInCurrentHistoryView(editingOriginalMessageId) {
for media in message.media {
if media is TelegramMediaFile || media is TelegramMediaImage {
mediaReference = .message(message: MessageReference(message), media: media)
}
}
}
}
}
for text in breakChatInputText(trimChatInputText(inputText)) {
if text.length != 0 {
var attributes: [MessageAttribute] = []
@ -4622,12 +4652,10 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
attributes.append(TextEntitiesMessageAttribute(entities: entities))
}
var webpage: TelegramMediaWebpage?
if let urlPreview = self.chatPresentationInterfaceState.urlPreview {
if self.chatPresentationInterfaceState.interfaceState.composeDisableUrlPreviews.contains(urlPreview.url) {
attributes.append(OutgoingContentInfoMessageAttribute(flags: [.disableLinkPreviews]))
} else {
webpage = urlPreview.webPage
attributes.append(WebpagePreviewMessageAttribute(leadingPreview: !urlPreview.positionBelowText, forceLargeMedia: urlPreview.largeMedia, isManuallyAdded: true, isSafe: false))
}
}
@ -4647,9 +4675,15 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
bubbleUpEmojiOrStickersets.removeAll()
}
messages.append(.message(text: text.string, attributes: attributes, inlineStickers: inlineStickers, mediaReference: webpage.flatMap(AnyMediaReference.standalone), threadId: self.chatLocation.threadId, replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets))
messages.append(.message(text: text.string, attributes: attributes, inlineStickers: inlineStickers, mediaReference: mediaReference, threadId: self.chatLocation.threadId, replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets))
mediaReference = nil
}
}
if let mediaReferenceValue = mediaReference {
mediaReference = nil
messages.append(.message(text: "", attributes: [], inlineStickers: inlineStickers, mediaReference: mediaReferenceValue, threadId: self.chatLocation.threadId, replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageSubject?.subjectModel, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []))
}
var forwardingToSameChat = false
if case let .peer(id) = self.chatPresentationInterfaceState.chatLocation, id.namespace == Namespaces.Peer.CloudUser, id != self.context.account.peerId, let forwardMessageIds = self.chatPresentationInterfaceState.interfaceState.forwardMessageIds, forwardMessageIds.count == 1 {
@ -4723,7 +4757,21 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate {
strongSelf.ignoreUpdateHeight = true
textInputPanelNode.text = ""
strongSelf.requestUpdateChatInterfaceState(.immediate, true, { $0.withUpdatedReplyMessageSubject(nil).withUpdatedSendMessageEffect(nil).withUpdatedPostSuggestionState(nil).withUpdatedForwardMessageIds(nil).withUpdatedForwardOptionsState(nil).withUpdatedComposeDisableUrlPreviews([]) })
strongSelf.requestUpdateChatInterfaceState(.immediate, true, { state in
var state = state
state = state.withUpdatedReplyMessageSubject(nil)
state = state.withUpdatedSendMessageEffect(nil)
if state.postSuggestionState != nil {
state = state.withUpdatedPostSuggestionState(nil)
state = state.withUpdatedEditMessage(nil)
}
state = state.withUpdatedForwardMessageIds(nil)
state = state.withUpdatedForwardOptionsState(nil)
state = state.withUpdatedComposeDisableUrlPreviews([])
return state
})
strongSelf.ignoreUpdateHeight = false
}
}, usedCorrelationId)

View file

@ -28,7 +28,7 @@ func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceS
break
}
if let editMessage = chatPresentationInterfaceState.interfaceState.editMessage {
if let editMessage = chatPresentationInterfaceState.interfaceState.editMessage, chatPresentationInterfaceState.interfaceState.postSuggestionState == nil {
if let editingUrlPreview = chatPresentationInterfaceState.editingUrlPreview, !editMessage.disableUrlPreviews.contains(editingUrlPreview.url) {
if let previewPanelNode = currentPanel as? WebpagePreviewAccessoryPanelNode {
previewPanelNode.interfaceInteraction = interfaceInteraction

View file

@ -564,6 +564,9 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate, Ch
let attachmentButton: HighlightableButtonNode
let attachmentButtonDisabledNode: HighlightableButtonNode
let searchLayoutClearButton: HighlightableButton
private let searchLayoutClearImageNode: ASImageNode
private var searchActivityIndicator: ActivityIndicator?