mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
c7e8071dd1
Added a setting to control whether open group messages older than 6 months should be pruned Added some defensive coding to prevent an edge-case which could cause a crash (wasn't filtering out a potential invalid row from the home screen data) Fixed a bug where preOffer call messages weren't correctly sending push notifications Fixed a bug where all incoming calls would be rejected and seen as calls disabled Fixed a bug where the copy on call info messages was displaying the sender's name instead of the thread contact's name for outgoing calls Fixed a bug where the input view wouldn't appear when creating new DM conversations Fixed a bug where threads might not show the message request approval UI Fixed an issue where some logic might not have run correctly when first registering an account Fixed a bug where the note to self thread could incorrectly appear when restoring a device Updated the GarbageCollectionJob to run onActive instead of onLaunch (since it's likely we will rarely launch) Updated the logic for erasing an account from a device
686 lines
32 KiB
Swift
686 lines
32 KiB
Swift
// 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 profilePictureToBeUploaded: UIImage?
|
|
private var displayNameToBeUploaded: String?
|
|
private var isEditingDisplayName = false { didSet { handleIsEditingDisplayNameChanged() } }
|
|
|
|
// 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
|
|
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)
|
|
}
|
|
|
|
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: 0xFCFCFC) : 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 debugReMigrateButton = getSettingButton(withTitle: "DEBUG - Re-Migrate Database", color: Colors.destructive, action: #selector(remigrateDatabase))
|
|
|
|
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(),
|
|
debugReMigrateButton,
|
|
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
|
|
|
|
if #available(iOS 13, *) { // Pre iOS 13 the user can't switch actively but the app still responds to system changes
|
|
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)
|
|
}
|
|
else {
|
|
let qrCodeIcon = isDarkMode ? #imageLiteral(resourceName: "QRCode").asTintedImage(color: .white) : #imageLiteral(resourceName: "QRCode").asTintedImage(color: .black)
|
|
let qrCodeButton = UIBarButtonItem(image: qrCodeIcon, style: .plain, target: self, action: #selector(showQRCode))
|
|
qrCodeButton.tintColor = Colors.text
|
|
navigationItem.rightBarButtonItem = qrCodeButton
|
|
}
|
|
}
|
|
}
|
|
|
|
func avatarDidChange(_ image: UIImage) {
|
|
let maxSize = Int(ProfileManager.maxAvatarDiameter)
|
|
profilePictureToBeUploaded = image.resizedImage(toFillPixelSize: CGSize(width: maxSize, height: maxSize))
|
|
updateProfile(isUpdatingDisplayName: false, isUpdatingProfilePicture: true)
|
|
}
|
|
|
|
func clearAvatar() {
|
|
profilePictureToBeUploaded = nil
|
|
updateProfile(isUpdatingDisplayName: false, isUpdatingProfilePicture: true)
|
|
}
|
|
|
|
private func updateProfile(isUpdatingDisplayName: Bool, isUpdatingProfilePicture: Bool) {
|
|
let userDefaults = UserDefaults.standard
|
|
let name: String? = (displayNameToBeUploaded ?? Profile.fetchOrCreateCurrentUser().name)
|
|
let profilePicture: UIImage? = (profilePictureToBeUploaded ?? ProfileManager.profileAvatar(id: getUserHexEncodedPublicKey()))
|
|
|
|
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self, displayNameToBeUploaded, profilePictureToBeUploaded] modalActivityIndicator in
|
|
ProfileManager.updateLocal(
|
|
queue: DispatchQueue.global(qos: .default),
|
|
profileName: (name ?? ""),
|
|
avatarImage: profilePicture,
|
|
requiredSync: true,
|
|
success: { db, updatedProfile in
|
|
if displayNameToBeUploaded != nil {
|
|
userDefaults[.lastDisplayNameUpdate] = Date()
|
|
}
|
|
|
|
if profilePictureToBeUploaded != nil {
|
|
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?.profilePictureToBeUploaded = nil
|
|
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(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 remigrateDatabase() {
|
|
let alert = UIAlertController(
|
|
title: "Session",
|
|
message: "Are you sure you want to re-migrate from your old database state?\n\nWarning: If you had a migration error and picked the \"Restore your account\" option this will result in a complete loss of data and the need to manually restore from the seed",
|
|
preferredStyle: .alert
|
|
)
|
|
alert.addAction(UIAlertAction(title: "Re-migrate", style: .destructive) { _ in
|
|
GRDBStorage.deleteDatabaseFiles()
|
|
try? GRDBStorage.deleteDbKeys()
|
|
exit(1)
|
|
})
|
|
alert.addAction(UIAlertAction(title: "Cancel", style: .default))
|
|
|
|
navigationController?.present(alert, animated: true)
|
|
}
|
|
|
|
@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)
|
|
}
|
|
}
|