From 428766c75bbb15af8bb9837070ae051f2ef2a6a6 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 22 Jan 2021 01:34:37 +0400 Subject: [PATCH] [WIP] Chat Import --- .../Sources/ChatImportActivityScreen.swift | 17 + .../MtProtoKit/Sources/MTTcpConnection.m | 367 ++++++++++++++++++ .../TelegramCore/Sources/MessageUtils.swift | 2 + .../ChatMessageAvatarAccessoryItem.swift | 16 +- .../Sources/ChatMessageBubbleItemNode.swift | 10 + .../TelegramUI/Sources/ChatMessageItem.swift | 6 +- .../Sources/ChatMessageReplyInfoNode.swift | 11 +- .../ChatMessageTextBubbleContentNode.swift | 10 +- .../Sources/ReplyAccessoryPanelNode.swift | 8 +- .../Sources/ShareExtensionContext.swift | 2 + 10 files changed, 441 insertions(+), 8 deletions(-) diff --git a/submodules/ChatImportUI/Sources/ChatImportActivityScreen.swift b/submodules/ChatImportUI/Sources/ChatImportActivityScreen.swift index 2fa50ef3c5..aa24a9ea8e 100644 --- a/submodules/ChatImportUI/Sources/ChatImportActivityScreen.swift +++ b/submodules/ChatImportUI/Sources/ChatImportActivityScreen.swift @@ -22,6 +22,7 @@ public final class ChatImportActivityScreen: ViewController { private let animationNode: AnimatedStickerNode private let radialStatus: RadialStatusNode + private let radialCheck: RadialStatusNode private let radialStatusBackground: ASImageNode private let radialStatusText: ImmediateTextNode private let progressText: ImmediateTextNode @@ -46,6 +47,7 @@ public final class ChatImportActivityScreen: ViewController { self.animationNode = AnimatedStickerNode() self.radialStatus = RadialStatusNode(backgroundNodeColor: .clear) + self.radialCheck = RadialStatusNode(backgroundNodeColor: .clear) self.radialStatusBackground = ASImageNode() self.radialStatusBackground.isUserInteractionEnabled = false self.radialStatusBackground.displaysAsynchronously = false @@ -90,6 +92,7 @@ public final class ChatImportActivityScreen: ViewController { self.addSubnode(self.animationNode) self.addSubnode(self.radialStatusBackground) self.addSubnode(self.radialStatus) + self.addSubnode(self.radialCheck) self.addSubnode(self.radialStatusText) self.addSubnode(self.progressText) self.addSubnode(self.statusText) @@ -159,6 +162,8 @@ public final class ChatImportActivityScreen: ViewController { self.animationNode.updateLayout(size: iconSize) self.radialStatus.frame = CGRect(origin: CGPoint(x: floor((layout.size.width - radialStatusSize.width) / 2.0), y: hideIcon ? contentOriginY : (contentOriginY + iconSize.height + maxIconStatusSpacing)), size: radialStatusSize) + let checkSize: CGFloat = 130.0 + self.radialCheck.frame = CGRect(origin: CGPoint(x: self.radialStatus.frame.minX + floor((self.radialStatus.frame.width - checkSize) / 2.0), y: self.radialStatus.frame.minY + floor((self.radialStatus.frame.height - checkSize) / 2.0)), size: CGSize(width: checkSize, height: checkSize)) self.radialStatusBackground.frame = self.radialStatus.frame self.radialStatusText.frame = CGRect(origin: CGPoint(x: self.radialStatus.frame.minX + floor((self.radialStatus.frame.width - radialStatusTextSize.width) / 2.0), y: self.radialStatus.frame.minY + floor((self.radialStatus.frame.height - radialStatusTextSize.height) / 2.0)), size: radialStatusTextSize) @@ -187,6 +192,18 @@ public final class ChatImportActivityScreen: ViewController { if let (layout, navigationHeight) = self.validLayout { self.containerLayoutUpdated(layout, navigationHeight: navigationHeight, transition: .immediate) self.radialStatus.transitionToState(.progress(color: self.presentationData.theme.list.itemAccentColor, lineWidth: 6.0, value: self.totalProgress, cancelEnabled: false), animated: animated, synchronous: true, completion: {}) + if isDone { + self.radialCheck.transitionToState(.progress(color: .clear, lineWidth: 6.0, value: self.totalProgress, cancelEnabled: false), animated: false, synchronous: true, completion: {}) + self.radialCheck.transitionToState(.check(self.presentationData.theme.list.itemAccentColor), animated: animated, synchronous: true, completion: {}) + + let transition: ContainedViewLayoutTransition + if animated { + transition = .animated(duration: 0.2, curve: .easeInOut) + } else { + transition = .immediate + } + transition.updateAlpha(node: self.radialStatusText, alpha: 0.0) + } } } } diff --git a/submodules/MtProtoKit/Sources/MTTcpConnection.m b/submodules/MtProtoKit/Sources/MTTcpConnection.m index 51b303dbb9..e5c55842fd 100644 --- a/submodules/MtProtoKit/Sources/MTTcpConnection.m +++ b/submodules/MtProtoKit/Sources/MTTcpConnection.m @@ -106,6 +106,373 @@ static void generate_public_key(unsigned char key[32], id pr } } +typedef enum { + HelloGenerationCommandInvalid = 0, + HelloGenerationCommandString = 1, + HelloGenerationCommandZero = 2, + HelloGenerationCommandRandom = 3, + HelloGenerationCommandDomain = 4, + HelloGenerationCommandGrease = 5, + HelloGenerationCommandKey = 6, + HelloGenerationCommandPushLengthPosition = 7, + HelloGenerationCommandPopLengthPosition = 8 +} HelloGenerationCommand; + +typedef struct { + int position; +} HelloParseState; + +static HelloGenerationCommand parseCommand(NSString *string, HelloParseState *state) { + if (state->position + 1 >= string.length) { + return HelloGenerationCommandInvalid; + } + unichar c = [string characterAtIndex:state->position]; + state->position += 1; + + if (c == 'S') { + return HelloGenerationCommandString; + } else if (c == 'Z') { + return HelloGenerationCommandZero; + } else if (c == 'R') { + return HelloGenerationCommandRandom; + } else if (c == 'D') { + return HelloGenerationCommandDomain; + } else if (c == 'G') { + return HelloGenerationCommandGrease; + } else if (c == 'K') { + return HelloGenerationCommandKey; + } else if (c == '[') { + return HelloGenerationCommandPushLengthPosition; + } else if (c == ']') { + return HelloGenerationCommandPopLengthPosition; + } else { + return HelloGenerationCommandInvalid; + } +} + +static bool parseSpace(NSString *string, HelloParseState *state) { + if (state->position + 1 >= string.length) { + return false; + } + bool hadSpace = false; + while (true) { + unichar c = [string characterAtIndex:state->position]; + state->position += 1; + if (c == ' ') { + hadSpace = true; + } else { + if (hadSpace) { + return true; + } else { + return false; + } + } + } + return true; +} + +static bool parseEndlineOrEnd(NSString *string, HelloParseState *state) { + if (state->position == string.length) { + return true; + } else if (state->position + 1 >= string.length) { + return false; + } else { + unichar c = [string characterAtIndex:state->position]; + state->position += 1; + return c == '\n'; + } +} + +static bool parseHexByte(unichar c, uint8_t *output) { + if (c >= '0' && c <= '9') { + *output = (uint8_t)(c - '0'); + } else if (c >= 'a' && c <= 'f') { + *output = (uint8_t)(c - 'a'); + } else if (c >= 'A' && c <= 'F') { + *output = (uint8_t)(c - 'A'); + } else { + return false; + } + return true; +} + +static NSData *parseHexStringArgument(NSString *string, HelloParseState *state) { + if (state->position >= string.length) { + return nil; + } + + NSMutableData *data = [[NSMutableData alloc] init]; + + while (true) { + if (state->position == string.length) { + return data; + } + + unichar c = [string characterAtIndex:state->position]; + state->position += 1; + if (c == '\\') { + if (state->position >= string.length) { + return nil; + } + c = [string characterAtIndex:state->position]; + state->position += 1; + if (c == 'x') { + if (state->position >= string.length) { + return nil; + } + unichar d1 = [string characterAtIndex:state->position]; + state->position += 1; + if (state->position >= string.length) { + return nil; + } + unichar d0 = [string characterAtIndex:state->position]; + state->position += 1; + + uint8_t c1 = 0; + if (!parseHexByte(d1, &c1)) { + return nil; + } + uint8_t c0 = 0; + if (!parseHexByte(d0, &c0)) { + return nil; + } + uint8_t byteValue = (c1 << 4) | c0; + [data appendBytes:&byteValue length:1]; + } else { + return nil; + } + } else if (c == '\n') { + return data; + } else { + return nil; + } + } + + return nil; +} + +static bool parseIntArgument(NSString *string, HelloParseState *state, int *output) { + if (state->position >= string.length) { + return false; + } + int value = 0; + while (true) { + if (state->position == string.length) { + *output = value; + return true; + } + + unichar c = [string characterAtIndex:state->position]; + state->position += 1; + + if (c == '\n') { + *output = value; + return true; + } else if (c >= '0' && c <= '9') { + value *= 10; + value += c; + } else { + return false; + } + } + return false; +} + +static NSData *executeGenerationCode(id provider, NSData *domain) { + NSString *code = @"S \"\\x16\\x03\\x01\\x02\\x00\\x01\\x00\\x01\\xfc\\x03\\x03\\n" + "Z 32" + "S \"\\x20\"\n" + "R 32\n" + "S \"\\x00\\x36\"\n" + "G 0\n" + "S \"\\x13\\x01\\x13\\x02\\x13\\x03\\xc0\\x2c\\xc0\\x2b\\xcc\\xa9\\xc0\\x30\\xc0\\x2f\\xcc\\xa8\\xc0\\x24\\xc0\\x23\\xc0\\x0a\\xc0\\x09\\xc0\\x28\\xc0\\x27\\xc0\\x14\\xc0\\x13\\x00\\x9d\\x00\\x9c\\x00\\x3d\\x00\\x3c\\x00\\x35\\x00\\x2f\\xc0\\x08\\xc0\\x12\\x00\\x0a\\x01\\x00\\x01\\x7d\"\n" + "G 2\n" + "S \"\\x00\\x00\\x00\\x00\"\n" + "[\n" + "[\n" + "S \"\\x00\"\n" + "[\n" + "D\n" + "]\n" + "]\n" + "]\n" + "S \"\\x00\\x17\\x00\\x00\\xff\\x01\\x00\\x01\\x00\\x00\\x0a\\x00\\x0c\\x00\\x0a\"\n" + "G 4\n" + "S \"\\x00\\x1d\\x00\\x17\\x00\\x18\\x00\\x19\\x00\\x0b\\x00\\x02\\x01\\x00\\x00\\x10\\x00\\x0e\\x00\\x0c\\x02\\x68\\x32\\x08\\x68\\x74\\x74\\x70\\x2f\\x31\\x2e\\x31\\x00\\x05\\x00\\x05\\x01\\x00\\x00\\x00\\x00\\x00\\x0d\\x00\\x18\\x00\\x16\\x04\\x03\\x08\\x04\\x04\\x01\\x05\\x03\\x02\\x03\\x08\\x05\\x08\\x05\\x05\\x01\\x08\\x06\\x06\\x01\\x02\\x01\\x00\\x12\\x00\\x00\\x00\\x33\\x00\\x2b\\x00\\x29\"\n" + "G 4\n" + "S \"\\x00\\x01\\x00\\x00\\x1d\\x00\\x20\"\n" + "K\n" + "S \"\\x00\\x2d\\x00\\x02\\x01\\x01\\x00\\x2b\\x00\\x0b\\x0a\"\n" + "G 6\n" + "S \"\\x03\\x04\\x03\\x03\\x03\\x02\\x03\\x01\"\n" + "G 3\n" + "S \"\\x00\\x01\\x00\\x00\\x15\""; + + int greaseCount = 8; + NSMutableData *greaseData = [[NSMutableData alloc] initWithLength:greaseCount]; + uint8_t *greaseBytes = (uint8_t *)greaseData.mutableBytes; + int result; + result = SecRandomCopyBytes(nil, greaseData.length, greaseData.mutableBytes); + + for (int i = 0; i < greaseData.length; i++) { + uint8_t c = greaseBytes[i]; + c = (c & 0xf0) | 0x0a; + greaseBytes[i] = c; + } + for (int i = 1; i < greaseData.length; i += 2) { + if (greaseBytes[i] == greaseBytes[i - 1]) { + greaseBytes[i] &= 0x10; + } + } + + NSMutableData *resultData = [[NSMutableData alloc] init]; + NSMutableArray *lengthStack = [[NSMutableArray alloc] init]; + + HelloParseState state; + state.position = 0; + + while (true) { + if (state.position >= code.length) { + break; + } else { + HelloGenerationCommand command = parseCommand(code, &state); + switch (command) { + case HelloGenerationCommandString: { + if (!parseSpace(code, &state)) { + return nil; + } + NSData *data = parseHexStringArgument(code, &state); + if (data == nil) { + return nil; + } + + [resultData appendData:data]; + + break; + } + case HelloGenerationCommandZero: { + if (!parseSpace(code, &state)) { + return false; + } + int zeroLength = 0; + if (!parseIntArgument(code, &state, &zeroLength)) { + return nil; + } + + NSMutableData *zeroData = [[NSMutableData alloc] initWithLength:zeroLength]; + [resultData appendData:zeroData]; + + break; + } + case HelloGenerationCommandRandom: { + if (!parseSpace(code, &state)) { + return nil; + } + int randomLength = 0; + if (!parseIntArgument(code, &state, &randomLength)) { + return nil; + } + + NSMutableData *randomData = [[NSMutableData alloc] initWithLength:randomLength]; + int randomResult = SecRandomCopyBytes(kSecRandomDefault, randomLength, randomData.mutableBytes); + if (randomResult != errSecSuccess) { + return nil; + } + [resultData appendData:randomData]; + + break; + } + case HelloGenerationCommandDomain: { + [resultData appendData:domain]; + if (!parseEndlineOrEnd(code, &state)) { + return nil; + } + break; + } + case HelloGenerationCommandGrease: { + if (!parseSpace(code, &state)) { + return nil; + } + int greaseIndex = 0; + if (!parseIntArgument(code, &state, &greaseIndex)) { + return nil; + } + + if (greaseIndex < 0 || greaseIndex >= greaseCount) { + return nil; + } + + [resultData appendBytes:&greaseBytes[greaseIndex] length:1]; + [resultData appendBytes:&greaseBytes[greaseIndex] length:1]; + + break; + } + case HelloGenerationCommandKey: { + if (!parseEndlineOrEnd(code, &state)) { + return nil; + } + + NSMutableData *key = [[NSMutableData alloc] initWithLength:32]; + generate_public_key(key.mutableBytes, provider); + [resultData appendData:key]; + + break; + } + case HelloGenerationCommandPushLengthPosition: { + if (!parseEndlineOrEnd(code, &state)) { + return nil; + } + + [lengthStack addObject:@(resultData.length)]; + NSMutableData *zeroData = [[NSMutableData alloc] initWithLength:2]; + [resultData appendData:zeroData]; + + break; + } + case HelloGenerationCommandPopLengthPosition: { + if (!parseEndlineOrEnd(code, &state)) { + return nil; + } + + if (lengthStack.count == 0) { + return nil; + } + + int position = [lengthStack[lengthStack.count - 1] intValue]; + uint16_t calculatedLength = resultData.length - 2 - position; + ((uint8_t *)resultData.mutableBytes)[position] = ((uint8_t *)&calculatedLength)[1]; + ((uint8_t *)resultData.mutableBytes)[position + 1] = ((uint8_t *)&calculatedLength)[0]; + [lengthStack removeLastObject]; + + break; + } + case HelloGenerationCommandInvalid: { + return nil; + } + default: { + return nil; + } + } + } + } + + int paddingLengthPosition = (int)resultData.length; + [lengthStack addObject:@(resultData.length)]; + NSMutableData *zeroData = [[NSMutableData alloc] initWithLength:2]; + [resultData appendData:zeroData]; + + while (resultData.length < 517) { + uint8_t zero = 0; + [resultData appendBytes:&zero length:1]; + } + + uint16_t calculatedLength = resultData.length - 2 - paddingLengthPosition; + ((uint8_t *)resultData.mutableBytes)[paddingLengthPosition] = ((uint8_t *)&calculatedLength)[1]; + ((uint8_t *)resultData.mutableBytes)[paddingLengthPosition + 1] = ((uint8_t *)&calculatedLength)[0]; + + return resultData; +} + @interface MTTcpConnectionData : NSObject @property (nonatomic, strong, readonly) NSString *ip; diff --git a/submodules/TelegramCore/Sources/MessageUtils.swift b/submodules/TelegramCore/Sources/MessageUtils.swift index 5395bf5630..e4e2920917 100644 --- a/submodules/TelegramCore/Sources/MessageUtils.swift +++ b/submodules/TelegramCore/Sources/MessageUtils.swift @@ -104,6 +104,8 @@ public extension Message { if let peer = self.peers[sourceReference.messageId.peerId] { return peer } + } else if let forwardInfo = self.forwardInfo, forwardInfo.flags.contains(.isImported), let author = forwardInfo.author { + return author } return self.author } diff --git a/submodules/TelegramUI/Sources/ChatMessageAvatarAccessoryItem.swift b/submodules/TelegramUI/Sources/ChatMessageAvatarAccessoryItem.swift index 0952fb41b6..9c13e45395 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAvatarAccessoryItem.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAvatarAccessoryItem.swift @@ -52,9 +52,15 @@ final class ChatMessageAvatarAccessoryItem: ListViewAccessoryItem { return false } if let forwardInfo = self.forwardInfo, let otherForwardInfo = other.forwardInfo { - if forwardInfo.flags.contains(.isImported) == forwardInfo.flags.contains(.isImported) { - if forwardInfo.authorSignature != otherForwardInfo.authorSignature { - return false + if forwardInfo.flags.contains(.isImported) && forwardInfo.flags.contains(.isImported) == forwardInfo.flags.contains(.isImported) { + if let authorSignature = forwardInfo.authorSignature, let otherAuthorSignature = otherForwardInfo.authorSignature { + if authorSignature != otherAuthorSignature { + return false + } + } else if let authorId = forwardInfo.author?.id, let otherAuthorId = other.forwardInfo?.author?.id { + if authorId != otherAuthorId { + return false + } } } else { return false @@ -74,7 +80,9 @@ final class ChatMessageAvatarAccessoryItem: ListViewAccessoryItem { let node = ChatMessageAvatarAccessoryItemNode() node.frame = CGRect(origin: CGPoint(), size: CGSize(width: 38.0, height: 38.0)) if let forwardInfo = self.forwardInfo, forwardInfo.flags.contains(.isImported) { - if let authorSignature = forwardInfo.authorSignature, !authorSignature.isEmpty { + if let author = forwardInfo.author { + node.setPeer(context: self.context, theme: self.context.sharedContext.currentPresentationData.with({ $0 }).theme, synchronousLoad: synchronous, peer: author, authorOfMessage: self.messageReference, emptyColor: self.emptyColor, controllerInteraction: self.controllerInteraction) + } else if let authorSignature = forwardInfo.authorSignature, !authorSignature.isEmpty { let components = authorSignature.components(separatedBy: " ") if !components.isEmpty, !components[0].hasPrefix("+") { var letters: [String] = [] diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index e4b9aac9d4..91ef103005 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -56,10 +56,16 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([ var isFile = false inner: for media in message.media { if let _ = media as? TelegramMediaImage { + if let forwardInfo = message.forwardInfo, forwardInfo.flags.contains(.isImported) { + messageWithCaptionToAdd = (message, itemAttributes) + } result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .media, neighborSpacing: .default))) } else if let file = media as? TelegramMediaFile { let isVideo = file.isVideo || (file.isAnimated && file.dimensions != nil) if isVideo { + if let forwardInfo = message.forwardInfo, forwardInfo.flags.contains(.isImported) { + messageWithCaptionToAdd = (message, itemAttributes) + } result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .media, neighborSpacing: .default))) } else { var neighborSpacing: ChatMessageBubbleRelativePosition.NeighbourSpacing = .default @@ -1036,6 +1042,10 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode } effectiveAuthor = source displayAuthorInfo = !mergedTop.merged && incoming && effectiveAuthor != nil + } else if let forwardInfo = item.content.firstMessage.forwardInfo, forwardInfo.flags.contains(.isImported), let author = forwardInfo.author { + ignoreForward = true + effectiveAuthor = author + displayAuthorInfo = !mergedTop.merged && incoming } else if let forwardInfo = item.content.firstMessage.forwardInfo, forwardInfo.flags.contains(.isImported), let authorSignature = forwardInfo.authorSignature { ignoreForward = true effectiveAuthor = TelegramUser(id: PeerId(namespace: Namespaces.Peer.Empty, id: Int32(clamping: authorSignature.persistentHashValue)), accessHash: nil, firstName: authorSignature, lastName: nil, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: UserInfoFlags()) diff --git a/submodules/TelegramUI/Sources/ChatMessageItem.swift b/submodules/TelegramUI/Sources/ChatMessageItem.swift index 7f892f552e..76e5f92943 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItem.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItem.swift @@ -131,7 +131,11 @@ private func messagesShouldBeMerged(accountPeerId: PeerId, _ lhs: Message, _ rhs lhsEffectiveTimestamp = lhsForwardInfo.date rhsEffectiveTimestamp = rhsForwardInfo.date - sameAuthor = lhsForwardInfo.authorSignature == rhsForwardInfo.authorSignature + if let lhsAuthorId = lhsForwardInfo.author?.id, let rhsAuthorId = rhsForwardInfo.author?.id { + sameAuthor = lhsAuthorId == rhsAuthorId + } else if let lhsAuthorSignature = lhsForwardInfo.authorSignature, let rhsAuthorSignature = rhsForwardInfo.authorSignature { + sameAuthor = lhsAuthorSignature == rhsAuthorSignature + } } if lhs.id.peerId.isRepliesOrSavedMessages(accountPeerId: accountPeerId) { diff --git a/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift b/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift index 17f5743d3d..af0467d891 100644 --- a/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift @@ -55,7 +55,16 @@ class ChatMessageReplyInfoNode: ASDisplayNode { let titleFont = Font.medium(fontSize) let textFont = Font.regular(fontSize) - let titleString = message.effectiveAuthor?.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder) ?? strings.User_DeletedAccount + var titleString = message.effectiveAuthor?.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder) ?? strings.User_DeletedAccount + + if let forwardInfo = message.forwardInfo, forwardInfo.flags.contains(.isImported) { + if let author = forwardInfo.author { + titleString = author.displayTitle(strings: strings, displayOrder: presentationData.nameDisplayOrder) + } else if let authorSignature = forwardInfo.authorSignature { + titleString = authorSignature + } + } + let (textString, isMedia) = descriptionStringForMessage(contentSettings: context.currentContentSettings.with { $0 }, message: message, strings: strings, nameDisplayOrder: presentationData.nameDisplayOrder, accountPeerId: context.account.peerId) let placeholderColor: UIColor = message.effectivelyIncoming(context.account.peerId) ? presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor : presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor diff --git a/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift index eab6980946..32d76e2312 100644 --- a/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift @@ -277,8 +277,10 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { if let entities = entities { attributedText = stringWithAppliedEntities(rawText, entities: entities, baseColor: messageTheme.primaryTextColor, linkColor: messageTheme.linkTextColor, baseFont: textFont, linkFont: textFont, boldFont: item.presentationData.messageBoldFont, italicFont: item.presentationData.messageItalicFont, boldItalicFont: item.presentationData.messageBoldItalicFont, fixedFont: item.presentationData.messageFixedFont, blockQuoteFont: item.presentationData.messageBlockQuoteFont) - } else { + } else if !rawText.isEmpty { attributedText = NSAttributedString(string: rawText, font: textFont, textColor: messageTheme.primaryTextColor) + } else { + attributedText = NSAttributedString(string: " ", font: textFont, textColor: messageTheme.primaryTextColor) } var cutout: TextNodeCutout? @@ -339,6 +341,12 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { boundingSize.height += layoutConstants.text.bubbleInsets.top + layoutConstants.text.bubbleInsets.bottom } + if attributedText.string.isEmpty, var adjustedStatusFrameValue = adjustedStatusFrame { + adjustedStatusFrameValue.origin.y = 1.0 + boundingSize.height = adjustedStatusFrameValue.maxY + 5.0 + adjustedStatusFrame = adjustedStatusFrameValue + } + return (boundingSize, { [weak self] animation, _ in if let strongSelf = self { strongSelf.item = item diff --git a/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift index 60b745909a..59fbe8fffb 100644 --- a/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift @@ -76,7 +76,13 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { let message = messageView.message var authorName = "" var text = "" - if let author = message?.effectiveAuthor { + if let forwardInfo = message?.forwardInfo, forwardInfo.flags.contains(.isImported) { + if let author = forwardInfo.author { + authorName = author.displayTitle(strings: strings, displayOrder: nameDisplayOrder) + } else if let authorSignature = forwardInfo.authorSignature { + authorName = authorSignature + } + } else if let author = message?.effectiveAuthor { authorName = author.displayTitle(strings: strings, displayOrder: nameDisplayOrder) } if let message = message { diff --git a/submodules/TelegramUI/Sources/ShareExtensionContext.swift b/submodules/TelegramUI/Sources/ShareExtensionContext.swift index 88563caca6..06df6d2617 100644 --- a/submodules/TelegramUI/Sources/ShareExtensionContext.swift +++ b/submodules/TelegramUI/Sources/ShareExtensionContext.swift @@ -490,6 +490,7 @@ public class ShareRootControllerImpl { controller.navigationPresentation = .default let beginWithPeer: (PeerId) -> Void = { peerId in + navigationController.view.endEditing(true) navigationController.pushViewController(ChatImportActivityScreen(context: context, cancel: { self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) }, peerId: peerId, archive: archive, mainEntry: mainFile, otherEntries: otherEntries)) @@ -607,6 +608,7 @@ public class ShareRootControllerImpl { controller.navigationPresentation = .default let beginWithPeer: (PeerId) -> Void = { peerId in + navigationController.view.endEditing(true) navigationController.pushViewController(ChatImportActivityScreen(context: context, cancel: { self?.getExtensionContext()?.completeRequest(returningItems: nil, completionHandler: nil) }, peerId: peerId, archive: archive, mainEntry: mainFile, otherEntries: otherEntries))