element-ios/Riot/Modules/Contacts/Views/ContactTableViewCell.m

366 lines
10 KiB
Objective-C

/*
Copyright 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 "ContactTableViewCell.h"
#import "ThemeService.h"
#import "GeneratedInterface-Swift.h"
#import "AvatarGenerator.h"
#import "Tools.h"
#import "MXRoom+Riot.h"
#import "NBPhoneNumberUtil.h"
@interface ContactTableViewCell()
{
/**
The observer of the presence for matrix user.
*/
id mxPresenceObserver;
}
@end
@implementation ContactTableViewCell
@synthesize mxRoom, delegate;
- (void)awakeFromNib
{
[super awakeFromNib];
// Disable by default interactions defined in the cell
// because we want [tableView didSelectRowAtIndexPath:] to be called
self.thumbnailView.userInteractionEnabled = NO;
}
- (void)customizeTableViewCellRendering
{
[super customizeTableViewCellRendering];
// apply the vector colours
self.contactDisplayNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
self.contactInformationLabel.textColor = ThemeService.shared.theme.textSecondaryColor;
self.powerLevelLabel.textColor = ThemeService.shared.theme.textSecondaryColor;
// Clear the default background color of a MXKImageView instance
self.thumbnailView.defaultBackgroundColor = [UIColor clearColor];
}
- (void)layoutSubviews
{
[super layoutSubviews];
// Round image view
self.thumbnailView.layer.cornerRadius = self.thumbnailView.frame.size.width / 2;
self.thumbnailView.clipsToBounds = YES;
}
- (void)prepareForReuse
{
[super prepareForReuse];
// Restore default values
self.contentView.alpha = 1;
self.userInteractionEnabled = YES;
self.accessoryType = UITableViewCellAccessoryNone;
self.accessoryView = nil;
}
- (void)setShowCustomAccessoryView:(BOOL)show
{
_showCustomAccessoryView = show;
self.customAccessViewWidthConstraint.constant = show ? 25 : 0;
}
- (void)setShowMatrixIdInDisplayName:(BOOL)showMatrixIdInDisplayName
{
_showMatrixIdInDisplayName = showMatrixIdInDisplayName;
if (contact)
{
[self refreshContactDisplayName];
}
}
#pragma mark - MXKCellRendering
// returns the first matrix id of the contact
// nil if there is none
- (NSString*)firstMatrixId
{
NSString* matrixId = nil;
if (contact.matrixIdentifiers.count > 0)
{
matrixId = contact.matrixIdentifiers.firstObject;
}
return matrixId;
}
- (void)render:(MXKCellData *)cellData
{
// Remove any pending observers
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (mxPresenceObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:mxPresenceObserver];
mxPresenceObserver = nil;
}
// Sanity check: accept only object of MXKContact classes or sub-classes
NSParameterAssert([cellData isKindOfClass:[MXKContact class]]);
contact = (MXKContact*)cellData;
// sanity check
// should never happen
if (!contact)
{
self.thumbnailView.image = nil;
self.contactDisplayNameLabel.text = nil;
self.contactInformationLabel.text = nil;
self.powerLevelLabel.text = nil;
return;
}
// Be warned when the thumbnail is updated
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onThumbnailUpdate:) name:kMXKContactThumbnailUpdateNotification object:nil];
[self refreshContactThumbnail];
[self refreshContactDisplayName];
if (contact.isMatrixContact)
{
// Observe contact presence change
MXWeakify(self);
mxPresenceObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKContactManagerMatrixUserPresenceChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
NSString* matrixId = self.firstMatrixId;
if (matrixId && [matrixId isEqualToString:notif.object])
{
[self refreshContactPresence];
}
}];
[self refreshContactPresence];
[self refreshContactBadgeImage];
}
else
{
[self refreshLocalContactInformation];
}
}
+ (CGFloat)heightForCellData:(MXKCellData*)cellData withMaximumWidth:(CGFloat)maxWidth
{
return 74;
}
- (void)didEndDisplay
{
// remove any pending observers
[[NSNotificationCenter defaultCenter] removeObserver:self];
if (mxPresenceObserver) {
[[NSNotificationCenter defaultCenter] removeObserver:mxPresenceObserver];
mxPresenceObserver = nil;
}
// Remove all gesture recognizer
while (self.thumbnailView.gestureRecognizers.count)
{
[self.thumbnailView removeGestureRecognizer:self.thumbnailView.gestureRecognizers[0]];
}
self.delegate = nil;
contact = nil;
}
#pragma mark Refresh cell part
- (void)refreshContactThumbnail
{
UIImage* image = [contact thumbnailWithPreferedSize:self.thumbnailView.frame.size];
if (!image)
{
NSArray *identifiers = contact.matrixIdentifiers;
if (identifiers.count)
{
image = [AvatarGenerator generateAvatarForMatrixItem:identifiers.firstObject withDisplayName:contact.displayName];
}
else if (contact.isThirdPartyInvite)
{
image = [AvatarGenerator generateAvatarForText:contact.displayName];
}
else if ((!contact.isMatrixContact && contact.phoneNumbers.count && !contact.emailAddresses.count))
{
image = [AvatarGenerator imageFromText:@"#" withBackgroundColor:ThemeService.shared.theme.tintColor];
}
else
{
image = [AvatarGenerator imageFromText:@"@" withBackgroundColor:ThemeService.shared.theme.tintColor];
}
}
self.thumbnailView.image = image;
}
- (void)refreshContactBadgeImage
{
NSString *matrixId = [self firstMatrixId];
if (matrixId)
{
[self.mxRoom encryptionTrustLevelForUserId:matrixId onComplete:^(UserEncryptionTrustLevel userEncryptionTrustLevel) {
self.avatarBadgeImageView.image = [EncryptionTrustLevelBadgeImageHelper userBadgeImageFor:userEncryptionTrustLevel];
}];
}
else
{
self.avatarBadgeImageView.image = [EncryptionTrustLevelBadgeImageHelper userBadgeImageFor:UserEncryptionTrustLevelUnknown];
}
}
- (void)refreshContactDisplayName
{
self.contactDisplayNameLabel.text = contact.displayName;
// Check whether the matrix identifier must be displayed.
if (_showMatrixIdInDisplayName)
{
// Append the matrix identifier to the display name.
NSArray *identifiers = contact.matrixIdentifiers;
if (identifiers.count)
{
NSString *userId = identifiers.firstObject;
// Check whether the display name is not already the matrix id
if (![contact.displayName isEqualToString:userId])
{
// Update the display name by adding the matrix id
NSMutableAttributedString *displayNameLabelText = [[NSMutableAttributedString alloc] initWithString:contact.displayName];
NSRange strRange = NSMakeRange(0, displayNameLabelText.length);
[displayNameLabelText addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:17 weight:UIFontWeightMedium] range:strRange];
NSMutableAttributedString *userIdStr = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@" (%@)", userId]];
strRange = NSMakeRange(0, userIdStr.length);
[userIdStr addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:17] range:strRange];
[displayNameLabelText appendAttributedString:userIdStr];
self.contactDisplayNameLabel.attributedText = displayNameLabelText;
}
}
}
}
- (void)refreshLocalContactInformation
{
// Display the first contact method in sub label.
NSString *subLabelText = nil;
if (contact.emailAddresses.count)
{
MXKEmail* email = contact.emailAddresses.firstObject;
subLabelText = email.emailAddress;
}
else if (contact.phoneNumbers.count)
{
MXKPhoneNumber *phoneNumber = contact.phoneNumbers.firstObject;
if (phoneNumber.nbPhoneNumber)
{
subLabelText = [[NBPhoneNumberUtil sharedInstance] format:phoneNumber.nbPhoneNumber numberFormat:NBEPhoneNumberFormatINTERNATIONAL error:nil];
}
else
{
subLabelText = phoneNumber.textNumber;
}
}
if (subLabelText.length)
{
self.contactInformationLabel.hidden = NO;
}
else
{
// Hide and fill the label with a fake string to harmonize the height of all the cells.
// This is a drawback of the self-sizing cell.
self.contactInformationLabel.hidden = YES;
subLabelText = @"No method";
}
self.contactInformationLabel.text = subLabelText;
}
- (void)refreshContactPresence
{
NSString* presenceText;
NSString* matrixId = self.firstMatrixId;
if (matrixId)
{
MXUser *user = nil;
// Consider here all sessions reported into contact manager
NSArray* mxSessions = [MXKContactManager sharedManager].mxSessions;
for (MXSession *mxSession in mxSessions)
{
user = [mxSession userWithUserId:matrixId];
if (user)
{
break;
}
}
presenceText = [Tools presenceText:user];
}
else if (contact.isThirdPartyInvite)
{
presenceText = [VectorL10n roomParticipantsOffline];
}
if (presenceText.length)
{
self.contactInformationLabel.hidden = NO;
}
else
{
// Hide and fill the label with a fake string to harmonize the height of all the cells.
// This is a drawback of the self-sizing cell.
self.contactInformationLabel.hidden = YES;
presenceText = @"No presence";
}
self.contactInformationLabel.text = presenceText;
}
#pragma mark - events
- (void)onThumbnailUpdate:(NSNotification *)notif
{
// sanity check
if ([notif.object isKindOfClass:[NSString class]])
{
NSString* contactID = notif.object;
if ([contactID isEqualToString:contact.contactID])
{
[self refreshContactThumbnail];
}
}
}
@end