2021-12-03 10:47:24 +00:00
|
|
|
/*
|
|
|
|
Copyright 2015 OpenMarket Ltd
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#import "MXKRoomBubbleComponent.h"
|
|
|
|
|
|
|
|
#import "MXEvent+MatrixKit.h"
|
|
|
|
#import "MXKSwiftHeader.h"
|
|
|
|
|
|
|
|
@implementation MXKRoomBubbleComponent
|
|
|
|
|
|
|
|
- (instancetype)initWithEvent:(MXEvent*)event roomState:(MXRoomState*)roomState eventFormatter:(MXKEventFormatter*)eventFormatter session:(MXSession*)session;
|
|
|
|
{
|
|
|
|
if (self = [super init])
|
|
|
|
{
|
|
|
|
// Build text component related to this event
|
|
|
|
_eventFormatter = eventFormatter;
|
|
|
|
MXKEventFormatterError error;
|
|
|
|
|
|
|
|
NSAttributedString *eventString = [_eventFormatter attributedStringFromEvent:event withRoomState:roomState error:&error];
|
|
|
|
|
|
|
|
// Store the potential error
|
|
|
|
event.mxkEventFormatterError = error;
|
|
|
|
|
|
|
|
_textMessage = nil;
|
|
|
|
_attributedTextMessage = eventString;
|
|
|
|
|
|
|
|
// Set date time
|
|
|
|
if (event.originServerTs != kMXUndefinedTimestamp)
|
|
|
|
{
|
|
|
|
_date = [NSDate dateWithTimeIntervalSince1970:(double)event.originServerTs/1000];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_date = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Keep ref on event (used to handle the read marker, or a potential event redaction).
|
|
|
|
_event = event;
|
|
|
|
|
|
|
|
_displayFix = MXKRoomBubbleComponentDisplayFixNone;
|
|
|
|
if ([event.content[@"format"] isEqualToString:kMXRoomMessageFormatHTML])
|
|
|
|
{
|
|
|
|
if ([((NSString*)event.content[@"formatted_body"]) containsString:@"<blockquote"])
|
|
|
|
{
|
|
|
|
_displayFix |= MXKRoomBubbleComponentDisplayFixHtmlBlockquote;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_showEncryptionBadge = [self shouldShowWarningBadgeForEvent:event roomState:(MXRoomState*)roomState session:session];
|
|
|
|
|
|
|
|
[self updateLinkWithRoomState:roomState];
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)updateWithEvent:(MXEvent*)event roomState:(MXRoomState*)roomState session:(MXSession*)session
|
|
|
|
{
|
|
|
|
// Report the new event
|
|
|
|
_event = event;
|
|
|
|
|
|
|
|
if (_event.isRedactedEvent)
|
|
|
|
{
|
|
|
|
// Do not use the live room state for redacted events as they occurred in the past
|
|
|
|
// Note: as we don't have valid room state in this case, userId will be used as display name
|
|
|
|
roomState = nil;
|
|
|
|
}
|
|
|
|
// Other calls to updateWithEvent are made to update the state of an event (ex: MXKEventStateSending to MXKEventStateDefault).
|
|
|
|
// They occur in live so we can use the room up-to-date state without making huge errors
|
|
|
|
|
|
|
|
_textMessage = nil;
|
|
|
|
|
|
|
|
MXKEventFormatterError error;
|
|
|
|
_attributedTextMessage = [_eventFormatter attributedStringFromEvent:event withRoomState:roomState error:&error];
|
|
|
|
|
|
|
|
_showEncryptionBadge = [self shouldShowWarningBadgeForEvent:event roomState:roomState session:session];
|
|
|
|
|
|
|
|
[self updateLinkWithRoomState:roomState];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)textMessage
|
|
|
|
{
|
|
|
|
if (!_textMessage)
|
|
|
|
{
|
|
|
|
_textMessage = _attributedTextMessage.string;
|
|
|
|
}
|
|
|
|
return _textMessage;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)updateLinkWithRoomState:(MXRoomState*)roomState
|
|
|
|
{
|
|
|
|
// Ensure link detection has been enabled
|
|
|
|
if (!MXKAppSettings.standardAppSettings.enableBubbleComponentLinkDetection)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only detect links in unencrypted rooms, for un-redacted message events that are text, notice or emote.
|
|
|
|
// Specifically check the room's encryption state rather than the event's as outgoing events are always unencrypted initially.
|
|
|
|
if (roomState.isEncrypted || self.event.eventType != MXEventTypeRoomMessage || [self.event isRedactedEvent])
|
|
|
|
{
|
|
|
|
self.link = nil; // Ensure there's no link for a redacted event
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-12-13 08:04:30 +00:00
|
|
|
NSString *messageType = self.event.content[kMXMessageTypeKey];
|
2021-12-03 10:47:24 +00:00
|
|
|
|
|
|
|
if (!messageType || !([messageType isEqualToString:kMXMessageTypeText] || [messageType isEqualToString:kMXMessageTypeNotice] || [messageType isEqualToString:kMXMessageTypeEmote]))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Detect links in the attributed string which gets updated when the message is edited.
|
|
|
|
// Restrict detection to the unquoted string so links are only found in the sender's message.
|
|
|
|
NSString *body = [self.attributedTextMessage mxk_unquotedString];
|
|
|
|
NSURL *url = [body mxk_firstURLDetected];
|
|
|
|
|
|
|
|
if (!url)
|
|
|
|
{
|
|
|
|
self.link = nil;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
self.link = url;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)shouldShowWarningBadgeForEvent:(MXEvent*)event roomState:(MXRoomState*)roomState session:(MXSession*)session
|
|
|
|
{
|
|
|
|
// Warning badges are unnecessary in unencrypted rooms
|
|
|
|
if (!roomState.isEncrypted)
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not all events are encrypted (e.g. state/reactions/redactions) and we only have encrypted cell subclasses for messages and attachments.
|
|
|
|
if (event.eventType != MXEventTypeRoomMessage && !event.isMediaAttachment)
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Always show a warning badge if there was a decryption error.
|
|
|
|
if (event.decryptionError)
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unencrypted message events should show a warning unless they're pending local echoes
|
|
|
|
if (!event.isEncrypted)
|
|
|
|
{
|
|
|
|
if (event.isLocalEvent
|
|
|
|
|| event.contentHasBeenEdited) // Local echo for an edit is clear but uses a true event id, the one of the edited event
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The encryption is in a good state.
|
|
|
|
// Only show a warning badge if there are trust issues.
|
|
|
|
if (event.sender)
|
|
|
|
{
|
|
|
|
MXUserTrustLevel *userTrustLevel = [session.crypto trustLevelForUser:event.sender];
|
|
|
|
MXDeviceInfo *deviceInfo = [session.crypto eventDeviceInfo:event];
|
|
|
|
|
|
|
|
if (userTrustLevel.isVerified && !deviceInfo.trustLevel.isVerified)
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Everything was fine
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|