mirror of https://github.com/oxen-io/session-ios
clean
parent
671c5f6ada
commit
3563d7d469
@ -1,681 +0,0 @@
|
||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import UIKit
|
||||
import SessionUIKit
|
||||
import SessionUtilitiesKit
|
||||
import SessionMessagingKit
|
||||
import SignalUtilitiesKit
|
||||
|
||||
final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
||||
private var displayNameToBeUploaded: String?
|
||||
private var isEditingDisplayName = false { didSet { handleIsEditingDisplayNameChanged() } }
|
||||
|
||||
private var viewWidth: NSLayoutConstraint?
|
||||
|
||||
// MARK: - Components
|
||||
|
||||
private lazy var profilePictureView: ProfilePictureView = {
|
||||
let result = ProfilePictureView()
|
||||
let size = Values.largeProfilePictureSize
|
||||
result.size = size
|
||||
result.set(.width, to: size)
|
||||
result.set(.height, to: size)
|
||||
result.accessibilityLabel = "Edit profile picture button"
|
||||
result.isAccessibilityElement = true
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var profilePictureUtilities: AvatarViewHelper = {
|
||||
let result = AvatarViewHelper()
|
||||
result.delegate = self
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var displayNameLabel: UILabel = {
|
||||
let result = UILabel()
|
||||
result.textColor = Colors.text
|
||||
result.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
|
||||
result.lineBreakMode = .byTruncatingTail
|
||||
result.textAlignment = .center
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var displayNameTextField: TextField = {
|
||||
let result = TextField(placeholder: NSLocalizedString("vc_settings_display_name_text_field_hint", comment: ""), usesDefaultHeight: false)
|
||||
result.textAlignment = .center
|
||||
result.accessibilityLabel = "Edit display name text field"
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var publicKeyLabel: UILabel = {
|
||||
let result = UILabel()
|
||||
result.textColor = Colors.text
|
||||
result.font = Fonts.spaceMono(ofSize: isIPhone5OrSmaller ? Values.mediumFontSize : Values.largeFontSize)
|
||||
result.numberOfLines = 0
|
||||
result.textAlignment = .center
|
||||
result.lineBreakMode = .byCharWrapping
|
||||
result.text = getUserHexEncodedPublicKey()
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var copyButton: Button = {
|
||||
let result = Button(style: .prominentOutline, size: .medium)
|
||||
result.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
|
||||
result.addTarget(self, action: #selector(copyPublicKey), for: UIControl.Event.touchUpInside)
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var settingButtonsStackView: UIStackView = {
|
||||
let result = UIStackView()
|
||||
result.axis = .vertical
|
||||
result.alignment = .fill
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var inviteButton: UIButton = {
|
||||
let result = UIButton()
|
||||
result.setTitle(NSLocalizedString("vc_settings_invite_a_friend_button_title", comment: ""), for: UIControl.State.normal)
|
||||
result.setTitleColor(Colors.text, for: UIControl.State.normal)
|
||||
result.titleLabel!.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
||||
result.addTarget(self, action: #selector(sendInvitation), for: UIControl.Event.touchUpInside)
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var faqButton: UIButton = {
|
||||
let result = UIButton()
|
||||
result.setTitle(NSLocalizedString("vc_settings_faq_button_title", comment: ""), for: UIControl.State.normal)
|
||||
result.setTitleColor(Colors.text, for: UIControl.State.normal)
|
||||
result.titleLabel!.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
||||
result.addTarget(self, action: #selector(openFAQ), for: UIControl.Event.touchUpInside)
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var surveyButton: UIButton = {
|
||||
let result = UIButton()
|
||||
result.setTitle(NSLocalizedString("vc_settings_survey_button_title", comment: ""), for: UIControl.State.normal)
|
||||
result.setTitleColor(Colors.text, for: UIControl.State.normal)
|
||||
result.titleLabel?.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
||||
result.addTarget(self, action: #selector(openSurvey), for: UIControl.Event.touchUpInside)
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var supportButton: UIButton = {
|
||||
let result = UIButton()
|
||||
result.setTitle(NSLocalizedString("vc_settings_support_button_title", comment: ""), for: UIControl.State.normal)
|
||||
result.setTitleColor(Colors.text, for: UIControl.State.normal)
|
||||
result.titleLabel!.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
||||
result.addTarget(self, action: #selector(shareLogs), for: UIControl.Event.touchUpInside)
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var helpTranslateButton: UIButton = {
|
||||
let result = UIButton()
|
||||
result.setTitle(NSLocalizedString("vc_settings_help_us_translate_button_title", comment: ""), for: UIControl.State.normal)
|
||||
result.setTitleColor(Colors.text, for: UIControl.State.normal)
|
||||
result.titleLabel!.font = .boldSystemFont(ofSize: Values.smallFontSize)
|
||||
result.addTarget(self, action: #selector(helpTranslate), for: UIControl.Event.touchUpInside)
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var logoImageView: UIImageView = {
|
||||
let result = UIImageView()
|
||||
result.set(.height, to: 24)
|
||||
result.contentMode = .scaleAspectFit
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
private lazy var versionLabel: UILabel = {
|
||||
let result = UILabel()
|
||||
result.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity)
|
||||
result.font = .systemFont(ofSize: Values.verySmallFontSize)
|
||||
result.numberOfLines = 0
|
||||
result.textAlignment = .center
|
||||
result.lineBreakMode = .byCharWrapping
|
||||
let version = Bundle.main.infoDictionary!["CFBundleShortVersionString"]!
|
||||
let buildNumber = Bundle.main.infoDictionary!["CFBundleVersion"]!
|
||||
result.text = "Version \(version) (\(buildNumber))"
|
||||
|
||||
return result
|
||||
}()
|
||||
|
||||
// MARK: - Settings
|
||||
|
||||
private static let buttonHeight = isIPhone5OrSmaller ? CGFloat(52) : CGFloat(75)
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
setUpGradientBackground()
|
||||
setUpNavBarStyle()
|
||||
setNavBarTitle(NSLocalizedString("vc_settings_title", comment: ""))
|
||||
|
||||
// Navigation bar buttons
|
||||
updateNavigationBarButtons()
|
||||
|
||||
// Profile picture view
|
||||
let profile: Profile = Profile.fetchOrCreateCurrentUser()
|
||||
let profilePictureTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(showEditProfilePictureUI))
|
||||
profilePictureView.addGestureRecognizer(profilePictureTapGestureRecognizer)
|
||||
profilePictureView
|
||||
.update(
|
||||
publicKey: profile.id,
|
||||
profile: profile,
|
||||
threadVariant: .contact
|
||||
)
|
||||
// Display name label
|
||||
displayNameLabel.text = profile.name
|
||||
|
||||
// Display name container
|
||||
let displayNameContainer = UIView()
|
||||
displayNameContainer.accessibilityLabel = "Edit display name text field"
|
||||
displayNameContainer.isAccessibilityElement = true
|
||||
displayNameContainer.addSubview(displayNameLabel)
|
||||
displayNameLabel.pin(to: displayNameContainer)
|
||||
displayNameContainer.addSubview(displayNameTextField)
|
||||
displayNameTextField.pin(to: displayNameContainer)
|
||||
displayNameContainer.set(.height, to: 40)
|
||||
displayNameTextField.alpha = 0
|
||||
let displayNameContainerTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(showEditDisplayNameUI))
|
||||
displayNameContainer.addGestureRecognizer(displayNameContainerTapGestureRecognizer)
|
||||
|
||||
// Header view
|
||||
let headerStackView = UIStackView(arrangedSubviews: [ profilePictureView, displayNameContainer ])
|
||||
headerStackView.axis = .vertical
|
||||
headerStackView.spacing = Values.smallSpacing
|
||||
headerStackView.alignment = .center
|
||||
|
||||
// Separator
|
||||
let separator = Separator(title: NSLocalizedString("your_session_id", comment: ""))
|
||||
|
||||
// Share button
|
||||
let shareButton = Button(style: .regular, size: .medium)
|
||||
shareButton.setTitle(NSLocalizedString("share", comment: ""), for: UIControl.State.normal)
|
||||
shareButton.addTarget(self, action: #selector(sharePublicKey), for: UIControl.Event.touchUpInside)
|
||||
|
||||
// Button container
|
||||
let buttonContainer = UIStackView(arrangedSubviews: [ copyButton, shareButton ])
|
||||
buttonContainer.axis = .horizontal
|
||||
buttonContainer.spacing = UIDevice.current.isIPad ? Values.iPadButtonSpacing : Values.mediumSpacing
|
||||
buttonContainer.distribution = .fillEqually
|
||||
|
||||
if (UIDevice.current.isIPad) {
|
||||
buttonContainer.layoutMargins = UIEdgeInsets(top: 0, left: Values.iPadButtonContainerMargin, bottom: 0, right: Values.iPadButtonContainerMargin)
|
||||
buttonContainer.isLayoutMarginsRelativeArrangement = true
|
||||
}
|
||||
// User session id container
|
||||
let userPublicKeyContainer = UIView(wrapping: publicKeyLabel, withInsets: .zero, shouldAdaptForIPadWithWidth: Values.iPadUserSessionIdContainerWidth)
|
||||
|
||||
// Top stack view
|
||||
let topStackView = UIStackView(arrangedSubviews: [ headerStackView, separator, userPublicKeyContainer, buttonContainer ])
|
||||
topStackView.axis = .vertical
|
||||
topStackView.spacing = Values.largeSpacing
|
||||
topStackView.alignment = .fill
|
||||
topStackView.layoutMargins = UIEdgeInsets(top: 0, left: Values.largeSpacing, bottom: 0, right: Values.largeSpacing)
|
||||
topStackView.isLayoutMarginsRelativeArrangement = true
|
||||
|
||||
// Setting buttons stack view
|
||||
getSettingButtons().forEach { settingButtonOrSeparator in
|
||||
settingButtonsStackView.addArrangedSubview(settingButtonOrSeparator)
|
||||
}
|
||||
|
||||
// Oxen logo
|
||||
updateLogo()
|
||||
let logoContainer = UIView()
|
||||
logoContainer.addSubview(logoImageView)
|
||||
logoImageView.pin(.top, to: .top, of: logoContainer)
|
||||
logoContainer.pin(.bottom, to: .bottom, of: logoImageView)
|
||||
logoImageView.centerXAnchor.constraint(equalTo: logoContainer.centerXAnchor, constant: -2).isActive = true
|
||||
|
||||
// Main stack view
|
||||
let stackView = UIStackView(arrangedSubviews: [ topStackView, settingButtonsStackView, inviteButton, faqButton, surveyButton, supportButton, helpTranslateButton, logoContainer, versionLabel ])
|
||||
stackView.axis = .vertical
|
||||
stackView.spacing = Values.largeSpacing
|
||||
stackView.alignment = .fill
|
||||
stackView.layoutMargins = UIEdgeInsets(top: Values.mediumSpacing, left: 0, bottom: Values.mediumSpacing, right: 0)
|
||||
stackView.isLayoutMarginsRelativeArrangement = true
|
||||
viewWidth = stackView.set(.width, to: UIScreen.main.bounds.width)
|
||||
|
||||
// Scroll view
|
||||
let scrollView = UIScrollView()
|
||||
scrollView.showsVerticalScrollIndicator = false
|
||||
scrollView.addSubview(stackView)
|
||||
stackView.pin(to: scrollView)
|
||||
view.addSubview(scrollView)
|
||||
scrollView.pin(to: view)
|
||||
}
|
||||
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
viewWidth?.constant = size.width
|
||||
}
|
||||
|
||||
private func getSettingButtons() -> [UIView] {
|
||||
func getSeparator() -> UIView {
|
||||
let result = UIView()
|
||||
result.backgroundColor = Colors.separator
|
||||
result.set(.height, to: Values.separatorThickness)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func getSettingButton(withTitle title: String, color: UIColor, action selector: Selector) -> UIButton {
|
||||
let button = UIButton()
|
||||
button.setTitle(title, for: UIControl.State.normal)
|
||||
button.setTitleColor(color, for: UIControl.State.normal)
|
||||
button.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize)
|
||||
button.titleLabel!.textAlignment = .center
|
||||
|
||||
func getImage(withColor color: UIColor) -> UIImage {
|
||||
let rect = CGRect(origin: CGPoint.zero, size: CGSize(width: 1, height: 1))
|
||||
UIGraphicsBeginImageContext(rect.size)
|
||||
|
||||
let context = UIGraphicsGetCurrentContext()!
|
||||
context.setFillColor(color.cgColor)
|
||||
context.fill(rect)
|
||||
|
||||
let image = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
return image!
|
||||
}
|
||||
|
||||
let backgroundColor = isLightMode ? UIColor(hex: 0xF9F9F9) : UIColor(hex: 0x1B1B1B)
|
||||
button.setBackgroundImage(getImage(withColor: backgroundColor), for: UIControl.State.normal)
|
||||
|
||||
let selectedColor = isLightMode ? UIColor(hex: 0xDFDFDF) : UIColor(hex: 0x0C0C0C)
|
||||
button.setBackgroundImage(getImage(withColor: selectedColor), for: UIControl.State.highlighted)
|
||||
button.addTarget(self, action: selector, for: UIControl.Event.touchUpInside)
|
||||
button.set(.height, to: SettingsVC.buttonHeight)
|
||||
|
||||
return button
|
||||
}
|
||||
|
||||
let pathButton = getSettingButton(withTitle: NSLocalizedString("vc_path_title", comment: ""), color: Colors.text, action: #selector(showPath))
|
||||
let pathStatusView = PathStatusView()
|
||||
pathStatusView.set(.width, to: PathStatusView.size)
|
||||
pathStatusView.set(.height, to: PathStatusView.size)
|
||||
|
||||
pathButton.addSubview(pathStatusView)
|
||||
pathStatusView.pin(.leading, to: .trailing, of: pathButton.titleLabel!, withInset: Values.smallSpacing)
|
||||
pathStatusView.autoVCenterInSuperview()
|
||||
|
||||
return [
|
||||
getSeparator(),
|
||||
pathButton,
|
||||
getSeparator(),
|
||||
getSettingButton(withTitle: NSLocalizedString("vc_settings_privacy_button_title", comment: ""), color: Colors.text, action: #selector(showPrivacySettings)),
|
||||
getSeparator(),
|
||||
getSettingButton(withTitle: NSLocalizedString("vc_settings_notifications_button_title", comment: ""), color: Colors.text, action: #selector(showNotificationSettings)),
|
||||
getSeparator(),
|
||||
getSettingButton(withTitle: NSLocalizedString("MESSAGE_REQUESTS_TITLE", comment: ""), color: Colors.text, action: #selector(showMessageRequests)),
|
||||
getSeparator(),
|
||||
getSettingButton(withTitle: NSLocalizedString("CHATS_TITLE", comment: ""), color: Colors.text, action: #selector(showChatSettings)),
|
||||
getSeparator(),
|
||||
getSettingButton(withTitle: NSLocalizedString("vc_settings_recovery_phrase_button_title", comment: ""), color: Colors.text, action: #selector(showSeed)),
|
||||
getSeparator(),
|
||||
getSettingButton(withTitle: NSLocalizedString("vc_settings_clear_all_data_button_title", comment: ""), color: Colors.destructive, action: #selector(clearAllData)),
|
||||
getSeparator()
|
||||
]
|
||||
}
|
||||
|
||||
// MARK: - General
|
||||
|
||||
@objc private func enableCopyButton() {
|
||||
copyButton.isUserInteractionEnabled = true
|
||||
|
||||
UIView.transition(
|
||||
with: copyButton,
|
||||
duration: 0.25,
|
||||
options: .transitionCrossDissolve,
|
||||
animations: {
|
||||
self.copyButton.setTitle("copy".localized(), for: .normal)
|
||||
},
|
||||
completion: nil
|
||||
)
|
||||
}
|
||||
|
||||
func avatarActionSheetTitle() -> String? { return "Update Profile Picture" }
|
||||
func fromViewController() -> UIViewController { return self }
|
||||
func hasClearAvatarAction() -> Bool { return false }
|
||||
func clearAvatarActionLabel() -> String { return "Clear" }
|
||||
|
||||
// MARK: - Updating
|
||||
|
||||
private func handleIsEditingDisplayNameChanged() {
|
||||
updateNavigationBarButtons()
|
||||
|
||||
UIView.animate(withDuration: 0.25) {
|
||||
self.displayNameLabel.alpha = self.isEditingDisplayName ? 0 : 1
|
||||
self.displayNameTextField.alpha = self.isEditingDisplayName ? 1 : 0
|
||||
}
|
||||
|
||||
if isEditingDisplayName {
|
||||
displayNameTextField.becomeFirstResponder()
|
||||
}
|
||||
else {
|
||||
displayNameTextField.resignFirstResponder()
|
||||
}
|
||||
}
|
||||
|
||||
private func updateNavigationBarButtons() {
|
||||
if isEditingDisplayName {
|
||||
let cancelButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(handleCancelDisplayNameEditingButtonTapped))
|
||||
cancelButton.tintColor = Colors.text
|
||||
cancelButton.accessibilityLabel = "Cancel button"
|
||||
cancelButton.isAccessibilityElement = true
|
||||
navigationItem.leftBarButtonItem = cancelButton
|
||||
|
||||
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handleSaveDisplayNameButtonTapped))
|
||||
doneButton.tintColor = Colors.text
|
||||
doneButton.accessibilityLabel = "Done button"
|
||||
doneButton.isAccessibilityElement = true
|
||||
navigationItem.rightBarButtonItem = doneButton
|
||||
}
|
||||
else {
|
||||
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
|
||||
closeButton.tintColor = Colors.text
|
||||
closeButton.accessibilityLabel = "Close button"
|
||||
closeButton.isAccessibilityElement = true
|
||||
navigationItem.leftBarButtonItem = closeButton
|
||||
|
||||
let appModeIcon: UIImage
|
||||
if isSystemDefault {
|
||||
appModeIcon = isDarkMode ? #imageLiteral(resourceName: "ic_theme_auto").withTintColor(.white) : #imageLiteral(resourceName: "ic_theme_auto").withTintColor(.black)
|
||||
}
|
||||
else {
|
||||
appModeIcon = isDarkMode ? #imageLiteral(resourceName: "ic_dark_theme_on").withTintColor(.white) : #imageLiteral(resourceName: "ic_dark_theme_off").withTintColor(.black)
|
||||
}
|
||||
|
||||
let appModeButton = UIButton()
|
||||
appModeButton.setImage(appModeIcon, for: UIControl.State.normal)
|
||||
appModeButton.tintColor = Colors.text
|
||||
appModeButton.addTarget(self, action: #selector(switchAppMode), for: UIControl.Event.touchUpInside)
|
||||
appModeButton.accessibilityLabel = "Switch app mode button"
|
||||
|
||||
let qrCodeIcon = isDarkMode ? #imageLiteral(resourceName: "QRCode").withTintColor(.white) : #imageLiteral(resourceName: "QRCode").withTintColor(.black)
|
||||
let qrCodeButton = UIButton()
|
||||
qrCodeButton.setImage(qrCodeIcon, for: UIControl.State.normal)
|
||||
qrCodeButton.tintColor = Colors.text
|
||||
qrCodeButton.addTarget(self, action: #selector(showQRCode), for: UIControl.Event.touchUpInside)
|
||||
qrCodeButton.accessibilityLabel = "Show QR code button"
|
||||
|
||||
let stackView = UIStackView(arrangedSubviews: [ appModeButton, qrCodeButton ])
|
||||
stackView.axis = .horizontal
|
||||
stackView.spacing = Values.mediumSpacing
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: stackView)
|
||||
}
|
||||
}
|
||||
|
||||
func avatarDidChange(_ image: UIImage?, filePath: String?) {
|
||||
updateProfile(
|
||||
profilePicture: image,
|
||||
profilePictureFilePath: filePath,
|
||||
isUpdatingDisplayName: false,
|
||||
isUpdatingProfilePicture: true
|
||||
)
|
||||
}
|
||||
|
||||
func clearAvatar() {
|
||||
updateProfile(
|
||||
profilePicture: nil,
|
||||
profilePictureFilePath: nil,
|
||||
isUpdatingDisplayName: false,
|
||||
isUpdatingProfilePicture: true
|
||||
)
|
||||
}
|
||||
|
||||
private func updateProfile(
|
||||
profilePicture: UIImage?,
|
||||
profilePictureFilePath: String?,
|
||||
isUpdatingDisplayName: Bool,
|
||||
isUpdatingProfilePicture: Bool
|
||||
) {
|
||||
let userDefaults = UserDefaults.standard
|
||||
let name: String? = (displayNameToBeUploaded ?? Profile.fetchOrCreateCurrentUser().name)
|
||||
let imageFilePath: String? = (profilePictureFilePath ?? ProfileManager.profileAvatarFilepath(id: getUserHexEncodedPublicKey()))
|
||||
|
||||
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self, displayNameToBeUploaded] modalActivityIndicator in
|
||||
ProfileManager.updateLocal(
|
||||
queue: DispatchQueue.global(qos: .default),
|
||||
profileName: (name ?? ""),
|
||||
image: profilePicture,
|
||||
imageFilePath: imageFilePath,
|
||||
requiredSync: true,
|
||||
success: { db, updatedProfile in
|
||||
if displayNameToBeUploaded != nil {
|
||||
userDefaults[.lastDisplayNameUpdate] = Date()
|
||||
}
|
||||
|
||||
if isUpdatingProfilePicture {
|
||||
userDefaults[.lastProfilePictureUpdate] = Date()
|
||||
}
|
||||
|
||||
try MessageSender.syncConfiguration(db, forceSyncNow: true).retainUntilComplete()
|
||||
|
||||
// Wait for the database transaction to complete before updating the UI
|
||||
db.afterNextTransactionCommit { _ in
|
||||
DispatchQueue.main.async {
|
||||
modalActivityIndicator.dismiss {
|
||||
self?.profilePictureView.update(
|
||||
publicKey: updatedProfile.id,
|
||||
profile: updatedProfile,
|
||||
threadVariant: .contact
|
||||
)
|
||||
self?.displayNameLabel.text = name
|
||||
self?.displayNameToBeUploaded = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
failure: { error in
|
||||
DispatchQueue.main.async {
|
||||
modalActivityIndicator.dismiss {
|
||||
let isMaxFileSizeExceeded = (error == .avatarUploadMaxFileSizeExceeded)
|
||||
let title = isMaxFileSizeExceeded ? "Maximum File Size Exceeded" : "Couldn't Update Profile"
|
||||
let message = isMaxFileSizeExceeded ? "Please select a smaller photo and try again" : "Please check your internet connection and try again"
|
||||
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "BUTTON_OK".localized(), style: .default, handler: nil))
|
||||
self?.present(alert, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@objc override internal func handleAppModeChangedNotification(_ notification: Notification) {
|
||||
super.handleAppModeChangedNotification(notification)
|
||||
|
||||
updateNavigationBarButtons()
|
||||
settingButtonsStackView.arrangedSubviews.forEach { settingButton in
|
||||
settingButtonsStackView.removeArrangedSubview(settingButton)
|
||||
settingButton.removeFromSuperview()
|
||||
}
|
||||
getSettingButtons().forEach { settingButtonOrSeparator in
|
||||
settingButtonsStackView.addArrangedSubview(settingButtonOrSeparator) // Re-do the setting buttons
|
||||
}
|
||||
updateLogo()
|
||||
}
|
||||
|
||||
private func updateLogo() {
|
||||
let logoName = isLightMode ? "OxenLightMode" : "OxenDarkMode"
|
||||
logoImageView.image = UIImage(named: logoName)!
|
||||
}
|
||||
|
||||
// MARK: - Interaction
|
||||
|
||||
@objc private func close() {
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc private func switchAppMode() {
|
||||
let alertVC = UIAlertController.init(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||
let systemModeAction = UIAlertAction.init(title: NSLocalizedString("system_mode_theme", comment: ""), style: .default) { _ in
|
||||
AppModeManager.shared.setAppModeToSystemDefault()
|
||||
}
|
||||
alertVC.addAction(systemModeAction)
|
||||
|
||||
let darkModeAction = UIAlertAction.init(title: NSLocalizedString("dark_mode_theme", comment: ""), style: .default) { _ in
|
||||
AppModeManager.shared.setCurrentAppMode(to: .dark)
|
||||
}
|
||||
alertVC.addAction(darkModeAction)
|
||||
|
||||
let lightModeAction = UIAlertAction.init(title: NSLocalizedString("light_mode_theme", comment: ""), style: .default) { _ in
|
||||
AppModeManager.shared.setCurrentAppMode(to: .light)
|
||||
}
|
||||
alertVC.addAction(lightModeAction)
|
||||
|
||||
let cancelAction = UIAlertAction.init(title: NSLocalizedString("TXT_CANCEL_TITLE", comment: ""), style: .cancel) {_ in }
|
||||
alertVC.addAction(cancelAction)
|
||||
|
||||
self.presentAlert(alertVC)
|
||||
}
|
||||
|
||||
@objc private func showQRCode() {
|
||||
let qrCodeVC = QRCodeVC()
|
||||
navigationController!.pushViewController(qrCodeVC, animated: true)
|
||||
}
|
||||
|
||||
@objc private func handleCancelDisplayNameEditingButtonTapped() {
|
||||
isEditingDisplayName = false
|
||||
}
|
||||
|
||||
@objc private func handleSaveDisplayNameButtonTapped() {
|
||||
func showError(title: String, message: String = "") {
|
||||
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_OK", comment: ""), style: .default, handler: nil))
|
||||
presentAlert(alert)
|
||||
}
|
||||
let displayName = displayNameTextField.text!.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
|
||||
guard !displayName.isEmpty else {
|
||||
return showError(title: NSLocalizedString("vc_settings_display_name_missing_error", comment: ""))
|
||||
}
|
||||
guard !ProfileManager.isToLong(profileName: displayName) else {
|
||||
return showError(title: NSLocalizedString("vc_settings_display_name_too_long_error", comment: ""))
|
||||
}
|
||||
isEditingDisplayName = false
|
||||
displayNameToBeUploaded = displayName
|
||||
updateProfile(
|
||||
profilePicture: nil,
|
||||
profilePictureFilePath: nil,
|
||||
isUpdatingDisplayName: true,
|
||||
isUpdatingProfilePicture: false
|
||||
)
|
||||
}
|
||||
|
||||
@objc private func showEditProfilePictureUI() {
|
||||
profilePictureUtilities.showChangeAvatarUI()
|
||||
}
|
||||
|
||||
@objc private func showEditDisplayNameUI() {
|
||||
isEditingDisplayName = true
|
||||
}
|
||||
|
||||
@objc private func copyPublicKey() {
|
||||
UIPasteboard.general.string = getUserHexEncodedPublicKey()
|
||||
copyButton.isUserInteractionEnabled = false
|
||||
UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
|
||||
self.copyButton.setTitle(NSLocalizedString("copied", comment: ""), for: UIControl.State.normal)
|
||||
}, completion: nil)
|
||||
Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(enableCopyButton), userInfo: nil, repeats: false)
|
||||
}
|
||||
|
||||
@objc private func sharePublicKey() {
|
||||
let shareVC = UIActivityViewController(activityItems: [ getUserHexEncodedPublicKey() ], applicationActivities: nil)
|
||||
if UIDevice.current.isIPad {
|
||||
shareVC.excludedActivityTypes = []
|
||||
shareVC.popoverPresentationController?.permittedArrowDirections = []
|
||||
shareVC.popoverPresentationController?.sourceView = self.view
|
||||
shareVC.popoverPresentationController?.sourceRect = self.view.bounds
|
||||
}
|
||||
navigationController!.present(shareVC, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc private func showPath() {
|
||||
let pathVC = PathVC()
|
||||
navigationController!.pushViewController(pathVC, animated: true)
|
||||
}
|
||||
|
||||
@objc private func showPrivacySettings() {
|
||||
let privacySettingsVC = PrivacySettingsTableViewController()
|
||||
navigationController!.pushViewController(privacySettingsVC, animated: true)
|
||||
}
|
||||
|
||||
@objc private func showNotificationSettings() {
|
||||
let notificationSettingsVC = NotificationSettingsViewController()
|
||||
navigationController!.pushViewController(notificationSettingsVC, animated: true)
|
||||
}
|
||||
|
||||
@objc private func showMessageRequests() {
|
||||
let viewController: MessageRequestsViewController = MessageRequestsViewController()
|
||||
self.navigationController?.pushViewController(viewController, animated: true)
|
||||
}
|
||||
|
||||
@objc private func showChatSettings() {
|
||||
let chatSettingsVC = ChatSettingsViewController()
|
||||
navigationController!.pushViewController(chatSettingsVC, animated: true)
|
||||
}
|
||||
|
||||
@objc private func showSeed() {
|
||||
let seedModal = SeedModal()
|
||||
seedModal.modalPresentationStyle = .overFullScreen
|
||||
seedModal.modalTransitionStyle = .crossDissolve
|
||||
present(seedModal, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc private func clearAllData() {
|
||||
let nukeDataModal = NukeDataModal()
|
||||
nukeDataModal.modalPresentationStyle = .overFullScreen
|
||||
nukeDataModal.modalTransitionStyle = .crossDissolve
|
||||
present(nukeDataModal, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc private func sendInvitation() {
|
||||
let invitation = "Hey, I've been using Session to chat with complete privacy and security. Come join me! Download it at https://getsession.org/. My Session ID is \(getUserHexEncodedPublicKey()) !"
|
||||
let shareVC = UIActivityViewController(activityItems: [ invitation ], applicationActivities: nil)
|
||||
if UIDevice.current.isIPad {
|
||||
shareVC.excludedActivityTypes = []
|
||||
shareVC.popoverPresentationController?.permittedArrowDirections = []
|
||||
shareVC.popoverPresentationController?.sourceView = self.view
|
||||
shareVC.popoverPresentationController?.sourceRect = self.view.bounds
|
||||
}
|
||||
navigationController!.present(shareVC, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc private func openFAQ() {
|
||||
let url = URL(string: "https://getsession.org/faq")!
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
|
||||
@objc private func openSurvey() {
|
||||
let url = URL(string: "https://getsession.org/survey")!
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
|
||||
@objc private func shareLogs() {
|
||||
let shareLogsModal = ShareLogsModal()
|
||||
shareLogsModal.modalPresentationStyle = .overFullScreen
|
||||
shareLogsModal.modalTransitionStyle = .crossDissolve
|
||||
present(shareLogsModal, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc private func helpTranslate() {
|
||||
let url = URL(string: "https://crowdin.com/project/session-ios")!
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppContext.h"
|
||||
#import "OWSNavigationController.h"
|
||||
#import <SignalUtilitiesKit/SignalUtilitiesKit-Swift.h>
|
||||
#import <SessionUIKit/SessionUIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface UINavigationController (OWSNavigationController) <UINavigationBarDelegate, NavBarLayoutDelegate>
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
// Expose that UINavigationController already secretly implements UIGestureRecognizerDelegate
|
||||
// so we can call [super navigationBar:shouldPopItem] in our own implementation to take advantage
|
||||
// of the important side effects of that method.
|
||||
@interface OWSNavigationController () <UIGestureRecognizerDelegate>
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation OWSNavigationController
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super initWithNavigationBarClass:[OWSNavigationBar class] toolbarClass:nil];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
[self setupNavbar];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController
|
||||
{
|
||||
self = [self init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
[self pushViewController:rootViewController animated:NO];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.interactivePopGestureRecognizer.delegate = self;
|
||||
}
|
||||
|
||||
- (BOOL)prefersStatusBarHidden
|
||||
{
|
||||
if (self.ows_prefersStatusBarHidden) {
|
||||
return self.ows_prefersStatusBarHidden.boolValue;
|
||||
}
|
||||
return [super prefersStatusBarHidden];
|
||||
}
|
||||
|
||||
#pragma mark - UINavigationBarDelegate
|
||||
|
||||
- (void)setupNavbar
|
||||
{
|
||||
if (![self.navigationBar isKindOfClass:[OWSNavigationBar class]]) {
|
||||
OWSFailDebug(@"navigationBar was unexpected class: %@", self.navigationBar);
|
||||
return;
|
||||
}
|
||||
OWSNavigationBar *navbar = (OWSNavigationBar *)self.navigationBar;
|
||||
navbar.navBarLayoutDelegate = self;
|
||||
[self updateLayoutForNavbar:navbar];
|
||||
}
|
||||
|
||||
// All OWSNavigationController serve as the UINavigationBarDelegate for their navbar.
|
||||
// We override shouldPopItem: in order to cancel some back button presses - for example,
|
||||
// if a view has unsaved changes.
|
||||
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
|
||||
{
|
||||
OWSAssertDebug(self.interactivePopGestureRecognizer.delegate == self);
|
||||
UIViewController *topViewController = self.topViewController;
|
||||
|
||||
// wasBackButtonClicked is YES if the back button was pressed but not
|
||||
// if a back gesture was performed or if the view is popped programmatically.
|
||||
BOOL wasBackButtonClicked = topViewController.navigationItem == item;
|
||||
BOOL result = YES;
|
||||
if (wasBackButtonClicked) {
|
||||
if ([topViewController conformsToProtocol:@protocol(OWSNavigationView)]) {
|
||||
id<OWSNavigationView> navigationView = (id<OWSNavigationView>)topViewController;
|
||||
result = ![navigationView shouldCancelNavigationBack];
|
||||
}
|
||||
}
|
||||
|
||||
// If we're not going to cancel the pop/back, we need to call the super
|
||||
// implementation since it has important side effects.
|
||||
if (result) {
|
||||
// NOTE: result might end up NO if the super implementation cancels the
|
||||
// the pop/back.
|
||||
[super navigationBar:navigationBar shouldPopItem:item];
|
||||
result = YES;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#pragma mark - UIGestureRecognizerDelegate
|
||||
|
||||
// We serve as the UIGestureRecognizerDelegate of the interactivePopGestureRecognizer
|
||||
// in order to cancel some "back" gestures - for example,
|
||||
// if a view has unsaved changes.
|
||||
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
|
||||
{
|
||||
OWSAssertDebug(gestureRecognizer == self.interactivePopGestureRecognizer);
|
||||
|
||||
UIViewController *topViewController = self.topViewController;
|
||||
if ([topViewController conformsToProtocol:@protocol(OWSNavigationView)]) {
|
||||
id<OWSNavigationView> navigationView = (id<OWSNavigationView>)topViewController;
|
||||
return ![navigationView shouldCancelNavigationBack];
|
||||
} else {
|
||||
UIViewController *rootViewController = self.viewControllers.firstObject;
|
||||
if (topViewController == rootViewController) {
|
||||
return NO;
|
||||
} else {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - NavBarLayoutDelegate
|
||||
|
||||
- (void)navBarCallLayoutDidChangeWithNavbar:(OWSNavigationBar *)navbar
|
||||
{
|
||||
[self updateLayoutForNavbar:navbar];
|
||||
[self setNeedsStatusBarAppearanceUpdate];
|
||||
}
|
||||
|
||||
- (UIStatusBarStyle)preferredStatusBarStyle
|
||||
{
|
||||
if (!CurrentAppContext().isMainApp) {
|
||||
return super.preferredStatusBarStyle;
|
||||
} else if (OWSWindowManager.sharedManager.hasCall) {
|
||||
// Status bar is overlaying the green "call banner"
|
||||
return UIStatusBarStyleLightContent;
|
||||
} else {
|
||||
return LKAppModeUtilities.isLightMode ? UIStatusBarStyleDefault : UIStatusBarStyleLightContent;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateLayoutForNavbar:(OWSNavigationBar *)navbar
|
||||
{
|
||||
OWSLogDebug(@"");
|
||||
|
||||
[UIView setAnimationsEnabled:NO];
|
||||
|
||||
if (!CurrentAppContext().isMainApp) {
|
||||
self.additionalSafeAreaInsets = UIEdgeInsetsZero;
|
||||
} else if (OWSWindowManager.sharedManager.hasCall) {
|
||||
self.additionalSafeAreaInsets = UIEdgeInsetsMake(20, 0, 0, 0);
|
||||
} else {
|
||||
self.additionalSafeAreaInsets = UIEdgeInsetsZero;
|
||||
}
|
||||
|
||||
[navbar layoutSubviews];
|
||||
[UIView setAnimationsEnabled:YES];
|
||||
}
|
||||
|
||||
#pragma mark - Orientation
|
||||
|
||||
- (BOOL)shouldAutorotate
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
Loading…
Reference in New Issue