final class RegisterVC : UIViewController { private var seed: Data! { didSet { updateKeyPair() } } private var keyPair: ECKeyPair! { didSet { updatePublicKeyLabel() } } // MARK: Components private lazy var publicKeyLabel: UILabel = { let result = UILabel() result.textColor = Colors.text result.font = Fonts.spaceMono(ofSize: Values.largeFontSize) result.numberOfLines = 0 result.textAlignment = .center result.lineBreakMode = .byCharWrapping return result }() private lazy var copyPublicKeyButton: Button = { let result = Button(style: .prominentOutline, size: .large) result.setTitle(NSLocalizedString("Copy", comment: ""), for: UIControl.State.normal) result.addTarget(self, action: #selector(copyPublicKey), for: UIControl.Event.touchUpInside) return result }() private lazy var legalLabel: UILabel = { let result = UILabel() result.textColor = Colors.text result.font = .systemFont(ofSize: Values.verySmallFontSize) let text = "By using this service, you agree to our Terms and Conditions and Privacy Statement" let attributedText = NSMutableAttributedString(string: text) attributedText.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.verySmallFontSize), range: (text as NSString).range(of: "Terms and Conditions")) attributedText.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.verySmallFontSize), range: (text as NSString).range(of: "Privacy Statement")) result.attributedText = attributedText result.numberOfLines = 0 result.textAlignment = .center result.lineBreakMode = .byWordWrapping return result }() // MARK: Settings override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent } // MARK: Lifecycle override func viewDidLoad() { // Set gradient background view.backgroundColor = .clear let gradient = Gradients.defaultLokiBackground view.setGradient(gradient) // Set up navigation bar let navigationBar = navigationController!.navigationBar navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default) navigationBar.shadowImage = UIImage() navigationBar.isTranslucent = false navigationBar.barTintColor = Colors.navigationBarBackground // Set up logo image view let logoImageView = UIImageView() logoImageView.image = #imageLiteral(resourceName: "Loki") logoImageView.contentMode = .scaleAspectFit logoImageView.set(.width, to: 32) logoImageView.set(.height, to: 32) navigationItem.titleView = logoImageView // Set up title label let titleLabel = UILabel() titleLabel.textColor = Colors.text titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize) titleLabel.text = NSLocalizedString("Say hello to your Loki Messenger ID", comment: "") titleLabel.numberOfLines = 0 titleLabel.lineBreakMode = .byWordWrapping // Set up explanation label let explanationLabel = UILabel() explanationLabel.textColor = Colors.text explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) explanationLabel.text = NSLocalizedString("Your Loki Messenger ID is the unique address that people can use to contact you on Loki Messenger. With no connection to your real identity, your Loki Messenger ID is totally anonymous and private by design.", comment: "") explanationLabel.numberOfLines = 0 explanationLabel.lineBreakMode = .byWordWrapping // Set up public key label container let publicKeyLabelContainer = UIView() publicKeyLabelContainer.addSubview(publicKeyLabel) publicKeyLabel.pin(to: publicKeyLabelContainer, withInset: Values.mediumSpacing) publicKeyLabelContainer.layer.cornerRadius = Values.textFieldCornerRadius publicKeyLabelContainer.layer.borderWidth = Values.borderThickness publicKeyLabelContainer.layer.borderColor = Colors.text.cgColor // Set up spacers let topSpacer = UIView.vStretchingSpacer() let bottomSpacer = UIView.vStretchingSpacer() // Set up register button let registerButton = Button(style: .prominentFilled, size: .large) registerButton.setTitle(NSLocalizedString("Continue", comment: ""), for: UIControl.State.normal) registerButton.titleLabel!.font = .boldSystemFont(ofSize: Values.mediumFontSize) registerButton.addTarget(self, action: #selector(register), for: UIControl.Event.touchUpInside) // Set up button stack view let buttonStackView = UIStackView(arrangedSubviews: [ registerButton, copyPublicKeyButton ]) buttonStackView.axis = .vertical buttonStackView.spacing = Values.mediumSpacing buttonStackView.alignment = .fill // Set up button stack view container let buttonStackViewContainer = UIView() buttonStackViewContainer.addSubview(buttonStackView) buttonStackView.pin(.leading, to: .leading, of: buttonStackViewContainer, withInset: Values.massiveSpacing) buttonStackView.pin(.top, to: .top, of: buttonStackViewContainer) buttonStackViewContainer.pin(.trailing, to: .trailing, of: buttonStackView, withInset: Values.massiveSpacing) buttonStackViewContainer.pin(.bottom, to: .bottom, of: buttonStackView) // Set up legal label legalLabel.isUserInteractionEnabled = true let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleLegalLabelTapped)) legalLabel.addGestureRecognizer(tapGestureRecognizer) // Set up legal label container let legalLabelContainer = UIView() legalLabelContainer.set(.height, to: Values.onboardingButtonBottomOffset) legalLabelContainer.addSubview(legalLabel) legalLabel.pin(.leading, to: .leading, of: legalLabelContainer, withInset: Values.massiveSpacing) legalLabel.pin(.top, to: .top, of: legalLabelContainer) legalLabelContainer.pin(.trailing, to: .trailing, of: legalLabel, withInset: Values.massiveSpacing) legalLabelContainer.pin(.bottom, to: .bottom, of: legalLabel, withInset: 10) // Set up top stack view let topStackView = UIStackView(arrangedSubviews: [ titleLabel, explanationLabel, publicKeyLabelContainer ]) topStackView.axis = .vertical topStackView.spacing = Values.veryLargeSpacing topStackView.alignment = .fill // Set up top stack view container let topStackViewContainer = UIView() topStackViewContainer.addSubview(topStackView) topStackView.pin(.leading, to: .leading, of: topStackViewContainer, withInset: Values.veryLargeSpacing) topStackView.pin(.top, to: .top, of: topStackViewContainer) topStackViewContainer.pin(.trailing, to: .trailing, of: topStackView, withInset: Values.veryLargeSpacing) topStackViewContainer.pin(.bottom, to: .bottom, of: topStackView) // Set up main stack view let mainStackView = UIStackView(arrangedSubviews: [ topSpacer, topStackViewContainer, bottomSpacer, buttonStackViewContainer, legalLabelContainer ]) mainStackView.axis = .vertical mainStackView.alignment = .fill view.addSubview(mainStackView) mainStackView.pin(to: view) topSpacer.heightAnchor.constraint(equalTo: bottomSpacer.heightAnchor, multiplier: 1).isActive = true // Peform initial seed update updateSeed() } // MARK: General @objc private func enableCopyButton() { copyPublicKeyButton.isUserInteractionEnabled = true UIView.transition(with: copyPublicKeyButton, duration: 0.25, options: .transitionCrossDissolve, animations: { self.copyPublicKeyButton.setTitle(NSLocalizedString("Copy", comment: ""), for: UIControl.State.normal) }, completion: nil) } // MARK: Updating private func updateSeed() { seed = Randomness.generateRandomBytes(16) } private func updateKeyPair() { keyPair = Curve25519.generateKeyPair(fromSeed: seed + seed) } private func updatePublicKeyLabel() { publicKeyLabel.text = keyPair.hexEncodedPublicKey } // MARK: Interaction @objc private func register() { let identityManager = OWSIdentityManager.shared() let databaseConnection = identityManager.value(forKey: "dbConnection") as! YapDatabaseConnection databaseConnection.setObject(seed.toHexString(), forKey: "LKLokiSeed", inCollection: OWSPrimaryStorageIdentityKeyStoreCollection) databaseConnection.setObject(keyPair!, forKey: OWSPrimaryStorageIdentityKeyStoreIdentityKey, inCollection: OWSPrimaryStorageIdentityKeyStoreCollection) TSAccountManager.sharedInstance().phoneNumberAwaitingVerification = keyPair!.hexEncodedPublicKey let displayNameVC = DisplayNameVC() navigationController!.pushViewController(displayNameVC, animated: true) } @objc private func copyPublicKey() { UIPasteboard.general.string = keyPair.hexEncodedPublicKey copyPublicKeyButton.isUserInteractionEnabled = false UIView.transition(with: copyPublicKeyButton, duration: 0.25, options: .transitionCrossDissolve, animations: { self.copyPublicKeyButton.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 handleLegalLabelTapped(_ tapGestureRecognizer: UITapGestureRecognizer) { let url = URL(string: "https://github.com/loki-project/loki-messenger-ios/blob/master/privacy-policy.md")! UIApplication.shared.open(url) } }