Add support for poll translation

This commit is contained in:
Ilya Laktyushin 2024-05-03 13:43:42 +04:00
parent 18a6a3c2a9
commit 63e45b4bbb
7 changed files with 184 additions and 53 deletions

View File

@ -1,11 +1,10 @@
import Postbox import Postbox
public class TranslationMessageAttribute: MessageAttribute, Equatable { public class TranslationMessageAttribute: MessageAttribute, Equatable {
public struct Additional : PostboxCoding, Equatable { public struct Additional : PostboxCoding, Equatable {
public let text: String public let text: String
public let entities: [MessageTextEntity] public let entities: [MessageTextEntity]
public init(text: String, entities: [MessageTextEntity]) { public init(text: String, entities: [MessageTextEntity]) {
self.text = text self.text = text
self.entities = entities self.entities = entities
@ -20,15 +19,13 @@ public class TranslationMessageAttribute: MessageAttribute, Equatable {
encoder.encodeString(self.text, forKey: "text") encoder.encodeString(self.text, forKey: "text")
encoder.encodeObjectArray(self.entities, forKey: "entities") encoder.encodeObjectArray(self.entities, forKey: "entities")
} }
} }
public let text: String public let text: String
public let entities: [MessageTextEntity] public let entities: [MessageTextEntity]
public let toLang: String public let toLang: String
public let additional:[Additional] public let additional: [Additional]
public var associatedPeerIds: [PeerId] { public var associatedPeerIds: [PeerId] {
return [] return []

View File

@ -27,6 +27,7 @@ swift_library(
"//submodules/TelegramUI/Components/Chat/PollBubbleTimerNode", "//submodules/TelegramUI/Components/Chat/PollBubbleTimerNode",
"//submodules/TelegramUI/Components/Chat/MergedAvatarsNode", "//submodules/TelegramUI/Components/Chat/MergedAvatarsNode",
"//submodules/TelegramUI/Components/TextNodeWithEntities", "//submodules/TelegramUI/Components/TextNodeWithEntities",
"//submodules/TelegramUI/Components/Chat/ShimmeringLinkNode",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -17,6 +17,7 @@ import ChatMessageItemCommon
import PollBubbleTimerNode import PollBubbleTimerNode
import MergedAvatarsNode import MergedAvatarsNode
import TextNodeWithEntities import TextNodeWithEntities
import ShimmeringLinkNode
private final class ChatMessagePollOptionRadioNodeParameters: NSObject { private final class ChatMessagePollOptionRadioNodeParameters: NSObject {
let timestamp: Double let timestamp: Double
@ -387,7 +388,7 @@ private final class ChatMessagePollOptionNode: ASDisplayNode {
private(set) var radioNode: ChatMessagePollOptionRadioNode? private(set) var radioNode: ChatMessagePollOptionRadioNode?
private let percentageNode: ASDisplayNode private let percentageNode: ASDisplayNode
private var percentageImage: UIImage? private var percentageImage: UIImage?
private var titleNode: TextNodeWithEntities? fileprivate var titleNode: TextNodeWithEntities?
private let buttonNode: HighlightTrackingButtonNode private let buttonNode: HighlightTrackingButtonNode
let separatorNode: ASDisplayNode let separatorNode: ASDisplayNode
private let resultBarNode: ASImageNode private let resultBarNode: ASImageNode
@ -491,22 +492,29 @@ private final class ChatMessagePollOptionNode: ASDisplayNode {
} }
} }
static func asyncLayout(_ maybeNode: ChatMessagePollOptionNode?) -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ poll: TelegramMediaPoll, _ option: TelegramMediaPollOption, _ optionResult: ChatMessagePollOptionResult?, _ constrainedWidth: CGFloat) -> (minimumWidth: CGFloat, layout: ((CGFloat) -> (CGSize, (Bool, Bool, Bool) -> ChatMessagePollOptionNode))) { static func asyncLayout(_ maybeNode: ChatMessagePollOptionNode?) -> (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ poll: TelegramMediaPoll, _ option: TelegramMediaPollOption, _ translation: TranslationMessageAttribute.Additional?, _ optionResult: ChatMessagePollOptionResult?, _ constrainedWidth: CGFloat) -> (minimumWidth: CGFloat, layout: ((CGFloat) -> (CGSize, (Bool, Bool, Bool) -> ChatMessagePollOptionNode))) {
let makeTitleLayout = TextNodeWithEntities.asyncLayout(maybeNode?.titleNode) let makeTitleLayout = TextNodeWithEntities.asyncLayout(maybeNode?.titleNode)
let currentResult = maybeNode?.currentResult let currentResult = maybeNode?.currentResult
let currentSelection = maybeNode?.currentSelection let currentSelection = maybeNode?.currentSelection
let currentTheme = maybeNode?.theme let currentTheme = maybeNode?.theme
return { context, presentationData, message, poll, option, optionResult, constrainedWidth in return { context, presentationData, message, poll, option, translation, optionResult, constrainedWidth in
let leftInset: CGFloat = 50.0 let leftInset: CGFloat = 50.0
let rightInset: CGFloat = 12.0 let rightInset: CGFloat = 12.0
let incoming = message.effectivelyIncoming(context.account.peerId) let incoming = message.effectivelyIncoming(context.account.peerId)
var optionText = option.text
var optionEntities = option.entities
if let translation {
optionText = translation.text
optionEntities = translation.entities
}
let optionTextColor: UIColor = incoming ? presentationData.theme.theme.chat.message.incoming.primaryTextColor : presentationData.theme.theme.chat.message.outgoing.primaryTextColor let optionTextColor: UIColor = incoming ? presentationData.theme.theme.chat.message.incoming.primaryTextColor : presentationData.theme.theme.chat.message.outgoing.primaryTextColor
let optionAttributedText = stringWithAppliedEntities( let optionAttributedText = stringWithAppliedEntities(
option.text, optionText,
entities: option.entities, entities: optionEntities,
baseColor: optionTextColor, baseColor: optionTextColor,
linkColor: optionTextColor, linkColor: optionTextColor,
baseFont: presentationData.messageFont, baseFont: presentationData.messageFont,
@ -627,6 +635,25 @@ private final class ChatMessagePollOptionNode: ASDisplayNode {
node.buttonNode.accessibilityLabel = option.text node.buttonNode.accessibilityLabel = option.text
if animated {
if let titleNode = node.titleNode, let cachedLayout = titleNode.textNode.cachedLayout {
if !cachedLayout.areLinesEqual(to: titleLayout) {
if let textContents = titleNode.textNode.contents {
let fadeNode = ASDisplayNode()
fadeNode.displaysAsynchronously = false
fadeNode.contents = textContents
fadeNode.frame = titleNode.textNode.frame
fadeNode.isLayerBacked = true
node.addSubnode(fadeNode)
fadeNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak fadeNode] _ in
fadeNode?.removeFromSupernode()
})
titleNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
}
}
}
}
let titleNode = titleApply(TextNodeWithEntities.Arguments( let titleNode = titleApply(TextNodeWithEntities.Arguments(
context: context, context: context,
cache: context.animationCache, cache: context.animationCache,
@ -828,6 +855,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
private let buttonNode: HighlightableButtonNode private let buttonNode: HighlightableButtonNode
private let statusNode: ChatMessageDateAndStatusNode private let statusNode: ChatMessageDateAndStatusNode
private var optionNodes: [ChatMessagePollOptionNode] = [] private var optionNodes: [ChatMessagePollOptionNode] = []
private var shimmeringNodes: [ShimmeringLinkNode] = []
private var poll: TelegramMediaPoll? private var poll: TelegramMediaPoll?
@ -996,7 +1024,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
} }
} }
var previousOptionNodeLayouts: [Data: (_ contet: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ poll: TelegramMediaPoll, _ option: TelegramMediaPollOption, _ optionResult: ChatMessagePollOptionResult?, _ constrainedWidth: CGFloat) -> (minimumWidth: CGFloat, layout: ((CGFloat) -> (CGSize, (Bool, Bool, Bool) -> ChatMessagePollOptionNode)))] = [:] var previousOptionNodeLayouts: [Data: (_ contet: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ poll: TelegramMediaPoll, _ option: TelegramMediaPollOption, _ translation: TranslationMessageAttribute.Additional?, _ optionResult: ChatMessagePollOptionResult?, _ constrainedWidth: CGFloat) -> (minimumWidth: CGFloat, layout: ((CGFloat) -> (CGSize, (Bool, Bool, Bool) -> ChatMessagePollOptionNode)))] = [:]
for optionNode in self.optionNodes { for optionNode in self.optionNodes {
if let option = optionNode.option { if let option = optionNode.option {
previousOptionNodeLayouts[option.opaqueIdentifier] = ChatMessagePollOptionNode.asyncLayout(optionNode) previousOptionNodeLayouts[option.opaqueIdentifier] = ChatMessagePollOptionNode.asyncLayout(optionNode)
@ -1114,11 +1142,28 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
let messageTheme = incoming ? item.presentationData.theme.theme.chat.message.incoming : item.presentationData.theme.theme.chat.message.outgoing let messageTheme = incoming ? item.presentationData.theme.theme.chat.message.incoming : item.presentationData.theme.theme.chat.message.outgoing
let attributedText: NSAttributedString
if let poll { var pollTitleText = poll?.text ?? ""
attributedText = stringWithAppliedEntities( var pollTitleEntities = poll?.textEntities ?? []
poll.text, var pollOptions: [TranslationMessageAttribute.Additional] = []
entities: poll.textEntities,
var isTranslating = false
if let poll, let translateToLanguage = item.associatedData.translateToLanguage, !poll.text.isEmpty && incoming {
isTranslating = true
for attribute in item.message.attributes {
if let attribute = attribute as? TranslationMessageAttribute, !attribute.text.isEmpty, attribute.toLang == translateToLanguage {
pollTitleText = attribute.text
pollTitleEntities = attribute.entities
pollOptions = attribute.additional
isTranslating = false
break
}
}
}
let attributedText = stringWithAppliedEntities(
pollTitleText,
entities: pollTitleEntities,
baseColor: messageTheme.primaryTextColor, baseColor: messageTheme.primaryTextColor,
linkColor: messageTheme.linkTextColor, linkColor: messageTheme.linkTextColor,
baseFont: item.presentationData.messageBoldFont, baseFont: item.presentationData.messageBoldFont,
@ -1130,9 +1175,6 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
blockQuoteFont: item.presentationData.messageBoldFont, blockQuoteFont: item.presentationData.messageBoldFont,
message: message message: message
) )
} else {
attributedText = NSAttributedString(string: "", font: item.presentationData.messageBoldFont, textColor: messageTheme.primaryTextColor)
}
let textInsets = UIEdgeInsets(top: 2.0, left: 0.0, bottom: 5.0, right: 0.0) let textInsets = UIEdgeInsets(top: 2.0, left: 0.0, bottom: 5.0, right: 0.0)
@ -1269,7 +1311,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
for i in 0 ..< poll.options.count { for i in 0 ..< poll.options.count {
let option = poll.options[i] let option = poll.options[i]
let makeLayout: (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ poll: TelegramMediaPoll, _ option: TelegramMediaPollOption, _ optionResult: ChatMessagePollOptionResult?, _ constrainedWidth: CGFloat) -> (minimumWidth: CGFloat, layout: ((CGFloat) -> (CGSize, (Bool, Bool, Bool) -> ChatMessagePollOptionNode))) let makeLayout: (_ context: AccountContext, _ presentationData: ChatPresentationData, _ message: Message, _ poll: TelegramMediaPoll, _ option: TelegramMediaPollOption, _ translation: TranslationMessageAttribute.Additional?, _ optionResult: ChatMessagePollOptionResult?, _ constrainedWidth: CGFloat) -> (minimumWidth: CGFloat, layout: ((CGFloat) -> (CGSize, (Bool, Bool, Bool) -> ChatMessagePollOptionNode)))
if let previous = previousOptionNodeLayouts[option.opaqueIdentifier] { if let previous = previousOptionNodeLayouts[option.opaqueIdentifier] {
makeLayout = previous makeLayout = previous
} else { } else {
@ -1285,7 +1327,13 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
} else if isClosed { } else if isClosed {
optionResult = ChatMessagePollOptionResult(normalized: 0, percent: 0, count: 0) optionResult = ChatMessagePollOptionResult(normalized: 0, percent: 0, count: 0)
} }
let result = makeLayout(item.context, item.presentationData, item.message, poll, option, optionResult, constrainedSize.width - layoutConstants.bubble.borderInset * 2.0)
var translation: TranslationMessageAttribute.Additional?
if !pollOptions.isEmpty && i < pollOptions.count {
translation = pollOptions[i]
}
let result = makeLayout(item.context, item.presentationData, item.message, poll, option, translation, optionResult, constrainedSize.width - layoutConstants.bubble.borderInset * 2.0)
boundingSize.width = max(boundingSize.width, result.minimumWidth + layoutConstants.bubble.borderInset * 2.0) boundingSize.width = max(boundingSize.width, result.minimumWidth + layoutConstants.bubble.borderInset * 2.0)
pollOptionsFinalizeLayouts.append(result.1) pollOptionsFinalizeLayouts.append(result.1)
} }
@ -1351,6 +1399,26 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
strongSelf.item = item strongSelf.item = item
strongSelf.poll = poll strongSelf.poll = poll
let cachedLayout = strongSelf.textNode.textNode.cachedLayout
if case .System = animation {
if let cachedLayout = cachedLayout {
if !cachedLayout.areLinesEqual(to: textLayout) {
if let textContents = strongSelf.textNode.textNode.contents {
let fadeNode = ASDisplayNode()
fadeNode.displaysAsynchronously = false
fadeNode.contents = textContents
fadeNode.frame = strongSelf.textNode.textNode.frame
fadeNode.isLayerBacked = true
strongSelf.addSubnode(fadeNode)
fadeNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak fadeNode] _ in
fadeNode?.removeFromSupernode()
})
strongSelf.textNode.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
}
}
}
}
let _ = textApply(TextNodeWithEntities.Arguments( let _ = textApply(TextNodeWithEntities.Arguments(
context: item.context, context: item.context,
cache: item.context.animationCache, cache: item.context.animationCache,
@ -1371,6 +1439,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
} }
} }
let optionNode = apply(animation.isAnimated, isRequesting, synchronousLoad) let optionNode = apply(animation.isAnimated, isRequesting, synchronousLoad)
let optionNodeFrame = CGRect(origin: CGPoint(x: layoutConstants.bubble.borderInset, y: verticalOffset), size: size)
if optionNode.supernode !== strongSelf { if optionNode.supernode !== strongSelf {
strongSelf.addSubnode(optionNode) strongSelf.addSubnode(optionNode)
let option = optionNode.option let option = optionNode.option
@ -1387,8 +1456,11 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
} }
strongSelf.updateSelection() strongSelf.updateSelection()
} }
optionNode.frame = optionNodeFrame
} else {
animation.animator.updateFrame(layer: optionNode.layer, frame: optionNodeFrame, completion: nil)
} }
optionNode.frame = CGRect(origin: CGPoint(x: layoutConstants.bubble.borderInset, y: verticalOffset), size: size)
verticalOffset += size.height verticalOffset += size.height
updatedOptionNodes.append(optionNode) updatedOptionNodes.append(optionNode)
optionNode.isUserInteractionEnabled = canVote && item.controllerInteraction.pollActionState.pollMessageIdsInProgress[item.message.id] == nil optionNode.isUserInteractionEnabled = canVote && item.controllerInteraction.pollActionState.pollMessageIdsInProgress[item.message.id] == nil
@ -1410,7 +1482,7 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
strongSelf.textNode.textNode.frame = textFrame strongSelf.textNode.textNode.frame = textFrame
} }
let typeFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: textFrame.maxY + titleTypeSpacing), size: typeLayout.size) let typeFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: textFrame.maxY + titleTypeSpacing), size: typeLayout.size)
strongSelf.typeNode.frame = typeFrame animation.animator.updateFrame(layer: strongSelf.typeNode.layer, frame: typeFrame, completion: nil)
let deadlineTimeout = poll?.deadlineTimeout let deadlineTimeout = poll?.deadlineTimeout
var displayDeadline = true var displayDeadline = true
@ -1536,7 +1608,8 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
let _ = votersApply() let _ = votersApply()
let votersFrame = CGRect(origin: CGPoint(x: floor((resultSize.width - votersLayout.size.width) / 2.0), y: verticalOffset + optionsVotersSpacing), size: votersLayout.size) let votersFrame = CGRect(origin: CGPoint(x: floor((resultSize.width - votersLayout.size.width) / 2.0), y: verticalOffset + optionsVotersSpacing), size: votersLayout.size)
strongSelf.votersNode.frame = votersFrame animation.animator.updateFrame(layer: strongSelf.votersNode.layer, frame: votersFrame, completion: nil)
if animation.isAnimated, let previousPoll = previousPoll, let poll = poll { if animation.isAnimated, let previousPoll = previousPoll, let poll = poll {
if previousPoll.results.totalVoters == nil && poll.results.totalVoters != nil { if previousPoll.results.totalVoters == nil && poll.results.totalVoters != nil {
strongSelf.votersNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) strongSelf.votersNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
@ -1571,6 +1644,8 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
strongSelf.updateSelection() strongSelf.updateSelection()
strongSelf.updatePollTooltipMessageState(animated: false) strongSelf.updatePollTooltipMessageState(animated: false)
strongSelf.updateIsTranslating(isTranslating)
} }
}) })
}) })
@ -1578,6 +1653,46 @@ public class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
} }
} }
private func updateIsTranslating(_ isTranslating: Bool) {
guard let item = self.item else {
return
}
var rects: [[CGRect]] = []
let titleRects = (self.textNode.textNode.rangeRects(in: NSRange(location: 0, length: self.textNode.textNode.cachedLayout?.attributedString?.length ?? 0))?.rects ?? []).map { self.textNode.textNode.view.convert($0, to: self.view) }
rects.append(titleRects)
for optionNode in self.optionNodes {
if let titleNode = optionNode.titleNode {
let optionRects = (titleNode.textNode.rangeRects(in: NSRange(location: 0, length: titleNode.textNode.cachedLayout?.attributedString?.length ?? 0))?.rects ?? []).map { titleNode.textNode.view.convert($0, to: self.view) }
rects.append(optionRects)
}
}
if isTranslating, !rects.isEmpty {
if self.shimmeringNodes.isEmpty {
for rects in rects {
let shimmeringNode = ShimmeringLinkNode(color: item.message.effectivelyIncoming(item.context.account.peerId) ? item.presentationData.theme.theme.chat.message.incoming.secondaryTextColor.withAlphaComponent(0.1) : item.presentationData.theme.theme.chat.message.outgoing.secondaryTextColor.withAlphaComponent(0.1))
shimmeringNode.updateRects(rects)
shimmeringNode.frame = self.bounds
shimmeringNode.updateLayout(self.bounds.size)
shimmeringNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.shimmeringNodes.append(shimmeringNode)
self.insertSubnode(shimmeringNode, belowSubnode: self.textNode.textNode)
}
}
} else if !self.shimmeringNodes.isEmpty {
let shimmeringNodes = self.shimmeringNodes
self.shimmeringNodes = []
for shimmeringNode in shimmeringNodes {
shimmeringNode.alpha = 0.0
shimmeringNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak shimmeringNode] _ in
shimmeringNode?.removeFromSupernode()
})
}
}
}
private func updateSelection() { private func updateSelection() {
guard let item = self.item, let poll = self.poll else { guard let item = self.item, let poll = self.poll else {
return return

View File

@ -998,6 +998,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
}) })
} }
} }
override public func updateTouchesAtPoint(_ point: CGPoint?) { override public func updateTouchesAtPoint(_ point: CGPoint?) {
if let item = self.item { if let item = self.item {
var rects: [CGRect]? var rects: [CGRect]?

View File

@ -1934,7 +1934,7 @@ private func editingItems(data: PeerInfoScreenData?, state: PeerInfoState, chatL
} }
} }
if let cachedData = data.cachedData as? CachedChannelData, isCreator || cachedData.flags.contains(.canViewStats) { if let cachedData = data.cachedData as? CachedChannelData, cachedData.flags.contains(.canViewStats) {
items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemStats, label: .none, text: presentationData.strings.Channel_Info_Stats, icon: UIImage(bundleImageName: "Chat/Info/StatsIcon"), action: { items[.peerAdditionalSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemStats, label: .none, text: presentationData.strings.Channel_Info_Stats, icon: UIImage(bundleImageName: "Chat/Info/StatsIcon"), action: {
interaction.openStats(false) interaction.openStats(false)
})) }))

