Clean up onboarding

This commit is contained in:
Niels Andriesse 2021-01-29 11:06:14 +11:00
parent c0d98c5393
commit 464e8864af
8 changed files with 113 additions and 85 deletions

View File

@ -44,6 +44,7 @@ target 'SignalUtilitiesKit' do
pod 'HKDFKit', :inhibit_warnings => true
pod 'libPhoneNumber-iOS', :inhibit_warnings => true
pod 'Mantle', git: 'https://github.com/signalapp/Mantle', branch: 'signal-master', :inhibit_warnings => true
pod 'NVActivityIndicatorView', :inhibit_warnings => true
pod 'PromiseKit', :inhibit_warnings => true
pod 'PureLayout', '~> 3.1.4', :inhibit_warnings => true
pod 'Reachability', :inhibit_warnings => true

View File

@ -221,6 +221,6 @@ SPEC CHECKSUMS:
YYImage: 6db68da66f20d9f169ceb94dfb9947c3867b9665
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
PODFILE CHECKSUM: 70f7bae656b18fbce6806589f2df6a5a87bce09e
PODFILE CHECKSUM: 887bd5e5307dd6b5f581fd79f6010cee9cc3e026
COCOAPODS: 1.10.0.rc.1

View File

@ -49,9 +49,6 @@ final class NewPrivateChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
closeButton.tintColor = Colors.text
navigationItem.leftBarButtonItem = closeButton
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handleDoneButtonTapped))
doneButton.tintColor = Colors.text
navigationItem.rightBarButtonItem = doneButton
// Set up page VC
let hasCameraAccess = (AVCaptureDevice.authorizationStatus(for: .video) == .authorized)
pages = [ enterPublicKeyVC, (hasCameraAccess ? scanQRCodeWrapperVC : scanQRCodePlaceholderVC) ]
@ -122,10 +119,6 @@ final class NewPrivateChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
dismiss(animated: true, completion: nil)
}
@objc private func handleDoneButtonTapped() {
enterPublicKeyVC.startNewPrivateChatIfPossible()
}
func controller(_ controller: OWSQRCodeScanningViewController, didDetectQRCodeWith string: String) {
let hexEncodedPublicKey = string
startNewPrivateChatIfPossible(with: hexEncodedPublicKey)
@ -146,6 +139,8 @@ final class NewPrivateChatVC : BaseVC, UIPageViewControllerDataSource, UIPageVie
private final class EnterPublicKeyVC : UIViewController {
weak var newPrivateChatVC: NewPrivateChatVC!
private var isKeyboardShowing = false
private var bottomConstraint: NSLayoutConstraint!
// MARK: Components
private let publicKeyTextView = TextView(placeholder: NSLocalizedString("vc_enter_public_key_text_field_hint", comment: ""))
@ -157,11 +152,36 @@ private final class EnterPublicKeyVC : UIViewController {
return result
}()
private lazy var userPublicKeyLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.text
result.font = Fonts.spaceMono(ofSize: Values.mediumFontSize)
result.numberOfLines = 0
result.textAlignment = .center
result.lineBreakMode = .byCharWrapping
result.text = getUserHexEncodedPublicKey()
return result
}()
private lazy var spacer1 = UIView.spacer(withHeight: Values.largeSpacing)
private lazy var spacer2 = UIView.spacer(withHeight: Values.largeSpacing)
private lazy var spacer3 = UIView.spacer(withHeight: Values.largeSpacing)
private lazy var separator = Separator(title: NSLocalizedString("your_session_id", comment: ""))
private lazy var buttonContainer: UIStackView = {
let result = UIStackView()
result.axis = .horizontal
result.spacing = Values.mediumSpacing
result.distribution = .fillEqually
return result
}()
// MARK: Lifecycle
override func viewDidLoad() {
// Remove background color
view.backgroundColor = .clear
// Set up explanation label
// Explanation label
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
explanationLabel.font = .systemFont(ofSize: Values.verySmallFontSize)
@ -169,25 +189,13 @@ private final class EnterPublicKeyVC : UIViewController {
explanationLabel.numberOfLines = 0
explanationLabel.textAlignment = .center
explanationLabel.lineBreakMode = .byWordWrapping
// Set up separator
let separator = Separator(title: NSLocalizedString("your_session_id", comment: ""))
// Set up user public key label
let userPublicKeyLabel = UILabel()
userPublicKeyLabel.textColor = Colors.text
userPublicKeyLabel.font = Fonts.spaceMono(ofSize: Values.mediumFontSize)
userPublicKeyLabel.numberOfLines = 0
userPublicKeyLabel.textAlignment = .center
userPublicKeyLabel.lineBreakMode = .byCharWrapping
userPublicKeyLabel.text = getUserHexEncodedPublicKey()
// Set up share button
// Share button
let shareButton = Button(style: .unimportant, 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
// Button container
buttonContainer.addArrangedSubview(copyButton)
buttonContainer.addArrangedSubview(shareButton)
// Next button
let nextButton = Button(style: .prominentOutline, size: .large)
nextButton.setTitle(NSLocalizedString("next", comment: ""), for: UIControl.State.normal)
@ -198,19 +206,30 @@ private final class EnterPublicKeyVC : UIViewController {
nextButton.pin(.top, to: .top, of: nextButtonContainer)
nextButtonContainer.pin(.trailing, to: .trailing, of: nextButton, withInset: 80)
nextButtonContainer.pin(.bottom, to: .bottom, of: nextButton)
// Set up stack view
let stackView = UIStackView(arrangedSubviews: [ publicKeyTextView, UIView.spacer(withHeight: Values.smallSpacing), explanationLabel, UIView.spacer(withHeight: Values.largeSpacing), separator, UIView.spacer(withHeight: Values.largeSpacing), userPublicKeyLabel, UIView.spacer(withHeight: Values.largeSpacing), buttonContainer, UIView.vStretchingSpacer(), nextButtonContainer ])
stackView.axis = .vertical
stackView.alignment = .fill
stackView.layoutMargins = UIEdgeInsets(top: Values.largeSpacing, left: Values.largeSpacing, bottom: Values.largeSpacing, right: Values.largeSpacing)
stackView.isLayoutMarginsRelativeArrangement = true
view.addSubview(stackView)
stackView.pin(to: view)
// Set up width constraint
// Main stack view
let mainStackView = UIStackView(arrangedSubviews: [ publicKeyTextView, UIView.spacer(withHeight: Values.smallSpacing), explanationLabel, spacer1, separator, spacer2, userPublicKeyLabel, spacer3, buttonContainer, UIView.vStretchingSpacer(), nextButtonContainer ])
mainStackView.axis = .vertical
mainStackView.alignment = .fill
mainStackView.layoutMargins = UIEdgeInsets(top: Values.largeSpacing, left: Values.largeSpacing, bottom: Values.largeSpacing, right: Values.largeSpacing)
mainStackView.isLayoutMarginsRelativeArrangement = true
view.addSubview(mainStackView)
mainStackView.pin(.leading, to: .leading, of: view)
mainStackView.pin(.top, to: .top, of: view)
view.pin(.trailing, to: .trailing, of: mainStackView)
bottomConstraint = view.pin(.bottom, to: .bottom, of: mainStackView)
// Width constraint
view.set(.width, to: UIScreen.main.bounds.width)
// Dismiss keyboard on tap
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
view.addGestureRecognizer(tapGestureRecognizer)
// Listen to keyboard notifications
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillChangeFrameNotification(_:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
notificationCenter.addObserver(self, selector: #selector(handleKeyboardWillHideNotification(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
// MARK: General
@ -229,6 +248,34 @@ private final class EnterPublicKeyVC : UIViewController {
}, completion: nil)
}
// MARK: Updating
@objc private func handleKeyboardWillChangeFrameNotification(_ notification: Notification) {
guard !isKeyboardShowing else { return }
isKeyboardShowing = true
guard let newHeight = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size.height else { return }
bottomConstraint.constant = newHeight
UIView.animate(withDuration: 0.25) {
[ self.spacer1, self.separator, self.spacer2, self.userPublicKeyLabel, self.spacer3, self.buttonContainer ].forEach {
$0.alpha = 0
$0.isHidden = true
}
self.view.layoutIfNeeded()
}
}
@objc private func handleKeyboardWillHideNotification(_ notification: Notification) {
guard isKeyboardShowing else { return }
isKeyboardShowing = false
bottomConstraint.constant = 0
UIView.animate(withDuration: 0.25) {
[ self.spacer1, self.separator, self.spacer2, self.userPublicKeyLabel, self.spacer3, self.buttonContainer ].forEach {
$0.alpha = 1
$0.isHidden = false
}
self.view.layoutIfNeeded()
}
}
// MARK: Interaction
@objc private func copyPublicKey() {
UIPasteboard.general.string = getUserHexEncodedPublicKey()

View File

@ -2622,7 +2622,7 @@
"view_seed_reminder_subtitle_3" = "Make sure to store your recovery phrase in a safe place";
"vc_path_title" = "Path";
"vc_path_explanation" = "Session hides your IP by bouncing your messages through several Service Nodes in Session's decentralized network. These are the countries your connection is currently being bounced through:";
"vc_path_explanation" = "Session hides your IP by routing your messages through multiple Service Nodes in Session's decentralized network. These are the countries your connection is currently being routed through:";
"vc_path_device_row_title" = "You";
"vc_path_guard_node_row_title" = "Entry Node";
"vc_path_service_node_row_title" = "Service Node";

View File

@ -40,12 +40,15 @@ final class FakeChatView : UIView {
stackView.spacing = spacing
stackView.alignment = .fill
stackView.set(.width, to: UIScreen.main.bounds.width)
stackView.layoutMargins = UIEdgeInsets(top: 8, leading: Values.veryLargeSpacing, bottom: 8, trailing: Values.veryLargeSpacing)
let vInset = Values.smallSpacing
stackView.layoutMargins = UIEdgeInsets(top: vInset, leading: Values.veryLargeSpacing, bottom: vInset, trailing: Values.veryLargeSpacing)
stackView.isLayoutMarginsRelativeArrangement = true
scrollView.addSubview(stackView)
stackView.pin(to: scrollView)
addSubview(scrollView)
scrollView.pin(to: self)
let height = chatBubbles.reduce(0) { $0 + $1.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize).height } + CGFloat(chatBubbles.count - 1) * spacing + 2 * vInset
scrollView.contentSize = CGSize(width: UIScreen.main.bounds.width, height: height)
}
private func getChatBubble(withText text: String, wasSentByCurrentUser: Bool) -> UIView {
@ -103,7 +106,7 @@ final class FakeChatView : UIView {
self?.showChatBubble(at: 4)
UIView.animate(withDuration: animationDuration) {
guard let self = self else { return }
self.scrollView.contentOffset = CGPoint(x: 0, y: self.chatBubbles[0].height() + self.spacing + self.chatBubbles[1].height() + self.spacing + self.chatBubbles[2].height() + self.spacing)
self.scrollView.contentOffset = CGPoint(x: 0, y: self.scrollView.contentSize.height - self.scrollView.bounds.height)
}
}
}

View File

@ -1,6 +1,5 @@
final class LandingVC : BaseVC {
private var fakeChatViewContentOffset: CGPoint!
// MARK: Components
private lazy var fakeChatView: FakeChatView = {
@ -79,36 +78,14 @@ final class LandingVC : BaseVC {
}
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidAppear(animated)
if let fakeChatViewContentOffset = fakeChatViewContentOffset {
fakeChatView.contentOffset = fakeChatViewContentOffset
}
}
// MARK: Interaction
@objc private func register() {
fakeChatViewContentOffset = fakeChatView.contentOffset
DispatchQueue.main.async {
self.fakeChatView.contentOffset = self.fakeChatViewContentOffset
}
let registerVC = RegisterVC()
navigationController!.pushViewController(registerVC, animated: true)
}
@objc private func restore() {
fakeChatViewContentOffset = fakeChatView.contentOffset
DispatchQueue.main.async {
self.fakeChatView.contentOffset = self.fakeChatViewContentOffset
}
let restoreVC = RestoreVC()
navigationController!.pushViewController(restoreVC, animated: true)
}
// MARK: Convenience
private func setUserInteractionEnabled(_ isEnabled: Bool) {
[ registerButton, restoreButton ].forEach {
$0.isUserInteractionEnabled = isEnabled
}
}
}

View File

@ -11,16 +11,12 @@ final class SeedVC : BaseVC {
return Mnemonic.encode(hexEncodedString: hexEncodedSeed)
}()
private lazy var redactedMnemonic: NSAttributedString = {
var mnemonic = self.mnemonic
let regex = try! NSRegularExpression(pattern: "\\w*", options: [])
let matches = regex.matches(in: mnemonic, options: .withoutAnchoringBounds, range: NSRange(location: 0, length: mnemonic.count))
let result = NSMutableAttributedString(string: mnemonic)
matches.forEach { match in
result.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.thick.rawValue, range: match.range)
result.addAttribute(.strikethroughColor, value: Colors.accent, range: match.range)
private lazy var redactedMnemonic: String = {
if isIPhone5OrSmaller {
return "▆▆▆▆ ▆▆▆▆▆▆ ▆▆▆ ▆▆▆▆▆▆▆ ▆▆ ▆▆▆▆ ▆▆▆ ▆▆▆▆▆ ▆▆▆ ▆ ▆▆▆▆ ▆▆ ▆▆▆▆▆▆▆ ▆▆▆▆▆"
} else {
return "▆▆▆▆ ▆▆▆▆▆▆ ▆▆▆ ▆▆▆▆▆▆▆ ▆▆ ▆▆▆▆ ▆▆▆ ▆▆▆▆▆ ▆▆▆ ▆ ▆▆▆▆ ▆▆ ▆▆▆▆▆▆▆ ▆▆▆▆▆ ▆▆▆▆▆▆▆▆ ▆▆ ▆▆▆ ▆▆▆▆▆▆▆"
}
return result
}()
// MARK: Components
@ -37,7 +33,7 @@ final class SeedVC : BaseVC {
private lazy var mnemonicLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.text
result.textColor = Colors.accent
result.font = Fonts.spaceMono(ofSize: Values.mediumFontSize)
result.numberOfLines = 0
result.textAlignment = .center
@ -77,7 +73,7 @@ final class SeedVC : BaseVC {
explanationLabel.numberOfLines = 0
explanationLabel.lineBreakMode = .byWordWrapping
// Set up mnemonic label
mnemonicLabel.attributedText = redactedMnemonic
mnemonicLabel.text = redactedMnemonic
let mnemonicLabelGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(revealMnemonic))
mnemonicLabel.addGestureRecognizer(mnemonicLabelGestureRecognizer)
mnemonicLabel.isUserInteractionEnabled = true
@ -159,7 +155,8 @@ final class SeedVC : BaseVC {
@objc private func revealMnemonic() {
UIView.transition(with: mnemonicLabel, duration: 0.25, options: .transitionCrossDissolve, animations: {
self.mnemonicLabel.attributedText = NSAttributedString(string: self.mnemonic)
self.mnemonicLabel.text = self.mnemonic
self.mnemonicLabel.textColor = Colors.text
}, completion: nil)
UIView.transition(with: seedReminderView.titleLabel, duration: 0.25, options: .transitionCrossDissolve, animations: {
let title = "Account Secured! 100%"
@ -171,8 +168,8 @@ final class SeedVC : BaseVC {
self.seedReminderView.subtitle = NSLocalizedString("view_seed_reminder_subtitle_3", comment: "")
}, completion: nil)
seedReminderView.setProgress(1, animated: true)
UserDefaults.standard[.hasViewedSeed] = true
NotificationCenter.default.post(name: .seedViewed, object: nil)
// UserDefaults.standard[.hasViewedSeed] = true
// NotificationCenter.default.post(name: .seedViewed, object: nil)
}
@objc private func copyMnemonic() {

View File

@ -5,6 +5,7 @@
import Foundation
import MediaPlayer
import SessionUIKit
import NVActivityIndicatorView
// A modal view that be used during blocking interactions (e.g. waiting on response from
// service or on the completion of a long-running local operation).
@ -16,8 +17,13 @@ public class ModalActivityIndicatorViewController: OWSViewController {
@objc
public var wasCancelled: Bool = false
var activityIndicator: UIActivityIndicatorView?
private lazy var spinner: NVActivityIndicatorView = {
let result = NVActivityIndicatorView(frame: CGRect.zero, type: .circleStrokeSpin, color: .white, padding: nil)
result.set(.width, to: 64)
result.set(.height, to: 64)
return result
}()
var presentTimer: Timer?
@ -72,11 +78,8 @@ public class ModalActivityIndicatorViewController: OWSViewController {
public override func loadView() {
super.loadView()
self.view.backgroundColor = UIColor(white: 0, alpha: 0.5)
self.view.backgroundColor = UIColor(white: 0, alpha: 0.6)
self.view.isOpaque = false
let activityIndicator = UIActivityIndicatorView(style: .whiteLarge)
self.activityIndicator = activityIndicator
if let message = message {
let messageLabel = UILabel()
@ -87,15 +90,15 @@ public class ModalActivityIndicatorViewController: OWSViewController {
messageLabel.textAlignment = .center
messageLabel.lineBreakMode = .byWordWrapping
messageLabel.set(.width, to: UIScreen.main.bounds.width - 2 * Values.mediumSpacing)
let stackView = UIStackView(arrangedSubviews: [ messageLabel, activityIndicator ])
let stackView = UIStackView(arrangedSubviews: [ messageLabel, spinner ])
stackView.axis = .vertical
stackView.spacing = Values.largeSpacing
stackView.alignment = .center
self.view.addSubview(stackView)
stackView.center(in: self.view)
} else {
self.view.addSubview(activityIndicator)
activityIndicator.autoCenterInSuperview()
self.view.addSubview(spinner)
spinner.autoCenterInSuperview()
}
if canCancel {
@ -123,7 +126,7 @@ public class ModalActivityIndicatorViewController: OWSViewController {
public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.activityIndicator?.startAnimating()
self.spinner.startAnimating()
// Hide the the modal and wait for a second before revealing it,
// to avoid "blipping" in the modal during short blocking operations.
@ -144,7 +147,7 @@ public class ModalActivityIndicatorViewController: OWSViewController {
public override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.activityIndicator?.stopAnimating()
self.spinner.stopAnimating()
clearTimer()
}