Implement seed reminder view

Also fix up QR codes and give RSS feeds their own icon
This commit is contained in:
Niels Andriesse 2019-12-12 11:10:26 +11:00
parent d05df87dd2
commit 1ab82341b9
14 changed files with 183 additions and 17 deletions

View File

@ -574,6 +574,8 @@
B82B4094239DF15900A248E7 /* ConversationTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B82B4093239DF15900A248E7 /* ConversationTitleView.swift */; };
B846365B22B7418B00AF1514 /* Identicon+ObjC.swift in Sources */ = {isa = PBXBuildFile; fileRef = B846365A22B7418B00AF1514 /* Identicon+ObjC.swift */; };
B84664F5235022F30083A1CD /* MentionUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = B84664F4235022F30083A1CD /* MentionUtilities.swift */; };
B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */; };
B85357C123A1B81900AAF6CD /* SeedReminderViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B85357C023A1B81900AAF6CD /* SeedReminderViewDelegate.swift */; };
B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; };
B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.swift */; };
B885D5F4233491AB00EE0D8E /* DeviceLinkingModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B885D5F3233491AB00EE0D8E /* DeviceLinkingModal.swift */; };
@ -1409,6 +1411,8 @@
B82B4093239DF15900A248E7 /* ConversationTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationTitleView.swift; sourceTree = "<group>"; };
B846365A22B7418B00AF1514 /* Identicon+ObjC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Identicon+ObjC.swift"; sourceTree = "<group>"; };
B84664F4235022F30083A1CD /* MentionUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentionUtilities.swift; sourceTree = "<group>"; };
B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedReminderView.swift; sourceTree = "<group>"; };
B85357C023A1B81900AAF6CD /* SeedReminderViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedReminderViewDelegate.swift; sourceTree = "<group>"; };
B86BD08323399ACF000F5AE3 /* Modal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modal.swift; sourceTree = "<group>"; };
B86BD08523399CEF000F5AE3 /* SeedModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeedModal.swift; sourceTree = "<group>"; };
B885D5F3233491AB00EE0D8E /* DeviceLinkingModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceLinkingModal.swift; sourceTree = "<group>"; };
@ -2785,6 +2789,8 @@
B8BB82B82394911B00BA5194 /* Separator.swift */,
B8CCF638239721E20091D419 /* TabBar.swift */,
B8BB82B423947F2D00BA5194 /* TextField.swift */,
B85357BE23A1AE0800AAF6CD /* SeedReminderView.swift */,
B85357C023A1B81900AAF6CD /* SeedReminderViewDelegate.swift */,
);
path = Components;
sourceTree = "<group>";
@ -3878,6 +3884,7 @@
340FC8BA204DAC8D007AEB0F /* FingerprintViewScanController.m in Sources */,
4585C4681ED8F8D200896AEA /* SafetyNumberConfirmationAlert.swift in Sources */,
4C20B2B920CA10DE001BAC90 /* ConversationSearchViewController.swift in Sources */,
B85357C123A1B81900AAF6CD /* SeedReminderViewDelegate.swift in Sources */,
450D19131F85236600970622 /* RemoteVideoView.m in Sources */,
34129B8621EF877A005457A8 /* LinkPreviewView.swift in Sources */,
34386A54207D271D009F5D9C /* NeverClearView.swift in Sources */,
@ -4046,6 +4053,7 @@
4521C3C01F59F3BA00B4C582 /* TextFieldHelper.swift in Sources */,
34D2CCDF206939B400CB1A14 /* DebugUIMessagesAction.m in Sources */,
340FC8AC204DAC8D007AEB0F /* PrivacySettingsTableViewController.m in Sources */,
B85357BF23A1AE0800AAF6CD /* SeedReminderView.swift in Sources */,
340FC8C5204DE223007AEB0F /* DebugUIBackup.m in Sources */,
4C11AA5020FD59C700351FBD /* MessageStatusView.swift in Sources */,
340FC8AE204DAC8D007AEB0F /* OWSSoundSettingsViewController.m in Sources */,

View File

