From 823d6de2e6dc89269ed9ecda708addc9bfb84ab6 Mon Sep 17 00:00:00 2001 From: Niels Andriesse Date: Mon, 2 Dec 2019 11:58:15 +1100 Subject: [PATCH] Start new conversation screen redesign --- Signal.xcodeproj/project.pbxproj | 12 ++ Signal/src/AppDelegate.m | 2 +- Signal/src/Loki/HomeVC.swift | 49 +++-- .../Loki/Messaging/NewConversationVCV2.swift | 170 ++++++++++++++++++ Signal/src/Loki/ProfilePictureView.swift | 10 +- Signal/src/Loki/SearchBar.swift | 6 +- Signal/src/Loki/Separator.swift | 65 +++++++ Signal/src/Loki/TextField.swift | 40 +++++ .../Loki/Utilities/Style Guide/Colors.swift | 3 +- .../Loki/Utilities/Style Guide/Values.swift | 10 +- Signal/src/environment/SignalApp.m | 7 +- .../translations/en.lproj/Localizable.strings | 4 + 12 files changed, 342 insertions(+), 36 deletions(-) create mode 100644 Signal/src/Loki/Messaging/NewConversationVCV2.swift create mode 100644 Signal/src/Loki/Separator.swift create mode 100644 Signal/src/Loki/TextField.swift diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 1a695ea01..343fe4a30 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -591,6 +591,9 @@ B8BB82AB238F669C00BA5194 /* ConversationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82AA238F669C00BA5194 /* ConversationCell.swift */; }; B8BB82AD238F734800BA5194 /* ProfilePictureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82AC238F734800BA5194 /* ProfilePictureView.swift */; }; B8BB82B12390C37000BA5194 /* SearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82B02390C37000BA5194 /* SearchBar.swift */; }; + B8BB82B323947E6B00BA5194 /* NewConversationVCV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82B223947E6B00BA5194 /* NewConversationVCV2.swift */; }; + B8BB82B523947F2D00BA5194 /* TextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82B423947F2D00BA5194 /* TextField.swift */; }; + B8BB82B92394911B00BA5194 /* Separator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8BB82B82394911B00BA5194 /* Separator.swift */; }; B90418E6183E9DD40038554A /* DateUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = B90418E5183E9DD40038554A /* DateUtil.m */; }; B9EB5ABD1884C002007CBB57 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B9EB5ABC1884C002007CBB57 /* MessageUI.framework */; }; BFF3FB9730634F37D25903F4 /* Pods_Signal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D17BB5C25D615AB49813100C /* Pods_Signal.framework */; }; @@ -1410,6 +1413,9 @@ B8BB82AA238F669C00BA5194 /* ConversationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationCell.swift; sourceTree = ""; }; B8BB82AC238F734800BA5194 /* ProfilePictureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilePictureView.swift; sourceTree = ""; }; B8BB82B02390C37000BA5194 /* SearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBar.swift; sourceTree = ""; }; + B8BB82B223947E6B00BA5194 /* NewConversationVCV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewConversationVCV2.swift; sourceTree = ""; }; + B8BB82B423947F2D00BA5194 /* TextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextField.swift; sourceTree = ""; }; + B8BB82B82394911B00BA5194 /* Separator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Separator.swift; sourceTree = ""; }; B90418E4183E9DD40038554A /* DateUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DateUtil.h; sourceTree = ""; }; B90418E5183E9DD40038554A /* DateUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DateUtil.m; sourceTree = ""; }; B97940251832BD2400BD66CB /* UIUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIUtil.h; sourceTree = ""; }; @@ -2673,6 +2679,8 @@ B8BB82A4238F627000BA5194 /* HomeVC.swift */, B8BB82AA238F669C00BA5194 /* ConversationCell.swift */, B8BB82B02390C37000BA5194 /* SearchBar.swift */, + B8BB82B423947F2D00BA5194 /* TextField.swift */, + B8BB82B82394911B00BA5194 /* Separator.swift */, B8BB82AC238F734800BA5194 /* ProfilePictureView.swift */, ); path = Loki; @@ -2744,6 +2752,7 @@ B8B26C8E234D629C004ED98C /* MentionCandidateSelectionView.swift */, B8B26C90234D8CBD004ED98C /* MentionCandidateSelectionViewDelegate.swift */, B89841E222B7579F00B1BDC6 /* NewConversationVC.swift */, + B8BB82B223947E6B00BA5194 /* NewConversationVCV2.swift */, 24BD2608234DA2050008EB0A /* JoinPublicChatVC.swift */, B893063E2383961A005EAA8E /* ScanQRCodeWrapperVC.swift */, ); @@ -3753,6 +3762,7 @@ files = ( 4CC0B59C20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift in Sources */, 3461293E1FD1D72B00532771 /* ExperienceUpgradeFinder.swift in Sources */, + B8BB82B323947E6B00BA5194 /* NewConversationVCV2.swift in Sources */, 34C4E2582118957600BEA353 /* WebRTCProto.swift in Sources */, 34D1F0BD1F8D108C0066283D /* AttachmentUploadView.m in Sources */, 452EC6DF205E9E30000E787C /* MediaGalleryViewController.swift in Sources */, @@ -3785,6 +3795,7 @@ B86BD08123399883000F5AE3 /* QRCodeModal.swift in Sources */, 4CFD151D22415AA400F2450F /* CallVideoHintView.swift in Sources */, 34D1F0AB1F867BFC0066283D /* OWSContactOffersCell.m in Sources */, + B8BB82B92394911B00BA5194 /* Separator.swift in Sources */, 343A65981FC4CFE7000477A1 /* ConversationScrollButton.m in Sources */, 34386A51207D0C01009F5D9C /* HomeViewController.m in Sources */, 34D1F0A91F867BFC0066283D /* ConversationViewCell.m in Sources */, @@ -3937,6 +3948,7 @@ 3488F9362191CC4000E524CC /* ConversationMediaView.swift in Sources */, 45F32C242057297A00A300D5 /* MessageDetailViewController.swift in Sources */, 3496955C219B605E00DCFE74 /* ImagePickerController.swift in Sources */, + B8BB82B523947F2D00BA5194 /* TextField.swift in Sources */, 34D1F0841F8678AA0066283D /* ConversationInputToolbar.m in Sources */, 457F671B20746193000EABCD /* QuotedReplyPreview.swift in Sources */, 34A6C28021E503E700B5B12E /* OWSImagePickerController.swift in Sources */, diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index d438cb471..5f11446e6 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -877,7 +877,7 @@ static NSTimeInterval launchStartedAt; return; } -// [SignalApp.sharedApp.homeViewController showNewConversationVC]; + [SignalApp.sharedApp.homeViewController createPrivateChat]; completionHandler(YES); }]; diff --git a/Signal/src/Loki/HomeVC.swift b/Signal/src/Loki/HomeVC.swift index fa40cd118..98cca4d2b 100644 --- a/Signal/src/Loki/HomeVC.swift +++ b/Signal/src/Loki/HomeVC.swift @@ -32,6 +32,26 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat return result }() + private lazy var newConversationButton: UIButton = { + let result = UIButton() + result.setTitle("+", for: UIControl.State.normal) + result.titleLabel!.font = .systemFont(ofSize: 35) + result.setTitleColor(UIColor(hex: 0x121212), for: UIControl.State.normal) + result.titleEdgeInsets = UIEdgeInsets(top: 0, left: 1, bottom: 4, right: 0) // Slight adjustment to make the plus exactly centered + result.backgroundColor = Colors.accent + let size = Values.newConversationButtonSize + result.layer.cornerRadius = size / 2 + result.layer.shadowPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: size, height: size))).cgPath + result.layer.shadowColor = Colors.newConversationButtonShadow.cgColor + result.layer.shadowOffset = CGSize(width: 0, height: 0.8) + result.layer.shadowOpacity = 1 + result.layer.shadowRadius = 6 + result.layer.masksToBounds = false + result.set(.width, to: size) + result.set(.height, to: size) + return result + }() + // MARK: Lifecycle override func viewDidLoad() { // Set gradient background @@ -63,22 +83,7 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat searchBar.sizeToFit() tableView.contentOffset = CGPoint(x: 0, y: searchBar.frame.height) // Set up new conversation button - let newConversationButton = UIButton() - newConversationButton.setTitle("+", for: UIControl.State.normal) - newConversationButton.titleLabel!.font = .systemFont(ofSize: 35) - newConversationButton.setTitleColor(UIColor(hex: 0x121212), for: UIControl.State.normal) - newConversationButton.titleEdgeInsets = UIEdgeInsets(top: 0, left: 1, bottom: 4, right: 0) // Slight adjustment to make the plus exactly centered - newConversationButton.backgroundColor = Colors.accent - let newConversationButtonSize = Values.newConversationButtonSize - newConversationButton.layer.cornerRadius = newConversationButtonSize / 2 - newConversationButton.layer.shadowPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: newConversationButtonSize, height: newConversationButtonSize))).cgPath - newConversationButton.layer.shadowColor = Colors.newConversationButtonShadow.cgColor - newConversationButton.layer.shadowOffset = CGSize(width: 0, height: 0.8) - newConversationButton.layer.shadowOpacity = 1 - newConversationButton.layer.shadowRadius = 6 - newConversationButton.layer.masksToBounds = false - newConversationButton.set(.width, to: newConversationButtonSize) - newConversationButton.set(.height, to: newConversationButtonSize) + newConversationButton.addTarget(self, action: #selector(createPrivateChat), for: UIControl.Event.touchUpInside) view.addSubview(newConversationButton) newConversationButton.center(.horizontal, in: view) newConversationButton.pin(.bottom, to: .bottom, of: view, withInset: -Values.newConversationButtonBottomOffset) // Negative due to how the constraint is set up @@ -229,14 +234,14 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { guard let thread = self.thread(at: indexPath.row) else { return } - show(thread, with: ConversationViewAction.none, animated: true) + show(thread, with: ConversationViewAction.none, highlightedMessageID: nil, animated: true) tableView.deselectRow(at: indexPath, animated: true) } - private func show(_ thread: TSThread, with action: ConversationViewAction, animated: Bool) { + @objc func show(_ thread: TSThread, with action: ConversationViewAction, highlightedMessageID: String?, animated: Bool) { DispatchMainThreadSafe { let conversationVC = ConversationViewController() - conversationVC.configure(for: thread, action: action, focusMessageId: nil) // TODO: focusMessageId + conversationVC.configure(for: thread, action: action, focusMessageId: highlightedMessageID) self.navigationController?.setViewControllers([ self, conversationVC ], animated: true) } } @@ -295,6 +300,12 @@ final class HomeVC : UIViewController, UITableViewDataSource, UITableViewDelegat // TODO: Implement } + @objc func createPrivateChat() { + let newConversationVC = NewConversationVCV2() + let navigationController = OWSNavigationController(rootViewController: newConversationVC) + present(navigationController, animated: true, completion: nil) + } + // MARK: Convenience private func thread(at index: Int) -> TSThread? { var thread: TSThread? = nil diff --git a/Signal/src/Loki/Messaging/NewConversationVCV2.swift b/Signal/src/Loki/Messaging/NewConversationVCV2.swift new file mode 100644 index 000000000..f68e6b42f --- /dev/null +++ b/Signal/src/Loki/Messaging/NewConversationVCV2.swift @@ -0,0 +1,170 @@ + +@objc(LKNewConversationVCV2) +final class NewConversationVCV2 : OWSViewController, OWSQRScannerDelegate { + + private lazy var userHexEncodedPublicKey: String = { + let userDefaults = UserDefaults.standard + if let masterHexEncodedPublicKey = userDefaults.string(forKey: "masterDeviceHexEncodedPublicKey") { + return masterHexEncodedPublicKey + } else { + return OWSIdentityManager.shared().identityKeyPair()!.hexEncodedPublicKey + } + }() + + // MARK: Components + private lazy var publicKeyTextField = TextField(placeholder: NSLocalizedString("Enter public key of recipient", comment: "")) + + // MARK: Lifecycle + override func viewDidLoad() { + // Set gradient background + view.backgroundColor = .clear + let gradient = Gradients.defaultLokiBackground + view.setGradient(gradient) + // Set navigation bar background color + if let navigationBar = navigationController?.navigationBar { + navigationBar.setBackgroundImage(UIImage(), for: UIBarMetrics.default) + navigationBar.shadowImage = UIImage() + navigationBar.isTranslucent = false + navigationBar.barTintColor = Colors.navigationBarBackground + } + // Set up the navigation bar buttons + let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close)) + closeButton.tintColor = Colors.text + navigationItem.leftBarButtonItem = closeButton + // Customize title + let titleLabel = UILabel() + titleLabel.text = NSLocalizedString("New Conversation", comment: "") + titleLabel.textColor = Colors.text + titleLabel.font = UIFont.boldSystemFont(ofSize: Values.veryLargeFontSize) + navigationItem.titleView = titleLabel + // Set up explanation label + let explanationLabel = UILabel() + explanationLabel.textColor = Colors.unimportant + explanationLabel.font = .systemFont(ofSize: Values.smallFontSize) + explanationLabel.text = NSLocalizedString("Users can share their public key by going into their account settings and tapping \"Share Public Key\", or by sharing their QR code.", comment: "") + explanationLabel.numberOfLines = 0 + explanationLabel.textAlignment = .center + explanationLabel.lineBreakMode = .byWordWrapping + // Set up separator + let separator = Separator(title: NSLocalizedString("Your Public Key", comment: "")) + + + +// // Background color & margins +// view.backgroundColor = Theme.backgroundColor +// view.layoutMargins = .zero +// // Navigation bar +// navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(close)) +// title = NSLocalizedString("New Conversation", comment: "") +// // Separator +// let separator = UIView() +// separator.autoSetDimension(.height, toSize: 1 / UIScreen.main.scale) +// separator.backgroundColor = Theme.hairlineColor +// // Explanation label +// let explanationLabel = UILabel() +// explanationLabel.textColor = Theme.primaryColor +// explanationLabel.font = UIFont.ows_dynamicTypeSubheadlineClamped +// explanationLabel.text = NSLocalizedString("Enter the public key of the person you'd like to securely message. They can share their public key with you by going into Loki Messenger's in-app settings and clicking \"Share Public Key\".", comment: "") +// explanationLabel.numberOfLines = 0 +// explanationLabel.lineBreakMode = .byWordWrapping +// // QR code button +// let qrCodeButtonFont = UIFont.ows_dynamicTypeBodyClamped.ows_mediumWeight() +// let qrCodeButtonHeight = qrCodeButtonFont.pointSize * 48 / 17 +// let qrCodeButton = OWSFlatButton.button(title: NSLocalizedString("Scan a QR Code Instead", comment: ""), font: qrCodeButtonFont, titleColor: .lokiGreen(), backgroundColor: .clear, target: self, selector: #selector(scanQRCode)) +// qrCodeButton.setBackgroundColors(upColor: .clear, downColor: .clear) +// qrCodeButton.autoSetDimension(.height, toSize: qrCodeButtonHeight) +// qrCodeButton.button.contentHorizontalAlignment = .left +// // Next button +// let nextButtonFont = UIFont.ows_dynamicTypeBodyClamped.ows_mediumWeight() +// let nextButtonHeight = nextButtonFont.pointSize * 48 / 17 +// let nextButton = OWSFlatButton.button(title: NSLocalizedString("Next", comment: ""), font: nextButtonFont, titleColor: .white, backgroundColor: .lokiGreen(), target: self, selector: #selector(handleNextButtonTapped)) +// nextButton.autoSetDimension(.height, toSize: nextButtonHeight) + // Stack view + let stackView = UIStackView(arrangedSubviews: [ publicKeyTextField, UIView.spacer(withHeight: Values.smallSpacing), explanationLabel, UIView.spacer(withHeight: Values.veryLargeSpacing), separator, UIView.vStretchingSpacer() ]) + stackView.axis = .vertical + stackView.alignment = .fill + stackView.layoutMargins = UIEdgeInsets(top: Values.mediumSpacing, left: Values.largeSpacing, bottom: Values.mediumSpacing, right: Values.largeSpacing) + stackView.isLayoutMarginsRelativeArrangement = true + view.addSubview(stackView) + stackView.pin(to: view) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + publicKeyTextField.becomeFirstResponder() + } + + // 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: "")) +// }, completion: nil) + } + + // MARK: Interaction + @objc private func close() { + dismiss(animated: true, completion: nil) + } + + @objc private func copyPublicKey() { +// UIPasteboard.general.string = userHexEncodedPublicKey +// copyButton.isUserInteractionEnabled = false +// UIView.transition(with: copyButton, duration: 0.25, options: .transitionCrossDissolve, animations: { +// self.copyButton.setTitle(NSLocalizedString("Copied ✓", comment: "")) +// }, completion: nil) +// Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(enableCopyButton), userInfo: nil, repeats: false) + } + + @objc private func sharePublicKey() { + let shareVC = UIActivityViewController(activityItems: [ userHexEncodedPublicKey ], applicationActivities: nil) + present(shareVC, animated: true, completion: nil) +// NSString *hexEncodedPublicKey; +// NSString *masterDeviceHexEncodedPublicKey = [NSUserDefaults.standardUserDefaults stringForKey:@"masterDeviceHexEncodedPublicKey"]; +// if (masterDeviceHexEncodedPublicKey != nil) { +// hexEncodedPublicKey = masterDeviceHexEncodedPublicKey; +// } else { +// hexEncodedPublicKey = OWSIdentityManager.sharedManager.identityKeyPair.hexEncodedPublicKey; +// } +// UIActivityViewController *shareVC = [[UIActivityViewController alloc] initWithActivityItems:@[ hexEncodedPublicKey ] applicationActivities:nil]; +// [self presentViewController:shareVC animated:YES completion:nil]; +// [LKAnalytics.shared track:@"Public Key Shared"]; + } + +// @objc private func scanQRCode() { +// ows_ask(forCameraPermissions: { [weak self] hasCameraAccess in +// if hasCameraAccess { +// let message = NSLocalizedString("Scan the QR code of the person you'd like to securely message. They can find their QR code by going into Loki Messenger's in-app settings and clicking \"Show QR Code\".", comment: "") +// let scanQRCodeWrapperVC = ScanQRCodeWrapperVC(message: message) +// scanQRCodeWrapperVC.delegate = self +// self?.navigationController!.pushViewController(scanQRCodeWrapperVC, animated: true) +// } else { +// // Do nothing +// } +// }) +// } +// +// func controller(_ controller: OWSQRCodeScanningViewController, didDetectQRCodeWith string: String) { +// Analytics.shared.track("QR Code Scanned") +// let hexEncodedPublicKey = string +// startNewConversationIfPossible(with: hexEncodedPublicKey) +// } + + @objc private func handleNextButtonTapped() { + let hexEncodedPublicKey = publicKeyTextField.text?.trimmingCharacters(in: .whitespaces) ?? "" + startNewConversationIfPossible(with: hexEncodedPublicKey) + } + + private func startNewConversationIfPossible(with hexEncodedPublicKey: String) { + if !ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) { + let alert = UIAlertController(title: NSLocalizedString("Invalid Public Key", comment: ""), message: NSLocalizedString("Please check the public key you entered and try again.", comment: ""), preferredStyle: .alert) + alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil)) + presentAlert(alert) + } else { + let thread = TSContactThread.getOrCreateThread(contactId: hexEncodedPublicKey) + Analytics.shared.track("New Conversation Started") + SignalApp.shared().presentConversation(for: thread, action: .compose, animated: false) + presentingViewController!.dismiss(animated: true, completion: nil) + } + } +} diff --git a/Signal/src/Loki/ProfilePictureView.swift b/Signal/src/Loki/ProfilePictureView.swift index 88ba18440..37888ffba 100644 --- a/Signal/src/Loki/ProfilePictureView.swift +++ b/Signal/src/Loki/ProfilePictureView.swift @@ -14,15 +14,15 @@ final class ProfilePictureView : UIView { // MARK: Lifecycle override init(frame: CGRect) { super.init(frame: frame) - initialize() + setUpViewHierarchy() } required init?(coder: NSCoder) { super.init(coder: coder) - initialize() + setUpViewHierarchy() } - private func initialize() { + private func setUpViewHierarchy() { // Set up image view addSubview(imageView) imageView.pin(.leading, to: .leading, of: self) @@ -82,8 +82,8 @@ final class ProfilePictureView : UIView { let result = UIImageView() result.layer.masksToBounds = true result.backgroundColor = Colors.unimportant - result.layer.borderColor = Colors.profilePictureBorder.cgColor - result.layer.borderWidth = Values.profilePictureBorderThickness + result.layer.borderColor = Colors.border.cgColor + result.layer.borderWidth = Values.borderThickness result.contentMode = .scaleAspectFit return result } diff --git a/Signal/src/Loki/SearchBar.swift b/Signal/src/Loki/SearchBar.swift index d6232929a..a1a1de48b 100644 --- a/Signal/src/Loki/SearchBar.swift +++ b/Signal/src/Loki/SearchBar.swift @@ -3,15 +3,15 @@ final class SearchBar : UISearchBar { override init(frame: CGRect) { super.init(frame: frame) - update() + setUpStyle() } required init?(coder: NSCoder) { super.init(coder: coder) - update() + setUpStyle() } - private func update() { + private func setUpStyle() { searchBarStyle = .minimal // Hide the border around the search bar barStyle = .black // Use Apple's black design as a base tintColor = Colors.accent // The cursor color diff --git a/Signal/src/Loki/Separator.swift b/Signal/src/Loki/Separator.swift new file mode 100644 index 000000000..774724653 --- /dev/null +++ b/Signal/src/Loki/Separator.swift @@ -0,0 +1,65 @@ + +final class Separator : UIView { + private let title: String + + // MARK: Components + private lazy var titleLabel: UILabel = { + let result = UILabel() + result.textColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) + result.font = .systemFont(ofSize: Values.smallFontSize) + result.textAlignment = .center + return result + }() + + private lazy var lineLayer: CAShapeLayer = { + let result = CAShapeLayer() + result.lineWidth = Values.separatorThickness + result.strokeColor = Colors.separator.cgColor + result.fillColor = UIColor.clear.cgColor + return result + }() + + // MARK: Initialization + init(title: String) { + self.title = title + super.init(frame: CGRect.zero) + setUpViewHierarchy() + } + + override init(frame: CGRect) { + preconditionFailure("Use init(title:) instead.") + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(title:) instead.") + } + + private func setUpViewHierarchy() { + titleLabel.text = title + addSubview(titleLabel) + titleLabel.center(.horizontal, in: self) + titleLabel.center(.vertical, in: self) + layer.insertSublayer(lineLayer, at: 0) + } + + // MARK: Updating + override func layoutSubviews() { + super.layoutSubviews() + updateLineLayer() + } + + private func updateLineLayer() { + let w = width() + let h = height() + let path = UIBezierPath() + path.move(to: CGPoint(x: 0, y: h / 2)) + let titleLabelFrame = titleLabel.frame.insetBy(dx: -10, dy: -6) + path.addLine(to: CGPoint(x: titleLabelFrame.origin.x, y: h / 2)) + let oval = UIBezierPath(roundedRect: titleLabelFrame, cornerRadius: Values.separatorCornerRadius) + path.append(oval) + path.move(to: CGPoint(x: titleLabelFrame.origin.x + titleLabelFrame.width, y: h / 2)) + path.addLine(to: CGPoint(x: w, y: h / 2)) + path.close() + lineLayer.path = path.cgPath + } +} diff --git a/Signal/src/Loki/TextField.swift b/Signal/src/Loki/TextField.swift new file mode 100644 index 000000000..06346e04c --- /dev/null +++ b/Signal/src/Loki/TextField.swift @@ -0,0 +1,40 @@ + +final class TextField : UITextField { + + init(placeholder: String) { + super.init(frame: CGRect.zero) + self.placeholder = placeholder + setUpStyle() + } + + override init(frame: CGRect) { + preconditionFailure("Use init(placeholder:) instead.") + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(placeholder:) instead.") + } + + private func setUpStyle() { + textColor = Colors.text + font = .systemFont(ofSize: Values.smallFontSize) + let placeholder = NSMutableAttributedString(string: self.placeholder!) + let placeholderColor = Colors.text.withAlphaComponent(Values.unimportantElementOpacity) + placeholder.addAttribute(.foregroundColor, value: placeholderColor, range: NSRange(location: 0, length: placeholder.length)) + attributedPlaceholder = placeholder + tintColor = Colors.accent + keyboardAppearance = .dark + set(.height, to: Values.textFieldHeight) + layer.borderColor = Colors.border.withAlphaComponent(Values.textFieldBorderOpacity).cgColor + layer.borderWidth = Values.borderThickness + layer.cornerRadius = Values.textFieldCornerRadius + } + + override func textRect(forBounds bounds: CGRect) -> CGRect { + return bounds.insetBy(dx: Values.largeSpacing, dy: Values.largeSpacing) + } + + override func editingRect(forBounds bounds: CGRect) -> CGRect { + return bounds.insetBy(dx: Values.largeSpacing, dy: Values.largeSpacing) + } +} diff --git a/Signal/src/Loki/Utilities/Style Guide/Colors.swift b/Signal/src/Loki/Utilities/Style Guide/Colors.swift index cf5e07247..183af7ba3 100644 --- a/Signal/src/Loki/Utilities/Style Guide/Colors.swift +++ b/Signal/src/Loki/Utilities/Style Guide/Colors.swift @@ -15,11 +15,12 @@ final class Colors : NSObject { @objc static let accent = UIColor(hex: 0x00F782) @objc static let text = UIColor(hex: 0xFFFFFF) @objc static let unimportant = UIColor(hex: 0xD8D8D8) - @objc static let profilePictureBorder = UIColor(hex: 0x979797) + @objc static let border = UIColor(hex: 0x979797) @objc static let conversationCellBackground = UIColor(hex: 0x1B1B1B) @objc static let conversationCellSelected = UIColor(hex: 0x0C0C0C) @objc static let navigationBarBackground = UIColor(hex: 0x161616) @objc static let searchBarPlaceholder = UIColor(hex: 0x8E8E93) // Also used for the icons @objc static let searchBarBackground = UIColor(red: 142 / 255, green: 142 / 255, blue: 147 / 255, alpha: 0.12) @objc static let newConversationButtonShadow = UIColor(hex: 0x077C44) + @objc static let separator = UIColor(hex: 0x36383C) } diff --git a/Signal/src/Loki/Utilities/Style Guide/Values.swift b/Signal/src/Loki/Utilities/Style Guide/Values.swift index 75afec7b7..b790a26da 100644 --- a/Signal/src/Loki/Utilities/Style Guide/Values.swift +++ b/Signal/src/Loki/Utilities/Style Guide/Values.swift @@ -3,8 +3,9 @@ final class Values : NSObject { // MARK: - Alpha Values - @objc static let inactiveElementOpacity = CGFloat(0.6) + @objc static let unimportantElementOpacity = CGFloat(0.6) @objc static let conversationCellTimestampOpacity = CGFloat(0.4) + @objc static let textFieldBorderOpacity = CGFloat(0.4) // MARK: - Font Sizes @objc static let smallFontSize = CGFloat(13) @@ -20,14 +21,19 @@ final class Values : NSObject { @objc static let smallProfilePictureSize = CGFloat(35) @objc static let mediumProfilePictureSize = CGFloat(45) @objc static let largeProfilePictureSize = CGFloat(65) - @objc static let profilePictureBorderThickness = CGFloat(1) + @objc static let borderThickness = CGFloat(1) @objc static let conversationCellStatusIndicatorSize = CGFloat(14) @objc static let searchBarHeight = CGFloat(36) @objc static let newConversationButtonSize = CGFloat(45) + @objc static let textFieldHeight = CGFloat(80) + @objc static let textFieldCornerRadius = CGFloat(8) + @objc static let separatorCornerRadius = CGFloat(10) + @objc static var separatorThickness: CGFloat { return 1 / UIScreen.main.scale } // MARK: - Distances @objc static let smallSpacing = CGFloat(8) @objc static let mediumSpacing = CGFloat(16) @objc static let largeSpacing = CGFloat(24) + @objc static let veryLargeSpacing = CGFloat(35) @objc static let newConversationButtonBottomOffset = CGFloat(52) } diff --git a/Signal/src/environment/SignalApp.m b/Signal/src/environment/SignalApp.m index 542a5fc11..078977c42 100644 --- a/Signal/src/environment/SignalApp.m +++ b/Signal/src/environment/SignalApp.m @@ -117,7 +117,7 @@ NS_ASSUME_NONNULL_BEGIN } } -// [self.homeViewController presentThread:thread action:action focusMessageId:focusMessageId animated:isAnimated]; + [self.homeViewController show:thread with:action highlightedMessageID:focusMessageId animated:isAnimated]; }); } @@ -145,10 +145,7 @@ NS_ASSUME_NONNULL_BEGIN } } -// [self.homeViewController presentThread:thread -// action:ConversationViewActionNone -// focusMessageId:nil -// animated:isAnimated]; + [self.homeViewController show:thread with:ConversationViewActionNone highlightedMessageID:nil animated:isAnimated]; }); } diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 9d1dda515..a0de2077b 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -2679,3 +2679,7 @@ "New Group" = "New Group"; "Delete" = "Delete"; "Search" = "Search"; +"New Conversation" = "New Conversation"; +"Enter public key of recipient" = "Enter public key of recipient"; +"Users can share their public key by going into their account settings and tapping \"Share Public Key\", or by sharing their QR code." = "Users can share their public key by going into their account settings and tapping \"Share Public Key\", or by sharing their QR code."; +"Your Public Key" = "Your Public Key";