/* Copyright 2018-2024 New Vector Ltd. Copyright 2017 Vector Creations Ltd Copyright 2015 OpenMarket Ltd SPDX-License-Identifier: AGPL-3.0-only Please see LICENSE in the repository root for full details. */ #import "MXKRoomMemberTableViewCell.h" @import MatrixSDK; #import "MXKAccount.h" #import "MXKImageView.h" #import "MXKPieChartView.h" #import "MXKRoomMemberCellDataStoring.h" #import "MXKRoomMemberListDataSource.h" #import "MXKTools.h" #import "NSBundle+MatrixKit.h" #import "MXKSwiftHeader.h" @interface MXKRoomMemberTableViewCell () { NSRange lastSeenRange; MXKPieChartView* pieChartView; } @end @implementation MXKRoomMemberTableViewCell - (void)awakeFromNib { [super awakeFromNib]; self.typingBadge.image = [NSBundle mxk_imageFromMXKAssetsBundleWithName:@"icon_keyboard"]; } - (void)customizeTableViewCellRendering { [super customizeTableViewCellRendering]; self.pictureView.defaultBackgroundColor = [UIColor clearColor]; } - (UIImage*)picturePlaceholder { return [NSBundle mxk_imageFromMXKAssetsBundleWithName:@"default-profile"]; } - (void)render:(MXKCellData *)cellData { // Sanity check: accept only object of MXKRoomMemberCellData classes or sub-classes NSParameterAssert([cellData isKindOfClass:[MXKRoomMemberCellData class]]); MXKRoomMemberCellData *memberCellData = (MXKRoomMemberCellData*)cellData; if (memberCellData) { mxSession = memberCellData.mxSession; memberId = memberCellData.roomMember.userId; self.userLabel.text = memberCellData.memberDisplayName; // Disable by default activity update mechanism (This is required in case of a reused cell). shouldUpdateActivityInfo = NO; // User thumbnail self.pictureView.mediaFolder = kMXMediaManagerAvatarThumbnailFolder; self.pictureView.enableInMemoryCache = YES; // Consider here the member avatar is stored unencrypted on Matrix media repo [self.pictureView setImageURI:memberCellData.roomMember.avatarUrl withType:nil andImageOrientation:UIImageOrientationUp toFitViewSize:self.pictureView.frame.size withMethod:MXThumbnailingMethodCrop previewImage:self.picturePlaceholder mediaManager:mxSession.mediaManager]; // Shade invited users if (memberCellData.roomMember.membership == MXMembershipInvite) { for (UIView *view in self.subviews) { view.alpha = 0.3; } } else { for (UIView *view in self.subviews) { view.alpha = 1; } } // Display the power level pie [self setPowerContainerValue:memberCellData.powerLevel]; // Prepare presence string and thumbnail border color NSString* presenceText = nil; UIColor* thumbnailBorderColor = nil; // Customize banned and left (kicked) members if (memberCellData.roomMember.membership == MXMembershipLeave || memberCellData.roomMember.membership == MXMembershipBan) { self.backgroundColor = [UIColor colorWithRed:0.8 green:0.8 blue:0.8 alpha:1.0]; presenceText = (memberCellData.roomMember.membership == MXMembershipLeave) ? [VectorL10n membershipLeave] : [VectorL10n membershipBan]; } else { self.backgroundColor = [UIColor whiteColor]; // get the user presence and his thumbnail border color if (memberCellData.roomMember.membership == MXMembershipInvite) { thumbnailBorderColor = [UIColor lightGrayColor]; presenceText = [VectorL10n membershipInvite]; } else { // Get the user that corresponds to this member MXUser *user = [mxSession userWithUserId:memberId]; // existing user ? if (user) { thumbnailBorderColor = [MXKAccount presenceColor:user.presence]; presenceText = [self lastActiveTime]; // Keep last seen range to update it lastSeenRange = NSMakeRange(self.userLabel.text.length + 2, presenceText.length); shouldUpdateActivityInfo = (presenceText.length != 0); } } } // if the thumbnail is defined if (thumbnailBorderColor) { self.pictureView.layer.borderWidth = 2; self.pictureView.layer.borderColor = thumbnailBorderColor.CGColor; } else { // remove the border // else it draws black border self.pictureView.layer.borderWidth = 0; } // and the presence text (if any) if (presenceText) { NSString* extraText = [NSString stringWithFormat:@"(%@)", presenceText]; self.userLabel.text = [NSString stringWithFormat:@"%@ %@", self.userLabel.text, extraText]; NSRange range = [self.userLabel.text rangeOfString:extraText]; UIFont* font = self.userLabel.font; // Create the attributes NSDictionary *attrs = [NSDictionary dictionaryWithObjectsAndKeys: font, NSFontAttributeName, self.userLabel.textColor, NSForegroundColorAttributeName, nil]; NSDictionary *subAttrs = [NSDictionary dictionaryWithObjectsAndKeys: font, NSFontAttributeName, [UIColor lightGrayColor], NSForegroundColorAttributeName, nil]; // Create the attributed string (text + attributes) NSMutableAttributedString *attributedText =[[NSMutableAttributedString alloc] initWithString:self.userLabel.text attributes:attrs]; [attributedText setAttributes:subAttrs range:range]; // Set it in our UILabel and we are done! [self.userLabel setAttributedText:attributedText]; } // Set typing badge visibility if (memberCellData.isTyping) { self.typingBadge.hidden = NO; [self.typingBadge.superview bringSubviewToFront:self.typingBadge]; } else { self.typingBadge.hidden = YES; } } } + (CGFloat)heightForCellData:(MXKCellData *)cellData withMaximumWidth:(CGFloat)maxWidth { // The height is fixed return 50; } - (NSString*)lastActiveTime { NSString* lastActiveTime = nil; // Get the user that corresponds to this member MXUser *user = [mxSession userWithUserId:memberId]; if (user) { // Prepare last active ago string lastActiveTime = [MXKTools formatSecondsIntervalFloored:(user.lastActiveAgo / 1000)]; // Check presence switch (user.presence) { case MXPresenceOffline: { lastActiveTime = [VectorL10n offline]; break; } case MXPresenceUnknown: { lastActiveTime = nil; break; } case MXPresenceOnline: case MXPresenceUnavailable: default: break; } } return lastActiveTime; } - (void)setPowerContainerValue:(CGFloat)progress { // no power level -> hide the pie if (0 == progress) { self.powerContainer.hidden = YES; return; } // display it self.powerContainer.hidden = NO; self.powerContainer.backgroundColor = [UIColor clearColor]; if (!pieChartView) { pieChartView = [[MXKPieChartView alloc] initWithFrame:self.powerContainer.bounds]; [self.powerContainer addSubview:pieChartView]; } pieChartView.progress = progress; } - (void)updateActivityInfo { // Check whether update is required. if (shouldUpdateActivityInfo) { NSString *lastSeen = [self lastActiveTime]; NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithAttributedString:self.userLabel.attributedText]; if (lastSeen.length) { [attributedText replaceCharactersInRange:lastSeenRange withString:lastSeen]; // Update last seen range lastSeenRange.length = lastSeen.length; } else { // remove presence info lastSeenRange.location -= 1; lastSeenRange.length += 2; [attributedText deleteCharactersInRange:lastSeenRange]; shouldUpdateActivityInfo = NO; } [self.userLabel setAttributedText:attributedText]; } } - (void)layoutSubviews { [super layoutSubviews]; // Round image view [_pictureView.layer setCornerRadius:_pictureView.frame.size.width / 2]; _pictureView.clipsToBounds = YES; } @end