element-ios/Riot/Modules/Settings/SettingsViewController.m

4606 lines
192 KiB
Objective-C

/*
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 "SettingsViewController.h"
#import "AvatarGenerator.h"
#import "BugReportViewController.h"
#import "WebViewViewController.h"
#import "CountryPickerViewController.h"
#import "LanguagePickerViewController.h"
#import "DeactivateAccountViewController.h"
#import "SecurityViewController.h"
#import "NBPhoneNumberUtil.h"
#import "RageShakeManager.h"
#import "ThemeService.h"
#import "TableViewCellWithPhoneNumberTextField.h"
#import "GBDeviceInfo_iOS.h"
#import "MediaPickerViewController.h"
#import "GeneratedInterface-Swift.h"
@import DesignKit;
NSString* const kSettingsViewControllerPhoneBookCountryCellId = @"kSettingsViewControllerPhoneBookCountryCellId";
typedef NS_ENUM(NSUInteger, SECTION_TAG)
{
SECTION_TAG_SIGN_OUT = 0,
SECTION_TAG_USER_SETTINGS,
SECTION_TAG_ACCOUNT,
SECTION_TAG_SENDING_MEDIA,
SECTION_TAG_LINKS,
SECTION_TAG_SECURITY,
SECTION_TAG_NOTIFICATIONS,
SECTION_TAG_CALLS,
SECTION_TAG_DISCOVERY,
SECTION_TAG_IDENTITY_SERVER,
SECTION_TAG_LOCAL_CONTACTS,
SECTION_TAG_IGNORED_USERS,
SECTION_TAG_INTEGRATIONS,
SECTION_TAG_USER_INTERFACE,
SECTION_TAG_TIMELINE,
SECTION_TAG_PRESENCE,
SECTION_TAG_ADVANCED,
SECTION_TAG_ABOUT,
SECTION_TAG_LABS,
SECTION_TAG_DEACTIVATE_ACCOUNT
};
typedef NS_ENUM(NSUInteger, USER_SETTINGS_INDEX)
{
USER_SETTINGS_PROFILE_PICTURE_INDEX = 0,
USER_SETTINGS_DISPLAYNAME_INDEX,
USER_SETTINGS_CHANGE_PASSWORD_INDEX,
USER_SETTINGS_FIRST_NAME_INDEX,
USER_SETTINGS_SURNAME_INDEX,
USER_SETTINGS_ADD_EMAIL_INDEX,
USER_SETTINGS_ADD_PHONENUMBER_INDEX
};
typedef NS_ENUM(NSUInteger, USER_SETTINGS_OFFSET)
{
USER_SETTINGS_EMAILS_OFFSET = 2000,
USER_SETTINGS_PHONENUMBERS_OFFSET = 1000
};
typedef NS_ENUM(NSUInteger, SENDING_MEDIA)
{
SENDING_MEDIA_CONFIRM_SIZE = 0
};
typedef NS_ENUM(NSUInteger, LINKS_SHOW_URL_PREVIEWS)
{
LINKS_SHOW_URL_PREVIEWS_INDEX = 0,
LINKS_SHOW_URL_PREVIEWS_DESCRIPTION_INDEX
};
typedef NS_ENUM(NSUInteger, NOTIFICATION_SETTINGS)
{
NOTIFICATION_SETTINGS_ENABLE_PUSH_INDEX = 0,
NOTIFICATION_SETTINGS_SYSTEM_SETTINGS,
NOTIFICATION_SETTINGS_SHOW_IN_APP_INDEX,
NOTIFICATION_SETTINGS_SHOW_DECODED_CONTENT,
NOTIFICATION_SETTINGS_PIN_MISSED_NOTIFICATIONS_INDEX,
NOTIFICATION_SETTINGS_PIN_UNREAD_INDEX,
NOTIFICATION_SETTINGS_DEFAULT_SETTINGS_INDEX,
NOTIFICATION_SETTINGS_MENTION_AND_KEYWORDS_SETTINGS_INDEX,
NOTIFICATION_SETTINGS_OTHER_SETTINGS_INDEX,
};
typedef NS_ENUM(NSUInteger, CALLS_ENABLE_STUN_SERVER)
{
CALLS_ENABLE_STUN_SERVER_FALLBACK_INDEX = 0
};
typedef NS_ENUM(NSUInteger, INTEGRATIONS)
{
INTEGRATIONS_INDEX
};
typedef NS_ENUM(NSUInteger, LOCAL_CONTACTS)
{
LOCAL_CONTACTS_SYNC_INDEX,
LOCAL_CONTACTS_PHONEBOOK_COUNTRY_INDEX
};
typedef NS_ENUM(NSUInteger, USER_INTERFACE)
{
USER_INTERFACE_LANGUAGE_INDEX = 0,
USER_INTERFACE_THEME_INDEX
};
typedef NS_ENUM(NSUInteger, TIMELINE)
{
TIMELINE_STYLE_INDEX,
TIMELINE_SHOW_REDACTIONS_IN_ROOM_HISTORY_INDEX,
TIMELINE_USE_ONLY_LATEST_USER_AVATAR_AND_NAME_INDEX
};
typedef NS_ENUM(NSUInteger, IDENTITY_SERVER)
{
IDENTITY_SERVER_INDEX
};
typedef NS_ENUM(NSUInteger, PRESENCE)
{
PRESENCE_OFFLINE_MODE = 0,
};
typedef NS_ENUM(NSUInteger, ADVANCED)
{
ADVANCED_CRASH_REPORT_INDEX = 0,
ADVANCED_ENABLE_RAGESHAKE_INDEX,
ADVANCED_MARK_ALL_AS_READ_INDEX,
ADVANCED_CLEAR_CACHE_INDEX,
ADVANCED_REPORT_BUG_INDEX,
};
typedef NS_ENUM(NSUInteger, ABOUT)
{
ABOUT_COPYRIGHT_INDEX = 0,
ABOUT_ACCEPTABLE_USE_INDEX,
ABOUT_PRIVACY_INDEX,
ABOUT_THIRD_PARTY_INDEX,
};
typedef NS_ENUM(NSUInteger, LABS_ENABLE)
{
LABS_ENABLE_RINGING_FOR_GROUP_CALLS_INDEX = 0,
LABS_ENABLE_THREADS_INDEX,
LABS_ENABLE_AUTO_REPORT_DECRYPTION_ERRORS,
LABS_ENABLE_LIVE_LOCATION_SHARING,
LABS_ENABLE_NEW_SESSION_MANAGER,
LABS_ENABLE_NEW_CLIENT_INFO_FEATURE,
LABS_ENABLE_WYSIWYG_COMPOSER,
LABS_ENABLE_VOICE_BROADCAST
};
typedef NS_ENUM(NSUInteger, SECURITY)
{
SECURITY_BUTTON_INDEX = 0,
DEVICE_MANAGER_INDEX
};
typedef NS_ENUM(NSUInteger, ACCOUNT)
{
ACCOUNT_MANAGE_INDEX = 0,
};
typedef void (^blockSettingsViewController_onReadyToDestroy)(void);
#pragma mark - SettingsViewController
@interface SettingsViewController () <UITextFieldDelegate, MXKCountryPickerViewControllerDelegate, MXKLanguagePickerViewControllerDelegate, DeactivateAccountViewControllerDelegate,
NotificationSettingsCoordinatorBridgePresenterDelegate,
SignOutFlowPresenterDelegate,
SingleImagePickerPresenterDelegate,
SettingsDiscoveryTableViewSectionDelegate, SettingsDiscoveryViewModelCoordinatorDelegate,
SettingsIdentityServerCoordinatorBridgePresenterDelegate,
ServiceTermsModalCoordinatorBridgePresenterDelegate,
TableViewSectionsDelegate,
ThreadsBetaCoordinatorBridgePresenterDelegate,
ChangePasswordCoordinatorBridgePresenterDelegate,
SSOAuthenticationPresenterDelegate>
{
// Current alert (if any).
__weak UIAlertController *currentAlert;
// listener
__weak id removedAccountObserver;
__weak id accountUserInfoObserver;
__weak id pushInfoUpdateObserver;
__weak id notificationCenterWillUpdateObserver;
__weak id notificationCenterDidUpdateObserver;
__weak id notificationCenterDidFailObserver;
// profile updates
// avatar
UIImage* newAvatarImage;
// the avatar image has been uploaded
NSString* uploadedAvatarURL;
// new display name
NSString* newDisplayName;
// New email address to bind
UITextField* newEmailTextField;
// New phone number to bind
TableViewCellWithPhoneNumberTextField * newPhoneNumberCell;
CountryPickerViewController *newPhoneNumberCountryPicker;
NBPhoneNumber *newPhoneNumber;
// Observe kAppDelegateDidTapStatusBarNotification to handle tap on clock status bar.
__weak id kAppDelegateDidTapStatusBarNotificationObserver;
// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
__weak id kThemeServiceDidChangeThemeNotificationObserver;
// Postpone destroy operation when saving or email binding is in progress
BOOL isSavingInProgress;
BOOL is3PIDBindingInProgress;
blockSettingsViewController_onReadyToDestroy onReadyToDestroyHandler;
BOOL keepNewEmailEditing;
BOOL keepNewPhoneNumberEditing;
// The current pushed view controller
UIViewController *pushedViewController;
SettingsIdentityServerCoordinatorBridgePresenter *identityServerSettingsCoordinatorBridgePresenter;
}
/**
Flag indicating whether the user is typing an email to bind.
*/
@property (nonatomic) BOOL newEmailEditingEnabled;
/**
Flag indicating whether the user is typing a phone number to bind.
*/
@property (nonatomic) BOOL newPhoneEditingEnabled;
/**
The current `UNUserNotificationCenter` notification settings for the app.
*/
@property (nonatomic) UNNotificationSettings *systemNotificationSettings;
@property (nonatomic, weak) DeactivateAccountViewController *deactivateAccountViewController;
@property (nonatomic, strong) NotificationSettingsCoordinatorBridgePresenter *notificationSettingsBridgePresenter;
@property (nonatomic, strong) SignOutFlowPresenter *signOutFlowPresenter;
@property (nonatomic, weak) UIButton *signOutButton;
@property (nonatomic, strong) SingleImagePickerPresenter *imagePickerPresenter;
@property (nonatomic, strong) SettingsDiscoveryViewModel *settingsDiscoveryViewModel;
@property (nonatomic, strong) SettingsDiscoveryTableViewSection *settingsDiscoveryTableViewSection;
@property (nonatomic, strong) SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter *discoveryThreePidDetailsPresenter;
@property (nonatomic, strong) TableViewSections *tableViewSections;
@property (nonatomic, strong) ReauthenticationCoordinatorBridgePresenter *reauthenticationCoordinatorBridgePresenter;
@property (nonatomic, strong) UserInteractiveAuthenticationService *userInteractiveAuthenticationService;
@property (nonatomic, strong) ThreadsBetaCoordinatorBridgePresenter *threadsBetaBridgePresenter;
@property (nonatomic, strong) ChangePasswordCoordinatorBridgePresenter *changePasswordBridgePresenter;
@property (nonatomic, strong) UserSessionsFlowCoordinatorBridgePresenter *userSessionsFlowCoordinatorBridgePresenter;
/**
Whether or not to check for contacts access after the user accepts the service terms. The value of this property is
set automatically when calling `prepareIdentityServiceAndPresentTermsWithSession:checkingAccessForContactsOnAccept`
*/
@property (nonatomic) BOOL serviceTermsModalShouldCheckAccessForContactsOnAccept;
@property (nonatomic) BOOL isPreparingIdentityService;
@property (nonatomic, strong) ServiceTermsModalCoordinatorBridgePresenter *serviceTermsModalCoordinatorBridgePresenter;
@property (nonatomic, strong) SSOAuthenticationPresenter *ssoAuthenticationPresenter;
@property (nonatomic) AnalyticsScreenTracker *screenTracker;
@end
@implementation SettingsViewController
- (UserInteractiveAuthenticationService*)userInteractiveAuthenticationService
{
if (!_userInteractiveAuthenticationService)
{
_userInteractiveAuthenticationService = [self createUserInteractiveAuthenticationService];
}
return _userInteractiveAuthenticationService;
}
+ (instancetype)instantiate
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
SettingsViewController *settingsViewController = [storyboard instantiateViewControllerWithIdentifier:@"SettingsViewController"];
return settingsViewController;
}
- (void)finalizeInit
{
[super finalizeInit];
// Setup `MXKViewControllerHandling` properties
self.enableBarTintColorStatusChange = NO;
self.rageShakeManager = [RageShakeManager sharedManager];
isSavingInProgress = NO;
is3PIDBindingInProgress = NO;
self.screenTracker = [[AnalyticsScreenTracker alloc] initWithScreen:AnalyticsScreenSettings];
}
- (void)dealloc {
// Fix for destroy not being called
[self destroy];
}
- (void)updateSections
{
NSMutableArray<Section*> *tmpSections = [NSMutableArray arrayWithCapacity:SECTION_TAG_DEACTIVATE_ACCOUNT + 1];
Section *sectionSignOut = [Section sectionWithTag:SECTION_TAG_SIGN_OUT];
[sectionSignOut addRowWithTag:0];
[tmpSections addObject:sectionSignOut];
Section *sectionUserSettings = [Section sectionWithTag:SECTION_TAG_USER_SETTINGS];
[sectionUserSettings addRowWithTag:USER_SETTINGS_PROFILE_PICTURE_INDEX];
[sectionUserSettings addRowWithTag:USER_SETTINGS_DISPLAYNAME_INDEX];
if (RiotSettings.shared.settingsScreenShowChangePassword)
{
[sectionUserSettings addRowWithTag:USER_SETTINGS_CHANGE_PASSWORD_INDEX];
}
if (BuildSettings.settingsScreenShowUserFirstName)
{
[sectionUserSettings addRowWithTag:USER_SETTINGS_FIRST_NAME_INDEX];
}
if (BuildSettings.settingsScreenShowUserSurname)
{
[sectionUserSettings addRowWithTag:USER_SETTINGS_SURNAME_INDEX];
}
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
// add linked emails
for (NSInteger index = 0; index < account.linkedEmails.count; index++)
{
[sectionUserSettings addRowWithTag: USER_SETTINGS_EMAILS_OFFSET + index];
}
// add linked phone numbers
for (NSInteger index = 0; index < account.linkedPhoneNumbers.count; index++)
{
[sectionUserSettings addRowWithTag: USER_SETTINGS_PHONENUMBERS_OFFSET + index];
}
// If the threePidChanges is nil we assume the capability to be true
if (!self.mainSession.homeserverCapabilities.threePidChanges ||
self.mainSession.homeserverCapabilities.threePidChanges.enabled) {
if (BuildSettings.settingsScreenAllowAddingEmailThreepids)
{
[sectionUserSettings addRowWithTag:USER_SETTINGS_ADD_EMAIL_INDEX];
}
if (BuildSettings.settingsScreenAllowAddingPhoneThreepids)
{
[sectionUserSettings addRowWithTag:USER_SETTINGS_ADD_PHONENUMBER_INDEX];
}
if (BuildSettings.settingsScreenShowThreepidExplanatory)
{
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:[VectorL10n settingsThreePidsManagementInformationPart1] attributes:@{}];
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:[VectorL10n settingsThreePidsManagementInformationPart2] attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.tintColor}]];
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:[VectorL10n settingsThreePidsManagementInformationPart3] attributes:@{}]];
sectionUserSettings.attributedFooterTitle = attributedString;
}
}
sectionUserSettings.headerTitle = [VectorL10n settingsUserSettings];
[tmpSections addObject:sectionUserSettings];
NSString *manageAccountURL = self.mainSession.homeserverWellknown.authentication.account;
if (manageAccountURL)
{
Section *account = [Section sectionWithTag: SECTION_TAG_ACCOUNT];
[account addRowWithTag:ACCOUNT_MANAGE_INDEX];
account.headerTitle = [VectorL10n settingsManageAccountTitle];
account.footerTitle = [VectorL10n settingsManageAccountDescription:manageAccountURL];
[tmpSections addObject:account];
}
if (BuildSettings.settingsScreenShowConfirmMediaSize)
{
Section *sectionMedia = [Section sectionWithTag:SECTION_TAG_SENDING_MEDIA];
[sectionMedia addRowWithTag:SENDING_MEDIA_CONFIRM_SIZE];
sectionMedia.headerTitle = [VectorL10n settingsSendingMedia];
sectionMedia.footerTitle = VectorL10n.settingsConfirmMediaSizeDescription;
[tmpSections addObject:sectionMedia];
}
Section *sectionLinks = [Section sectionWithTag:SECTION_TAG_LINKS];
[sectionLinks addRowWithTag:LINKS_SHOW_URL_PREVIEWS_INDEX];
sectionLinks.headerTitle = [VectorL10n settingsLinks];
sectionLinks.footerTitle = VectorL10n.settingsShowUrlPreviewsDescription;
[tmpSections addObject:sectionLinks];
Section *sectionSecurity = [Section sectionWithTag:SECTION_TAG_SECURITY];
[sectionSecurity addRowWithTag:SECURITY_BUTTON_INDEX];
if (RiotSettings.shared.enableNewSessionManager)
{
// NOTE: Add device manager entry point in the security section atm for debug purpose
[sectionSecurity addRowWithTag:DEVICE_MANAGER_INDEX];
}
sectionSecurity.headerTitle = [VectorL10n settingsSecurity];
[tmpSections addObject:sectionSecurity];
Section *sectionNotificationSettings = [Section sectionWithTag:SECTION_TAG_NOTIFICATIONS];
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_ENABLE_PUSH_INDEX];
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_SYSTEM_SETTINGS];
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_SHOW_IN_APP_INDEX];
if (RiotSettings.shared.settingsScreenShowNotificationDecodedContentOption)
{
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_SHOW_DECODED_CONTENT];
}
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_PIN_MISSED_NOTIFICATIONS_INDEX];
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_PIN_UNREAD_INDEX];
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_DEFAULT_SETTINGS_INDEX];
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_MENTION_AND_KEYWORDS_SETTINGS_INDEX];
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_OTHER_SETTINGS_INDEX];
sectionNotificationSettings.headerTitle = [VectorL10n settingsNotifications];
[tmpSections addObject:sectionNotificationSettings];
if (BuildSettings.allowVoIPUsage && BuildSettings.stunServerFallbackUrlString && RiotSettings.shared.settingsScreenShowEnableStunServerFallback)
{
Section *sectionCalls = [Section sectionWithTag:SECTION_TAG_CALLS];
sectionCalls.headerTitle = [VectorL10n settingsCallsSettings];
// Remove "stun:"
NSString* stunFallbackHost = [BuildSettings.stunServerFallbackUrlString componentsSeparatedByString:@":"].lastObject;
sectionCalls.footerTitle = [VectorL10n settingsCallsStunServerFallbackDescription:stunFallbackHost];
[sectionCalls addRowWithTag:CALLS_ENABLE_STUN_SERVER_FALLBACK_INDEX];
[tmpSections addObject:sectionCalls];
}
if (BuildSettings.settingsScreenShowDiscoverySettings)
{
Section *sectionDiscovery = [Section sectionWithTag:SECTION_TAG_DISCOVERY];
NSInteger count = self.settingsDiscoveryTableViewSection.numberOfRows;
for (NSInteger index = 0; index < count; index++)
{
[sectionDiscovery addRowWithTag:index];
}
sectionDiscovery.headerTitle = [VectorL10n settingsDiscoverySettings];
sectionDiscovery.attributedFooterTitle = self.settingsDiscoveryTableViewSection.attributedFooterTitle;
[tmpSections addObject:sectionDiscovery];
}
if (BuildSettings.settingsScreenAllowIdentityServerConfig)
{
Section *sectionIdentityServer = [Section sectionWithTag:SECTION_TAG_IDENTITY_SERVER];
[sectionIdentityServer addRowWithTag:IDENTITY_SERVER_INDEX];
sectionIdentityServer.headerTitle = [VectorL10n settingsIdentityServerSettings];
sectionIdentityServer.footerTitle = account.mxSession.identityService.identityServer ? VectorL10n.settingsIdentityServerDescription : VectorL10n.settingsIdentityServerNoIsDescription;
[tmpSections addObject:sectionIdentityServer];
}
if (BuildSettings.allowLocalContactsAccess)
{
Section *sectionLocalContacts = [Section sectionWithTag:SECTION_TAG_LOCAL_CONTACTS];
[sectionLocalContacts addRowWithTag:LOCAL_CONTACTS_SYNC_INDEX];
if (MXKAppSettings.standardAppSettings.syncLocalContacts)
{
[sectionLocalContacts addRowWithTag:LOCAL_CONTACTS_PHONEBOOK_COUNTRY_INDEX];
}
NSString *headerTitle = UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPhone ? VectorL10n.settingsPhoneContacts : VectorL10n.settingsContacts;
sectionLocalContacts.headerTitle = headerTitle;
sectionLocalContacts.footerTitle = VectorL10n.settingsContactsEnableSyncDescription;
[tmpSections addObject:sectionLocalContacts];
}
MXSession *session = [AppDelegate theDelegate].mxSessions.firstObject;
if (session.ignoredUsers.count)
{
Section *sectionIgnoredUsers = [Section sectionWithTag:SECTION_TAG_IGNORED_USERS];
for (NSInteger index = 0; index < session.ignoredUsers.count; index++)
{
[sectionIgnoredUsers addRowWithTag:index];
}
sectionIgnoredUsers.headerTitle = [VectorL10n settingsIgnoredUsers];
[tmpSections addObject:sectionIgnoredUsers];
}
if (RiotSettings.shared.matrixApps)
{
Section *sectionIntegrations = [Section sectionWithTag:SECTION_TAG_INTEGRATIONS];
[sectionIntegrations addRowWithTag:INTEGRATIONS_INDEX];
sectionIntegrations.headerTitle = [VectorL10n settingsIntegrations];
NSString *integrationManager = [WidgetManager.sharedManager configForUser:session.myUser.userId].apiUrl;
NSString *integrationManagerDomain = [NSURL URLWithString:integrationManager].host;
sectionIntegrations.footerTitle = [VectorL10n settingsIntegrationsAllowDescription:integrationManagerDomain];
[tmpSections addObject:sectionIntegrations];
}
Section *sectionUserInterface = [Section sectionWithTag:SECTION_TAG_USER_INTERFACE];
sectionUserInterface.headerTitle = [VectorL10n settingsUserInterface];
[sectionUserInterface addRowWithTag:USER_INTERFACE_LANGUAGE_INDEX];
[sectionUserInterface addRowWithTag:USER_INTERFACE_THEME_INDEX];
[tmpSections addObject:sectionUserInterface];
Section *sectionTimeline = [Section sectionWithTag:SECTION_TAG_TIMELINE];
sectionTimeline.headerTitle = VectorL10n.settingsTimeline;
if (BuildSettings.roomScreenAllowTimelineStyleConfiguration)
{
[sectionTimeline addRowWithTag:TIMELINE_STYLE_INDEX];
}
[sectionTimeline addRowWithTag:TIMELINE_SHOW_REDACTIONS_IN_ROOM_HISTORY_INDEX];
[sectionTimeline addRowWithTag:TIMELINE_USE_ONLY_LATEST_USER_AVATAR_AND_NAME_INDEX];
[tmpSections addObject:sectionTimeline];
if(BuildSettings.settingsScreenPresenceAllowConfiguration)
{
Section *sectionPresence = [Section sectionWithTag:SECTION_TAG_PRESENCE];
[sectionPresence addRowWithTag:PRESENCE_OFFLINE_MODE];
sectionPresence.headerTitle = VectorL10n.settingsPresence;
sectionPresence.footerTitle = VectorL10n.settingsPresenceOfflineModeDescription;
[tmpSections addObject:sectionPresence];
}
Section *sectionAdvanced = [Section sectionWithTag:SECTION_TAG_ADVANCED];
sectionAdvanced.headerTitle = [VectorL10n settingsAdvanced];
if (BuildSettings.settingsScreenAllowChangingCrashUsageDataSettings)
{
[sectionAdvanced addRowWithTag:ADVANCED_CRASH_REPORT_INDEX];
}
if (BuildSettings.settingsScreenAllowChangingRageshakeSettings)
{
[sectionAdvanced addRowWithTag:ADVANCED_ENABLE_RAGESHAKE_INDEX];
}
[sectionAdvanced addRowWithTag:ADVANCED_MARK_ALL_AS_READ_INDEX];
[sectionAdvanced addRowWithTag:ADVANCED_CLEAR_CACHE_INDEX];
if (BuildSettings.settingsScreenAllowBugReportingManually)
{
[sectionAdvanced addRowWithTag:ADVANCED_REPORT_BUG_INDEX];
}
[tmpSections addObject:sectionAdvanced];
Section *sectionAbout = [Section sectionWithTag:SECTION_TAG_ABOUT];
if (BuildSettings.applicationCopyrightUrlString.length)
{
[sectionAbout addRowWithTag:ABOUT_COPYRIGHT_INDEX];
}
if (BuildSettings.applicationAcceptableUsePolicyUrlString.length)
{
[sectionAbout addRowWithTag:ABOUT_ACCEPTABLE_USE_INDEX];
}
if (BuildSettings.applicationPrivacyPolicyUrlString.length)
{
[sectionAbout addRowWithTag:ABOUT_PRIVACY_INDEX];
}
[sectionAbout addRowWithTag:ABOUT_THIRD_PARTY_INDEX];
sectionAbout.headerTitle = VectorL10n.settingsAbout;
if (BuildSettings.settingsScreenShowAdvancedSettings)
{
sectionAbout.footerTitle = [self buildAboutSectionFooterTitleWithAccount:account];
}
[tmpSections addObject:sectionAbout];
if (BuildSettings.settingsScreenShowLabSettings)
{
Section *sectionLabs = [Section sectionWithTag:SECTION_TAG_LABS];
[sectionLabs addRowWithTag:LABS_ENABLE_RINGING_FOR_GROUP_CALLS_INDEX];
[sectionLabs addRowWithTag:LABS_ENABLE_THREADS_INDEX];
[sectionLabs addRowWithTag:LABS_ENABLE_AUTO_REPORT_DECRYPTION_ERRORS];
if (BuildSettings.locationSharingEnabled)
{
[sectionLabs addRowWithTag:LABS_ENABLE_LIVE_LOCATION_SHARING];
}
[sectionLabs addRowWithTag:LABS_ENABLE_NEW_SESSION_MANAGER];
[sectionLabs addRowWithTag:LABS_ENABLE_NEW_CLIENT_INFO_FEATURE];
if (@available(iOS 15.0, *))
{
[sectionLabs addRowWithTag:LABS_ENABLE_WYSIWYG_COMPOSER];
}
[sectionLabs addRowWithTag:LABS_ENABLE_VOICE_BROADCAST];
sectionLabs.headerTitle = [VectorL10n settingsLabs];
if (sectionLabs.hasAnyRows)
{
[tmpSections addObject:sectionLabs];
}
}
if (BuildSettings.settingsScreenAllowDeactivatingAccount && !self.mainSession.homeserverWellknown.authentication)
{
Section *sectionDeactivate = [Section sectionWithTag:SECTION_TAG_DEACTIVATE_ACCOUNT];
[sectionDeactivate addRowWithTag:0];
sectionDeactivate.headerTitle = [VectorL10n settingsDeactivateAccount];
[tmpSections addObject:sectionDeactivate];
}
// update sections
_tableViewSections.sections = tmpSections;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.title = [VectorL10n settingsTitle];
[self vc_removeBackTitle];
[self.tableView registerClass:MXKTableViewCellWithLabelAndTextField.class forCellReuseIdentifier:[MXKTableViewCellWithLabelAndTextField defaultReuseIdentifier]];
[self.tableView registerClass:MXKTableViewCellWithLabelAndSwitch.class forCellReuseIdentifier:[MXKTableViewCellWithLabelAndSwitch defaultReuseIdentifier]];
[self.tableView registerClass:MXKTableViewCellWithLabelAndMXKImageView.class forCellReuseIdentifier:[MXKTableViewCellWithLabelAndMXKImageView defaultReuseIdentifier]];
[self.tableView registerClass:TableViewCellWithPhoneNumberTextField.class forCellReuseIdentifier:[TableViewCellWithPhoneNumberTextField defaultReuseIdentifier]];
[self.tableView registerNib:MXKTableViewCellWithTextView.nib forCellReuseIdentifier:[MXKTableViewCellWithTextView defaultReuseIdentifier]];
[self.tableView registerNib:SectionFooterView.nib forHeaderFooterViewReuseIdentifier:[SectionFooterView defaultReuseIdentifier]];
// Enable self sizing cells and footers
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 50;
self.tableView.sectionFooterHeight = UITableViewAutomaticDimension;
self.tableView.estimatedSectionFooterHeight = 50;
MXWeakify(self);
// Add observer to handle removed accounts
removedAccountObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountManagerDidRemoveAccountNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
if ([MXKAccountManager sharedManager].accounts.count)
{
// Refresh table to remove this account
[self refreshSettings];
}
}];
// Add observer to handle accounts update
accountUserInfoObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountUserInfoDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self stopActivityIndicator];
[self refreshSettings];
}];
// Add observer to push settings
pushInfoUpdateObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountAPNSActivityDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self stopActivityIndicator];
[self refreshSettings];
}];
[self registerAccountDataDidChangeIdentityServerNotification];
// Add each matrix session, to update the view controller appearance according to mx sessions state
NSArray *sessions = [AppDelegate theDelegate].mxSessions;
for (MXSession *mxSession in sessions)
{
[self addMatrixSession:mxSession];
}
[self setupDiscoverySection];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:@selector(onSave:)];
self.navigationItem.rightBarButtonItem.accessibilityIdentifier=@"SettingsVCNavBarSaveButton";
// Observe user interface theme change.
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self userInterfaceThemeDidChange];
}];
[self userInterfaceThemeDidChange];
_tableViewSections = [TableViewSections new];
_tableViewSections.delegate = self;
[self updateSections];
}
- (void)userInterfaceThemeDidChange
{
[ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar];
self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor;
// Check the table view style to select its bg color.
self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor);
self.view.backgroundColor = self.tableView.backgroundColor;
self.tableView.separatorColor = ThemeService.shared.theme.lineBreakColor;
if (self.tableView.dataSource)
{
[self refreshSettings];
}
[self setNeedsStatusBarAppearanceUpdate];
}
- (UIStatusBarStyle)preferredStatusBarStyle
{
return ThemeService.shared.theme.statusBarStyle;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)destroy
{
// Release the potential pushed view controller
[self releasePushedViewController];
if (kThemeServiceDidChangeThemeNotificationObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:kThemeServiceDidChangeThemeNotificationObserver];
kThemeServiceDidChangeThemeNotificationObserver = nil;
}
if (isSavingInProgress || is3PIDBindingInProgress)
{
__weak typeof(self) weakSelf = self;
onReadyToDestroyHandler = ^() {
if (weakSelf)
{
typeof(self) self = weakSelf;
[self destroy];
}
};
}
else
{
// Dispose all resources
[self reset];
[super destroy];
}
identityServerSettingsCoordinatorBridgePresenter = nil;
}
- (void)onMatrixSessionStateDidChange:(NSNotification *)notif
{
MXSession *mxSession = notif.object;
// Check whether the concerned session is a new one which is not already associated with this view controller.
if (mxSession.state == MXSessionStateInitialised && [self.mxSessions indexOfObject:mxSession] != NSNotFound)
{
// Store this new session
[self addMatrixSession:mxSession];
}
else
{
[super onMatrixSessionStateDidChange:notif];
}
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.screenTracker trackScreen];
// Refresh display
[self refreshSettings];
// Refresh linked emails and phone numbers in parallel
[self loadAccount3PIDs];
MXWeakify(self);
// Observe kAppDelegateDidTapStatusBarNotificationObserver.
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self.tableView setContentOffset:CGPointMake(-self.tableView.adjustedContentInset.left, -self.tableView.adjustedContentInset.top) animated:YES];
}];
newPhoneNumberCountryPicker = nil;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Release the potential pushed view controller
[self releasePushedViewController];
[self.settingsDiscoveryTableViewSection reload];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if (currentAlert)
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
currentAlert = nil;
}
if (notificationCenterWillUpdateObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:notificationCenterWillUpdateObserver];
notificationCenterWillUpdateObserver = nil;
}
if (notificationCenterDidUpdateObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:notificationCenterDidUpdateObserver];
notificationCenterDidUpdateObserver = nil;
}
if (notificationCenterDidFailObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:notificationCenterDidFailObserver];
notificationCenterDidFailObserver = nil;
}
if (kAppDelegateDidTapStatusBarNotificationObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:kAppDelegateDidTapStatusBarNotificationObserver];
kAppDelegateDidTapStatusBarNotificationObserver = nil;
}
}
#pragma mark - Internal methods
- (void)pushViewController:(UIViewController*)viewController
{
// Keep ref on pushed view controller
pushedViewController = viewController;
[self.navigationController pushViewController:viewController animated:YES];
}
- (void)releasePushedViewController
{
if (pushedViewController)
{
if ([pushedViewController isKindOfClass:[UINavigationController class]])
{
UINavigationController *navigationController = (UINavigationController*)pushedViewController;
for (id subViewController in navigationController.viewControllers)
{
if ([subViewController respondsToSelector:@selector(destroy)])
{
[subViewController destroy];
}
}
}
else if ([pushedViewController respondsToSelector:@selector(destroy)])
{
[(id)pushedViewController destroy];
}
pushedViewController = nil;
}
}
- (void)dismissKeyboard
{
[newEmailTextField resignFirstResponder];
[newPhoneNumberCell.mxkTextField resignFirstResponder];
}
- (void)reset
{
// Remove observers
if (removedAccountObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:removedAccountObserver];
removedAccountObserver = nil;
}
if (accountUserInfoObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:accountUserInfoObserver];
accountUserInfoObserver = nil;
}
if (pushInfoUpdateObserver)
{
[[NSNotificationCenter defaultCenter] removeObserver:pushInfoUpdateObserver];
pushInfoUpdateObserver = nil;
}
[[NSNotificationCenter defaultCenter] removeObserver:self];
onReadyToDestroyHandler = nil;
}
-(void)setNewEmailEditingEnabled:(BOOL)newEmailEditingEnabled
{
if (newEmailEditingEnabled != _newEmailEditingEnabled)
{
// Update the flag
_newEmailEditingEnabled = newEmailEditingEnabled;
if (!newEmailEditingEnabled)
{
// Dismiss the keyboard
[newEmailTextField resignFirstResponder];
newEmailTextField = nil;
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView beginUpdates];
// Refresh the corresponding table view cell with animation
NSIndexPath *addEmailIndexPath = [self.tableViewSections exactIndexPathForRowTag:USER_SETTINGS_ADD_EMAIL_INDEX
sectionTag:SECTION_TAG_USER_SETTINGS];
if (addEmailIndexPath)
{
[self.tableView reloadRowsAtIndexPaths:@[addEmailIndexPath] withRowAnimation:UITableViewRowAnimationFade];
}
[self.tableView endUpdates];
});
}
}
-(void)setNewPhoneEditingEnabled:(BOOL)newPhoneEditingEnabled
{
if (newPhoneEditingEnabled != _newPhoneEditingEnabled)
{
// Update the flag
_newPhoneEditingEnabled = newPhoneEditingEnabled;
if (!newPhoneEditingEnabled)
{
// Dismiss the keyboard
[newPhoneNumberCell.mxkTextField resignFirstResponder];
newPhoneNumberCell = nil;
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView beginUpdates];
// Refresh the corresponding table view cell with animation
NSIndexPath *addPhoneIndexPath = [self.tableViewSections exactIndexPathForRowTag:USER_SETTINGS_ADD_PHONENUMBER_INDEX
sectionTag:SECTION_TAG_USER_SETTINGS];
if (addPhoneIndexPath)
{
[self.tableView reloadRowsAtIndexPaths:@[addPhoneIndexPath] withRowAnimation:UITableViewRowAnimationFade];
}
[self.tableView endUpdates];
});
}
}
- (void)showValidationEmailDialogWithMessage:(NSString*)message for3PidAddSession:(MX3PidAddSession*)threePidAddSession threePidAddManager:(MX3PidAddManager*)threePidAddManager authenticationParameters:(NSDictionary*)authenticationParameters
{
MXWeakify(self);
[currentAlert dismissViewControllerAnimated:NO completion:nil];
UIAlertController *validationAlert = [UIAlertController alertControllerWithTitle:[VectorL10n accountEmailValidationTitle]
message:message
preferredStyle:UIAlertControllerStyleAlert];
[validationAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
[self stopActivityIndicator];
// Reset new email adding
self.newEmailEditingEnabled = NO;
}]];
[validationAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n continue] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self tryFinaliseAddEmailSession:threePidAddSession withAuthenticationParameters:authenticationParameters
threePidAddManager:threePidAddManager];
}]];
[validationAlert mxk_setAccessibilityIdentifier:@"SettingsVCEmailValidationAlert"];
[self presentViewController:validationAlert animated:YES completion:nil];
currentAlert = validationAlert;
}
- (void)tryFinaliseAddEmailSession:(MX3PidAddSession*)threePidAddSession withAuthenticationParameters:(NSDictionary*)authParams threePidAddManager:(MX3PidAddManager*)threePidAddManager
{
self->is3PIDBindingInProgress = YES;
[threePidAddManager tryFinaliseAddEmailSession:threePidAddSession authParams:authParams success:^{
self->is3PIDBindingInProgress = NO;
// Check whether destroy has been called during email binding
if (self->onReadyToDestroyHandler)
{
// Ready to destroy
self->onReadyToDestroyHandler();
self->onReadyToDestroyHandler = nil;
}
else
{
self->currentAlert = nil;
[self stopActivityIndicator];
// Reset new email adding
self.newEmailEditingEnabled = NO;
// Update linked emails
[self loadAccount3PIDs];
}
} failure:^(NSError * _Nonnull error) {
MXLogDebug(@"[SettingsViewController] tryFinaliseAddEmailSession: Failed to bind email");
MXError *mxError = [[MXError alloc] initWithNSError:error];
if (mxError && [mxError.errcode isEqualToString:kMXErrCodeStringForbidden])
{
MXLogDebug(@"[SettingsViewController] tryFinaliseAddEmailSession: Wrong credentials");
// Ask password again
UIAlertController *passwordPrompt = [UIAlertController alertControllerWithTitle:nil
message:[VectorL10n settingsAdd3pidInvalidPasswordMessage]
preferredStyle:UIAlertControllerStyleAlert];
MXWeakify(self);
[passwordPrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n retry] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
[self showAuthenticationIfNeededForAdding:kMX3PIDMediumEmail withSession:self.mainSession completion:^(NSDictionary *authParams) {
[self tryFinaliseAddEmailSession:threePidAddSession withAuthenticationParameters:authParams threePidAddManager:threePidAddManager];
}];
}]];
[self presentViewController:passwordPrompt animated:YES completion:nil];
self->currentAlert = passwordPrompt;
return;
}
self->is3PIDBindingInProgress = NO;
// Check whether destroy has been called during email binding
if (self->onReadyToDestroyHandler)
{
// Ready to destroy
self->onReadyToDestroyHandler();
self->onReadyToDestroyHandler = nil;
}
else
{
self->currentAlert = nil;
// Display the same popup again if the error is M_THREEPID_AUTH_FAILED
MXError *mxError = [[MXError alloc] initWithNSError:error];
if (mxError && [mxError.errcode isEqualToString:kMXErrCodeStringThreePIDAuthFailed])
{
[self showValidationEmailDialogWithMessage:[VectorL10n accountEmailValidationError] for3PidAddSession:threePidAddSession threePidAddManager:threePidAddManager authenticationParameters:authParams];
}
else
{
[self stopActivityIndicator];
// Notify user
NSString *myUserId = self.mainSession.myUser.userId; // TODO: Hanlde multi-account
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}
}
}];
}
- (void)showValidationMsisdnDialogWithMessage:(NSString*)message for3PidAddSession:(MX3PidAddSession*)threePidAddSession threePidAddManager:(MX3PidAddManager*)threePidAddManager authenticationParameters:(NSDictionary*)authenticationParameters
{
MXWeakify(self);
[currentAlert dismissViewControllerAnimated:NO completion:nil];
UIAlertController *validationAlert = [UIAlertController alertControllerWithTitle:[VectorL10n accountMsisdnValidationTitle]
message:message
preferredStyle:UIAlertControllerStyleAlert];
[validationAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
[self stopActivityIndicator];
// Reset new phone adding
self.newPhoneEditingEnabled = NO;
}]];
[validationAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
textField.secureTextEntry = NO;
textField.placeholder = nil;
textField.keyboardType = UIKeyboardTypeDecimalPad;
}];
[validationAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n submit] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
NSString *smsCode = [self->currentAlert textFields].firstObject.text;
self->currentAlert = nil;
if (smsCode.length)
{
[self finaliseAddPhoneNumberSession:threePidAddSession withToken:smsCode andAuthenticationParameters:authenticationParameters message:message threePidAddManager:threePidAddManager];
}
else
{
// Ask again the sms token
[self showValidationMsisdnDialogWithMessage:message for3PidAddSession:threePidAddSession threePidAddManager:threePidAddManager authenticationParameters:authenticationParameters];
}
}]];
[validationAlert mxk_setAccessibilityIdentifier: @"SettingsVCMsisdnValidationAlert"];
[self presentViewController:validationAlert animated:YES completion:nil];
currentAlert = validationAlert;
}
- (void)finaliseAddPhoneNumberSession:(MX3PidAddSession*)threePidAddSession withToken:(NSString*)token andAuthenticationParameters:(NSDictionary*)authParams message:(NSString*)message threePidAddManager:(MX3PidAddManager*)threePidAddManager
{
self->is3PIDBindingInProgress = YES;
[threePidAddManager finaliseAddPhoneNumberSession:threePidAddSession withToken:token authParams:authParams success:^{
self->is3PIDBindingInProgress = NO;
// Check whether destroy has been called during the binding
if (self->onReadyToDestroyHandler)
{
// Ready to destroy
self->onReadyToDestroyHandler();
self->onReadyToDestroyHandler = nil;
}
else
{
[self stopActivityIndicator];
// Reset new phone adding
self.newPhoneEditingEnabled = NO;
// Update linked 3pids
[self loadAccount3PIDs];
}
} failure:^(NSError * _Nonnull error) {
MXLogDebug(@"[SettingsViewController] finaliseAddPhoneNumberSession: Failed to submit the sms token");
MXError *mxError = [[MXError alloc] initWithNSError:error];
if (mxError && [mxError.errcode isEqualToString:kMXErrCodeStringForbidden])
{
MXLogDebug(@"[SettingsViewController] finaliseAddPhoneNumberSession: Wrong authentication credentials");
// Ask password again
UIAlertController *passwordPrompt = [UIAlertController alertControllerWithTitle:nil
message:[VectorL10n settingsAdd3pidInvalidPasswordMessage]
preferredStyle:UIAlertControllerStyleAlert];
MXWeakify(self);
[passwordPrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n retry] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
[self showAuthenticationIfNeededForAdding:kMX3PIDMediumMSISDN withSession:self.mainSession completion:^(NSDictionary *authParams) {
[self finaliseAddPhoneNumberSession:threePidAddSession withToken:token andAuthenticationParameters:authParams message:message threePidAddManager:threePidAddManager];
}];
}]];
[self presentViewController:passwordPrompt animated:YES completion:nil];
self->currentAlert = passwordPrompt;
return;
}
self->is3PIDBindingInProgress = NO;
// Check whether destroy has been called during phone binding
if (self->onReadyToDestroyHandler)
{
// Ready to destroy
self->onReadyToDestroyHandler();
self->onReadyToDestroyHandler = nil;
}
else
{
// Ignore connection cancellation error
if (([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled))
{
[self stopActivityIndicator];
return;
}
// Alert user
NSString *title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey];
NSString *msg = [error.userInfo valueForKey:NSLocalizedDescriptionKey];
if (!title)
{
if (msg)
{
title = msg;
msg = nil;
}
else
{
title = [VectorL10n error];
}
}
UIAlertController *errorAlert = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
MXWeakify(self);
[errorAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n ok] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
// Ask again the sms token
[self showValidationMsisdnDialogWithMessage:message for3PidAddSession:threePidAddSession threePidAddManager:threePidAddManager authenticationParameters:authParams];
}]];
[errorAlert mxk_setAccessibilityIdentifier: @"SettingsVCErrorAlert"];
[self presentViewController:errorAlert animated:YES completion:nil];
self->currentAlert = errorAlert;
}
}];
}
- (void)loadAccount3PIDs
{
// Refresh the account 3PIDs list
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
[account load3PIDs:^{
NSArray<MXThirdPartyIdentifier*> *thirdPartyIdentifiers = account.threePIDs ?: @[];
[self.settingsDiscoveryViewModel updateWithThirdPartyIdentifiers:thirdPartyIdentifiers];
// Refresh all the table (A slide down animation is observed when we limit the refresh to the concerned section).
// Note: The use of 'reloadData' handles the case where the account has been logged out.
[self refreshSettings];
} failure:^(NSError *error) {
// Display the data that has been loaded last time
// Note: The use of 'reloadData' handles the case where the account has been logged out.
[self refreshSettings];
}];
}
- (void)editNewEmailTextField
{
if (newEmailTextField && ![newEmailTextField becomeFirstResponder])
{
// Retry asynchronously
dispatch_async(dispatch_get_main_queue(), ^{
[self editNewEmailTextField];
});
}
}
- (void)editNewPhoneNumberTextField
{
if (newPhoneNumberCell && ![newPhoneNumberCell.mxkTextField becomeFirstResponder])
{
// Retry asynchronously
dispatch_async(dispatch_get_main_queue(), ^{
[self editNewPhoneNumberTextField];
});
}
}
- (void)refreshSettings
{
// Check whether a text input is currently edited
keepNewEmailEditing = newEmailTextField ? newEmailTextField.isFirstResponder : NO;
keepNewPhoneNumberEditing = newPhoneNumberCell ? newPhoneNumberCell.mxkTextField.isFirstResponder : NO;
// Trigger a full table reloadData
[self updateSections];
// Restore the previous edited field
if (keepNewEmailEditing)
{
[self editNewEmailTextField];
keepNewEmailEditing = NO;
}
else if (keepNewPhoneNumberEditing)
{
[self editNewPhoneNumberTextField];
keepNewPhoneNumberEditing = NO;
}
// Update notification access
[self refreshSystemNotificationSettings];
[[MXKAccountManager sharedManager].activeAccounts.firstObject loadCurrentPusher:nil failure:nil];
}
- (void)refreshSystemNotificationSettings
{
MXWeakify(self);
// Get the system notification settings to check authorization status and configuration.
[UNUserNotificationCenter.currentNotificationCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
dispatch_async(dispatch_get_main_queue(), ^{
MXStrongifyAndReturnIfNil(self);
self.systemNotificationSettings = settings;
[self.tableView reloadData];
});
}];
}
- (void)formatNewPhoneNumber
{
if (newPhoneNumber)
{
NSString *formattedNumber = [[NBPhoneNumberUtil sharedInstance] format:newPhoneNumber numberFormat:NBEPhoneNumberFormatINTERNATIONAL error:nil];
NSString *prefix = newPhoneNumberCell.mxkLabel.text;
if ([formattedNumber hasPrefix:prefix])
{
// Format the display phone number
newPhoneNumberCell.mxkTextField.text = [formattedNumber substringFromIndex:prefix.length];
}
}
}
- (void)setupDiscoverySection
{
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
NSArray<MXThirdPartyIdentifier*> *thirdPartyIdentifiers = account.threePIDs ?: @[];
SettingsDiscoveryViewModel *viewModel = [[SettingsDiscoveryViewModel alloc] initWithSession:self.mainSession thirdPartyIdentifiers:thirdPartyIdentifiers];
viewModel.coordinatorDelegate = self;
SettingsDiscoveryTableViewSection *discoverySection = [[SettingsDiscoveryTableViewSection alloc] initWithViewModel:viewModel];
discoverySection.delegate = self;
self.settingsDiscoveryViewModel = viewModel;
self.settingsDiscoveryTableViewSection = discoverySection;
}
- (UserInteractiveAuthenticationService*)createUserInteractiveAuthenticationService
{
MXSession *session = self.mainSession;
UserInteractiveAuthenticationService *userInteractiveAuthenticationService;
if (session)
{
userInteractiveAuthenticationService = [[UserInteractiveAuthenticationService alloc] initWithSession:session];
}
return userInteractiveAuthenticationService;
}
- (void)scrollToDiscoverySection
{
// settingsDiscoveryTableViewSection is a dynamic section, so check number of rows before scroll to avoid crashes
if (self.settingsDiscoveryTableViewSection.numberOfRows > 0)
{
NSIndexPath *discoveryIndexPath = [_tableViewSections exactIndexPathForRowTag:0 sectionTag:SECTION_TAG_DISCOVERY];
if (discoveryIndexPath)
{
[self.tableView scrollToRowAtIndexPath:discoveryIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}
else
{
// this won't be precise in scroll location, but seems the best option for now
NSIndexPath *discoveryIndexPath = [_tableViewSections nearestIndexPathForRowTag:0 sectionTag:SECTION_TAG_DISCOVERY];
if (discoveryIndexPath)
{
[self.tableView scrollToRowAtIndexPath:discoveryIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
}
}
- (void)scrollToUserSettingsSection
{
NSIndexPath *discoveryIndexPath = [_tableViewSections exactIndexPathForRowTag:USER_SETTINGS_ADD_EMAIL_INDEX
sectionTag:SECTION_TAG_USER_SETTINGS];
if (discoveryIndexPath)
{
[self.tableView scrollToRowAtIndexPath:discoveryIndexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}
- (NSString*)buildAboutSectionFooterTitleWithAccount:(MXKAccount*)account
{
NSMutableString *footerText = [NSMutableString new];
AppInfo *appInfo = AppInfo.current;
NSString *appName = appInfo.displayName;
NSString *appVersion = appInfo.appVersion.bundleShortVersion;
NSString *buildVersion = appInfo.appVersion.bundleVersion;
NSString *appVersionInfo = [NSString stringWithFormat:@"%@ %@ (%@)", appName, appVersion, buildVersion];
NSString *loggedUserInfo = [VectorL10n settingsConfigUserId:account.mxCredentials.userId];
NSString *homeserverInfo = [VectorL10n settingsConfigHomeServer:account.mxCredentials.homeServer];
NSString *sdkVersionInfo = [NSString stringWithFormat:@"Matrix SDK %@", MatrixSDKVersion];
[footerText appendFormat:@"%@\n", loggedUserInfo];
[footerText appendFormat:@"%@\n", homeserverInfo];
[footerText appendFormat:@"%@\n", appVersionInfo];
[footerText appendFormat:@"%@\n", sdkVersionInfo];
[footerText appendFormat:@"%@", self.mainSession.crypto.version];
return [footerText copy];
}
- (UITableViewCell *)buildMessageBubblesCellForTableView:(UITableView*)tableView
atIndexPath:(NSIndexPath*)indexPath
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsEnableRoomMessageBubbles];
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.roomScreenEnableMessageBubbles;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableRoomMessageBubbles:) forControlEvents:UIControlEventTouchUpInside];
return labelAndSwitchCell;
}
- (UITableViewCell *)buildAutoReportDecryptionErrorsCellForTableView:(UITableView*)tableView
atIndexPath:(NSIndexPath*)indexPath
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsLabsEnableAutoReportDecryptionErrors];
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.enableUISIAutoReporting;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableAutoReportDecryptionErrors:) forControlEvents:UIControlEventTouchUpInside];
return labelAndSwitchCell;
}
- (UITableViewCell *)buildLiveLocationSharingCellForTableView:(UITableView*)tableView
atIndexPath:(NSIndexPath*)indexPath
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsLabsEnableLiveLocationSharing];
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.enableLiveLocationSharing;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableLiveLocationSharing:) forControlEvents:UIControlEventTouchUpInside];
return labelAndSwitchCell;
}
#pragma mark - 3Pid Add
- (void)showAuthenticationIfNeededForAdding:(MX3PIDMedium)medium withSession:(MXSession*)session completion:(void (^)(NSDictionary* authParams))completion
{
[self startActivityIndicator];
MXWeakify(self);
void (^animationCompletion)(void) = ^void () {
MXStrongifyAndReturnIfNil(self);
[self stopActivityIndicator];
[self.reauthenticationCoordinatorBridgePresenter dismissWithAnimated:YES completion:^{}];
self.reauthenticationCoordinatorBridgePresenter = nil;
};
NSString *title;
if ([medium isEqualToString:kMX3PIDMediumMSISDN])
{
title = [VectorL10n settingsAdd3pidPasswordTitleMsidsn];
}
else
{
title = [VectorL10n settingsAdd3pidPasswordTitleEmail];
}
NSString *message = [VectorL10n settingsAdd3pidPasswordMessage];
[session.matrixRestClient add3PIDOnlyWithSessionId:@"" clientSecret:[MXTools generateSecret] authParams:nil success:^{
} failure:^(NSError *error) {
if (error)
{
[self.userInteractiveAuthenticationService authenticationSessionFromRequestError:error success:^(MXAuthenticationSession * _Nullable authenticationSession) {
if (authenticationSession)
{
ReauthenticationCoordinatorParameters *coordinatorParameters = [[ReauthenticationCoordinatorParameters alloc] initWithSession:self.mainSession presenter:self title:title message:message authenticationSession:authenticationSession];
ReauthenticationCoordinatorBridgePresenter *reauthenticationPresenter = [ReauthenticationCoordinatorBridgePresenter new];
[reauthenticationPresenter presentWith:coordinatorParameters animated:YES success:^(NSDictionary<NSString *,id> *_Nullable authParams) {
completion(authParams);
} cancel:^{
animationCompletion();
} failure:^(NSError * _Nonnull error) {
animationCompletion();
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
self.reauthenticationCoordinatorBridgePresenter = reauthenticationPresenter;
}
else
{
animationCompletion();
completion(nil);
}
} failure:^(NSError * _Nonnull error) {
animationCompletion();
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
}
else
{
animationCompletion();
[[AppDelegate theDelegate] showErrorAsAlert:error];
}
}];
}
#pragma mark - Segues
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Keep ref on destinationViewController
[super prepareForSegue:segue sender:sender];
// FIXME add night mode
}
#pragma mark - UITableView data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// update the save button if there is an update
[self updateSaveButtonStatus];
return _tableViewSections.sections.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
Section *sectionObject = [_tableViewSections sectionAtIndex:section];
return sectionObject.rows.count;
}
- (MXKTableViewCellWithLabelAndTextField*)getLabelAndTextFieldCell:(UITableView*)tableView forIndexPath:(NSIndexPath *)indexPath
{
MXKTableViewCellWithLabelAndTextField *cell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithLabelAndTextField defaultReuseIdentifier] forIndexPath:indexPath];
cell.mxkLabelLeadingConstraint.constant = tableView.vc_separatorInset.left;
cell.mxkTextFieldLeadingConstraint.constant = 16;
cell.mxkTextFieldTrailingConstraint.constant = 15;
cell.mxkLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
cell.mxkTextField.userInteractionEnabled = YES;
cell.mxkTextField.borderStyle = UITextBorderStyleNone;
cell.mxkTextField.textAlignment = NSTextAlignmentRight;
cell.mxkTextField.textColor = ThemeService.shared.theme.textSecondaryColor;
cell.mxkTextField.tintColor = ThemeService.shared.theme.tintColor;
cell.mxkTextField.font = [UIFont systemFontOfSize:16];
cell.mxkTextField.placeholder = nil;
cell.accessoryType = UITableViewCellAccessoryNone;
cell.accessoryView = nil;
cell.alpha = 1.0f;
cell.userInteractionEnabled = YES;
[cell layoutIfNeeded];
return cell;
}
- (MXKTableViewCellWithLabelAndSwitch*)getLabelAndSwitchCell:(UITableView*)tableView forIndexPath:(NSIndexPath *)indexPath
{
MXKTableViewCellWithLabelAndSwitch *cell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithLabelAndSwitch defaultReuseIdentifier] forIndexPath:indexPath];
cell.mxkLabelLeadingConstraint.constant = tableView.vc_separatorInset.left;
cell.mxkSwitchTrailingConstraint.constant = 15;
cell.mxkLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
[cell.mxkSwitch removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside];
// Force layout before reusing a cell (fix switch displayed outside the screen)
[cell layoutIfNeeded];
return cell;
}
- (MXKTableViewCell*)getDefaultTableViewCell:(UITableView*)tableView
{
MXKTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCell defaultReuseIdentifier]];
if (!cell)
{
cell = [[MXKTableViewCell alloc] init];
}
else
{
cell.selectionStyle = UITableViewCellSelectionStyleDefault;
cell.accessoryType = UITableViewCellAccessoryNone;
cell.accessoryView = nil;
}
cell.textLabel.accessibilityIdentifier = nil;
cell.textLabel.font = [UIFont systemFontOfSize:17];
cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
cell.contentView.backgroundColor = UIColor.clearColor;
return cell;
}
- (MXKTableViewCellWithTextView*)textViewCellForTableView:(UITableView*)tableView atIndexPath:(NSIndexPath *)indexPath
{
MXKTableViewCellWithTextView *textViewCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithTextView defaultReuseIdentifier] forIndexPath:indexPath];
textViewCell.mxkTextView.textColor = ThemeService.shared.theme.textPrimaryColor;
textViewCell.mxkTextView.font = [UIFont systemFontOfSize:17];
textViewCell.mxkTextView.backgroundColor = [UIColor clearColor];
textViewCell.mxkTextViewLeadingConstraint.constant = tableView.vc_separatorInset.left;
textViewCell.mxkTextViewTrailingConstraint.constant = tableView.vc_separatorInset.right;
textViewCell.mxkTextView.accessibilityIdentifier = nil;
return textViewCell;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSIndexPath *tagsIndexPath = [_tableViewSections tagsIndexPathFromTableViewIndexPath:indexPath];
NSInteger section = tagsIndexPath.section;
NSInteger row = tagsIndexPath.row;
// set the cell to a default value to avoid application crashes
UITableViewCell *cell = [[UITableViewCell alloc] init];
cell.backgroundColor = [UIColor redColor];
// check if there is a valid session
if (([AppDelegate theDelegate].mxSessions.count == 0) || ([MXKAccountManager sharedManager].activeAccounts.count == 0))
{
// else use a default cell
return cell;
}
MXSession* session = self.mainSession;
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
if (section == SECTION_TAG_SIGN_OUT)
{
MXKTableViewCellWithButton *signOutCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]];
if (!signOutCell)
{
signOutCell = [[MXKTableViewCellWithButton alloc] init];
}
else
{
// Fix https://github.com/vector-im/riot-ios/issues/1354
// Do not move this line in prepareForReuse because of https://github.com/vector-im/riot-ios/issues/1323
signOutCell.mxkButton.titleLabel.text = nil;
}
NSString* title = [VectorL10n settingsSignOut];
[signOutCell.mxkButton setTitle:title forState:UIControlStateNormal];
[signOutCell.mxkButton setTitle:title forState:UIControlStateHighlighted];
[signOutCell.mxkButton setTintColor:ThemeService.shared.theme.tintColor];
signOutCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17];
[signOutCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside];
[signOutCell.mxkButton addTarget:self action:@selector(onSignout:) forControlEvents:UIControlEventTouchUpInside];
signOutCell.mxkButton.accessibilityIdentifier=@"SettingsVCSignOutButton";
cell = signOutCell;
}
else if (section == SECTION_TAG_USER_SETTINGS)
{
MXMyUser* myUser = session.myUser;
if (row == USER_SETTINGS_PROFILE_PICTURE_INDEX)
{
MXKTableViewCellWithLabelAndMXKImageView *profileCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithLabelAndMXKImageView defaultReuseIdentifier] forIndexPath:indexPath];
profileCell.mxkLabelLeadingConstraint.constant = tableView.vc_separatorInset.left;
profileCell.mxkImageViewTrailingConstraint.constant = 10;
profileCell.mxkImageViewWidthConstraint.constant = profileCell.mxkImageViewHeightConstraint.constant = 30;
profileCell.mxkImageViewDisplayBoxType = MXKTableViewCellDisplayBoxTypeCircle;
if (!profileCell.mxkImageView.gestureRecognizers.count)
{
// tap on avatar to update it
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onProfileAvatarTap:)];
[profileCell.mxkImageView addGestureRecognizer:tap];
}
profileCell.mxkLabel.text = [VectorL10n settingsProfilePicture];
profileCell.accessibilityIdentifier=@"SettingsVCProfilPictureStaticText";
profileCell.mxkLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
// if the user defines a new avatar
if (newAvatarImage)
{
profileCell.mxkImageView.image = newAvatarImage;
}
else
{
UIImage* avatarImage = [AvatarGenerator generateAvatarForMatrixItem:myUser.userId withDisplayName:myUser.displayname];
if (myUser.avatarUrl)
{
profileCell.mxkImageView.enableInMemoryCache = YES;
[profileCell.mxkImageView setImageURI:myUser.avatarUrl
withType:nil
andImageOrientation:UIImageOrientationUp
toFitViewSize:profileCell.mxkImageView.frame.size
withMethod:MXThumbnailingMethodCrop
previewImage:avatarImage
mediaManager:session.mediaManager];
}
else
{
profileCell.mxkImageView.image = avatarImage;
}
}
cell = profileCell;
}
else if (row == USER_SETTINGS_DISPLAYNAME_INDEX)
{
MXKTableViewCellWithLabelAndTextField *displaynameCell = [self getLabelAndTextFieldCell:tableView forIndexPath:indexPath];
displaynameCell.mxkLabel.text = [VectorL10n settingsDisplayName];
displaynameCell.mxkTextField.text = myUser.displayname;
displaynameCell.mxkTextField.tag = row;
displaynameCell.mxkTextField.delegate = self;
[displaynameCell.mxkTextField removeTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
[displaynameCell.mxkTextField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
displaynameCell.mxkTextField.accessibilityIdentifier=@"SettingsVCDisplayNameTextField";
cell = displaynameCell;
}
else if (row == USER_SETTINGS_FIRST_NAME_INDEX)
{
MXKTableViewCellWithLabelAndTextField *firstCell = [self getLabelAndTextFieldCell:tableView forIndexPath:indexPath];
firstCell.mxkLabel.text = [VectorL10n settingsFirstName];
firstCell.mxkTextField.userInteractionEnabled = NO;
cell = firstCell;
}
else if (row == USER_SETTINGS_SURNAME_INDEX)
{
MXKTableViewCellWithLabelAndTextField *surnameCell = [self getLabelAndTextFieldCell:tableView forIndexPath:indexPath];
surnameCell.mxkLabel.text = [VectorL10n settingsSurname];
surnameCell.mxkTextField.userInteractionEnabled = NO;
cell = surnameCell;
}
else if (row >= USER_SETTINGS_EMAILS_OFFSET)
{
NSInteger emailIndex = row - USER_SETTINGS_EMAILS_OFFSET;
MXKTableViewCellWithLabelAndTextField *emailCell = [self getLabelAndTextFieldCell:tableView forIndexPath:indexPath];
emailCell.mxkLabel.text = [VectorL10n settingsEmailAddress];
emailCell.mxkTextField.text = account.linkedEmails[emailIndex];
emailCell.mxkTextField.userInteractionEnabled = NO;
cell = emailCell;
}
else if (row >= USER_SETTINGS_PHONENUMBERS_OFFSET)
{
NSInteger phoneNumberIndex = row - USER_SETTINGS_PHONENUMBERS_OFFSET;
MXKTableViewCellWithLabelAndTextField *phoneCell = [self getLabelAndTextFieldCell:tableView forIndexPath:indexPath];
phoneCell.mxkLabel.text = [VectorL10n settingsPhoneNumber];
phoneCell.mxkTextField.text = [MXKTools readableMSISDN:account.linkedPhoneNumbers[phoneNumberIndex]];
phoneCell.mxkTextField.userInteractionEnabled = NO;
cell = phoneCell;
}
else if (row == USER_SETTINGS_ADD_EMAIL_INDEX)
{
MXKTableViewCellWithLabelAndTextField *newEmailCell = [self getLabelAndTextFieldCell:tableView forIndexPath:indexPath];
// Render the cell according to the `newEmailEditingEnabled` property
if (!_newEmailEditingEnabled)
{
newEmailCell.mxkLabel.text = [VectorL10n settingsAddEmailAddress];
newEmailCell.mxkTextField.text = nil;
newEmailCell.mxkTextField.userInteractionEnabled = NO;
newEmailCell.accessoryView = [[UIImageView alloc] initWithImage:[AssetImages.plusIcon.image vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]];
}
else
{
newEmailCell.mxkLabel.text = nil;
newEmailCell.mxkTextField.placeholder = [VectorL10n settingsEmailAddressPlaceholder];
newEmailCell.mxkTextField.attributedPlaceholder = [[NSAttributedString alloc]
initWithString:newEmailCell.mxkTextField.placeholder
attributes:@{NSForegroundColorAttributeName: ThemeService.shared.theme.placeholderTextColor}];
newEmailCell.mxkTextField.text = newEmailTextField.text;
newEmailCell.mxkTextField.userInteractionEnabled = YES;
newEmailCell.mxkTextField.keyboardType = UIKeyboardTypeEmailAddress;
newEmailCell.mxkTextField.autocorrectionType = UITextAutocorrectionTypeNo;
newEmailCell.mxkTextField.spellCheckingType = UITextSpellCheckingTypeNo;
newEmailCell.mxkTextField.delegate = self;
newEmailCell.mxkTextField.accessibilityIdentifier=@"SettingsVCAddEmailTextField";
[newEmailCell.mxkTextField removeTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
[newEmailCell.mxkTextField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
[newEmailCell.mxkTextField removeTarget:self action:@selector(textFieldDidEnd:) forControlEvents:UIControlEventEditingDidEnd];
[newEmailCell.mxkTextField addTarget:self action:@selector(textFieldDidEnd:) forControlEvents:UIControlEventEditingDidEnd];
// When displaying the textfield the 1st time, open the keyboard
if (!newEmailTextField)
{
newEmailTextField = newEmailCell.mxkTextField;
[self editNewEmailTextField];
}
else
{
// Update the current text field.
newEmailTextField = newEmailCell.mxkTextField;
}
UIImage *accessoryViewImage = [AssetImages.plusIcon.image vc_tintedImageUsingColor:ThemeService.shared.theme.tintColor];
newEmailCell.accessoryView = [[UIImageView alloc] initWithImage:accessoryViewImage];
}
newEmailCell.mxkTextField.tag = row;
cell = newEmailCell;
}
else if (row == USER_SETTINGS_ADD_PHONENUMBER_INDEX)
{
// Render the cell according to the `newPhoneEditingEnabled` property
if (!_newPhoneEditingEnabled)
{
MXKTableViewCellWithLabelAndTextField *newPhoneCell = [self getLabelAndTextFieldCell:tableView forIndexPath:indexPath];
newPhoneCell.mxkLabel.text = [VectorL10n settingsAddPhoneNumber];
newPhoneCell.mxkTextField.text = nil;
newPhoneCell.mxkTextField.userInteractionEnabled = NO;
newPhoneCell.accessoryView = [[UIImageView alloc] initWithImage:[AssetImages.plusIcon.image vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]];
cell = newPhoneCell;
}
else
{
TableViewCellWithPhoneNumberTextField * newPhoneCell = [self.tableView dequeueReusableCellWithIdentifier:[TableViewCellWithPhoneNumberTextField defaultReuseIdentifier] forIndexPath:indexPath];
[newPhoneCell.countryCodeButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside];
[newPhoneCell.countryCodeButton addTarget:self action:@selector(selectPhoneNumberCountry:) forControlEvents:UIControlEventTouchUpInside];
newPhoneCell.countryCodeButton.accessibilityIdentifier = @"SettingsVCPhoneCountryButton";
newPhoneCell.mxkLabel.font = newPhoneCell.mxkTextField.font = [UIFont systemFontOfSize:16];
newPhoneCell.mxkTextField.userInteractionEnabled = YES;
newPhoneCell.mxkTextField.keyboardType = UIKeyboardTypePhonePad;
newPhoneCell.mxkTextField.autocorrectionType = UITextAutocorrectionTypeNo;
newPhoneCell.mxkTextField.spellCheckingType = UITextSpellCheckingTypeNo;
newPhoneCell.mxkTextField.delegate = self;
newPhoneCell.mxkTextField.accessibilityIdentifier=@"SettingsVCAddPhoneTextField";
[newPhoneCell.mxkTextField removeTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
[newPhoneCell.mxkTextField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];
[newPhoneCell.mxkTextField removeTarget:self action:@selector(textFieldDidEnd:) forControlEvents:UIControlEventEditingDidEnd];
[newPhoneCell.mxkTextField addTarget:self action:@selector(textFieldDidEnd:) forControlEvents:UIControlEventEditingDidEnd];
newPhoneCell.mxkTextField.tag = row;
// When displaying the textfield the 1st time, open the keyboard
if (!newPhoneNumberCell)
{
NSString *countryCode = [MXKAppSettings standardAppSettings].phonebookCountryCode;
if (!countryCode)
{
// If none, consider the preferred locale
NSLocale *local = [[NSLocale alloc] initWithLocaleIdentifier:[[NSBundle mainBundle] preferredLocalizations][0]];
if ([local respondsToSelector:@selector(countryCode)])
{
countryCode = local.countryCode;
}
if (!countryCode)
{
countryCode = @"GB";
}
}
newPhoneCell.isoCountryCode = countryCode;
newPhoneCell.mxkTextField.text = nil;
newPhoneNumberCell = newPhoneCell;
[self editNewPhoneNumberTextField];
}
else
{
newPhoneCell.isoCountryCode = newPhoneNumberCell.isoCountryCode;
newPhoneCell.mxkTextField.text = newPhoneNumberCell.mxkTextField.text;
newPhoneNumberCell = newPhoneCell;
}
UIImage *accessoryViewImage = [AssetImages.plusIcon.image vc_tintedImageUsingColor:ThemeService.shared.theme.tintColor];
newPhoneCell.accessoryView = [[UIImageView alloc] initWithImage:accessoryViewImage];
cell = newPhoneCell;
}
}
else if (row == USER_SETTINGS_CHANGE_PASSWORD_INDEX)
{
MXKTableViewCellWithLabelAndTextField *passwordCell = [self getLabelAndTextFieldCell:tableView forIndexPath:indexPath];
passwordCell.mxkLabel.text = [VectorL10n settingsChangePassword];
passwordCell.mxkTextField.text = @"*********";
passwordCell.mxkTextField.userInteractionEnabled = NO;
passwordCell.mxkLabel.accessibilityIdentifier=@"SettingsVCChangePwdStaticText";
cell = passwordCell;
}
}
else if (section == SECTION_TAG_SENDING_MEDIA)
{
if (row == SENDING_MEDIA_CONFIRM_SIZE)
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsConfirmMediaSize];
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.showMediaCompressionPrompt;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleConfirmMediaSize:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
}
else if (section == SECTION_TAG_LINKS)
{
if (row == LINKS_SHOW_URL_PREVIEWS_INDEX)
{
MXKTableViewCellWithLabelAndSwitch *labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsShowUrlPreviews];
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.roomScreenShowsURLPreviews;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableURLPreviews:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
else if (row == LINKS_SHOW_URL_PREVIEWS_DESCRIPTION_INDEX)
{
MXKTableViewCell *descriptionCell = [self getDefaultTableViewCell:tableView];
descriptionCell.textLabel.text = [VectorL10n settingsShowUrlPreviewsDescription];
descriptionCell.textLabel.numberOfLines = 0;
descriptionCell.selectionStyle = UITableViewCellSelectionStyleNone;
cell = descriptionCell;
}
}
else if (section == SECTION_TAG_NOTIFICATIONS)
{
if (row == NOTIFICATION_SETTINGS_ENABLE_PUSH_INDEX)
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsEnablePushNotif];
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(togglePushNotifications:) forControlEvents:UIControlEventTouchUpInside];
BOOL isPushEnabled = account.pushNotificationServiceIsActive;
// Even if push is enabled for the account, the user may have turned off notifications in system settings
if (isPushEnabled && self.systemNotificationSettings)
{
isPushEnabled = self.systemNotificationSettings.authorizationStatus == UNAuthorizationStatusAuthorized;
}
labelAndSwitchCell.mxkSwitch.on = isPushEnabled;
cell = labelAndSwitchCell;
}
else if (row == NOTIFICATION_SETTINGS_SYSTEM_SETTINGS)
{
cell = [tableView dequeueReusableCellWithIdentifier:kSettingsViewControllerPhoneBookCountryCellId];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:kSettingsViewControllerPhoneBookCountryCellId];
}
cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
cell.textLabel.text = [VectorL10n settingsDeviceNotifications];
cell.detailTextLabel.text = @"";
[cell vc_setAccessoryDisclosureIndicatorWithCurrentTheme];
cell.selectionStyle = UITableViewCellSelectionStyleDefault;
}
else if (row == NOTIFICATION_SETTINGS_SHOW_IN_APP_INDEX)
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = VectorL10n.settingsEnableInappNotifications;
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.showInAppNotifications;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = account.pushNotificationServiceIsActive;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleShowInAppNotifications:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
else if (row == NOTIFICATION_SETTINGS_SHOW_DECODED_CONTENT)
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsShowDecryptedContent];
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.showDecryptedContentInNotifications;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = account.pushNotificationServiceIsActive;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleShowDecodedContent:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
else if (row == NOTIFICATION_SETTINGS_PIN_MISSED_NOTIFICATIONS_INDEX)
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsPinRoomsWithMissedNotif];
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.pinRoomsWithMissedNotificationsOnHome;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(togglePinRoomsWithMissedNotif:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
else if (row == NOTIFICATION_SETTINGS_PIN_UNREAD_INDEX)
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsPinRoomsWithUnread];
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.pinRoomsWithUnreadMessagesOnHome;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(togglePinRoomsWithUnread:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
else if (row == NOTIFICATION_SETTINGS_DEFAULT_SETTINGS_INDEX || row == NOTIFICATION_SETTINGS_MENTION_AND_KEYWORDS_SETTINGS_INDEX || row == NOTIFICATION_SETTINGS_OTHER_SETTINGS_INDEX)
{
cell = [self getDefaultTableViewCell:tableView];
if (row == NOTIFICATION_SETTINGS_DEFAULT_SETTINGS_INDEX)
{
cell.textLabel.text = [VectorL10n settingsDefault];
}
else if (row == NOTIFICATION_SETTINGS_MENTION_AND_KEYWORDS_SETTINGS_INDEX)
{
cell.textLabel.text = [VectorL10n settingsMentionsAndKeywords];
}
else if (row == NOTIFICATION_SETTINGS_OTHER_SETTINGS_INDEX)
{
cell.textLabel.text = [VectorL10n settingsOther];
}
[cell vc_setAccessoryDisclosureIndicatorWithCurrentTheme];
}
}
else if (section == SECTION_TAG_CALLS)
{
if (row == CALLS_ENABLE_STUN_SERVER_FALLBACK_INDEX)
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsCallsStunServerFallbackButton];
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.allowStunServerFallback;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleStunServerFallback:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
}
else if (section == SECTION_TAG_DISCOVERY)
{
cell = [self.settingsDiscoveryTableViewSection cellForRowAtRow:row];
}
else if (section == SECTION_TAG_IDENTITY_SERVER)
{
switch (row)
{
case IDENTITY_SERVER_INDEX:
{
MXKTableViewCell *isCell = [self getDefaultTableViewCell:tableView];
if (account.mxSession.identityService.identityServer)
{
isCell.textLabel.text = account.mxSession.identityService.identityServer;
}
else
{
isCell.textLabel.text = [VectorL10n settingsIdentityServerNoIs];
}
[isCell vc_setAccessoryDisclosureIndicatorWithCurrentTheme];
cell = isCell;
break;
}
default:
break;
}
}
else if (section == SECTION_TAG_INTEGRATIONS)
{
switch (row) {
case INTEGRATIONS_INDEX:
{
RiotSharedSettings *sharedSettings = [[RiotSharedSettings alloc] initWithSession:session];
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsIntegrationsAllowButton];
labelAndSwitchCell.mxkSwitch.on = sharedSettings.hasIntegrationProvisioningEnabled;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleAllowIntegrations:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
break;
}
default:
break;
}
}
else if (section == SECTION_TAG_USER_INTERFACE)
{
if (row == USER_INTERFACE_LANGUAGE_INDEX)
{
cell = [tableView dequeueReusableCellWithIdentifier:kSettingsViewControllerPhoneBookCountryCellId];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:kSettingsViewControllerPhoneBookCountryCellId];
}
NSString *language = [NSBundle mxk_language];
if (!language)
{
language = [MXKLanguagePickerViewController defaultLanguage];
}
NSString *languageDescription = [MXKLanguagePickerViewController languageDescription:language];
// Capitalise the description in the language locale
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:language];
languageDescription = [languageDescription capitalizedStringWithLocale:locale];
cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
cell.textLabel.text = [VectorL10n settingsUiLanguage];
cell.detailTextLabel.text = languageDescription;
[cell vc_setAccessoryDisclosureIndicatorWithCurrentTheme];
cell.selectionStyle = UITableViewCellSelectionStyleDefault;
}
else if (row == USER_INTERFACE_THEME_INDEX)
{
cell = [tableView dequeueReusableCellWithIdentifier:kSettingsViewControllerPhoneBookCountryCellId];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:kSettingsViewControllerPhoneBookCountryCellId];
}
NSString *theme = RiotSettings.shared.userInterfaceTheme;
if (!theme)
{
theme = @"auto";
}
theme = [NSString stringWithFormat:@"settings_ui_theme_%@", theme];
NSString *i18nTheme = NSLocalizedStringFromTable(theme, @"Vector", nil);
cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
cell.textLabel.text = [VectorL10n settingsUiTheme];
cell.detailTextLabel.text = i18nTheme;
[cell vc_setAccessoryDisclosureIndicatorWithCurrentTheme];
cell.selectionStyle = UITableViewCellSelectionStyleDefault;
}
}
else if (section == SECTION_TAG_TIMELINE)
{
if (row == TIMELINE_STYLE_INDEX)
{
cell = [self buildMessageBubblesCellForTableView:tableView atIndexPath:indexPath];
}
else if (row == TIMELINE_SHOW_REDACTIONS_IN_ROOM_HISTORY_INDEX)
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = VectorL10n.settingsUiShowRedactionsInRoomHistory;
labelAndSwitchCell.mxkSwitch.on = [MXKAppSettings standardAppSettings].showRedactionsInRoomHistory;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleShowRedacted:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
else if (row == TIMELINE_USE_ONLY_LATEST_USER_AVATAR_AND_NAME_INDEX)
{
MXKTableViewCellWithLabelAndSwitch *labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = VectorL10n.settingsLabsUseOnlyLatestUserAvatarAndName;
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.roomScreenUseOnlyLatestUserAvatarAndName;
labelAndSwitchCell.mxkSwitch.enabled = YES;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleUseOnlyLatestUserAvatarAndName:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
}
else if (section == SECTION_TAG_IGNORED_USERS)
{
MXKTableViewCell *ignoredUserCell = [self getDefaultTableViewCell:tableView];
ignoredUserCell.textLabel.text = session.ignoredUsers[row];
cell = ignoredUserCell;
}
else if (section == SECTION_TAG_LOCAL_CONTACTS)
{
if (row == LOCAL_CONTACTS_SYNC_INDEX)
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.numberOfLines = 0;
labelAndSwitchCell.mxkLabel.text = VectorL10n.settingsContactsEnableSync;
labelAndSwitchCell.mxkSwitch.on = [MXKAppSettings standardAppSettings].syncLocalContacts;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleLocalContactsSync:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
else if (row == LOCAL_CONTACTS_PHONEBOOK_COUNTRY_INDEX)
{
cell = [tableView dequeueReusableCellWithIdentifier:kSettingsViewControllerPhoneBookCountryCellId];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:kSettingsViewControllerPhoneBookCountryCellId];
}
NSString* countryCode = [[MXKAppSettings standardAppSettings] phonebookCountryCode];
NSLocale *local = [[NSLocale alloc] initWithLocaleIdentifier:[[NSBundle mainBundle] preferredLocalizations][0]];
NSString *countryName = [local displayNameForKey:NSLocaleCountryCode value:countryCode];
cell.textLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
cell.textLabel.text = [VectorL10n settingsContactsPhonebookCountry];
cell.detailTextLabel.text = countryName;
[cell vc_setAccessoryDisclosureIndicatorWithCurrentTheme];
cell.selectionStyle = UITableViewCellSelectionStyleDefault;
}
}
else if (section == SECTION_TAG_PRESENCE)
{
if (row == PRESENCE_OFFLINE_MODE)
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = VectorL10n.settingsPresenceOfflineMode;
MXKAccount *account = MXKAccountManager.sharedManager.accounts.firstObject;
labelAndSwitchCell.mxkSwitch.on = account.preferredSyncPresence == MXPresenceOffline;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = YES;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(togglePresenceOfflineMode:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
}
else if (section == SECTION_TAG_ADVANCED)
{
if (row == ADVANCED_CRASH_REPORT_INDEX)
{
MXKTableViewCellWithLabelAndSwitch* sendCrashReportCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
sendCrashReportCell.mxkLabel.text = VectorL10n.settingsAnalyticsAndCrashData;
sendCrashReportCell.mxkSwitch.on = RiotSettings.shared.enableAnalytics;
sendCrashReportCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
sendCrashReportCell.mxkSwitch.enabled = YES;
[sendCrashReportCell.mxkSwitch addTarget:self action:@selector(toggleAnalytics:) forControlEvents:UIControlEventTouchUpInside];
cell = sendCrashReportCell;
}
else if (row == ADVANCED_ENABLE_RAGESHAKE_INDEX)
{
MXKTableViewCellWithLabelAndSwitch* enableRageShakeCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
enableRageShakeCell.mxkLabel.text = [VectorL10n settingsEnableRageshake];
enableRageShakeCell.mxkSwitch.on = RiotSettings.shared.enableRageShake;
enableRageShakeCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
enableRageShakeCell.mxkSwitch.enabled = YES;
[enableRageShakeCell.mxkSwitch addTarget:self action:@selector(toggleEnableRageShake:) forControlEvents:UIControlEventTouchUpInside];
cell = enableRageShakeCell;
}
else if (row == ADVANCED_MARK_ALL_AS_READ_INDEX)
{
MXKTableViewCellWithButton *markAllBtnCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]];
if (!markAllBtnCell)
{
markAllBtnCell = [[MXKTableViewCellWithButton alloc] init];
}
else
{
// Fix https://github.com/vector-im/riot-ios/issues/1354
markAllBtnCell.mxkButton.titleLabel.text = nil;
}
NSString *btnTitle = [VectorL10n settingsMarkAllAsRead];
[markAllBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal];
[markAllBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateHighlighted];
[markAllBtnCell.mxkButton setTintColor:ThemeService.shared.theme.tintColor];
markAllBtnCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17];
[markAllBtnCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside];
[markAllBtnCell.mxkButton addTarget:self action:@selector(markAllAsRead:) forControlEvents:UIControlEventTouchUpInside];
markAllBtnCell.mxkButton.accessibilityIdentifier = nil;
cell = markAllBtnCell;
}
else if (row == ADVANCED_CLEAR_CACHE_INDEX)
{
MXKTableViewCellWithButton *clearCacheBtnCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]];
if (!clearCacheBtnCell)
{
clearCacheBtnCell = [[MXKTableViewCellWithButton alloc] init];
}
else
{
// Fix https://github.com/vector-im/riot-ios/issues/1354
clearCacheBtnCell.mxkButton.titleLabel.text = nil;
}
NSString *btnTitle = [VectorL10n settingsClearCache];
[clearCacheBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal];
[clearCacheBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateHighlighted];
[clearCacheBtnCell.mxkButton setTintColor:ThemeService.shared.theme.tintColor];
clearCacheBtnCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17];
[clearCacheBtnCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside];
[clearCacheBtnCell.mxkButton addTarget:self action:@selector(clearCache:) forControlEvents:UIControlEventTouchUpInside];
clearCacheBtnCell.mxkButton.accessibilityIdentifier = nil;
cell = clearCacheBtnCell;
}
else if (row == ADVANCED_REPORT_BUG_INDEX)
{
MXKTableViewCellWithButton *reportBugBtnCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]];
if (!reportBugBtnCell)
{
reportBugBtnCell = [[MXKTableViewCellWithButton alloc] init];
}
else
{
// Fix https://github.com/vector-im/riot-ios/issues/1354
reportBugBtnCell.mxkButton.titleLabel.text = nil;
}
NSString *btnTitle = [VectorL10n settingsReportBug];
[reportBugBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal];
[reportBugBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateHighlighted];
[reportBugBtnCell.mxkButton setTintColor:ThemeService.shared.theme.tintColor];
reportBugBtnCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17];
[reportBugBtnCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside];
[reportBugBtnCell.mxkButton addTarget:self action:@selector(reportBug:) forControlEvents:UIControlEventTouchUpInside];
reportBugBtnCell.mxkButton.accessibilityIdentifier = nil;
cell = reportBugBtnCell;
}
}
else if (section == SECTION_TAG_ABOUT)
{
if (row == ABOUT_ACCEPTABLE_USE_INDEX)
{
MXKTableViewCell *termAndConditionCell = [self getDefaultTableViewCell:tableView];
termAndConditionCell.textLabel.text = [VectorL10n settingsAcceptableUse];
[termAndConditionCell vc_setAccessoryDisclosureIndicatorWithCurrentTheme];
cell = termAndConditionCell;
}
else if (row == ABOUT_COPYRIGHT_INDEX)
{
MXKTableViewCell *copyrightCell = [self getDefaultTableViewCell:tableView];
copyrightCell.textLabel.text = [VectorL10n settingsCopyright];
[copyrightCell vc_setAccessoryDisclosureIndicatorWithCurrentTheme];
cell = copyrightCell;
}
else if (row == ABOUT_PRIVACY_INDEX)
{
MXKTableViewCell *privacyPolicyCell = [self getDefaultTableViewCell:tableView];
privacyPolicyCell.textLabel.text = [VectorL10n settingsPrivacyPolicy];
[privacyPolicyCell vc_setAccessoryDisclosureIndicatorWithCurrentTheme];
cell = privacyPolicyCell;
}
else if (row == ABOUT_THIRD_PARTY_INDEX)
{
MXKTableViewCell *thirdPartyCell = [self getDefaultTableViewCell:tableView];
thirdPartyCell.textLabel.text = [VectorL10n settingsThirdPartyNotices];
[thirdPartyCell vc_setAccessoryDisclosureIndicatorWithCurrentTheme];
cell = thirdPartyCell;
}
}
else if (section == SECTION_TAG_LABS)
{
if (row == LABS_ENABLE_RINGING_FOR_GROUP_CALLS_INDEX)
{
MXKTableViewCellWithLabelAndSwitch *labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsLabsEnableRingingForGroupCalls];
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.enableRingingForGroupCalls;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableRingingForGroupCalls:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
else if (row == LABS_ENABLE_THREADS_INDEX)
{
MXKTableViewCellWithLabelAndSwitch *labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsLabsEnableThreads];
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.enableThreads;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableThreads:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
else if (row == LABS_ENABLE_AUTO_REPORT_DECRYPTION_ERRORS)
{
cell = [self buildAutoReportDecryptionErrorsCellForTableView:tableView atIndexPath:indexPath];
}
else if (row == LABS_ENABLE_LIVE_LOCATION_SHARING)
{
cell = [self buildLiveLocationSharingCellForTableView:tableView atIndexPath:indexPath];
}
else if (row == LABS_ENABLE_NEW_SESSION_MANAGER)
{
MXKTableViewCellWithLabelAndSwitch *labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsLabsEnableNewSessionManager];
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.enableNewSessionManager;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableNewSessionManager:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
else if (row == LABS_ENABLE_NEW_CLIENT_INFO_FEATURE)
{
MXKTableViewCellWithLabelAndSwitch *labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsLabsEnableNewClientInfoFeature];
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.enableClientInformationFeature;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableNewClientInfoFeature:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
else if (row == LABS_ENABLE_WYSIWYG_COMPOSER)
{
MXKTableViewCellWithLabelAndSwitch *labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsLabsEnableWysiwygComposer];
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.enableWysiwygComposer;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableWysiwygComposerFeature:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
else if (row == LABS_ENABLE_VOICE_BROADCAST)
{
MXKTableViewCellWithLabelAndSwitch *labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = [VectorL10n settingsLabsEnableVoiceBroadcast];
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.enableVoiceBroadcast;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableVoiceBroadcastFeature:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
}
else if (section == SECTION_TAG_SECURITY)
{
switch (row)
{
case SECURITY_BUTTON_INDEX:
cell = [self getDefaultTableViewCell:tableView];
cell.textLabel.text = [VectorL10n securitySettingsTitle];
[cell vc_setAccessoryDisclosureIndicatorWithCurrentTheme];
break;
case DEVICE_MANAGER_INDEX:
cell = [self getDefaultTableViewCell:tableView];
cell.textLabel.text = [VectorL10n userSessionsSettings];
[cell vc_setAccessoryDisclosureIndicatorWithCurrentTheme];
break;
}
}
else if (section == SECTION_TAG_DEACTIVATE_ACCOUNT)
{
MXKTableViewCellWithButton *deactivateAccountBtnCell = [tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]];
if (!deactivateAccountBtnCell)
{
deactivateAccountBtnCell = [[MXKTableViewCellWithButton alloc] init];
}
else
{
// Fix https://github.com/vector-im/riot-ios/issues/1354
deactivateAccountBtnCell.mxkButton.titleLabel.text = nil;
}
NSString *btnTitle = [VectorL10n settingsDeactivateMyAccount];
[deactivateAccountBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateNormal];
[deactivateAccountBtnCell.mxkButton setTitle:btnTitle forState:UIControlStateHighlighted];
[deactivateAccountBtnCell.mxkButton setTintColor:ThemeService.shared.theme.warningColor];
deactivateAccountBtnCell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17];
[deactivateAccountBtnCell.mxkButton removeTarget:self action:nil forControlEvents:UIControlEventTouchUpInside];
[deactivateAccountBtnCell.mxkButton addTarget:self action:@selector(deactivateAccountAction) forControlEvents:UIControlEventTouchUpInside];
deactivateAccountBtnCell.mxkButton.accessibilityIdentifier = nil;
cell = deactivateAccountBtnCell;
}
else if (section == SECTION_TAG_ACCOUNT)
{
switch (row)
{
case ACCOUNT_MANAGE_INDEX:
cell = [self getDefaultTableViewCell:tableView];
cell.textLabel.text = [VectorL10n settingsManageAccountAction];
[cell vc_setAccessoryDisclosureIndicatorWithCurrentTheme];
break;
}
}
return cell;
}
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
Section *sectionObj = [_tableViewSections sectionAtIndex:section];
return sectionObj.headerTitle;
}
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
{
if ([view isKindOfClass:UITableViewHeaderFooterView.class])
{
// Customize label style
UITableViewHeaderFooterView *tableViewHeaderFooterView = (UITableViewHeaderFooterView*)view;
tableViewHeaderFooterView.textLabel.textColor = ThemeService.shared.theme.colors.secondaryContent;
tableViewHeaderFooterView.textLabel.font = ThemeService.shared.theme.fonts.footnote;
}
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
NSAttributedString *attributedFooterTitle = [_tableViewSections sectionAtIndex:section].attributedFooterTitle;
if (!attributedFooterTitle)
{
return nil;
}
SectionFooterView *view = [tableView dequeueReusableHeaderFooterViewWithIdentifier:SectionFooterView.defaultReuseIdentifier];
[view updateWithTheme:ThemeService.shared.theme];
view.leadingInset = tableView.vc_separatorInset.left;
[view updateWithAttributedText:attributedFooterTitle];
if (section == SECTION_TAG_USER_SETTINGS)
{
UIGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(scrollToDiscoverySection)];
[view addGestureRecognizer:recognizer];
}
else if (section == SECTION_TAG_DISCOVERY && self.settingsDiscoveryTableViewSection.footerShouldScrollToUserSettings)
{
UIGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(scrollToUserSettingsSection)];
[view addGestureRecognizer:recognizer];
}
return view;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
NSIndexPath *tagsIndexPath = [_tableViewSections tagsIndexPathFromTableViewIndexPath:indexPath];
NSInteger section = tagsIndexPath.section;
NSInteger row = tagsIndexPath.row;
if (section == SECTION_TAG_USER_SETTINGS)
{
return row >= USER_SETTINGS_PHONENUMBERS_OFFSET;
}
return NO;
}
- (void)tableView:(UITableView*)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath*)indexPath
{
// iOS8 requires this method to enable editing (see editActionsForRowAtIndexPath).
}
#pragma mark - UITableView delegate
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;
{
cell.backgroundColor = ThemeService.shared.theme.backgroundColor;
if (cell.selectionStyle != UITableViewCellSelectionStyleNone)
{
// Update the selected background view
if (ThemeService.shared.theme.selectedBackgroundColor)
{
cell.selectedBackgroundView = [[UIView alloc] init];
cell.selectedBackgroundView.backgroundColor = ThemeService.shared.theme.selectedBackgroundColor;
}
else
{
if (tableView.style == UITableViewStylePlain)
{
cell.selectedBackgroundView = nil;
}
else
{
cell.selectedBackgroundView.backgroundColor = nil;
}
}
}
}
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSIndexPath *tagsIndexPath = [_tableViewSections tagsIndexPathFromTableViewIndexPath:indexPath];
NSInteger section = tagsIndexPath.section;
NSInteger row = tagsIndexPath.row;
NSMutableArray* actions;
// Add the swipe to delete user's email or phone number
if (section == SECTION_TAG_USER_SETTINGS)
{
if (row >= USER_SETTINGS_PHONENUMBERS_OFFSET)
{
actions = [[NSMutableArray alloc] init];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
CGFloat cellHeight = cell ? cell.frame.size.height : 50;
// Patch: Force the width of the button by adding whitespace characters into the title string.
UITableViewRowAction *leaveAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive title:@" " handler:^(UITableViewRowAction *action, NSIndexPath *indexPath){
[self onRemove3PID:indexPath];
}];
leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"remove_icon_pink" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(50, cellHeight) resourceSize:CGSizeMake(24, 24)];
[actions insertObject:leaveAction atIndex:0];
}
}
return actions;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.tableView == tableView)
{
NSIndexPath *tagsIndexPath = [_tableViewSections tagsIndexPathFromTableViewIndexPath:indexPath];
NSInteger section = tagsIndexPath.section;
NSInteger row = tagsIndexPath.row;
if (section == SECTION_TAG_USER_INTERFACE)
{
if (row == USER_INTERFACE_LANGUAGE_INDEX)
{
// Display the language picker
LanguagePickerViewController *languagePickerViewController = [LanguagePickerViewController languagePickerViewController];
languagePickerViewController.selectedLanguage = [NSBundle mxk_language];
languagePickerViewController.delegate = self;
[self pushViewController:languagePickerViewController];
}
else if (row == USER_INTERFACE_THEME_INDEX)
{
[self showThemePicker];
}
}
else if (section == SECTION_TAG_NOTIFICATIONS && row == NOTIFICATION_SETTINGS_SYSTEM_SETTINGS)
{
[self openSystemSettingsApp];
}
else if (section == SECTION_TAG_DISCOVERY)
{
[self.settingsDiscoveryTableViewSection selectRow:row];
}
else if (section == SECTION_TAG_IDENTITY_SERVER)
{
switch (row)
{
case IDENTITY_SERVER_INDEX:
[self showIdentityServerSettingsScreen];
break;
}
}
else if (section == SECTION_TAG_IGNORED_USERS)
{
MXSession* session = self.mainSession;
NSString *ignoredUserId = session.ignoredUsers[row];
if (ignoredUserId)
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
__weak typeof(self) weakSelf = self;
UIAlertController *unignorePrompt = [UIAlertController alertControllerWithTitle:[VectorL10n settingsUnignoreUser:ignoredUserId] message:nil preferredStyle:UIAlertControllerStyleAlert];
[unignorePrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n yes]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
MXSession* session = self.mainSession;
// Remove the member from the ignored user list
[self startActivityIndicator];
[session unIgnoreUsers:@[ignoredUserId] success:^{
[self stopActivityIndicator];
} failure:^(NSError *error) {
[self stopActivityIndicator];
MXLogDebug(@"[SettingsViewController] Unignore %@ failed", ignoredUserId);
NSString *myUserId = session.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}];
}
}]];
[unignorePrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n no]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
}
}]];
[unignorePrompt mxk_setAccessibilityIdentifier: @"SettingsVCUnignoreAlert"];
[self presentViewController:unignorePrompt animated:YES completion:nil];
currentAlert = unignorePrompt;
}
}
else if (section == SECTION_TAG_ABOUT)
{
if (row == ABOUT_COPYRIGHT_INDEX)
{
WebViewViewController *webViewViewController = [[WebViewViewController alloc] initWithURL:BuildSettings.applicationCopyrightUrlString];
webViewViewController.title = [VectorL10n settingsCopyright];
[webViewViewController vc_setLargeTitleDisplayMode:UINavigationItemLargeTitleDisplayModeNever];
[self pushViewController:webViewViewController];
}
else if (row == ABOUT_ACCEPTABLE_USE_INDEX)
{
WebViewViewController *webViewViewController = [[WebViewViewController alloc] initWithURL:BuildSettings.applicationAcceptableUsePolicyUrlString];
webViewViewController.title = [VectorL10n settingsAcceptableUse];
[webViewViewController vc_setLargeTitleDisplayMode:UINavigationItemLargeTitleDisplayModeNever];
[self pushViewController:webViewViewController];
}
else if (row == ABOUT_PRIVACY_INDEX)
{
WebViewViewController *webViewViewController = [[WebViewViewController alloc] initWithURL:BuildSettings.applicationPrivacyPolicyUrlString];
webViewViewController.title = [VectorL10n settingsPrivacyPolicy];
[webViewViewController vc_setLargeTitleDisplayMode:UINavigationItemLargeTitleDisplayModeNever];
[self pushViewController:webViewViewController];
}
else if (row == ABOUT_THIRD_PARTY_INDEX)
{
NSString *htmlFile = [[NSBundle mainBundle] pathForResource:@"third_party_licenses" ofType:@"html" inDirectory:nil];
WebViewViewController *webViewViewController = [[WebViewViewController alloc] initWithLocalHTMLFile:htmlFile];
webViewViewController.title = [VectorL10n settingsThirdPartyNotices];
[webViewViewController vc_setLargeTitleDisplayMode:UINavigationItemLargeTitleDisplayModeNever];
[self pushViewController:webViewViewController];
}
}
else if (section == SECTION_TAG_USER_SETTINGS)
{
if (row == USER_SETTINGS_PROFILE_PICTURE_INDEX)
{
[self onProfileAvatarTap:nil];
}
else if (row == USER_SETTINGS_CHANGE_PASSWORD_INDEX)
{
[self displayPasswordAlert];
}
else if (row == USER_SETTINGS_ADD_EMAIL_INDEX)
{
if (!self.newEmailEditingEnabled)
{
// Enable the new email text field
self.newEmailEditingEnabled = YES;
}
else if (newEmailTextField)
{
[self onAddNewEmail:newEmailTextField];
}
}
else if (row == USER_SETTINGS_ADD_PHONENUMBER_INDEX)
{
if (!self.newPhoneEditingEnabled)
{
// Enable the new phone text field
self.newPhoneEditingEnabled = YES;
}
else if (newPhoneNumberCell.mxkTextField)
{
[self onAddNewPhone:newPhoneNumberCell.mxkTextField];
}
}
}
else if (section == SECTION_TAG_LOCAL_CONTACTS)
{
if (row == LOCAL_CONTACTS_PHONEBOOK_COUNTRY_INDEX)
{
CountryPickerViewController *countryPicker = [CountryPickerViewController countryPickerViewController];
countryPicker.view.tag = SECTION_TAG_LOCAL_CONTACTS;
countryPicker.delegate = self;
countryPicker.showCountryCallingCode = YES;
[self pushViewController:countryPicker];
}
}
else if (section == SECTION_TAG_SECURITY)
{
switch (row)
{
case SECURITY_BUTTON_INDEX:
{
SecurityViewController *securityViewController = [SecurityViewController instantiateWithMatrixSession:self.mainSession];
[self pushViewController:securityViewController];
break;
}
case DEVICE_MANAGER_INDEX:
{
[self showUserSessionsFlow];
break;
}
}
}
else if (section == SECTION_TAG_NOTIFICATIONS)
{
switch (row) {
case NOTIFICATION_SETTINGS_DEFAULT_SETTINGS_INDEX:
[self showNotificationSettings:NotificationSettingsScreenDefaultNotifications];
break;
case NOTIFICATION_SETTINGS_MENTION_AND_KEYWORDS_SETTINGS_INDEX:
[self showNotificationSettings:NotificationSettingsScreenMentionsAndKeywords];
break;
case NOTIFICATION_SETTINGS_OTHER_SETTINGS_INDEX:
[self showNotificationSettings:NotificationSettingsScreenOther];
break;
}
}
else if (section == SECTION_TAG_ACCOUNT)
{
switch(row) {
case ACCOUNT_MANAGE_INDEX:
[self onManageAccountTap];
break;
}
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
}
#pragma mark - actions
- (void)onSignout:(id)sender
{
self.signOutButton = (UIButton*)sender;
SignOutFlowPresenter *flowPresenter = [[SignOutFlowPresenter alloc] initWithSession:self.mainSession presentingViewController:self];
flowPresenter.delegate = self;
[flowPresenter startWithSourceView:self.signOutButton];
self.signOutFlowPresenter = flowPresenter;
}
- (void)onRemove3PID:(NSIndexPath*)indexPath
{
NSIndexPath *tagsIndexPath = [_tableViewSections tagsIndexPathFromTableViewIndexPath:indexPath];
NSInteger section = tagsIndexPath.section;
NSInteger row = tagsIndexPath.row;
if (section == SECTION_TAG_USER_SETTINGS)
{
NSString *address, *medium;
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
NSString *promptMsg;
if (row >= USER_SETTINGS_EMAILS_OFFSET)
{
medium = kMX3PIDMediumEmail;
row = row - USER_SETTINGS_EMAILS_OFFSET;
NSArray<NSString *> *linkedEmails = account.linkedEmails;
if (row < linkedEmails.count)
{
address = linkedEmails[row];
promptMsg = [VectorL10n settingsRemoveEmailPromptMsg:address];
}
}
else if (row >= USER_SETTINGS_PHONENUMBERS_OFFSET)
{
medium = kMX3PIDMediumMSISDN;
row = row - USER_SETTINGS_PHONENUMBERS_OFFSET;
NSArray<NSString *> *linkedPhones = account.linkedPhoneNumbers;
if (row < linkedPhones.count)
{
address = linkedPhones[row];
NSString *e164 = [NSString stringWithFormat:@"+%@", address];
NBPhoneNumber *phoneNb = [[NBPhoneNumberUtil sharedInstance] parse:e164 defaultRegion:nil error:nil];
NSString *phoneNumber = [[NBPhoneNumberUtil sharedInstance] format:phoneNb numberFormat:NBEPhoneNumberFormatINTERNATIONAL error:nil];
promptMsg = [VectorL10n settingsRemovePhonePromptMsg:phoneNumber];
}
}
if (address && medium)
{
__weak typeof(self) weakSelf = self;
if (currentAlert)
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
currentAlert = nil;
}
// Remove ?
UIAlertController *removePrompt = [UIAlertController alertControllerWithTitle:[VectorL10n settingsRemovePromptTitle] message:promptMsg preferredStyle:UIAlertControllerStyleAlert];
[removePrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
}
}]];
[removePrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n remove]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
[self startActivityIndicator];
[self.mainSession.matrixRestClient remove3PID:address medium:medium success:^{
if (weakSelf)
{
typeof(self) self = weakSelf;
[self stopActivityIndicator];
// Update linked 3pids
[self loadAccount3PIDs];
}
} failure:^(NSError *error) {
MXLogDebug(@"[SettingsViewController] Remove 3PID: %@ failed", address);
if (weakSelf)
{
typeof(self) self = weakSelf;
[self stopActivityIndicator];
NSString *myUserId = self.mainSession.myUser.userId; // TODO: Hanlde multi-account
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}
}];
}
}]];
[removePrompt mxk_setAccessibilityIdentifier: @"SettingsVCRemove3PIDAlert"];
[self presentViewController:removePrompt animated:YES completion:nil];
currentAlert = removePrompt;
}
}
}
- (void)toggleConfirmMediaSize:(UISwitch *)sender
{
RiotSettings.shared.showMediaCompressionPrompt = sender.on;
}
- (void)togglePushNotifications:(UISwitch *)sender
{
// Check first whether the user allow notification from system settings
if (self.systemNotificationSettings.authorizationStatus == UNAuthorizationStatusDenied)
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
__weak typeof(self) weakSelf = self;
NSString *title = [VectorL10n settingsNotificationsDisabledAlertTitle];
NSString *message = [VectorL10n settingsNotificationsDisabledAlertMessage];
UIAlertController *showSettingsPrompt = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
[showSettingsPrompt addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
}
}]];
UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:[VectorL10n settings]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
[self openSystemSettingsApp];
}
}];
[showSettingsPrompt addAction:settingsAction];
showSettingsPrompt.preferredAction = settingsAction;
[showSettingsPrompt mxk_setAccessibilityIdentifier: @"SettingsVCPushNotificationsAlert"];
[self presentViewController:showSettingsPrompt animated:YES completion:nil];
currentAlert = showSettingsPrompt;
// Keep the the switch off.
sender.on = NO;
}
else if ([MXKAccountManager sharedManager].activeAccounts.count)
{
[self startActivityIndicator];
MXKAccountManager *accountManager = [MXKAccountManager sharedManager];
MXKAccount* account = accountManager.activeAccounts.firstObject;
if (accountManager.apnsDeviceToken)
{
[account enablePushNotifications:!account.pushNotificationServiceIsActive success:^{
[self stopActivityIndicator];
} failure:^(NSError *error) {
[self stopActivityIndicator];
}];
}
else
{
// Obtain device token when user has just enabled access to notifications from system settings
[[AppDelegate theDelegate] registerForRemoteNotificationsWithCompletion:^(NSError * error) {
if (error)
{
[sender setOn:NO animated:YES];
[self stopActivityIndicator];
}
else
{
[account enablePushNotifications:YES success:^{
[self stopActivityIndicator];
} failure:^(NSError *error) {
[self stopActivityIndicator];
}];
}
}];
}
}
}
- (void)toggleShowInAppNotifications:(UISwitch *)sender
{
RiotSettings.shared.showInAppNotifications = sender.isOn;
}
- (void)openSystemSettingsApp
{
NSURL *settingsAppURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
[[UIApplication sharedApplication] openURL:settingsAppURL options:@{} completionHandler:nil];
}
- (void)toggleCallKit:(UISwitch *)sender
{
[MXKAppSettings standardAppSettings].enableCallKit = sender.isOn;
}
- (void)toggleStunServerFallback:(UISwitch *)sender
{
RiotSettings.shared.allowStunServerFallback = sender.isOn;
self.mainSession.callManager.fallbackSTUNServer = RiotSettings.shared.allowStunServerFallback ? BuildSettings.stunServerFallbackUrlString : nil;
}
- (void)toggleAllowIntegrations:(UISwitch *)sender
{
MXSession *session = self.mainSession;
[self startActivityIndicator];
__block RiotSharedSettings *sharedSettings = [[RiotSharedSettings alloc] initWithSession:session];
[sharedSettings setIntegrationProvisioningWithEnabled:sender.isOn success:^{
sharedSettings = nil;
[self stopActivityIndicator];
} failure:^(NSError * _Nullable error) {
sharedSettings = nil;
[sender setOn:!sender.isOn animated:YES];
[self stopActivityIndicator];
}];
}
- (void)toggleShowDecodedContent:(UISwitch *)sender
{
RiotSettings.shared.showDecryptedContentInNotifications = sender.isOn;
}
- (void)toggleLocalContactsSync:(UISwitch *)sender
{
if (sender.on)
{
// First check if the service terms have already been accepted
MXSession *session = self.mxSessions.firstObject;
if (session.identityService.areAllTermsAgreed)
{
// If they have we only require local contacts access.
[self checkAccessForContacts];
}
else
{
[self prepareIdentityServiceAndPresentTermsWithSession:session checkingAccessForContactsOnAccept:YES];
}
}
else
{
[MXKAppSettings standardAppSettings].syncLocalContacts = NO;
[self updateSections];
}
}
- (void)toggleEnableURLPreviews:(UISwitch *)sender
{
RiotSettings.shared.roomScreenShowsURLPreviews = sender.on;
// Any loaded cell data is now invalid and should be refreshed for the new value.
[[MXKRoomDataSourceManager sharedManagerForMatrixSession:self.mainSession] reset];
}
- (void)toggleAnalytics:(UISwitch *)sender
{
if (sender.isOn)
{
MXLogDebug(@"[SettingsViewController] enable automatic crash report and analytics sending");
[Analytics.shared optInWith:self.mainSession];
}
else
{
MXLogDebug(@"[SettingsViewController] disable automatic crash report and analytics sending");
[Analytics.shared optOut];
// Remove potential crash file.
[MXLogger deleteCrashLog];
}
}
- (void)toggleEnableRageShake:(UISwitch *)sender
{
RiotSettings.shared.enableRageShake = sender.isOn;
[self updateSections];
}
- (void)toggleEnableRingingForGroupCalls:(UISwitch *)sender
{
RiotSettings.shared.enableRingingForGroupCalls = sender.isOn;
}
- (void)toggleEnableThreads:(UISwitch *)sender
{
if (sender.isOn && !self.mainSession.store.supportedMatrixVersions.supportsThreads)
{
// user wants to turn on the threads setting but the server does not support it
if (self.threadsBetaBridgePresenter)
{
[self.threadsBetaBridgePresenter dismissWithAnimated:YES completion:nil];
self.threadsBetaBridgePresenter = nil;
}
self.threadsBetaBridgePresenter = [[ThreadsBetaCoordinatorBridgePresenter alloc] initWithThreadId:@""
infoText:VectorL10n.threadsDiscourageInformation1
additionalText:VectorL10n.threadsDiscourageInformation2];
self.threadsBetaBridgePresenter.delegate = self;
[self.threadsBetaBridgePresenter presentFrom:self.presentedViewController?:self animated:YES];
return;
}
[self enableThreads:sender.isOn];
}
- (void)enableThreads:(BOOL)enable
{
RiotSettings.shared.enableThreads = enable;
MXSDKOptions.sharedInstance.enableThreads = enable;
[[MXKRoomDataSourceManager sharedManagerForMatrixSession:self.mainSession] reset];
[[AppDelegate theDelegate] restoreEmptyDetailsViewController];
}
- (void)toggleEnableNewSessionManager:(UISwitch *)sender
{
RiotSettings.shared.enableNewSessionManager = sender.isOn;
[self updateSections];
}
- (void)toggleEnableNewClientInfoFeature:(UISwitch *)sender
{
BOOL isEnabled = sender.isOn;
RiotSettings.shared.enableClientInformationFeature = isEnabled;
MXSDKOptions.sharedInstance.enableNewClientInformationFeature = isEnabled;
[self.mainSession updateClientInformation];
}
- (void)toggleEnableWysiwygComposerFeature:(UISwitch *)sender
{
RiotSettings.shared.enableWysiwygComposer = sender.isOn;
}
- (void)toggleEnableVoiceBroadcastFeature:(UISwitch *)sender
{
RiotSettings.shared.enableVoiceBroadcast = sender.isOn;
}
- (void)togglePinRoomsWithMissedNotif:(UISwitch *)sender
{
RiotSettings.shared.pinRoomsWithMissedNotificationsOnHome = sender.isOn;
}
- (void)togglePinRoomsWithUnread:(UISwitch *)sender
{
RiotSettings.shared.pinRoomsWithUnreadMessagesOnHome = sender.on;
}
- (void)toggleUseOnlyLatestUserAvatarAndName:(UISwitch *)sender
{
RiotSettings.shared.roomScreenUseOnlyLatestUserAvatarAndName = sender.isOn;
}
- (void)markAllAsRead:(id)sender
{
// Feedback: disable button and run activity indicator
UIButton *button = (UIButton*)sender;
button.enabled = NO;
[self startActivityIndicator];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[[AppDelegate theDelegate] markAllMessagesAsRead];
[self stopActivityIndicator];
button.enabled = YES;
});
}
- (void)clearCache:(id)sender
{
// Feedback: disable button and run activity indicator
UIButton *button = (UIButton*)sender;
button.enabled = NO;
[self launchClearCache];
}
- (void)launchClearCache
{
[self startActivityIndicator];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[[AppDelegate theDelegate] reloadMatrixSessions:YES];
});
}
- (void)reportBug:(id)sender
{
BugReportViewController *bugReportViewController = [BugReportViewController bugReportViewController];
[bugReportViewController showInViewController:self];
}
- (void)selectPhoneNumberCountry:(id)sender
{
newPhoneNumberCountryPicker = [CountryPickerViewController countryPickerViewController];
newPhoneNumberCountryPicker.view.tag = SECTION_TAG_USER_SETTINGS;
newPhoneNumberCountryPicker.delegate = self;
newPhoneNumberCountryPicker.showCountryCallingCode = YES;
[self pushViewController:newPhoneNumberCountryPicker];
}
- (void)onSave:(id)sender
{
// sanity check
if ([MXKAccountManager sharedManager].activeAccounts.count == 0)
{
return;
}
self.navigationItem.rightBarButtonItem.enabled = NO;
[self startActivityIndicator];
isSavingInProgress = YES;
__weak typeof(self) weakSelf = self;
MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
MXMyUser* myUser = account.mxSession.myUser;
if (newDisplayName && ![myUser.displayname isEqualToString:newDisplayName])
{
// Save display name
[account setUserDisplayName:newDisplayName success:^{
if (weakSelf)
{
// Update the current displayname
typeof(self) self = weakSelf;
self->newDisplayName = nil;
// Go to the next change saving step
[self onSave:nil];
}
} failure:^(NSError *error) {
MXLogDebug(@"[SettingsViewController] Failed to set displayName");
if (weakSelf)
{
typeof(self) self = weakSelf;
[self handleErrorDuringProfileChangeSaving:error];
}
}];
return;
}
if (newAvatarImage)
{
// Retrieve the current picture and make sure its orientation is up
UIImage *updatedPicture = [MXKTools forceImageOrientationUp:newAvatarImage];
// Upload picture
MXMediaLoader *uploader = [MXMediaManager prepareUploaderWithMatrixSession:account.mxSession initialRange:0 andRange:1.0];
[uploader uploadData:UIImageJPEGRepresentation(updatedPicture, 0.5) filename:nil mimeType:@"image/jpeg" success:^(NSString *url) {
if (weakSelf)
{
typeof(self) self = weakSelf;
// Store uploaded picture url and trigger picture saving
self->uploadedAvatarURL = url;
self->newAvatarImage = nil;
[self onSave:nil];
}
} failure:^(NSError *error) {
MXLogDebug(@"[SettingsViewController] Failed to upload image");
if (weakSelf)
{
typeof(self) self = weakSelf;
[self handleErrorDuringProfileChangeSaving:error];
}
}];
return;
}
else if (uploadedAvatarURL)
{
[account setUserAvatarUrl:uploadedAvatarURL
success:^{
if (weakSelf)
{
typeof(self) self = weakSelf;
self->uploadedAvatarURL = nil;
[self onSave:nil];
}
}
failure:^(NSError *error) {
MXLogDebug(@"[SettingsViewController] Failed to set avatar url");
if (weakSelf)
{
typeof(self) self = weakSelf;
[self handleErrorDuringProfileChangeSaving:error];
}
}];
return;
}
// Backup is complete
isSavingInProgress = NO;
[self stopActivityIndicator];
// Check whether destroy has been called durign saving
if (onReadyToDestroyHandler)
{
// Ready to destroy
onReadyToDestroyHandler();
onReadyToDestroyHandler = nil;
}
else
{
[self updateSections];
}
}
- (void)handleErrorDuringProfileChangeSaving:(NSError*)error
{
// Sanity check: retrieve the current root view controller
UIViewController *rootViewController = [AppDelegate theDelegate].window.rootViewController;
if (rootViewController)
{
__weak typeof(self) weakSelf = self;
// Alert user
NSString *title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey];
if (!title)
{
title = [VectorL10n settingsFailToUpdateProfile];
}
NSString *msg = [error.userInfo valueForKey:NSLocalizedDescriptionKey];
[currentAlert dismissViewControllerAnimated:NO completion:nil];
UIAlertController *errorAlert = [UIAlertController alertControllerWithTitle:title message:msg preferredStyle:UIAlertControllerStyleAlert];
[errorAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
// Reset the updated displayname
self->newDisplayName = nil;
// Discard picture change
self->uploadedAvatarURL = nil;
self->newAvatarImage = nil;
// Loop to end saving
[self onSave:nil];
}
}]];
[errorAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n retry]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
// Loop to retry saving
[self onSave:nil];
}
}]];
[errorAlert mxk_setAccessibilityIdentifier: @"SettingsVCSaveChangesFailedAlert"];
[rootViewController presentViewController:errorAlert animated:YES completion:nil];
currentAlert = errorAlert;
}
}
- (IBAction)onAddNewEmail:(id)sender
{
// Ignore empty field
if (!newEmailTextField.text.length)
{
// Reset new email adding
self.newEmailEditingEnabled = NO;
return;
}
// Email check
if (![MXTools isEmailAddress:newEmailTextField.text])
{
__weak typeof(self) weakSelf = self;
[currentAlert dismissViewControllerAnimated:NO completion:nil];
UIAlertController *errorAlert = [UIAlertController alertControllerWithTitle:[VectorL10n accountErrorEmailWrongTitle]
message:[VectorL10n accountErrorEmailWrongDescription]
preferredStyle:UIAlertControllerStyleAlert];
[errorAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n ok]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
}
}]];
[errorAlert mxk_setAccessibilityIdentifier: @"SettingsVCAddEmailAlert"];
[self presentViewController:errorAlert animated:YES completion:nil];
currentAlert = errorAlert;
return;
}
// Dismiss the keyboard
[newEmailTextField resignFirstResponder];
MXSession* session = self.mainSession;
[self showAuthenticationIfNeededForAdding:kMX3PIDMediumEmail withSession:session completion:^(NSDictionary *authParams) {
[self startActivityIndicator];
__block MX3PidAddSession *thirdPidAddSession;
thirdPidAddSession = [session.threePidAddManager startAddEmailSessionWithEmail:self->newEmailTextField.text nextLink:nil success:^{
[self showValidationEmailDialogWithMessage:[VectorL10n accountEmailValidationMessage]
for3PidAddSession:thirdPidAddSession
threePidAddManager:session.threePidAddManager
authenticationParameters:authParams];
} failure:^(NSError * _Nonnull error) {
[self stopActivityIndicator];
MXLogDebug(@"[SettingsViewController] Failed to request email token");
// Translate the potential MX error.
MXError *mxError = [[MXError alloc] initWithNSError:error];
if (mxError
&& ([mxError.errcode isEqualToString:kMXErrCodeStringThreePIDInUse]
|| [mxError.errcode isEqualToString:kMXErrCodeStringServerNotTrusted]))
{
NSMutableDictionary *userInfo;
if (error.userInfo)
{
userInfo = [NSMutableDictionary dictionaryWithDictionary:error.userInfo];
}
else
{
userInfo = [NSMutableDictionary dictionary];
}
userInfo[NSLocalizedFailureReasonErrorKey] = nil;
if ([mxError.errcode isEqualToString:kMXErrCodeStringThreePIDInUse])
{
userInfo[NSLocalizedDescriptionKey] = [VectorL10n authEmailInUse];
userInfo[@"error"] = [VectorL10n authEmailInUse];
}
else
{
userInfo[NSLocalizedDescriptionKey] = [VectorL10n authUntrustedIdServer];
userInfo[@"error"] = [VectorL10n authUntrustedIdServer];
}
error = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo];
}
else if ([error.domain isEqualToString:MX3PidAddManagerErrorDomain]
&& error.code == MX3PidAddManagerErrorDomainIdentityServerRequired)
{
error = [NSError errorWithDomain:error.domain
code:error.code
userInfo:@{
NSLocalizedDescriptionKey: [VectorL10n authEmailIsRequired]
}];
}
// Notify user
NSString *myUserId = session.myUser.userId; // TODO: Hanlde multi-account
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}];
}];
}
- (IBAction)onAddNewPhone:(id)sender
{
// Ignore empty field
if (!newPhoneNumberCell.mxkTextField.text.length)
{
// Disable the new phone edition if the text field is empty
self.newPhoneEditingEnabled = NO;
return;
}
// Phone check
if (![[NBPhoneNumberUtil sharedInstance] isValidNumber:newPhoneNumber])
{
[currentAlert dismissViewControllerAnimated:NO completion:nil];
__weak typeof(self) weakSelf = self;
UIAlertController *errorAlert = [UIAlertController alertControllerWithTitle:[VectorL10n accountErrorMsisdnWrongTitle]
message:[VectorL10n accountErrorMsisdnWrongDescription]
preferredStyle:UIAlertControllerStyleAlert];
[errorAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n ok]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
}
}]];
[errorAlert mxk_setAccessibilityIdentifier: @"SettingsVCAddMsisdnAlert"];
[self presentViewController:errorAlert animated:YES completion:nil];
currentAlert = errorAlert;
return;
}
// Dismiss the keyboard
[newPhoneNumberCell.mxkTextField resignFirstResponder];
MXSession* session = self.mainSession;
NSString *e164 = [[NBPhoneNumberUtil sharedInstance] format:newPhoneNumber numberFormat:NBEPhoneNumberFormatE164 error:nil];
NSString *msisdn;
if ([e164 hasPrefix:@"+"])
{
msisdn = e164;
}
else if ([e164 hasPrefix:@"00"])
{
msisdn = [NSString stringWithFormat:@"+%@", [e164 substringFromIndex:2]];
}
NSString *countryCode = newPhoneNumberCell.isoCountryCode;
[self showAuthenticationIfNeededForAdding:kMX3PIDMediumMSISDN withSession:session completion:^(NSDictionary *authParams) {
[self startActivityIndicator];
__block MX3PidAddSession *new3Pid;
new3Pid = [session.threePidAddManager startAddPhoneNumberSessionWithPhoneNumber:msisdn countryCode:countryCode success:^{
[self showValidationMsisdnDialogWithMessage:[VectorL10n accountMsisdnValidationMessage] for3PidAddSession:new3Pid threePidAddManager:session.threePidAddManager authenticationParameters:authParams];
} failure:^(NSError *error) {
[self stopActivityIndicator];
MXLogDebug(@"[SettingsViewController] Failed to request msisdn token");
// Translate the potential MX error.
MXError *mxError = [[MXError alloc] initWithNSError:error];
if (mxError
&& ([mxError.errcode isEqualToString:kMXErrCodeStringThreePIDInUse]
|| [mxError.errcode isEqualToString:kMXErrCodeStringServerNotTrusted]))
{
NSMutableDictionary *userInfo;
if (error.userInfo)
{
userInfo = [NSMutableDictionary dictionaryWithDictionary:error.userInfo];
}
else
{
userInfo = [NSMutableDictionary dictionary];
}
userInfo[NSLocalizedFailureReasonErrorKey] = nil;
if ([mxError.errcode isEqualToString:kMXErrCodeStringThreePIDInUse])
{
userInfo[NSLocalizedDescriptionKey] = [VectorL10n authPhoneInUse];
userInfo[@"error"] = [VectorL10n authPhoneInUse];
}
else
{
userInfo[NSLocalizedDescriptionKey] = [VectorL10n authUntrustedIdServer];
userInfo[@"error"] = [VectorL10n authUntrustedIdServer];
}
error = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo];
}
else if ([error.domain isEqualToString:MX3PidAddManagerErrorDomain]
&& error.code == MX3PidAddManagerErrorDomainIdentityServerRequired)
{
error = [NSError errorWithDomain:error.domain
code:error.code
userInfo:@{
NSLocalizedDescriptionKey: [VectorL10n authPhoneIsRequired]
}];
}
// Notify user
NSString *myUserId = session.myUser.userId;
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
}];
}];
}
- (void)updateSaveButtonStatus
{
if ([AppDelegate theDelegate].mxSessions.count > 0)
{
MXSession* session = self.mainSession;
MXMyUser* myUser = session.myUser;
BOOL saveButtonEnabled = (nil != newAvatarImage);
if (!saveButtonEnabled)
{
if (newDisplayName)
{
saveButtonEnabled = ![myUser.displayname isEqualToString:newDisplayName];
}
}
self.navigationItem.rightBarButtonItem.enabled = saveButtonEnabled;
}
}
- (void)onProfileAvatarTap:(UITapGestureRecognizer *)recognizer
{
SingleImagePickerPresenter *singleImagePickerPresenter = [[SingleImagePickerPresenter alloc] initWithSession:self.mainSession];
singleImagePickerPresenter.delegate = self;
NSIndexPath *indexPath = [_tableViewSections exactIndexPathForRowTag:USER_SETTINGS_PROFILE_PICTURE_INDEX
sectionTag:SECTION_TAG_USER_SETTINGS];
if (indexPath)
{
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
UIView *sourceView = cell;
[singleImagePickerPresenter presentFrom:self sourceView:sourceView sourceRect:sourceView.bounds animated:YES];
self.imagePickerPresenter = singleImagePickerPresenter;
}
}
- (void)onManageAccountTap
{
NSURL *url = [NSURL URLWithString: self.mainSession.homeserverWellknown.authentication.account];
if (url) {
SSOAccountService *service = [[SSOAccountService alloc] initWithAccountURL:url];
SSOAuthenticationPresenter *presenter = [[SSOAuthenticationPresenter alloc] initWithSsoAuthenticationService:service];
presenter.delegate = self;
self.ssoAuthenticationPresenter = presenter;
[presenter presentForIdentityProvider:nil with:@"" from:self animated:YES];
}
}
- (void)showThemePicker
{
__weak typeof(self) weakSelf = self;
__block UIAlertAction *autoAction, *lightAction, *darkAction, *blackAction;
NSString *themePickerMessage;
void (^actionBlock)(UIAlertAction *action) = ^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
NSString *newTheme;
if (action == autoAction)
{
newTheme = @"auto";
}
else if (action == lightAction)
{
newTheme = @"light";
}
else if (action == darkAction)
{
newTheme = @"dark";
}
else if (action == blackAction)
{
newTheme = @"black";
}
NSString *theme = RiotSettings.shared.userInterfaceTheme;
if (newTheme && ![newTheme isEqualToString:theme])
{
// Clear fake Riot Avatars based on the previous theme.
[AvatarGenerator clear];
// The user wants to select this theme
RiotSettings.shared.userInterfaceTheme = newTheme;
ThemeService.shared.themeId = newTheme;
// This is a hack to force the background colour of the container view of the navigation controller
// This is needed only for hot theme update as the UIViewControllerWrapperView of the RioNavigationController is not updated
self.view.superview.backgroundColor = ThemeService.shared.theme.backgroundColor;
[self updateSections];
}
}
};
// Show "auto" only from iOS 11
autoAction = [UIAlertAction actionWithTitle:[VectorL10n settingsUiThemeAuto]
style:UIAlertActionStyleDefault
handler:actionBlock];
// Explain what is "auto"
if (@available(iOS 13, *))
{
// Observe application did become active for iOS appearance setting changes
themePickerMessage = [VectorL10n settingsUiThemePickerMessageMatchSystemTheme];
}
else
{
// Observe "Invert Colours" settings changes (available since iOS 11)
themePickerMessage = [VectorL10n settingsUiThemePickerMessageInvertColours];
}
lightAction = [UIAlertAction actionWithTitle:[VectorL10n settingsUiThemeLight]
style:UIAlertActionStyleDefault
handler:actionBlock];
darkAction = [UIAlertAction actionWithTitle:[VectorL10n settingsUiThemeDark]
style:UIAlertActionStyleDefault
handler:actionBlock];
blackAction = [UIAlertAction actionWithTitle:[VectorL10n settingsUiThemeBlack]
style:UIAlertActionStyleDefault
handler:actionBlock];
UIAlertController *themePicker = [UIAlertController alertControllerWithTitle:[VectorL10n settingsUiThemePickerTitle]
message:themePickerMessage
preferredStyle:UIAlertControllerStyleActionSheet];
if (autoAction)
{
[themePicker addAction:autoAction];
}
[themePicker addAction:lightAction];
[themePicker addAction:darkAction];
[themePicker addAction:blackAction];
// Cancel button
[themePicker addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
style:UIAlertActionStyleCancel
handler:nil]];
NSIndexPath *indexPath = [_tableViewSections exactIndexPathForRowTag:USER_INTERFACE_THEME_INDEX
sectionTag:SECTION_TAG_USER_INTERFACE];
if (indexPath)
{
UIView *fromCell = [self.tableView cellForRowAtIndexPath:indexPath];
[themePicker popoverPresentationController].sourceView = fromCell;
[themePicker popoverPresentationController].sourceRect = fromCell.bounds;
[self presentViewController:themePicker animated:YES completion:nil];
}
}
- (void)deactivateAccountAction
{
DeactivateAccountViewController *deactivateAccountViewController = [DeactivateAccountViewController instantiateWithMatrixSession:self.mainSession];
UINavigationController *navigationController = [[RiotNavigationController alloc] initWithRootViewController:deactivateAccountViewController];
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:navigationController animated:YES completion:nil];
deactivateAccountViewController.delegate = self;
self.deactivateAccountViewController = deactivateAccountViewController;
}
- (void)toggleShowRedacted:(UISwitch *)sender
{
[MXKAppSettings standardAppSettings].showRedactionsInRoomHistory = sender.isOn;
}
- (void)togglePresenceOfflineMode:(UISwitch *)sender
{
MXKAccount *account = MXKAccountManager.sharedManager.accounts.firstObject;
if (sender.isOn)
{
account.preferredSyncPresence = MXPresenceOffline;
}
else
{
account.preferredSyncPresence = MXPresenceOnline;
}
}
- (void)toggleEnableRoomMessageBubbles:(UISwitch *)sender
{
RiotSettings.shared.roomScreenEnableMessageBubbles = sender.isOn;
[[RoomTimelineConfiguration shared] updateStyleWithIdentifier:RiotSettings.shared.roomTimelineStyleIdentifier];
// Close all room data sources
// Be sure to use new room timeline style configurations
MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:self.mainSession];
[roomDataSourceManager reset];
}
- (void)toggleEnableAutoReportDecryptionErrors:(UISwitch *)sender
{
RiotSettings.shared.enableUISIAutoReporting = sender.isOn;
}
- (void)toggleEnableLiveLocationSharing:(UISwitch *)sender
{
RiotSettings.shared.enableLiveLocationSharing = sender.isOn;
}
#pragma mark - TextField listener
- (IBAction)textFieldDidChange:(id)sender
{
UITextField* textField = (UITextField*)sender;
if (textField.tag == USER_SETTINGS_DISPLAYNAME_INDEX)
{
// Remove white space from both ends
newDisplayName = [textField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
[self updateSaveButtonStatus];
}
else if (textField.tag == USER_SETTINGS_ADD_PHONENUMBER_INDEX)
{
newPhoneNumber = [[NBPhoneNumberUtil sharedInstance] parse:textField.text defaultRegion:newPhoneNumberCell.isoCountryCode error:nil];
[self formatNewPhoneNumber];
}
}
- (IBAction)textFieldDidEnd:(id)sender
{
UITextField* textField = (UITextField*)sender;
// Disable the new email edition if the user leaves the text field empty
if (textField.tag == USER_SETTINGS_ADD_EMAIL_INDEX && textField.text.length == 0 && !keepNewEmailEditing)
{
self.newEmailEditingEnabled = NO;
}
else if (textField.tag == USER_SETTINGS_ADD_PHONENUMBER_INDEX && textField.text.length == 0 && !keepNewPhoneNumberEditing && !newPhoneNumberCountryPicker)
{
// Disable the new phone edition if the user leaves the text field empty
self.newPhoneEditingEnabled = NO;
}
}
#pragma mark - UITextField delegate
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
if (textField.tag == USER_SETTINGS_DISPLAYNAME_INDEX)
{
textField.textAlignment = NSTextAlignmentLeft;
}
}
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if (textField.tag == USER_SETTINGS_DISPLAYNAME_INDEX)
{
textField.textAlignment = NSTextAlignmentRight;
}
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if (textField.tag == USER_SETTINGS_DISPLAYNAME_INDEX)
{
[textField resignFirstResponder];
}
else if (textField.tag == USER_SETTINGS_ADD_EMAIL_INDEX)
{
[self onAddNewEmail:textField];
}
return YES;
}
#pragma password update management
- (void)displayPasswordAlert
{
self.changePasswordBridgePresenter = [[ChangePasswordCoordinatorBridgePresenter alloc] initWithSession:self.mainSession];
self.changePasswordBridgePresenter.delegate = self;
[self.changePasswordBridgePresenter presentFrom:self animated:YES];
}
#pragma mark - MXKCountryPickerViewControllerDelegate
- (void)countryPickerViewController:(MXKCountryPickerViewController *)countryPickerViewController didSelectCountry:(NSString *)isoCountryCode
{
if (countryPickerViewController.view.tag == SECTION_TAG_LOCAL_CONTACTS)
{
[MXKAppSettings standardAppSettings].phonebookCountryCode = isoCountryCode;
}
else if (countryPickerViewController.view.tag == SECTION_TAG_USER_SETTINGS)
{
if (newPhoneNumberCell)
{
newPhoneNumberCell.isoCountryCode = isoCountryCode;
newPhoneNumber = [[NBPhoneNumberUtil sharedInstance] parse:newPhoneNumberCell.mxkTextField.text defaultRegion:isoCountryCode error:nil];
[self formatNewPhoneNumber];
}
}
[countryPickerViewController withdrawViewControllerAnimated:YES completion:nil];
}
#pragma mark - MXKCountryPickerViewControllerDelegate
- (void)languagePickerViewController:(MXKLanguagePickerViewController *)languagePickerViewController didSelectLangugage:(NSString *)language
{
if (![language isEqualToString:[NSBundle mxk_language]]
|| (language == nil && [NSBundle mxk_language]))
{
[NSBundle mxk_setLanguage:language];
UIApplication.sharedApplication.accessibilityLanguage = language;
// Store user settings
NSUserDefaults *sharedUserDefaults = [MXKAppSettings standardAppSettings].sharedUserDefaults;
[sharedUserDefaults setObject:language forKey:@"appLanguage"];
// Do a reload in order to recompute strings in the new language
// Note that "reloadMatrixSessions:NO" will reset room summaries
[self startActivityIndicator];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[[AppDelegate theDelegate] reloadMatrixSessions:NO];
});
}
}
#pragma mark - DeactivateAccountViewControllerDelegate
- (void)deactivateAccountViewControllerDidDeactivateWithSuccess:(DeactivateAccountViewController *)deactivateAccountViewController
{
MXLogDebug(@"[SettingsViewController] Deactivate account with success");
[[AppDelegate theDelegate] logoutSendingRequestServer:NO completion:^(BOOL isLoggedOut) {
MXLogDebug(@"[SettingsViewController] Complete clear user data after account deactivation");
}];
}
- (void)deactivateAccountViewControllerDidCancel:(DeactivateAccountViewController *)deactivateAccountViewController
{
[deactivateAccountViewController dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - NotificationSettingsCoordinatorBridgePresenter
- (void)showNotificationSettings: (NotificationSettingsScreen)screen API_AVAILABLE(ios(14.0))
{
NotificationSettingsCoordinatorBridgePresenter *notificationSettingsBridgePresenter = [[NotificationSettingsCoordinatorBridgePresenter alloc] initWithSession:self.mainSession];
notificationSettingsBridgePresenter.delegate = self;
MXWeakify(self);
[notificationSettingsBridgePresenter pushFrom:self.navigationController animated:YES screen:screen popCompletion:^{
MXStrongifyAndReturnIfNil(self);
self.notificationSettingsBridgePresenter = nil;
}];
self.notificationSettingsBridgePresenter = notificationSettingsBridgePresenter;
}
#pragma mark - NotificationSettingsCoordinatorBridgePresenterDelegate
- (void)notificationSettingsCoordinatorBridgePresenterDelegateDidComplete:(NotificationSettingsCoordinatorBridgePresenter *)coordinatorBridgePresenter API_AVAILABLE(ios(14.0))
{
[self.notificationSettingsBridgePresenter dismissWithAnimated:YES completion:nil];
self.notificationSettingsBridgePresenter = nil;
}
#pragma mark - SignOutFlowPresenterDelegate
- (void)signOutFlowPresenterDidStartLoading:(SignOutFlowPresenter *)presenter
{
[self startActivityIndicator];
self.view.userInteractionEnabled = NO;
self.signOutButton.enabled = NO;
}
- (void)signOutFlowPresenterDidStopLoading:(SignOutFlowPresenter *)presenter
{
[self stopActivityIndicator];
self.view.userInteractionEnabled = YES;
self.signOutButton.enabled = YES;
}
- (void)signOutFlowPresenter:(SignOutFlowPresenter *)presenter didFailWith:(NSError *)error
{
[[AppDelegate theDelegate] showErrorAsAlert:error];
}
#pragma mark - SingleImagePickerPresenterDelegate
- (void)singleImagePickerPresenterDidCancel:(SingleImagePickerPresenter *)presenter
{
[presenter dismissWithAnimated:YES completion:nil];
self.imagePickerPresenter = nil;
}
- (void)singleImagePickerPresenter:(SingleImagePickerPresenter *)presenter didSelectImageData:(NSData *)imageData withUTI:(MXKUTI *)uti
{
[presenter dismissWithAnimated:YES completion:nil];
self.imagePickerPresenter = nil;
newAvatarImage = [UIImage imageWithData:imageData];
[self updateSections];
}
#pragma mark - Identity server updates
- (void)registerAccountDataDidChangeIdentityServerNotification
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAccountDataDidChangeIdentityServerNotification:) name:kMXSessionAccountDataDidChangeIdentityServerNotification object:nil];
}
- (void)handleAccountDataDidChangeIdentityServerNotification:(NSNotification*)notification
{
[self refreshSettings];
}
#pragma mark - SettingsDiscoveryTableViewSectionDelegate
- (void)settingsDiscoveryTableViewSectionDidUpdate:(SettingsDiscoveryTableViewSection *)settingsDiscoveryTableViewSection
{
[self updateSections];
}
- (MXKTableViewCell *)settingsDiscoveryTableViewSection:(SettingsDiscoveryTableViewSection *)settingsDiscoveryTableViewSection tableViewCellClass:(Class)tableViewCellClass forRow:(NSInteger)forRow
{
MXKTableViewCell *tableViewCell;
if ([tableViewCellClass isEqual:[MXKTableViewCell class]])
{
tableViewCell = [self getDefaultTableViewCell:self.tableView];
}
else if ([tableViewCellClass isEqual:[MXKTableViewCellWithTextView class]])
{
NSIndexPath *indexPath = [_tableViewSections exactIndexPathForRowTag:forRow sectionTag:SECTION_TAG_DISCOVERY];
if (indexPath)
{
tableViewCell = [self textViewCellForTableView:self.tableView atIndexPath:indexPath];
}
}
else if ([tableViewCellClass isEqual:[MXKTableViewCellWithButton class]])
{
MXKTableViewCellWithButton *cell = [self.tableView dequeueReusableCellWithIdentifier:[MXKTableViewCellWithButton defaultReuseIdentifier]];
if (!cell)
{
cell = [[MXKTableViewCellWithButton alloc] init];
}
else
{
// Fix https://github.com/vector-im/riot-ios/issues/1354
cell.mxkButton.titleLabel.text = nil;
}
cell.mxkButton.titleLabel.font = [UIFont systemFontOfSize:17];
[cell.mxkButton setTintColor:ThemeService.shared.theme.tintColor];
tableViewCell = cell;
}
else if ([tableViewCellClass isEqual:[MXKTableViewCellWithLabelAndSwitch class]])
{
NSIndexPath *indexPath = [_tableViewSections exactIndexPathForRowTag:forRow sectionTag:SECTION_TAG_DISCOVERY];
if (indexPath)
{
tableViewCell = [self getLabelAndSwitchCell:self.tableView forIndexPath:indexPath];
}
}
return tableViewCell;
}
#pragma mark - SettingsDiscoveryViewModelCoordinatorDelegate
- (void)settingsDiscoveryViewModel:(SettingsDiscoveryViewModel *)viewModel didSelectThreePidWith:(NSString *)medium and:(NSString *)address
{
SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter *discoveryThreePidDetailsPresenter = [[SettingsDiscoveryThreePidDetailsCoordinatorBridgePresenter alloc] initWithSession:self.mainSession medium:medium adress:address];
MXWeakify(self);
[discoveryThreePidDetailsPresenter pushFrom:self.navigationController animated:YES popCompletion:^{
MXStrongifyAndReturnIfNil(self);
self.discoveryThreePidDetailsPresenter = nil;
}];
self.discoveryThreePidDetailsPresenter = discoveryThreePidDetailsPresenter;
}
- (void)settingsDiscoveryViewModelDidTapAcceptIdentityServerTerms:(SettingsDiscoveryViewModel *)viewModel
{
MXSession *session = self.mainSession;
if (!session.identityService.areAllTermsAgreed)
{
[self prepareIdentityServiceAndPresentTermsWithSession:session checkingAccessForContactsOnAccept:NO];
}
}
#pragma mark - Local Contacts Sync
- (void)checkAccessForContacts
{
MXWeakify(self);
// Check for contacts access, showing a pop-up if necessary.
[MXKTools checkAccessForContacts:VectorL10n.contactsAddressBookPermissionDeniedAlertTitle
withManualChangeMessage:VectorL10n.contactsAddressBookPermissionDeniedAlertMessage
showPopUpInViewController:self
completionHandler:^(BOOL granted) {
MXStrongifyAndReturnIfNil(self);
if (granted)
{
// When granted, local contacts can be shown.
[MXKAppSettings standardAppSettings].syncLocalContacts = YES;
[self updateSections];
}
}];
}
#pragma mark - Identity server
- (void)showIdentityServerSettingsScreen
{
identityServerSettingsCoordinatorBridgePresenter = [[SettingsIdentityServerCoordinatorBridgePresenter alloc] initWithSession:self.mainSession];
[identityServerSettingsCoordinatorBridgePresenter pushFrom:self.navigationController animated:YES popCompletion:nil];
identityServerSettingsCoordinatorBridgePresenter.delegate = self;
}
- (void)prepareIdentityServiceAndPresentTermsWithSession:(MXSession *)session
checkingAccessForContactsOnAccept:(BOOL)checkAccessForContacts
{
if (self.isPreparingIdentityService)
{
return;
}
self.isPreparingIdentityService = YES;
self.serviceTermsModalShouldCheckAccessForContactsOnAccept = checkAccessForContacts;
MXWeakify(self);
// The preparation can take some time so indicate this to the user
[self startActivityIndicator];
[session prepareIdentityServiceForTermsWithDefault:RiotSettings.shared.identityServerUrlString
success:^(MXSession *session, NSString *baseURL, NSString *accessToken) {
MXStrongifyAndReturnIfNil(self);
[self stopActivityIndicator];
self.isPreparingIdentityService = NO;
// Present the terms of the identity server.
[self presentIdentityServerTermsWithSession:session baseURL:baseURL andAccessToken:accessToken];
} failure:^(NSError *error) {
MXStrongifyAndReturnIfNil(self);
[self stopActivityIndicator];
self.isPreparingIdentityService = NO;
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:VectorL10n.findYourContactsIdentityServiceError
message:nil
preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:VectorL10n.ok
style:UIAlertActionStyleDefault
handler:nil]];
[self presentViewController:alertController animated:YES completion:nil];
[MXKAppSettings standardAppSettings].syncLocalContacts = NO;
[self updateSections];
}];
}
- (void)presentIdentityServerTermsWithSession:(MXSession*)mxSession baseURL:(NSString*)baseURL andAccessToken:(NSString*)accessToken
{
if (!mxSession || !baseURL || !accessToken || self.serviceTermsModalCoordinatorBridgePresenter.isPresenting)
{
return;
}
self.serviceTermsModalCoordinatorBridgePresenter = [[ServiceTermsModalCoordinatorBridgePresenter alloc] initWithSession:mxSession
baseUrl:baseURL
serviceType:MXServiceTypeIdentityService
accessToken:accessToken];
self.serviceTermsModalCoordinatorBridgePresenter.delegate = self;
[self.serviceTermsModalCoordinatorBridgePresenter presentFrom:self animated:YES];
}
#pragma mark SettingsIdentityServerCoordinatorBridgePresenterDelegate
- (void)settingsIdentityServerCoordinatorBridgePresenterDelegateDidComplete:(SettingsIdentityServerCoordinatorBridgePresenter *)coordinatorBridgePresenter
{
identityServerSettingsCoordinatorBridgePresenter = nil;
[self refreshSettings];
}
#pragma mark ServiceTermsModalCoordinatorBridgePresenterDelegate
- (void)serviceTermsModalCoordinatorBridgePresenterDelegateDidAccept:(ServiceTermsModalCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter
{
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
[self.settingsDiscoveryTableViewSection reload];
if (self.serviceTermsModalShouldCheckAccessForContactsOnAccept)
{
[self checkAccessForContacts];
}
}];
self.serviceTermsModalCoordinatorBridgePresenter = nil;
}
- (void)serviceTermsModalCoordinatorBridgePresenterDelegateDidDecline:(ServiceTermsModalCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter session:(MXSession *)session
{
// Terms weren't accepted: disable contacts toggle and refresh discovery
[self updateSections];
[self.settingsDiscoveryTableViewSection reload];
[coordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
self.serviceTermsModalCoordinatorBridgePresenter = nil;
}
- (void)serviceTermsModalCoordinatorBridgePresenterDelegateDidClose:(ServiceTermsModalCoordinatorBridgePresenter * _Nonnull)coordinatorBridgePresenter
{
// Terms weren't accepted: disable contacts toggle and refresh discovery
[self updateSections];
[self.settingsDiscoveryTableViewSection reload];
self.serviceTermsModalCoordinatorBridgePresenter = nil;
}
#pragma mark - TableViewSectionsDelegate
- (void)tableViewSectionsDidUpdateSections:(TableViewSections *)sections
{
[self.tableView reloadData];
}
#pragma mark - ThreadsBetaCoordinatorBridgePresenterDelegate
- (void)threadsBetaCoordinatorBridgePresenterDelegateDidTapEnable:(ThreadsBetaCoordinatorBridgePresenter *)coordinatorBridgePresenter
{
MXWeakify(self);
[self.threadsBetaBridgePresenter dismissWithAnimated:YES completion:^{
MXStrongifyAndReturnIfNil(self);
[self enableThreads:YES];
}];
}
- (void)threadsBetaCoordinatorBridgePresenterDelegateDidTapCancel:(ThreadsBetaCoordinatorBridgePresenter *)coordinatorBridgePresenter
{
MXWeakify(self);
[self.threadsBetaBridgePresenter dismissWithAnimated:YES completion:^{
MXStrongifyAndReturnIfNil(self);
[self updateSections];
}];
}
#pragma mark - ChangePasswordCoordinatorBridgePresenterDelegate
- (void)changePasswordCoordinatorBridgePresenterDidComplete:(ChangePasswordCoordinatorBridgePresenter *)bridgePresenter
{
[bridgePresenter dismissWithAnimated:YES completion:^{
self.changePasswordBridgePresenter = nil;
}];
}
- (void)changePasswordCoordinatorBridgePresenterDidCancel:(ChangePasswordCoordinatorBridgePresenter *)bridgePresenter
{
[bridgePresenter dismissWithAnimated:YES completion:nil];
self.changePasswordBridgePresenter = nil;
}
#pragma mark - User sessions management
- (void)showUserSessionsFlow
{
if (!self.mainSession)
{
MXLogError(@"[SettingsViewController] Cannot show user sessions flow, no user session available");
return;
}
if (!self.navigationController)
{
MXLogError(@"[SettingsViewController] Cannot show user sessions flow, no navigation controller available");
return;
}
UserSessionsFlowCoordinatorBridgePresenter *userSessionsFlowCoordinatorBridgePresenter = [[UserSessionsFlowCoordinatorBridgePresenter alloc] initWithMxSession:self.mainSession];
MXWeakify(self);
userSessionsFlowCoordinatorBridgePresenter.completion = ^{
MXStrongifyAndReturnIfNil(self);
self.userSessionsFlowCoordinatorBridgePresenter = nil;
};
self.userSessionsFlowCoordinatorBridgePresenter = userSessionsFlowCoordinatorBridgePresenter;
[self.userSessionsFlowCoordinatorBridgePresenter pushFrom:self.navigationController animated:YES];
}
#pragma mark - SSOAuthenticationPresenterDelegate
- (void)ssoAuthenticationPresenterDidCancel:(SSOAuthenticationPresenter *)presenter
{
self.ssoAuthenticationPresenter = nil;
MXLogDebug(@"OIDC account management complete.")
}
- (void)ssoAuthenticationPresenter:(SSOAuthenticationPresenter *)presenter authenticationDidFailWithError:(NSError *)error
{
self.ssoAuthenticationPresenter = nil;
MXLogError(@"OIDC account management failed.")
}
- (void)ssoAuthenticationPresenter:(SSOAuthenticationPresenter *)presenter
authenticationSucceededWithToken:(NSString *)token
usingIdentityProvider:(SSOIdentityProvider *)identityProvider
{
self.ssoAuthenticationPresenter = nil;
MXLogWarning(@"Unexpected callback after OIDC account management.")
}
@end