element-ios/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m

884 lines
40 KiB
Mathematica
Raw Normal View History

/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations 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 "MXKRoomBubbleTableViewCell+Riot.h"
#import <objc/runtime.h>
#import "RoomBubbleCellData.h"
#import "ThemeService.h"
#import "GeneratedInterface-Swift.h"
#define VECTOR_ROOMBUBBLETABLEVIEWCELL_MARK_X 48
#define VECTOR_ROOMBUBBLETABLEVIEWCELL_MARK_WIDTH 4
NSString *const kMXKRoomBubbleCellRiotEditButtonPressed = @"kMXKRoomBubbleCellRiotEditButtonPressed";
2017-06-26 18:22:32 +00:00
NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellTapOnReceiptsContainer";
NSString *const kMXKRoomBubbleCellTapOnAddReaction = @"kMXKRoomBubbleCellTapOnAddReaction";
NSString *const kMXKRoomBubbleCellLongPressOnReactionView = @"kMXKRoomBubbleCellLongPressOnReactionView";
NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestAcceptPressed = @"kMXKRoomBubbleCellKeyVerificationAcceptPressed";
NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed = @"kMXKRoomBubbleCellKeyVerificationDeclinePressed";
@implementation MXKRoomBubbleTableViewCell (Riot)
- (void)addTimestampLabelForComponent:(NSUInteger)componentIndex
{
BOOL isFirstDisplayedComponent = (componentIndex == 0);
BOOL isLastMessageMostRecentComponent = NO;
RoomBubbleCellData *roomBubbleCellData;
if ([bubbleData isKindOfClass:RoomBubbleCellData.class])
{
roomBubbleCellData = (RoomBubbleCellData*)bubbleData;
isFirstDisplayedComponent = (componentIndex == roomBubbleCellData.oldestComponentIndex);
isLastMessageMostRecentComponent = roomBubbleCellData.containsLastMessage && (componentIndex == roomBubbleCellData.mostRecentComponentIndex);
}
// Display timestamp on the left for selected component when it cannot overlap other UI elements like user's avatar
BOOL displayLabelOnLeft = roomBubbleCellData.displayTimestampForSelectedComponentOnLeftWhenPossible
&& !isLastMessageMostRecentComponent
&& (!isFirstDisplayedComponent || roomBubbleCellData.shouldHideSenderInformation);
[self addTimestampLabelForComponent:componentIndex displayOnLeft:displayLabelOnLeft];
}
- (void)addTimestampLabelForComponent:(NSUInteger)componentIndex
displayOnLeft:(BOOL)displayLabelOnLeft
{
MXKRoomBubbleComponent *component;
NSArray *bubbleComponents = bubbleData.bubbleComponents;
if (componentIndex < bubbleComponents.count)
{
component = bubbleComponents[componentIndex];
}
if (component && component.date)
{
BOOL isFirstDisplayedComponent = (componentIndex == 0);
RoomBubbleCellData *roomBubbleCellData;
if ([bubbleData isKindOfClass:RoomBubbleCellData.class])
{
roomBubbleCellData = (RoomBubbleCellData*)bubbleData;
isFirstDisplayedComponent = (componentIndex == roomBubbleCellData.oldestComponentIndex);
}
[self addTimestampLabelForComponentIndex:componentIndex
isFirstDisplayedComponent:isFirstDisplayedComponent
viewTag:componentIndex
displayOnLeft:displayLabelOnLeft];
}
}
- (void)addTimestampLabelForComponentIndex:(NSInteger)componentIndex
isFirstDisplayedComponent:(BOOL)isFirstDisplayedComponent
viewTag:(NSInteger)viewTag
displayOnLeft:(BOOL)displayOnLeft
{
if (!self.bubbleInfoContainer)
{
MXLogDebug(@"[MXKRoomBubbleTableViewCell+Riot] bubbleInfoContainer property is missing for cell class: %@", NSStringFromClass(self.class));
return;
}
NSArray *bubbleComponents = bubbleData.bubbleComponents;
MXKRoomBubbleComponent *component = bubbleComponents[componentIndex];
self.bubbleInfoContainer.hidden = NO;
CGFloat timeLabelPosX;
CGFloat timeLabelPosY;
CGFloat timeLabelHeight = PlainRoomCellLayoutConstants.timestampLabelHeight;
CGFloat timeLabelWidth;
NSTextAlignment timeLabelTextAlignment;
CGRect componentFrame = [self componentFrameInContentViewForIndex:componentIndex];
if (displayOnLeft)
{
CGFloat leftMargin = 10.0;
CGFloat rightMargin = (self.contentView.frame.size.width - (self.bubbleInfoContainer.frame.origin.x + self.bubbleInfoContainer.frame.size.width));
timeLabelPosX = 0;
if (CGRectEqualToRect(componentFrame, CGRectNull) == false)
{
timeLabelPosY = componentFrame.origin.y - self.bubbleInfoContainerTopConstraint.constant;
}
else
{
timeLabelPosY = component.position.y + self.msgTextViewTopConstraint.constant - self.bubbleInfoContainerTopConstraint.constant;
}
timeLabelWidth = self.contentView.frame.size.width - leftMargin - rightMargin;
timeLabelTextAlignment = NSTextAlignmentLeft;
}
else
{
timeLabelPosX = self.bubbleInfoContainer.frame.size.width - PlainRoomCellLayoutConstants.timestampLabelWidth;
if (isFirstDisplayedComponent)
{
timeLabelPosY = 0;
}
else if (CGRectEqualToRect(componentFrame, CGRectNull) == false)
{
timeLabelPosY = componentFrame.origin.y - self.bubbleInfoContainerTopConstraint.constant - timeLabelHeight;
}
else
{
timeLabelPosY = component.position.y + self.msgTextViewTopConstraint.constant - timeLabelHeight - self.bubbleInfoContainerTopConstraint.constant;
}
timeLabelWidth = PlainRoomCellLayoutConstants.timestampLabelWidth;
timeLabelTextAlignment = NSTextAlignmentRight;
}
timeLabelPosY = MAX(0.0, timeLabelPosY);
UILabel *timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(timeLabelPosX, timeLabelPosY, timeLabelWidth, timeLabelHeight)];
timeLabel.text = [bubbleData.eventFormatter timeStringFromDate:component.date];
timeLabel.textAlignment = timeLabelTextAlignment;
timeLabel.textColor = ThemeService.shared.theme.textSecondaryColor;
timeLabel.font = [UIFont systemFontOfSize:12 weight:UIFontWeightLight];
timeLabel.adjustsFontSizeToFitWidth = YES;
timeLabel.tag = viewTag;
[timeLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
timeLabel.accessibilityIdentifier = @"timestampLabel";
[self.bubbleInfoContainer addSubview:timeLabel];
// Define timeLabel constraints (to handle auto-layout in case of screen rotation)
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:timeLabel
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.bubbleInfoContainer
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:timeLabel
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.bubbleInfoContainer
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:timeLabelPosY];
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:timeLabel
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:timeLabelWidth];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:timeLabel
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:timeLabelHeight];
// Available on iOS 8 and later
[NSLayoutConstraint activateConstraints:@[rightConstraint, topConstraint, widthConstraint, heightConstraint]];
}
- (void)selectComponent:(NSUInteger)componentIndex
{
[self selectComponent:componentIndex showEditButton:NO showTimestamp:YES];
}
- (void)selectComponent:(NSUInteger)componentIndex showEditButton:(BOOL)showEditButton showTimestamp:(BOOL)showTimestamp
{
if (componentIndex < bubbleData.bubbleComponents.count)
{
if (showTimestamp)
{
// Add time label
[self addTimestampLabelForComponent:componentIndex];
}
// Blur timestamp labels which are not related to the selected component (if any)
for (UIView* view in self.bubbleInfoContainer.subviews)
{
// Note dateTime label tag is equal to the index of the related component.
if (view.tag != componentIndex)
{
view.alpha = 0.2;
}
}
// Retrieve the read receipts container related to the selected component (if any)
// Blur the others
for (UIView* view in self.tmpSubviews)
{
// Note read receipt container tag is equal to the index of the related component.
if (view.tag != componentIndex)
{
view.alpha = 0.2;
}
}
if (showEditButton)
{
// Add the edit button
[self addEditButtonForComponent:componentIndex completion:nil];
}
}
}
- (void)markComponent:(NSUInteger)componentIndex
{
NSArray *bubbleComponents = bubbleData.bubbleComponents;
if (componentIndex < bubbleComponents.count)
{
MXKRoomBubbleComponent *component = bubbleComponents[componentIndex];
// Define the marker frame
CGFloat markPosY = component.position.y + self.msgTextViewTopConstraint.constant;
NSInteger mostRecentComponentIndex = bubbleComponents.count - 1;
if ([bubbleData isKindOfClass:RoomBubbleCellData.class])
{
mostRecentComponentIndex = ((RoomBubbleCellData*)bubbleData).mostRecentComponentIndex;
}
// Compute the mark height.
// Use the rest of the cell height by default.
CGFloat markHeight = self.contentView.frame.size.height - markPosY;
if (componentIndex != mostRecentComponentIndex)
{
// There is another component (with display) after this component in the cell.
// Stop the marker height to the top of this component.
for (NSInteger index = componentIndex + 1; index < bubbleComponents.count; index ++)
{
MXKRoomBubbleComponent *nextComponent = bubbleComponents[index];
if (nextComponent.attributedTextMessage)
{
markHeight = nextComponent.position.y - component.position.y;
break;
}
}
}
UIView *markerView = [[UIView alloc] initWithFrame:CGRectMake(VECTOR_ROOMBUBBLETABLEVIEWCELL_MARK_X,
markPosY,
VECTOR_ROOMBUBBLETABLEVIEWCELL_MARK_WIDTH,
markHeight)];
2019-01-11 10:45:27 +00:00
markerView.backgroundColor = ThemeService.shared.theme.tintColor;
[markerView setTranslatesAutoresizingMaskIntoConstraints:NO];
markerView.accessibilityIdentifier = @"markerView";
[self.contentView addSubview:markerView];
// Define the marker constraints
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:markerView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.contentView
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:VECTOR_ROOMBUBBLETABLEVIEWCELL_MARK_X];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:markerView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.contentView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:markPosY];
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:markerView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:VECTOR_ROOMBUBBLETABLEVIEWCELL_MARK_WIDTH];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:markerView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:markHeight];
// Available on iOS 8 and later
[NSLayoutConstraint activateConstraints:@[leftConstraint, topConstraint, widthConstraint, heightConstraint]];
// Store the created button
self.markerView = markerView;
}
}
- (void)addDateLabel
{
self.bubbleInfoContainer.hidden = NO;
NSDate *date = bubbleData.date;
if (date)
{
UILabel *timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.bubbleInfoContainer.frame.size.width, PlainRoomCellLayoutConstants.timestampLabelHeight)];
timeLabel.text = [bubbleData.eventFormatter dateStringFromDate:date withTime:NO];
timeLabel.textAlignment = NSTextAlignmentRight;
2019-01-11 10:45:27 +00:00
timeLabel.textColor = ThemeService.shared.theme.textSecondaryColor;
if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)])
{
timeLabel.font = [UIFont systemFontOfSize:12 weight:UIFontWeightLight];
}
else
{
timeLabel.font = [UIFont systemFontOfSize:12];
}
timeLabel.adjustsFontSizeToFitWidth = YES;
[timeLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
timeLabel.accessibilityIdentifier = @"dateLabel";
[self.bubbleInfoContainer addSubview:timeLabel];
// Define timeLabel constraints (to handle auto-layout in case of screen rotation)
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:timeLabel
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.bubbleInfoContainer
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:timeLabel
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.bubbleInfoContainer
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:timeLabel
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.bubbleInfoContainer
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:timeLabel
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:PlainRoomCellLayoutConstants.timestampLabelHeight];
// Available on iOS 8 and later
[NSLayoutConstraint activateConstraints:@[rightConstraint, topConstraint, widthConstraint, heightConstraint]];
}
}
- (void)setBlurred:(BOOL)blurred
{
objc_setAssociatedObject(self, @selector(blurred), @(blurred), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if (blurred)
{
self.bubbleOverlayContainer.hidden = NO;
2019-01-11 10:45:27 +00:00
self.bubbleOverlayContainer.backgroundColor = ThemeService.shared.theme.backgroundColor;
self.bubbleOverlayContainer.alpha = 0.8;
self.bubbleOverlayContainer.userInteractionEnabled = YES;
// Blur subviews if any
for (UIView* view in self.bubbleOverlayContainer.subviews)
{
view.alpha = 0.2;
}
// Move this view in front
2021-07-06 21:03:56 +00:00
[self.bubbleOverlayContainer.superview bringSubviewToFront:self.bubbleOverlayContainer];
}
else
{
if (self.bubbleOverlayContainer.subviews.count)
{
// Keep this overlay visible, adjust background color
self.bubbleOverlayContainer.backgroundColor = [UIColor clearColor];
self.bubbleOverlayContainer.alpha = 1;
self.bubbleOverlayContainer.userInteractionEnabled = NO;
// Restore subviews display
for (UIView* view in self.bubbleOverlayContainer.subviews)
{
view.alpha = 1;
}
}
else
{
self.bubbleOverlayContainer.hidden = YES;
}
}
}
- (BOOL)blurred
{
NSNumber *associatedBlurred = objc_getAssociatedObject(self, @selector(blurred));
if (associatedBlurred)
{
return [associatedBlurred boolValue];
}
return NO;
}
- (void)setEditButton:(UIButton *)editButton
{
objc_setAssociatedObject(self, @selector(editButton), editButton, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIButton*)editButton
{
return objc_getAssociatedObject(self, @selector(editButton));
}
- (void)setMarkerView:(UIView *)markerView
{
objc_setAssociatedObject(self, @selector(markerView), markerView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(UIView *)markerView
{
return objc_getAssociatedObject(self, @selector(markerView));
}
- (void)setMessageStatusViews:(NSArray *)arrayOfViews
{
objc_setAssociatedObject(self, @selector(messageStatusViews), arrayOfViews, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSArray *)messageStatusViews
{
return objc_getAssociatedObject(self, @selector(messageStatusViews));
}
- (void)updateUserNameColor
{
static UserNameColorGenerator *userNameColorGenerator;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
userNameColorGenerator = [UserNameColorGenerator new];
});
id<Theme> theme = ThemeService.shared.theme;
userNameColorGenerator.defaultColor = theme.textPrimaryColor;
userNameColorGenerator.userNameColors = theme.userNameColors;
NSString *senderId = self.bubbleData.senderId;
if (senderId)
{
self.userNameLabel.textColor = [userNameColorGenerator colorFrom:senderId];
}
else
{
self.userNameLabel.textColor = userNameColorGenerator.defaultColor;
}
}
- (CGRect)componentFrameInTableViewForIndex:(NSInteger)componentIndex
{
CGRect componentFrameInContentView = [self componentFrameInContentViewForIndex:componentIndex];
return [self.contentView convertRect:componentFrameInContentView toView:self.superview];
}
- (CGRect)surroundingFrameInTableViewForComponentIndex:(NSInteger)componentIndex
{
CGRect surroundingFrame;
CGRect componentFrameInContentView = [self componentFrameInContentViewForIndex:componentIndex];
MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = self;
MXKRoomBubbleCellData *bubbleCellData = roomBubbleTableViewCell.bubbleData;
NSInteger firstVisibleComponentIndex = NSNotFound;
NSInteger lastMostRecentComponentIndex = NSNotFound;
if ([bubbleCellData isKindOfClass:[RoomBubbleCellData class]])
{
RoomBubbleCellData *roomBubbleCellData = (RoomBubbleCellData*)bubbleCellData;
firstVisibleComponentIndex = [roomBubbleCellData firstVisibleComponentIndex];
if (roomBubbleCellData.containsLastMessage
&& roomBubbleCellData.mostRecentComponentIndex != NSNotFound
&& roomBubbleCellData.firstVisibleComponentIndex != roomBubbleCellData.mostRecentComponentIndex
&& componentIndex == roomBubbleCellData.mostRecentComponentIndex)
{
lastMostRecentComponentIndex = roomBubbleCellData.mostRecentComponentIndex;
}
}
// Do not overlap timestamp for last message
if (lastMostRecentComponentIndex != NSNotFound)
{
CGFloat componentBottomY = componentFrameInContentView.origin.y + componentFrameInContentView.size.height;
CGFloat x = 0;
CGFloat y = componentFrameInContentView.origin.y - PlainRoomCellLayoutConstants.timestampLabelHeight;
CGFloat width = roomBubbleTableViewCell.contentView.frame.size.width;
CGFloat height = componentBottomY - y;
surroundingFrame = CGRectMake(x, y, width, height);
} // Do not overlap user name label for first visible component
else if (!CGRectEqualToRect(componentFrameInContentView, CGRectNull)
&& firstVisibleComponentIndex != NSNotFound
&& componentIndex <= firstVisibleComponentIndex
&& roomBubbleTableViewCell.userNameLabel
&& roomBubbleTableViewCell.userNameLabel.isHidden == NO)
{
CGFloat componentBottomY = componentFrameInContentView.origin.y + componentFrameInContentView.size.height;
CGFloat x = 0;
CGFloat y = roomBubbleTableViewCell.userNameLabel.frame.origin.y;
CGFloat width = roomBubbleTableViewCell.contentView.frame.size.width;
CGFloat height = componentBottomY - y;
surroundingFrame = CGRectMake(x, y, width, height);
}
else
{
surroundingFrame = componentFrameInContentView;
}
return [self.contentView convertRect:surroundingFrame toView:self.superview];
}
- (CGRect)componentFrameInContentViewForIndex:(NSInteger)componentIndex
{
MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = self;
MXKRoomBubbleCellData *bubbleCellData = roomBubbleTableViewCell.bubbleData;
MXKRoomBubbleComponent *selectedComponent;
if (bubbleCellData.bubbleComponents.count > componentIndex)
{
selectedComponent = bubbleCellData.bubbleComponents[componentIndex];
}
if (!selectedComponent)
{
return CGRectNull;
}
CGFloat selectedComponenContentViewYOffset = 0;
CGFloat selectedComponentPositionY = 0;
CGFloat selectedComponentHeight = 0;
CGRect componentFrame = CGRectNull;
if (roomBubbleTableViewCell.attachmentView)
{
CGRect attachamentViewFrame = roomBubbleTableViewCell.attachmentView.frame;
selectedComponenContentViewYOffset = attachamentViewFrame.origin.y;
selectedComponentHeight = attachamentViewFrame.size.height;
}
else if (roomBubbleTableViewCell.messageTextView)
{
CGFloat textMessageHeight = 0;
if ([bubbleCellData isKindOfClass:[RoomBubbleCellData class]])
{
RoomBubbleCellData *roomBubbleCellData = (RoomBubbleCellData*)bubbleCellData;
if (!roomBubbleCellData.attachment && selectedComponent.attributedTextMessage)
{
textMessageHeight = [roomBubbleCellData rawTextHeight:selectedComponent.attributedTextMessage];
}
}
selectedComponentPositionY = selectedComponent.position.y;
if (textMessageHeight > 0)
{
selectedComponentHeight = textMessageHeight;
}
else
{
selectedComponentHeight = roomBubbleTableViewCell.frame.size.height - selectedComponentPositionY;
}
Release 1.9.12 (#7081) * Update voice broadcast tiles UI (#6965) * Translated using Weblate (German) Currently translated at 100.0% (2307 of 2307 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ * speeding the animation a bit * tests and identifier improvements * fix * changelog * removed unused code * Avoid unnecessary send state request (#6970) * comment * Curate MXCrypto protocol methods * Add voice broadcast initial state in bubble data (#6972) - Add voice broadcast initial state in bubble data - Remove the local record after sending * Voice Broadcast: log and block unexpected state change * Sing out bottom bar * new line * Enable WYSIWYG plain text support * Remove change on Apple swift-collections revision * removed RiotSettings a non RiotSwiftUI reference from the ViewState code * fixed a test * Complete MXCryptoV2 implementation * Multi session logut * Switch the CI to code 14 and the iOS 14 simulator, fix UI tests * Fixes #6987 - Prevent ZXing from unnecessarily requesting camera access * Fixes #6988 - Prevent actor switching when tearing down the rendezvous * Separator fix * Removed warnings * add Z-Labs tag or rich text editor and update to the new label naming * changelog * Hide old sessions list when the new dm is enabled * Add changelog.d file * Sing out filtering * Avoid simultaneous state changes (#6986) * Improve kebab menu in UserSessionOverview * Add UI tests * Add changelog.d file * No customization for emptycell (#7000) * PSG-976 Exclude current session from security recommendations and other sessions * Padding fix * Fixed unit tests * Add empty onLearnMoreAction closure * Add InfoView skeleton * Add UserSessionOverviewViewBindings * Style info view * Add bottom sheet modifier * Localise content * Add inactive sessions copy * Fix bug in InlineTextButton * Improve UserSessionCardView * Add “learn more” button in UserOtherSessions * Show bottom sheet in user other sessions * Show rename info alert * Refine UX * Add iOS 15- fallback * Refine InfoView * Add UI tests * Improve UserOtherSessionsUITests * Improve InlineTextButton API * Add changelod.d file * Fix failing UTs * Hide keyboard in UserSessionName * Add .viewSessionInfo view action * Voice Broadcast - BugFix - send the last chunk (#7002) * Voice Broadcast - BugFix - send the last chunk with the right sequence number - we reset now and teardown the service only after the last chunk is sent * updated package + tests * change log * Bug Fix : Crash if the room has avatar and voice broadcast tiles * Add MVVM-C for InfoSheet * improving UI tests for slow CI * removing comment * test improvements for slow ci * Show bottom sheet in other sessions screen * Show bottom sheet in rename session screen * Delete bottom sheet modifier * Show rename sheet * UI and unit tests * Refresh fix * Changelog * Add InfoSheet SwiftUI preview * simplify the test to make it pass on the CI * Fix memory leak * Cleanup UI tests * improving tests for the CI * Fixed IRC-style message and commands support in Rich text editor * tests updated for the CI * test improvements * removing a test that can't pass on the CI due to its speed * Changelog * CryptoV2 changes * Display crypto version * Voice broadcast - Disable the sleep mode during the recording until we are able to handle it Currently go to "sleep mode" pauses the voice broadcast recording * Add issue automation for the VoIP team * Renamed sign out to logout * Renamed sign out to logout * Renamed sign out to logout * Sign out of all other sessions * Fix typo in issue automation * Fixed unit tests * Translations update from Weblate (#7017) * Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2311 of 2311 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/ * Translated using Weblate (Estonian) Currently translated at 100.0% (2311 of 2311 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ * Translated using Weblate (German) Currently translated at 100.0% (2311 of 2311 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ * Translated using Weblate (German) Currently translated at 100.0% (2311 of 2311 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ * Translated using Weblate (Dutch) Currently translated at 100.0% (2311 of 2311 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/nl/ Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com> Co-authored-by: Priit Jõerüüt <riot@joeruut.com> Co-authored-by: Vri <element@vrifox.cc> Co-authored-by: Johan Smits <johan@smitsmail.net> * Prepare for new sprint * Prepare for new sprint * Threads: added support to read receipts (MSC3771) - Update after review * Threads: added support to notifications count (MSC3773) * Update RiotSwiftUI/Modules/UserSessions/UserOtherSessions/Test/UI/UserOtherSessionsUITests.swift Co-authored-by: aringenbach <80891108+aringenbach@users.noreply.github.com> * Update RiotSwiftUI/Modules/UserSessions/UserOtherSessions/Test/UI/UserOtherSessionsUITests.swift Co-authored-by: aringenbach <80891108+aringenbach@users.noreply.github.com> * Comment fix * the test may fail on CI without blocking the task/check * tests may fail on CI * test improvement * test may fail on CI * Hide push toggles for http pushers when there is no server support * changelog * Code review fixes * Threads: added support to read receipts (MSC3771) - Update after review * Synchronise composer and toolbar resizing animation duration * Add kResizeComposerAnimationDuration constant description * fix for 6946 * Threads: add support to labs flag for read receipts * Cleanup * Code review fixes, created DestructiveButton * Update issue automation Stop using deprecated ProjectNext API in favour of the new ProjectV2 one * Update PR automation Stop using deprecated ProjectNext API in favour of the new ProjectV2 one * Code review fixes * Map location info * Map location info * Add location feature in UserSessionsOverview * Add “show location” feature in other sessions list * Add “show location“ feature in session overview * Fix Package.resolved * Cleanup merge leftovers * Cleanup code * Cleanup * Add show/hide ip persistency * Add location info in UserOtherSessions * Refine settings logic * Mock settings in UserSessionsOverviewViewModel * Add settings service in UserOtherSessionsViewModel * Inject setting service in UserSessionOverviewViewModel * Add changelog.d file * Fix UTs * Cleanup merge leftovers * Add animations * Fix failing test * Amend title font * Amend copies * Device Manager: Session list item is not tappable everywhere * changelog * Threads notification count in main timeline including un participated threads * Changed title and body * Removed "Do not ask again" button * Remove indication about plain text mode coming soon * Prevent `Unable to activate constraint with anchors .. because they have no common ancestor.` crashes. Only link toasts to the top safe area instead of the navigation controller * Revert "Replace attributed string height calculation with a more reliable implementation" This reverts commit 81773cd1e515cc391c1f21b499f61141cb03c810. * Revert "Fix timeline items text height calculation" This reverts commit 8f9eddee501702de84192316bd5b2ff9512d681a. * Revert "Fixes vector-im/element-ios/issues/6441 - Incorrect timeline item text height calculation (#6679)" This reverts commit 405c2d8e324c08c1a40e037aeb3c54e93f30bc9f. * Fixes vector-im/element-ios/issues/6441 - Incorrect timeline item text height calculation * Prepare for new sprint * Refine bottom sheet layout * updated pod * changelog * Switch to using an API key for interactions with AppStoreConnect while on CI; update fastlane and dependencies * Rich-text editor: Fix text formatting enabled inconsistent state * Labs: Rich-text editor - Fix text formatting switch losing the current content of the composer * Re-order View computed properties and move to private mark * Add intrinsic sized bottom sheet * Snooze controller * Changelog * Fix composer view model tests * Rich-text editor: enable translations between Markdown and HTML when toggling text formatting * Force a layout on the room bubble cell messageTextView to get a correct frame * Move Move UserAgentParserTests * Add UserSessionDetailsUITests * Improve UserSessionNameUITests * Cleanup tests * Improve UserSessionNameViewModelTests * Test empty state for UserOtherSessions * Fix typo * Cleanup unused code * Add changelog.d file * Threads: removed "unread_thread_notifications" from sync filters for server that doesn't support MSC3773 * Remove 10s wait on failed initial sync * Threads: removed "unread_thread_notifications" from sync filters for server that doesn't support MSC3773 - Update after review * Revert "Device Manager: Session list item is not tappable everywhere" This reverts commit e6367cba4c8f0cb2cdfe5e3381dcbb7bc0f94c52. * Fixup session list item is not tappable everywhere * Fix accessibility id in UserOtherSessions * Poll not usable after logging out and back in * Changelog * Removed init * voice dictation now works * plain text * Add voice broadcast slider (#7010) * Display number of unread messages above threads button * Translations update from Weblate (#7080) * Translated using Weblate (Dutch) Currently translated at 100.0% (2311 of 2311 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/nl/ * Translated using Weblate (German) Currently translated at 100.0% (2311 of 2311 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ * Translated using Weblate (Dutch) Currently translated at 100.0% (2312 of 2312 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/nl/ * Translated using Weblate (German) Currently translated at 100.0% (2312 of 2312 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ * Translated using Weblate (Albanian) Currently translated at 99.6% (2303 of 2312 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sq/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2312 of 2312 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (2312 of 2312 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ * Translated using Weblate (Estonian) Currently translated at 100.0% (2312 of 2312 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (2312 of 2312 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ * Translated using Weblate (Slovak) Currently translated at 100.0% (2312 of 2312 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/ * Translated using Weblate (Italian) Currently translated at 100.0% (2312 of 2312 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/ * Translated using Weblate (German) Currently translated at 100.0% (2313 of 2313 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (2313 of 2313 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (2313 of 2313 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ * Translated using Weblate (Dutch) Currently translated at 100.0% (2315 of 2315 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/nl/ * Translated using Weblate (German) Currently translated at 100.0% (2315 of 2315 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2315 of 2315 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/ * Translated using Weblate (Italian) Currently translated at 100.0% (2315 of 2315 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (2315 of 2315 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ * Translated using Weblate (Estonian) Currently translated at 100.0% (2315 of 2315 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (2315 of 2315 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ * Translated using Weblate (Slovak) Currently translated at 100.0% (2315 of 2315 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2315 of 2315 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/ * Translated using Weblate (Albanian) Currently translated at 99.5% (2305 of 2315 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sq/ * Translated using Weblate (German) Currently translated at 100.0% (2317 of 2317 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ * Translated using Weblate (Japanese) Currently translated at 66.2% (1534 of 2317 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ja/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2317 of 2317 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (2317 of 2317 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ * Translated using Weblate (Estonian) Currently translated at 100.0% (2317 of 2317 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (2317 of 2317 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ * Translated using Weblate (Slovak) Currently translated at 100.0% (2317 of 2317 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/ * Translated using Weblate (Japanese) Currently translated at 66.2% (1534 of 2317 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ja/ * Translated using Weblate (German) Currently translated at 100.0% (2326 of 2326 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (2326 of 2326 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ * Translated using Weblate (Estonian) Currently translated at 100.0% (2326 of 2326 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (2326 of 2326 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ * Translated using Weblate (Estonian) Currently translated at 100.0% (2326 of 2326 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ * Translated using Weblate (German) Currently translated at 100.0% (2326 of 2326 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (2326 of 2326 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ * Translated using Weblate (Estonian) Currently translated at 100.0% (2326 of 2326 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ * Translated using Weblate (Slovak) Currently translated at 100.0% (2326 of 2326 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/ * Translated using Weblate (Italian) Currently translated at 100.0% (2326 of 2326 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2326 of 2326 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (2326 of 2326 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ * Translated using Weblate (French) Currently translated at 97.6% (2272 of 2326 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/fr/ * Translated using Weblate (Russian) Currently translated at 80.5% (1873 of 2326 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/ * Translated using Weblate (Russian) Currently translated at 80.7% (1879 of 2326 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/ Co-authored-by: Roel ter Maat <roel.termaat@nedap.com> Co-authored-by: Vri <element@vrifox.cc> Co-authored-by: Besnik Bleta <besnik@programeshqip.org> Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com> Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com> Co-authored-by: Priit Jõerüüt <riot@joeruut.com> Co-authored-by: Linerly <linerly@protonmail.com> Co-authored-by: Jozef Gaal <preklady@mayday.sk> Co-authored-by: random <dictionary@tutamail.com> Co-authored-by: Szimszon <github@oregpreshaz.eu> Co-authored-by: Suguru Hirahara <ovestekona@protonmail.com> Co-authored-by: Thibault Martin <mail@thibaultmart.in> Co-authored-by: Platon Terekhov <ockenfels_vevent@aleeas.com> * changelog.d: Upgrade MatrixSDK version ([v0.24.3](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.24.3)). * version++ Co-authored-by: giomfo <gforet@matrix.org> Co-authored-by: Yoan Pintas <y.pintas@gmail.com> Co-authored-by: Vri <element@vrifox.cc> Co-authored-by: Anderas <andyuhnak@gmail.com> Co-authored-by: Mauro Romito <mauro.romito@element.io> Co-authored-by: Velin92 <34335419+Velin92@users.noreply.github.com> Co-authored-by: Giom Foret <giom@matrix.org> Co-authored-by: Aleksandrs Proskurins <paleksandrs@gmail.com> Co-authored-by: aringenbach <arnaudr@element.io> Co-authored-by: manuroe <manuroe@users.noreply.github.com> Co-authored-by: David Langley <langley.dave@gmail.com> Co-authored-by: Stefan Ceriu <stefanc@matrix.org> Co-authored-by: Alfonso Grillo <alfogrillo@gmail.com> Co-authored-by: Alfonso Grillo <alfogrillo@element.io> Co-authored-by: Kat Gerasimova <ekaterinag@element.io> Co-authored-by: Element Translate Bot <admin@riot.im> Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com> Co-authored-by: Priit Jõerüüt <riot@joeruut.com> Co-authored-by: Johan Smits <johan@smitsmail.net> Co-authored-by: gulekismail <ismailgulek0@gmail.com> Co-authored-by: Gil Eluard <gile@element.io> Co-authored-by: Aleksandrs Proskurins <aleksandrsp@element.io> Co-authored-by: aringenbach <80891108+aringenbach@users.noreply.github.com> Co-authored-by: Stefan Ceriu <stefan.ceriu@gmail.com> Co-authored-by: Roel ter Maat <roel.termaat@nedap.com> Co-authored-by: Besnik Bleta <besnik@programeshqip.org> Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com> Co-authored-by: Linerly <linerly@protonmail.com> Co-authored-by: Jozef Gaal <preklady@mayday.sk> Co-authored-by: random <dictionary@tutamail.com> Co-authored-by: Szimszon <github@oregpreshaz.eu> Co-authored-by: Suguru Hirahara <ovestekona@protonmail.com> Co-authored-by: Thibault Martin <mail@thibaultmart.in> Co-authored-by: Platon Terekhov <ockenfels_vevent@aleeas.com>
2022-11-15 13:40:36 +00:00
// Force the textView used underneath to layout its frame properly
[roomBubbleTableViewCell setNeedsLayout];
[roomBubbleTableViewCell layoutIfNeeded];
selectedComponenContentViewYOffset = roomBubbleTableViewCell.messageTextView.frame.origin.y;
}
if (roomBubbleTableViewCell.attachmentView || roomBubbleTableViewCell.messageTextView)
{
CGFloat x = 0;
CGFloat y = selectedComponenContentViewYOffset + selectedComponentPositionY;
CGFloat width = roomBubbleTableViewCell.contentView.frame.size.width;
componentFrame = CGRectMake(x, y, width, selectedComponentHeight);
}
else
{
componentFrame = roomBubbleTableViewCell.bounds;
}
return componentFrame;
}
+ (CGFloat)attachmentBubbleCellHeightForCellData:(MXKCellData *)cellData withMaximumWidth:(CGFloat)maxWidth
{
MXKRoomBubbleTableViewCell* cell = [self cellWithOriginalXib];
CGFloat rowHeight = 0;
RoomBubbleCellData *bubbleData;
if ([cellData isKindOfClass:[RoomBubbleCellData class]])
{
bubbleData = (RoomBubbleCellData*)cellData;
}
if (bubbleData && cell.attachmentView && bubbleData.isAttachmentWithThumbnail)
{
// retrieve the suggested image view height
rowHeight = bubbleData.contentSize.height;
// Check here the minimum height defined in cell view for text message
if (cell.attachViewMinHeightConstraint && rowHeight < cell.attachViewMinHeightConstraint.constant)
{
rowHeight = cell.attachViewMinHeightConstraint.constant;
}
// Finalize the row height by adding the vertical constraints.
rowHeight += cell.attachViewTopConstraint.constant;
CGFloat additionalHeight = bubbleData.additionalContentHeight;
if (additionalHeight)
{
rowHeight += additionalHeight;
}
else
{
rowHeight += cell.attachViewBottomConstraint.constant;
}
}
return rowHeight;
}
- (void)updateTickViewWithFailedEventIds:(NSSet *)failedEventIds
{
for (UIView *tickView in self.messageStatusViews)
{
[tickView removeFromSuperview];
}
self.messageStatusViews = nil;
NSMutableArray *statusViews = [NSMutableArray new];
UIView *tickView = nil;
if ([bubbleData isKindOfClass:RoomBubbleCellData.class]
&& ((RoomBubbleCellData*)bubbleData).componentIndexOfSentMessageTick >= 0)
{
UIImage *image = AssetImages.sentMessageTick.image;
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
tickView = [[UIImageView alloc] initWithImage:image];
tickView.tintColor = ThemeService.shared.theme.textTertiaryColor;
[statusViews addObject:tickView];
[self addTickView:tickView atIndex:((RoomBubbleCellData*)bubbleData).componentIndexOfSentMessageTick];
}
NSInteger index = bubbleData.bubbleComponents.count;
while (index--)
{
MXKRoomBubbleComponent *component = bubbleData.bubbleComponents[index];
NSArray<MXReceiptData*> *receipts = bubbleData.readReceipts[component.event.eventId];
if (receipts.count == 0) {
if (component.event.sentState == MXEventSentStateUploading
|| component.event.sentState == MXEventSentStateEncrypting
|| component.event.sentState == MXEventSentStatePreparing
|| component.event.sentState == MXEventSentStateSending)
{
if ([failedEventIds containsObject:component.event.eventId] || (bubbleData.attachment && component.event.sentState != MXEventSentStateSending))
{
UIView *progressContentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 40, 40)];
CircleProgressView *progressView = [[CircleProgressView alloc] initWithFrame:CGRectMake(24, 24, 16, 16)];
progressView.lineColor = ThemeService.shared.theme.textTertiaryColor;
[progressContentView addSubview:progressView];
self.progressChartView = progressView;
tickView = progressContentView;
[progressView startAnimating];
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onProgressLongPressGesture:)];
[tickView addGestureRecognizer:longPress];
}
else
{
UIImage *image = AssetImages.sendingMessageTick.image;
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
tickView = [[UIImageView alloc] initWithImage:image];
tickView.tintColor = ThemeService.shared.theme.textTertiaryColor;
}
[statusViews addObject:tickView];
[self addTickView:tickView atIndex:index];
}
}
if (component.event.sentState == MXEventSentStateFailed)
{
tickView = [[UIImageView alloc] initWithImage:AssetImages.errorMessageTick.image];
[statusViews addObject:tickView];
[self addTickView:tickView atIndex:index];
}
}
if (statusViews.count)
{
self.messageStatusViews = statusViews;
}
}
#pragma mark - User actions
- (IBAction)onEditButtonPressed:(id)sender
{
if (self.delegate)
{
MXEvent *selectedEvent = nil;
// Note edit button tag is equal to the index of the related component.
NSInteger index = ((UIView*)sender).tag;
NSArray *bubbleComponents = bubbleData.bubbleComponents;
if (index < bubbleComponents.count)
{
MXKRoomBubbleComponent *component = bubbleComponents[index];
selectedEvent = component.event;
}
if (selectedEvent)
{
[self.delegate cell:self didRecognizeAction:kMXKRoomBubbleCellRiotEditButtonPressed userInfo:@{kMXKRoomBubbleCellEventKey:selectedEvent}];
}
}
}
- (IBAction)onReceiptContainerTap:(UITapGestureRecognizer *)sender
{
if (self.delegate)
{
[self.delegate cell:self didRecognizeAction:kMXKRoomBubbleCellTapOnReceiptsContainer userInfo:@{kMXKRoomBubbleCellReceiptsContainerKey : sender.view}];
}
}
#pragma mark - Internals
- (void)addTickView:(UIView *)tickView atIndex:(NSInteger)index
{
CGRect componentFrame = [self componentFrameInContentViewForIndex: index];
tickView.frame = CGRectMake(self.contentView.bounds.size.width - tickView.frame.size.width - 2 * PlainRoomCellLayoutConstants.readReceiptsViewRightMargin, CGRectGetMaxY(componentFrame) - tickView.frame.size.height, tickView.frame.size.width, tickView.frame.size.height);
[self.contentView addSubview:tickView];
}
- (void)addEditButtonForComponent:(NSUInteger)componentIndex completion:(void (^ __nullable)(BOOL finished))completion
{
MXKRoomBubbleComponent *component = bubbleData.bubbleComponents[componentIndex];
// Check whether this is the first displayed component.
BOOL isFirstDisplayedComponent = (componentIndex == 0);
if ([bubbleData isKindOfClass:RoomBubbleCellData.class])
{
isFirstDisplayedComponent = (componentIndex == ((RoomBubbleCellData*)bubbleData).oldestComponentIndex);
}
// Define 'Edit' button frame
UIImage *editIcon = AssetImages.editIcon.image;
CGFloat editBtnPosX = self.bubbleInfoContainer.frame.size.width - PlainRoomCellLayoutConstants.timestampLabelWidth - 22 - editIcon.size.width / 2;
CGFloat editBtnPosY = isFirstDisplayedComponent ? -13 : component.position.y + self.msgTextViewTopConstraint.constant - self.bubbleInfoContainerTopConstraint.constant - 13;
UIButton *editButton = [[UIButton alloc] initWithFrame:CGRectMake(editBtnPosX, editBtnPosY, 44, 44)];
[editButton setImage:editIcon forState:UIControlStateNormal];
[editButton setImage:editIcon forState:UIControlStateSelected];
editButton.tag = componentIndex;
[editButton addTarget:self action:@selector(onEditButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[editButton setTranslatesAutoresizingMaskIntoConstraints:NO];
editButton.accessibilityIdentifier = @"editButton";
[self.bubbleInfoContainer addSubview:editButton];
self.bubbleInfoContainer.userInteractionEnabled = YES;
// Define edit button constraints
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:editButton
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.bubbleInfoContainer
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:editBtnPosX];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:editButton
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.bubbleInfoContainer
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:editBtnPosY];
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:editButton
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:44];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:editButton
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:44];
// Available on iOS 8 and later
[NSLayoutConstraint activateConstraints:@[leftConstraint, topConstraint, widthConstraint, heightConstraint]];
// Store the created button
self.editButton = editButton;
}
- (IBAction)onProgressLongPressGesture:(UILongPressGestureRecognizer*)recognizer
{
if (recognizer.state == UIGestureRecognizerStateBegan && self.delegate)
{
[self.delegate cell:self didRecognizeAction:kMXKRoomBubbleCellLongPressOnProgressView userInfo:nil];
}
}
@end