@ -8,7 +8,7 @@ final class Button : UIButton {
}
enum Size {
case medium, large
case medium, large, small
}
init(style: Style, size: Size) {
@ -50,6 +50,7 @@ final class Button : UIButton {
}
let height: CGFloat
switch size {
case .small: height = Values.smallButtonHeight
case .medium: height = Values.mediumButtonHeight
case .large: height = Values.largeButtonHeight
}
@ -58,7 +59,8 @@ final class Button : UIButton {
backgroundColor = fillColor
layer.borderColor = borderColor.cgColor
layer.borderWidth = Values.borderThickness
titleLabel!.font = Fonts.spaceMono(ofSize: Values.mediumFontSize)
let fontSize = (size == .small) ? Values.smallFontSize : Values.mediumFontSize
titleLabel!.font = Fonts.spaceMono(ofSize: fontSize)
setTitleColor(textColor, for: UIControl.State.normal)
}
}

View File

@ -121,9 +121,11 @@ final class ConversationCell : UITableViewCell {
} else {
// TODO: Handle
}
profilePictureView.isRSSFeed = (threadViewModel.threadRecord as? TSGroupThread)?.isRSSFeed ?? false
} else {
profilePictureView.hexEncodedPublicKey = threadViewModel.contactIdentifier!
profilePictureView.additionalHexEncodedPublicKey = nil
profilePictureView.isRSSFeed = false
}
profilePictureView.update()
displayNameLabel.text = getDisplayName()

View File

