another.im-ios/Monal/Classes/MLSettingsTableViewController.m
2024-11-18 15:53:52 +01:00

538 lines
18 KiB
Objective-C

//
// MLSettingsTableViewController.m
// Monal
//
// Created by Anurodh Pokharel on 12/26/17.
// Copyright © 2017 Monal.im. All rights reserved.
//
#import "MLSettingsTableViewController.h"
#import "MLWebViewController.h"
#import "MLSwitchCell.h"
#import "HelperTools.h"
#import "DataLayer.h"
#import "MLXMPPManager.h"
#import "XMPPEdit.h"
#import "MonalAppDelegate.h"
#import "ActiveChatsViewController.h"
#import <Monal-Swift.h>
@import SafariServices;
enum kSettingSection {
kSettingSectionAccounts,
kSettingSectionApp,
kSettingSectionSupport,
kSettingSectionAbout,
kSettingSectionCount
};
enum SettingsAccountRows {
#ifndef IS_QUICKSY
QuickSettingsRow,
AdvancedSettingsRow,
#endif
SettingsAccountRowsCnt
};
enum SettingsAppRows {
GeneralSettingsRow,
SoundsRow,
SettingsAppRowsCnt
};
enum SettingsSupportRow {
EmailRow,
SubmitABugRow,
OpenFAQRow,
SettingsSupportRowCnt
};
enum SettingsAboutRows {
RateMonalRow,
OpenSourceRow,
PrivacyRow,
AboutRow,
#ifdef DEBUG
LogRow,
#endif
VersionRow,
SettingsAboutRowsCntORLogRow,
SettingsAboutRowsWithLogCnt
};
//this will hold all disabled rows of all enums (this is needed because the code below still references these rows)
enum DummySettingsRows {
#ifdef IS_QUICKSY
QuickSettingsRow,
AdvancedSettingsRow,
#endif
DummySettingsRowsBegin = 100,
};
@interface MLSettingsTableViewController () {
int _tappedVersionInfo;
}
@property (nonatomic, strong) NSArray* sections;
@property (nonatomic, strong) NSArray* accountRows;
@property (nonatomic, strong) NSArray* appRows;
@property (nonatomic, strong) NSArray* supportRows;
@property (nonatomic, strong) NSDateFormatter* uptimeFormatter;
@property (nonatomic, strong) NSIndexPath* selected;
@end
@implementation MLSettingsTableViewController
-(IBAction) close:(id) sender
{
_tappedVersionInfo = 0;
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void) viewDidLoad
{
[super viewDidLoad];
[self setupAccountsView];
[self.tableView registerNib:[UINib nibWithNibName:@"MLSwitchCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"AccountCell"];
self.splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeOneBesideSecondary;
#if !TARGET_OS_MACCATALYST
self.splitViewController.primaryBackgroundStyle = UISplitViewControllerBackgroundStyleSidebar;
#endif
}
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self refreshAccountList];
_tappedVersionInfo = 0;
self.selected = nil;
[self presentSplitPlaceholder];
}
-(void) viewWillDisappear:(BOOL) animated
{
[super viewWillDisappear:animated];
[((MonalAppDelegate*)UIApplication.sharedApplication.delegate).activeChats sheetDismissed];
}
-(void) presentSplitPlaceholder
{
// only show placeholder if we use a split view
if(!self.splitViewController.collapsed)
{
DDLogVerbose(@"Presenting Settings Placeholder...");
UIViewController* detailsViewController = [[SwiftuiInterface new] makeViewWithName:@"ChatPlaceholder"];
[self showDetailViewController:detailsViewController sender:self];
}
}
#pragma mark - key commands
-(BOOL) canBecomeFirstResponder
{
return YES;
}
-(NSArray<UIKeyCommand*>*) keyCommands
{
return @[[UIKeyCommand keyCommandWithInput:UIKeyInputEscape modifierFlags:0 action:@selector(close:)]];
}
#pragma mark - Table view data source
-(NSInteger) numberOfSectionsInTableView:(UITableView*) tableView
{
return kSettingSectionCount;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
switch(section)
{
#ifdef IS_QUICKSY
case kSettingSectionAccounts: return [self getAccountNum] + SettingsAccountRowsCnt;
#else
case kSettingSectionAccounts: return [self getAccountNum] + SettingsAccountRowsCnt;
#endif
case kSettingSectionApp: return SettingsAppRowsCnt;
case kSettingSectionSupport: return SettingsSupportRowCnt;
#ifndef DEBUG
case kSettingSectionAbout: return [[HelperTools defaultsDB] boolForKey:@"showLogInSettings"] ? SettingsAboutRowsWithLogCnt : SettingsAboutRowsCntORLogRow;
#else
case kSettingSectionAbout: return SettingsAboutRowsCntORLogRow;
#endif
default:
unreachable();
}
return 0;
}
-(void) prepareForSegue:(UIStoryboardSegue*) segue sender:(id) sender
{
if([segue.identifier isEqualToString:@"showOpenSource"])
{
UINavigationController* nav = (UINavigationController*) segue.destinationViewController;
MLWebViewController* web = (MLWebViewController*) nav.topViewController;
NSBundle* mainBundle = [NSBundle mainBundle];
NSString* myFile = [mainBundle pathForResource: @"opensource" ofType: @"html"];
[web initViewWithUrl:[NSURL fileURLWithPath:myFile]];
}
else if([segue.identifier isEqualToString:@"showAbout"])
{
UINavigationController* nav = (UINavigationController*) segue.destinationViewController;
MLWebViewController* web = (MLWebViewController*) nav.topViewController;
[web initViewWithUrl:[NSURL URLWithString:@"https://monal-im.org/about"]];
}
else if([segue.identifier isEqualToString:@"showPrivacy"])
{
UINavigationController* nav = (UINavigationController*) segue.destinationViewController;
MLWebViewController* web = (MLWebViewController*) nav.topViewController;
[web initViewWithUrl:[NSURL URLWithString:@"https://monal-im.org/privacy"]];
}
else if([segue.identifier isEqualToString:@"showBug"])
{
UINavigationController* nav = (UINavigationController*) segue.destinationViewController;
MLWebViewController* web = (MLWebViewController*) nav.topViewController;
[web initViewWithUrl:[NSURL URLWithString:@"https://github.com/monal-im/Monal/issues"]];
}
else if([segue.identifier isEqualToString:@"showFAQ"])
{
UINavigationController* nav = (UINavigationController*) segue.destinationViewController;
MLWebViewController* web = (MLWebViewController*) nav.topViewController;
[web initViewWithUrl:[NSURL URLWithString:@"https://github.com/monal-im/Monal/wiki/FAQ---Frequently-Asked-Questions"]];
}
else if([segue.identifier isEqualToString:@"editXMPP"])
{
XMPPEdit* editor = (XMPPEdit*) segue.destinationViewController.childViewControllers.firstObject; // segue.destinationViewController;
if(self.selected && self.selected.row >= (int) [self getAccountNum])
{
editor.accountID = [NSNumber numberWithInt:-1];
}
else
{
MLAssert(self.selected != nil, @"self.selected must not be nil");
editor.originIndex = self.selected;
editor.accountID = [self getAccountIDByIndex:self.selected.row];
}
}
}
-(UITableViewCell*) tableView:(UITableView*) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath
{
MLSwitchCell* cell = [tableView dequeueReusableCellWithIdentifier:@"AccountCell" forIndexPath:indexPath];
switch((int)indexPath.section)
{
case kSettingSectionAccounts: {
if(indexPath.row < (int) [self getAccountNum])
{
// User selected an account
[self initContactCell:cell forAccNo:indexPath.row];
}
else
{
#ifndef IS_QUICKSY
MLAssert(indexPath.row - [self getAccountNum] < SettingsAccountRowsCnt, @"Tried to tap onto a row meant to be for a concrete account, not for quick or advanced settings");
// User selected one of the 'add account' promts
switch(indexPath.row - [self getAccountNum]) {
case QuickSettingsRow:
[cell initTapCell:NSLocalizedString(@"Add Account", @"")];
break;
case AdvancedSettingsRow:
[cell initTapCell:NSLocalizedString(@"Add Account (advanced)", @"")];
break;
default:
unreachable();
}
#endif
}
break;
}
case kSettingSectionApp: {
switch(indexPath.row) {
case GeneralSettingsRow:
[cell initTapCell:NSLocalizedString(@"General Settings", @"")];
break;
case SoundsRow:
[cell initTapCell:NSLocalizedString(@"Sounds", @"")];
break;
default:
unreachable();
}
break;
}
case kSettingSectionSupport: {
switch(indexPath.row) {
case EmailRow:
[cell initTapCell:NSLocalizedString(@"Email Support", @"")];
break;
case SubmitABugRow:
[cell initTapCell:NSLocalizedString(@"Submit A Bug", @"")];
break;
case OpenFAQRow:
[cell initTapCell:NSLocalizedString(@"Frequently Asked Questions", @"")];
break;
default:
unreachable();
}
break;
}
case kSettingSectionAbout: {
switch(indexPath.row) {
case RateMonalRow: {
#ifdef IS_QUICKSY
[cell initTapCell:NSLocalizedString(@"Rate Quicksy", @"")];
#else
[cell initTapCell:NSLocalizedString(@"Rate Monal", @"")];
#endif
break;
}
case OpenSourceRow: {
[cell initTapCell:NSLocalizedString(@"Open Source", @"")];
break;
}
case PrivacyRow: {
[cell initTapCell:NSLocalizedString(@"Privacy", @"")];
break;
}
case AboutRow: {
[cell initTapCell:NSLocalizedString(@"About", @"")];
break;
}
case VersionRow: {
[cell initCell:NSLocalizedString(@"Version", @"") withLabel:[HelperTools appBuildVersionInfoFor:MLVersionTypeIQ]];
break;
}
#ifdef DEBUG
case LogRow:
#endif
case SettingsAboutRowsCntORLogRow: {
[cell initTapCell:NSLocalizedString(@"Debug", @"")];
break;
}
default: {
unreachable();
}
}
break;
}
default:
unreachable();
}
return cell;
}
-(NSString*) tableView:(UITableView*) tableView titleForHeaderInSection:(NSInteger) section
{
switch(section) {
case kSettingSectionAccounts:
return nil; //the account section does not need a heading (its the first one)
case kSettingSectionApp:
return NSLocalizedString(@"App", @"");
case kSettingSectionSupport:
return NSLocalizedString(@"Support", @"");
case kSettingSectionAbout:
return NSLocalizedString(@"About", @"");
default:
unreachable();
}
return nil; //needed to make the compiler happy
}
-(void)tableView:(UITableView*) tableView didSelectRowAtIndexPath:(NSIndexPath*) indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
switch(indexPath.section)
{
case kSettingSectionAccounts: {
self.selected = indexPath;
if(indexPath.row < (int) [self getAccountNum])
[self performSegueWithIdentifier:@"editXMPP" sender:self];
else
{
switch(indexPath.row - [self getAccountNum]) {
case QuickSettingsRow: {
UIViewController* loginView = [[SwiftuiInterface new] makeViewWithName:@"LogIn"];
[self showDetailViewController:loginView sender:self];
break;
}
case AdvancedSettingsRow: {
UIViewController* advancedLoginView = [[SwiftuiInterface new] makeViewWithName:@"AdvancedLogIn"];
[self showDetailViewController:advancedLoginView sender:self];
break;
}
default:
unreachable();
}
}
break;
}
case kSettingSectionApp: {
switch(indexPath.row) {
case GeneralSettingsRow: {
UIViewController* privacyViewController = [[SwiftuiInterface new] makeViewWithName:@"GeneralSettings"];
[self showDetailViewController:privacyViewController sender:self];
break;
}
case SoundsRow:
[self performSegueWithIdentifier:@"showSounds" sender:self];
break;
default:
unreachable();
}
break;
}
case kSettingSectionSupport: {
switch(indexPath.row) {
case EmailRow:
[self composeMail];
break;
case SubmitABugRow:
[self performSegueWithIdentifier:@"showBug" sender:self];
break;
case OpenFAQRow:
[self performSegueWithIdentifier:@"showFAQ" sender:self];
break;
default:
unreachable();
}
break;
}
case kSettingSectionAbout: {
switch(indexPath.row) {
case RateMonalRow:
#if TARGET_OS_MACCATALYST
[self openStoreProductViewControllerWithITunesItemIdentifier:1637078500];
#elif defined(IS_QUICKSY)
[self openStoreProductViewControllerWithITunesItemIdentifier:6538727270];
#else
[self openStoreProductViewControllerWithITunesItemIdentifier:317711500];
#endif
break;
case OpenSourceRow:
[self performSegueWithIdentifier:@"showOpenSource" sender:self];
break;
case PrivacyRow:
[self performSegueWithIdentifier:@"showPrivacy" sender:self];
break;
case AboutRow:
[self performSegueWithIdentifier:@"showAbout" sender:self];
break;
#ifdef DEBUG
case LogRow:
#endif
case SettingsAboutRowsCntORLogRow:{
UIViewController* logView = [[SwiftuiInterface new] makeViewWithName:@"DebugView"];
[self showDetailViewController:logView sender:self];
break;
}
case VersionRow: {
#ifndef DEBUG
if(_tappedVersionInfo >= 16)
{
[[HelperTools defaultsDB] setBool:YES forKey:@"showLogInSettings"];
[tableView reloadData];
}
else
_tappedVersionInfo++;
#endif
UIPasteboard* pastboard = UIPasteboard.generalPasteboard;
pastboard.string = [HelperTools appBuildVersionInfoFor:MLVersionTypeIQ];
break;
}
default:
unreachable();
}
break;
}
default:
unreachable();
}
}
-(void) openLink:(NSString *) link
{
NSURL* url = [NSURL URLWithString:link];
if ([url.scheme isEqualToString:@"http"] || [url.scheme isEqualToString:@"https"]) {
SFSafariViewController* safariView = [[ SFSafariViewController alloc] initWithURL:url];
[self presentViewController:safariView animated:YES completion:nil];
}
}
#pragma mark - Actions
-(void) openStoreProductViewControllerWithITunesItemIdentifier:(NSInteger) iTunesItemIdentifier {
SKStoreProductViewController *storeViewController = [SKStoreProductViewController new];
storeViewController.delegate = self;
NSNumber* identifier = [NSNumber numberWithInteger:iTunesItemIdentifier];
//, @"action":@"write-review"
NSDictionary* parameters = @{ SKStoreProductParameterITunesItemIdentifier:identifier};
[storeViewController loadProductWithParameters:parameters
completionBlock:^(BOOL result, NSError *error) {
if (result)
[self presentViewController:storeViewController
animated:YES
completion:nil];
else NSLog(@"SKStoreProductViewController: %@", error);
}];
}
-(void) composeMail
{
if([MFMailComposeViewController canSendMail]) {
MFMailComposeViewController* composeVC = [MFMailComposeViewController new];
composeVC.mailComposeDelegate = self;
[composeVC setToRecipients:@[@"info@monal-im.org"]];
[self presentViewController:composeVC animated:YES completion:nil];
}
else
{
UIAlertController* messageAlert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Error", @"") message:NSLocalizedString(@"There is no configured email account. Please email info@monal-im.org .", @"") preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* closeAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Close", @"") style:UIAlertActionStyleCancel handler:^(UIAlertAction* action __unused) {
}];
[messageAlert addAction:closeAction];
[self presentViewController:messageAlert animated:YES completion:nil];
}
}
#pragma mark - Message ui delegate
- (void)mailComposeController:(MFMailComposeViewController *)controller
didFinishWithResult:(MFMailComposeResult)result
error:(NSError *)error
{
[controller dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - SKStoreProductViewControllerDelegate
-(void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController {
[viewController dismissViewControllerAnimated:YES completion:nil];
}
@end