mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-29 12:46:58 +02:00
Merge pull request #5518 from vector-im/steve/bubbles_text_msg
Message bubbles: Create new text message cells for bubbles
This commit is contained in:
@@ -46,6 +46,11 @@
|
|||||||
*/
|
*/
|
||||||
NSAttributedString *attributedTextMessage;
|
NSAttributedString *attributedTextMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Same as attributedTextMessage but without vertical positioning vertical blank space.
|
||||||
|
*/
|
||||||
|
NSAttributedString *attributedTextMessageWithoutPositioningSpace;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The optional text pattern to be highlighted in the body of the message.
|
The optional text pattern to be highlighted in the body of the message.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
@implementation MXKRoomBubbleCellData
|
@implementation MXKRoomBubbleCellData
|
||||||
@synthesize senderId, targetId, roomId, senderDisplayName, senderAvatarUrl, senderAvatarPlaceholder, targetDisplayName, targetAvatarUrl, targetAvatarPlaceholder, isEncryptedRoom, isPaginationFirstBubble, shouldHideSenderInformation, date, isIncoming, isAttachmentWithThumbnail, isAttachmentWithIcon, attachment, senderFlair;
|
@synthesize senderId, targetId, roomId, senderDisplayName, senderAvatarUrl, senderAvatarPlaceholder, targetDisplayName, targetAvatarUrl, targetAvatarPlaceholder, isEncryptedRoom, isPaginationFirstBubble, shouldHideSenderInformation, date, isIncoming, isAttachmentWithThumbnail, isAttachmentWithIcon, attachment, senderFlair;
|
||||||
@synthesize textMessage, attributedTextMessage;
|
@synthesize textMessage, attributedTextMessage, attributedTextMessageWithoutPositioningSpace;
|
||||||
@synthesize shouldHideSenderName, isTyping, showBubbleDateTime, showBubbleReceipts, useCustomDateTimeLabel, useCustomReceipts, useCustomUnsentButton, hasNoDisplay;
|
@synthesize shouldHideSenderName, isTyping, showBubbleDateTime, showBubbleReceipts, useCustomDateTimeLabel, useCustomReceipts, useCustomUnsentButton, hasNoDisplay;
|
||||||
@synthesize tag;
|
@synthesize tag;
|
||||||
@synthesize collapsable, collapsed, collapsedAttributedTextMessage, prevCollapsableCellData, nextCollapsableCellData, collapseState;
|
@synthesize collapsable, collapsed, collapsedAttributedTextMessage, prevCollapsableCellData, nextCollapsableCellData, collapseState;
|
||||||
|
|||||||
@@ -163,6 +163,10 @@
|
|||||||
*/
|
*/
|
||||||
@property (nonatomic) NSAttributedString *attributedTextMessage;
|
@property (nonatomic) NSAttributedString *attributedTextMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Same as attributedTextMessage but without vertical positioning blank space
|
||||||
|
*/
|
||||||
|
@property (nonatomic) NSAttributedString *attributedTextMessageWithoutPositioningSpace;
|
||||||
/**
|
/**
|
||||||
The raw text message (without attributes)
|
The raw text message (without attributes)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -75,11 +75,9 @@
|
|||||||
- (MXKCellData*)renderedCellData;
|
- (MXKCellData*)renderedCellData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Reset the cell.
|
Stop processes no more needed when cell is not visible.
|
||||||
|
|
||||||
The cell is no more displayed. This is time to release resources and removing listeners.
|
The cell is no more displayed but still recycled. This is time to stop animation.
|
||||||
In case of UITableViewCell or UIContentViewCell object, the cell must reset in a state
|
|
||||||
that it can be reusable.
|
|
||||||
*/
|
*/
|
||||||
- (void)didEndDisplay;
|
- (void)didEndDisplay;
|
||||||
|
|
||||||
|
|||||||
@@ -241,30 +241,18 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat
|
|||||||
|
|
||||||
- (NSAttributedString*)attributedTextMessage
|
- (NSAttributedString*)attributedTextMessage
|
||||||
{
|
{
|
||||||
@synchronized(bubbleComponents)
|
[self buildAttributedStringIfNeeded];
|
||||||
{
|
|
||||||
if (self.hasAttributedTextMessage && !attributedTextMessage.length)
|
|
||||||
{
|
|
||||||
// Attributed text message depends on the room read receipts which must be retrieved on the main thread to prevent us from race conditions.
|
|
||||||
// Check here the current thread, this is just a sanity check because the attributed text message
|
|
||||||
// is requested during the rendering step which takes place on the main thread.
|
|
||||||
if ([NSThread currentThread] != [NSThread mainThread])
|
|
||||||
{
|
|
||||||
MXLogDebug(@"[RoomBubbleCellData] attributedTextMessage called on wrong thread");
|
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
||||||
self.attributedTextMessage = [self makeAttributedString];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.attributedTextMessage = [self makeAttributedString];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return attributedTextMessage;
|
return attributedTextMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSAttributedString*)attributedTextMessageWithoutPositioningSpace
|
||||||
|
{
|
||||||
|
[self buildAttributedStringIfNeeded];
|
||||||
|
|
||||||
|
return attributedTextMessageWithoutPositioningSpace;
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL)hasNoDisplay
|
- (BOOL)hasNoDisplay
|
||||||
{
|
{
|
||||||
if (self.tag == RoomBubbleCellDataTagKeyVerificationNoDisplay)
|
if (self.tag == RoomBubbleCellDataTagKeyVerificationNoDisplay)
|
||||||
@@ -378,18 +366,25 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat
|
|||||||
[self setNeedsUpdateAdditionalContentHeight];
|
[self setNeedsUpdateAdditionalContentHeight];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSAttributedString*)makeAttributedString
|
- (void)buildAttributedString
|
||||||
{
|
{
|
||||||
// CAUTION: This method must be called on the main thread.
|
// CAUTION: This method must be called on the main thread.
|
||||||
|
|
||||||
// Return the collapsed string only for cells series header
|
// Return the collapsed string only for cells series header
|
||||||
if (self.collapsed && self.collapsedAttributedTextMessage && self.nextCollapsableCellData)
|
if (self.collapsed && self.collapsedAttributedTextMessage && self.nextCollapsableCellData)
|
||||||
{
|
{
|
||||||
return super.collapsedAttributedTextMessage;
|
NSAttributedString *attributedString = super.collapsedAttributedTextMessage;
|
||||||
|
|
||||||
|
self.attributedTextMessage = attributedString;
|
||||||
|
self.attributedTextMessageWithoutPositioningSpace = attributedString;
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NSMutableAttributedString *currentAttributedTextMsg;
|
NSMutableAttributedString *currentAttributedTextMsg;
|
||||||
|
|
||||||
|
NSMutableAttributedString *currentAttributedTextMsgWithoutVertSpace = [NSMutableAttributedString new];
|
||||||
|
|
||||||
NSInteger selectedComponentIndex = self.selectedComponentIndex;
|
NSInteger selectedComponentIndex = self.selectedComponentIndex;
|
||||||
NSInteger lastMessageIndex = self.containsLastMessage ? self.mostRecentComponentIndex : NSNotFound;
|
NSInteger lastMessageIndex = self.containsLastMessage ? self.mostRecentComponentIndex : NSNotFound;
|
||||||
|
|
||||||
@@ -416,11 +411,15 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat
|
|||||||
{
|
{
|
||||||
currentAttributedTextMsg = [[NSMutableAttributedString alloc] initWithAttributedString:[RoomBubbleCellData timestampVerticalWhitespace]];
|
currentAttributedTextMsg = [[NSMutableAttributedString alloc] initWithAttributedString:[RoomBubbleCellData timestampVerticalWhitespace]];
|
||||||
[currentAttributedTextMsg appendAttributedString:componentString];
|
[currentAttributedTextMsg appendAttributedString:componentString];
|
||||||
|
|
||||||
|
[currentAttributedTextMsgWithoutVertSpace appendAttributedString:componentString];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Init attributed string with the first text component
|
// Init attributed string with the first text component
|
||||||
currentAttributedTextMsg = [[NSMutableAttributedString alloc] initWithAttributedString:componentString];
|
currentAttributedTextMsg = [[NSMutableAttributedString alloc] initWithAttributedString:componentString];
|
||||||
|
|
||||||
|
[currentAttributedTextMsgWithoutVertSpace appendAttributedString:componentString];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self addVerticalWhitespaceToString:currentAttributedTextMsg forEvent:component.event.eventId];
|
[self addVerticalWhitespaceToString:currentAttributedTextMsg forEvent:component.event.eventId];
|
||||||
@@ -456,10 +455,45 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat
|
|||||||
[currentAttributedTextMsg appendAttributedString:componentString];
|
[currentAttributedTextMsg appendAttributedString:componentString];
|
||||||
|
|
||||||
[self addVerticalWhitespaceToString:currentAttributedTextMsg forEvent:component.event.eventId];
|
[self addVerticalWhitespaceToString:currentAttributedTextMsg forEvent:component.event.eventId];
|
||||||
|
|
||||||
|
[currentAttributedTextMsgWithoutVertSpace appendAttributedString:componentString];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return currentAttributedTextMsg;
|
// With bubbles the text is truncated with quote messages containing vertical border view
|
||||||
|
// Add horizontal space to fix the issue
|
||||||
|
if (self.displayFix & MXKRoomBubbleComponentDisplayFixHtmlBlockquote)
|
||||||
|
{
|
||||||
|
[currentAttributedTextMsgWithoutVertSpace appendString:@" "];
|
||||||
|
}
|
||||||
|
|
||||||
|
self.attributedTextMessage = currentAttributedTextMsg;
|
||||||
|
|
||||||
|
self.attributedTextMessageWithoutPositioningSpace = currentAttributedTextMsgWithoutVertSpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)buildAttributedStringIfNeeded
|
||||||
|
{
|
||||||
|
@synchronized(bubbleComponents)
|
||||||
|
{
|
||||||
|
if (self.hasAttributedTextMessage && !attributedTextMessage.length)
|
||||||
|
{
|
||||||
|
// Attributed text message depends on the room read receipts which must be retrieved on the main thread to prevent us from race conditions.
|
||||||
|
// Check here the current thread, this is just a sanity check because the attributed text message
|
||||||
|
// is requested during the rendering step which takes place on the main thread.
|
||||||
|
if ([NSThread currentThread] != [NSThread mainThread])
|
||||||
|
{
|
||||||
|
MXLogDebug(@"[RoomBubbleCellData] attributedTextMessage called on wrong thread");
|
||||||
|
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||||
|
[self buildAttributedString];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[self buildAttributedString];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSInteger)firstVisibleComponentIndex
|
- (NSInteger)firstVisibleComponentIndex
|
||||||
|
|||||||
@@ -669,6 +669,13 @@ const CGFloat kTypingCellHeight = 24;
|
|||||||
[self updateCellLayoutIfNeeded:bubbleCell withCellData:cellData];
|
[self updateCellLayoutIfNeeded:bubbleCell withCellData:cellData];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ([cell conformsToProtocol:@protocol(Themable)])
|
||||||
|
{
|
||||||
|
id<Themable> cellThemable = (id<Themable>)cell;
|
||||||
|
|
||||||
|
[cellThemable updateWithTheme:ThemeService.shared.theme];
|
||||||
|
}
|
||||||
|
|
||||||
return cell;
|
return cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2813,6 +2813,61 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (roomBubbleCellData.getFirstBubbleComponentWithDisplay.event.isEmote)
|
||||||
|
{
|
||||||
|
if (bubbleData.isIncoming)
|
||||||
|
{
|
||||||
|
if (bubbleData.isPaginationFirstBubble)
|
||||||
|
{
|
||||||
|
if (bubbleData.shouldHideSenderName)
|
||||||
|
{
|
||||||
|
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierIncomingEmoteEncryptedWithPaginationTitleWithoutSenderName : RoomTimelineCellIdentifierIncomingEmoteWithPaginationTitleWithoutSenderName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierIncomingEmoteEncryptedWithPaginationTitle : RoomTimelineCellIdentifierIncomingEmoteWithPaginationTitle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (bubbleData.shouldHideSenderInformation)
|
||||||
|
{
|
||||||
|
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierIncomingEmoteEncryptedWithoutSenderInfo : RoomTimelineCellIdentifierIncomingEmoteWithoutSenderInfo;
|
||||||
|
}
|
||||||
|
else if (bubbleData.shouldHideSenderName)
|
||||||
|
{
|
||||||
|
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierIncomingEmoteEncryptedWithoutSenderName : RoomTimelineCellIdentifierIncomingEmoteWithoutSenderName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierIncomingEmoteEncrypted : RoomTimelineCellIdentifierIncomingEmote;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (bubbleData.isPaginationFirstBubble)
|
||||||
|
{
|
||||||
|
if (bubbleData.shouldHideSenderName)
|
||||||
|
{
|
||||||
|
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierOutgoingEmoteEncryptedWithPaginationTitleWithoutSenderName : RoomTimelineCellIdentifierOutgoingEmoteWithPaginationTitleWithoutSenderName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierOutgoingEmoteEncryptedWithPaginationTitle : RoomTimelineCellIdentifierOutgoingEmoteWithPaginationTitle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (bubbleData.shouldHideSenderInformation)
|
||||||
|
{
|
||||||
|
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierOutgoingEmoteEncryptedWithoutSenderInfo : RoomTimelineCellIdentifierOutgoingEmoteWithoutSenderInfo;
|
||||||
|
}
|
||||||
|
else if (bubbleData.shouldHideSenderName)
|
||||||
|
{
|
||||||
|
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierOutgoingEmoteEncryptedWithoutSenderName : RoomTimelineCellIdentifierOutgoingEmoteWithoutSenderName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierOutgoingEmoteEncrypted : RoomTimelineCellIdentifierOutgoingEmote;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (bubbleData.isIncoming)
|
else if (bubbleData.isIncoming)
|
||||||
{
|
{
|
||||||
if (bubbleData.isAttachmentWithThumbnail)
|
if (bubbleData.isAttachmentWithThumbnail)
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType {
|
|||||||
override var pictureView: MXKImageView! {
|
override var pictureView: MXKImageView! {
|
||||||
get {
|
get {
|
||||||
guard let bubbleCellContentView = self.bubbleCellContentView,
|
guard let bubbleCellContentView = self.bubbleCellContentView,
|
||||||
bubbleCellContentView.showSenderInfo else {
|
bubbleCellContentView.showSenderAvatar else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,8 +93,7 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType {
|
|||||||
|
|
||||||
override var userNameLabel: UILabel! {
|
override var userNameLabel: UILabel! {
|
||||||
get {
|
get {
|
||||||
guard let bubbleCellContentView = self.bubbleCellContentView,
|
guard let bubbleCellContentView = self.bubbleCellContentView, bubbleCellContentView.showSenderName else {
|
||||||
bubbleCellContentView.showSenderInfo else {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +110,7 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType {
|
|||||||
override var userNameTapGestureMaskView: UIView! {
|
override var userNameTapGestureMaskView: UIView! {
|
||||||
get {
|
get {
|
||||||
guard let bubbleCellContentView = self.bubbleCellContentView,
|
guard let bubbleCellContentView = self.bubbleCellContentView,
|
||||||
bubbleCellContentView.showSenderInfo else {
|
bubbleCellContentView.showSenderName else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,25 +143,7 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType {
|
|||||||
|
|
||||||
// MARK: - Public
|
// MARK: - Public
|
||||||
|
|
||||||
// MARK: - Overrides
|
func removeDecorationViews() {
|
||||||
|
|
||||||
override func setupViews() {
|
|
||||||
super.setupViews()
|
|
||||||
|
|
||||||
let showEncryptionStatus = bubbleCellContentView?.showEncryptionStatus ?? false
|
|
||||||
|
|
||||||
if showEncryptionStatus {
|
|
||||||
self.setupEncryptionStatusViewTapGestureRecognizer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override class func defaultReuseIdentifier() -> String! {
|
|
||||||
return String(describing: self)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func didEndDisplay() {
|
|
||||||
super.didEndDisplay()
|
|
||||||
|
|
||||||
if let bubbleCellReadReceiptsDisplayable = self as? BubbleCellReadReceiptsDisplayable {
|
if let bubbleCellReadReceiptsDisplayable = self as? BubbleCellReadReceiptsDisplayable {
|
||||||
bubbleCellReadReceiptsDisplayable.removeReadReceiptsView()
|
bubbleCellReadReceiptsDisplayable.removeReadReceiptsView()
|
||||||
}
|
}
|
||||||
@@ -178,6 +159,36 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType {
|
|||||||
if let timestampDisplayable = self as? TimestampDisplayable {
|
if let timestampDisplayable = self as? TimestampDisplayable {
|
||||||
timestampDisplayable.removeTimestampView()
|
timestampDisplayable.removeTimestampView()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let urlPreviewDisplayable = self as? RoomCellURLPreviewDisplayable {
|
||||||
|
urlPreviewDisplayable.removeURLPreviewView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Overrides
|
||||||
|
|
||||||
|
override var isTextViewNeedsPositioningVerticalSpace: Bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override func setupViews() {
|
||||||
|
super.setupViews()
|
||||||
|
|
||||||
|
let showEncryptionStatus = bubbleCellContentView?.showEncryptionStatus ?? false
|
||||||
|
|
||||||
|
if showEncryptionStatus {
|
||||||
|
self.setupEncryptionStatusViewTapGestureRecognizer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override class func defaultReuseIdentifier() -> String! {
|
||||||
|
return String(describing: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func prepareForReuse() {
|
||||||
|
super.prepareForReuse()
|
||||||
|
|
||||||
|
self.removeDecorationViews()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func render(_ cellData: MXKCellData!) {
|
override func render(_ cellData: MXKCellData!) {
|
||||||
@@ -234,11 +245,28 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType {
|
|||||||
self.bubbleCellContentView = bubbleCellContentView
|
self.bubbleCellContentView = bubbleCellContentView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - RoomCellURLPreviewDisplayable
|
||||||
|
// Cannot use default implementation with ObjC protocol, if self conforms to BubbleCellReadReceiptsDisplayable method below will be used
|
||||||
|
|
||||||
|
func addURLPreviewView(_ urlPreviewView: UIView) {
|
||||||
|
self.bubbleCellContentView?.addURLPreviewView(urlPreviewView)
|
||||||
|
|
||||||
|
// tmpSubviews is used for touch detection in MXKRoomBubbleTableViewCell
|
||||||
|
self.addTemporarySubview(urlPreviewView)
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeURLPreviewView() {
|
||||||
|
self.bubbleCellContentView?.removeURLPreviewView()
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - BubbleCellReadReceiptsDisplayable
|
// MARK: - BubbleCellReadReceiptsDisplayable
|
||||||
// Cannot use default implementation with ObjC protocol, if self conforms to BubbleCellReadReceiptsDisplayable method below will be used
|
// Cannot use default implementation with ObjC protocol, if self conforms to BubbleCellReadReceiptsDisplayable method below will be used
|
||||||
|
|
||||||
func addReadReceiptsView(_ readReceiptsView: UIView) {
|
func addReadReceiptsView(_ readReceiptsView: UIView) {
|
||||||
self.bubbleCellContentView?.addReadReceiptsView(readReceiptsView)
|
self.bubbleCellContentView?.addReadReceiptsView(readReceiptsView)
|
||||||
|
|
||||||
|
// tmpSubviews is used for touch detection in MXKRoomBubbleTableViewCell
|
||||||
|
self.addTemporarySubview(readReceiptsView)
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeReadReceiptsView() {
|
func removeReadReceiptsView() {
|
||||||
@@ -250,6 +278,9 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType {
|
|||||||
|
|
||||||
func addReactionsView(_ reactionsView: UIView) {
|
func addReactionsView(_ reactionsView: UIView) {
|
||||||
self.bubbleCellContentView?.addReactionsView(reactionsView)
|
self.bubbleCellContentView?.addReactionsView(reactionsView)
|
||||||
|
|
||||||
|
// tmpSubviews is used for touch detection in MXKRoomBubbleTableViewCell
|
||||||
|
self.addTemporarySubview(reactionsView)
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeReactionsView() {
|
func removeReactionsView() {
|
||||||
@@ -260,6 +291,9 @@ class BaseBubbleCell: MXKRoomBubbleTableViewCell, BaseBubbleCellType {
|
|||||||
|
|
||||||
func addThreadSummaryView(_ threadSummaryView: ThreadSummaryView) {
|
func addThreadSummaryView(_ threadSummaryView: ThreadSummaryView) {
|
||||||
self.bubbleCellContentView?.addThreadSummaryView(threadSummaryView)
|
self.bubbleCellContentView?.addThreadSummaryView(threadSummaryView)
|
||||||
|
|
||||||
|
// tmpSubviews is used for touch detection in MXKRoomBubbleTableViewCell
|
||||||
|
self.addTemporarySubview(threadSummaryView)
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeThreadSummaryView() {
|
func removeThreadSummaryView() {
|
||||||
|
|||||||
@@ -29,15 +29,18 @@ final class BubbleCellContentView: UIView, NibLoadable {
|
|||||||
@IBOutlet weak var paginationLabel: UILabel!
|
@IBOutlet weak var paginationLabel: UILabel!
|
||||||
@IBOutlet weak var paginationSeparatorView: UIView!
|
@IBOutlet weak var paginationSeparatorView: UIView!
|
||||||
|
|
||||||
@IBOutlet weak var senderInfoContainerView: UIView!
|
@IBOutlet weak var userNameContainerView: UIView!
|
||||||
@IBOutlet weak var avatarImageView: MXKImageView!
|
|
||||||
@IBOutlet weak var userNameLabel: UILabel!
|
@IBOutlet weak var userNameLabel: UILabel!
|
||||||
@IBOutlet weak var userNameTouchMaskView: UIView!
|
@IBOutlet weak var userNameTouchMaskView: UIView!
|
||||||
|
|
||||||
|
@IBOutlet weak var avatarContainerView: UIView!
|
||||||
|
@IBOutlet weak var avatarImageView: MXKImageView!
|
||||||
|
|
||||||
@IBOutlet weak var innerContentView: UIView!
|
@IBOutlet weak var innerContentView: UIView!
|
||||||
|
|
||||||
@IBOutlet weak var innerContentViewLeadingConstraint: NSLayoutConstraint!
|
@IBOutlet weak var innerContentViewLeadingConstraint: NSLayoutConstraint!
|
||||||
@IBOutlet weak var innerContentViewTrailingConstraint: NSLayoutConstraint!
|
@IBOutlet weak var innerContentViewTrailingConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet weak var innerContentViewBottomContraint: NSLayoutConstraint!
|
||||||
|
|
||||||
@IBOutlet weak var encryptionStatusContainerView: UIView!
|
@IBOutlet weak var encryptionStatusContainerView: UIView!
|
||||||
@IBOutlet weak var encryptionImageView: UIImageView!
|
@IBOutlet weak var encryptionImageView: UIImageView!
|
||||||
@@ -45,18 +48,38 @@ final class BubbleCellContentView: UIView, NibLoadable {
|
|||||||
@IBOutlet weak var bubbleInfoContainer: UIView!
|
@IBOutlet weak var bubbleInfoContainer: UIView!
|
||||||
@IBOutlet weak var bubbleInfoContainerTopConstraint: NSLayoutConstraint!
|
@IBOutlet weak var bubbleInfoContainerTopConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
|
@IBOutlet weak var urlPreviewContainerView: UIView!
|
||||||
|
@IBOutlet weak var urlPreviewContentView: UIView!
|
||||||
|
@IBOutlet weak var urlPreviewContentViewLeadingConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet weak var urlPreviewContentViewTrailingConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
@IBOutlet weak var readReceiptsContainerView: UIView!
|
@IBOutlet weak var readReceiptsContainerView: UIView!
|
||||||
@IBOutlet weak var readReceiptsContentView: UIView!
|
@IBOutlet weak var readReceiptsContentView: UIView!
|
||||||
|
|
||||||
@IBOutlet weak var reactionsContainerView: UIView!
|
@IBOutlet weak var reactionsContainerView: UIView!
|
||||||
@IBOutlet weak var reactionsContentView: UIView!
|
@IBOutlet weak var reactionsContentView: UIView!
|
||||||
|
@IBOutlet weak var reactionsContentViewLeadingConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet weak var reactionsContentViewTrailingConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
@IBOutlet weak var threadSummaryContainerView: UIView!
|
@IBOutlet weak var threadSummaryContainerView: UIView!
|
||||||
|
@IBOutlet weak var threadSummaryContentView: UIView!
|
||||||
|
@IBOutlet weak var threadSummaryContentViewLeadingConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet weak var threadSummaryContentViewTrailingConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet weak var threadSummaryContentViewBottomConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
@IBOutlet weak var bubbleOverlayContainer: UIView!
|
@IBOutlet weak var bubbleOverlayContainer: UIView!
|
||||||
|
|
||||||
// MARK: Private
|
// MARK: Private
|
||||||
|
|
||||||
|
private var showURLPreview: Bool {
|
||||||
|
get {
|
||||||
|
return !self.urlPreviewContainerView.isHidden
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
self.urlPreviewContainerView.isHidden = !newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var showReadReceipts: Bool {
|
private var showReadReceipts: Bool {
|
||||||
get {
|
get {
|
||||||
return !self.readReceiptsContainerView.isHidden
|
return !self.readReceiptsContainerView.isHidden
|
||||||
@@ -96,10 +119,29 @@ final class BubbleCellContentView: UIView, NibLoadable {
|
|||||||
|
|
||||||
var showSenderInfo: Bool {
|
var showSenderInfo: Bool {
|
||||||
get {
|
get {
|
||||||
return !self.senderInfoContainerView.isHidden
|
return self.showSenderAvatar && self.showSenderName
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
self.senderInfoContainerView.isHidden = !newValue
|
self.showSenderAvatar = newValue
|
||||||
|
self.showSenderName = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var showSenderAvatar: Bool {
|
||||||
|
get {
|
||||||
|
return !self.avatarContainerView.isHidden
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
self.avatarContainerView.isHidden = !newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var showSenderName: Bool {
|
||||||
|
get {
|
||||||
|
return !self.userNameContainerView.isHidden
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
self.userNameContainerView.isHidden = !newValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,6 +154,8 @@ final class BubbleCellContentView: UIView, NibLoadable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var decorationViewsAlignment: RoomCellDecorationAlignment = .left
|
||||||
|
|
||||||
// MARK: - Setup
|
// MARK: - Setup
|
||||||
|
|
||||||
class func instantiate() -> BubbleCellContentView {
|
class func instantiate() -> BubbleCellContentView {
|
||||||
@@ -147,7 +191,24 @@ extension BubbleCellContentView: BubbleCellReactionsDisplayable {
|
|||||||
|
|
||||||
func addReactionsView(_ reactionsView: UIView) {
|
func addReactionsView(_ reactionsView: UIView) {
|
||||||
self.reactionsContentView.vc_removeAllSubviews()
|
self.reactionsContentView.vc_removeAllSubviews()
|
||||||
|
|
||||||
|
// Update reactions alignment according to current decoration alignment
|
||||||
|
if let bubbleReactionsView = reactionsView as? BubbleReactionsView {
|
||||||
|
|
||||||
|
let reactionsAlignment: BubbleReactionsViewAlignment
|
||||||
|
|
||||||
|
switch self.decorationViewsAlignment {
|
||||||
|
case .left:
|
||||||
|
reactionsAlignment = .left
|
||||||
|
case .right:
|
||||||
|
reactionsAlignment = .right
|
||||||
|
}
|
||||||
|
|
||||||
|
bubbleReactionsView.alignment = reactionsAlignment
|
||||||
|
}
|
||||||
|
|
||||||
self.reactionsContentView.vc_addSubViewMatchingParent(reactionsView)
|
self.reactionsContentView.vc_addSubViewMatchingParent(reactionsView)
|
||||||
|
|
||||||
self.showReactions = true
|
self.showReactions = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,22 +222,85 @@ extension BubbleCellContentView: BubbleCellReactionsDisplayable {
|
|||||||
extension BubbleCellContentView: BubbleCellThreadSummaryDisplayable {
|
extension BubbleCellContentView: BubbleCellThreadSummaryDisplayable {
|
||||||
|
|
||||||
func addThreadSummaryView(_ threadSummaryView: ThreadSummaryView) {
|
func addThreadSummaryView(_ threadSummaryView: ThreadSummaryView) {
|
||||||
self.threadSummaryContainerView.vc_removeAllSubviews()
|
|
||||||
self.threadSummaryContainerView.addSubview(threadSummaryView)
|
guard let containerView = self.threadSummaryContentView else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
containerView.vc_removeAllSubviews()
|
||||||
|
|
||||||
|
containerView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
containerView.addSubview(threadSummaryView)
|
||||||
|
|
||||||
|
let leadingConstraint: NSLayoutConstraint
|
||||||
|
let trailingConstraint: NSLayoutConstraint
|
||||||
|
|
||||||
|
if self.decorationViewsAlignment == .right {
|
||||||
|
leadingConstraint = threadSummaryView.leadingAnchor.constraint(greaterThanOrEqualTo: containerView.leadingAnchor)
|
||||||
|
trailingConstraint = threadSummaryView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor)
|
||||||
|
} else {
|
||||||
|
leadingConstraint = threadSummaryView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor)
|
||||||
|
trailingConstraint = threadSummaryView.trailingAnchor.constraint(lessThanOrEqualTo: containerView.trailingAnchor)
|
||||||
|
}
|
||||||
|
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
threadSummaryView.leadingAnchor.constraint(equalTo: innerContentView.leadingAnchor),
|
leadingConstraint,
|
||||||
threadSummaryView.topAnchor.constraint(equalTo: threadSummaryContainerView.topAnchor),
|
threadSummaryView.topAnchor.constraint(equalTo: containerView.topAnchor),
|
||||||
threadSummaryView.heightAnchor.constraint(equalToConstant: RoomBubbleCellLayout.threadSummaryViewHeight),
|
threadSummaryView.heightAnchor.constraint(equalToConstant: RoomBubbleCellLayout.threadSummaryViewHeight),
|
||||||
threadSummaryView.bottomAnchor.constraint(equalTo: threadSummaryContainerView.bottomAnchor),
|
threadSummaryView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
|
||||||
threadSummaryView.trailingAnchor.constraint(lessThanOrEqualTo: threadSummaryContainerView.trailingAnchor,
|
trailingConstraint
|
||||||
constant: -RoomBubbleCellLayout.reactionsViewRightMargin)
|
|
||||||
])
|
])
|
||||||
|
|
||||||
self.showThreadSummary = true
|
self.showThreadSummary = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeThreadSummaryView() {
|
func removeThreadSummaryView() {
|
||||||
self.showThreadSummary = false
|
self.showThreadSummary = false
|
||||||
self.threadSummaryContainerView.vc_removeAllSubviews()
|
self.threadSummaryContentView.vc_removeAllSubviews()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - RoomCellURLPreviewDisplayable
|
||||||
|
extension BubbleCellContentView: RoomCellURLPreviewDisplayable {
|
||||||
|
|
||||||
|
func addURLPreviewView(_ urlPreviewView: UIView) {
|
||||||
|
|
||||||
|
guard let containerView = self.urlPreviewContentView else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
containerView.vc_removeAllSubviews()
|
||||||
|
|
||||||
|
containerView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
containerView.addSubview(urlPreviewView)
|
||||||
|
|
||||||
|
if let urlPreviewView = urlPreviewView as? URLPreviewView {
|
||||||
|
urlPreviewView.availableWidth = containerView.frame.width
|
||||||
|
}
|
||||||
|
|
||||||
|
let leadingConstraint: NSLayoutConstraint
|
||||||
|
let trailingConstraint: NSLayoutConstraint
|
||||||
|
|
||||||
|
if self.decorationViewsAlignment == .right {
|
||||||
|
leadingConstraint = urlPreviewView.leadingAnchor.constraint(greaterThanOrEqualTo: containerView.leadingAnchor)
|
||||||
|
trailingConstraint = urlPreviewView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor)
|
||||||
|
} else {
|
||||||
|
leadingConstraint = urlPreviewView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor)
|
||||||
|
trailingConstraint = urlPreviewView.trailingAnchor.constraint(lessThanOrEqualTo: containerView.trailingAnchor)
|
||||||
|
}
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
leadingConstraint,
|
||||||
|
urlPreviewView.topAnchor.constraint(equalTo: containerView.topAnchor),
|
||||||
|
urlPreviewView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
|
||||||
|
trailingConstraint
|
||||||
|
])
|
||||||
|
|
||||||
|
self.showURLPreview = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeURLPreviewView() {
|
||||||
|
self.showURLPreview = false
|
||||||
|
self.urlPreviewContentView.vc_removeAllSubviews()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
|
||||||
|
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
@@ -17,7 +18,7 @@
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="595" height="97"/>
|
<rect key="frame" x="0.0" y="0.0" width="595" height="97"/>
|
||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</view>
|
</view>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" translatesAutoresizingMaskIntoConstraints="NO" id="5GX-gn-bK1">
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="5GX-gn-bK1">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="595" height="97"/>
|
<rect key="frame" x="0.0" y="0.0" width="595" height="97"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<view hidden="YES" contentMode="scaleToFill" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="u1e-Q2-PhY">
|
<view hidden="YES" contentMode="scaleToFill" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="u1e-Q2-PhY">
|
||||||
@@ -62,8 +63,8 @@
|
|||||||
<constraint firstAttribute="bottom" secondItem="Ro1-vP-6Ha" secondAttribute="bottom" constant="10" id="UcW-P4-rQv"/>
|
<constraint firstAttribute="bottom" secondItem="Ro1-vP-6Ha" secondAttribute="bottom" constant="10" id="UcW-P4-rQv"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="w0C-6a-f5M">
|
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1cK-e6-Mg5">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="595" height="31"/>
|
<rect key="frame" x="0.0" y="0.0" width="595" height="0.0"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<view clipsSubviews="YES" contentMode="scaleAspectFill" translatesAutoresizingMaskIntoConstraints="NO" id="yXz-Za-4yR" customClass="MXKImageView">
|
<view clipsSubviews="YES" contentMode="scaleAspectFill" translatesAutoresizingMaskIntoConstraints="NO" id="yXz-Za-4yR" customClass="MXKImageView">
|
||||||
<rect key="frame" x="13" y="10" width="30" height="30"/>
|
<rect key="frame" x="13" y="10" width="30" height="30"/>
|
||||||
@@ -74,6 +75,16 @@
|
|||||||
<constraint firstAttribute="width" constant="30" id="y7F-jl-kEF"/>
|
<constraint firstAttribute="width" constant="30" id="y7F-jl-kEF"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="yXz-Za-4yR" firstAttribute="leading" secondItem="1cK-e6-Mg5" secondAttribute="leading" constant="13" id="UjU-eY-ARJ"/>
|
||||||
|
<constraint firstItem="yXz-Za-4yR" firstAttribute="top" secondItem="1cK-e6-Mg5" secondAttribute="top" constant="10" id="jlf-dg-fp0"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="w0C-6a-f5M">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="595" height="31"/>
|
||||||
|
<subviews>
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ohU-Sc-mgb">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ohU-Sc-mgb">
|
||||||
<rect key="frame" x="46" y="4" width="534" height="30"/>
|
<rect key="frame" x="46" y="4" width="534" height="30"/>
|
||||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
@@ -94,9 +105,7 @@
|
|||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="bottom" secondItem="meG-P8-61b" secondAttribute="bottom" constant="3" id="HDT-eS-UWo"/>
|
<constraint firstAttribute="bottom" secondItem="meG-P8-61b" secondAttribute="bottom" constant="3" id="HDT-eS-UWo"/>
|
||||||
<constraint firstItem="ohU-Sc-mgb" firstAttribute="trailing" secondItem="meG-P8-61b" secondAttribute="trailing" id="Lbz-vD-hax"/>
|
<constraint firstItem="ohU-Sc-mgb" firstAttribute="trailing" secondItem="meG-P8-61b" secondAttribute="trailing" id="Lbz-vD-hax"/>
|
||||||
<constraint firstItem="yXz-Za-4yR" firstAttribute="leading" secondItem="w0C-6a-f5M" secondAttribute="leading" constant="13" id="QlM-xG-bob"/>
|
|
||||||
<constraint firstItem="ohU-Sc-mgb" firstAttribute="bottom" secondItem="meG-P8-61b" secondAttribute="bottom" constant="6" id="U50-ZB-dRG"/>
|
<constraint firstItem="ohU-Sc-mgb" firstAttribute="bottom" secondItem="meG-P8-61b" secondAttribute="bottom" constant="6" id="U50-ZB-dRG"/>
|
||||||
<constraint firstItem="yXz-Za-4yR" firstAttribute="top" secondItem="w0C-6a-f5M" secondAttribute="top" constant="10" id="UMP-G3-iPN"/>
|
|
||||||
<constraint firstItem="meG-P8-61b" firstAttribute="top" secondItem="w0C-6a-f5M" secondAttribute="top" constant="10" id="baE-tR-0Ck"/>
|
<constraint firstItem="meG-P8-61b" firstAttribute="top" secondItem="w0C-6a-f5M" secondAttribute="top" constant="10" id="baE-tR-0Ck"/>
|
||||||
<constraint firstItem="ohU-Sc-mgb" firstAttribute="leading" secondItem="meG-P8-61b" secondAttribute="leading" constant="-10" id="ktD-JC-hfG"/>
|
<constraint firstItem="ohU-Sc-mgb" firstAttribute="leading" secondItem="meG-P8-61b" secondAttribute="leading" constant="-10" id="ktD-JC-hfG"/>
|
||||||
<constraint firstItem="ohU-Sc-mgb" firstAttribute="top" secondItem="meG-P8-61b" secondAttribute="top" constant="-6" id="s5V-Fj-iNL"/>
|
<constraint firstItem="ohU-Sc-mgb" firstAttribute="top" secondItem="meG-P8-61b" secondAttribute="top" constant="-6" id="s5V-Fj-iNL"/>
|
||||||
@@ -159,26 +168,25 @@
|
|||||||
<constraint firstItem="oeI-eO-mFK" firstAttribute="leading" secondItem="vcq-cR-uBc" secondAttribute="leading" priority="750" id="vsh-pW-S46"/>
|
<constraint firstItem="oeI-eO-mFK" firstAttribute="leading" secondItem="vcq-cR-uBc" secondAttribute="leading" priority="750" id="vsh-pW-S46"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4zo-V8-CNe">
|
<view hidden="YES" clipsSubviews="YES" contentMode="scaleAspectFit" verticalHuggingPriority="254" translatesAutoresizingMaskIntoConstraints="NO" id="57V-Sl-EmD">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="595" height="12"/>
|
<rect key="frame" x="0.0" y="0.0" width="595" height="0.0"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rQt-NH-Cb0">
|
<view clipsSubviews="YES" contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="vpX-Nl-AEt">
|
||||||
<rect key="frame" x="439" y="0.0" width="150" height="12"/>
|
<rect key="frame" x="56" y="0.0" width="524" height="0.0"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="150" id="fsY-DK-hUg"/>
|
<constraint firstAttribute="height" priority="250" placeholder="YES" id="0Sv-R6-k3e"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="bottom" secondItem="rQt-NH-Cb0" secondAttribute="bottom" id="6yV-vn-doj"/>
|
<constraint firstAttribute="trailing" secondItem="vpX-Nl-AEt" secondAttribute="trailing" constant="15" id="ZmA-Ns-chh"/>
|
||||||
<constraint firstItem="rQt-NH-Cb0" firstAttribute="top" secondItem="4zo-V8-CNe" secondAttribute="top" id="DNc-jy-Urh"/>
|
<constraint firstAttribute="bottom" secondItem="vpX-Nl-AEt" secondAttribute="bottom" id="blD-s1-b1N"/>
|
||||||
<constraint firstAttribute="height" constant="12" id="TxZ-dJ-UI6"/>
|
<constraint firstItem="vpX-Nl-AEt" firstAttribute="top" secondItem="57V-Sl-EmD" secondAttribute="top" id="sFV-XK-lE4"/>
|
||||||
<constraint firstAttribute="trailing" secondItem="rQt-NH-Cb0" secondAttribute="trailing" constant="6" id="lq2-AY-Lus"/>
|
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<view hidden="YES" clipsSubviews="YES" contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="Dj1-m6-1Jw">
|
<view hidden="YES" clipsSubviews="YES" contentMode="scaleAspectFit" verticalHuggingPriority="252" translatesAutoresizingMaskIntoConstraints="NO" id="Dj1-m6-1Jw">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="595" height="0.0"/>
|
<rect key="frame" x="0.0" y="0.0" width="595" height="0.0"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<view clipsSubviews="YES" contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="SNw-aM-ILI">
|
<view clipsSubviews="YES" contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="SNw-aM-ILI">
|
||||||
@@ -196,17 +204,54 @@
|
|||||||
<constraint firstAttribute="trailing" secondItem="SNw-aM-ILI" secondAttribute="trailing" constant="15" id="ynR-d4-6cf"/>
|
<constraint firstAttribute="trailing" secondItem="SNw-aM-ILI" secondAttribute="trailing" constant="15" id="ynR-d4-6cf"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
|
<view hidden="YES" contentMode="scaleToFill" verticalHuggingPriority="253" translatesAutoresizingMaskIntoConstraints="NO" id="4zo-V8-CNe">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="595" height="16"/>
|
||||||
|
<subviews>
|
||||||
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rQt-NH-Cb0">
|
||||||
|
<rect key="frame" x="439" y="0.0" width="150" height="16"/>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="16" id="WZx-xk-gav"/>
|
||||||
|
<constraint firstAttribute="width" constant="150" id="fsY-DK-hUg"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="rQt-NH-Cb0" secondAttribute="bottom" id="6yV-vn-doj"/>
|
||||||
|
<constraint firstItem="rQt-NH-Cb0" firstAttribute="top" secondItem="4zo-V8-CNe" secondAttribute="top" id="DNc-jy-Urh"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="rQt-NH-Cb0" secondAttribute="trailing" constant="6" id="lq2-AY-Lus"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
<view hidden="YES" clipsSubviews="YES" contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="2eB-kB-m20">
|
<view hidden="YES" clipsSubviews="YES" contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="2eB-kB-m20">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="595" height="0.0"/>
|
<rect key="frame" x="0.0" y="0.0" width="595" height="0.0"/>
|
||||||
|
<subviews>
|
||||||
|
<view clipsSubviews="YES" contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="snf-Ea-To0">
|
||||||
|
<rect key="frame" x="56" y="0.0" width="524" height="0.0"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" priority="250" placeholder="YES" id="ymW-ys-P0T"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="snf-Ea-To0" secondAttribute="bottom" id="4tt-w0-4JE"/>
|
||||||
|
<constraint firstItem="snf-Ea-To0" firstAttribute="top" secondItem="2eB-kB-m20" secondAttribute="top" id="Im9-Z0-ieI"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="snf-Ea-To0" secondAttribute="trailing" constant="15" id="Qwm-Of-Zgc"/>
|
||||||
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
</subviews>
|
</subviews>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="Dj1-m6-1Jw" firstAttribute="width" secondItem="5GX-gn-bK1" secondAttribute="width" id="0Px-jL-CMJ"/>
|
<constraint firstItem="Dj1-m6-1Jw" firstAttribute="width" secondItem="5GX-gn-bK1" secondAttribute="width" id="0Px-jL-CMJ"/>
|
||||||
|
<constraint firstItem="1cK-e6-Mg5" firstAttribute="width" secondItem="5GX-gn-bK1" secondAttribute="width" id="2De-xT-k5e"/>
|
||||||
<constraint firstItem="meG-P8-61b" firstAttribute="trailing" secondItem="oeI-eO-mFK" secondAttribute="trailing" id="2Dy-o0-r33"/>
|
<constraint firstItem="meG-P8-61b" firstAttribute="trailing" secondItem="oeI-eO-mFK" secondAttribute="trailing" id="2Dy-o0-r33"/>
|
||||||
|
<constraint firstItem="snf-Ea-To0" firstAttribute="leading" secondItem="oeI-eO-mFK" secondAttribute="leading" id="2lm-T3-dEu"/>
|
||||||
<constraint firstItem="w0C-6a-f5M" firstAttribute="width" secondItem="5GX-gn-bK1" secondAttribute="width" id="5nl-Le-VDi"/>
|
<constraint firstItem="w0C-6a-f5M" firstAttribute="width" secondItem="5GX-gn-bK1" secondAttribute="width" id="5nl-Le-VDi"/>
|
||||||
<constraint firstItem="u1e-Q2-PhY" firstAttribute="width" secondItem="5GX-gn-bK1" secondAttribute="width" id="KrJ-dm-TaV"/>
|
<constraint firstItem="u1e-Q2-PhY" firstAttribute="width" secondItem="5GX-gn-bK1" secondAttribute="width" id="KrJ-dm-TaV"/>
|
||||||
|
<constraint firstItem="57V-Sl-EmD" firstAttribute="width" secondItem="5GX-gn-bK1" secondAttribute="width" id="a2p-Bn-M5e"/>
|
||||||
<constraint firstItem="4zo-V8-CNe" firstAttribute="width" secondItem="5GX-gn-bK1" secondAttribute="width" id="bdq-sQ-NQy"/>
|
<constraint firstItem="4zo-V8-CNe" firstAttribute="width" secondItem="5GX-gn-bK1" secondAttribute="width" id="bdq-sQ-NQy"/>
|
||||||
|
<constraint firstItem="vpX-Nl-AEt" firstAttribute="leading" secondItem="oeI-eO-mFK" secondAttribute="leading" id="lTt-Qx-fuQ"/>
|
||||||
<constraint firstItem="meG-P8-61b" firstAttribute="leading" secondItem="oeI-eO-mFK" secondAttribute="leading" id="lq1-xP-tea"/>
|
<constraint firstItem="meG-P8-61b" firstAttribute="leading" secondItem="oeI-eO-mFK" secondAttribute="leading" id="lq1-xP-tea"/>
|
||||||
<constraint firstItem="SNw-aM-ILI" firstAttribute="leading" secondItem="oeI-eO-mFK" secondAttribute="leading" id="x1n-oT-dez"/>
|
<constraint firstItem="SNw-aM-ILI" firstAttribute="leading" secondItem="oeI-eO-mFK" secondAttribute="leading" id="x1n-oT-dez"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
@@ -227,6 +272,7 @@
|
|||||||
<nil key="simulatedBottomBarMetrics"/>
|
<nil key="simulatedBottomBarMetrics"/>
|
||||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||||
<connections>
|
<connections>
|
||||||
|
<outlet property="avatarContainerView" destination="1cK-e6-Mg5" id="8j5-Km-Jfj"/>
|
||||||
<outlet property="avatarImageView" destination="yXz-Za-4yR" id="f56-93-gxa"/>
|
<outlet property="avatarImageView" destination="yXz-Za-4yR" id="f56-93-gxa"/>
|
||||||
<outlet property="bubbleInfoContainer" destination="7Y6-Py-paB" id="uLv-MM-HIL"/>
|
<outlet property="bubbleInfoContainer" destination="7Y6-Py-paB" id="uLv-MM-HIL"/>
|
||||||
<outlet property="bubbleInfoContainerTopConstraint" destination="Wx9-0o-vzm" id="nLG-nC-lwV"/>
|
<outlet property="bubbleInfoContainerTopConstraint" destination="Wx9-0o-vzm" id="nLG-nC-lwV"/>
|
||||||
@@ -234,6 +280,7 @@
|
|||||||
<outlet property="encryptionImageView" destination="Ujc-3c-e5B" id="7zc-Y7-1jT"/>
|
<outlet property="encryptionImageView" destination="Ujc-3c-e5B" id="7zc-Y7-1jT"/>
|
||||||
<outlet property="encryptionStatusContainerView" destination="uHE-o7-sCe" id="Dl7-QS-WKl"/>
|
<outlet property="encryptionStatusContainerView" destination="uHE-o7-sCe" id="Dl7-QS-WKl"/>
|
||||||
<outlet property="innerContentView" destination="oeI-eO-mFK" id="ap1-He-C6g"/>
|
<outlet property="innerContentView" destination="oeI-eO-mFK" id="ap1-He-C6g"/>
|
||||||
|
<outlet property="innerContentViewBottomContraint" destination="8M5-uW-82s" id="Vn0-6C-LSW"/>
|
||||||
<outlet property="innerContentViewLeadingConstraint" destination="0Fr-0L-9tU" id="ByY-oe-d8Y"/>
|
<outlet property="innerContentViewLeadingConstraint" destination="0Fr-0L-9tU" id="ByY-oe-d8Y"/>
|
||||||
<outlet property="innerContentViewTrailingConstraint" destination="Pbe-4d-q6Y" id="24b-AS-hX4"/>
|
<outlet property="innerContentViewTrailingConstraint" destination="Pbe-4d-q6Y" id="24b-AS-hX4"/>
|
||||||
<outlet property="paginationLabel" destination="r7y-FK-GWS" id="R9V-ix-mDu"/>
|
<outlet property="paginationLabel" destination="r7y-FK-GWS" id="R9V-ix-mDu"/>
|
||||||
@@ -241,10 +288,20 @@
|
|||||||
<outlet property="paginationTitleContainerView" destination="u1e-Q2-PhY" id="Osl-dF-fpA"/>
|
<outlet property="paginationTitleContainerView" destination="u1e-Q2-PhY" id="Osl-dF-fpA"/>
|
||||||
<outlet property="reactionsContainerView" destination="Dj1-m6-1Jw" id="p5e-n1-Wca"/>
|
<outlet property="reactionsContainerView" destination="Dj1-m6-1Jw" id="p5e-n1-Wca"/>
|
||||||
<outlet property="reactionsContentView" destination="SNw-aM-ILI" id="wMe-f0-qhq"/>
|
<outlet property="reactionsContentView" destination="SNw-aM-ILI" id="wMe-f0-qhq"/>
|
||||||
|
<outlet property="reactionsContentViewLeadingConstraint" destination="x1n-oT-dez" id="LUh-3Q-SVr"/>
|
||||||
|
<outlet property="reactionsContentViewTrailingConstraint" destination="ynR-d4-6cf" id="Sy0-o9-vAC"/>
|
||||||
<outlet property="readReceiptsContainerView" destination="4zo-V8-CNe" id="7ek-u4-CX8"/>
|
<outlet property="readReceiptsContainerView" destination="4zo-V8-CNe" id="7ek-u4-CX8"/>
|
||||||
<outlet property="readReceiptsContentView" destination="rQt-NH-Cb0" id="tqw-je-kp9"/>
|
<outlet property="readReceiptsContentView" destination="rQt-NH-Cb0" id="tqw-je-kp9"/>
|
||||||
<outlet property="senderInfoContainerView" destination="w0C-6a-f5M" id="fZE-vY-0nR"/>
|
|
||||||
<outlet property="threadSummaryContainerView" destination="2eB-kB-m20" id="0Y4-4E-I9K"/>
|
<outlet property="threadSummaryContainerView" destination="2eB-kB-m20" id="0Y4-4E-I9K"/>
|
||||||
|
<outlet property="threadSummaryContentView" destination="snf-Ea-To0" id="6Oo-Gj-e4Z"/>
|
||||||
|
<outlet property="threadSummaryContentViewBottomConstraint" destination="4tt-w0-4JE" id="6db-Jz-jys"/>
|
||||||
|
<outlet property="threadSummaryContentViewLeadingConstraint" destination="2lm-T3-dEu" id="jR6-sh-Nul"/>
|
||||||
|
<outlet property="threadSummaryContentViewTrailingConstraint" destination="Qwm-Of-Zgc" id="dfE-aR-MQQ"/>
|
||||||
|
<outlet property="urlPreviewContainerView" destination="57V-Sl-EmD" id="9c6-ai-BY8"/>
|
||||||
|
<outlet property="urlPreviewContentView" destination="vpX-Nl-AEt" id="P8S-Fg-VFQ"/>
|
||||||
|
<outlet property="urlPreviewContentViewLeadingConstraint" destination="lTt-Qx-fuQ" id="5Fc-zl-b1j"/>
|
||||||
|
<outlet property="urlPreviewContentViewTrailingConstraint" destination="ZmA-Ns-chh" id="EXg-Rr-cuJ"/>
|
||||||
|
<outlet property="userNameContainerView" destination="w0C-6a-f5M" id="fZE-vY-0nR"/>
|
||||||
<outlet property="userNameLabel" destination="meG-P8-61b" id="ETK-ag-WYR"/>
|
<outlet property="userNameLabel" destination="meG-P8-61b" id="ETK-ag-WYR"/>
|
||||||
<outlet property="userNameTouchMaskView" destination="ohU-Sc-mgb" id="FwW-aL-kc5"/>
|
<outlet property="userNameTouchMaskView" destination="ohU-Sc-mgb" id="FwW-aL-kc5"/>
|
||||||
</connections>
|
</connections>
|
||||||
@@ -253,5 +310,8 @@
|
|||||||
</objects>
|
</objects>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="encryption_warning" width="12" height="12"/>
|
<image name="encryption_warning" width="12" height="12"/>
|
||||||
|
<systemColor name="systemBackgroundColor">
|
||||||
|
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
</systemColor>
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 New Vector 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 Foundation
|
||||||
|
|
||||||
|
/// BubbleCellContentView decoration view items alignment
|
||||||
|
enum RoomCellDecorationAlignment {
|
||||||
|
case left
|
||||||
|
case right
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 New Vector 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 Foundation
|
||||||
|
|
||||||
|
/// `RoomCellURLPreviewDisplayable` is a protocol indicating that a cell support displaying a URL preview.
|
||||||
|
@objc protocol RoomCellURLPreviewDisplayable {
|
||||||
|
func addURLPreviewView(_ urlPreviewView: UIView)
|
||||||
|
func removeURLPreviewView()
|
||||||
|
}
|
||||||
@@ -239,6 +239,16 @@ extern NSString *const kMXKRoomBubbleCellUrlItemInteraction;
|
|||||||
*/
|
*/
|
||||||
@property (nonatomic) WKWebView *attachmentWebView;
|
@property (nonatomic) WKWebView *attachmentWebView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Indicate true if the cell needs vertical space in the text to position UI components.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, readonly) BOOL isTextViewNeedsPositioningVerticalSpace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Use bubbleData.attributedTextMessage or bubbleData.attributedTextMessageWithoutPositioningSpace according to isTextViewNeedsPositioningVerticalSpace value.
|
||||||
|
*/
|
||||||
|
@property (nonatomic, readonly) NSAttributedString *suitableAttributedTextMessage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Called during the designated initializer of the UITableViewCell class to set the default
|
Called during the designated initializer of the UITableViewCell class to set the default
|
||||||
properties values.
|
properties values.
|
||||||
|
|||||||
@@ -30,6 +30,8 @@
|
|||||||
#import "MXKMessageTextView.h"
|
#import "MXKMessageTextView.h"
|
||||||
#import "UITextView+MatrixKit.h"
|
#import "UITextView+MatrixKit.h"
|
||||||
|
|
||||||
|
#import "GeneratedInterface-Swift.h"
|
||||||
|
|
||||||
#pragma mark - Constant definitions
|
#pragma mark - Constant definitions
|
||||||
NSString *const kMXKRoomBubbleCellTapOnMessageTextView = @"kMXKRoomBubbleCellTapOnMessageTextView";
|
NSString *const kMXKRoomBubbleCellTapOnMessageTextView = @"kMXKRoomBubbleCellTapOnMessageTextView";
|
||||||
NSString *const kMXKRoomBubbleCellTapOnSenderNameLabel = @"kMXKRoomBubbleCellTapOnSenderNameLabel";
|
NSString *const kMXKRoomBubbleCellTapOnSenderNameLabel = @"kMXKRoomBubbleCellTapOnSenderNameLabel";
|
||||||
@@ -122,6 +124,7 @@ static BOOL _disableLongPressGestureOnEvent;
|
|||||||
_allTextHighlighted = NO;
|
_allTextHighlighted = NO;
|
||||||
_isAutoAnimatedGif = NO;
|
_isAutoAnimatedGif = NO;
|
||||||
_tmpSubviews = [NSMutableArray array];
|
_tmpSubviews = [NSMutableArray array];
|
||||||
|
_isTextViewNeedsPositioningVerticalSpace = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)awakeFromNib
|
- (void)awakeFromNib
|
||||||
@@ -366,18 +369,23 @@ static BOOL _disableLongPressGestureOnEvent;
|
|||||||
{
|
{
|
||||||
if (_allTextHighlighted)
|
if (_allTextHighlighted)
|
||||||
{
|
{
|
||||||
NSMutableAttributedString *highlightedString = [[NSMutableAttributedString alloc] initWithAttributedString:bubbleData.attributedTextMessage];
|
NSMutableAttributedString *highlightedString = [[NSMutableAttributedString alloc] initWithAttributedString:self.suitableAttributedTextMessage];
|
||||||
UIColor *color = self.tintColor ? self.tintColor : [UIColor lightGrayColor];
|
UIColor *color = self.tintColor ? self.tintColor : [UIColor lightGrayColor];
|
||||||
[highlightedString addAttribute:NSBackgroundColorAttributeName value:color range:NSMakeRange(0, highlightedString.length)];
|
[highlightedString addAttribute:NSBackgroundColorAttributeName value:color range:NSMakeRange(0, highlightedString.length)];
|
||||||
self.messageTextView.attributedText = highlightedString;
|
self.messageTextView.attributedText = highlightedString;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
self.messageTextView.attributedText = bubbleData.attributedTextMessage;
|
self.messageTextView.attributedText = self.suitableAttributedTextMessage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSAttributedString *)suitableAttributedTextMessage
|
||||||
|
{
|
||||||
|
return self.isTextViewNeedsPositioningVerticalSpace ? bubbleData.attributedTextMessage : bubbleData.attributedTextMessageWithoutPositioningSpace;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)highlightTextMessageForEvent:(NSString*)eventId
|
- (void)highlightTextMessageForEvent:(NSString*)eventId
|
||||||
{
|
{
|
||||||
if (self.messageTextView)
|
if (self.messageTextView)
|
||||||
@@ -389,7 +397,7 @@ static BOOL _disableLongPressGestureOnEvent;
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Restore original string
|
// Restore original string
|
||||||
self.messageTextView.attributedText = bubbleData.attributedTextMessage;
|
self.messageTextView.attributedText = self.suitableAttributedTextMessage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -559,14 +567,14 @@ static BOOL _disableLongPressGestureOnEvent;
|
|||||||
// Underline attached file name
|
// Underline attached file name
|
||||||
if (self.isBubbleDataContainsFileAttachment)
|
if (self.isBubbleDataContainsFileAttachment)
|
||||||
{
|
{
|
||||||
NSMutableAttributedString *updatedText = [[NSMutableAttributedString alloc] initWithAttributedString:bubbleData.attributedTextMessage];
|
NSMutableAttributedString *updatedText = [[NSMutableAttributedString alloc] initWithAttributedString:self.suitableAttributedTextMessage];
|
||||||
[updatedText addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:NSMakeRange(0, updatedText.length)];
|
[updatedText addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInteger:NSUnderlineStyleSingle] range:NSMakeRange(0, updatedText.length)];
|
||||||
|
|
||||||
newText = updatedText;
|
newText = updatedText;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
newText = bubbleData.attributedTextMessage;
|
newText = self.suitableAttributedTextMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update the text only if it is required
|
// update the text only if it is required
|
||||||
@@ -927,8 +935,24 @@ static BOOL _disableLongPressGestureOnEvent;
|
|||||||
}
|
}
|
||||||
else if (cell.messageTextView)
|
else if (cell.messageTextView)
|
||||||
{
|
{
|
||||||
|
CGFloat maxTextViewWidth;
|
||||||
|
|
||||||
|
RoomTimelineConfiguration *timelineConfiguration = [RoomTimelineConfiguration shared];
|
||||||
|
|
||||||
|
id<RoomCellLayoutUpdating> cellLayoutUpdater = timelineConfiguration.currentStyle.cellLayoutUpdater;
|
||||||
|
|
||||||
|
// Handle updated text view layout if needed
|
||||||
|
if (cellLayoutUpdater)
|
||||||
|
{
|
||||||
|
maxTextViewWidth = [cellLayoutUpdater maximumTextViewWidthFor:cell cellData:cellData maximumCellWidth:maxWidth];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
maxTextViewWidth = maxWidth - (cell.msgTextViewLeadingConstraint.constant + cell.msgTextViewTrailingConstraint.constant);
|
||||||
|
}
|
||||||
|
|
||||||
// Update maximum width available for the textview
|
// Update maximum width available for the textview
|
||||||
bubbleData.maxTextViewWidth = maxWidth - (cell.msgTextViewLeadingConstraint.constant + cell.msgTextViewTrailingConstraint.constant);
|
bubbleData.maxTextViewWidth = maxTextViewWidth;
|
||||||
|
|
||||||
// Retrieve the suggested height of the message content
|
// Retrieve the suggested height of the message content
|
||||||
rowHeight = bubbleData.contentSize.height;
|
rowHeight = bubbleData.contentSize.height;
|
||||||
@@ -950,101 +974,33 @@ static BOOL _disableLongPressGestureOnEvent;
|
|||||||
{
|
{
|
||||||
[super prepareForReuse];
|
[super prepareForReuse];
|
||||||
|
|
||||||
|
bubbleData = nil;
|
||||||
|
delegate = nil;
|
||||||
|
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
|
||||||
|
self.readReceiptsAlignment = ReadReceiptAlignmentLeft;
|
||||||
|
|
||||||
|
_allTextHighlighted = NO;
|
||||||
|
_isAutoAnimatedGif = NO;
|
||||||
|
|
||||||
|
[self removeHTMLBlockquoteSideBorderViews];
|
||||||
|
[self removeTemporarySubviews];
|
||||||
|
[self cleanAttachmentView];
|
||||||
|
[self clearBubbleInfoContainer];
|
||||||
|
[self clearBubbleOverlayContainer];
|
||||||
|
[self resetConstraintsConstantToDefault];
|
||||||
|
[self clearAttachmentWebView];
|
||||||
|
|
||||||
[self didEndDisplay];
|
[self didEndDisplay];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)didEndDisplay
|
- (void)didEndDisplay
|
||||||
{
|
{
|
||||||
bubbleData = nil;
|
[self removeReadMarkerView];
|
||||||
|
[self cleanProgressView];
|
||||||
|
|
||||||
for (UIView *sideBorder in htmlBlockquoteSideBorderViews)
|
// TODO: Stop gif animation
|
||||||
{
|
|
||||||
[sideBorder removeFromSuperview];
|
|
||||||
}
|
|
||||||
[htmlBlockquoteSideBorderViews removeAllObjects];
|
|
||||||
htmlBlockquoteSideBorderViews = nil;
|
|
||||||
|
|
||||||
if (_attachmentWebView)
|
|
||||||
{
|
|
||||||
[_attachmentWebView removeFromSuperview];
|
|
||||||
_attachmentWebView.navigationDelegate = nil;
|
|
||||||
_attachmentWebView = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_readMarkerView)
|
|
||||||
{
|
|
||||||
[_readMarkerView removeFromSuperview];
|
|
||||||
_readMarkerView = nil;
|
|
||||||
_readMarkerViewTopConstraint = nil;
|
|
||||||
_readMarkerViewLeadingConstraint = nil;
|
|
||||||
_readMarkerViewTrailingConstraint = nil;
|
|
||||||
_readMarkerViewHeightConstraint = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.attachmentView)
|
|
||||||
{
|
|
||||||
// Remove all gesture recognizer
|
|
||||||
while (self.attachmentView.gestureRecognizers.count)
|
|
||||||
{
|
|
||||||
[self.attachmentView removeGestureRecognizer:self.attachmentView.gestureRecognizers[0]];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent the cell from displaying again the image in case of reuse.
|
|
||||||
self.attachmentView.image = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove potential dateTime (or unsent) label(s)
|
|
||||||
if (self.bubbleInfoContainer && self.bubbleInfoContainer.subviews.count > 0)
|
|
||||||
{
|
|
||||||
NSArray* subviews = self.bubbleInfoContainer.subviews;
|
|
||||||
|
|
||||||
for (UIView *view in subviews)
|
|
||||||
{
|
|
||||||
[view removeFromSuperview];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.bubbleInfoContainer.hidden = YES;
|
|
||||||
|
|
||||||
// Remove temporary subviews
|
|
||||||
for (UIView *view in self.tmpSubviews)
|
|
||||||
{
|
|
||||||
[view removeFromSuperview];
|
|
||||||
}
|
|
||||||
[self.tmpSubviews removeAllObjects];
|
|
||||||
|
|
||||||
// Remove potential overlay subviews
|
|
||||||
if (self.bubbleOverlayContainer)
|
|
||||||
{
|
|
||||||
NSArray* subviews = self.bubbleOverlayContainer.subviews;
|
|
||||||
|
|
||||||
for (UIView *view in subviews)
|
|
||||||
{
|
|
||||||
[view removeFromSuperview];
|
|
||||||
}
|
|
||||||
|
|
||||||
self.bubbleOverlayContainer.hidden = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.progressView)
|
|
||||||
{
|
|
||||||
[self stopProgressUI];
|
|
||||||
|
|
||||||
// Remove long tap gesture on the progressView
|
|
||||||
while (self.progressView.gestureRecognizers.count)
|
|
||||||
{
|
|
||||||
[self.progressView removeGestureRecognizer:self.progressView.gestureRecognizers[0]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
||||||
|
|
||||||
delegate = nil;
|
|
||||||
|
|
||||||
self.readReceiptsAlignment = ReadReceiptAlignmentLeft;
|
|
||||||
_allTextHighlighted = NO;
|
|
||||||
_isAutoAnimatedGif = NO;
|
|
||||||
|
|
||||||
[self resetConstraintsConstantToDefault];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)shouldInteractWithURL:(NSURL *)URL urlItemInteraction:(UITextItemInteraction)urlItemInteraction associatedEvent:(MXEvent*)associatedEvent
|
- (BOOL)shouldInteractWithURL:(NSURL *)URL urlItemInteraction:(UITextItemInteraction)urlItemInteraction associatedEvent:(MXEvent*)associatedEvent
|
||||||
@@ -1135,6 +1091,111 @@ static BOOL _disableLongPressGestureOnEvent;
|
|||||||
[self.tmpSubviews addObject:subview];
|
[self.tmpSubviews addObject:subview];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - Cleaning
|
||||||
|
|
||||||
|
- (void)removeHTMLBlockquoteSideBorderViews
|
||||||
|
{
|
||||||
|
for (UIView *sideBorder in htmlBlockquoteSideBorderViews)
|
||||||
|
{
|
||||||
|
[sideBorder removeFromSuperview];
|
||||||
|
}
|
||||||
|
[htmlBlockquoteSideBorderViews removeAllObjects];
|
||||||
|
htmlBlockquoteSideBorderViews = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)removeReadMarkerView
|
||||||
|
{
|
||||||
|
if (_readMarkerView)
|
||||||
|
{
|
||||||
|
[_readMarkerView removeFromSuperview];
|
||||||
|
_readMarkerView = nil;
|
||||||
|
_readMarkerViewTopConstraint = nil;
|
||||||
|
_readMarkerViewLeadingConstraint = nil;
|
||||||
|
_readMarkerViewTrailingConstraint = nil;
|
||||||
|
_readMarkerViewHeightConstraint = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)removeTemporarySubviews
|
||||||
|
{
|
||||||
|
// Remove temporary subviews
|
||||||
|
for (UIView *view in self.tmpSubviews)
|
||||||
|
{
|
||||||
|
[view removeFromSuperview];
|
||||||
|
}
|
||||||
|
[self.tmpSubviews removeAllObjects];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)cleanAttachmentView
|
||||||
|
{
|
||||||
|
if (self.attachmentView)
|
||||||
|
{
|
||||||
|
// Remove all gesture recognizer
|
||||||
|
while (self.attachmentView.gestureRecognizers.count)
|
||||||
|
{
|
||||||
|
[self.attachmentView removeGestureRecognizer:self.attachmentView.gestureRecognizers[0]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent the cell from displaying again the image in case of reuse.
|
||||||
|
self.attachmentView.image = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)clearBubbleInfoContainer
|
||||||
|
{
|
||||||
|
// Remove potential dateTime (or unsent) label(s)
|
||||||
|
if (self.bubbleInfoContainer && self.bubbleInfoContainer.subviews.count > 0)
|
||||||
|
{
|
||||||
|
NSArray* subviews = self.bubbleInfoContainer.subviews;
|
||||||
|
|
||||||
|
for (UIView *view in subviews)
|
||||||
|
{
|
||||||
|
[view removeFromSuperview];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.bubbleInfoContainer.hidden = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)clearBubbleOverlayContainer
|
||||||
|
{
|
||||||
|
// Remove potential overlay subviews
|
||||||
|
if (self.bubbleOverlayContainer)
|
||||||
|
{
|
||||||
|
NSArray* subviews = self.bubbleOverlayContainer.subviews;
|
||||||
|
|
||||||
|
for (UIView *view in subviews)
|
||||||
|
{
|
||||||
|
[view removeFromSuperview];
|
||||||
|
}
|
||||||
|
|
||||||
|
self.bubbleOverlayContainer.hidden = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)cleanProgressView
|
||||||
|
{
|
||||||
|
if (self.progressView)
|
||||||
|
{
|
||||||
|
[self stopProgressUI];
|
||||||
|
|
||||||
|
// Remove long tap gesture on the progressView
|
||||||
|
while (self.progressView.gestureRecognizers.count)
|
||||||
|
{
|
||||||
|
[self.progressView removeGestureRecognizer:self.progressView.gestureRecognizers[0]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)clearAttachmentWebView
|
||||||
|
{
|
||||||
|
if (_attachmentWebView)
|
||||||
|
{
|
||||||
|
[_attachmentWebView removeFromSuperview];
|
||||||
|
_attachmentWebView.navigationDelegate = nil;
|
||||||
|
_attachmentWebView = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - Attachment progress handling
|
#pragma mark - Attachment progress handling
|
||||||
|
|
||||||
- (void)updateProgressUI:(NSDictionary*)statisticsDict
|
- (void)updateProgressUI:(NSDictionary*)statisticsDict
|
||||||
|
|||||||
@@ -47,6 +47,34 @@ typedef NS_ENUM(NSUInteger, RoomTimelineCellIdentifier) {
|
|||||||
RoomTimelineCellIdentifierOutgoingTextMessageEncryptedWithoutSenderName,
|
RoomTimelineCellIdentifierOutgoingTextMessageEncryptedWithoutSenderName,
|
||||||
RoomTimelineCellIdentifierOutgoingTextMessageEncryptedWithPaginationTitleWithoutSenderName,
|
RoomTimelineCellIdentifierOutgoingTextMessageEncryptedWithPaginationTitleWithoutSenderName,
|
||||||
|
|
||||||
|
// - Emote
|
||||||
|
// -- Incoming
|
||||||
|
// --- Clear
|
||||||
|
RoomTimelineCellIdentifierIncomingEmote,
|
||||||
|
RoomTimelineCellIdentifierIncomingEmoteWithoutSenderInfo,
|
||||||
|
RoomTimelineCellIdentifierIncomingEmoteWithPaginationTitle,
|
||||||
|
RoomTimelineCellIdentifierIncomingEmoteWithoutSenderName,
|
||||||
|
RoomTimelineCellIdentifierIncomingEmoteWithPaginationTitleWithoutSenderName,
|
||||||
|
// --- Encrypted
|
||||||
|
RoomTimelineCellIdentifierIncomingEmoteEncrypted,
|
||||||
|
RoomTimelineCellIdentifierIncomingEmoteEncryptedWithoutSenderInfo,
|
||||||
|
RoomTimelineCellIdentifierIncomingEmoteEncryptedWithPaginationTitle,
|
||||||
|
RoomTimelineCellIdentifierIncomingEmoteEncryptedWithoutSenderName,
|
||||||
|
RoomTimelineCellIdentifierIncomingEmoteEncryptedWithPaginationTitleWithoutSenderName,
|
||||||
|
// -- Outgoing
|
||||||
|
// --- Clear
|
||||||
|
RoomTimelineCellIdentifierOutgoingEmote,
|
||||||
|
RoomTimelineCellIdentifierOutgoingEmoteWithoutSenderInfo,
|
||||||
|
RoomTimelineCellIdentifierOutgoingEmoteWithPaginationTitle,
|
||||||
|
RoomTimelineCellIdentifierOutgoingEmoteWithoutSenderName,
|
||||||
|
RoomTimelineCellIdentifierOutgoingEmoteWithPaginationTitleWithoutSenderName,
|
||||||
|
// --- Encrypted
|
||||||
|
RoomTimelineCellIdentifierOutgoingEmoteEncrypted,
|
||||||
|
RoomTimelineCellIdentifierOutgoingEmoteEncryptedWithoutSenderInfo,
|
||||||
|
RoomTimelineCellIdentifierOutgoingEmoteEncryptedWithPaginationTitle,
|
||||||
|
RoomTimelineCellIdentifierOutgoingEmoteEncryptedWithoutSenderName,
|
||||||
|
RoomTimelineCellIdentifierOutgoingEmoteEncryptedWithPaginationTitleWithoutSenderName,
|
||||||
|
|
||||||
// - Attachment
|
// - Attachment
|
||||||
// -- Incoming
|
// -- Incoming
|
||||||
// --- Clear
|
// --- Clear
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import MatrixSDK
|
||||||
|
|
||||||
@objc protocol SizableBaseBubbleCellType: BaseBubbleCellType {
|
@objc protocol SizableBaseBubbleCellType: BaseBubbleCellType {
|
||||||
static func sizingViewHeightHashValue(from bubbleCellData: MXKRoomBubbleCellData) -> Int
|
static func sizingViewHeightHashValue(from bubbleCellData: MXKRoomBubbleCellData) -> Int
|
||||||
@@ -33,6 +34,8 @@ class SizableBaseBubbleCell: BaseBubbleCell, SizableBaseBubbleCellType {
|
|||||||
private static let reactionsViewSizer = BubbleReactionsViewSizer()
|
private static let reactionsViewSizer = BubbleReactionsViewSizer()
|
||||||
private static let reactionsViewModelBuilder = BubbleReactionsViewModelBuilder()
|
private static let reactionsViewModelBuilder = BubbleReactionsViewModelBuilder()
|
||||||
|
|
||||||
|
private static let urlPreviewViewSizer = URLPreviewViewSizer()
|
||||||
|
|
||||||
private class var sizingView: SizableBaseBubbleCell {
|
private class var sizingView: SizableBaseBubbleCell {
|
||||||
let sizingView: SizableBaseBubbleCell
|
let sizingView: SizableBaseBubbleCell
|
||||||
|
|
||||||
@@ -135,7 +138,22 @@ class SizableBaseBubbleCell: BaseBubbleCell, SizableBaseBubbleCellType {
|
|||||||
if sizingView is BubbleCellThreadSummaryDisplayable,
|
if sizingView is BubbleCellThreadSummaryDisplayable,
|
||||||
let roomBubbleCellData = cellData as? RoomBubbleCellData,
|
let roomBubbleCellData = cellData as? RoomBubbleCellData,
|
||||||
roomBubbleCellData.hasThreadRoot {
|
roomBubbleCellData.hasThreadRoot {
|
||||||
|
|
||||||
|
let bottomMargin = sizingView.bubbleCellContentView?.threadSummaryContentViewBottomConstraint.constant ?? 0
|
||||||
|
|
||||||
height += RoomBubbleCellLayout.threadSummaryViewHeight
|
height += RoomBubbleCellLayout.threadSummaryViewHeight
|
||||||
|
height += bottomMargin
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add URL preview view height if needed
|
||||||
|
if sizingView is RoomCellURLPreviewDisplayable,
|
||||||
|
let roomBubbleCellData = cellData as? RoomBubbleCellData, let firstBubbleComponent =
|
||||||
|
roomBubbleCellData.getFirstBubbleComponentWithDisplay(), firstBubbleComponent.showURLPreview, let urlPreviewData = firstBubbleComponent.urlPreviewData as? URLPreviewData {
|
||||||
|
|
||||||
|
let urlPreviewMaxWidth = sizingView.bubbleCellContentView?.urlPreviewContentView.frame.width ?? roomBubbleCellData.maxTextViewWidth
|
||||||
|
|
||||||
|
let urlPreviewHeight = self.urlPreviewViewSizer.height(for: urlPreviewData, fittingWidth: urlPreviewMaxWidth)
|
||||||
|
height+=urlPreviewHeight
|
||||||
}
|
}
|
||||||
|
|
||||||
return height
|
return height
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 New Vector 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 Foundation
|
||||||
|
|
||||||
|
|
||||||
|
/// Bubble style room cell layout constants
|
||||||
|
@objcMembers
|
||||||
|
final class BubbleRoomCellLayoutConstants: NSObject {
|
||||||
|
|
||||||
|
static let outgoingBubbleBackgroundMargins: UIEdgeInsets = UIEdgeInsets(top: 0, left: 80, bottom: 0, right: 34)
|
||||||
|
|
||||||
|
static let incomingBubbleBackgroundMargins: UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 80)
|
||||||
|
|
||||||
|
static let threadSummaryViewMargins: UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 5, right: 0)
|
||||||
|
}
|
||||||
+72
-184
@@ -50,40 +50,16 @@ class BubbleRoomCellLayoutUpdater: RoomCellLayoutUpdating {
|
|||||||
|
|
||||||
func updateLayout(forIncomingTextMessageCell cell: MXKRoomBubbleTableViewCell, andCellData cellData: MXKRoomBubbleCellData) {
|
func updateLayout(forIncomingTextMessageCell cell: MXKRoomBubbleTableViewCell, andCellData cellData: MXKRoomBubbleCellData) {
|
||||||
|
|
||||||
if let messageBubbleBackgroundView = cell.messageBubbleBackgroundView {
|
|
||||||
|
|
||||||
if self.canUseBubbleBackground(forCell: cell, withCellData: cellData) {
|
|
||||||
|
|
||||||
messageBubbleBackgroundView.isHidden = false
|
|
||||||
|
|
||||||
self.updateMessageBubbleBackgroundView(messageBubbleBackgroundView, withCell: cell, andCellData: cellData)
|
|
||||||
} else {
|
|
||||||
messageBubbleBackgroundView.isHidden = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLayout(forOutgoingTextMessageCell cell: MXKRoomBubbleTableViewCell, andCellData cellData: MXKRoomBubbleCellData) {
|
func updateLayout(forOutgoingTextMessageCell cell: MXKRoomBubbleTableViewCell, andCellData cellData: MXKRoomBubbleCellData) {
|
||||||
|
|
||||||
if let messageBubbleBackgroundView = cell.messageBubbleBackgroundView {
|
|
||||||
|
|
||||||
if self.canUseBubbleBackground(forCell: cell, withCellData: cellData) {
|
|
||||||
|
|
||||||
messageBubbleBackgroundView.isHidden = false
|
|
||||||
|
|
||||||
self.updateMessageBubbleBackgroundView(messageBubbleBackgroundView, withCell: cell, andCellData: cellData)
|
|
||||||
} else {
|
|
||||||
messageBubbleBackgroundView.isHidden = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupLayout(forIncomingTextMessageCell cell: MXKRoomBubbleTableViewCell) {
|
func setupLayout(forIncomingTextMessageCell cell: MXKRoomBubbleTableViewCell) {
|
||||||
|
|
||||||
self.setupIncomingMessageTextViewMargins(for: cell)
|
self.setupIncomingMessageTextViewMargins(for: cell)
|
||||||
|
|
||||||
self.addBubbleBackgroundViewToCell(cell, backgroundColor: self.incomingColor)
|
|
||||||
|
|
||||||
cell.setNeedsUpdateConstraints()
|
cell.setNeedsUpdateConstraints()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,8 +70,6 @@ class BubbleRoomCellLayoutUpdater: RoomCellLayoutUpdating {
|
|||||||
// Hide avatar view
|
// Hide avatar view
|
||||||
cell.pictureView?.isHidden = true
|
cell.pictureView?.isHidden = true
|
||||||
|
|
||||||
self.addBubbleBackgroundViewToCell(cell, backgroundColor: self.outgoingColor)
|
|
||||||
|
|
||||||
cell.setNeedsUpdateConstraints()
|
cell.setNeedsUpdateConstraints()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,6 +97,49 @@ class BubbleRoomCellLayoutUpdater: RoomCellLayoutUpdating {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func maximumTextViewWidth(for cell: MXKRoomBubbleTableViewCell, cellData: MXKCellData, maximumCellWidth: CGFloat) -> CGFloat {
|
||||||
|
|
||||||
|
guard cell.messageTextView != nil else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
let maxTextViewWidth: CGFloat
|
||||||
|
|
||||||
|
let textViewleftMargin: CGFloat
|
||||||
|
let textViewRightMargin: CGFloat
|
||||||
|
|
||||||
|
if let roomBubbleCellData = cellData as? RoomBubbleCellData, cell is MXKRoomIncomingTextMsgBubbleCell || cell is MXKRoomOutgoingTextMsgBubbleCell {
|
||||||
|
|
||||||
|
if roomBubbleCellData.isIncoming {
|
||||||
|
let textViewInsets = self.getIncomingMessageTextViewInsets(from: cell)
|
||||||
|
|
||||||
|
textViewleftMargin = cell.msgTextViewLeadingConstraint.constant + textViewInsets.left
|
||||||
|
// Right inset is in fact margin in this case
|
||||||
|
textViewRightMargin = textViewInsets.right
|
||||||
|
} else {
|
||||||
|
let textViewMargins = self.getOutgoingMessageTextViewMargins(from: cell)
|
||||||
|
|
||||||
|
textViewleftMargin = textViewMargins.left
|
||||||
|
textViewRightMargin = textViewMargins.right
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
textViewleftMargin = cell.msgTextViewLeadingConstraint.constant
|
||||||
|
textViewRightMargin = cell.msgTextViewTrailingConstraint.constant
|
||||||
|
}
|
||||||
|
|
||||||
|
maxTextViewWidth = maximumCellWidth - (textViewleftMargin + textViewRightMargin)
|
||||||
|
|
||||||
|
guard maxTextViewWidth >= 0 else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
guard maxTextViewWidth <= maximumCellWidth else {
|
||||||
|
return maxTextViewWidth
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxTextViewWidth
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Themable
|
// MARK: Themable
|
||||||
|
|
||||||
func update(theme: Theme) {
|
func update(theme: Theme) {
|
||||||
@@ -131,166 +148,21 @@ class BubbleRoomCellLayoutUpdater: RoomCellLayoutUpdating {
|
|||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
// MARK: Bubble background view
|
// MARK: Text message
|
||||||
|
|
||||||
private func createBubbleBackgroundView(with backgroundColor: UIColor) -> RoomMessageBubbleBackgroundView {
|
|
||||||
|
|
||||||
let bubbleBackgroundView = RoomMessageBubbleBackgroundView()
|
|
||||||
bubbleBackgroundView.backgroundColor = backgroundColor
|
|
||||||
|
|
||||||
return bubbleBackgroundView
|
|
||||||
}
|
|
||||||
|
|
||||||
private func addBubbleBackgroundViewToCell(_ bubbleCell: MXKRoomBubbleTableViewCell, backgroundColor: UIColor) {
|
|
||||||
|
|
||||||
guard let messageTextView = bubbleCell.messageTextView else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let topMargin: CGFloat = 0.0
|
|
||||||
let leftMargin: CGFloat = 5.0
|
|
||||||
let rightMargin: CGFloat = 45.0 // Add extra space for timestamp
|
|
||||||
|
|
||||||
let bubbleBackgroundView = self.createBubbleBackgroundView(with: backgroundColor)
|
|
||||||
|
|
||||||
bubbleCell.contentView.insertSubview(bubbleBackgroundView, at: 0)
|
|
||||||
|
|
||||||
let topAnchor = messageTextView.topAnchor
|
|
||||||
let leadingAnchor = messageTextView.leadingAnchor
|
|
||||||
let trailingAnchor = messageTextView.trailingAnchor
|
|
||||||
|
|
||||||
bubbleBackgroundView.updateHeight(messageTextView.frame.height)
|
|
||||||
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
bubbleBackgroundView.topAnchor.constraint(equalTo: topAnchor, constant: topMargin),
|
|
||||||
bubbleBackgroundView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: -leftMargin),
|
|
||||||
bubbleBackgroundView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: rightMargin)
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
private func canUseBubbleBackground(forCell cell: MXKRoomBubbleTableViewCell, withCellData cellData: MXKRoomBubbleCellData) -> Bool {
|
|
||||||
|
|
||||||
guard let firstComponent = cellData.getFirstBubbleComponentWithDisplay(), let firstEvent = firstComponent.event else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
switch firstEvent.eventType {
|
|
||||||
case .roomMessage:
|
|
||||||
if let messageType = firstEvent.messageType {
|
|
||||||
switch messageType {
|
|
||||||
case .text, .file:
|
|
||||||
return true
|
|
||||||
case .emote:
|
|
||||||
// Explicitely disable bubble for emotes
|
|
||||||
return false
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
private func getTextMessageHeight(for cell: MXKRoomBubbleTableViewCell, andCellData cellData: MXKRoomBubbleCellData) -> CGFloat? {
|
|
||||||
|
|
||||||
guard let roomBubbleCellData = cellData as? RoomBubbleCellData,
|
|
||||||
let lastBubbleComponent = cellData.getLastBubbleComponentWithDisplay(),
|
|
||||||
let firstComponent = roomBubbleCellData.getFirstBubbleComponentWithDisplay() else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let bubbleHeight: CGFloat
|
|
||||||
|
|
||||||
let lastEventId = lastBubbleComponent.event.eventId
|
|
||||||
let lastMessageBottomPosition = cell.bottomPosition(ofEvent: lastEventId)
|
|
||||||
|
|
||||||
let firstEventId = firstComponent.event.eventId
|
|
||||||
let firstMessageTopPosition = cell.topPosition(ofEvent: firstEventId)
|
|
||||||
|
|
||||||
let additionalContentHeight = roomBubbleCellData.additionalContentHeight
|
|
||||||
|
|
||||||
bubbleHeight = lastMessageBottomPosition - firstMessageTopPosition - additionalContentHeight
|
|
||||||
|
|
||||||
guard bubbleHeight >= 0 else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return bubbleHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Improve text message height calculation
|
|
||||||
// This method is closer to final result but lack of stability because of extra vertical space not handled here.
|
|
||||||
// private func getTextMessageHeight(for cell: MXKRoomBubbleTableViewCell, andCellData cellData: MXKRoomBubbleCellData) -> CGFloat? {
|
|
||||||
//
|
|
||||||
// guard let roomBubbleCellData = cellData as? RoomBubbleCellData,
|
|
||||||
// let firstComponent = roomBubbleCellData.getFirstBubbleComponentWithDisplay() else {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let bubbleHeight: CGFloat
|
|
||||||
//
|
|
||||||
// let componentIndex = cellData.bubbleComponentIndex(forEventId: firstComponent.event.eventId)
|
|
||||||
//
|
|
||||||
// let componentFrame = cell.componentFrameInContentView(for: componentIndex)
|
|
||||||
//
|
|
||||||
// bubbleHeight = componentFrame.height
|
|
||||||
//
|
|
||||||
// guard bubbleHeight >= 0 else {
|
|
||||||
// return nil
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return bubbleHeight
|
|
||||||
// }
|
|
||||||
|
|
||||||
private func getMessageBubbleBackgroundHeight(for cell: MXKRoomBubbleTableViewCell, andCellData cellData: MXKRoomBubbleCellData) -> CGFloat? {
|
|
||||||
|
|
||||||
var finalBubbleHeight: CGFloat?
|
|
||||||
let extraMargin: CGFloat = 4.0
|
|
||||||
|
|
||||||
if let bubbleHeight = self.getTextMessageHeight(for: cell, andCellData: cellData) {
|
|
||||||
finalBubbleHeight = bubbleHeight + extraMargin
|
|
||||||
|
|
||||||
} else if let messageTextViewHeight = cell.messageTextView?.frame.height {
|
|
||||||
|
|
||||||
finalBubbleHeight = messageTextViewHeight + extraMargin
|
|
||||||
}
|
|
||||||
|
|
||||||
return finalBubbleHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
private func updateMessageBubbleBackgroundView(_ roomMessageBubbleBackgroundView: RoomMessageBubbleBackgroundView, withCell cell: MXKRoomBubbleTableViewCell, andCellData cellData: MXKRoomBubbleCellData) -> Bool {
|
|
||||||
|
|
||||||
if let bubbleHeight = self.getMessageBubbleBackgroundHeight(for: cell, andCellData: cellData) {
|
|
||||||
return roomMessageBubbleBackgroundView.updateHeight(bubbleHeight)
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func getIncomingMessageTextViewInsets(from bubbleCell: MXKRoomBubbleTableViewCell) -> UIEdgeInsets {
|
private func getIncomingMessageTextViewInsets(from bubbleCell: MXKRoomBubbleTableViewCell) -> UIEdgeInsets {
|
||||||
|
|
||||||
let messageViewMarginTop: CGFloat
|
let bubbleBgRightMargin: CGFloat = 45
|
||||||
let messageViewMarginBottom: CGFloat = -2.0
|
let messageViewMarginTop: CGFloat = 0
|
||||||
let messageViewMarginLeft: CGFloat = 3.0
|
let messageViewMarginBottom: CGFloat = -0
|
||||||
let messageViewMarginRight: CGFloat = 80
|
let messageViewMarginLeft: CGFloat = 0
|
||||||
|
let messageViewMarginRight: CGFloat = 80 + bubbleBgRightMargin
|
||||||
if bubbleCell.userNameLabel != nil {
|
|
||||||
messageViewMarginTop = 10.0
|
|
||||||
} else {
|
|
||||||
messageViewMarginTop = 0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
let messageViewInsets = UIEdgeInsets(top: messageViewMarginTop, left: messageViewMarginLeft, bottom: messageViewMarginBottom, right: messageViewMarginRight)
|
let messageViewInsets = UIEdgeInsets(top: messageViewMarginTop, left: messageViewMarginLeft, bottom: messageViewMarginBottom, right: messageViewMarginRight)
|
||||||
|
|
||||||
return messageViewInsets
|
return messageViewInsets
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Text message
|
|
||||||
|
|
||||||
private func setupIncomingMessageTextViewMargins(for cell: MXKRoomBubbleTableViewCell) {
|
private func setupIncomingMessageTextViewMargins(for cell: MXKRoomBubbleTableViewCell) {
|
||||||
|
|
||||||
guard cell.messageTextView != nil else {
|
guard cell.messageTextView != nil else {
|
||||||
@@ -302,7 +174,23 @@ class BubbleRoomCellLayoutUpdater: RoomCellLayoutUpdating {
|
|||||||
cell.msgTextViewBottomConstraint.constant += messageViewInsets.bottom
|
cell.msgTextViewBottomConstraint.constant += messageViewInsets.bottom
|
||||||
cell.msgTextViewTopConstraint.constant += messageViewInsets.top
|
cell.msgTextViewTopConstraint.constant += messageViewInsets.top
|
||||||
cell.msgTextViewLeadingConstraint.constant += messageViewInsets.left
|
cell.msgTextViewLeadingConstraint.constant += messageViewInsets.left
|
||||||
cell.msgTextViewTrailingConstraint.constant += messageViewInsets.right
|
|
||||||
|
// Right inset is in fact margin in this case
|
||||||
|
cell.msgTextViewTrailingConstraint.constant = messageViewInsets.right
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getOutgoingMessageTextViewMargins(from bubbleCell: MXKRoomBubbleTableViewCell) -> UIEdgeInsets {
|
||||||
|
|
||||||
|
let innerContentLeftMargin: CGFloat = 57
|
||||||
|
|
||||||
|
let messageViewMarginTop: CGFloat = 0
|
||||||
|
let messageViewMarginBottom: CGFloat = 0
|
||||||
|
let messageViewMarginLeft: CGFloat = 80.0 + innerContentLeftMargin
|
||||||
|
let messageViewMarginRight: CGFloat = 78.0
|
||||||
|
|
||||||
|
let messageViewInsets = UIEdgeInsets(top: messageViewMarginTop, left: messageViewMarginLeft, bottom: messageViewMarginBottom, right: messageViewMarginRight)
|
||||||
|
|
||||||
|
return messageViewInsets
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupOutgoingMessageTextViewMargins(for cell: MXKRoomBubbleTableViewCell) {
|
private func setupOutgoingMessageTextViewMargins(for cell: MXKRoomBubbleTableViewCell) {
|
||||||
@@ -313,16 +201,14 @@ class BubbleRoomCellLayoutUpdater: RoomCellLayoutUpdating {
|
|||||||
|
|
||||||
let contentView = cell.contentView
|
let contentView = cell.contentView
|
||||||
|
|
||||||
let leftMargin: CGFloat = 80.0
|
let messageViewMargins = self.getOutgoingMessageTextViewMargins(from: cell)
|
||||||
let rightMargin: CGFloat = 78.0
|
|
||||||
let bottomMargin: CGFloat = -2.0
|
|
||||||
|
|
||||||
cell.msgTextViewLeadingConstraint.isActive = false
|
cell.msgTextViewLeadingConstraint.isActive = false
|
||||||
cell.msgTextViewTrailingConstraint.isActive = false
|
cell.msgTextViewTrailingConstraint.isActive = false
|
||||||
|
|
||||||
let leftConstraint = messageTextView.leadingAnchor.constraint(greaterThanOrEqualTo: contentView.leadingAnchor, constant: leftMargin)
|
let leftConstraint = messageTextView.leadingAnchor.constraint(greaterThanOrEqualTo: contentView.leadingAnchor, constant: messageViewMargins.left)
|
||||||
|
|
||||||
let rightConstraint = messageTextView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -rightMargin)
|
let rightConstraint = messageTextView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -messageViewMargins.right)
|
||||||
|
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
leftConstraint,
|
leftConstraint,
|
||||||
@@ -332,9 +218,11 @@ class BubbleRoomCellLayoutUpdater: RoomCellLayoutUpdating {
|
|||||||
cell.msgTextViewLeadingConstraint = leftConstraint
|
cell.msgTextViewLeadingConstraint = leftConstraint
|
||||||
cell.msgTextViewTrailingConstraint = rightConstraint
|
cell.msgTextViewTrailingConstraint = rightConstraint
|
||||||
|
|
||||||
cell.msgTextViewBottomConstraint.constant += bottomMargin
|
cell.msgTextViewBottomConstraint.constant += messageViewMargins.bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: File attachment
|
||||||
|
|
||||||
private func setupOutgoingFileAttachViewMargins(for cell: MXKRoomBubbleTableViewCell) {
|
private func setupOutgoingFileAttachViewMargins(for cell: MXKRoomBubbleTableViewCell) {
|
||||||
|
|
||||||
guard let attachmentView = cell.attachmentView else {
|
guard let attachmentView = cell.attachmentView else {
|
||||||
|
|||||||
+21
-19
@@ -29,24 +29,19 @@ class BubbleRoomTimelineCellDecorator: PlainRoomTimelineCellDecorator {
|
|||||||
|
|
||||||
override func addTimestampLabel(toCell cell: MXKRoomBubbleTableViewCell, cellData: RoomBubbleCellData) {
|
override func addTimestampLabel(toCell cell: MXKRoomBubbleTableViewCell, cellData: RoomBubbleCellData) {
|
||||||
|
|
||||||
if let timestampDisplayable = cell as? TimestampDisplayable, let timestampLabel = self.createTimestampLabel(for: cellData) {
|
guard let timestampLabel = self.createTimestampLabel(for: cellData) else {
|
||||||
|
super.addTimestampLabel(toCell: cell, cellData: cellData)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let timestampDisplayable = cell as? TimestampDisplayable {
|
||||||
|
|
||||||
timestampDisplayable.addTimestampView(timestampLabel)
|
timestampDisplayable.addTimestampView(timestampLabel)
|
||||||
|
|
||||||
} else if let bubbleBackgroundView = cell.messageBubbleBackgroundView, bubbleBackgroundView.isHidden == false, let timestampLabel = self.createTimestampLabel(for: cellData) {
|
|
||||||
|
|
||||||
// If cell contains a bubble background, add the timestamp inside of it
|
|
||||||
|
|
||||||
self.addTimestampLabel(timestampLabel,
|
|
||||||
to: cell,
|
|
||||||
on: bubbleBackgroundView,
|
|
||||||
constrainingView: bubbleBackgroundView)
|
|
||||||
|
|
||||||
} else if cellData.isAttachmentWithThumbnail {
|
} else if cellData.isAttachmentWithThumbnail {
|
||||||
|
|
||||||
if cellData.attachment?.type == .sticker,
|
if cellData.attachment?.type == .sticker,
|
||||||
let attachmentView = cell.attachmentView,
|
let attachmentView = cell.attachmentView {
|
||||||
let timestampLabel = self.createTimestampLabel(for: cellData) {
|
|
||||||
|
|
||||||
// Prevent overlap with send status icon
|
// Prevent overlap with send status icon
|
||||||
let bottomMargin: CGFloat = 20.0
|
let bottomMargin: CGFloat = 20.0
|
||||||
@@ -59,9 +54,11 @@ class BubbleRoomTimelineCellDecorator: PlainRoomTimelineCellDecorator {
|
|||||||
rightMargin: rightMargin,
|
rightMargin: rightMargin,
|
||||||
bottomMargin: bottomMargin)
|
bottomMargin: bottomMargin)
|
||||||
|
|
||||||
} else if let attachmentView = cell.attachmentView, let timestampLabel = self.createTimestampLabel(for: cellData, textColor: self.theme.baseIconPrimaryColor) {
|
} else if let attachmentView = cell.attachmentView {
|
||||||
// For media with thumbnail cells, add timestamp inside thumbnail
|
// For media with thumbnail cells, add timestamp inside thumbnail
|
||||||
|
|
||||||
|
timestampLabel.textColor = self.theme.baseIconPrimaryColor
|
||||||
|
|
||||||
self.addTimestampLabel(timestampLabel,
|
self.addTimestampLabel(timestampLabel,
|
||||||
to: cell,
|
to: cell,
|
||||||
on: cell.contentView,
|
on: cell.contentView,
|
||||||
@@ -70,7 +67,7 @@ class BubbleRoomTimelineCellDecorator: PlainRoomTimelineCellDecorator {
|
|||||||
} else {
|
} else {
|
||||||
super.addTimestampLabel(toCell: cell, cellData: cellData)
|
super.addTimestampLabel(toCell: cell, cellData: cellData)
|
||||||
}
|
}
|
||||||
} else if let voiceMessageCell = cell as? VoiceMessageBubbleCell, let playbackView = voiceMessageCell.playbackController?.playbackView, let timestampLabel = self.createTimestampLabel(for: cellData) {
|
} else if let voiceMessageCell = cell as? VoiceMessageBubbleCell, let playbackView = voiceMessageCell.playbackController?.playbackView {
|
||||||
|
|
||||||
// Add timestamp on cell inherting from VoiceMessageBubbleCell
|
// Add timestamp on cell inherting from VoiceMessageBubbleCell
|
||||||
|
|
||||||
@@ -79,7 +76,7 @@ class BubbleRoomTimelineCellDecorator: PlainRoomTimelineCellDecorator {
|
|||||||
on: cell.contentView,
|
on: cell.contentView,
|
||||||
constrainingView: playbackView)
|
constrainingView: playbackView)
|
||||||
|
|
||||||
} else if let fileWithoutThumbnailCell = cell as? FileWithoutThumbnailBaseBubbleCell, let fileAttachementView = fileWithoutThumbnailCell.fileAttachementView, let timestampLabel = self.createTimestampLabel(for: cellData) {
|
} else if let fileWithoutThumbnailCell = cell as? FileWithoutThumbnailBaseBubbleCell, let fileAttachementView = fileWithoutThumbnailCell.fileAttachementView {
|
||||||
|
|
||||||
// Add timestamp on cell inherting from VoiceMessageBubbleCell
|
// Add timestamp on cell inherting from VoiceMessageBubbleCell
|
||||||
|
|
||||||
@@ -96,13 +93,13 @@ class BubbleRoomTimelineCellDecorator: PlainRoomTimelineCellDecorator {
|
|||||||
override func addReactionView(_ reactionsView: BubbleReactionsView,
|
override func addReactionView(_ reactionsView: BubbleReactionsView,
|
||||||
toCell cell: MXKRoomBubbleTableViewCell, cellData: RoomBubbleCellData, contentViewPositionY: CGFloat, upperDecorationView: UIView?) {
|
toCell cell: MXKRoomBubbleTableViewCell, cellData: RoomBubbleCellData, contentViewPositionY: CGFloat, upperDecorationView: UIView?) {
|
||||||
|
|
||||||
cell.addTemporarySubview(reactionsView)
|
|
||||||
|
|
||||||
if let reactionsDisplayable = cell as? BubbleCellReactionsDisplayable {
|
if let reactionsDisplayable = cell as? BubbleCellReactionsDisplayable {
|
||||||
reactionsDisplayable.addReactionsView(reactionsView)
|
reactionsDisplayable.addReactionsView(reactionsView)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cell.addTemporarySubview(reactionsView)
|
||||||
|
|
||||||
reactionsView.translatesAutoresizingMaskIntoConstraints = false
|
reactionsView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
||||||
let cellContentView = cell.contentView
|
let cellContentView = cell.contentView
|
||||||
@@ -170,6 +167,10 @@ class BubbleRoomTimelineCellDecorator: PlainRoomTimelineCellDecorator {
|
|||||||
cellData: RoomBubbleCellData,
|
cellData: RoomBubbleCellData,
|
||||||
contentViewPositionY: CGFloat) {
|
contentViewPositionY: CGFloat) {
|
||||||
|
|
||||||
|
if let urlPreviewDisplayable = cell as? RoomCellURLPreviewDisplayable {
|
||||||
|
urlPreviewView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
urlPreviewDisplayable.addURLPreviewView(urlPreviewView)
|
||||||
|
} else {
|
||||||
cell.addTemporarySubview(urlPreviewView)
|
cell.addTemporarySubview(urlPreviewView)
|
||||||
|
|
||||||
let cellContentView = cell.contentView
|
let cellContentView = cell.contentView
|
||||||
@@ -209,6 +210,7 @@ class BubbleRoomTimelineCellDecorator: PlainRoomTimelineCellDecorator {
|
|||||||
urlPreviewView.topAnchor.constraint(equalTo: cellContentView.topAnchor, constant: topMargin)
|
urlPreviewView.topAnchor.constraint(equalTo: cellContentView.topAnchor, constant: topMargin)
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override func addThreadSummaryView(_ threadSummaryView: ThreadSummaryView,
|
override func addThreadSummaryView(_ threadSummaryView: ThreadSummaryView,
|
||||||
toCell cell: MXKRoomBubbleTableViewCell,
|
toCell cell: MXKRoomBubbleTableViewCell,
|
||||||
@@ -216,11 +218,11 @@ class BubbleRoomTimelineCellDecorator: PlainRoomTimelineCellDecorator {
|
|||||||
contentViewPositionY: CGFloat,
|
contentViewPositionY: CGFloat,
|
||||||
upperDecorationView: UIView?) {
|
upperDecorationView: UIView?) {
|
||||||
|
|
||||||
cell.addTemporarySubview(threadSummaryView)
|
|
||||||
|
|
||||||
if let threadSummaryDisplayable = cell as? BubbleCellThreadSummaryDisplayable {
|
if let threadSummaryDisplayable = cell as? BubbleCellThreadSummaryDisplayable {
|
||||||
threadSummaryDisplayable.addThreadSummaryView(threadSummaryView)
|
threadSummaryDisplayable.addThreadSummaryView(threadSummaryView)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
cell.addTemporarySubview(threadSummaryView)
|
||||||
threadSummaryView.translatesAutoresizingMaskIntoConstraints = false
|
threadSummaryView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
||||||
let cellContentView = cell.contentView
|
let cellContentView = cell.contentView
|
||||||
|
|||||||
+87
-10
@@ -64,6 +64,28 @@
|
|||||||
[self registerFileWithoutThumbnailCellsForTableView:tableView];
|
[self registerFileWithoutThumbnailCellsForTableView:tableView];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)registerIncomingTextMessageCellsForTableView:(UITableView*)tableView
|
||||||
|
{
|
||||||
|
// Also register legacy cells for notice and emotes
|
||||||
|
[super registerIncomingTextMessageCellsForTableView:tableView];
|
||||||
|
|
||||||
|
[tableView registerClass:TextMessageIncomingBubbleCell.class forCellReuseIdentifier:TextMessageIncomingBubbleCell.defaultReuseIdentifier];
|
||||||
|
[tableView registerClass:TextMessageIncomingWithoutSenderInfoBubbleCell.class forCellReuseIdentifier:TextMessageIncomingWithoutSenderInfoBubbleCell.defaultReuseIdentifier];
|
||||||
|
[tableView registerClass:TextMessageIncomingWithoutSenderNameBubbleCell.class forCellReuseIdentifier:TextMessageIncomingWithoutSenderNameBubbleCell.defaultReuseIdentifier];
|
||||||
|
[tableView registerClass:TextMessageIncomingWithPaginationTitleBubbleCell.class forCellReuseIdentifier:TextMessageIncomingWithPaginationTitleBubbleCell.defaultReuseIdentifier];
|
||||||
|
[tableView registerClass:TextMessageIncomingWithPaginationTitleWithoutSenderNameBubbleCell.class forCellReuseIdentifier:TextMessageIncomingWithPaginationTitleWithoutSenderNameBubbleCell.defaultReuseIdentifier];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)registerOutgoingTextMessageCellsForTableView:(UITableView*)tableView
|
||||||
|
{
|
||||||
|
// Also register legacy cells for notice and emotes
|
||||||
|
[super registerOutgoingTextMessageCellsForTableView:tableView];
|
||||||
|
|
||||||
|
[tableView registerClass:TextMessageOutgoingWithoutSenderInfoBubbleCell.class forCellReuseIdentifier:TextMessageOutgoingWithoutSenderInfoBubbleCell.defaultReuseIdentifier];
|
||||||
|
[tableView registerClass:TextMessageOutgoingWithPaginationTitleBubbleCell.class forCellReuseIdentifier:TextMessageOutgoingWithPaginationTitleBubbleCell.defaultReuseIdentifier];
|
||||||
|
[tableView registerClass:TextMessageOutgoingWithPaginationTitleWithoutSenderNameBubbleCell.class forCellReuseIdentifier:TextMessageOutgoingWithPaginationTitleWithoutSenderNameBubbleCell.defaultReuseIdentifier];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)registerVoiceMessageCellsForTableView:(UITableView*)tableView
|
- (void)registerVoiceMessageCellsForTableView:(UITableView*)tableView
|
||||||
{
|
{
|
||||||
// Incoming
|
// Incoming
|
||||||
@@ -112,22 +134,77 @@
|
|||||||
|
|
||||||
#pragma mark - Mapping
|
#pragma mark - Mapping
|
||||||
|
|
||||||
|
- (NSDictionary<NSNumber*, Class>*)incomingTextMessageCellsMapping
|
||||||
|
{
|
||||||
|
return @{
|
||||||
|
// Clear
|
||||||
|
@(RoomTimelineCellIdentifierIncomingTextMessage) : TextMessageIncomingBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingTextMessageWithoutSenderInfo) : TextMessageIncomingWithoutSenderInfoBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingTextMessageWithPaginationTitle) : TextMessageIncomingWithPaginationTitleBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingTextMessageWithoutSenderName) : TextMessageIncomingWithoutSenderNameBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingTextMessageWithPaginationTitleWithoutSenderName) : TextMessageIncomingWithPaginationTitleWithoutSenderNameBubbleCell.class,
|
||||||
|
// Encrypted
|
||||||
|
@(RoomTimelineCellIdentifierIncomingTextMessageEncrypted) : TextMessageIncomingBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingTextMessageEncryptedWithoutSenderInfo) : TextMessageIncomingWithoutSenderInfoBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingTextMessageEncryptedWithPaginationTitle) : TextMessageIncomingWithPaginationTitleBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingTextMessageEncryptedWithoutSenderName) : TextMessageIncomingWithoutSenderNameBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingTextMessageEncryptedWithPaginationTitleWithoutSenderName) : TextMessageIncomingWithPaginationTitleWithoutSenderNameBubbleCell.class,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
- (NSDictionary<NSNumber*, Class>*)outgoingTextMessageCellsMapping
|
- (NSDictionary<NSNumber*, Class>*)outgoingTextMessageCellsMapping
|
||||||
{
|
{
|
||||||
// Hide sender info and avatar for bubble outgoing messages
|
// Hide sender info and avatar for bubble outgoing messages
|
||||||
return @{
|
return @{
|
||||||
// Clear
|
// Clear
|
||||||
@(RoomTimelineCellIdentifierOutgoingTextMessage) : RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.class,
|
@(RoomTimelineCellIdentifierOutgoingTextMessage) : TextMessageOutgoingWithoutSenderInfoBubbleCell.class,
|
||||||
@(RoomTimelineCellIdentifierOutgoingTextMessageWithoutSenderInfo) : RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.class,
|
@(RoomTimelineCellIdentifierOutgoingTextMessageWithoutSenderInfo) : TextMessageOutgoingWithoutSenderInfoBubbleCell.class,
|
||||||
@(RoomTimelineCellIdentifierOutgoingTextMessageWithPaginationTitle) : RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.class,
|
@(RoomTimelineCellIdentifierOutgoingTextMessageWithPaginationTitle) : TextMessageOutgoingWithPaginationTitleBubbleCell.class,
|
||||||
@(RoomTimelineCellIdentifierOutgoingTextMessageWithoutSenderName) : RoomOutgoingTextMsgWithoutSenderNameBubbleCell.class,
|
@(RoomTimelineCellIdentifierOutgoingTextMessageWithoutSenderName) : TextMessageOutgoingWithoutSenderInfoBubbleCell.class,
|
||||||
@(RoomTimelineCellIdentifierOutgoingTextMessageWithPaginationTitleWithoutSenderName) : RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.class,
|
@(RoomTimelineCellIdentifierOutgoingTextMessageWithPaginationTitleWithoutSenderName) : TextMessageOutgoingWithPaginationTitleWithoutSenderNameBubbleCell.class,
|
||||||
// Encrypted
|
// Encrypted
|
||||||
@(RoomTimelineCellIdentifierOutgoingTextMessageEncrypted) : RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.class,
|
@(RoomTimelineCellIdentifierOutgoingTextMessageEncrypted) : TextMessageOutgoingWithoutSenderInfoBubbleCell.class,
|
||||||
@(RoomTimelineCellIdentifierOutgoingTextMessageEncryptedWithoutSenderInfo) : RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.class,
|
@(RoomTimelineCellIdentifierOutgoingTextMessageEncryptedWithoutSenderInfo) : TextMessageOutgoingWithoutSenderInfoBubbleCell.class,
|
||||||
@(RoomTimelineCellIdentifierOutgoingTextMessageEncryptedWithPaginationTitle) : RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.class,
|
@(RoomTimelineCellIdentifierOutgoingTextMessageEncryptedWithPaginationTitle) : TextMessageOutgoingWithPaginationTitleBubbleCell.class,
|
||||||
@(RoomTimelineCellIdentifierOutgoingTextMessageEncryptedWithoutSenderName) : RoomOutgoingEncryptedTextMsgWithoutSenderNameBubbleCell.class,
|
@(RoomTimelineCellIdentifierOutgoingTextMessageEncryptedWithoutSenderName) : TextMessageOutgoingWithoutSenderInfoBubbleCell.class,
|
||||||
@(RoomTimelineCellIdentifierOutgoingTextMessageEncryptedWithPaginationTitleWithoutSenderName) : RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.class,
|
@(RoomTimelineCellIdentifierOutgoingTextMessageEncryptedWithPaginationTitleWithoutSenderName) : TextMessageOutgoingWithPaginationTitleWithoutSenderNameBubbleCell.class,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary<NSNumber*, Class>*)incomingEmoteCellsMapping
|
||||||
|
{
|
||||||
|
return @{
|
||||||
|
// Clear
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmote) : TextMessageIncomingBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmoteWithoutSenderInfo) : TextMessageIncomingWithoutSenderInfoBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmoteWithPaginationTitle) : TextMessageIncomingWithPaginationTitleBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmoteWithoutSenderName) : TextMessageIncomingWithoutSenderNameBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmoteWithPaginationTitleWithoutSenderName) : TextMessageIncomingWithPaginationTitleWithoutSenderNameBubbleCell.class,
|
||||||
|
// Encrypted
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmoteEncrypted) : TextMessageIncomingBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmoteEncryptedWithoutSenderInfo) : TextMessageIncomingWithoutSenderInfoBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmoteEncryptedWithPaginationTitle) : TextMessageIncomingWithPaginationTitleBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmoteEncryptedWithoutSenderName) : TextMessageIncomingWithoutSenderNameBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmoteEncryptedWithPaginationTitleWithoutSenderName) : TextMessageIncomingWithPaginationTitleWithoutSenderNameBubbleCell.class,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary<NSNumber*, Class>*)outgoingEmoteCellsMapping
|
||||||
|
{
|
||||||
|
// Hide sender info and avatar for bubble outgoing messages
|
||||||
|
return @{
|
||||||
|
// Clear
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmote) : TextMessageOutgoingWithoutSenderInfoBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmoteWithoutSenderInfo) : TextMessageOutgoingWithoutSenderInfoBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmoteWithPaginationTitle) : TextMessageOutgoingWithPaginationTitleBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmoteWithoutSenderName) : TextMessageOutgoingWithoutSenderInfoBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmoteWithPaginationTitleWithoutSenderName) : TextMessageOutgoingWithPaginationTitleBubbleCell.class,
|
||||||
|
// Encrypted
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmoteEncrypted) : TextMessageOutgoingWithoutSenderInfoBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmoteEncryptedWithoutSenderInfo) : TextMessageOutgoingWithoutSenderInfoBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmoteEncryptedWithPaginationTitle) : TextMessageOutgoingWithPaginationTitleBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmoteEncryptedWithoutSenderName) : TextMessageOutgoingWithoutSenderInfoBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmoteEncryptedWithPaginationTitleWithoutSenderName) : TextMessageOutgoingWithPaginationTitleBubbleCell.class
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 New Vector 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 Foundation
|
||||||
|
|
||||||
|
protocol BubbleIncomingRoomCellProtocol: BubbleRoomCellProtocol {
|
||||||
|
}
|
||||||
|
|
||||||
|
extension BubbleIncomingRoomCellProtocol {
|
||||||
|
|
||||||
|
// MARK: - Public
|
||||||
|
|
||||||
|
func setupBubbleDecorations() {
|
||||||
|
self.bubbleCellContentView?.decorationViewsAlignment = .left
|
||||||
|
self.setupDecorationConstraints()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func setupDecorationConstraints() {
|
||||||
|
|
||||||
|
self.setupURLPreviewContentViewContraints()
|
||||||
|
self.setupReactionsContentViewContraints()
|
||||||
|
self.setupThreadSummaryViewContentViewContraints()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupReactionsContentViewContraints() {
|
||||||
|
|
||||||
|
self.bubbleCellContentView?.reactionsContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupThreadSummaryViewContentViewContraints() {
|
||||||
|
self.bubbleCellContentView?.threadSummaryContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right
|
||||||
|
|
||||||
|
self.bubbleCellContentView?.threadSummaryContentViewBottomConstraint.constant = BubbleRoomCellLayoutConstants.threadSummaryViewMargins.bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupURLPreviewContentViewContraints() {
|
||||||
|
self.bubbleCellContentView?.urlPreviewContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right
|
||||||
|
}
|
||||||
|
}
|
||||||
+118
@@ -0,0 +1,118 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 New Vector 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 Foundation
|
||||||
|
|
||||||
|
protocol BubbleOutgoingRoomCellProtocol: BubbleRoomCellProtocol {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Default implementation
|
||||||
|
extension BubbleOutgoingRoomCellProtocol {
|
||||||
|
|
||||||
|
// MARK: - Public
|
||||||
|
|
||||||
|
func setupBubbleDecorations() {
|
||||||
|
self.bubbleCellContentView?.decorationViewsAlignment = .right
|
||||||
|
self.setupDecorationConstraints()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func setupDecorationConstraints() {
|
||||||
|
|
||||||
|
self.setupURLPreviewContentViewContraints()
|
||||||
|
self.setupReactionsContentViewContraints()
|
||||||
|
self.setupThreadSummaryViewContentViewContraints()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupReactionsContentViewContraints() {
|
||||||
|
guard let bubbleCellContentView = self.bubbleCellContentView, let reactionsContentView = bubbleCellContentView.reactionsContentView, let reactionsContainerView = bubbleCellContentView.reactionsContainerView else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove leading constraint
|
||||||
|
|
||||||
|
bubbleCellContentView.reactionsContentViewLeadingConstraint.isActive = false
|
||||||
|
bubbleCellContentView.reactionsContentViewLeadingConstraint = nil
|
||||||
|
|
||||||
|
// Setup new leading constraint
|
||||||
|
|
||||||
|
let leadingConstraint = self.setupDecorationViewLeadingContraint(containerView: reactionsContainerView, contentView: reactionsContentView)
|
||||||
|
|
||||||
|
bubbleCellContentView.reactionsContentViewLeadingConstraint = leadingConstraint
|
||||||
|
|
||||||
|
// Update trailing constraint
|
||||||
|
|
||||||
|
bubbleCellContentView.reactionsContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupThreadSummaryViewContentViewContraints() {
|
||||||
|
|
||||||
|
guard let bubbleCellContentView = self.bubbleCellContentView, let threadSummaryContentView = bubbleCellContentView.threadSummaryContentView, let threadSummaryContainerView = bubbleCellContentView.threadSummaryContainerView else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove leading constraint
|
||||||
|
|
||||||
|
bubbleCellContentView.threadSummaryContentViewLeadingConstraint.isActive = false
|
||||||
|
bubbleCellContentView.threadSummaryContentViewLeadingConstraint = nil
|
||||||
|
|
||||||
|
// Setup new leading constraint
|
||||||
|
|
||||||
|
let leadingConstraint = self.setupDecorationViewLeadingContraint(containerView: threadSummaryContainerView, contentView: threadSummaryContentView)
|
||||||
|
|
||||||
|
bubbleCellContentView.threadSummaryContentViewLeadingConstraint = leadingConstraint
|
||||||
|
|
||||||
|
// Update trailing constraint
|
||||||
|
|
||||||
|
bubbleCellContentView.threadSummaryContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right
|
||||||
|
|
||||||
|
// Update bottom constraint
|
||||||
|
|
||||||
|
bubbleCellContentView.threadSummaryContentViewBottomConstraint.constant = BubbleRoomCellLayoutConstants.threadSummaryViewMargins.bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupURLPreviewContentViewContraints() {
|
||||||
|
|
||||||
|
guard let bubbleCellContentView = self.bubbleCellContentView, let contentView = bubbleCellContentView.urlPreviewContentView, let containerView = bubbleCellContentView.urlPreviewContainerView else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove leading constraint
|
||||||
|
|
||||||
|
bubbleCellContentView.urlPreviewContentViewLeadingConstraint.isActive = false
|
||||||
|
bubbleCellContentView.urlPreviewContentViewLeadingConstraint = nil
|
||||||
|
|
||||||
|
// Setup new leading constraint
|
||||||
|
|
||||||
|
let leadingConstraint = self.setupDecorationViewLeadingContraint(containerView: containerView, contentView: contentView)
|
||||||
|
|
||||||
|
bubbleCellContentView.urlPreviewContentViewLeadingConstraint = leadingConstraint
|
||||||
|
|
||||||
|
// Update trailing constraint
|
||||||
|
|
||||||
|
bubbleCellContentView.urlPreviewContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setupDecorationViewLeadingContraint(containerView: UIView,
|
||||||
|
contentView: UIView) -> NSLayoutConstraint {
|
||||||
|
|
||||||
|
let leadingConstraint = contentView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.left)
|
||||||
|
leadingConstraint.isActive = true
|
||||||
|
return leadingConstraint
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 New Vector 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 Foundation
|
||||||
|
|
||||||
|
protocol BubbleRoomCellProtocol {
|
||||||
|
|
||||||
|
var bubbleCellContentView: BubbleCellContentView? { get }
|
||||||
|
|
||||||
|
func setupBubbleDecorations()
|
||||||
|
}
|
||||||
+1
-5
@@ -23,11 +23,7 @@ class FileWithoutThumbnailBaseBubbleCell: SizableBaseBubbleCell, BubbleCellReact
|
|||||||
override func render(_ cellData: MXKCellData!) {
|
override func render(_ cellData: MXKCellData!) {
|
||||||
super.render(cellData)
|
super.render(cellData)
|
||||||
|
|
||||||
guard let data = cellData as? RoomBubbleCellData else {
|
self.fileAttachementView?.titleLabel.attributedText = self.suitableAttributedTextMessage
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.fileAttachementView?.title = data.attributedTextMessage.string
|
|
||||||
|
|
||||||
self.update(theme: ThemeService.shared().theme)
|
self.update(theme: ThemeService.shared().theme)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-11
@@ -31,7 +31,7 @@ final class FileWithoutThumbnailCellContentView: UIView, NibLoadable {
|
|||||||
// MARK: Outlets
|
// MARK: Outlets
|
||||||
|
|
||||||
@IBOutlet private weak var iconImageView: UIImageView!
|
@IBOutlet private weak var iconImageView: UIImageView!
|
||||||
@IBOutlet private weak var titleLabel: UILabel!
|
@IBOutlet private(set) weak var titleLabel: UILabel!
|
||||||
|
|
||||||
// MARK: Public
|
// MARK: Public
|
||||||
|
|
||||||
@@ -44,15 +44,6 @@ final class FileWithoutThumbnailCellContentView: UIView, NibLoadable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var title: String? {
|
|
||||||
get {
|
|
||||||
return self.titleLabel.text
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
self.titleLabel.text = newValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Setup
|
// MARK: - Setup
|
||||||
|
|
||||||
static func instantiate() -> FileWithoutThumbnailCellContentView {
|
static func instantiate() -> FileWithoutThumbnailCellContentView {
|
||||||
@@ -79,4 +70,3 @@ final class FileWithoutThumbnailCellContentView: UIView, NibLoadable {
|
|||||||
self.titleLabel.textColor = theme.textPrimaryColor
|
self.titleLabel.textColor = theme.textPrimaryColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class FileWithoutThumbnailIncomingBubbleCell: FileWithoutThumbnailBaseBubbleCell {
|
class FileWithoutThumbnailIncomingBubbleCell: FileWithoutThumbnailBaseBubbleCell, BubbleIncomingRoomCellProtocol {
|
||||||
|
|
||||||
override func setupViews() {
|
override func setupViews() {
|
||||||
super.setupViews()
|
super.setupViews()
|
||||||
@@ -29,6 +29,8 @@ class FileWithoutThumbnailIncomingBubbleCell: FileWithoutThumbnailBaseBubbleCell
|
|||||||
|
|
||||||
bubbleCellContentView?.innerContentViewTrailingConstraint.constant = messageViewMarginRight
|
bubbleCellContentView?.innerContentViewTrailingConstraint.constant = messageViewMarginRight
|
||||||
bubbleCellContentView?.innerContentViewLeadingConstraint.constant = messageLeftMargin
|
bubbleCellContentView?.innerContentViewLeadingConstraint.constant = messageLeftMargin
|
||||||
|
|
||||||
|
self.setupBubbleDecorations()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func update(theme: Theme) {
|
override func update(theme: Theme) {
|
||||||
|
|||||||
+3
-1
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class FileWithoutThumbnailOutoingWithoutSenderInfoBubbleCell: FileWithoutThumbnailBaseBubbleCell {
|
class FileWithoutThumbnailOutoingWithoutSenderInfoBubbleCell: FileWithoutThumbnailBaseBubbleCell, BubbleOutgoingRoomCellProtocol {
|
||||||
|
|
||||||
override func setupViews() {
|
override func setupViews() {
|
||||||
super.setupViews()
|
super.setupViews()
|
||||||
@@ -30,6 +30,8 @@ class FileWithoutThumbnailOutoingWithoutSenderInfoBubbleCell: FileWithoutThumbna
|
|||||||
|
|
||||||
bubbleCellContentView?.innerContentViewTrailingConstraint.constant = rightMargin
|
bubbleCellContentView?.innerContentViewTrailingConstraint.constant = rightMargin
|
||||||
bubbleCellContentView?.innerContentViewLeadingConstraint.constant = leftMargin
|
bubbleCellContentView?.innerContentViewLeadingConstraint.constant = leftMargin
|
||||||
|
|
||||||
|
self.setupBubbleDecorations()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func update(theme: Theme) {
|
override func update(theme: Theme) {
|
||||||
|
|||||||
+3
-1
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class LocationIncomingBubbleCell: LocationBubbleCell {
|
class LocationIncomingBubbleCell: LocationBubbleCell, BubbleIncomingRoomCellProtocol {
|
||||||
|
|
||||||
override func setupViews() {
|
override func setupViews() {
|
||||||
super.setupViews()
|
super.setupViews()
|
||||||
@@ -27,5 +27,7 @@ class LocationIncomingBubbleCell: LocationBubbleCell {
|
|||||||
|
|
||||||
bubbleCellContentView?.innerContentViewTrailingConstraint.constant = messageViewMarginRight
|
bubbleCellContentView?.innerContentViewTrailingConstraint.constant = messageViewMarginRight
|
||||||
bubbleCellContentView?.innerContentViewLeadingConstraint.constant = messageLeftMargin
|
bubbleCellContentView?.innerContentViewLeadingConstraint.constant = messageLeftMargin
|
||||||
|
|
||||||
|
self.setupBubbleDecorations()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-7
@@ -16,18 +16,17 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class LocationOutgoingWithoutSenderInfoBubbleCell: LocationBubbleCell {
|
class LocationOutgoingWithoutSenderInfoBubbleCell: LocationBubbleCell, BubbleOutgoingRoomCellProtocol {
|
||||||
|
|
||||||
override func setupViews() {
|
override func setupViews() {
|
||||||
super.setupViews()
|
super.setupViews()
|
||||||
|
|
||||||
bubbleCellContentView?.showSenderInfo = false
|
bubbleCellContentView?.showSenderInfo = false
|
||||||
|
|
||||||
// TODO: Use constants
|
bubbleCellContentView?.innerContentViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.right
|
||||||
// Same as outgoing message
|
|
||||||
let rightMargin: CGFloat = 34.0
|
|
||||||
let leftMargin: CGFloat = 80.0
|
|
||||||
|
|
||||||
bubbleCellContentView?.innerContentViewTrailingConstraint.constant = rightMargin
|
bubbleCellContentView?.innerContentViewLeadingConstraint.constant = BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.left
|
||||||
bubbleCellContentView?.innerContentViewLeadingConstraint.constant = leftMargin
|
|
||||||
|
self.setupBubbleDecorations()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class PollIncomingBubbleCell: PollBaseBubbleCell {
|
class PollIncomingBubbleCell: PollBaseBubbleCell, BubbleIncomingRoomCellProtocol {
|
||||||
|
|
||||||
override func setupViews() {
|
override func setupViews() {
|
||||||
super.setupViews()
|
super.setupViews()
|
||||||
@@ -28,6 +28,8 @@ class PollIncomingBubbleCell: PollBaseBubbleCell {
|
|||||||
|
|
||||||
bubbleCellContentView?.innerContentViewTrailingConstraint.constant = messageViewMarginRight
|
bubbleCellContentView?.innerContentViewTrailingConstraint.constant = messageViewMarginRight
|
||||||
bubbleCellContentView?.innerContentViewLeadingConstraint.constant = messageLeftMargin
|
bubbleCellContentView?.innerContentViewLeadingConstraint.constant = messageLeftMargin
|
||||||
|
|
||||||
|
self.setupBubbleDecorations()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func update(theme: Theme) {
|
override func update(theme: Theme) {
|
||||||
|
|||||||
+3
-1
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class PollOutgoingWithoutSenderInfoBubbleCell: PollBaseBubbleCell {
|
class PollOutgoingWithoutSenderInfoBubbleCell: PollBaseBubbleCell, BubbleOutgoingRoomCellProtocol {
|
||||||
|
|
||||||
override func setupViews() {
|
override func setupViews() {
|
||||||
super.setupViews()
|
super.setupViews()
|
||||||
@@ -31,6 +31,8 @@ class PollOutgoingWithoutSenderInfoBubbleCell: PollBaseBubbleCell {
|
|||||||
|
|
||||||
bubbleCellContentView?.innerContentViewTrailingConstraint.constant = rightMargin
|
bubbleCellContentView?.innerContentViewTrailingConstraint.constant = rightMargin
|
||||||
bubbleCellContentView?.innerContentViewLeadingConstraint.constant = leftMargin
|
bubbleCellContentView?.innerContentViewLeadingConstraint.constant = leftMargin
|
||||||
|
|
||||||
|
self.setupBubbleDecorations()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func update(theme: Theme) {
|
override func update(theme: Theme) {
|
||||||
|
|||||||
+112
@@ -0,0 +1,112 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 New Vector 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 UIKit
|
||||||
|
|
||||||
|
class TextMessageBaseBubbleCell: SizableBaseBubbleCell, RoomCellURLPreviewDisplayable, BubbleCellReactionsDisplayable, BubbleCellThreadSummaryDisplayable, BubbleCellReadReceiptsDisplayable {
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
|
weak var textMessageContentView: TextMessageBubbleCellContentView?
|
||||||
|
|
||||||
|
override var messageTextView: UITextView! {
|
||||||
|
get {
|
||||||
|
return self.textMessageContentView?.textView
|
||||||
|
}
|
||||||
|
set { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Overrides
|
||||||
|
|
||||||
|
override func setupViews() {
|
||||||
|
super.setupViews()
|
||||||
|
|
||||||
|
bubbleCellContentView?.backgroundColor = .clear
|
||||||
|
|
||||||
|
guard let contentView = bubbleCellContentView?.innerContentView else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Use constants
|
||||||
|
|
||||||
|
let messageLeftMargin: CGFloat = 48
|
||||||
|
|
||||||
|
bubbleCellContentView?.innerContentViewLeadingConstraint.constant = messageLeftMargin
|
||||||
|
|
||||||
|
self.bubbleCellContentView?.innerContentViewBottomContraint.constant = 5.0
|
||||||
|
|
||||||
|
self.bubbleCellContentView?.innerContentViewTrailingConstraint.constant = 34.0
|
||||||
|
|
||||||
|
let textMessageContentView = TextMessageBubbleCellContentView.instantiate()
|
||||||
|
|
||||||
|
contentView.vc_addSubViewMatchingParent(textMessageContentView)
|
||||||
|
|
||||||
|
self.textMessageContentView = textMessageContentView
|
||||||
|
}
|
||||||
|
|
||||||
|
override func update(theme: Theme) {
|
||||||
|
super.update(theme: theme)
|
||||||
|
|
||||||
|
if let messageTextView = self.messageTextView {
|
||||||
|
messageTextView.tintColor = theme.tintColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// self.setupDebug()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func setupDebug() {
|
||||||
|
|
||||||
|
self.bubbleCellContentView?.innerContentView.backgroundColor = .yellow
|
||||||
|
|
||||||
|
self.bubbleCellContentView?.layer.borderWidth = 1.0
|
||||||
|
self.bubbleCellContentView?.layer.borderColor = UIColor.red.cgColor
|
||||||
|
|
||||||
|
self.textMessageContentView?.layer.borderColor = UIColor.blue.cgColor
|
||||||
|
self.textMessageContentView?.layer.borderWidth = 1.0
|
||||||
|
|
||||||
|
|
||||||
|
self.bubbleCellContentView?.readReceiptsContainerView.layer.borderColor = UIColor.yellow.cgColor
|
||||||
|
self.bubbleCellContentView?.readReceiptsContainerView.layer.borderWidth = 1.0
|
||||||
|
|
||||||
|
self.bubbleCellContentView?.reactionsContainerView.layer.borderColor = UIColor.blue.cgColor
|
||||||
|
self.bubbleCellContentView?.reactionsContainerView.layer.borderWidth = 1.0
|
||||||
|
self.bubbleCellContentView?.reactionsContentView.backgroundColor = .blue
|
||||||
|
|
||||||
|
self.bubbleCellContentView?.threadSummaryContainerView.layer.borderColor = UIColor.purple.cgColor
|
||||||
|
self.bubbleCellContentView?.threadSummaryContainerView.layer.borderWidth = 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - RoomCellTimestampDisplayable
|
||||||
|
extension TextMessageBaseBubbleCell: TimestampDisplayable {
|
||||||
|
|
||||||
|
func addTimestampView(_ timestampView: UIView) {
|
||||||
|
guard let messageBubbleBackgroundView = self.textMessageContentView?.bubbleBackgroundView else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
messageBubbleBackgroundView.removeTimestampView()
|
||||||
|
messageBubbleBackgroundView.addTimestampView(timestampView)
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeTimestampView() {
|
||||||
|
guard let messageBubbleBackgroundView = self.textMessageContentView?.bubbleBackgroundView else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
messageBubbleBackgroundView.removeTimestampView()
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 New Vector 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 UIKit
|
||||||
|
import Reusable
|
||||||
|
|
||||||
|
final class TextMessageBubbleCellContentView: UIView, NibLoadable {
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
|
// MARK: Outlets
|
||||||
|
|
||||||
|
@IBOutlet private(set) weak var bubbleBackgroundView: RoomMessageBubbleBackgroundView!
|
||||||
|
|
||||||
|
@IBOutlet weak var bubbleBackgroundViewLeadingConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet weak var bubbleBackgroundViewTrailingConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
|
@IBOutlet private(set) weak var textView: UITextView!
|
||||||
|
|
||||||
|
// MARK: - Setup
|
||||||
|
|
||||||
|
static func instantiate() -> TextMessageBubbleCellContentView {
|
||||||
|
return TextMessageBubbleCellContentView.loadFromNib()
|
||||||
|
}
|
||||||
|
}
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||||
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||||
|
<view contentMode="scaleToFill" id="8T9-hj-ply" customClass="TextMessageBubbleCellContentView" customModule="Riot" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="313" height="82"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wRb-1K-GDt" customClass="RoomMessageBubbleBackgroundView" customModule="Riot" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="152" height="82"/>
|
||||||
|
<subviews>
|
||||||
|
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" bounces="NO" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" editable="NO" text="text message" translatesAutoresizingMaskIntoConstraints="NO" id="1WT-up-UFQ" customClass="MXKMessageTextView">
|
||||||
|
<rect key="frame" x="5" y="5" width="102" height="72"/>
|
||||||
|
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<accessibility key="accessibilityConfiguration" identifier="MessageTextView"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||||
|
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||||
|
<dataDetectorType key="dataDetectorTypes" link="YES"/>
|
||||||
|
</textView>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="1WT-up-UFQ" secondAttribute="bottom" constant="5" id="EMT-p9-z4L"/>
|
||||||
|
<constraint firstItem="1WT-up-UFQ" firstAttribute="leading" secondItem="wRb-1K-GDt" secondAttribute="leading" constant="5" id="GhZ-J0-v1m"/>
|
||||||
|
<constraint firstItem="1WT-up-UFQ" firstAttribute="top" secondItem="wRb-1K-GDt" secondAttribute="top" constant="5" id="OpF-jQ-D8Q"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="1WT-up-UFQ" secondAttribute="trailing" constant="45" id="Y8T-HE-6NX"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="wRb-1K-GDt" firstAttribute="leading" secondItem="8T9-hj-ply" secondAttribute="leading" id="D7Y-mm-KEV"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="wRb-1K-GDt" secondAttribute="bottom" id="JmB-yf-eq5"/>
|
||||||
|
<constraint firstItem="wRb-1K-GDt" firstAttribute="top" secondItem="8T9-hj-ply" secondAttribute="top" id="Jmy-ue-Qtg"/>
|
||||||
|
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="wRb-1K-GDt" secondAttribute="trailing" constant="80" id="UwK-6h-MvN"/>
|
||||||
|
</constraints>
|
||||||
|
<nil key="simulatedTopBarMetrics"/>
|
||||||
|
<nil key="simulatedBottomBarMetrics"/>
|
||||||
|
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="bubbleBackgroundView" destination="wRb-1K-GDt" id="TWb-A8-15y"/>
|
||||||
|
<outlet property="bubbleBackgroundViewLeadingConstraint" destination="D7Y-mm-KEV" id="4le-fw-axb"/>
|
||||||
|
<outlet property="bubbleBackgroundViewTrailingConstraint" destination="UwK-6h-MvN" id="ZCA-gE-L11"/>
|
||||||
|
<outlet property="textView" destination="1WT-up-UFQ" id="Zh6-C0-kdV"/>
|
||||||
|
</connections>
|
||||||
|
<point key="canvasLocation" x="-1454.3478260869567" y="-392.41071428571428"/>
|
||||||
|
</view>
|
||||||
|
</objects>
|
||||||
|
</document>
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 New Vector 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 UIKit
|
||||||
|
|
||||||
|
class TextMessageIncomingBubbleCell: TextMessageBaseBubbleCell, BubbleIncomingRoomCellProtocol {
|
||||||
|
|
||||||
|
// MARK: - Overrides
|
||||||
|
|
||||||
|
override func setupViews() {
|
||||||
|
super.setupViews()
|
||||||
|
|
||||||
|
bubbleCellContentView?.showSenderInfo = true
|
||||||
|
|
||||||
|
self.setupBubbleConstraints()
|
||||||
|
self.setupBubbleDecorations()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func update(theme: Theme) {
|
||||||
|
super.update(theme: theme)
|
||||||
|
|
||||||
|
self.textMessageContentView?.bubbleBackgroundView?.backgroundColor = theme.roomCellIncomingBubbleBackgroundColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func setupBubbleConstraints() {
|
||||||
|
|
||||||
|
self.textMessageContentView?.bubbleBackgroundViewLeadingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.left
|
||||||
|
|
||||||
|
let innerContentViewTrailingMargin = self.bubbleCellContentView?.innerContentViewTrailingConstraint.constant ?? 0
|
||||||
|
|
||||||
|
self.textMessageContentView?.bubbleBackgroundViewTrailingConstraint.constant = BubbleRoomCellLayoutConstants.incomingBubbleBackgroundMargins.right - innerContentViewTrailingMargin
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 New Vector 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 UIKit
|
||||||
|
|
||||||
|
class TextMessageIncomingWithPaginationTitleBubbleCell: TextMessageIncomingBubbleCell {
|
||||||
|
|
||||||
|
override func setupViews() {
|
||||||
|
super.setupViews()
|
||||||
|
|
||||||
|
bubbleCellContentView?.showPaginationTitle = true
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 New Vector 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 UIKit
|
||||||
|
|
||||||
|
class TextMessageIncomingWithPaginationTitleWithoutSenderNameBubbleCell: TextMessageIncomingWithPaginationTitleBubbleCell {
|
||||||
|
|
||||||
|
override func setupViews() {
|
||||||
|
super.setupViews()
|
||||||
|
|
||||||
|
bubbleCellContentView?.showSenderName = false
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 New Vector 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 UIKit
|
||||||
|
|
||||||
|
class TextMessageIncomingWithoutSenderInfoBubbleCell: TextMessageIncomingBubbleCell {
|
||||||
|
|
||||||
|
override func setupViews() {
|
||||||
|
super.setupViews()
|
||||||
|
|
||||||
|
bubbleCellContentView?.showSenderInfo = false
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 New Vector 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 UIKit
|
||||||
|
|
||||||
|
class TextMessageIncomingWithoutSenderNameBubbleCell: TextMessageIncomingBubbleCell {
|
||||||
|
|
||||||
|
override func setupViews() {
|
||||||
|
super.setupViews()
|
||||||
|
|
||||||
|
bubbleCellContentView?.showSenderName = false
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 New Vector 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 UIKit
|
||||||
|
|
||||||
|
class TextMessageOutgoingWithPaginationTitleBubbleCell: TextMessageOutgoingWithoutSenderInfoBubbleCell {
|
||||||
|
|
||||||
|
override func setupViews() {
|
||||||
|
super.setupViews()
|
||||||
|
|
||||||
|
bubbleCellContentView?.showPaginationTitle = true
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 New Vector 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.
|
||||||
|
//
|
||||||
|
|
||||||
|
class TextMessageOutgoingWithPaginationTitleWithoutSenderNameBubbleCell: TextMessageOutgoingWithPaginationTitleBubbleCell {
|
||||||
|
|
||||||
|
override func setupViews() {
|
||||||
|
super.setupViews()
|
||||||
|
|
||||||
|
bubbleCellContentView?.showSenderName = false
|
||||||
|
}
|
||||||
|
}
|
||||||
+73
@@ -0,0 +1,73 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 New Vector 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 UIKit
|
||||||
|
|
||||||
|
class TextMessageOutgoingWithoutSenderInfoBubbleCell: TextMessageBaseBubbleCell, BubbleOutgoingRoomCellProtocol {
|
||||||
|
|
||||||
|
// MARK: - Overrides
|
||||||
|
|
||||||
|
override func setupViews() {
|
||||||
|
super.setupViews()
|
||||||
|
|
||||||
|
bubbleCellContentView?.showSenderInfo = false
|
||||||
|
|
||||||
|
self.setupBubbleConstraints()
|
||||||
|
self.setupBubbleDecorations()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func update(theme: Theme) {
|
||||||
|
super.update(theme: theme)
|
||||||
|
|
||||||
|
self.textMessageContentView?.bubbleBackgroundView?.backgroundColor = theme.roomCellOutgoingBubbleBackgroundColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func setupBubbleConstraints() {
|
||||||
|
|
||||||
|
guard let containerView = self.textMessageContentView, let bubbleBackgroundView = containerView.bubbleBackgroundView else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove existing contraints
|
||||||
|
|
||||||
|
if let bubbleBackgroundViewLeadingConstraint = self.textMessageContentView?.bubbleBackgroundViewLeadingConstraint {
|
||||||
|
bubbleBackgroundViewLeadingConstraint.isActive = false
|
||||||
|
self.textMessageContentView?.bubbleBackgroundViewLeadingConstraint = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let bubbleBackgroundViewTrailingConstraint = self.textMessageContentView?.bubbleBackgroundViewTrailingConstraint {
|
||||||
|
bubbleBackgroundViewTrailingConstraint.isActive = false
|
||||||
|
self.textMessageContentView?.bubbleBackgroundViewTrailingConstraint = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup new constraints
|
||||||
|
|
||||||
|
let leadingConstraint = bubbleBackgroundView.leadingAnchor.constraint(greaterThanOrEqualTo: containerView.leadingAnchor, constant: BubbleRoomCellLayoutConstants.outgoingBubbleBackgroundMargins.left)
|
||||||
|
|
||||||
|
let trailingConstraint = bubbleBackgroundView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: 0)
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
leadingConstraint,
|
||||||
|
trailingConstraint
|
||||||
|
])
|
||||||
|
|
||||||
|
self.textMessageContentView?.bubbleBackgroundViewLeadingConstraint = leadingConstraint
|
||||||
|
|
||||||
|
self.textMessageContentView?.bubbleBackgroundViewTrailingConstraint = trailingConstraint
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-1
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class VoiceMessageIncomingBubbleCell: VoiceMessageBubbleCell {
|
class VoiceMessageIncomingBubbleCell: VoiceMessageBubbleCell, BubbleIncomingRoomCellProtocol {
|
||||||
|
|
||||||
override func setupViews() {
|
override func setupViews() {
|
||||||
super.setupViews()
|
super.setupViews()
|
||||||
@@ -30,6 +30,8 @@ class VoiceMessageIncomingBubbleCell: VoiceMessageBubbleCell {
|
|||||||
bubbleCellContentView?.innerContentViewLeadingConstraint.constant = messageLeftMargin
|
bubbleCellContentView?.innerContentViewLeadingConstraint.constant = messageLeftMargin
|
||||||
|
|
||||||
playbackController.playbackView.stackViewTrailingContraint.constant = playbackViewRightMargin
|
playbackController.playbackView.stackViewTrailingContraint.constant = playbackViewRightMargin
|
||||||
|
|
||||||
|
self.setupBubbleDecorations()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func update(theme: Theme) {
|
override func update(theme: Theme) {
|
||||||
|
|||||||
+1
@@ -17,6 +17,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class VoiceMessageIncomingWithPaginationTitleBubbleCell: VoiceMessageIncomingBubbleCell {
|
class VoiceMessageIncomingWithPaginationTitleBubbleCell: VoiceMessageIncomingBubbleCell {
|
||||||
|
|
||||||
override func setupViews() {
|
override func setupViews() {
|
||||||
super.setupViews()
|
super.setupViews()
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class VoiceMessageOutgoingWithoutSenderInfoBubbleCell: VoiceMessageBubbleCell {
|
class VoiceMessageOutgoingWithoutSenderInfoBubbleCell: VoiceMessageBubbleCell, BubbleOutgoingRoomCellProtocol {
|
||||||
|
|
||||||
override func setupViews() {
|
override func setupViews() {
|
||||||
super.setupViews()
|
super.setupViews()
|
||||||
@@ -33,6 +33,8 @@ class VoiceMessageOutgoingWithoutSenderInfoBubbleCell: VoiceMessageBubbleCell {
|
|||||||
bubbleCellContentView?.innerContentViewLeadingConstraint.constant = leftMargin
|
bubbleCellContentView?.innerContentViewLeadingConstraint.constant = leftMargin
|
||||||
|
|
||||||
playbackController.playbackView.stackViewTrailingContraint.constant = playbackViewRightMargin
|
playbackController.playbackView.stackViewTrailingContraint.constant = playbackViewRightMargin
|
||||||
|
|
||||||
|
self.setupBubbleDecorations()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func update(theme: Theme) {
|
override func update(theme: Theme) {
|
||||||
|
|||||||
+4
@@ -77,7 +77,11 @@ class RoomMessageBubbleBackgroundView: UIView {
|
|||||||
|
|
||||||
// MARK: - TimestampDisplayable
|
// MARK: - TimestampDisplayable
|
||||||
extension RoomMessageBubbleBackgroundView: TimestampDisplayable {
|
extension RoomMessageBubbleBackgroundView: TimestampDisplayable {
|
||||||
|
|
||||||
func addTimestampView(_ timestampView: UIView) {
|
func addTimestampView(_ timestampView: UIView) {
|
||||||
|
|
||||||
|
self.removeTimestampView()
|
||||||
|
|
||||||
self.addTimestampView(timestampView, rightMargin: 8.0, bottomMargin: 4.0)
|
self.addTimestampView(timestampView, rightMargin: 8.0, bottomMargin: 4.0)
|
||||||
self.timestampView = timestampView
|
self.timestampView = timestampView
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
|
|
||||||
#pragma mark - Registration
|
#pragma mark - Registration
|
||||||
|
|
||||||
|
- (void)registerIncomingTextMessageCellsForTableView:(UITableView*)tableView;
|
||||||
|
|
||||||
|
- (void)registerOutgoingTextMessageCellsForTableView:(UITableView*)tableView;
|
||||||
|
|
||||||
- (void)registerVoiceMessageCellsForTableView:(UITableView*)tableView;
|
- (void)registerVoiceMessageCellsForTableView:(UITableView*)tableView;
|
||||||
|
|
||||||
- (void)registerPollCellsForTableView:(UITableView*)tableView;
|
- (void)registerPollCellsForTableView:(UITableView*)tableView;
|
||||||
@@ -30,8 +34,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
|
|
||||||
#pragma mark - Mapping
|
#pragma mark - Mapping
|
||||||
|
|
||||||
|
- (NSDictionary<NSNumber*, Class>*)incomingTextMessageCellsMapping;
|
||||||
|
|
||||||
- (NSDictionary<NSNumber*, Class>*)outgoingTextMessageCellsMapping;
|
- (NSDictionary<NSNumber*, Class>*)outgoingTextMessageCellsMapping;
|
||||||
|
|
||||||
|
- (NSDictionary<NSNumber*, Class>*)incomingEmoteCellsMapping;
|
||||||
|
|
||||||
|
- (NSDictionary<NSNumber*, Class>*)outgoingEmoteCellsMapping;
|
||||||
|
|
||||||
- (NSDictionary<NSNumber*, Class>*)outgoingAttachmentCellsMapping;
|
- (NSDictionary<NSNumber*, Class>*)outgoingAttachmentCellsMapping;
|
||||||
|
|
||||||
- (NSDictionary<NSNumber*, Class>*)incomingAttachmentWithoutThumbnailCellsMapping;
|
- (NSDictionary<NSNumber*, Class>*)incomingAttachmentWithoutThumbnailCellsMapping;
|
||||||
|
|||||||
@@ -275,6 +275,13 @@
|
|||||||
NSDictionary *outgoingTextMessageCellsMapping = [self outgoingTextMessageCellsMapping];
|
NSDictionary *outgoingTextMessageCellsMapping = [self outgoingTextMessageCellsMapping];
|
||||||
[cellClasses addEntriesFromDictionary:outgoingTextMessageCellsMapping];
|
[cellClasses addEntriesFromDictionary:outgoingTextMessageCellsMapping];
|
||||||
|
|
||||||
|
// Emote
|
||||||
|
NSDictionary *incomingEmoteCellsMapping = [self incomingEmoteCellsMapping];
|
||||||
|
[cellClasses addEntriesFromDictionary:incomingEmoteCellsMapping];
|
||||||
|
|
||||||
|
NSDictionary *outgoingEmoteCellsMapping = [self outgoingEmoteCellsMapping];
|
||||||
|
[cellClasses addEntriesFromDictionary:outgoingEmoteCellsMapping];
|
||||||
|
|
||||||
// Attachment
|
// Attachment
|
||||||
|
|
||||||
NSDictionary *incomingAttachmentCellsMapping = [self incomingAttachmentCellsMapping];
|
NSDictionary *incomingAttachmentCellsMapping = [self incomingAttachmentCellsMapping];
|
||||||
@@ -360,6 +367,42 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSDictionary<NSNumber*, Class>*)incomingEmoteCellsMapping
|
||||||
|
{
|
||||||
|
return @{
|
||||||
|
// Clear
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmote) : RoomIncomingTextMsgBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmoteWithoutSenderInfo) : RoomIncomingTextMsgWithoutSenderInfoBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmoteWithPaginationTitle) : RoomIncomingTextMsgWithPaginationTitleBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmoteWithoutSenderName) : RoomIncomingTextMsgWithoutSenderNameBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmoteWithPaginationTitleWithoutSenderName) : RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.class,
|
||||||
|
// Encrypted
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmoteEncrypted) : RoomIncomingEncryptedTextMsgBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmoteEncryptedWithoutSenderInfo) : RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmoteEncryptedWithPaginationTitle) : RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmoteEncryptedWithoutSenderName) : RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierIncomingEmoteEncryptedWithPaginationTitleWithoutSenderName) : RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.class,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary<NSNumber*, Class>*)outgoingEmoteCellsMapping
|
||||||
|
{
|
||||||
|
return @{
|
||||||
|
// Clear
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmote) : RoomOutgoingTextMsgBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmoteWithoutSenderInfo) : RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmoteWithPaginationTitle) : RoomOutgoingTextMsgWithPaginationTitleBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmoteWithoutSenderName) : RoomOutgoingTextMsgWithoutSenderNameBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmoteWithPaginationTitleWithoutSenderName) : RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.class,
|
||||||
|
// Encrypted
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmoteEncrypted) : RoomOutgoingEncryptedTextMsgBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmoteEncryptedWithoutSenderInfo) : RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmoteEncryptedWithPaginationTitle) : RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmoteEncryptedWithoutSenderName) : RoomOutgoingEncryptedTextMsgWithoutSenderNameBubbleCell.class,
|
||||||
|
@(RoomTimelineCellIdentifierOutgoingEmoteEncryptedWithPaginationTitleWithoutSenderName) : RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.class,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
- (NSDictionary<NSNumber*, Class>*)incomingAttachmentCellsMapping
|
- (NSDictionary<NSNumber*, Class>*)incomingAttachmentCellsMapping
|
||||||
{
|
{
|
||||||
return @{
|
return @{
|
||||||
|
|||||||
@@ -29,4 +29,6 @@ protocol RoomCellLayoutUpdating: Themable {
|
|||||||
func setupLayout(forOutgoingFileAttachmentCell cell: MXKRoomBubbleTableViewCell)
|
func setupLayout(forOutgoingFileAttachmentCell cell: MXKRoomBubbleTableViewCell)
|
||||||
|
|
||||||
func updateLayout(forSelectedStickerCell cell: RoomSelectedStickerBubbleCell)
|
func updateLayout(forSelectedStickerCell cell: RoomSelectedStickerBubbleCell)
|
||||||
|
|
||||||
|
func maximumTextViewWidth(for cell: MXKRoomBubbleTableViewCell, cellData: MXKCellData, maximumCellWidth: CGFloat) -> CGFloat
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 New Vector 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 Lircense.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
/// `URLPreviewViewSizer` allows to determine reactions view height for a given urlPreviewData and width.
|
||||||
|
class URLPreviewViewSizer {
|
||||||
|
|
||||||
|
// MARK: - Constants
|
||||||
|
|
||||||
|
private static let sizingView = URLPreviewView.instantiate()
|
||||||
|
|
||||||
|
// MARK: - Public
|
||||||
|
|
||||||
|
func height(for urlPreviewData: URLPreviewData, fittingWidth width: CGFloat) -> CGFloat {
|
||||||
|
|
||||||
|
let sizingView = URLPreviewViewSizer.sizingView
|
||||||
|
|
||||||
|
sizingView.frame.size.height = 1.0
|
||||||
|
sizingView.preview = urlPreviewData
|
||||||
|
sizingView.availableWidth = width
|
||||||
|
|
||||||
|
sizingView.setNeedsLayout()
|
||||||
|
sizingView.layoutIfNeeded()
|
||||||
|
|
||||||
|
let fittingSize = CGSize(width: width, height: UIView.layoutFittingCompressedSize.height)
|
||||||
|
|
||||||
|
return sizingView.systemLayoutSizeFitting(fittingSize).height
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -67,3 +67,4 @@
|
|||||||
#import "MXKRoomDataSourceManager.h"
|
#import "MXKRoomDataSourceManager.h"
|
||||||
#import "MXRoom+Sync.h"
|
#import "MXRoom+Sync.h"
|
||||||
#import "UIAlertController+MatrixKit.h"
|
#import "UIAlertController+MatrixKit.h"
|
||||||
|
#import "MXKMessageTextView.h"
|
||||||
|
|||||||
Reference in New Issue
Block a user