Files
bundesmessenger-ios/Riot/Modules/Contacts/Views/ContactTableViewCell.m
T
JanNiklas Grabowski b298dedc22 chore: update from foss 1.11.19 (MESSENGER-6656)
Merge commit 'f823ab9aae70e8d15ed7cc079210dd9bbbb6c8e1' into feature/foss_update_1_11_19

* commit 'f823ab9aae70e8d15ed7cc079210dd9bbbb6c8e1':
  finish version++
  version++
  comments
  update submodule
  remove obsolete tests
  removed unused code
  update submodule
  fix
  Libolm removal
  update license macro
  update license
  Prepare for new sprint

# Conflicts:
#	Config/AppVersion.xcconfig
#	IDETemplateMacros.plist
#	LICENSE
#	README.md
#	Riot/Categories/MXSession+Riot.m
#	Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift
#	Riot/Managers/KeyValueStorage/Extensions/Keychain.swift
#	Riot/Managers/KeyValueStorage/KeyValueStore.swift
#	Riot/Managers/KeyValueStorage/KeychainStore.swift
#	Riot/Managers/KeyValueStorage/MemoryStore.swift
#	Riot/Managers/PushNotification/PushNotificationService.m
#	Riot/Managers/Settings/RiotSettings.swift
#	Riot/Managers/Settings/Shared/RiotSharedSettings.swift
#	Riot/Modules/Analytics/AnalyticsUIElement.swift
#	Riot/Modules/Application/AppCoordinator.swift
#	Riot/Modules/Application/LegacyAppDelegate.h
#	Riot/Modules/Application/LegacyAppDelegate.m
#	Riot/Modules/Authentication/Legacy/AuthenticationViewController.h
#	Riot/Modules/Authentication/Legacy/AuthenticationViewController.m
#	Riot/Modules/Authentication/Legacy/Views/AuthInputsView.h
#	Riot/Modules/Authentication/Legacy/Views/AuthInputsView.m
#	Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m
#	Riot/Modules/Common/Recents/RecentsViewController.m
#	Riot/Modules/Common/WebViewController/WebViewViewController.m
#	Riot/Modules/Contacts/Details/ContactDetailsViewController.m
#	Riot/Modules/Contacts/Views/ContactTableViewCell.m
#	Riot/Modules/Favorites/FavouritesViewController.h
#	Riot/Modules/Favorites/FavouritesViewController.m
#	Riot/Modules/GlobalSearch/UnifiedSearchViewController.m
#	Riot/Modules/People/PeopleViewController.h
#	Riot/Modules/People/PeopleViewController.m
#	Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift
#	Riot/Modules/Room/DataSources/RoomDataSource.m
#	Riot/Modules/Room/Files/RoomFilesViewController.m
#	Riot/Modules/Room/Members/Detail/RoomMemberDetailsViewController.m
#	Riot/Modules/Room/Members/RoomParticipantsViewController.m
#	Riot/Modules/Room/RoomViewController.m
#	Riot/Modules/Room/Settings/RoomSettingsViewController.m
#	Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCell.swift
#	Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroCellContentView.swift
#	Riot/Modules/Room/TimelineCells/RoomCreationIntro/RoomCreationIntroViewData.swift
#	Riot/Modules/Room/TimelineCells/RoomTimelineCellIdentifier.h
#	Riot/Modules/Rooms/RoomsViewController.h
#	Riot/Modules/Rooms/ShowDirectory/Cells/Network/DirectoryNetworkTableHeaderFooterView.swift
#	Riot/Modules/Rooms/ShowDirectory/Cells/Room/DirectoryRoomTableViewCell.swift
#	Riot/Modules/Rooms/ShowDirectory/PublicRoomsDirectoryViewModel.swift
#	Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift
#	Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift
#	Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift
#	Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift
#	Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinator.swift
#	Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewController.swift
#	Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewModel.swift
#	Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewModelType.swift
#	Riot/Modules/SetPinCode/PinCodePreferences.swift
#	Riot/Modules/SetPinCode/SetupBiometrics/BiometricsAuthenticationPresenter.swift
#	Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m
#	Riot/Modules/Settings/Security/SecurityViewController.m
#	Riot/Modules/Settings/SettingsViewController.m
#	Riot/Modules/SplitView/SplitViewCoordinator.swift
#	Riot/Modules/SplitView/SplitViewCoordinatorType.swift
#	Riot/Modules/StartChat/StartChatViewController.m
#	Riot/Modules/TabBar/MasterTabBarController.h
#	Riot/Modules/TabBar/MasterTabBarController.m
#	Riot/Utils/EventFormatter.m
#	Riot/Utils/HTMLFormatter.swift
#	Riot/Utils/Tools.m
#	RiotNSE/NotificationService.swift
2024-10-18 15:45:54 +02:00