@ -144,11 +144,11 @@
private func updateSubtitleForCurrentStatus() {
DispatchQueue.main.async {
switch self.currentStatus {
case .calculatingPoW: self.subtitleLabel.text = "Calculating proof of work"
case .contactingNetwork: self.subtitleLabel.text = "Contacting service node network"
case .sendingMessage: self.subtitleLabel.text = "Sending message"
case .messageSent: self.subtitleLabel.text = "Message sent securely"
case .messageFailed: self.subtitleLabel.text = "Message failed to send"
case .calculatingPoW: self.subtitleLabel.text = NSLocalizedString("Encrypting message", comment: "")
case .contactingNetwork: self.subtitleLabel.text = NSLocalizedString("Tracing a path", comment: "")
case .sendingMessage: self.subtitleLabel.text = NSLocalizedString("Sending message", comment: "")
case .messageSent: self.subtitleLabel.text = NSLocalizedString("Message sent securely", comment: "")
case .messageFailed: self.subtitleLabel.text = NSLocalizedString("Message failed to send", comment: "")
case nil:
let subtitle = NSMutableAttributedString()
if self.thread.isMuted {

View File

@ -4,6 +4,7 @@ final class ProfilePictureView : UIView {
private var imageViewWidthConstraint: NSLayoutConstraint!
private var imageViewHeightConstraint: NSLayoutConstraint!
@objc var size: CGFloat = 0 // Not an implicitly unwrapped optional due to Obj-C limitations
@objc var isRSSFeed = false
@objc var hexEncodedPublicKey: String!
@objc var additionalHexEncodedPublicKey: String?
@ -11,6 +12,15 @@ final class ProfilePictureView : UIView {
private lazy var imageView = getImageView()
private lazy var additionalImageView = getImageView()
private lazy var rssLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.text
result.font = .systemFont(ofSize: Values.smallFontSize)
result.textAlignment = .center
result.text = "RSS"
return result
}()
// MARK: Lifecycle
override init(frame: CGRect) {
super.init(frame: frame)
@ -35,6 +45,12 @@ final class ProfilePictureView : UIView {
additionalImageView.set(.width, to: additionalImageViewSize)
additionalImageView.set(.height, to: additionalImageViewSize)
additionalImageView.layer.cornerRadius = additionalImageViewSize / 2
// Set up RSS label
addSubview(rssLabel)
rssLabel.pin(.leading, to: .leading, of: self)
rssLabel.pin(.top, to: .top, of: self)
rssLabel.autoPinWidth(toWidthOf: imageView)
rssLabel.autoPinHeight(toHeightOf: imageView)
}
// MARK: Updating
@ -47,7 +63,7 @@ final class ProfilePictureView : UIView {
return OWSProfileManager.shared().profileAvatar(forRecipientId: hexEncodedPublicKey) ?? Identicon.generateIcon(string: hexEncodedPublicKey, size: size)
}
let size: CGFloat
if let additionalHexEncodedPublicKey = additionalHexEncodedPublicKey {
if let additionalHexEncodedPublicKey = additionalHexEncodedPublicKey, !isRSSFeed {
size = Values.smallProfilePictureSize
imageViewWidthConstraint = imageView.set(.width, to: size)
imageViewHeightConstraint = imageView.set(.height, to: size)
@ -60,8 +76,10 @@ final class ProfilePictureView : UIView {
additionalImageView.isHidden = true
additionalImageView.image = nil
}
imageView.image = getProfilePicture(of: size, for: hexEncodedPublicKey)
imageView.image = isRSSFeed ? nil : getProfilePicture(of: size, for: hexEncodedPublicKey)
imageView.backgroundColor = isRSSFeed ? UIColor(hex: 0x353535) : Colors.unimportant
imageView.layer.cornerRadius = size / 2
rssLabel.isHidden = !isRSSFeed
}
// MARK: Convenience

View File

@ -0,0 +1,84 @@
final class SeedReminderView : UIView {
var title = NSAttributedString(string: "") { didSet { titleLabel.attributedText = title } }
var subtitle = "" { didSet { subtitleLabel.text = subtitle } }
var delegate: SeedReminderViewDelegate?
// MARK: Components
private lazy var progressIndicatorView: UIProgressView = {
let result = UIProgressView()
result.progressViewStyle = .bar
result.progressTintColor = Colors.accent
result.backgroundColor = UIColor(hex: 0xFFFFFF).withAlphaComponent(0.1)
result.set(.height, to: Values.progressBarThickness)
return result
}()
private lazy var titleLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.text
result.font = .boldSystemFont(ofSize: Values.smallFontSize)
result.lineBreakMode = .byTruncatingTail
return result
}()
private lazy var subtitleLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity)
result.font = .systemFont(ofSize: Values.verySmallFontSize)
result.lineBreakMode = .byTruncatingTail
return result
}()
// MARK: Lifecycle
override init(frame: CGRect) {
super.init(frame: frame)
setUpViewHierarchy()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setUpViewHierarchy()
}
private func setUpViewHierarchy() {
// Set background color
backgroundColor = Colors.cellBackground
// Set up label stack view
let labelStackView = UIStackView(arrangedSubviews: [ titleLabel, subtitleLabel ])
labelStackView.axis = .vertical
labelStackView.spacing = 4
// Set up button
let button = Button(style: .prominentOutline, size: .small)
button.setTitle(NSLocalizedString("Continue", comment: ""), for: UIControl.State.normal)
button.set(.width, to: 80)
button.addTarget(self, action: #selector(handleContinueButtonTapped), for: UIControl.Event.touchUpInside)
// Set up content stack view
let contentStackView = UIStackView(arrangedSubviews: [ labelStackView, UIView.hStretchingSpacer(), button ])
contentStackView.axis = .horizontal
contentStackView.spacing = 4
contentStackView.alignment = .center
contentStackView.layoutMargins = UIEdgeInsets(top: 0, leading: Values.mediumSpacing + Values.accentLineThickness, bottom: 0, trailing: Values.mediumSpacing)
contentStackView.isLayoutMarginsRelativeArrangement = true
// Set up separator
let separator = UIView()
separator.set(.height, to: Values.separatorThickness)
separator.backgroundColor = Colors.separator
// Set up stack view
let stackView = UIStackView(arrangedSubviews: [ progressIndicatorView, contentStackView, separator ])
stackView.axis = .vertical
stackView.spacing = Values.mediumSpacing
addSubview(stackView)
stackView.pin(to: self)
}
// MARK: Updating
func setProgress(_ progress: Float, animated isAnimated: Bool) {
progressIndicatorView.setProgress(progress, animated: isAnimated)
}
// MARK: Updating
@objc private func handleContinueButtonTapped() {
delegate?.handleContinueButtonTapped(from: self)
}
}

View File

@ -0,0 +1,5 @@
protocol SeedReminderViewDelegate {
func handleContinueButtonTapped(from seedReminderView: SeedReminderView)
}

View File

@ -19,6 +19,7 @@ final class Values : NSObject {
@objc static let massiveFontSize = CGFloat(50)
// MARK: - Element Sizes
@objc static let smallButtonHeight = CGFloat(27)
@objc static let mediumButtonHeight = CGFloat(34)
@objc static let largeButtonHeight = CGFloat(45)
@objc static let accentLineThickness = CGFloat(4)
@ -43,7 +44,7 @@ final class Values : NSObject {
@objc static let fakeChatViewHeight = CGFloat(234)
@objc static var composeViewTextFieldBorderThickness: CGFloat { return 1 / UIScreen.main.scale }
@objc static let messageBubbleCornerRadius: CGFloat = 10
@objc static let messageProgressBarThickness: CGFloat = 2
@objc static let progressBarThickness: CGFloat = 2
// MARK: - Distances
@objc static let verySmallSpacing = CGFloat(4)

View File

@ -1,13 +1,19 @@
enum QRCode {
static func generate(for string: String, isInverted: Bool = true) -> UIImage {
static func generate(for string: String, hasBackground: Bool = false) -> UIImage {
let data = string.data(using: .utf8)
var qrCodeAsCIImage: CIImage
let filter1 = CIFilter(name: "CIQRCodeGenerator")!
filter1.setValue(data, forKey: "inputMessage")
qrCodeAsCIImage = filter1.outputImage!
if isInverted {
if hasBackground {
let filter2 = CIFilter(name: "CIFalseColor")!
filter2.setValue(qrCodeAsCIImage, forKey: "inputImage")
filter2.setValue(CIColor(color: UIColor(hex: 0xFFFFFF)), forKey: "inputColor0")
filter2.setValue(CIColor(color: UIColor(hex: 0x1B1B1B)), forKey: "inputColor1")
qrCodeAsCIImage = filter2.outputImage!
} else {
let filter2 = CIFilter(name: "CIColorInvert")!
filter2.setValue(qrCodeAsCIImage, forKey: "inputImage")
qrCodeAsCIImage = filter2.outputImage!

View File

@ -1,5 +1,5 @@
final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate, UIViewControllerPreviewingDelegate {
final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegate, UIScrollViewDelegate, UIViewControllerPreviewingDelegate, SeedReminderViewDelegate {
private var threadViewModelCache: [String:ThreadViewModel] = [:]
private var isObservingDatabase = true
private var isViewVisible = false { didSet { updateIsObservingDatabase() } }
@ -22,6 +22,18 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat
override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent }
// MARK: Components
private lazy var seedReminderView: SeedReminderView = {
let result = SeedReminderView()
let title = "You're almost finished! 80%"
let attributedTitle = NSMutableAttributedString(string: title)
attributedTitle.addAttribute(.foregroundColor, value: Colors.accent, range: (title as NSString).range(of: "80%"))
result.title = attributedTitle
result.subtitle = NSLocalizedString("Secure your account by saving your seed", comment: "")
result.setProgress(0.8, animated: false)
result.delegate = self
return result
}()
private lazy var searchBar = SearchBar()
private lazy var tableView: UITableView = {
@ -74,11 +86,19 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat
titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
navigationItem.titleView = titleLabel
// Set up seed reminder view
view.addSubview(seedReminderView)
seedReminderView.pin(.leading, to: .leading, of: view)
seedReminderView.pin(.top, to: .top, of: view)
seedReminderView.pin(.trailing, to: .trailing, of: view)
// Set up table view
tableView.dataSource = self
tableView.delegate = self
view.addSubview(tableView)
tableView.pin(to: view)
tableView.pin(.leading, to: .leading, of: view)
tableView.pin(.top, to: .bottom, of: seedReminderView)
tableView.pin(.trailing, to: .trailing, of: view)
tableView.pin(.bottom, to: .bottom, of: view)
// Set up search bar
tableView.tableHeaderView = searchBar
searchBar.sizeToFit()
@ -219,6 +239,10 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat
}
// MARK: Interaction
func handleContinueButtonTapped(from seedReminderView: SeedReminderView) {
// TODO: Implement
}
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
searchBar.resignFirstResponder()
}

View File

@ -170,6 +170,14 @@ private final class ViewMyQRCodeVC : UIViewController {
let qrCode = QRCode.generate(for: userHexEncodedPublicKey)
qrCodeImageView.image = qrCode
qrCodeImageView.contentMode = .scaleAspectFit
qrCodeImageView.set(.height, to: 240)
qrCodeImageView.set(.width, to: 240)
// Set up QR code image view container
let qrCodeImageViewContainer = UIView()
qrCodeImageViewContainer.addSubview(qrCodeImageView)
qrCodeImageView.center(.horizontal, in: qrCodeImageViewContainer)
qrCodeImageView.pin(.top, to: .top, of: qrCodeImageViewContainer)
qrCodeImageView.pin(.bottom, to: .bottom, of: qrCodeImageViewContainer)
// Set up explanation label
let explanationLabel = UILabel()
explanationLabel.textColor = Colors.text
@ -193,7 +201,7 @@ private final class ViewMyQRCodeVC : UIViewController {
shareButtonContainer.pin(.trailing, to: .trailing, of: shareButton, withInset: 80)
shareButtonContainer.pin(.bottom, to: .bottom, of: shareButton)
// Set up stack view
let stackView = UIStackView(arrangedSubviews: [ titleLabel, qrCodeImageView, explanationLabel, shareButtonContainer, UIView.vStretchingSpacer() ])
let stackView = UIStackView(arrangedSubviews: [ titleLabel, qrCodeImageViewContainer, explanationLabel, shareButtonContainer, UIView.vStretchingSpacer() ])
stackView.axis = .vertical
stackView.spacing = Values.largeSpacing
stackView.alignment = .fill
@ -215,7 +223,7 @@ private final class ViewMyQRCodeVC : UIViewController {
// MARK: Interaction
@objc private func shareQRCode() {
let qrCode = QRCode.generate(for: userHexEncodedPublicKey, isInverted: false)
let qrCode = QRCode.generate(for: userHexEncodedPublicKey, hasBackground: true)
let shareVC = UIActivityViewController(activityItems: [ qrCode ], applicationActivities: nil)
qrCodeVC.navigationController!.present(shareVC, animated: true, completion: nil)
}

View File

@ -104,6 +104,7 @@ NS_ASSUME_NONNULL_BEGIN
if (!self.capture) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
self.capture = [[ZXCapture alloc] init];
self.capture.invert = YES;
self.capture.camera = self.capture.back;
self.capture.focusMode = AVCaptureFocusModeContinuousAutoFocus;
self.capture.delegate = self;

View File

@ -697,7 +697,7 @@ typedef enum : NSUInteger {
[self.collectionView autoPinEdgeToSuperviewSafeArea:ALEdgeTrailing];
_progressIndicatorView = [UIProgressView new];
[self.progressIndicatorView autoSetDimension:ALDimensionHeight toSize:LKValues.messageProgressBarThickness];
[self.progressIndicatorView autoSetDimension:ALDimensionHeight toSize:LKValues.progressBarThickness];
self.progressIndicatorView.progressViewStyle = UIProgressViewStyleBar;
self.progressIndicatorView.progressTintColor = LKColors.accent;
self.progressIndicatorView.trackTintColor = UIColor.clearColor;

View File

@ -2740,3 +2740,10 @@
"Enter your seed" = "Enter your seed";
"Message" = "Message";
"You" = "You";
"Encrypting message" = "Encrypting message";
"Tracing a path" = "Tracing a path";
"Sending message" = "Sending message";
"Message sent securely" = "Message sent securely";
"Message failed to send" = "Message failed to send";
"Secure your account by saving your seed" = "Secure your account by saving your seed";
"Continue" = "Continue";