123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- /**
- * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
- #import "NCUserInterfaceController.h"
- #import "AFNetworking.h"
- #import "JDStatusBarNotification.h"
- #import "UIView+Toast.h"
- #import "AuthenticationViewController.h"
- #import "LoginViewController.h"
- #import "NCAppBranding.h"
- #import "NCConnectionController.h"
- #import "NCDatabaseManager.h"
- #import "NCRoomsManager.h"
- #import "NCSettingsController.h"
- #import "NotificationCenterNotifications.h"
- #import "NextcloudTalk-Swift.h"
- @interface NCUserInterfaceController () <LoginViewControllerDelegate, AuthenticationViewControllerDelegate>
- {
- LoginViewController *_loginViewController;
- AuthenticationViewController *_authViewController;
- NCPushNotification *_pendingPushNotification;
- NSMutableDictionary *_pendingCallKitCall;
- NSDictionary *_pendingLocalNotification;
- NSURLComponents *_pendingURL;
- BOOL _waitingForServerCapabilities;
- }
- @end
- @implementation NCUserInterfaceController
- + (NCUserInterfaceController *)sharedInstance
- {
- static dispatch_once_t once;
- static NCUserInterfaceController *sharedInstance;
- dispatch_once(&once, ^{
- sharedInstance = [[self alloc] init];
- });
- return sharedInstance;
- }
- - (id)init
- {
- self = [super init];
- if (self) {
- [self configureToasts];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appStateHasChanged:) name:NCAppStateHasChangedNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(connectionStateHasChanged:) name:NCConnectionStateHasChangedNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(presentTalkNotInstalledWarningAlert) name:NCTalkNotInstalledNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(presentTalkOutdatedWarningAlert) name:NCOutdatedTalkVersionNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(presentServerMaintenanceModeWarning:) name:NCServerMaintenanceModeNotification object:nil];
- }
- return self;
- }
- - (void)dealloc
- {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- }
- - (void)configureToasts
- {
- CSToastStyle *style = [[CSToastStyle alloc] initWithDefaultStyle];
- style.messageFont = [UIFont systemFontOfSize:15.0];
- style.backgroundColor = [UIColor colorWithWhite:0.3 alpha:1];
- style.cornerRadius = 5.0;
-
- [CSToastManager setSharedStyle:style];
- }
- - (void)presentLoginViewController
- {
- [self presentLoginViewControllerForServerURL:nil withUser:nil];
- }
- - (void)presentLoginViewControllerForServerURL:(NSString *)serverURL withUser:(NSString *)user
- {
- if (forceDomain && domain) {
- _authViewController = [[AuthenticationViewController alloc] initWithServerUrl:domain];
- _authViewController.delegate = self;
- _authViewController.modalPresentationStyle = ([[NCDatabaseManager sharedInstance] numberOfAccounts] == 0) ? UIModalPresentationFullScreen : UIModalPresentationAutomatic;
- [_mainViewController presentViewController:_authViewController animated:YES completion:nil];
- } else {
- // Don't open a login if we're in a call
- if ([[NCRoomsManager sharedInstance] callViewController]) {
- return;
- }
-
- // Leave chat if we're currently in one
- if ([[NCRoomsManager sharedInstance] chatViewController]) {
- [self presentConversationsList];
- }
-
- if (!_loginViewController || [_mainViewController presentedViewController] != _loginViewController) {
- _loginViewController = [[LoginViewController alloc] init];
- _loginViewController.delegate = self;
- _loginViewController.modalPresentationStyle = ([[NCDatabaseManager sharedInstance] numberOfAccounts] == 0) ? UIModalPresentationFullScreen : UIModalPresentationAutomatic;
-
- [_mainViewController presentViewController:_loginViewController animated:YES completion:nil];
- }
-
- if (serverURL) {
- [_loginViewController startLoginProcessWithServerURL:serverURL withUser:user];
- }
- }
- }
- - (void)presentLoggedOutInvalidCredentialsAlert
- {
- UIAlertController * alert = [UIAlertController
- alertControllerWithTitle:NSLocalizedString(@"Logged out", nil)
- message:NSLocalizedString(@"Credentials for this account were no longer valid", nil)
- preferredStyle:UIAlertControllerStyleAlert];
-
- UIAlertAction* okButton = [UIAlertAction
- actionWithTitle:NSLocalizedString(@"OK", nil)
- style:UIAlertActionStyleDefault
- handler:nil];
-
- [alert addAction:okButton];
- [_mainViewController presentViewController:alert animated:YES completion:nil];
- }
- - (void)presentOfflineWarningAlert
- {
- UIAlertController * alert = [UIAlertController
- alertControllerWithTitle:NSLocalizedString(@"Disconnected", nil)
- message:NSLocalizedString(@"It seems that there is no internet connection.", nil)
- preferredStyle:UIAlertControllerStyleAlert];
-
- UIAlertAction* okButton = [UIAlertAction
- actionWithTitle:NSLocalizedString(@"OK", nil)
- style:UIAlertActionStyleDefault
- handler:nil];
-
- [alert addAction:okButton];
- [_mainViewController presentViewController:alert animated:YES completion:nil];
- }
- - (void)presentTalkNotInstalledWarningAlert
- {
- UIAlertController * alert = [UIAlertController
- alertControllerWithTitle:[NSString stringWithFormat:NSLocalizedString(@"%@ not installed", @"{app name} is not installed"), talkAppName]
- message:[NSString stringWithFormat:NSLocalizedString(@"It seems that %@ is not installed in your server.", @"It seems that {app name} is not installed in your server."), talkAppName]
- preferredStyle:UIAlertControllerStyleAlert];
-
- UIAlertAction* okButton = [UIAlertAction
- actionWithTitle:NSLocalizedString(@"OK", nil)
- style:UIAlertActionStyleDefault
- handler:^(UIAlertAction * _Nonnull action) {
- [self logOutCurrentUser];
- }];
-
- [alert addAction:okButton];
- [_mainViewController presentViewController:alert animated:YES completion:nil];
- }
- - (void)presentTalkOutdatedWarningAlert
- {
- UIAlertController * alert = [UIAlertController
- alertControllerWithTitle:[NSString stringWithFormat:NSLocalizedString(@"%@ version not supported", @"{app name} version not supported"), talkAppName]
- message:[NSString stringWithFormat:NSLocalizedString(@"Please update your server with the latest %@ version available.", @"Please update your server with the latest {app name} version available."), talkAppName]
- preferredStyle:UIAlertControllerStyleAlert];
-
- UIAlertAction* okButton = [UIAlertAction
- actionWithTitle:NSLocalizedString(@"OK", nil)
- style:UIAlertActionStyleDefault
- handler:^(UIAlertAction * _Nonnull action) {
- [self logOutCurrentUser];
- }];
-
- [alert addAction:okButton];
- [_mainViewController presentViewController:alert animated:YES completion:nil];
- }
- - (void)presentAccountNotConfiguredAlertForUser:(NSString *)user inServer:(NSString *)server
- {
- UIAlertController * alert = [UIAlertController
- alertControllerWithTitle:NSLocalizedString(@"Account not configured", nil)
- message:[NSString stringWithFormat:NSLocalizedString(@"There is no account for user %@ in server %@ configured in this app.", nil), user, server]
- preferredStyle:UIAlertControllerStyleAlert];
-
- UIAlertAction* okButton = [UIAlertAction
- actionWithTitle:NSLocalizedString(@"OK", nil)
- style:UIAlertActionStyleDefault
- handler:nil];
-
- [alert addAction:okButton];
- [_mainViewController presentViewController:alert animated:YES completion:nil];
- }
- - (void)presentServerMaintenanceModeWarning:(NSNotification *)notification
- {
- TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
- NSString *accountId = [notification.userInfo objectForKey:@"accountId"];
-
- if (accountId && [activeAccount.accountId isEqualToString:accountId]) {
- [[JDStatusBarNotificationPresenter sharedPresenter] presentWithText:NSLocalizedString(@"Server is currently in maintenance mode", nil) dismissAfterDelay:4.0f includedStyle:JDStatusBarNotificationIncludedStyleError];
- }
- }
- - (void)logOutAccountWithAccountId:(NSString *)accountId
- {
- [[NCSettingsController sharedInstance] logoutAccountWithAccountId:accountId withCompletionBlock:^(NSError *error) {
- [[NCUserInterfaceController sharedInstance] presentConversationsList];
- [[NCConnectionController sharedInstance] checkAppState];
- }];
- }
- - (void)logOutCurrentUser
- {
- TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
- [self logOutAccountWithAccountId:activeAccount.accountId];
- }
- - (void)presentChatForLocalNotification:(NSDictionary *)userInfo
- {
- if ([NCConnectionController sharedInstance].appState != kAppStateReady) {
- _waitingForServerCapabilities = YES;
- _pendingLocalNotification = userInfo;
- return;
- }
- [[NSNotificationCenter defaultCenter] postNotificationName:NCLocalNotificationJoinChatNotification
- object:self
- userInfo:userInfo];
- }
- - (void)presentChatForPushNotification:(NCPushNotification *)pushNotification
- {
- if ([NCConnectionController sharedInstance].appState != kAppStateReady) {
- _waitingForServerCapabilities = YES;
- _pendingPushNotification = pushNotification;
- return;
- }
- NSDictionary *userInfo = [NSDictionary dictionaryWithObject:pushNotification forKey:@"pushNotification"];
- [[NSNotificationCenter defaultCenter] postNotificationName:NCPushNotificationJoinChatNotification
- object:self
- userInfo:userInfo];
- }
- - (void)presentAlertForPushNotification:(NCPushNotification *)pushNotification
- {
- if ([NCConnectionController sharedInstance].appState != kAppStateReady) {
- _waitingForServerCapabilities = YES;
- _pendingPushNotification = pushNotification;
- return;
- }
- UIAlertController * alert = [UIAlertController
- alertControllerWithTitle:[pushNotification bodyForRemoteAlerts]
- message:NSLocalizedString(@"Do you want to join this call?", nil)
- preferredStyle:UIAlertControllerStyleAlert];
-
- UIAlertAction *joinAudioButton = [UIAlertAction
- actionWithTitle:NSLocalizedString(@"Join call (audio only)", nil)
- style:UIAlertActionStyleDefault
- handler:^(UIAlertAction * _Nonnull action) {
- NSDictionary *userInfo = [NSDictionary dictionaryWithObject:pushNotification forKey:@"pushNotification"];
- [[NSNotificationCenter defaultCenter] postNotificationName:NCPushNotificationJoinAudioCallAcceptedNotification
- object:self
- userInfo:userInfo];
- }];
-
- UIAlertAction *joinVideoButton = [UIAlertAction
- actionWithTitle:NSLocalizedString(@"Join call with video", nil)
- style:UIAlertActionStyleDefault
- handler:^(UIAlertAction * _Nonnull action) {
- NSDictionary *userInfo = [NSDictionary dictionaryWithObject:pushNotification forKey:@"pushNotification"];
- [[NSNotificationCenter defaultCenter] postNotificationName:NCPushNotificationJoinVideoCallAcceptedNotification
- object:self
- userInfo:userInfo];
- }];
-
- UIAlertAction* cancelButton = [UIAlertAction
- actionWithTitle:NSLocalizedString(@"Cancel", nil)
- style:UIAlertActionStyleCancel
- handler:nil];
-
- [joinAudioButton setValue:[UIImage systemImageNamed:@"phone"] forKey:@"image"];
- [joinVideoButton setValue:[UIImage systemImageNamed:@"video"] forKey:@"image"];
-
- [alert addAction:joinAudioButton];
- [alert addAction:joinVideoButton];
- [alert addAction:cancelButton];
-
- // Do not show join call dialog until we don't handle 'hangup current call'/'join new one' properly.
- if (![NCRoomsManager sharedInstance].callViewController) {
- [_mainViewController dismissViewControllerAnimated:NO completion:nil];
- [_mainViewController presentViewController:alert animated:YES completion:nil];
- } else {
- NSLog(@"Not showing join call dialog due to in a call.");
- }
- }
- - (void)presentAlertViewController:(UIAlertController *)alertViewController
- {
- if (_mainViewController.presentedViewController != nil) {
- // When the callview is presented, we need to show the alert this way
- [_mainViewController.presentedViewController presentViewController:alertViewController animated:YES completion:nil];
- } else {
- [_mainViewController presentViewController:alertViewController animated:YES completion:nil];
- }
- }
- - (void)presentAlertIfNotPresentedAlready:(UIAlertController *)alertViewController
- {
- if (alertViewController != _mainViewController.presentedViewController) {
- [self presentAlertViewController:alertViewController];
- }
- }
- - (void)presentAlertWithTitle:(NSString *)title withMessage:(NSString *)message
- {
- UIAlertController *alertDialog = [UIAlertController alertControllerWithTitle:title
- message:message
- preferredStyle:UIAlertControllerStyleAlert];
- UIAlertAction *okAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleDefault handler:nil];
- [alertDialog addAction:okAction];
- [self presentAlertViewController:alertDialog];
- }
- - (void)presentConversationsList
- {
- [_mainViewController dismissViewControllerAnimated:YES completion:nil];
- [_mainViewController popSecondaryColumnToRootViewController];
- [_mainViewController showColumn:UISplitViewControllerColumnPrimary];
- }
- - (void)popToConversationsList
- {
- [_mainViewController popSecondaryColumnToRootViewController];
- [_mainViewController showColumn:UISplitViewControllerColumnPrimary];
- }
- - (void)presentChatViewController:(ChatViewController *)chatViewController
- {
- [self presentConversationsList];
- [_mainViewController showDetailViewController:chatViewController sender:self];
- [_roomsTableViewController setSelectedRoomToken:chatViewController.room.token];
- }
- - (void)presentCallViewController:(CallViewController *)callViewController withCompletionBlock:(PresentCallControllerCompletionBlock)block
- {
- [_mainViewController dismissViewControllerAnimated:NO completion:nil];
- [_mainViewController presentViewController:callViewController animated:YES completion:^{
- if (block) {
- block();
- }
- }];
- }
- - (void)presentCallKitCallInRoom:(NSString *)token withVideoEnabled:(BOOL)video
- {
- NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:token forKey:@"roomToken"];
- [userInfo setValue:@(video) forKey:@"isVideoEnabled"];
- if ([NCConnectionController sharedInstance].appState != kAppStateReady) {
- _waitingForServerCapabilities = YES;
- _pendingCallKitCall = userInfo;
- return;
- }
- [self startCallKitCall:userInfo];
- }
- - (void)startCallKitCall:(NSMutableDictionary *)callDict
- {
- NSString *roomToken = [callDict objectForKey:@"roomToken"];
- BOOL video = [[callDict objectForKey:@"isVideoEnabled"] boolValue];
- [[NCRoomsManager sharedInstance] joinCallWithCallToken:roomToken withVideo:video asInitiator:YES recordingConsent:NO];
- }
- - (void)presentChatForURL:(NSURLComponents *)urlComponents
- {
- if ([NCConnectionController sharedInstance].appState != kAppStateReady) {
- _waitingForServerCapabilities = YES;
- _pendingURL = urlComponents;
- return;
- }
-
- NSArray *queryItems = urlComponents.queryItems;
- NSString *server = [NCUtils valueForKey:@"server" fromQueryItems:queryItems];
- NSString *user = [NCUtils valueForKey:@"user" fromQueryItems:queryItems];
- NSString *withUser = [NCUtils valueForKey:@"withUser" fromQueryItems:queryItems];
- NSString *withRoomToken = [NCUtils valueForKey:@"withRoomToken" fromQueryItems:queryItems];
- TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForUserId:user inServer:server];
-
- if (!account) {
- [self presentAccountNotConfiguredAlertForUser:user inServer:server];
- return;
- }
- NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:account.accountId forKey:@"accountId"];
- if (withUser) {
- [userInfo setValue:withUser forKey:@"withUser"];
- } else if (withRoomToken) {
- [userInfo setValue:withRoomToken forKey:@"withRoomToken"];
- } else { return; }
- [[NSNotificationCenter defaultCenter] postNotificationName:NCURLWantsToOpenConversationNotification
- object:self
- userInfo:userInfo];
- }
- - (void)presentSettingsViewController
- {
- [self presentConversationsList];
- UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
- NCNavigationController *settingsNC = [storyboard instantiateViewControllerWithIdentifier:@"settingsNC"];
- [self.mainViewController presentViewController:settingsNC animated:YES completion:nil];
- }
- - (void)presentShareLinkDialogForRoom:(NCRoom *)room inViewContoller:(UITableViewController *)viewController forIndexPath:(NSIndexPath *)indexPath
- {
- NSString *roomLinkURL = room.linkURL;
- if (!roomLinkURL) {
- return;
- }
- NSArray *items = @[roomLinkURL];
- UIActivityViewController *controller = [[UIActivityViewController alloc]initWithActivityItems:items applicationActivities:nil];
- NSString *appDisplayName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"];
- NSString *emailSubject = [NSString stringWithFormat:NSLocalizedString(@"%@ invitation", nil), appDisplayName];
- [controller setValue:emailSubject forKey:@"subject"];
- // Presentation on iPads
- if (indexPath) {
- controller.popoverPresentationController.sourceView = viewController.tableView;
- controller.popoverPresentationController.sourceRect = [viewController.tableView rectForRowAtIndexPath:indexPath];
- }
- [viewController presentViewController:controller animated:YES completion:nil];
- controller.completionWithItemsHandler = ^(NSString *activityType,
- BOOL completed,
- NSArray *returnedItems,
- NSError *error) {
- if (error) {
- NSLog(@"An Error occured sharing room: %@, %@", error.localizedDescription, error.localizedFailureReason);
- }
- };
- }
- #pragma mark - Notifications
- - (void)appStateHasChanged:(NSNotification *)notification
- {
- AppState appState = [[notification.userInfo objectForKey:@"appState"] intValue];
- if (appState == kAppStateReady && _waitingForServerCapabilities) {
- _waitingForServerCapabilities = NO;
- if (_pendingPushNotification) {
- if (_pendingPushNotification.type == NCPushNotificationTypeCall) {
- [self presentAlertForPushNotification:_pendingPushNotification];
- } else {
- [self presentChatForPushNotification:_pendingPushNotification];
- }
- } else if (_pendingCallKitCall) {
- [self startCallKitCall:_pendingCallKitCall];
- } else if (_pendingURL) {
- [self presentChatForURL:_pendingURL];
- }
- }
- }
- - (void)connectionStateHasChanged:(NSNotification *)notification
- {
- ConnectionState connectionState = [[notification.userInfo objectForKey:@"connectionState"] intValue];
- switch (connectionState) {
- case kConnectionStateDisconnected:
- [[JDStatusBarNotificationPresenter sharedPresenter] presentWithText:NSLocalizedString(@"Network not available", nil) dismissAfterDelay:4.0f includedStyle:JDStatusBarNotificationIncludedStyleError];
- break;
-
- case kConnectionStateConnected:
- [[JDStatusBarNotificationPresenter sharedPresenter] presentWithText:NSLocalizedString(@"Network available", nil) dismissAfterDelay:4.0f includedStyle:JDStatusBarNotificationIncludedStyleSuccess];
- break;
-
- default:
- break;
- }
- }
- #pragma mark - LoginViewControllerDelegate
- - (void)loginViewControllerDidFinish:(LoginViewController *)viewController
- {
- [_mainViewController dismissViewControllerAnimated:YES completion:^{
- [[NCConnectionController sharedInstance] checkAppState];
- // Get server capabilities again to check if user is allowed to use Nextcloud Talk
- TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
- [[NCSettingsController sharedInstance] getCapabilitiesForAccountId:activeAccount.accountId withCompletionBlock:nil];
- }];
- }
- #pragma mark - AuthenticationViewControllerDelegate
- - (void)authenticationViewControllerDidFinish:(AuthenticationViewController *)viewController
- {
- [_mainViewController dismissViewControllerAnimated:YES completion:^{
- [[NCConnectionController sharedInstance] checkAppState];
- // Get server capabilities again to check if user is allowed to use Nextcloud Talk
- TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
- [[NCSettingsController sharedInstance] getCapabilitiesForAccountId:activeAccount.accountId withCompletionBlock:nil];
- }];
- }
- @end
|