417 lines
13 KiB
Objective-C

/*
Copyright 2024 New Vector Ltd.
Copyright 2017 Vector Creations Ltd
Copyright 2015 OpenMarket Ltd
Copyright (c) 2021 BWI GmbH
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;
// BWI: 5208 add federation icon
self.federationImage = FederationIconHelper.shared.federationBadgeImage;
self.federationImageView = [[UIImageView alloc] initWithImage:self.federationImage];
self.federationImageView.translatesAutoresizingMaskIntoConstraints = NO;
self.federationImageView.contentMode = UIViewContentModeScaleAspectFit;
[self.contentView addSubview:self.federationImageView];
// scale and new with federation icon
float scaleFactor = ((self.thumbnailView.frame.size.height * 0.34) / self.federationImage.size.height);
float imageWidth = scaleFactor * self.federationImage.size.width;
// offset federation icon
float offsetTrailing = 3 * scaleFactor;
float offsetBotom = 1;
[self.federationImageView.bottomAnchor constraintEqualToAnchor:self.thumbnailView.bottomAnchor constant:offsetBotom].active = YES;
[self.federationImageView.trailingAnchor constraintEqualToAnchor:self.thumbnailView.trailingAnchor constant:offsetTrailing].active = YES;
[self.federationImageView.heightAnchor constraintEqualToConstant:(self.thumbnailView.frame.size.height * 0.34)].active = YES;
[self.federationImageView.widthAnchor constraintEqualToConstant:imageWidth].active = YES;
}
- (void)customizeTableViewCellRendering
{
[super customizeTableViewCellRendering];
// apply the vector colours
self.contactDisplayNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
self.contactInformationLabel.textColor = ThemeService.shared.theme.textSecondaryColor;
self.contactInformationLabel.hidden = BWIBuildSettings.shared.allowLocalContactPresence ? NO : YES;
self.powerLevelLabel.textColor = ThemeService.shared.theme.textSecondaryColor;
// Clear the default background color of a MXKImageView instance
self.thumbnailView.defaultBackgroundColor = [UIColor clearColor];
// bwi: 5695 remove avatar borders
self.thumbnailView.contentMode = UIViewContentModeScaleAspectFill;
}
- (void)layoutSubviews
{
[super layoutSubviews];
// Round image view
self.thumbnailView.layer.cornerRadius = self.thumbnailView.frame.size.width / 2;
self.thumbnailView.clipsToBounds = YES;
// BWI: 5208 add federation icon
self.federationImage = FederationIconHelper.shared.federationBadgeImage;
self.federationImageView.image = self.federationImage;
}
- (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;
}
// BWI: 5208 add federation icon
if (BWIBuildSettings.shared.isFederationEnabled)
{
self.federationImageView.hidden = !contact.bwiIsFederated;
}
else
{
self.federationImageView.hidden = YES;
}
// 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];
[self refreshLocalContactInformation];
}
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];
}
}
// bwi: 5695 remove avatar borders
self.thumbnailView.contentMode = UIViewContentModeScaleAspectFill;
self.thumbnailView.image = image;
}
- (void)refreshContactBadgeImage
{
NSString *matrixId = [self firstMatrixId];
// bwi: #5236 remove encryption status shield
if (BWIBuildSettings.shared.showEncryptionStatusBadgeOnAvatar)
{
if (matrixId)
{
[self.mxRoom encryptionTrustLevelForUserId:matrixId onComplete:^(UserEncryptionTrustLevel userEncryptionTrustLevel) {
self.avatarBadgeImageView.image = [EncryptionTrustLevelBadgeImageHelper userBadgeImageFor:userEncryptionTrustLevel];
}];
}
else
{
self.avatarBadgeImageView.image = [EncryptionTrustLevelBadgeImageHelper userBadgeImageFor:UserEncryptionTrustLevelUnknown];
}
self.avatarBadgeImageView.hidden = NO;
}
else
{
self.avatarBadgeImageView.image = nil;
self.avatarBadgeImageView.hidden = YES;
}
}
- (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(![BWIBuildSettings.shared showContactIdentifierInDetailRow]) {
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;
}
}
} else {
NSArray *identifiers = contact.matrixIdentifiers;
if (identifiers.count)
{
NSString *userId = identifiers.firstObject;
subLabelText = userId;
}
}
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
{
if (BWIBuildSettings.shared.allowLocalContactPresence) {
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];
}
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