mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-22 01:22:46 +02:00
b298dedc22
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
417 lines
13 KiB
Objective-C
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
|