mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 11:23:48 +00:00
[WIP] Chat Import
This commit is contained in:
parent
b3740d69b3
commit
428766c75b
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,6 +106,373 @@ static void generate_public_key(unsigned char key[32], id<EncryptionProvider> 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<EncryptionProvider> 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<NSNumber *> *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;
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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] = []
|
||||
|
@ -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())
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
|
Loading…
x
Reference in New Issue
Block a user