mirror of https://github.com/oxen-io/session-ios
574 changed files with 1868 additions and 40003 deletions
@ -1,145 +0,0 @@
|
||||
// |
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved. |
||||
// |
||||
|
||||
import Foundation |
||||
import UIKit |
||||
import ContactsUI |
||||
|
||||
class AddContactShareToExistingContactViewController: ContactsPicker, ContactsPickerDelegate, CNContactViewControllerDelegate { |
||||
|
||||
// TODO - there are some hard coded assumptions in this VC that assume we are *pushed* onto a |
||||
// navigation controller. That seems fine for now, but if we need to be presented as a modal, |
||||
// or need to notify our presenter about our dismisall or other contact actions, a delegate |
||||
// would be helpful. It seems like this would require some broad changes to the ContactShareViewHelper, |
||||
// so I've left it as is for now, since it happens to work. |
||||
// weak var addToExistingContactDelegate: AddContactShareToExistingContactViewControllerDelegate? |
||||
|
||||
let contactShare: ContactShareViewModel |
||||
|
||||
required init(contactShare: ContactShareViewModel) { |
||||
self.contactShare = contactShare |
||||
super.init(allowsMultipleSelection: false, subtitleCellType: .none) |
||||
|
||||
self.contactsPickerDelegate = self |
||||
} |
||||
|
||||
required public init?(coder aDecoder: NSCoder) { |
||||
notImplemented() |
||||
} |
||||
|
||||
@objc required public init(allowsMultipleSelection: Bool, subtitleCellType: SubtitleCellValue) { |
||||
notImplemented() |
||||
} |
||||
|
||||
// MARK: - ContactsPickerDelegate |
||||
|
||||
func contactsPicker(_: ContactsPicker, contactFetchDidFail error: NSError) { |
||||
owsFailDebug("with error: \(error)") |
||||
|
||||
guard let navigationController = self.navigationController else { |
||||
owsFailDebug("navigationController was unexpectedly nil") |
||||
return |
||||
} |
||||
|
||||
navigationController.popViewController(animated: true) |
||||
} |
||||
|
||||
func contactsPickerDidCancel(_: ContactsPicker) { |
||||
Logger.debug("") |
||||
guard let navigationController = self.navigationController else { |
||||
owsFailDebug("navigationController was unexpectedly nil") |
||||
return |
||||
} |
||||
|
||||
navigationController.popViewController(animated: true) |
||||
} |
||||
|
||||
func contactsPicker(_: ContactsPicker, didSelectContact oldContact: Contact) { |
||||
Logger.debug("") |
||||
|
||||
let contactsManager = Environment.shared.contactsManager |
||||
guard let oldCNContact = contactsManager?.cnContact(withId: oldContact.cnContactId) else { |
||||
owsFailDebug("could not load old CNContact.") |
||||
return |
||||
} |
||||
guard let newCNContact = OWSContacts.systemContact(for: self.contactShare.dbRecord, imageData: self.contactShare.avatarImageData) else { |
||||
owsFailDebug("could not load new CNContact.") |
||||
return |
||||
} |
||||
merge(oldCNContact: oldCNContact, newCNContact: newCNContact) |
||||
} |
||||
|
||||
func merge(oldCNContact: CNContact, newCNContact: CNContact) { |
||||
Logger.debug("") |
||||
|
||||
let mergedCNContact: CNContact = Contact.merge(cnContact: oldCNContact, newCNContact: newCNContact) |
||||
|
||||
// Not actually a "new" contact, but this brings up the edit form rather than the "Read" form |
||||
// saving our users a tap in some cases when we already know they want to edit. |
||||
let contactViewController: CNContactViewController = CNContactViewController(forNewContact: mergedCNContact) |
||||
|
||||
// Default title is "New Contact". We could give a more descriptive title, but anything |
||||
// seems redundant - the context is sufficiently clear. |
||||
contactViewController.title = "" |
||||
contactViewController.allowsActions = false |
||||
contactViewController.allowsEditing = true |
||||
contactViewController.delegate = self |
||||
|
||||
let modal = OWSNavigationController(rootViewController: contactViewController) |
||||
self.present(modal, animated: true) |
||||
} |
||||
|
||||
func contactsPicker(_: ContactsPicker, didSelectMultipleContacts contacts: [Contact]) { |
||||
Logger.debug("") |
||||
owsFailDebug("only supports single contact select") |
||||
|
||||
guard let navigationController = self.navigationController else { |
||||
owsFailDebug("navigationController was unexpectedly nil") |
||||
return |
||||
} |
||||
|
||||
navigationController.popViewController(animated: true) |
||||
} |
||||
|
||||
func contactsPicker(_: ContactsPicker, shouldSelectContact contact: Contact) -> Bool { |
||||
return true |
||||
} |
||||
|
||||
// MARK: - CNContactViewControllerDelegate |
||||
|
||||
public func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?) { |
||||
Logger.debug("") |
||||
|
||||
guard let navigationController = self.navigationController else { |
||||
owsFailDebug("navigationController was unexpectedly nil") |
||||
return |
||||
} |
||||
|
||||
// TODO this is weird - ideally we'd do something like |
||||
// self.delegate?.didFinishAddingContact |
||||
// and the delegate, which knows about our presentation context could do the right thing. |
||||
// |
||||
// As it is, we happen to always be *pushing* this view controller onto a navcontroller, so the |
||||
// following works in all current cases. |
||||
// |
||||
// If we ever wanted to do something different, like present this in a modal, we'd have to rethink. |
||||
|
||||
// We want to pop *this* view *and* the still presented CNContactViewController in a single animation. |
||||
// Note this happens for *cancel* and for *done*. Unfortunately, I don't know of a way to detect the difference |
||||
// between the two, since both just call this method. |
||||
guard let myIndex = navigationController.viewControllers.firstIndex(of: self) else { |
||||
owsFailDebug("myIndex was unexpectedly nil") |
||||
navigationController.popViewController(animated: true) |
||||
navigationController.popViewController(animated: true) |
||||
return |
||||
} |
||||
|
||||
let previousViewControllerIndex = navigationController.viewControllers.index(before: myIndex) |
||||
let previousViewController = navigationController.viewControllers[previousViewControllerIndex] |
||||
|
||||
self.dismiss(animated: false) { |
||||
navigationController.popToViewController(previousViewController, animated: true) |
||||
} |
||||
} |
||||
} |
@ -1,9 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSTableViewController.h" |
||||
|
||||
@interface AdvancedSettingsTableViewController : OWSTableViewController |
||||
|
||||
@end |
@ -1,312 +0,0 @@
|
||||
// |
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved. |
||||
// |
||||
|
||||
#import "AdvancedSettingsTableViewController.h" |
||||
#import "DebugLogger.h" |
||||
#import "DomainFrontingCountryViewController.h" |
||||
#import "OWSCountryMetadata.h" |
||||
#import "Pastelog.h" |
||||
#import "Session-Swift.h" |
||||
#import "TSAccountManager.h" |
||||
#import <PromiseKit/AnyPromise.h> |
||||
#import <Reachability/Reachability.h> |
||||
#import <SignalUtilitiesKit/Environment.h> |
||||
#import <SignalUtilitiesKit/OWSPreferences.h> |
||||
#import <SignalUtilitiesKit/OWSSignalService.h> |
||||
|
||||
NS_ASSUME_NONNULL_BEGIN |
||||
|
||||
@interface AdvancedSettingsTableViewController () |
||||
|
||||
@property (nonatomic) Reachability *reachability; |
||||
|
||||
@end |
||||
|
||||
#pragma mark - |
||||
|
||||
@implementation AdvancedSettingsTableViewController |
||||
|
||||
- (void)loadView |
||||
{ |
||||
[super loadView]; |
||||
|
||||
self.title = NSLocalizedString(@"SETTINGS_ADVANCED_TITLE", @""); |
||||
|
||||
self.reachability = [Reachability reachabilityForInternetConnection]; |
||||
|
||||
[self observeNotifications]; |
||||
|
||||
[self updateTableContents]; |
||||
} |
||||
|
||||
- (void)observeNotifications |
||||
{ |
||||
[[NSNotificationCenter defaultCenter] addObserver:self |
||||
selector:@selector(socketStateDidChange) |
||||
name:kNSNotification_OWSWebSocketStateDidChange |
||||
object:nil]; |
||||
[[NSNotificationCenter defaultCenter] addObserver:self |
||||
selector:@selector(reachabilityChanged) |
||||
name:kReachabilityChangedNotification |
||||
object:nil]; |
||||
} |
||||
|
||||
- (void)dealloc |
||||
{ |
||||
[[NSNotificationCenter defaultCenter] removeObserver:self]; |
||||
} |
||||
|
||||
- (void)socketStateDidChange |
||||
{ |
||||
OWSAssertIsOnMainThread(); |
||||
|
||||
[self updateTableContents]; |
||||
} |
||||
|
||||
- (void)reachabilityChanged |
||||
{ |
||||
OWSAssertIsOnMainThread(); |
||||
|
||||
[self updateTableContents]; |
||||
} |
||||
|
||||
- (void)viewWillAppear:(BOOL)animated |
||||
{ |
||||
[super viewWillAppear:animated]; |
||||
|
||||
[self updateTableContents]; |
||||
} |
||||
|
||||
#pragma mark - Table Contents |
||||
|
||||
- (void)updateTableContents |
||||
{ |
||||
OWSTableContents *contents = [OWSTableContents new]; |
||||
|
||||
__weak AdvancedSettingsTableViewController *weakSelf = self; |
||||
|
||||
OWSTableSection *loggingSection = [OWSTableSection new]; |
||||
loggingSection.headerTitle = NSLocalizedString(@"LOGGING_SECTION", nil); |
||||
[loggingSection addItem:[OWSTableItem switchItemWithText:NSLocalizedString(@"SETTINGS_ADVANCED_DEBUGLOG", @"") |
||||
accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"enable_debug_log") |
||||
isOnBlock:^{ |
||||
return [OWSPreferences isLoggingEnabled]; |
||||
} |
||||
isEnabledBlock:^{ |
||||
return YES; |
||||
} |
||||
target:weakSelf |
||||
selector:@selector(didToggleEnableLogSwitch:)]]; |
||||
|
||||
|
||||
if ([OWSPreferences isLoggingEnabled]) { |
||||
[loggingSection |
||||
addItem:[OWSTableItem actionItemWithText:NSLocalizedString(@"SETTINGS_ADVANCED_SUBMIT_DEBUGLOG", @"") |
||||
accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"submit_debug_log") |
||||
actionBlock:^{ |
||||
OWSLogInfo(@"Submitting debug logs"); |
||||
[DDLog flushLog]; |
||||
[Pastelog submitLogs]; |
||||
}]]; |
||||
} |
||||
|
||||
[contents addSection:loggingSection]; |
||||
|
||||
OWSTableSection *pushNotificationsSection = [OWSTableSection new]; |
||||
pushNotificationsSection.headerTitle |
||||
= NSLocalizedString(@"PUSH_REGISTER_TITLE", @"Used in table section header and alert view title contexts"); |
||||
[pushNotificationsSection addItem:[OWSTableItem actionItemWithText:NSLocalizedString(@"REREGISTER_FOR_PUSH", nil) |
||||
accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME( |
||||
self, @"reregister_push_notifications") |
||||
actionBlock:^{ |
||||
[weakSelf syncPushTokens]; |
||||
}]]; |
||||
[contents addSection:pushNotificationsSection]; |
||||
|
||||
// Censorship circumvention has certain disadvantages so it should only be |
||||
// used if necessary. Therefore: |
||||
// |
||||
// * We disable this setting if the user has a phone number from a censored region - |
||||
// censorship circumvention will be auto-activated for this user. |
||||
// * We disable this setting if the user is already connected; they're not being |
||||
// censored. |
||||
// * We continue to show this setting so long as it is set to allow users to disable |
||||
// it, for example when they leave a censored region. |
||||
OWSTableSection *censorshipSection = [OWSTableSection new]; |
||||
censorshipSection.headerTitle = NSLocalizedString(@"SETTINGS_ADVANCED_CENSORSHIP_CIRCUMVENTION_HEADER", |
||||
@"Table header for the 'censorship circumvention' section."); |
||||
BOOL isAnySocketOpen = TSSocketManager.shared.highestSocketState == OWSWebSocketStateOpen; |
||||
if (OWSSignalService.sharedInstance.hasCensoredPhoneNumber) { |
||||
if (OWSSignalService.sharedInstance.isCensorshipCircumventionManuallyDisabled) { |
||||
censorshipSection.footerTitle |
||||
= NSLocalizedString(@"SETTINGS_ADVANCED_CENSORSHIP_CIRCUMVENTION_FOOTER_MANUALLY_DISABLED", |
||||
@"Table footer for the 'censorship circumvention' section shown when censorship circumvention has " |
||||
@"been manually disabled."); |
||||
} else { |
||||
censorshipSection.footerTitle = NSLocalizedString( |
||||
@"SETTINGS_ADVANCED_CENSORSHIP_CIRCUMVENTION_FOOTER_AUTO_ENABLED", |
||||
@"Table footer for the 'censorship circumvention' section shown when censorship circumvention has been " |
||||
@"auto-enabled based on local phone number."); |
||||
} |
||||
} else if (isAnySocketOpen) { |
||||
censorshipSection.footerTitle |
||||
= NSLocalizedString(@"SETTINGS_ADVANCED_CENSORSHIP_CIRCUMVENTION_FOOTER_WEBSOCKET_CONNECTED", |
||||
@"Table footer for the 'censorship circumvention' section shown when the app is connected to the " |
||||
@"Signal service."); |
||||
} else if (!self.reachability.isReachable) { |
||||
censorshipSection.footerTitle |
||||
= NSLocalizedString(@"SETTINGS_ADVANCED_CENSORSHIP_CIRCUMVENTION_FOOTER_NO_CONNECTION", |
||||
@"Table footer for the 'censorship circumvention' section shown when the app is not connected to the " |
||||
@"internet."); |
||||
} else { |
||||
censorshipSection.footerTitle = NSLocalizedString(@"SETTINGS_ADVANCED_CENSORSHIP_CIRCUMVENTION_FOOTER", |
||||
@"Table footer for the 'censorship circumvention' section when censorship circumvention can be manually " |
||||
@"enabled."); |
||||
} |
||||
|
||||
// Do enable if : |
||||
// |
||||
// * ...Censorship circumvention is already manually enabled (to allow users to disable it). |
||||
// |
||||
// Otherwise, don't enable if: |
||||
// |
||||
// * ...Censorship circumvention is already enabled based on the local phone number. |
||||
// * ...The websocket is connected, since that demonstrates that no censorship is in effect. |
||||
// * ...The internet is not reachable, since we don't want to let users to activate |
||||
// censorship circumvention unnecessarily, e.g. if they just don't have a valid |
||||
// internet connection. |
||||
OWSTableSwitchBlock isCensorshipCircumventionOnBlock = ^{ |
||||
return OWSSignalService.sharedInstance.isCensorshipCircumventionActive; |
||||
}; |
||||
Reachability *reachability = self.reachability; |
||||
OWSTableSwitchBlock isManualCensorshipCircumventionOnEnabledBlock = ^{ |
||||
OWSSignalService *service = OWSSignalService.sharedInstance; |
||||
if (service.isCensorshipCircumventionActive) { |
||||
return YES; |
||||
} else if (service.hasCensoredPhoneNumber && service.isCensorshipCircumventionManuallyDisabled) { |
||||
return YES; |
||||
} else if (TSSocketManager.shared.highestSocketState == OWSWebSocketStateOpen) { |
||||
return NO; |
||||
} else { |
||||
return reachability.isReachable; |
||||
} |
||||
}; |
||||
|
||||
[censorshipSection |
||||
addItem:[OWSTableItem switchItemWithText:NSLocalizedString(@"SETTINGS_ADVANCED_CENSORSHIP_CIRCUMVENTION", |
||||
@"Label for the 'manual censorship circumvention' switch.") |
||||
accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME(self, @"censorship_circumvention") |
||||
isOnBlock:isCensorshipCircumventionOnBlock |
||||
isEnabledBlock:isManualCensorshipCircumventionOnEnabledBlock |
||||
target:weakSelf |
||||
selector:@selector(didToggleEnableCensorshipCircumventionSwitch:)]]; |
||||
|
||||
if (OWSSignalService.sharedInstance.isCensorshipCircumventionManuallyActivated) { |
||||
OWSCountryMetadata *manualCensorshipCircumventionCountry = |
||||
[weakSelf ensureManualCensorshipCircumventionCountry]; |
||||
OWSAssertDebug(manualCensorshipCircumventionCountry); |
||||
NSString *text = [NSString |
||||
stringWithFormat:NSLocalizedString(@"SETTINGS_ADVANCED_CENSORSHIP_CIRCUMVENTION_COUNTRY_FORMAT", |
||||
@"Label for the 'manual censorship circumvention' country. Embeds {{the manual " |
||||
@"censorship circumvention country}}."), |
||||
manualCensorshipCircumventionCountry.localizedCountryName]; |
||||
[censorshipSection addItem:[OWSTableItem disclosureItemWithText:text |
||||
actionBlock:^{ |
||||
[weakSelf showDomainFrontingCountryView]; |
||||
}]]; |
||||
} |
||||
[contents addSection:censorshipSection]; |
||||
|
||||
self.contents = contents; |
||||
} |
||||
|
||||
- (void)showDomainFrontingCountryView |
||||
{ |
||||
DomainFrontingCountryViewController *vc = [DomainFrontingCountryViewController new]; |
||||
[self.navigationController pushViewController:vc animated:YES]; |
||||
} |
||||
|
||||
- (OWSCountryMetadata *)ensureManualCensorshipCircumventionCountry |
||||
{ |
||||
OWSAssertIsOnMainThread(); |
||||
|
||||
OWSCountryMetadata *countryMetadata = nil; |
||||
NSString *countryCode = OWSSignalService.sharedInstance.manualCensorshipCircumventionCountryCode; |
||||
if (countryCode) { |
||||
countryMetadata = [OWSCountryMetadata countryMetadataForCountryCode:countryCode]; |
||||
} |
||||
|
||||
if (!countryMetadata) { |
||||
countryCode = [PhoneNumber defaultCountryCode]; |
||||
if (countryCode) { |
||||
countryMetadata = [OWSCountryMetadata countryMetadataForCountryCode:countryCode]; |
||||
} |
||||
} |
||||
|
||||
if (!countryMetadata) { |
||||
countryCode = @"US"; |
||||
countryMetadata = [OWSCountryMetadata countryMetadataForCountryCode:countryCode]; |
||||
OWSAssertDebug(countryMetadata); |
||||
} |
||||
|
||||
if (countryMetadata) { |
||||
// Ensure the "manual censorship circumvention" country state is in sync. |
||||
OWSSignalService.sharedInstance.manualCensorshipCircumventionCountryCode = countryCode; |
||||
} |
||||
|
||||
return countryMetadata; |
||||
} |
||||
|
||||
#pragma mark - Actions |
||||
|
||||
- (void)syncPushTokens |
||||
{ |
||||
OWSSyncPushTokensJob *job = |
||||
[[OWSSyncPushTokensJob alloc] initWithAccountManager:AppEnvironment.shared.accountManager |
||||
preferences:Environment.shared.preferences]; |
||||
job.uploadOnlyIfStale = NO; |
||||
[job run] |
||||
.then(^{ |
||||
[OWSAlerts showAlertWithTitle:NSLocalizedString(@"PUSH_REGISTER_SUCCESS", |
||||
@"Title of alert shown when push tokens sync job succeeds.")]; |
||||
}) |
||||
.catch(^(NSError *error) { |
||||
[OWSAlerts showAlertWithTitle:NSLocalizedString(@"REGISTRATION_BODY", |
||||
@"Title of alert shown when push tokens sync job fails.")]; |
||||
}); |
||||
} |
||||
|
||||
- (void)didToggleEnableLogSwitch:(UISwitch *)sender |
||||
{ |
||||
if (!sender.isOn) { |
||||
OWSLogInfo(@"disabling logging."); |
||||
[[DebugLogger sharedLogger] wipeLogs]; |
||||
[[DebugLogger sharedLogger] disableFileLogging]; |
||||
} else { |
||||
[[DebugLogger sharedLogger] enableFileLogging]; |
||||
OWSLogInfo(@"enabling logging."); |
||||
} |
||||
|
||||
[OWSPreferences setIsLoggingEnabled:sender.isOn]; |
||||
|
||||
[self updateTableContents]; |
||||
} |
||||
|
||||
- (void)didToggleEnableCensorshipCircumventionSwitch:(UISwitch *)sender |
||||
{ |
||||
OWSSignalService *service = OWSSignalService.sharedInstance; |
||||
if (sender.isOn) { |
||||
service.isCensorshipCircumventionManuallyDisabled = NO; |
||||
service.isCensorshipCircumventionManuallyActivated = YES; |
||||
} else { |
||||
service.isCensorshipCircumventionManuallyDisabled = YES; |
||||
service.isCensorshipCircumventionManuallyActivated = NO; |
||||
} |
||||
|
||||
[self updateTableContents]; |
||||
} |
||||
|
||||
@end |
||||
|
||||
NS_ASSUME_NONNULL_END |