session-ios/Session/Settings/SettingsVC.swift

450 lines
23 KiB
Swift
Raw Normal View History

2020-02-20 04:37:17 +01:00
final class SettingsVC : BaseVC, AvatarViewHelperDelegate {
2019-12-06 01:54:04 +01:00
private var profilePictureToBeUploaded: UIImage?
private var displayNameToBeUploaded: String?
private var isEditingDisplayName = false { didSet { handleIsEditingDisplayNameChanged() } }
// MARK: Components
2019-12-05 00:10:09 +01:00
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)
2020-12-17 01:37:53 +01:00
result.accessibilityLabel = "Edit profile picture button"
result.isAccessibilityElement = true
2019-12-05 00:10:09 +01:00
return result
}()
2019-12-06 01:54:04 +01:00
private lazy var profilePictureUtilities: AvatarViewHelper = {
let result = AvatarViewHelper()
result.delegate = self
return result
}()
2019-12-05 00:10:09 +01:00
private lazy var displayNameLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.text
result.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
2019-12-05 00:10:09 +01:00
result.lineBreakMode = .byTruncatingTail
2019-12-06 01:54:04 +01:00
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)
2019-12-06 01:54:04 +01:00
result.textAlignment = .center
2020-12-17 01:37:53 +01:00
result.accessibilityLabel = "Edit display name text field"
2019-12-05 00:10:09 +01:00
return result
}()
private lazy var copyButton: Button = {
2019-12-06 04:42:43 +01:00
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
}()
2021-02-22 04:47:40 +01:00
// MARK: Settings
private static let buttonHeight = isIPhone5OrSmaller ? CGFloat(52) : CGFloat(75)
// MARK: Lifecycle
override func viewDidLoad() {
2020-02-20 04:37:17 +01:00
super.viewDidLoad()
setUpGradientBackground()
setUpNavBarStyle()
setNavBarTitle(NSLocalizedString("vc_settings_title", comment: ""))
// Set up navigation bar buttons
let backButton = UIBarButtonItem(title: "Back", style: .plain, target: nil, action: nil)
backButton.tintColor = Colors.text
navigationItem.backBarButtonItem = backButton
2019-12-06 01:54:04 +01:00
updateNavigationBarButtons()
// Set up profile picture view
2019-12-06 01:54:04 +01:00
let profilePictureTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(showEditProfilePictureUI))
profilePictureView.addGestureRecognizer(profilePictureTapGestureRecognizer)
2021-02-26 04:42:46 +01:00
profilePictureView.publicKey = getUserHexEncodedPublicKey()
profilePictureView.update()
// Set up display name label
2020-11-16 00:34:47 +01:00
displayNameLabel.text = OWSProfileManager.shared().profileNameForRecipient(withID: getUserHexEncodedPublicKey())
2019-12-06 01:54:04 +01:00
// Set up display name container
let displayNameContainer = UIView()
2020-12-17 01:37:53 +01:00
displayNameContainer.accessibilityLabel = "Edit display name text field"
displayNameContainer.isAccessibilityElement = true
2019-12-06 01:54:04 +01:00
displayNameContainer.addSubview(displayNameLabel)
displayNameLabel.pin(to: displayNameContainer)
displayNameContainer.addSubview(displayNameTextField)
displayNameTextField.pin(to: displayNameContainer)
displayNameContainer.set(.height, to: 40)
displayNameTextField.alpha = 0
2020-01-08 06:10:12 +01:00
let displayNameContainerTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(showEditDisplayNameUI))
displayNameContainer.addGestureRecognizer(displayNameContainerTapGestureRecognizer)
// Set up header view
2019-12-06 01:54:04 +01:00
let headerStackView = UIStackView(arrangedSubviews: [ profilePictureView, displayNameContainer ])
headerStackView.axis = .vertical
headerStackView.spacing = Values.smallSpacing
headerStackView.alignment = .center
// Set up separator
let separator = Separator(title: NSLocalizedString("your_session_id", comment: ""))
// Set up public key label
let publicKeyLabel = UILabel()
publicKeyLabel.textColor = Colors.text
2020-06-18 06:41:02 +02:00
publicKeyLabel.font = Fonts.spaceMono(ofSize: isIPhone5OrSmaller ? Values.mediumFontSize : Values.largeFontSize)
publicKeyLabel.numberOfLines = 0
publicKeyLabel.textAlignment = .center
publicKeyLabel.lineBreakMode = .byCharWrapping
2020-11-16 00:34:47 +01:00
publicKeyLabel.text = getUserHexEncodedPublicKey()
// Set up share button
2019-12-05 00:10:09 +01:00
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)
// Set up button container
let buttonContainer = UIStackView(arrangedSubviews: [ copyButton, shareButton ])
buttonContainer.axis = .horizontal
buttonContainer.spacing = Values.mediumSpacing
buttonContainer.distribution = .fillEqually
2019-12-05 00:10:09 +01:00
// Set up top stack view
let topStackView = UIStackView(arrangedSubviews: [ headerStackView, separator, publicKeyLabel, 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
// Set up setting buttons stack view
2020-08-24 03:49:08 +02:00
getSettingButtons().forEach { settingButtonOrSeparator in
settingButtonsStackView.addArrangedSubview(settingButtonOrSeparator)
}
2020-09-04 02:15:33 +02:00
// Set up version label
let versionLabel = UILabel()
2021-01-29 01:46:32 +01:00
versionLabel.textColor = Colors.text.withAlphaComponent(Values.mediumOpacity)
2020-09-04 02:15:33 +02:00
versionLabel.font = .systemFont(ofSize: Values.verySmallFontSize)
versionLabel.numberOfLines = 0
versionLabel.textAlignment = .center
versionLabel.lineBreakMode = .byCharWrapping
let version = Bundle.main.infoDictionary!["CFBundleShortVersionString"]!
let buildNumber = Bundle.main.infoDictionary!["CFBundleVersion"]!
versionLabel.text = "Version \(version) (\(buildNumber))"
// Set up stack view
2020-09-04 02:15:33 +02:00
let stackView = UIStackView(arrangedSubviews: [ topStackView, settingButtonsStackView, versionLabel ])
stackView.axis = .vertical
stackView.spacing = Values.largeSpacing
stackView.alignment = .fill
2019-12-05 00:10:09 +01:00
stackView.layoutMargins = UIEdgeInsets(top: Values.mediumSpacing, left: 0, bottom: Values.mediumSpacing, right: 0)
stackView.isLayoutMarginsRelativeArrangement = true
2019-12-05 00:10:09 +01:00
stackView.set(.width, to: UIScreen.main.bounds.width)
// Set up scroll view
let scrollView = UIScrollView()
scrollView.showsVerticalScrollIndicator = false
scrollView.addSubview(stackView)
stackView.pin(to: scrollView)
view.addSubview(scrollView)
scrollView.pin(to: view)
}
2019-12-09 03:24:03 +01:00
private func getSettingButtons() -> [UIView] {
func getSeparator() -> UIView {
let result = UIView()
result.backgroundColor = Colors.separator
result.set(.height, to: Values.separatorThickness)
return result
}
2019-12-05 00:10:09 +01:00
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!
}
2020-08-26 08:20:46 +02:00
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)
2019-12-05 00:10:09 +01:00
button.addTarget(self, action: selector, for: UIControl.Event.touchUpInside)
2021-02-22 04:47:40 +01:00
button.set(.height, to: SettingsVC.buttonHeight)
2019-12-05 00:10:09 +01:00
return button
}
var result = [
2019-12-09 03:24:03 +01:00
getSeparator(),
getSettingButton(withTitle: NSLocalizedString("vc_settings_privacy_button_title", comment: ""), color: Colors.text, action: #selector(showPrivacySettings)),
2019-12-09 03:24:03 +01:00
getSeparator(),
2020-11-02 04:46:08 +01:00
getSettingButton(withTitle: NSLocalizedString("vc_settings_notifications_button_title", comment: ""), color: Colors.text, action: #selector(showNotificationSettings)),
getSeparator(),
2020-11-16 00:34:47 +01:00
getSettingButton(withTitle: "Invite", color: Colors.text, action: #selector(sendInvitation)),
getSeparator()
]
if !KeyPairUtilities.hasV2KeyPair() {
result += [
getSettingButton(withTitle: "Upgrade Session ID", color: Colors.text, action: #selector(upgradeSessionID)),
getSeparator()
]
}
result += [
2020-11-16 00:34:47 +01:00
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()
2019-12-05 00:10:09 +01:00
]
return result
2019-12-05 00:10:09 +01:00
}
// MARK: General
@objc private func enableCopyButton() {
copyButton.isUserInteractionEnabled = true
UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.copyButton.setTitle(NSLocalizedString("copy", comment: ""), for: UIControl.State.normal)
}, completion: nil)
}
2019-12-06 01:54:04 +01:00
func avatarActionSheetTitle() -> String? {
return "Update Profile Picture"
2019-12-06 01:54:04 +01:00
}
func fromViewController() -> UIViewController {
return self
}
func hasClearAvatarAction() -> Bool {
2020-08-04 05:38:54 +02:00
return false
2019-12-06 01:54:04 +01:00
}
func clearAvatarActionLabel() -> String {
return "Clear"
2019-12-06 01:54:04 +01:00
}
// 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
2020-12-17 01:37:53 +01:00
cancelButton.accessibilityLabel = "Cancel button"
cancelButton.isAccessibilityElement = true
2019-12-06 01:54:04 +01:00
navigationItem.leftBarButtonItem = cancelButton
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handleSaveDisplayNameButtonTapped))
doneButton.tintColor = Colors.text
2020-12-17 01:37:53 +01:00
doneButton.accessibilityLabel = "Done button"
doneButton.isAccessibilityElement = true
2019-12-06 01:54:04 +01:00
navigationItem.rightBarButtonItem = doneButton
} else {
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
closeButton.tintColor = Colors.text
2020-12-17 01:37:53 +01:00
closeButton.accessibilityLabel = "Close button"
closeButton.isAccessibilityElement = true
2019-12-06 01:54:04 +01:00
navigationItem.leftBarButtonItem = closeButton
if #available(iOS 13, *) { // Pre iOS 13 the user can't switch actively but the app still responds to system changes
2020-08-24 04:14:22 +02:00
let 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
2020-08-24 04:14:22 +02:00
appModeButton.addTarget(self, action: #selector(switchAppMode), for: UIControl.Event.touchUpInside)
2020-12-17 01:37:53 +01:00
appModeButton.accessibilityLabel = "Switch app mode button"
2020-08-24 04:14:22 +02:00
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)
2020-12-17 01:37:53 +01:00
qrCodeButton.accessibilityLabel = "Show QR code button"
2020-08-24 04:14:22 +02:00
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
}
2019-12-06 01:54:04 +01:00
}
}
func avatarDidChange(_ image: UIImage) {
let maxSize = Int(kOWSProfileManager_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) {
2021-02-23 06:01:06 +01:00
let userDefaults = UserDefaults.standard
2020-11-16 00:34:47 +01:00
let displayName = displayNameToBeUploaded ?? OWSProfileManager.shared().profileNameForRecipient(withID: getUserHexEncodedPublicKey())
let profilePicture = profilePictureToBeUploaded ?? OWSProfileManager.shared().profileAvatar(forRecipientId: getUserHexEncodedPublicKey())
2021-02-23 06:01:06 +01:00
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self, displayNameToBeUploaded, profilePictureToBeUploaded] modalActivityIndicator in
2019-12-06 01:54:04 +01:00
OWSProfileManager.shared().updateLocalProfileName(displayName, avatarImage: profilePicture, success: {
2021-02-23 06:01:06 +01:00
if displayNameToBeUploaded != nil {
userDefaults[.lastDisplayNameUpdate] = Date()
}
if profilePictureToBeUploaded != nil {
userDefaults[.lastProfilePictureUpdate] = Date()
}
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.forceSyncConfigurationNowIfNeeded().retainUntilComplete()
2019-12-06 01:54:04 +01:00
DispatchQueue.main.async {
modalActivityIndicator.dismiss {
guard let self = self else { return }
self.profilePictureView.update()
self.displayNameLabel.text = displayName
self.profilePictureToBeUploaded = nil
self.displayNameToBeUploaded = nil
}
}
}, failure: { error in
2019-12-06 01:54:04 +01:00
DispatchQueue.main.async {
modalActivityIndicator.dismiss {
var isMaxFileSizeExceeded = false
2020-11-11 07:45:50 +01:00
if let error = error as? DotNetAPI.Error {
isMaxFileSizeExceeded = (error == .maxFileSizeExceeded)
}
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)
2019-12-06 01:54:04 +01:00
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil)
}
}
2020-02-17 06:46:13 +01:00
}, requiresSync: true)
2019-12-06 01:54:04 +01:00
}
}
@objc override internal func handleAppModeChangedNotification(_ notification: Notification) {
super.handleAppModeChangedNotification(notification)
2020-08-24 04:14:22 +02:00
updateNavigationBarButtons()
settingButtonsStackView.arrangedSubviews.forEach { settingButton in
settingButtonsStackView.removeArrangedSubview(settingButton)
settingButton.removeFromSuperview()
}
2020-08-24 03:49:08 +02:00
getSettingButtons().forEach { settingButtonOrSeparator in
settingButtonsStackView.addArrangedSubview(settingButtonOrSeparator) // Re-do the setting buttons
}
}
2019-12-06 01:54:04 +01:00
// MARK: Interaction
@objc private func close() {
dismiss(animated: true, completion: nil)
}
2020-08-17 02:08:30 +02:00
@objc private func switchAppMode() {
let newAppMode: AppMode = isLightMode ? .dark : .light
AppModeManager.shared.setCurrentAppMode(to: newAppMode)
2020-08-17 02:08:30 +02:00
}
@objc private func showQRCode() {
2019-12-05 05:28:32 +01:00
let qrCodeVC = QRCodeVC()
navigationController!.pushViewController(qrCodeVC, animated: true)
}
2019-12-06 01:54:04 +01:00
@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("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: ""))
2019-12-06 01:54:04 +01:00
}
guard !OWSProfileManager.shared().isProfileNameTooLong(displayName) else {
return showError(title: NSLocalizedString("vc_settings_display_name_too_long_error", comment: ""))
2019-12-06 01:54:04 +01:00
}
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() {
2020-11-16 00:34:47 +01:00
UIPasteboard.general.string = getUserHexEncodedPublicKey()
copyButton.isUserInteractionEnabled = false
UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.copyButton.setTitle("Copied", for: UIControl.State.normal)
}, completion: nil)
Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(enableCopyButton), userInfo: nil, repeats: false)
}
@objc private func sharePublicKey() {
2020-11-16 00:34:47 +01:00
let shareVC = UIActivityViewController(activityItems: [ getUserHexEncodedPublicKey() ], applicationActivities: nil)
navigationController!.present(shareVC, animated: true, completion: nil)
}
2019-12-05 00:10:09 +01:00
@objc private func showPrivacySettings() {
let privacySettingsVC = PrivacySettingsTableViewController()
navigationController!.pushViewController(privacySettingsVC, animated: true)
}
2019-12-05 00:10:09 +01:00
@objc private func showNotificationSettings() {
let notificationSettingsVC = NotificationSettingsViewController()
navigationController!.pushViewController(notificationSettingsVC, animated: true)
}
2020-11-02 04:46:08 +01:00
@objc private func sendInvitation() {
2020-11-16 00:34:47 +01:00
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())!"
2020-11-02 04:46:08 +01:00
let shareVC = UIActivityViewController(activityItems: [ invitation ], applicationActivities: nil)
navigationController!.present(shareVC, animated: true, completion: nil)
}
@objc private func upgradeSessionID() {
let message = "Youre upgrading to a new Session ID. This will give you improved privacy and security, but it will clear ALL app data. Contacts and conversations will be lost. Proceed?"
let alert = UIAlertController(title: "Upgrade Session ID?", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Yes", style: .destructive) { _ in
2020-12-16 06:22:46 +01:00
Storage.prepareForV2KeyPairMigration()
})
alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
}
2019-12-05 00:10:09 +01:00
@objc private func showSeed() {
let seedModal = SeedModal()
seedModal.modalPresentationStyle = .overFullScreen
2019-12-05 01:42:31 +01:00
seedModal.modalTransitionStyle = .crossDissolve
2019-12-05 00:10:09 +01:00
present(seedModal, animated: true, completion: nil)
}
2019-12-05 00:10:09 +01:00
@objc private func clearAllData() {
let nukeDataModal = NukeDataModal()
nukeDataModal.modalPresentationStyle = .overFullScreen
2019-12-05 01:42:31 +01:00
nukeDataModal.modalTransitionStyle = .crossDissolve
2019-12-05 00:10:09 +01:00
present(nukeDataModal, animated: true, completion: nil)
}
}