View File

@ -2474,22 +2474,30 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
guard message.adAttribute == nil && message.id.namespace == Namespaces.Message.Cloud else { guard message.adAttribute == nil && message.id.namespace == Namespaces.Message.Cloud else {
continue continue
} }
if !message.text.isEmpty && message.author?.id != self.context.account.peerId { guard message.author?.id != self.context.account.peerId else {
if let translation = message.attributes.first(where: { $0 is TranslationMessageAttribute }) as? TranslationMessageAttribute, translation.toLang == translateToLanguage { continue
} else {
messageIdsToTranslate.append(message.id)
} }
if let translation = message.attributes.first(where: { $0 is TranslationMessageAttribute }) as? TranslationMessageAttribute, translation.toLang == translateToLanguage {
continue
}
if !message.text.isEmpty {
messageIdsToTranslate.append(message.id)
} else if let _ = message.media.first(where: { $0 is TelegramMediaPoll }) {
messageIdsToTranslate.append(message.id)
} }
case let .MessageGroupEntry(_, messages, _): case let .MessageGroupEntry(_, messages, _):
for (message, _, _, _, _) in messages { for (message, _, _, _, _) in messages {
guard message.adAttribute == nil && message.id.namespace == Namespaces.Message.Cloud else { guard message.adAttribute == nil && message.id.namespace == Namespaces.Message.Cloud else {
continue continue
} }
if !message.text.isEmpty && message.author?.id != self.context.account.peerId { guard message.author?.id != self.context.account.peerId else {
if let translation = message.attributes.first(where: { $0 is TranslationMessageAttribute }) as? TranslationMessageAttribute, translation.toLang == translateToLanguage { continue
} else {
messageIdsToTranslate.append(message.id)
} }
if let translation = message.attributes.first(where: { $0 is TranslationMessageAttribute }) as? TranslationMessageAttribute, translation.toLang == translateToLanguage {
continue
}
if !message.text.isEmpty {
messageIdsToTranslate.append(message.id)
} }
} }
default: default:

View File

@ -126,13 +126,22 @@ public func translateMessageIds(context: AccountContext, messageIds: [EngineMess
} }
} }
} }
if !message.text.isEmpty && message.author?.id != context.account.peerId { guard message.author?.id != context.account.peerId else {
continue
}
if let translation = message.attributes.first(where: { $0 is TranslationMessageAttribute }) as? TranslationMessageAttribute, translation.toLang == toLang { if let translation = message.attributes.first(where: { $0 is TranslationMessageAttribute }) as? TranslationMessageAttribute, translation.toLang == toLang {
} else { continue
}
if !message.text.isEmpty {
if !messageIdsSet.contains(messageId) { if !messageIdsSet.contains(messageId) {
messageIdsToTranslate.append(messageId) messageIdsToTranslate.append(messageId)
messageIdsSet.insert(messageId) messageIdsSet.insert(messageId)
} }
} else if let _ = message.media.first(where: { $0 is TelegramMediaPoll }) {
if !messageIdsSet.contains(messageId) {
messageIdsToTranslate.append(messageId)
messageIdsSet.insert(messageId)
} }
} }
} else { } else {