Implement mention rendering

This commit is contained in:
Niels Andriesse 2019-10-08 15:02:03 +11:00
parent cbc12977d3
commit a1d40a5933
3 changed files with 57 additions and 15 deletions

View File

@ -676,12 +676,14 @@ NS_ASSUME_NONNULL_BEGIN
TSOutgoingMessage *outgoingMessage = (TSOutgoingMessage *)self.viewItem.interaction;
shouldIgnoreEvents = outgoingMessage.messageState != TSOutgoingMessageStateSent;
}
NSString *threadID = self.viewItem.interaction.uniqueThreadId;
[self.class loadForTextDisplay:self.bodyTextView
displayableText:self.displayableBodyText
searchText:self.delegate.lastSearchedText
textColor:self.bodyTextColor
font:self.textMessageFont
shouldIgnoreEvents:shouldIgnoreEvents];
shouldIgnoreEvents:shouldIgnoreEvents
threadID:threadID];
}
+ (void)loadForTextDisplay:(OWSMessageTextView *)textView
@ -690,11 +692,11 @@ NS_ASSUME_NONNULL_BEGIN
textColor:(UIColor *)textColor
font:(UIFont *)font
shouldIgnoreEvents:(BOOL)shouldIgnoreEvents
threadID:(NSString *)threadID
{
textView.hidden = NO;
textView.textColor = textColor;
// Honor dynamic type in the message bodies.
textView.font = font;
textView.linkTextAttributes = @{
NSForegroundColorAttributeName : textColor,
@ -703,21 +705,43 @@ NS_ASSUME_NONNULL_BEGIN
textView.shouldIgnoreEvents = shouldIgnoreEvents;
NSString *text = displayableText.displayText;
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc]
initWithString:text
attributes:@{ NSFontAttributeName : font, NSForegroundColorAttributeName : textColor }];
NSError *error1;
NSRegularExpression *regex1 = [[NSRegularExpression alloc] initWithPattern:@"@\\w*" options:0 error:&error1];
OWSAssertDebug(error1 == nil);
NSSet<NSString *> *knownUserIDs = LKAPI.userHexEncodedPublicKeyCache[threadID];
NSMutableSet<NSValue *> *mentions = [NSMutableSet new];
NSTextCheckingResult *match = [regex1 firstMatchInString:text options:NSMatchingWithoutAnchoringBounds range:NSMakeRange(0, text.length)];
if (match != nil) {
while (YES) {
NSString *userID = [[text substringWithRange:match.range] stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:@""];
NSUInteger matchEnd;
if ([knownUserIDs containsObject:userID]) {
NSString *userDisplayName = [Environment.shared.contactsManager attributedContactOrProfileNameForPhoneIdentifier:userID primaryFont:font secondaryFont:font].string;
text = [text stringByReplacingCharactersInRange:match.range withString:[NSString stringWithFormat:@"@%@", userDisplayName]];
[mentions addObject:[NSValue valueWithRange:NSMakeRange(match.range.location, userDisplayName.length + 1)]];
matchEnd = match.range.location + userDisplayName.length;
} else {
matchEnd = match.range.location + match.range.length;
}
match = [regex1 firstMatchInString:text options:NSMatchingWithoutAnchoringBounds range:NSMakeRange(matchEnd, text.length - matchEnd)];
if (match == nil) { break; }
}
}
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:text attributes:@{ NSFontAttributeName : font, NSForegroundColorAttributeName : textColor }];
for (NSValue *mention in mentions) {
NSRange range = mention.rangeValue;
[attributedText addAttribute:NSBackgroundColorAttributeName value:UIColor.lokiDarkGray range:range];
[attributedText addAttribute:NSForegroundColorAttributeName value:UIColor.ows_whiteColor range:range];
}
if (searchText.length >= ConversationSearchController.kMinimumSearchTextLength) {
NSString *searchableText = [FullTextSearchFinder normalizeWithText:searchText];
NSError *error;
NSRegularExpression *regex =
[[NSRegularExpression alloc] initWithPattern:[NSRegularExpression escapedPatternForString:searchableText]
options:NSRegularExpressionCaseInsensitive
error:&error];
OWSAssertDebug(error == nil);
NSError *error2;
NSRegularExpression *regex2 = [[NSRegularExpression alloc] initWithPattern:[NSRegularExpression escapedPatternForString:searchableText] options:NSRegularExpressionCaseInsensitive error:&error2];
OWSAssertDebug(error2 == nil);
for (NSTextCheckingResult *match in
[regex matchesInString:text options:NSMatchingWithoutAnchoringBounds range:NSMakeRange(0, text.length)]) {
[regex2 matchesInString:text options:NSMatchingWithoutAnchoringBounds range:NSMakeRange(0, text.length)]) {
OWSAssertDebug(match.range.length >= ConversationSearchController.kMinimumSearchTextLength);
[attributedText addAttribute:NSBackgroundColorAttributeName value:UIColor.yellowColor range:match.range];
[attributedText addAttribute:NSForegroundColorAttributeName value:UIColor.ows_blackColor range:match.range];

View File

@ -260,7 +260,7 @@ public final class LokiAPI : NSObject {
}
}
// MARK: Caching
// MARK: Message Caching
private static let receivedMessageHashValuesKey = "receivedMessageHashValuesKey"
private static let receivedMessageHashValuesCollection = "receivedMessageHashValuesCollection"
@ -293,6 +293,19 @@ public final class LokiAPI : NSObject {
transaction.setObject(receivedMessageHashValues, forKey: receivedMessageHashValuesKey, inCollection: receivedMessageHashValuesCollection)
}
}
// MARK: User ID Caching
@objc static var userHexEncodedPublicKeyCache: [String:Set<String>] = [:] // Thread ID to set of user hex encoded public keys
@objc public static func cache(_ userHexEncodedPublicKey: String, for thread: String) {
if let cache = userHexEncodedPublicKeyCache[thread] {
var mutableCache = cache
mutableCache.insert(userHexEncodedPublicKey)
userHexEncodedPublicKeyCache[thread] = mutableCache
} else {
userHexEncodedPublicKeyCache[thread] = [ userHexEncodedPublicKey ]
}
}
}
// MARK: Error Handling

View File

@ -1413,15 +1413,20 @@ NS_ASSUME_NONNULL_BEGIN
return nil;
}
// Loki: Cache the user hex encoded public key (for mentions)
[LKAPI cache:incomingMessage.authorId for:oldGroupThread.uniqueId];
[self finalizeIncomingMessage:incomingMessage
thread:oldGroupThread
envelope:envelope
transaction:transaction];
// Loki: Map the message ID to the message server ID if needed
if (dataMessage.publicChatInfo != nil && dataMessage.publicChatInfo.hasServerID) {
[self.primaryStorage setIDForMessageWithServerID:dataMessage.publicChatInfo.serverID to:incomingMessage.uniqueId in:transaction];
}
// Loki: Generate a link preview if needed
dispatch_async(dispatch_get_main_queue(), ^{
NSString *linkPreviewURL = [OWSLinkPreview previewURLForRawBodyText:incomingMessage.body];
if (linkPreviewURL != nil) {