mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
00455e1163
commit
ab5e3ae947
@ -985,6 +985,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
private var cachedChatListText: (String, String)?
|
||||
private var cachedChatListSearchResult: CachedChatListSearchResult?
|
||||
private var cachedChatListQuoteSearchResult: CachedChatListSearchResult?
|
||||
private var cachedCustomTextEntities: CachedCustomTextEntities?
|
||||
|
||||
var layoutParams: (ChatListItem, first: Bool, last: Bool, firstWithHeader: Bool, nextIsPinned: Bool, ListViewItemLayoutParams, countersSize: CGFloat)?
|
||||
@ -1585,6 +1586,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let currentItem = self.layoutParams?.0
|
||||
let currentChatListText = self.cachedChatListText
|
||||
let currentChatListSearchResult = self.cachedChatListSearchResult
|
||||
let currentChatListQuoteSearchResult = self.cachedChatListQuoteSearchResult
|
||||
let currentCustomTextEntities = self.cachedCustomTextEntities
|
||||
|
||||
return { item, params, first, last, firstWithHeader, nextIsPinned in
|
||||
@ -1869,6 +1871,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
var chatListText: (String, String)?
|
||||
var chatListSearchResult: CachedChatListSearchResult?
|
||||
var chatListQuoteSearchResult: CachedChatListSearchResult?
|
||||
var customTextEntities: CachedCustomTextEntities?
|
||||
|
||||
let contentImageSide: CGFloat = max(10.0, min(20.0, floor(item.presentationData.fontSize.baseDisplaySize * 18.0 / 17.0)))
|
||||
@ -2016,15 +2019,45 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
composedString = NSMutableAttributedString(attributedString: messageString)
|
||||
}
|
||||
|
||||
var composedReplyString: NSMutableAttributedString?
|
||||
if let searchQuery = item.interaction.searchTextHighightState {
|
||||
var quoteText: String?
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? ReplyMessageAttribute {
|
||||
if let quote = attribute.quote {
|
||||
quoteText = quote.text
|
||||
}
|
||||
} else if let attribute = attribute as? QuotedReplyMessageAttribute {
|
||||
if let quote = attribute.quote {
|
||||
quoteText = quote.text
|
||||
}
|
||||
}
|
||||
}
|
||||
if let quoteText {
|
||||
let quoteString = foldLineBreaks(stringWithAppliedEntities(quoteText, entities: [], baseColor: theme.messageTextColor, linkColor: theme.messageTextColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: italicTextFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: nil))
|
||||
composedReplyString = NSMutableAttributedString(attributedString: quoteString)
|
||||
}
|
||||
|
||||
if let cached = currentChatListSearchResult, cached.matches(text: composedString.string, searchQuery: searchQuery) {
|
||||
chatListSearchResult = cached
|
||||
} else {
|
||||
let (ranges, text) = findSubstringRanges(in: composedString.string, query: searchQuery)
|
||||
chatListSearchResult = CachedChatListSearchResult(text: text, searchQuery: searchQuery, resultRanges: ranges)
|
||||
}
|
||||
|
||||
if let composedReplyString {
|
||||
if let cached = currentChatListQuoteSearchResult, cached.matches(text: composedReplyString.string, searchQuery: searchQuery) {
|
||||
chatListQuoteSearchResult = cached
|
||||
} else {
|
||||
let (ranges, text) = findSubstringRanges(in: composedReplyString.string, query: searchQuery)
|
||||
chatListQuoteSearchResult = CachedChatListSearchResult(text: text, searchQuery: searchQuery, resultRanges: ranges)
|
||||
}
|
||||
} else {
|
||||
chatListQuoteSearchResult = nil
|
||||
}
|
||||
} else {
|
||||
chatListSearchResult = nil
|
||||
chatListQuoteSearchResult = nil
|
||||
}
|
||||
|
||||
if let chatListSearchResult = chatListSearchResult, let firstRange = chatListSearchResult.resultRanges.first {
|
||||
@ -2053,6 +2086,34 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
composedString = composedString.attributedSubstring(from: NSMakeRange(leftOrigin, composedString.length - leftOrigin)).mutableCopy() as! NSMutableAttributedString
|
||||
composedString.insert(NSAttributedString(string: "\u{2026}", attributes: [NSAttributedString.Key.font: textFont, NSAttributedString.Key.foregroundColor: theme.messageTextColor]), at: 0)
|
||||
}
|
||||
} else if var composedReplyString, let chatListQuoteSearchResult, let firstRange = chatListQuoteSearchResult.resultRanges.first {
|
||||
for range in chatListQuoteSearchResult.resultRanges {
|
||||
let stringRange = NSRange(range, in: chatListQuoteSearchResult.text)
|
||||
if stringRange.location >= 0 && stringRange.location + stringRange.length <= composedReplyString.length {
|
||||
var stringRange = stringRange
|
||||
if stringRange.location > composedReplyString.length {
|
||||
continue
|
||||
} else if stringRange.location + stringRange.length > composedReplyString.length {
|
||||
stringRange.length = composedReplyString.length - stringRange.location
|
||||
}
|
||||
composedReplyString.addAttribute(.foregroundColor, value: theme.messageHighlightedTextColor, range: stringRange)
|
||||
}
|
||||
}
|
||||
|
||||
let firstRangeOrigin = chatListQuoteSearchResult.text.distance(from: chatListQuoteSearchResult.text.startIndex, to: firstRange.lowerBound)
|
||||
if firstRangeOrigin > 24 {
|
||||
var leftOrigin: Int = 0
|
||||
(composedReplyString.string as NSString).enumerateSubstrings(in: NSMakeRange(0, firstRangeOrigin), options: [.byWords, .reverse]) { (str, range1, _, _) in
|
||||
let distanceFromEnd = firstRangeOrigin - range1.location
|
||||
if (distanceFromEnd > 12 || range1.location == 0) && leftOrigin == 0 {
|
||||
leftOrigin = range1.location
|
||||
}
|
||||
}
|
||||
composedReplyString = composedReplyString.attributedSubstring(from: NSMakeRange(leftOrigin, composedReplyString.length - leftOrigin)).mutableCopy() as! NSMutableAttributedString
|
||||
composedReplyString.insert(NSAttributedString(string: "\u{2026}", attributes: [NSAttributedString.Key.font: textFont, NSAttributedString.Key.foregroundColor: theme.messageTextColor]), at: 0)
|
||||
}
|
||||
|
||||
composedString = composedReplyString
|
||||
}
|
||||
|
||||
attributedText = composedString
|
||||
@ -2712,6 +2773,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
strongSelf.currentItemHeight = itemHeight
|
||||
strongSelf.cachedChatListText = chatListText
|
||||
strongSelf.cachedChatListSearchResult = chatListSearchResult
|
||||
strongSelf.cachedChatListQuoteSearchResult = chatListQuoteSearchResult
|
||||
strongSelf.cachedCustomTextEntities = customTextEntities
|
||||
strongSelf.onlineIsVoiceChat = onlineIsVoiceChat
|
||||
|
||||
|
@ -738,14 +738,21 @@ final class ChatSendMessageActionSheetControllerNode: ViewControllerTracingNode,
|
||||
var textFrame = self.textFieldFrame
|
||||
textFrame.origin = CGPoint(x: 13.0, y: 6.0 - UIScreenPixel)
|
||||
textFrame.size.height = self.textInputView.contentSize.height
|
||||
textFrame.size.width -= self.textInputView.textContainerInset.right
|
||||
if let textInputView = self.textInputView as? ChatInputTextView {
|
||||
textFrame.size.width -= textInputView.defaultTextContainerInset.right
|
||||
} else {
|
||||
textFrame.size.width -= self.textInputView.textContainerInset.right
|
||||
}
|
||||
|
||||
if self.textInputView.isRTL {
|
||||
textFrame.origin.x -= messageOriginDelta
|
||||
}
|
||||
|
||||
self.fromMessageTextNode.frame = textFrame
|
||||
self.fromMessageTextNode.updateLayout(size: textFrame.size)
|
||||
|
||||
self.toMessageTextNode.frame = textFrame
|
||||
self.toMessageTextNode.updateLayout(size: textFrame.size)
|
||||
}
|
||||
|
||||
@objc private func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
|
@ -738,7 +738,11 @@ public extension ContainedViewLayoutTransition {
|
||||
case .immediate:
|
||||
layer.removeAnimation(forKey: "position")
|
||||
layer.removeAnimation(forKey: "bounds")
|
||||
layer.frame = frame
|
||||
if let view = layer.delegate as? UIView {
|
||||
view.frame = frame
|
||||
} else {
|
||||
layer.frame = frame
|
||||
}
|
||||
if let completion = completion {
|
||||
completion(true)
|
||||
}
|
||||
|
@ -184,6 +184,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
|
||||
case chatReplyOptionsTip = 50
|
||||
case displayStoryInteractionGuide = 51
|
||||
case dismissedPremiumAppIconsBadge = 52
|
||||
case replyQuoteTextSelectionTip = 53
|
||||
|
||||
var key: ValueBoxKey {
|
||||
let v = ValueBoxKey(length: 4)
|
||||
@ -449,6 +450,10 @@ private struct ApplicationSpecificNoticeKeys {
|
||||
static func dismissedPremiumAppIconsBadge() -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.dismissedPremiumAppIconsBadge.key)
|
||||
}
|
||||
|
||||
static func replyQuoteTextSelectionTip() -> NoticeEntryKey {
|
||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.replyQuoteTextSelectionTip.key)
|
||||
}
|
||||
}
|
||||
|
||||
public struct ApplicationSpecificNotice {
|
||||
@ -924,6 +929,30 @@ public struct ApplicationSpecificNotice {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func getReplyQuoteTextSelectionTips(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
|
||||
return accountManager.transaction { transaction -> Int32 in
|
||||
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.replyQuoteTextSelectionTip())?.get(ApplicationSpecificCounterNotice.self) {
|
||||
return value.value
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func incrementReplyQuoteTextSelectionTips(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int32 = 1) -> Signal<Void, NoError> {
|
||||
return accountManager.transaction { transaction -> Void in
|
||||
var currentValue: Int32 = 0
|
||||
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.replyQuoteTextSelectionTip())?.get(ApplicationSpecificCounterNotice.self) {
|
||||
currentValue = value.value
|
||||
}
|
||||
currentValue += count
|
||||
|
||||
if let entry = CodableEntry(ApplicationSpecificCounterNotice(value: currentValue)) {
|
||||
transaction.setNotice(ApplicationSpecificNoticeKeys.replyQuoteTextSelectionTip(), entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static func getMessageViewsPrivacyTips(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
|
||||
return accountManager.transaction { transaction -> Int32 in
|
||||
|
@ -1488,8 +1488,10 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
}
|
||||
|
||||
var replyBackgroundFrame: CGRect?
|
||||
if let (replyInfoSize, replyInfoApply) = replyInfoApply {
|
||||
let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 11.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 9.0)), y: headersOffset + 8.0 + messageInfoSize.height), size: replyInfoSize)
|
||||
replyBackgroundFrame = replyInfoFrame
|
||||
|
||||
let replyInfoNode = replyInfoApply(replyInfoFrame.size, synchronousLoads, animation)
|
||||
if strongSelf.replyInfoNode == nil {
|
||||
@ -1504,8 +1506,8 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
strongSelf.replyInfoNode = nil
|
||||
}
|
||||
|
||||
if let replyBackgroundNode = strongSelf.replyBackgroundNode {
|
||||
replyBackgroundNode.frame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 10.0)) - 4.0, y: headersOffset + 6.0), size: CGSize(width: messageInfoSize.width + 8.0, height: messageInfoSize.height + 5.0))
|
||||
if let replyBackgroundNode = strongSelf.replyBackgroundNode, let replyBackgroundFrame {
|
||||
replyBackgroundNode.frame = replyBackgroundFrame
|
||||
|
||||
let cornerRadius = replyBackgroundNode.frame.height <= 22.0 ? replyBackgroundNode.frame.height / 2.0 : 8.0
|
||||
replyBackgroundNode.update(size: replyBackgroundNode.bounds.size, cornerRadius: cornerRadius, transition: .immediate)
|
||||
@ -1974,6 +1976,10 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
return .optionalAction({
|
||||
item.controllerInteraction.navigateToStory(item.message, attribute.storyId)
|
||||
})
|
||||
} else if let attribute = attribute as? QuotedReplyMessageAttribute {
|
||||
return .action({
|
||||
item.controllerInteraction.attemptedNavigationToPrivateQuote(attribute.peerId.flatMap { item.message.peers[$0] })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -843,14 +843,14 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode {
|
||||
if let current = self.backgroundView {
|
||||
backgroundView = current
|
||||
animation.animator.updateFrame(layer: backgroundView.layer, frame: backgroundFrame, completion: nil)
|
||||
backgroundView.update(size: backgroundFrame.size, primaryColor: mainColor, secondaryColor: secondaryColor, pattern: nil, animation: animation)
|
||||
} else {
|
||||
backgroundView = MessageInlineBlockBackgroundView()
|
||||
self.backgroundView = backgroundView
|
||||
backgroundView.frame = backgroundFrame
|
||||
self.transformContainer.view.insertSubview(backgroundView, at: 0)
|
||||
backgroundView.update(size: backgroundFrame.size, primaryColor: mainColor, secondaryColor: secondaryColor, pattern: nil, animation: .None)
|
||||
}
|
||||
|
||||
backgroundView.update(size: backgroundFrame.size, primaryColor: mainColor, secondaryColor: secondaryColor, pattern: nil, animation: animation)
|
||||
} else {
|
||||
if let backgroundView = self.backgroundView {
|
||||
self.backgroundView = nil
|
||||
|
@ -3955,6 +3955,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
return .action({
|
||||
item.controllerInteraction.navigateToStory(item.message, attribute.storyId)
|
||||
})
|
||||
} else if let attribute = attribute as? QuotedReplyMessageAttribute {
|
||||
return .action({
|
||||
item.controllerInteraction.attemptedNavigationToPrivateQuote(attribute.peerId.flatMap { item.message.peers[$0] })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4159,6 +4163,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI
|
||||
return .action({
|
||||
item.controllerInteraction.navigateToStory(item.message, attribute.storyId)
|
||||
})
|
||||
} else if let attribute = attribute as? QuotedReplyMessageAttribute {
|
||||
return .action({
|
||||
item.controllerInteraction.attemptedNavigationToPrivateQuote(attribute.peerId.flatMap { item.message.peers[$0] })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -761,8 +761,10 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureReco
|
||||
}
|
||||
}
|
||||
|
||||
var replyBackgroundFrame: CGRect?
|
||||
if let (replyInfoSize, replyInfoApply) = replyInfoApply {
|
||||
let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 11.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 9.0)), y: 8.0 + messageInfoSize.height), size: replyInfoSize)
|
||||
replyBackgroundFrame = replyInfoFrame
|
||||
|
||||
let replyInfoNode = replyInfoApply(replyInfoFrame.size, synchronousLoads, animation)
|
||||
if strongSelf.replyInfoNode == nil {
|
||||
@ -777,8 +779,8 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureReco
|
||||
strongSelf.replyInfoNode = nil
|
||||
}
|
||||
|
||||
if let replyBackgroundNode = strongSelf.replyBackgroundNode {
|
||||
replyBackgroundNode.frame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 10.0)) - 4.0, y: 6.0), size: CGSize(width: messageInfoSize.width + 8.0, height: messageInfoSize.height + 5.0))
|
||||
if let replyBackgroundNode = strongSelf.replyBackgroundNode, let replyBackgroundFrame {
|
||||
replyBackgroundNode.frame = replyBackgroundFrame
|
||||
|
||||
let cornerRadius = replyBackgroundNode.frame.height <= 22.0 ? replyBackgroundNode.frame.height / 2.0 : 8.0
|
||||
replyBackgroundNode.update(size: replyBackgroundNode.bounds.size, cornerRadius: cornerRadius, transition: .immediate)
|
||||
@ -963,6 +965,10 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureReco
|
||||
return .optionalAction({
|
||||
item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.quote?.text))
|
||||
})
|
||||
} else if let attribute = attribute as? QuotedReplyMessageAttribute {
|
||||
return .action({
|
||||
item.controllerInteraction.attemptedNavigationToPrivateQuote(attribute.peerId.flatMap { item.message.peers[$0] })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -999,8 +999,10 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
var replyBackgroundFrame: CGRect?
|
||||
if let (replyInfoSize, replyInfoApply) = replyInfoApply {
|
||||
let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (displayVideoFrame.maxX - width + 5.0) : (width - messageInfoSize.width - bubbleEdgeInset - 9.0 + 10.0)), y: 8.0 + messageInfoSize.height), size: replyInfoSize)
|
||||
replyBackgroundFrame = replyInfoFrame
|
||||
|
||||
let replyInfoNode = replyInfoApply(replyInfoFrame.size, false, animation)
|
||||
if strongSelf.replyInfoNode == nil {
|
||||
@ -1015,8 +1017,8 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
strongSelf.replyInfoNode = nil
|
||||
}
|
||||
|
||||
if let replyBackgroundNode = strongSelf.replyBackgroundNode {
|
||||
let replyBackgroundFrame = CGRect(origin: CGPoint(x: (!incoming ? (displayVideoFrame.maxX - width + 4.0) : (width - messageInfoSize.width - bubbleEdgeInset)) - 4.0, y: 6.0), size: CGSize(width: messageInfoSize.width + 8.0, height: messageInfoSize.height + 5.0))
|
||||
if let replyBackgroundNode = strongSelf.replyBackgroundNode, let replyBackgroundFrame {
|
||||
let replyBackgroundFrame = replyBackgroundFrame
|
||||
animation.animator.updateFrame(layer: replyBackgroundNode.layer, frame: replyBackgroundFrame, completion: nil)
|
||||
|
||||
let cornerRadius = replyBackgroundNode.frame.height <= 22.0 ? replyBackgroundNode.frame.height / 2.0 : 8.0
|
||||
@ -1294,6 +1296,9 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
} else if let attribute = attribute as? ReplyStoryAttribute {
|
||||
item.controllerInteraction.navigateToStory(item.message, attribute.storyId)
|
||||
return
|
||||
} else if let attribute = attribute as? QuotedReplyMessageAttribute {
|
||||
item.controllerInteraction.attemptedNavigationToPrivateQuote(attribute.peerId.flatMap { item.message.peers[$0] })
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +101,6 @@ public enum ChatMessageMerge: Int32 {
|
||||
|
||||
public protocol ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
|
||||
func updateSelectionState(animated: Bool)
|
||||
func updateSublayerTransformOffset(layer: CALayer, offset: CGPoint)
|
||||
}
|
||||
|
||||
public protocol ChatMessageItem: ListViewItem {
|
||||
|
@ -436,7 +436,7 @@ private let avatarFont = avatarPlaceholderFont(size: 16.0)
|
||||
|
||||
private let maxVideoLoopCount = 3
|
||||
|
||||
public final class ChatMessageAvatarHeaderNodeImpl: ListViewItemHeaderNode {
|
||||
public final class ChatMessageAvatarHeaderNodeImpl: ListViewItemHeaderNode, ChatMessageAvatarHeaderNode {
|
||||
private let context: AccountContext
|
||||
private var presentationData: ChatPresentationData
|
||||
private let controllerInteraction: ChatControllerInteraction
|
||||
|
@ -215,6 +215,9 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
||||
case .standalone:
|
||||
let serviceColor = serviceMessageColorComponents(theme: arguments.presentationData.theme.theme, wallpaper: arguments.presentationData.theme.wallpaper)
|
||||
titleColor = serviceColor.primaryText
|
||||
if dashSecondaryColor != nil {
|
||||
secondaryColor = .clear
|
||||
}
|
||||
|
||||
mainColor = serviceMessageColorComponents(chatTheme: arguments.presentationData.theme.theme.chat, wallpaper: arguments.presentationData.theme.wallpaper).primaryText
|
||||
dustColor = titleColor
|
||||
@ -457,7 +460,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
||||
imageDimensions = representation.dimensions.cgSize
|
||||
}
|
||||
break
|
||||
} else if let file = media as? TelegramMediaFile, file.isVideo && !file.isVideoSticker {
|
||||
} else if let file = media as? TelegramMediaFile, !file.isVideoSticker {
|
||||
updatedMediaReference = .message(message: MessageReference(message), media: file)
|
||||
|
||||
if let dimensions = file.dimensions {
|
||||
@ -527,14 +530,12 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
||||
var textCutoutWidth: CGFloat = 0.0
|
||||
if arguments.quote != nil || arguments.replyForward?.quote != nil {
|
||||
additionalTitleWidth += 10.0
|
||||
if case .bubble = arguments.type {
|
||||
maxTitleNumberOfLines = 2
|
||||
maxTextNumberOfLines = 5
|
||||
if imageTextInset != 0.0 {
|
||||
adjustedConstrainedTextSize.width += imageTextInset
|
||||
textCutout = TextNodeCutout(topLeft: CGSize(width: imageTextInset + 6.0, height: 10.0))
|
||||
textCutoutWidth = imageTextInset + 6.0
|
||||
}
|
||||
maxTitleNumberOfLines = 2
|
||||
maxTextNumberOfLines = 5
|
||||
if imageTextInset != 0.0 {
|
||||
adjustedConstrainedTextSize.width += imageTextInset
|
||||
textCutout = TextNodeCutout(topLeft: CGSize(width: imageTextInset + 6.0, height: 10.0))
|
||||
textCutoutWidth = imageTextInset + 6.0
|
||||
}
|
||||
}
|
||||
|
||||
@ -723,10 +724,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
||||
node.contentNode.view.insertSubview(node.backgroundView, at: 0)
|
||||
}
|
||||
|
||||
var backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: realSize.width, height: realSize.height + 2.0))
|
||||
if case .standalone = arguments.type {
|
||||
backgroundFrame.size.height -= 1.0
|
||||
}
|
||||
let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: realSize.width, height: realSize.height))
|
||||
|
||||
node.backgroundView.frame = backgroundFrame
|
||||
|
||||
@ -749,30 +747,6 @@ public class ChatMessageReplyInfoNode: ASDisplayNode {
|
||||
animation: animation
|
||||
)
|
||||
|
||||
let _ = secondaryColor
|
||||
/*if let secondaryColor {
|
||||
let lineDashView: UIImageView
|
||||
if let current = node.lineDashView {
|
||||
lineDashView = current
|
||||
} else {
|
||||
lineDashView = UIImageView(image: PresentationResourcesChat.chatReplyLineDashTemplateImage(arguments.presentationData.theme.theme, incoming: isIncoming))
|
||||
lineDashView.clipsToBounds = true
|
||||
node.lineDashView = lineDashView
|
||||
node.contentNode.view.addSubview(lineDashView)
|
||||
}
|
||||
lineDashView.tintColor = secondaryColor
|
||||
lineDashView.frame = CGRect(origin: .zero, size: CGSize(width: 12.0, height: backgroundFrame.height))
|
||||
lineDashView.layer.cornerRadius = 6.0
|
||||
if #available(iOS 13.0, *) {
|
||||
lineDashView.layer.cornerCurve = .continuous
|
||||
}
|
||||
} else {
|
||||
if let lineDashView = node.lineDashView {
|
||||
node.lineDashView = nil
|
||||
lineDashView.removeFromSuperview()
|
||||
}
|
||||
}*/
|
||||
|
||||
if arguments.quote != nil || arguments.replyForward?.quote != nil {
|
||||
let quoteIconView: UIImageView
|
||||
if let current = node.quoteIconView {
|
||||
|
@ -1068,8 +1068,10 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
}
|
||||
|
||||
var replyBackgroundFrame: CGRect?
|
||||
if let (replyInfoSize, replyInfoApply) = replyInfoApply {
|
||||
let replyInfoFrame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 11.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 9.0)), y: headersOffset + 8.0 + messageInfoSize.height), size: replyInfoSize)
|
||||
replyBackgroundFrame = replyInfoFrame
|
||||
|
||||
let replyInfoNode = replyInfoApply(replyInfoFrame.size, synchronousLoads, animation)
|
||||
if strongSelf.replyInfoNode == nil {
|
||||
@ -1084,8 +1086,8 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
strongSelf.replyInfoNode = nil
|
||||
}
|
||||
|
||||
if let replyBackgroundNode = strongSelf.replyBackgroundNode {
|
||||
replyBackgroundNode.frame = CGRect(origin: CGPoint(x: (!incoming ? (params.leftInset + layoutConstants.bubble.edgeInset + 10.0) : (params.width - params.rightInset - messageInfoSize.width - layoutConstants.bubble.edgeInset - 10.0)) - 4.0, y: headersOffset + 6.0), size: CGSize(width: messageInfoSize.width + 8.0, height: messageInfoSize.height + 5.0))
|
||||
if let replyBackgroundNode = strongSelf.replyBackgroundNode, let replyBackgroundFrame {
|
||||
replyBackgroundNode.frame = replyBackgroundFrame
|
||||
|
||||
let cornerRadius = replyBackgroundNode.frame.height <= 22.0 ? replyBackgroundNode.frame.height / 2.0 : 8.0
|
||||
replyBackgroundNode.update(size: replyBackgroundNode.bounds.size, cornerRadius: cornerRadius, transition: .immediate)
|
||||
@ -1341,6 +1343,10 @@ public class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
return .optionalAction({
|
||||
item.controllerInteraction.navigateToStory(item.message, attribute.storyId)
|
||||
})
|
||||
} else if let attribute = attribute as? QuotedReplyMessageAttribute {
|
||||
return .optionalAction({
|
||||
item.controllerInteraction.attemptedNavigationToPrivateQuote(attribute.peerId.flatMap { item.message.peers[$0] })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1108,13 +1108,18 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
let enableCopy = !item.associatedData.isCopyProtectionEnabled && !item.message.isCopyProtected()
|
||||
textSelectionNode.enableCopy = enableCopy
|
||||
|
||||
let enableQuote = true
|
||||
var enableQuote = !item.message.text.isEmpty
|
||||
var enableOtherActions = true
|
||||
if let subject = item.associatedData.subject, case let .messageOptions(_, _, info) = subject, case .reply = info {
|
||||
enableOtherActions = false
|
||||
} else if item.controllerInteraction.canSetupReply(item.message) == .reply {
|
||||
enableOtherActions = false
|
||||
}
|
||||
|
||||
if !item.controllerInteraction.canSendMessages() && !enableCopy {
|
||||
enableQuote = false
|
||||
}
|
||||
|
||||
textSelectionNode.enableQuote = enableQuote
|
||||
textSelectionNode.enableTranslate = enableOtherActions
|
||||
textSelectionNode.enableShare = enableOtherActions
|
||||
|
@ -519,6 +519,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}, setupReply: { _ in
|
||||
}, canSetupReply: { _ in
|
||||
return .none
|
||||
}, canSendMessages: {
|
||||
return false
|
||||
}, navigateToFirstDateMessage: { _, _ in
|
||||
}, requestRedeliveryOfFailedMessages: { _ in
|
||||
}, addContact: { _ in
|
||||
@ -571,6 +573,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}, dismissTextInput: {
|
||||
}, scrollToMessageId: { _ in
|
||||
}, navigateToStory: { _, _ in
|
||||
}, attemptedNavigationToPrivateQuote: { _ in
|
||||
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings,
|
||||
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: self.backgroundNode))
|
||||
self.controllerInteraction = controllerInteraction
|
||||
|
@ -155,6 +155,7 @@ public final class ChatControllerInteraction {
|
||||
public let openSearch: () -> Void
|
||||
public let setupReply: (MessageId) -> Void
|
||||
public let canSetupReply: (Message) -> ChatControllerInteractionSwipeAction
|
||||
public let canSendMessages: () -> Bool
|
||||
public let navigateToFirstDateMessage: (Int32, Bool) -> Void
|
||||
public let requestRedeliveryOfFailedMessages: (MessageId) -> Void
|
||||
public let addContact: (String) -> Void
|
||||
@ -203,6 +204,7 @@ public final class ChatControllerInteraction {
|
||||
public let dismissTextInput: () -> Void
|
||||
public let scrollToMessageId: (MessageIndex) -> Void
|
||||
public let navigateToStory: (Message, StoryId) -> Void
|
||||
public let attemptedNavigationToPrivateQuote: (Peer?) -> Void
|
||||
|
||||
public var canPlayMedia: Bool = false
|
||||
public var hiddenMedia: [MessageId: [Media]] = [:]
|
||||
@ -270,6 +272,7 @@ public final class ChatControllerInteraction {
|
||||
openSearch: @escaping () -> Void,
|
||||
setupReply: @escaping (MessageId) -> Void,
|
||||
canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction,
|
||||
canSendMessages: @escaping () -> Bool,
|
||||
navigateToFirstDateMessage: @escaping(Int32, Bool) ->Void,
|
||||
requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void,
|
||||
addContact: @escaping (String) -> Void,
|
||||
@ -317,6 +320,7 @@ public final class ChatControllerInteraction {
|
||||
dismissTextInput: @escaping () -> Void,
|
||||
scrollToMessageId: @escaping (MessageIndex) -> Void,
|
||||
navigateToStory: @escaping (Message, StoryId) -> Void,
|
||||
attemptedNavigationToPrivateQuote: @escaping (Peer?) -> Void,
|
||||
automaticMediaDownloadSettings: MediaAutoDownloadSettings,
|
||||
pollActionState: ChatInterfacePollActionState,
|
||||
stickerSettings: ChatInterfaceStickerSettings,
|
||||
@ -367,6 +371,7 @@ public final class ChatControllerInteraction {
|
||||
self.openSearch = openSearch
|
||||
self.setupReply = setupReply
|
||||
self.canSetupReply = canSetupReply
|
||||
self.canSendMessages = canSendMessages
|
||||
self.navigateToFirstDateMessage = navigateToFirstDateMessage
|
||||
self.requestRedeliveryOfFailedMessages = requestRedeliveryOfFailedMessages
|
||||
self.addContact = addContact
|
||||
@ -414,6 +419,7 @@ public final class ChatControllerInteraction {
|
||||
self.dismissTextInput = dismissTextInput
|
||||
self.scrollToMessageId = scrollToMessageId
|
||||
self.navigateToStory = navigateToStory
|
||||
self.attemptedNavigationToPrivateQuote = attemptedNavigationToPrivateQuote
|
||||
|
||||
self.automaticMediaDownloadSettings = automaticMediaDownloadSettings
|
||||
|
||||
|
@ -15,6 +15,7 @@ import ChatMessageTextBubbleContentNode
|
||||
import TextFormat
|
||||
import ChatMessageItemView
|
||||
import ChatMessageBubbleItemNode
|
||||
import TelegramNotices
|
||||
|
||||
private enum OptionsId: Hashable {
|
||||
case reply
|
||||
@ -286,12 +287,16 @@ private func generateChatReplyOptionItems(selfController: ChatControllerImpl, ch
|
||||
return .complete()
|
||||
}
|
||||
|
||||
let items = selfController.context.account.postbox.messagesAtIds([replySubject.messageId])
|
||||
let items = combineLatest(queue: .mainQueue(),
|
||||
selfController.context.account.postbox.messagesAtIds([replySubject.messageId]),
|
||||
ApplicationSpecificNotice.getReplyQuoteTextSelectionTips(accountManager: selfController.context.sharedContext.accountManager)
|
||||
)
|
||||
|> deliverOnMainQueue
|
||||
|> map { [weak selfController, weak chatController] messages -> [ContextMenuItem] in
|
||||
|> map { [weak selfController, weak chatController] messages, quoteTextSelectionTips -> ContextController.Items in
|
||||
guard let selfController, let chatController else {
|
||||
return []
|
||||
return ContextController.Items(content: .list([]))
|
||||
}
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
if replySubject.quote != nil {
|
||||
@ -398,18 +403,21 @@ private func generateChatReplyOptionItems(selfController: ChatControllerImpl, ch
|
||||
})))
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Reply in Another Chat", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Replace"), color: theme.contextMenu.primaryColor) }, action: { [weak selfController] c, f in
|
||||
f(.default)
|
||||
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
guard let replySubject = selfController.presentationInterfaceState.interfaceState.replyMessageSubject else {
|
||||
return
|
||||
}
|
||||
moveReplyMessageToAnotherChat(selfController: selfController, replySubject: replySubject)
|
||||
})))
|
||||
if selfController.presentationInterfaceState.copyProtectionEnabled || messages.first?.isCopyProtected() == true {
|
||||
} else {
|
||||
//TODO:localize
|
||||
items.append(.action(ContextMenuActionItem(text: "Reply in Another Chat", icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Replace"), color: theme.contextMenu.primaryColor) }, action: { [weak selfController] c, f in
|
||||
f(.default)
|
||||
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
guard let replySubject = selfController.presentationInterfaceState.interfaceState.replyMessageSubject else {
|
||||
return
|
||||
}
|
||||
moveReplyMessageToAnotherChat(selfController: selfController, replySubject: replySubject)
|
||||
})))
|
||||
}
|
||||
|
||||
items.append(.separator)
|
||||
|
||||
@ -441,14 +449,17 @@ private func generateChatReplyOptionItems(selfController: ChatControllerImpl, ch
|
||||
})))
|
||||
}
|
||||
|
||||
return items
|
||||
var tip: ContextController.Tip?
|
||||
if quoteTextSelectionTips <= 3, let message = messages.first, !message.text.isEmpty {
|
||||
tip = .quoteSelection
|
||||
}
|
||||
|
||||
return ContextController.Items(content: .list(items), tip: tip)
|
||||
}
|
||||
|
||||
var tip: ContextController.Tip?
|
||||
if "".isEmpty {
|
||||
tip = .quoteSelection
|
||||
}
|
||||
return items |> map { ContextController.Items(content: .list($0), tip: tip) }
|
||||
let _ = ApplicationSpecificNotice.incrementReplyQuoteTextSelectionTips(accountManager: selfController.context.sharedContext.accountManager).startStandalone()
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
private func chatReplyOptions(selfController: ChatControllerImpl, sourceNode: ASDisplayNode, getContextController: @escaping () -> ContextController?, selectionState: Promise<ChatControllerSubject.MessageOptionsInfo.SelectionState>) -> ContextController.Source? {
|
||||
|
@ -0,0 +1,91 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import ContextUI
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramNotices
|
||||
import ChatSendMessageActionUI
|
||||
import AccountContext
|
||||
|
||||
func chatMessageDisplaySendMessageOptions(selfController: ChatControllerImpl, node: ASDisplayNode, gesture: ContextGesture) {
|
||||
guard let peerId = selfController.chatLocation.peerId, let textInputView = selfController.chatDisplayNode.textInputView(), let layout = selfController.validLayout else {
|
||||
return
|
||||
}
|
||||
let previousSupportedOrientations = selfController.supportedOrientations
|
||||
if layout.size.width > layout.size.height {
|
||||
selfController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .landscape)
|
||||
} else {
|
||||
selfController.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
}
|
||||
|
||||
let _ = ApplicationSpecificNotice.incrementChatMessageOptionsTip(accountManager: selfController.context.sharedContext.accountManager, count: 4).startStandalone()
|
||||
|
||||
var hasEntityKeyboard = false
|
||||
if case .media = selfController.presentationInterfaceState.inputMode {
|
||||
hasEntityKeyboard = true
|
||||
}
|
||||
|
||||
let _ = (selfController.context.account.viewTracker.peerView(peerId)
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak selfController] peerView in
|
||||
guard let selfController, let peer = peerViewMainPeer(peerView) else {
|
||||
return
|
||||
}
|
||||
var sendWhenOnlineAvailable = false
|
||||
if let presence = peerView.peerPresences[peer.id] as? TelegramUserPresence, case let .present(until) = presence.status {
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
if currentTime > until {
|
||||
sendWhenOnlineAvailable = true
|
||||
}
|
||||
}
|
||||
if peer.id.namespace == Namespaces.Peer.CloudUser && peer.id.id._internalGetInt64Value() == 777000 {
|
||||
sendWhenOnlineAvailable = false
|
||||
}
|
||||
|
||||
if sendWhenOnlineAvailable {
|
||||
let _ = ApplicationSpecificNotice.incrementSendWhenOnlineTip(accountManager: selfController.context.sharedContext.accountManager, count: 4).startStandalone()
|
||||
}
|
||||
|
||||
let controller = ChatSendMessageActionSheetController(context: selfController.context, updatedPresentationData: selfController.updatedPresentationData, peerId: selfController.presentationInterfaceState.chatLocation.peerId, forwardMessageIds: selfController.presentationInterfaceState.interfaceState.forwardMessageIds, hasEntityKeyboard: hasEntityKeyboard, gesture: gesture, sourceSendButton: node, textInputView: textInputView, canSendWhenOnline: sendWhenOnlineAvailable, completion: { [weak selfController] in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
selfController.supportedOrientations = previousSupportedOrientations
|
||||
}, sendMessage: { [weak selfController] mode in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
switch mode {
|
||||
case .generic:
|
||||
selfController.controllerInteraction?.sendCurrentMessage(false)
|
||||
case .silently:
|
||||
selfController.controllerInteraction?.sendCurrentMessage(true)
|
||||
case .whenOnline:
|
||||
selfController.chatDisplayNode.sendCurrentMessage(scheduleTime: scheduleWhenOnlineTimestamp) { [weak selfController] in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
selfController.updateChatPresentationInterfaceState(animated: true, interactive: false, saveInterfaceState: selfController.presentationInterfaceState.subject != .scheduledMessages, {
|
||||
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedForwardMessageIds(nil).withUpdatedForwardOptionsState(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))) }
|
||||
})
|
||||
selfController.openScheduledMessages()
|
||||
}
|
||||
}
|
||||
}, schedule: { [weak selfController] in
|
||||
guard let selfController else {
|
||||
return
|
||||
}
|
||||
selfController.controllerInteraction?.scheduleCurrentMessage()
|
||||
})
|
||||
controller.emojiViewProvider = selfController.chatDisplayNode.textInputPanelNode?.emojiViewProvider
|
||||
selfController.sendMessageActionsController = controller
|
||||
if layout.isNonExclusive {
|
||||
selfController.present(controller, in: .window(.root))
|
||||
} else {
|
||||
selfController.presentInGlobalOverlay(controller, with: nil)
|
||||
}
|
||||
})
|
||||
}
|
@ -3448,7 +3448,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
})
|
||||
}, openSearch: {
|
||||
}, setupReply: { [weak self] messageId in
|
||||
self?.interfaceInteraction?.setupReplyMessage(messageId, { _, _ in })
|
||||
self?.interfaceInteraction?.setupReplyMessage(messageId, { _, f in f() })
|
||||
}, canSetupReply: { [weak self] message in
|
||||
if !message.flags.contains(.Incoming) {
|
||||
if !message.flags.intersection([.Failed, .Sending, .Unsent]).isEmpty {
|
||||
@ -3471,6 +3471,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
return .none
|
||||
}, canSendMessages: { [weak self] in
|
||||
guard let self else {
|
||||
return false
|
||||
}
|
||||
return canSendMessagesToChat(self.presentationInterfaceState)
|
||||
}, navigateToFirstDateMessage: { [weak self] timestamp, alreadyThere in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -5011,6 +5016,24 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
)
|
||||
self.push(storyContainerScreen)
|
||||
})
|
||||
}, attemptedNavigationToPrivateQuote: { [weak self] peer in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
//TODO:localize
|
||||
let text: String
|
||||
if let peer = peer as? TelegramChannel {
|
||||
if case .broadcast = peer.info {
|
||||
text = "This quote is from a private channel"
|
||||
} else {
|
||||
text = "This quote is from a private group"
|
||||
}
|
||||
} else if peer is TelegramGroup {
|
||||
text = "This quote is from a private group"
|
||||
} else {
|
||||
text = "This quote is from a private chat"
|
||||
}
|
||||
self.controllerInteraction?.displayUndo(.info(title: nil, text: text, timeout: nil))
|
||||
}, automaticMediaDownloadSettings: self.automaticMediaDownloadSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: self.stickerSettings, presentationContext: ChatPresentationContext(context: context, backgroundNode: self.chatBackgroundNode))
|
||||
controllerInteraction.enableFullTranslucency = context.sharedContext.energyUsageSettings.fullTranslucency
|
||||
|
||||
@ -10547,78 +10570,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
strongSelf.window?.presentInGlobalOverlay(slowmodeTooltipController)
|
||||
}, displaySendMessageOptions: { [weak self] node, gesture in
|
||||
if let strongSelf = self, let peerId = strongSelf.chatLocation.peerId, let textInputView = strongSelf.chatDisplayNode.textInputView(), let layout = strongSelf.validLayout {
|
||||
let previousSupportedOrientations = strongSelf.supportedOrientations
|
||||
if layout.size.width > layout.size.height {
|
||||
strongSelf.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .landscape)
|
||||
} else {
|
||||
strongSelf.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
}
|
||||
|
||||
let _ = ApplicationSpecificNotice.incrementChatMessageOptionsTip(accountManager: strongSelf.context.sharedContext.accountManager, count: 4).startStandalone()
|
||||
|
||||
var hasEntityKeyboard = false
|
||||
if case .media = strongSelf.presentationInterfaceState.inputMode {
|
||||
hasEntityKeyboard = true
|
||||
}
|
||||
|
||||
let _ = (strongSelf.context.account.viewTracker.peerView(peerId)
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).startStandalone(next: { [weak self] peerView in
|
||||
guard let strongSelf = self, let peer = peerViewMainPeer(peerView) else {
|
||||
return
|
||||
}
|
||||
var sendWhenOnlineAvailable = false
|
||||
if let presence = peerView.peerPresences[peer.id] as? TelegramUserPresence, case let .present(until) = presence.status {
|
||||
let currentTime = Int32(CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970)
|
||||
if currentTime > until {
|
||||
sendWhenOnlineAvailable = true
|
||||
}
|
||||
}
|
||||
if peer.id.namespace == Namespaces.Peer.CloudUser && peer.id.id._internalGetInt64Value() == 777000 {
|
||||
sendWhenOnlineAvailable = false
|
||||
}
|
||||
|
||||
if sendWhenOnlineAvailable {
|
||||
let _ = ApplicationSpecificNotice.incrementSendWhenOnlineTip(accountManager: strongSelf.context.sharedContext.accountManager, count: 4).startStandalone()
|
||||
}
|
||||
|
||||
let controller = ChatSendMessageActionSheetController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: strongSelf.presentationInterfaceState.chatLocation.peerId, forwardMessageIds: strongSelf.presentationInterfaceState.interfaceState.forwardMessageIds, hasEntityKeyboard: hasEntityKeyboard, gesture: gesture, sourceSendButton: node, textInputView: textInputView, canSendWhenOnline: sendWhenOnlineAvailable, completion: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.supportedOrientations = previousSupportedOrientations
|
||||
}
|
||||
}, sendMessage: { [weak self] mode in
|
||||
if let strongSelf = self {
|
||||
switch mode {
|
||||
case .generic:
|
||||
strongSelf.controllerInteraction?.sendCurrentMessage(false)
|
||||
case .silently:
|
||||
strongSelf.controllerInteraction?.sendCurrentMessage(true)
|
||||
case .whenOnline:
|
||||
strongSelf.chatDisplayNode.sendCurrentMessage(scheduleTime: scheduleWhenOnlineTimestamp) { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, saveInterfaceState: strongSelf.presentationInterfaceState.subject != .scheduledMessages, {
|
||||
$0.updatedInterfaceState { $0.withUpdatedReplyMessageSubject(nil).withUpdatedForwardMessageIds(nil).withUpdatedForwardOptionsState(nil).withUpdatedComposeInputState(ChatTextInputState(inputText: NSAttributedString(string: ""))) }
|
||||
})
|
||||
strongSelf.openScheduledMessages()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, schedule: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.controllerInteraction?.scheduleCurrentMessage()
|
||||
}
|
||||
})
|
||||
controller.emojiViewProvider = strongSelf.chatDisplayNode.textInputPanelNode?.emojiViewProvider
|
||||
strongSelf.sendMessageActionsController = controller
|
||||
if layout.isNonExclusive {
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
} else {
|
||||
strongSelf.presentInGlobalOverlay(controller, with: nil)
|
||||
}
|
||||
})
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
chatMessageDisplaySendMessageOptions(selfController: self, node: node, gesture: gesture)
|
||||
}, openScheduledMessages: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.openScheduledMessages()
|
||||
|
@ -649,6 +649,10 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
}
|
||||
}
|
||||
|
||||
if !canSendMessagesToChat(chatPresentationInterfaceState) && (chatPresentationInterfaceState.copyProtectionEnabled || message.isCopyProtected()) {
|
||||
canReply = false
|
||||
}
|
||||
|
||||
for media in messages[0].media {
|
||||
if let story = media as? TelegramMediaStory {
|
||||
if let story = message.associatedStories[story.storyId], story.data.isEmpty {
|
||||
|
@ -123,6 +123,8 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
||||
}, setupReply: { _ in
|
||||
}, canSetupReply: { _ in
|
||||
return .none
|
||||
}, canSendMessages: {
|
||||
return false
|
||||
}, navigateToFirstDateMessage: { _, _ in
|
||||
}, requestRedeliveryOfFailedMessages: { _ in
|
||||
}, addContact: { _ in
|
||||
@ -172,6 +174,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
||||
}, dismissTextInput: {
|
||||
}, scrollToMessageId: { _ in
|
||||
}, navigateToStory: { _, _ in
|
||||
}, attemptedNavigationToPrivateQuote: { _ in
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings, pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: nil))
|
||||
|
||||
self.dimNode = ASDisplayNode()
|
||||
|
@ -2874,6 +2874,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
}, setupReply: { _ in
|
||||
}, canSetupReply: { _ in
|
||||
return .none
|
||||
}, canSendMessages: {
|
||||
return false
|
||||
}, navigateToFirstDateMessage: { _, _ in
|
||||
}, requestRedeliveryOfFailedMessages: { _ in
|
||||
}, addContact: { _ in
|
||||
@ -2923,6 +2925,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
}, dismissTextInput: {
|
||||
}, scrollToMessageId: { _ in
|
||||
}, navigateToStory: { _, _ in
|
||||
}, attemptedNavigationToPrivateQuote: { _ 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
|
||||
|
@ -1512,6 +1512,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
}, presentGlobalOverlayController: { _, _ in }, callPeer: { _, _ in }, longTap: { _, _ in }, openCheckoutOrReceipt: { _ in }, openSearch: { }, setupReply: { _ in
|
||||
}, canSetupReply: { _ in
|
||||
return .none
|
||||
}, canSendMessages: {
|
||||
return false
|
||||
}, navigateToFirstDateMessage: { _, _ in
|
||||
}, requestRedeliveryOfFailedMessages: { _ in
|
||||
}, addContact: { _ in
|
||||
@ -1561,6 +1563,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
}, dismissTextInput: {
|
||||
}, scrollToMessageId: { _ in
|
||||
}, navigateToStory: { _, _ in
|
||||
}, attemptedNavigationToPrivateQuote: { _ in
|
||||
}, automaticMediaDownloadSettings: MediaAutoDownloadSettings.defaultSettings,
|
||||
pollActionState: ChatInterfacePollActionState(), stickerSettings: ChatInterfaceStickerSettings(), presentationContext: ChatPresentationContext(context: context, backgroundNode: backgroundNode as? WallpaperBackgroundNode))
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user