mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 21:45:19 +00:00
Comments [WIP]
This commit is contained in:
parent
00642ecbe7
commit
d94d771feb
@ -5813,3 +5813,5 @@ Any member of this group will be able to see messages in the channel.";
|
||||
"Channel.CommentsGroup.HeaderGroupSet" = "%@ is linking the group as it's discussion board.";
|
||||
|
||||
"RepliesChat.DescriptionText" = "This chat helps you keep track of replies to your comments in Channels.";
|
||||
|
||||
"Channel.DiscussionMessageUnavailable" = "Sorry, this post has been removed from the discussion group.";
|
||||
|
@ -10,11 +10,6 @@ public class AnimatedCountLabelNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
public enum Segment: Equatable {
|
||||
public enum Key: Hashable {
|
||||
case number
|
||||
case text(Int)
|
||||
}
|
||||
|
||||
case number(Int, NSAttributedString)
|
||||
case text(Int, NSAttributedString)
|
||||
|
||||
@ -34,10 +29,37 @@ public class AnimatedCountLabelNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate enum ResolvedSegment: Equatable {
|
||||
public enum Key: Hashable {
|
||||
case number(Int)
|
||||
case text(Int)
|
||||
}
|
||||
|
||||
case number(id: Int, value: Int, string: NSAttributedString)
|
||||
case text(id: Int, string: NSAttributedString)
|
||||
|
||||
public static func ==(lhs: ResolvedSegment, rhs: ResolvedSegment) -> Bool {
|
||||
switch lhs {
|
||||
case let .number(id, number, text):
|
||||
if case let .number(rhsId, rhsNumber, rhsText) = rhs, id == rhsId, number == rhsNumber, text.isEqual(to: rhsText) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .text(index, text):
|
||||
if case let .text(rhsIndex, rhsText) = rhs, index == rhsIndex, text.isEqual(to: rhsText) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public var attributedText: NSAttributedString {
|
||||
switch self {
|
||||
case let .number(_, text):
|
||||
case let .number(_, _, text):
|
||||
return text
|
||||
case let .text(_, text):
|
||||
return text
|
||||
@ -46,28 +68,53 @@ public class AnimatedCountLabelNode: ASDisplayNode {
|
||||
|
||||
var key: Key {
|
||||
switch self {
|
||||
case .number:
|
||||
return .number
|
||||
case let .number(id, _, _):
|
||||
return .number(id)
|
||||
case let .text(index, _):
|
||||
return .text(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var resolvedSegments: [Segment.Key: (Segment, TextNode)] = [:]
|
||||
fileprivate var resolvedSegments: [ResolvedSegment.Key: (ResolvedSegment, TextNode)] = [:]
|
||||
|
||||
override public init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
public func asyncLayout() -> (CGSize, [Segment]) -> (Layout, (Bool) -> Void) {
|
||||
var segmentLayouts: [Segment.Key: (TextNodeLayoutArguments) -> (TextNodeLayout, () -> TextNode)] = [:]
|
||||
var segmentLayouts: [ResolvedSegment.Key: (TextNodeLayoutArguments) -> (TextNodeLayout, () -> TextNode)] = [:]
|
||||
let wasEmpty = self.resolvedSegments.isEmpty
|
||||
for (segmentKey, segmentAndTextNode) in self.resolvedSegments {
|
||||
segmentLayouts[segmentKey] = TextNode.asyncLayout(segmentAndTextNode.1)
|
||||
}
|
||||
|
||||
return { [weak self] size, segments in
|
||||
return { [weak self] size, initialSegments in
|
||||
var segments: [ResolvedSegment] = []
|
||||
loop: for segment in initialSegments {
|
||||
switch segment {
|
||||
case let .number(value, string):
|
||||
if string.string.isEmpty {
|
||||
continue loop
|
||||
}
|
||||
let attributes = string.attributes(at: 0, longestEffectiveRange: nil, in: NSRange(location: 0, length: 1))
|
||||
|
||||
var remainingValue = value
|
||||
|
||||
while true {
|
||||
let digitValue = remainingValue % 10
|
||||
|
||||
segments.insert(.number(id: 1000 - segments.count, value: value, string: NSAttributedString(string: "\(digitValue)", attributes: attributes)), at: 0)
|
||||
remainingValue /= 10
|
||||
if remainingValue == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
case let .text(id, string):
|
||||
segments.append(.text(id: id, string: string))
|
||||
}
|
||||
}
|
||||
|
||||
for segment in segments {
|
||||
if segmentLayouts[segment.key] == nil {
|
||||
segmentLayouts[segment.key] = TextNode.asyncLayout(nil)
|
||||
@ -77,17 +124,17 @@ public class AnimatedCountLabelNode: ASDisplayNode {
|
||||
var contentSize = CGSize()
|
||||
var remainingSize = size
|
||||
|
||||
var calculatedSegments: [Segment.Key: (TextNodeLayout, CGFloat, () -> TextNode)] = [:]
|
||||
var calculatedSegments: [ResolvedSegment.Key: (TextNodeLayout, CGFloat, () -> TextNode)] = [:]
|
||||
var isTruncated = false
|
||||
|
||||
var validKeys: [Segment.Key] = []
|
||||
var validKeys: [ResolvedSegment.Key] = []
|
||||
|
||||
for segment in segments {
|
||||
validKeys.append(segment.key)
|
||||
let (layout, apply) = segmentLayouts[segment.key]!(TextNodeLayoutArguments(attributedString: segment.attributedText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: remainingSize, alignment: .left, lineSpacing: 0.0, cutout: nil, insets: UIEdgeInsets(), lineColor: nil, textShadowColor: nil, textStroke: nil))
|
||||
var effectiveSegmentWidth = layout.size.width
|
||||
if case .number = segment {
|
||||
effectiveSegmentWidth = ceil(effectiveSegmentWidth / 2.0) * 2.0
|
||||
//effectiveSegmentWidth = ceil(effectiveSegmentWidth / 2.0) * 2.0
|
||||
} else if segment.attributedText.string == " " {
|
||||
effectiveSegmentWidth = max(effectiveSegmentWidth, 4.0)
|
||||
}
|
||||
@ -115,7 +162,7 @@ public class AnimatedCountLabelNode: ASDisplayNode {
|
||||
for segment in segments {
|
||||
var animation: (CGFloat, Double)?
|
||||
if let (currentSegment, currentTextNode) = strongSelf.resolvedSegments[segment.key] {
|
||||
if case let .number(currentValue, _) = currentSegment, case let .number(updatedValue, _) = segment, animated, !wasEmpty, currentValue != updatedValue, let snapshot = currentTextNode.layer.snapshotContentTree() {
|
||||
if case let .number(_, currentValue, currentString) = currentSegment, case let .number(_, updatedValue, updatedString) = segment, animated, !wasEmpty, currentValue != updatedValue, currentString.string != updatedString.string, let snapshot = currentTextNode.layer.snapshotContentTree() {
|
||||
let offsetY: CGFloat
|
||||
if currentValue > updatedValue {
|
||||
offsetY = -floor(currentTextNode.bounds.height * 0.6)
|
||||
@ -164,7 +211,7 @@ public class AnimatedCountLabelNode: ASDisplayNode {
|
||||
strongSelf.resolvedSegments[segment.key] = (segment, textNode)
|
||||
}
|
||||
|
||||
var removeKeys: [Segment.Key] = []
|
||||
var removeKeys: [ResolvedSegment.Key] = []
|
||||
for key in strongSelf.resolvedSegments.keys {
|
||||
if !validKeys.contains(key) {
|
||||
removeKeys.append(key)
|
||||
|
@ -37,8 +37,6 @@ private func isLocked(passcodeSettings: PresentationPasscodeSettings, state: Loc
|
||||
}
|
||||
|
||||
private func getCoveringViewSnaphot(window: Window1) -> UIImage? {
|
||||
print("getCoveringViewSnaphot")
|
||||
|
||||
let scale: CGFloat = 0.5
|
||||
let unscaledSize = window.hostView.containerView.frame.size
|
||||
return generateImage(CGSize(width: floor(unscaledSize.width * scale), height: floor(unscaledSize.height * scale)), rotatedContext: { size, context in
|
||||
|
@ -229,6 +229,7 @@ static int32_t fixedTimeDifferenceValue = 0;
|
||||
_useTempAuthKeys = useTempAuthKeys;
|
||||
#if DEBUG
|
||||
_tempKeyExpiration = 1 * 60 * 60;
|
||||
_tempKeyExpiration = 5;
|
||||
#else
|
||||
_tempKeyExpiration = 24 * 60 * 60;
|
||||
#endif
|
||||
@ -380,12 +381,12 @@ static int32_t fixedTimeDifferenceValue = 0;
|
||||
if (datacenterAuthInfoById != nil) {
|
||||
_datacenterAuthInfoById = [[NSMutableDictionary alloc] initWithDictionary:datacenterAuthInfoById];
|
||||
#if DEBUG
|
||||
/*NSArray<NSNumber *> *keys = [_datacenterAuthInfoById allKeys];
|
||||
NSArray<NSNumber *> *keys = [_datacenterAuthInfoById allKeys];
|
||||
for (NSNumber *key in keys) {
|
||||
if (parseAuthInfoMapKeyInteger(key).selector != MTDatacenterAuthInfoSelectorPersistent) {
|
||||
[_datacenterAuthInfoById removeObjectForKey:key];
|
||||
}
|
||||
}*/
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -402,7 +402,13 @@ typedef enum {
|
||||
arc4random_buf(&random, 1);
|
||||
[dataWithHash appendBytes:&random length:1];
|
||||
}
|
||||
#if DEBUG
|
||||
assert(dataWithHash.length == 255);
|
||||
#endif
|
||||
NSData *encryptedData = MTRsaEncrypt(_encryptionProvider, [publicKey objectForKey:@"key"], dataWithHash);
|
||||
if (MTLogEnabled()) {
|
||||
MTLog(@"[MTDatacenterAuthMessageService#%p encryptedData.length = %d]", self, encryptedData.length);
|
||||
}
|
||||
if (encryptedData.length < 256)
|
||||
{
|
||||
NSMutableData *newEncryptedData = [[NSMutableData alloc] init];
|
||||
@ -415,6 +421,9 @@ typedef enum {
|
||||
[newEncryptedData appendData:encryptedData];
|
||||
encryptedData = newEncryptedData;
|
||||
}
|
||||
#if DEBUG
|
||||
assert(encryptedData.length == 256);
|
||||
#endif
|
||||
|
||||
_dhEncryptedData = encryptedData;
|
||||
} else {
|
||||
|
@ -6,6 +6,7 @@ import TelegramApi
|
||||
|
||||
private struct DiscussionMessage {
|
||||
public var messageId: MessageId
|
||||
public var channelMessageId: MessageId?
|
||||
public var isChannelPost: Bool
|
||||
public var maxMessage: MessageId?
|
||||
public var maxReadIncomingMessageId: MessageId?
|
||||
@ -143,6 +144,14 @@ private class ReplyThreadHistoryContextImpl {
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
var channelMessageId: MessageId?
|
||||
for attribute in topMessage.attributes {
|
||||
if let attribute = attribute as? SourceReferenceMessageAttribute {
|
||||
channelMessageId = attribute.messageId
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var peers: [Peer] = []
|
||||
var peerPresences: [PeerId: PeerPresence] = [:]
|
||||
|
||||
@ -188,6 +197,7 @@ private class ReplyThreadHistoryContextImpl {
|
||||
|
||||
return .single(DiscussionMessage(
|
||||
messageId: parsedIndex.id,
|
||||
channelMessageId: channelMessageId,
|
||||
isChannelPost: isChannelPost,
|
||||
maxMessage: resolvedMaxMessage,
|
||||
maxReadIncomingMessageId: readInboxMaxId.flatMap { readMaxId in
|
||||
@ -388,6 +398,7 @@ public struct ChatReplyThreadMessage: Equatable {
|
||||
}
|
||||
|
||||
public var messageId: MessageId
|
||||
public var channelMessageId: MessageId?
|
||||
public var isChannelPost: Bool
|
||||
public var maxMessage: MessageId?
|
||||
public var maxReadIncomingMessageId: MessageId?
|
||||
@ -396,8 +407,9 @@ public struct ChatReplyThreadMessage: Equatable {
|
||||
public var initialAnchor: Anchor
|
||||
public var isNotAvailable: Bool
|
||||
|
||||
fileprivate init(messageId: MessageId, isChannelPost: Bool, maxMessage: MessageId?, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?, initialFilledHoles: IndexSet, initialAnchor: Anchor, isNotAvailable: Bool) {
|
||||
fileprivate init(messageId: MessageId, channelMessageId: MessageId?, isChannelPost: Bool, maxMessage: MessageId?, maxReadIncomingMessageId: MessageId?, maxReadOutgoingMessageId: MessageId?, initialFilledHoles: IndexSet, initialAnchor: Anchor, isNotAvailable: Bool) {
|
||||
self.messageId = messageId
|
||||
self.channelMessageId = channelMessageId
|
||||
self.isChannelPost = isChannelPost
|
||||
self.maxMessage = maxMessage
|
||||
self.maxReadIncomingMessageId = maxReadIncomingMessageId
|
||||
@ -445,6 +457,14 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
|
||||
return nil
|
||||
}
|
||||
|
||||
var channelMessageId: MessageId?
|
||||
for attribute in topMessage.attributes {
|
||||
if let attribute = attribute as? SourceReferenceMessageAttribute {
|
||||
channelMessageId = attribute.messageId
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var peers: [Peer] = []
|
||||
var peerPresences: [PeerId: PeerPresence] = [:]
|
||||
|
||||
@ -490,6 +510,7 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
|
||||
|
||||
return DiscussionMessage(
|
||||
messageId: parsedIndex.id,
|
||||
channelMessageId: channelMessageId,
|
||||
isChannelPost: isChannelPost,
|
||||
maxMessage: resolvedMaxMessage,
|
||||
maxReadIncomingMessageId: readInboxMaxId.flatMap { readMaxId in
|
||||
@ -530,6 +551,7 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
|
||||
|
||||
return DiscussionMessage(
|
||||
messageId: discussionMessageId,
|
||||
channelMessageId: messageId,
|
||||
isChannelPost: true,
|
||||
maxMessage: replyInfo.maxMessageId,
|
||||
maxReadIncomingMessageId: replyInfo.maxReadIncomingMessageId,
|
||||
@ -730,6 +752,7 @@ public func fetchChannelReplyThreadMessage(account: Account, messageId: MessageI
|
||||
|
||||
return .single(ChatReplyThreadMessage(
|
||||
messageId: discussionMessage.messageId,
|
||||
channelMessageId: discussionMessage.channelMessageId,
|
||||
isChannelPost: discussionMessage.isChannelPost,
|
||||
maxMessage: discussionMessage.maxMessage,
|
||||
maxReadIncomingMessageId: discussionMessage.maxReadIncomingMessageId,
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -289,6 +289,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
private var stickerSettingsDisposable: Disposable?
|
||||
|
||||
private var applicationInForegroundDisposable: Disposable?
|
||||
private var applicationInFocusDisposable: Disposable?
|
||||
|
||||
private var checkedPeerChatServiceActions = false
|
||||
|
||||
@ -3071,6 +3072,17 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
})
|
||||
|
||||
if case let .peer(peerId) = chatLocation, peerId.namespace == Namespaces.Peer.SecretChat {
|
||||
self.applicationInFocusDisposable = (context.sharedContext.applicationBindings.applicationIsActive
|
||||
|> distinctUntilChanged
|
||||
|> deliverOn(Queue.mainQueue())).start(next: { [weak self] value in
|
||||
guard let strongSelf = self, strongSelf.isNodeLoaded else {
|
||||
return
|
||||
}
|
||||
strongSelf.chatDisplayNode.updateIsBlurred(!value)
|
||||
})
|
||||
}
|
||||
|
||||
self.canReadHistoryDisposable = (combineLatest(context.sharedContext.applicationBindings.applicationInForeground, self.canReadHistory.get()) |> map { a, b in
|
||||
return a && b
|
||||
} |> deliverOnMainQueue).start(next: { [weak self] value in
|
||||
@ -3139,6 +3151,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self.presentationDataDisposable?.dispose()
|
||||
self.searchDisposable?.dispose()
|
||||
self.applicationInForegroundDisposable?.dispose()
|
||||
self.applicationInFocusDisposable?.dispose()
|
||||
self.canReadHistoryDisposable?.dispose()
|
||||
self.networkStateDisposable?.dispose()
|
||||
self.chatAdditionalDataDisposable.dispose()
|
||||
@ -8461,7 +8474,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}, error: { _ in
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
present(textAlertController(context: context, title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
present(textAlertController(context: context, title: nil, text: presentationData.strings.Channel_DiscussionMessageUnavailable, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil)
|
||||
})
|
||||
|
||||
cancelImpl = { [weak statusController] in
|
||||
|
@ -14,6 +14,7 @@ import TelegramNotices
|
||||
import ReactionSelectionNode
|
||||
import TelegramUniversalVideoContent
|
||||
import ChatInterfaceState
|
||||
import FastBlur
|
||||
|
||||
final class VideoNavigationControllerDropContentItem: NavigationControllerDropContentItem {
|
||||
let itemNode: OverlayMediaItemNode
|
||||
@ -309,6 +310,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
let backgroundNode: WallpaperBackgroundNode
|
||||
let backgroundImageDisposable = MetaDisposable()
|
||||
let historyNode: ChatHistoryListNode
|
||||
var blurredHistoryNode: ASImageNode?
|
||||
let reactionContainerNode: ReactionSelectionParentNode
|
||||
let historyNodeContainer: ASDisplayNode
|
||||
let loadingNode: ChatLoadingNode
|
||||
@ -1220,6 +1222,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
transition.updateFrame(node: self.historyNodeContainer, frame: contentBounds)
|
||||
transition.updateBounds(node: self.historyNode, bounds: CGRect(origin: CGPoint(), size: contentBounds.size))
|
||||
transition.updatePosition(node: self.historyNode, position: CGPoint(x: contentBounds.size.width / 2.0, y: contentBounds.size.height / 2.0))
|
||||
if let blurredHistoryNode = self.blurredHistoryNode {
|
||||
transition.updateFrame(node: blurredHistoryNode, frame: contentBounds)
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.loadingNode, frame: contentBounds)
|
||||
|
||||
@ -2933,4 +2938,39 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
self.updateHasEmbeddedTitleContent?()
|
||||
}
|
||||
}
|
||||
|
||||
func updateIsBlurred(_ isBlurred: Bool) {
|
||||
if isBlurred {
|
||||
if self.blurredHistoryNode == nil {
|
||||
let unscaledSize = self.historyNode.frame.size
|
||||
let image = generateImage(CGSize(width: floor(unscaledSize.width), height: floor(unscaledSize.height)), opaque: true, scale: 1.0, rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
UIGraphicsPushContext(context)
|
||||
|
||||
let backgroundFrame = self.backgroundNode.view.convert(self.backgroundNode.bounds, to: self.historyNode.supernode?.view)
|
||||
self.backgroundNode.view.drawHierarchy(in: backgroundFrame, afterScreenUpdates: false)
|
||||
|
||||
context.translateBy(x: size.width / 2.0, y: size.height / 2.0)
|
||||
context.scaleBy(x: -1.0, y: -1.0)
|
||||
context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0)
|
||||
|
||||
self.historyNode.view.drawHierarchy(in: CGRect(origin: CGPoint(), size: unscaledSize), afterScreenUpdates: false)
|
||||
|
||||
UIGraphicsPopContext()
|
||||
}).flatMap(applyScreenshotEffectToImage)
|
||||
let blurredHistoryNode = ASImageNode()
|
||||
blurredHistoryNode.image = image
|
||||
blurredHistoryNode.frame = self.historyNode.frame
|
||||
self.blurredHistoryNode = blurredHistoryNode
|
||||
self.historyNode.supernode?.insertSubnode(blurredHistoryNode, aboveSubnode: self.historyNode)
|
||||
}
|
||||
} else {
|
||||
if let blurredHistoryNode = self.blurredHistoryNode {
|
||||
self.blurredHistoryNode = nil
|
||||
blurredHistoryNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
self.historyNode.isHidden = isBlurred
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ func chatHistoryEntriesForView(location: ChatLocation, view: MessageHistoryView,
|
||||
if case let .replyThread(replyThreadMessage) = location, view.earlierId == nil, !view.holeEarlier, !view.isLoading {
|
||||
loop: for entry in view.additionalData {
|
||||
switch entry {
|
||||
case let .message(id, messages) where id == replyThreadMessage.messageId:
|
||||
case let .message(id, messages) where id == replyThreadMessage.effectiveTopId:
|
||||
if !messages.isEmpty {
|
||||
let selection: ChatHistoryMessageSelection = .none
|
||||
|
||||
|
@ -18,6 +18,12 @@ import ListMessageItem
|
||||
import AccountContext
|
||||
import ChatInterfaceState
|
||||
|
||||
extension ChatReplyThreadMessage {
|
||||
var effectiveTopId: MessageId {
|
||||
return self.channelMessageId ?? self.messageId
|
||||
}
|
||||
}
|
||||
|
||||
private class ChatHistoryListSelectionRecognizer: UIPanGestureRecognizer {
|
||||
private let selectionGestureActivationThreshold: CGFloat = 5.0
|
||||
|
||||
@ -643,7 +649,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
additionalData.append(.peer(replyThreadMessage.messageId.peerId))
|
||||
}
|
||||
|
||||
additionalData.append(.message(replyThreadMessage.messageId))
|
||||
additionalData.append(.message(replyThreadMessage.effectiveTopId))
|
||||
}
|
||||
|
||||
let currentViewVersion = Atomic<Int?>(value: nil)
|
||||
@ -1216,7 +1222,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
if hasUnconsumedMention && !hasUnconsumedContent {
|
||||
messageIdsWithUnseenPersonalMention.append(message.id)
|
||||
}
|
||||
if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.messageId == message.id {
|
||||
if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.effectiveTopId == message.id {
|
||||
isTopReplyThreadMessageShownValue = true
|
||||
}
|
||||
case let .MessageGroupEntry(_, messages, _):
|
||||
@ -1246,7 +1252,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
|
||||
if hasUnconsumedMention && !hasUnconsumedContent {
|
||||
messageIdsWithUnseenPersonalMention.append(message.id)
|
||||
}
|
||||
if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.messageId == message.id {
|
||||
if case let .replyThread(replyThreadMessage) = self.chatLocation, replyThreadMessage.effectiveTopId == message.id {
|
||||
isTopReplyThreadMessageShownValue = true
|
||||
}
|
||||
}
|
||||
|
@ -457,7 +457,7 @@ func contextMenuForChatPresentationIntefaceState(chatPresentationInterfaceState:
|
||||
|
||||
var isReplyThreadHead = false
|
||||
if case let .replyThread(replyThreadMessage) = chatPresentationInterfaceState.chatLocation {
|
||||
isReplyThreadHead = messages[0].id == replyThreadMessage.messageId
|
||||
isReplyThreadHead = messages[0].id == replyThreadMessage.effectiveTopId
|
||||
}
|
||||
|
||||
if !isReplyThreadHead, data.canReply {
|
||||
|
@ -55,7 +55,7 @@ class ChatMessageShareButton: HighlightableButtonNode {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(presentationData: ChatPresentationData, message: Message, account: Account) -> CGSize {
|
||||
func update(presentationData: ChatPresentationData, chatLocation: ChatLocation, message: Message, account: Account) -> CGSize {
|
||||
var isReplies = false
|
||||
var replyCount = 0
|
||||
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = channel.info {
|
||||
@ -67,6 +67,10 @@ class ChatMessageShareButton: HighlightableButtonNode {
|
||||
}
|
||||
}
|
||||
}
|
||||
if case let .replyThread(replyThreadMessage) = chatLocation, replyThreadMessage.effectiveTopId == message.id {
|
||||
replyCount = 0
|
||||
isReplies = false
|
||||
}
|
||||
|
||||
if self.theme !== presentationData.theme.theme || self.isReplies != isReplies {
|
||||
self.theme = presentationData.theme.theme
|
||||
@ -569,7 +573,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
|
||||
if replyThreadMessage.isChannelPost, replyThreadMessage.messageId == item.message.id {
|
||||
if replyThreadMessage.isChannelPost, replyThreadMessage.effectiveTopId == item.message.id {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
|
||||
@ -925,7 +929,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
strongSelf.addSubnode(updatedShareButtonNode)
|
||||
updatedShareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, message: item.message, account: item.context.account)
|
||||
let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, chatLocation: item.chatLocation, message: item.message, account: item.context.account)
|
||||
updatedShareButtonNode.frame = CGRect(origin: CGPoint(x: updatedImageFrame.maxX + 8.0, y: updatedImageFrame.maxY - buttonSize.height - 4.0), size: buttonSize)
|
||||
} else if let shareButtonNode = strongSelf.shareButtonNode {
|
||||
shareButtonNode.removeFromSupernode()
|
||||
@ -1361,7 +1365,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
return
|
||||
}
|
||||
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == item.message.id {
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.effectiveTopId == item.message.id {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -144,6 +144,9 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(
|
||||
if let channel = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case let .broadcast(info) = channel.info, info.flags.contains(.hasDiscussionGroup) {
|
||||
hasDiscussion = true
|
||||
}
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.effectiveTopId == firstMessage.id {
|
||||
hasDiscussion = false
|
||||
}
|
||||
|
||||
if hasDiscussion {
|
||||
var canComment = false
|
||||
@ -879,7 +882,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
allowFullWidth = true
|
||||
}
|
||||
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.messageId == firstMessage.id {
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.effectiveTopId == firstMessage.id {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
|
||||
@ -906,7 +909,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
let isFailed = item.content.firstMessage.effectivelyFailed(timestamp: item.context.account.network.getApproximateRemoteTimestamp())
|
||||
|
||||
var needShareButton = false
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == item.message.id {
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.effectiveTopId == item.message.id {
|
||||
needShareButton = false
|
||||
allowFullWidth = true
|
||||
} else if isFailed || Namespaces.Message.allScheduled.contains(item.message.id.namespace) {
|
||||
@ -2955,7 +2958,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
default:
|
||||
break
|
||||
}
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == item.message.id {
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.effectiveTopId == item.message.id {
|
||||
canHaveSelection = false
|
||||
}
|
||||
|
||||
|
@ -194,7 +194,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.messageId == item.message.id {
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.effectiveTopId == item.message.id {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
|
||||
@ -473,7 +473,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
strongSelf.addSubnode(updatedShareButtonNode)
|
||||
updatedShareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, message: item.message, account: item.context.account)
|
||||
let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, chatLocation: item.chatLocation, message: item.message, account: item.context.account)
|
||||
updatedShareButtonNode.frame = CGRect(origin: CGPoint(x: videoFrame.maxX - 7.0, y: videoFrame.maxY - 24.0 - buttonSize.height), size: buttonSize)
|
||||
} else if let shareButtonNode = strongSelf.shareButtonNode {
|
||||
shareButtonNode.removeFromSupernode()
|
||||
@ -858,7 +858,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
|
||||
return
|
||||
}
|
||||
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == item.message.id {
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.effectiveTopId == item.message.id {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -277,7 +277,11 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
||||
var consumableContentIcon: UIImage?
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? ConsumableContentMessageAttribute {
|
||||
if !attribute.consumed {
|
||||
var isConsumed = attribute.consumed
|
||||
if let channel = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = channel.info {
|
||||
isConsumed = true
|
||||
}
|
||||
if !isConsumed {
|
||||
if incoming {
|
||||
consumableContentIcon = PresentationResourcesChat.chatBubbleConsumableContentIncomingIcon(presentationData.theme.theme)
|
||||
} else {
|
||||
|
@ -327,7 +327,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
|
||||
if let peer = message.peers[message.id.peerId] as? TelegramChannel, case .broadcast = peer.info {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
} else if case let .replyThread(replyThreadMessage) = chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.messageId == message.id {
|
||||
} else if case let .replyThread(replyThreadMessage) = chatLocation, replyThreadMessage.isChannelPost, replyThreadMessage.effectiveTopId == message.id {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
if !hasActionMedia && !isBroadcastChannel {
|
||||
|
@ -257,7 +257,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
|
||||
if replyThreadMessage.isChannelPost, replyThreadMessage.messageId == item.message.id {
|
||||
if replyThreadMessage.isChannelPost, replyThreadMessage.effectiveTopId == item.message.id {
|
||||
isBroadcastChannel = true
|
||||
}
|
||||
|
||||
@ -568,7 +568,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
strongSelf.addSubnode(updatedShareButtonNode)
|
||||
updatedShareButtonNode.addTarget(strongSelf, action: #selector(strongSelf.shareButtonPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, message: item.message, account: item.context.account)
|
||||
let buttonSize = updatedShareButtonNode.update(presentationData: item.presentationData, chatLocation: item.chatLocation, message: item.message, account: item.context.account)
|
||||
let shareButtonFrame = CGRect(origin: CGPoint(x: baseShareButtonFrame.minX, y: baseShareButtonFrame.maxY - buttonSize.height), size: buttonSize)
|
||||
transition.updateFrame(node: updatedShareButtonNode, frame: shareButtonFrame)
|
||||
} else if let shareButtonNode = strongSelf.shareButtonNode {
|
||||
@ -922,7 +922,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
return
|
||||
}
|
||||
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.messageId == item.message.id {
|
||||
if case let .replyThread(replyThreadMessage) = item.chatLocation, replyThreadMessage.effectiveTopId == item.message.id {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -456,7 +456,12 @@ final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
|
||||
var accessibilityText = ""
|
||||
for segment in self.titleNode.segments {
|
||||
accessibilityText.append(segment.attributedText.string)
|
||||
switch segment {
|
||||
case let .number(_, string):
|
||||
accessibilityText.append(string.string)
|
||||
case let .text(_, string):
|
||||
accessibilityText.append(string.string)
|
||||
}
|
||||
}
|
||||
|
||||
self.accessibilityLabel = accessibilityText
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit abf983bf498ed0ddbdc3a4e2326b218f62a69d0c
|
||||
Subproject commit 64f96a1b4fcfb8afdb0fb7749082cb42cdad7901
|
Binary file not shown.
@ -449,12 +449,12 @@ public final class WalletStrings: Equatable {
|
||||
public var Wallet_Send_ConfirmationConfirm: String { return self._s[218]! }
|
||||
public var Wallet_Created_ExportErrorTitle: String { return self._s[219]! }
|
||||
public var Wallet_Info_TransactionPendingHeader: String { return self._s[220]! }
|
||||
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
|
||||
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
|
||||
let form = getPluralizationForm(self.lc, value)
|
||||
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
||||
return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue)
|
||||
}
|
||||
public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
|
||||
public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
|
||||
let form = getPluralizationForm(self.lc, value)
|
||||
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
|
||||
return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)
|
||||
|
Loading…
x
Reference in New Issue
Block a user