diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 0b34e517f..6939b7a95 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -166,6 +166,7 @@ 3496957421A301A100DCFE74 /* OWSBackupAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3496956B21A301A100DCFE74 /* OWSBackupAPI.swift */; }; 349EA07C2162AEA800F7B17F /* OWS111UDAttributesMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 349EA07B2162AEA700F7B17F /* OWS111UDAttributesMigration.swift */; }; 34A4C61E221613D00042EF2E /* OnboardingVerificationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34A4C61D221613D00042EF2E /* OnboardingVerificationViewController.swift */; }; + 34A4C62022175C5C0042EF2E /* OnboardingProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34A4C61F22175C5C0042EF2E /* OnboardingProfileViewController.swift */; }; 34A55F3720485465002CC6DE /* OWS2FARegistrationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A55F3520485464002CC6DE /* OWS2FARegistrationViewController.m */; }; 34A6C28021E503E700B5B12E /* OWSImagePickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34A6C27F21E503E600B5B12E /* OWSImagePickerController.swift */; }; 34A8B3512190A40E00218A25 /* MediaAlbumCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34A8B3502190A40E00218A25 /* MediaAlbumCellView.swift */; }; @@ -848,6 +849,7 @@ 3496956D21A301A100DCFE74 /* OWSBackupIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackupIO.h; sourceTree = ""; }; 349EA07B2162AEA700F7B17F /* OWS111UDAttributesMigration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWS111UDAttributesMigration.swift; sourceTree = ""; }; 34A4C61D221613D00042EF2E /* OnboardingVerificationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingVerificationViewController.swift; sourceTree = ""; }; + 34A4C61F22175C5C0042EF2E /* OnboardingProfileViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingProfileViewController.swift; sourceTree = ""; }; 34A55F3520485464002CC6DE /* OWS2FARegistrationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWS2FARegistrationViewController.m; sourceTree = ""; }; 34A55F3620485464002CC6DE /* OWS2FARegistrationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWS2FARegistrationViewController.h; sourceTree = ""; }; 34A6C27F21E503E600B5B12E /* OWSImagePickerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSImagePickerController.swift; sourceTree = ""; }; @@ -1474,6 +1476,7 @@ 3448E15D221333F5004B052E /* OnboardingController.swift */, 3448E15B22133274004B052E /* OnboardingPermissionsViewController.swift */, 3448E16322135FFA004B052E /* OnboardingPhoneNumberViewController.swift */, + 34A4C61F22175C5C0042EF2E /* OnboardingProfileViewController.swift */, 3448E15F22134C88004B052E /* OnboardingSplashViewController.swift */, 34A4C61D221613D00042EF2E /* OnboardingVerificationViewController.swift */, 346E9D5321B040B600562252 /* RegistrationController.swift */, @@ -3491,6 +3494,7 @@ 343A65981FC4CFE7000477A1 /* ConversationScrollButton.m in Sources */, 34386A51207D0C01009F5D9C /* HomeViewController.m in Sources */, 34D1F0A91F867BFC0066283D /* ConversationViewCell.m in Sources */, + 34A4C62022175C5C0042EF2E /* OnboardingProfileViewController.swift in Sources */, 4505C2BF1E648EA300CEBF41 /* ExperienceUpgrade.swift in Sources */, EF764C351DB67CC5000D9A87 /* UIViewController+Permissions.m in Sources */, 45CD81EF1DC030E7004C9430 /* SyncPushTokensJob.swift in Sources */, diff --git a/Signal/src/Signal-Bridging-Header.h b/Signal/src/Signal-Bridging-Header.h index a8f7d2804..12e45a336 100644 --- a/Signal/src/Signal-Bridging-Header.h +++ b/Signal/src/Signal-Bridging-Header.h @@ -8,6 +8,7 @@ // Separate iOS Frameworks from other imports. #import "AppSettingsViewController.h" #import "AttachmentUploadView.h" +#import "AvatarViewHelper.h" #import "ContactCellView.h" #import "ContactTableViewCell.h" #import "ConversationViewCell.h" diff --git a/Signal/src/ViewControllers/AvatarViewHelper.h b/Signal/src/ViewControllers/AvatarViewHelper.h index 01d6b71f8..82ecc282d 100644 --- a/Signal/src/ViewControllers/AvatarViewHelper.h +++ b/Signal/src/ViewControllers/AvatarViewHelper.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. // #import @@ -13,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN @protocol AvatarViewHelperDelegate -- (NSString *)avatarActionSheetTitle; +- (nullable NSString *)avatarActionSheetTitle; - (void)avatarDidChange:(UIImage *)image; diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 2e8ab8ba1..a0b62b970 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -482,6 +482,19 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations [self.searchResultsController viewDidAppear:animated]; self.hasEverAppeared = YES; + + dispatch_async(dispatch_get_main_queue(), ^{ + OnboardingController *onboardingController = [OnboardingController new]; + [onboardingController + updateWithPhoneNumber:[[OnboardingPhoneNumber alloc] initWithE164:@"+13213214321" userInput:@"3213214321"]]; + + // UIViewController *view = [onboardingController initialViewController]; + UIViewController *view = + [[OnboardingProfileViewController alloc] initWithOnboardingController:onboardingController]; + OWSNavigationController *navigationController = + [[OWSNavigationController alloc] initWithRootViewController:view]; + [self presentViewController:navigationController animated:YES completion:nil]; + }); } - (void)viewDidDisappear:(BOOL)animated diff --git a/Signal/src/ViewControllers/NewGroupViewController.m b/Signal/src/ViewControllers/NewGroupViewController.m index 258fd631a..562d97c37 100644 --- a/Signal/src/ViewControllers/NewGroupViewController.m +++ b/Signal/src/ViewControllers/NewGroupViewController.m @@ -612,7 +612,7 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - AvatarViewHelperDelegate -- (NSString *)avatarActionSheetTitle +- (nullable NSString *)avatarActionSheetTitle { return NSLocalizedString( @"NEW_GROUP_ADD_PHOTO_ACTION", @"Action Sheet title prompting the user for a group avatar"); diff --git a/Signal/src/ViewControllers/ProfileViewController.m b/Signal/src/ViewControllers/ProfileViewController.m index 90d65feb1..2ce609506 100644 --- a/Signal/src/ViewControllers/ProfileViewController.m +++ b/Signal/src/ViewControllers/ProfileViewController.m @@ -573,7 +573,7 @@ NSString *const kProfileView_LastPresentedDate = @"kProfileView_LastPresentedDat #pragma mark - AvatarViewHelperDelegate -- (NSString *)avatarActionSheetTitle +- (nullable NSString *)avatarActionSheetTitle { return NSLocalizedString( @"PROFILE_VIEW_AVATAR_ACTIONSHEET_TITLE", @"Action Sheet title prompting the user for a profile avatar"); diff --git a/Signal/src/ViewControllers/Registration/OnboardingController.swift b/Signal/src/ViewControllers/Registration/OnboardingController.swift index a457b56fb..c8b91a6df 100644 --- a/Signal/src/ViewControllers/Registration/OnboardingController.swift +++ b/Signal/src/ViewControllers/Registration/OnboardingController.swift @@ -450,3 +450,21 @@ public class OnboardingController: NSObject { } } } + +// MARK: - + +public extension UIView { + public func addBottomStroke() -> UIView { + return addBottomStroke(color: Theme.middleGrayColor, strokeWidth: CGHairlineWidth()) + } + + public func addBottomStroke(color: UIColor, strokeWidth: CGFloat) -> UIView { + let strokeView = UIView() + strokeView.backgroundColor = color + addSubview(strokeView) + strokeView.autoSetDimension(.height, toSize: strokeWidth) + strokeView.autoPinWidthToSuperview() + strokeView.autoPinEdge(toSuperviewEdge: .bottom) + return strokeView + } +} diff --git a/Signal/src/ViewControllers/Registration/OnboardingPhoneNumberViewController.swift b/Signal/src/ViewControllers/Registration/OnboardingPhoneNumberViewController.swift index 388843b2b..ef0e7c791 100644 --- a/Signal/src/ViewControllers/Registration/OnboardingPhoneNumberViewController.swift +++ b/Signal/src/ViewControllers/Registration/OnboardingPhoneNumberViewController.swift @@ -62,7 +62,7 @@ public class OnboardingPhoneNumberViewController: OnboardingBaseViewController { countryRow.isUserInteractionEnabled = true countryRow.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(countryRowTapped))) countryRow.autoSetDimension(.height, toSize: rowHeight) - _ = addBottomStroke(countryRow) + _ = countryRow.addBottomStroke() callingCodeLabel.textColor = Theme.primaryColor callingCodeLabel.font = UIFont.ows_dynamicTypeBodyClamped @@ -70,7 +70,7 @@ public class OnboardingPhoneNumberViewController: OnboardingBaseViewController { callingCodeLabel.setCompressionResistanceHorizontalHigh() callingCodeLabel.isUserInteractionEnabled = true callingCodeLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(countryCodeTapped))) - _ = addBottomStroke(callingCodeLabel) + _ = callingCodeLabel.addBottomStroke() callingCodeLabel.autoSetDimension(.width, toSize: rowHeight, relation: .greaterThanOrEqual) phoneNumberTextField.textAlignment = .left @@ -81,8 +81,8 @@ public class OnboardingPhoneNumberViewController: OnboardingBaseViewController { phoneNumberTextField.setContentHuggingHorizontalLow() phoneNumberTextField.setCompressionResistanceHorizontalLow() - phoneStrokeNormal = addBottomStroke(phoneNumberTextField) - phoneStrokeError = addBottomStroke(phoneNumberTextField, color: .ows_destructiveRed, strokeWidth: 2) + phoneStrokeNormal = phoneNumberTextField.addBottomStroke() + phoneStrokeError = phoneNumberTextField.addBottomStroke(color: .ows_destructiveRed, strokeWidth: 2) let phoneNumberRow = UIStackView(arrangedSubviews: [ callingCodeLabel, @@ -140,20 +140,6 @@ public class OnboardingPhoneNumberViewController: OnboardingBaseViewController { validationWarningLabel.autoPinEdge(.leading, to: .leading, of: phoneNumberTextField) } - private func addBottomStroke(_ view: UIView) -> UIView { - return addBottomStroke(view, color: Theme.middleGrayColor, strokeWidth: CGHairlineWidth()) - } - - private func addBottomStroke(_ view: UIView, color: UIColor, strokeWidth: CGFloat) -> UIView { - let strokeView = UIView() - strokeView.backgroundColor = color - view.addSubview(strokeView) - strokeView.autoSetDimension(.height, toSize: strokeWidth) - strokeView.autoPinWidthToSuperview() - strokeView.autoPinEdge(toSuperviewEdge: .bottom) - return strokeView - } - // MARK: - View Lifecycle public override func viewWillAppear(_ animated: Bool) { diff --git a/Signal/src/ViewControllers/Registration/OnboardingProfileViewController.swift b/Signal/src/ViewControllers/Registration/OnboardingProfileViewController.swift new file mode 100644 index 000000000..7d00e4d48 --- /dev/null +++ b/Signal/src/ViewControllers/Registration/OnboardingProfileViewController.swift @@ -0,0 +1,412 @@ +// +// Copyright (c) 2019 Open Whisper Systems. All rights reserved. +// + +import UIKit + +@objc +public class OnboardingProfileViewController: OnboardingBaseViewController { + +// private var titleLabel: UILabel? +// private let phoneNumberTextField = UITextField() +// private let onboardingCodeView = OnboardingCodeView() +// private var codeStateLink: OWSFlatButton? +// private let errorLabel = UILabel() + private let avatarView = AvatarImageView() + private let nameTextfield = UITextField() + private var avatar: UIImage? + private let cameraCircle = UIView.container() + + private let avatarViewHelper = AvatarViewHelper() + + override public func loadView() { + super.loadView() + + avatarViewHelper.delegate = self + + view.backgroundColor = Theme.backgroundColor + view.layoutMargins = .zero + + let titleLabel = self.titleLabel(text: NSLocalizedString("ONBOARDING_PROFILE_TITLE", comment: "Title of the 'onboarding profile' view.")) + + let explanationLabel = self.explanationLabel(explanationText: NSLocalizedString("ONBOARDING_PROFILE_EXPLANATION", + comment: "Explanation in the 'onboarding profile' view.")) + + let nextButton = self.button(title: NSLocalizedString("BUTTON_NEXT", + comment: "Label for the 'next' button."), + selector: #selector(nextPressed)) + + avatarView.autoSetDimensions(to: CGSize(width: CGFloat(avatarSize), height: CGFloat(avatarSize))) + + let cameraImageView = UIImageView() + cameraImageView.image = UIImage(named: "settings-avatar-camera") + cameraCircle.backgroundColor = Theme.backgroundColor + cameraCircle.addSubview(cameraImageView) + let cameraCircleDiameter: CGFloat = 40 + cameraCircle.autoSetDimensions(to: CGSize(width: cameraCircleDiameter, height: cameraCircleDiameter)) + cameraCircle.layer.shadowColor = UIColor(white: 0, alpha: 0.15).cgColor + cameraCircle.layer.shadowRadius = 5 + cameraCircle.layer.shadowOffset = CGSize(width: 1, height: 1) + cameraCircle.layer.shadowOpacity = 1 + cameraCircle.layer.cornerRadius = cameraCircleDiameter * 0.5 + cameraCircle.clipsToBounds = false + cameraImageView.autoCenterInSuperview() + + let avatarWrapper = UIView.container() + avatarWrapper.isUserInteractionEnabled = true + avatarWrapper.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(avatarTapped))) + avatarWrapper.addSubview(avatarView) + avatarView.autoPinEdgesToSuperviewEdges(with: UIEdgeInsets(top: 4, leading: 4, bottom: 4, trailing: 4)) + avatarWrapper.addSubview(cameraCircle) + cameraCircle.autoPinEdge(toSuperviewEdge: .trailing) + cameraCircle.autoPinEdge(toSuperviewEdge: .bottom) + + nameTextfield.textAlignment = .left + nameTextfield.delegate = self + nameTextfield.returnKeyType = .done + nameTextfield.textColor = Theme.primaryColor +// nameTextfield.tintColor = UIColor.ows_materialBlue + nameTextfield.font = UIFont.ows_dynamicTypeBodyClamped + nameTextfield.placeholder = NSLocalizedString("ONBOARDING_PROFILE_NAME_PLACEHOLDER", + comment: "Placeholder text for the profile name in the 'onboarding profile' view.") + nameTextfield.setContentHuggingHorizontalLow() + nameTextfield.setCompressionResistanceHorizontalLow() + + let nameWrapper = UIView.container() + nameWrapper.setCompressionResistanceHorizontalLow() + nameWrapper.setContentHuggingHorizontalLow() + nameWrapper.addSubview(nameTextfield) + nameTextfield.autoPinWidthToSuperview() + nameTextfield.autoPinEdge(toSuperviewEdge: .top, withInset: 8) + nameTextfield.autoPinEdge(toSuperviewEdge: .bottom, withInset: 8) + _ = nameWrapper.addBottomStroke() + + let profileRow = UIStackView(arrangedSubviews: [ + avatarWrapper, + nameWrapper + ]) + profileRow.axis = .horizontal + profileRow.alignment = .center + profileRow.spacing = 8 + + let topSpacer = UIView.vStretchingSpacer() + let bottomSpacer = UIView.vStretchingSpacer() + + let stackView = UIStackView(arrangedSubviews: [ + titleLabel, + topSpacer, + profileRow, + UIView.spacer(withHeight: 25), + explanationLabel, + UIView.spacer(withHeight: 20), + nextButton, + bottomSpacer + ]) + stackView.axis = .vertical + stackView.alignment = .fill + stackView.layoutMargins = UIEdgeInsets(top: 32, left: 32, bottom: 32, right: 32) + stackView.isLayoutMarginsRelativeArrangement = true + view.addSubview(stackView) + stackView.autoPinWidthToSuperview() + stackView.autoPin(toTopLayoutGuideOf: self, withInset: 0) + autoPinView(toBottomOfViewControllerOrKeyboard: stackView, avoidNotch: true) + + // Ensure whitespace is balanced, so inputs are vertically centered. + topSpacer.autoMatch(.height, to: .height, of: bottomSpacer) + + updateAvatarView() + } + + private let avatarSize: UInt = 80 + + private func updateAvatarView() { + if let avatar = avatar { + avatarView.image = avatar + cameraCircle.isHidden = true + return + } + + let defaultAvatar = OWSContactAvatarBuilder(forLocalUserWithDiameter: avatarSize).buildDefaultImage() + avatarView.image = defaultAvatar + cameraCircle.isHidden = false + } + +// // MARK: - Code State +// +// private let countdownDuration: TimeInterval = 60 +// private var codeCountdownTimer: Timer? +// private var codeCountdownStart: NSDate? +// +// deinit { +// if let codeCountdownTimer = codeCountdownTimer { +// codeCountdownTimer.invalidate() +// } +// } +// +// private func startCodeCountdown() { +// codeCountdownStart = NSDate() +// codeCountdownTimer = Timer.weakScheduledTimer(withTimeInterval: 1, target: self, selector: #selector(codeCountdownTimerFired), userInfo: nil, repeats: true) +// } +// +// @objc +// public func codeCountdownTimerFired() { +// guard let codeCountdownStart = codeCountdownStart else { +// owsFailDebug("Missing codeCountdownStart.") +// return +// } +// guard let codeCountdownTimer = codeCountdownTimer else { +// owsFailDebug("Missing codeCountdownTimer.") +// return +// } +// +// let countdownInterval = abs(codeCountdownStart.timeIntervalSinceNow) +// +// guard countdownInterval < countdownDuration else { +// // Countdown complete. +// codeCountdownTimer.invalidate() +// self.codeCountdownTimer = nil +// +// if codeState != .pending { +// owsFailDebug("Unexpected codeState: \(codeState)") +// } +// codeState = .possiblyNotDelivered +// updateCodeState() +// return +// } +// +// // Update the "code state" UI to reflect the countdown. +// updateCodeState() +// } +// +// private func updateCodeState() { +// AssertIsOnMainThread() +// +// guard let codeCountdownStart = codeCountdownStart else { +// owsFailDebug("Missing codeCountdownStart.") +// return +// } +// guard let titleLabel = titleLabel else { +// owsFailDebug("Missing titleLabel.") +// return +// } +// guard let codeStateLink = codeStateLink else { +// owsFailDebug("Missing codeStateLink.") +// return +// } +// +// var e164PhoneNumber = "" +// if let phoneNumber = onboardingController.phoneNumber { +// e164PhoneNumber = phoneNumber.e164 +// } +// let formattedPhoneNumber = PhoneNumber.bestEffortLocalizedPhoneNumber(withE164: e164PhoneNumber) +// +// // Update titleLabel +// switch codeState { +// case .pending, .possiblyNotDelivered: +// titleLabel.text = String(format: NSLocalizedString("ONBOARDING_VERIFICATION_TITLE_DEFAULT_FORMAT", +// comment: "Format for the title of the 'onboarding verification' view. Embeds {{the user's phone number}}."), +// formattedPhoneNumber) +// case .resent: +// titleLabel.text = String(format: NSLocalizedString("ONBOARDING_VERIFICATION_TITLE_RESENT_FORMAT", +// comment: "Format for the title of the 'onboarding verification' view after the verification code has been resent. Embeds {{the user's phone number}}."), +// formattedPhoneNumber) +// } +// +// // Update codeStateLink +// switch codeState { +// case .pending: +// let countdownInterval = abs(codeCountdownStart.timeIntervalSinceNow) +// let countdownRemaining = max(0, countdownDuration - countdownInterval) +// let formattedCountdown = OWSFormat.formatDurationSeconds(Int(round(countdownRemaining))) +// let text = String(format: NSLocalizedString("ONBOARDING_VERIFICATION_CODE_COUNTDOWN_FORMAT", +// comment: "Format for the label of the 'pending code' label of the 'onboarding verification' view. Embeds {{the time until the code can be resent}}."), +// formattedCountdown) +// codeStateLink.setTitle(title: text, font: .ows_dynamicTypeBodyClamped, titleColor: Theme.secondaryColor) +//// codeStateLink.setBackgroundColors(upColor: Theme.backgroundColor) +// case .possiblyNotDelivered: +// codeStateLink.setTitle(title: NSLocalizedString("ONBOARDING_VERIFICATION_ORIGINAL_CODE_MISSING_LINK", +// comment: "Label for link that can be used when the original code did not arrive."), +// font: .ows_dynamicTypeBodyClamped, +// titleColor: .ows_materialBlue) +// case .resent: +// codeStateLink.setTitle(title: NSLocalizedString("ONBOARDING_VERIFICATION_RESENT_CODE_MISSING_LINK", +// comment: "Label for link that can be used when the resent code did not arrive."), +// font: .ows_dynamicTypeBodyClamped, +// titleColor: .ows_materialBlue) +// } +// } + + public override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + _ = nameTextfield.becomeFirstResponder() + } + + // MARK: - Events + + @objc func avatarTapped(sender: UIGestureRecognizer) { + guard sender.state == .recognized else { + return + } + showAvatarActionSheet() + } + + @objc func nextPressed() { + Logger.info("") + + // TODO: +// parseAndTryToRegister() + } + + private func showAvatarActionSheet() { + AssertIsOnMainThread() + + Logger.info("") + + avatarViewHelper.showChangeAvatarUI() + +// let alert = UIAlertController(title: NSLocalizedString("CHECK_FOR_BACKUP_FAILED_TITLE", +// comment: "Title for alert shown when the app failed to check for an existing backup."), +// message: NSLocalizedString("CHECK_FOR_BACKUP_FAILED_MESSAGE", +// comment: "Message for alert shown when the app failed to check for an existing backup."), +// preferredStyle: .alert) +// alert.addAction(UIAlertAction(title: NSLocalizedString("REGISTER_FAILED_TRY_AGAIN", comment: ""), +// style: .default) { (_) in +// self.checkCanImportBackup(fromView: view) +// }) +// alert.addAction(UIAlertAction(title: NSLocalizedString("CHECK_FOR_BACKUP_DO_NOT_RESTORE", comment: "The label for the 'do not restore backup' button."), +// style: .destructive) { (_) in +// self.showProfileView(fromView: view) +// }) +// view.present(alert, animated: true) + } + + // @objc func backLinkTapped() { +// Logger.info("") +// +// self.navigationController?.popViewController(animated: true) +// } +// +// @objc func resendCodeLinkTapped() { +// Logger.info("") +// +// switch codeState { +// case .pending: +// // Ignore taps until the countdown expires. +// break +// case .possiblyNotDelivered, .resent: +// showResendActionSheet() +// } +// } +// +// private func showResendActionSheet() { +// Logger.info("") +// +// let actionSheet = UIAlertController(title: NSLocalizedString("ONBOARDING_VERIFICATION_RESEND_CODE_ALERT_TITLE", +// comment: "Title for the 'resend code' alert in the 'onboarding verification' view."), +// message: NSLocalizedString("ONBOARDING_VERIFICATION_RESEND_CODE_ALERT_MESSAGE", +// comment: "Message for the 'resend code' alert in the 'onboarding verification' view."), +// preferredStyle: .actionSheet) +// +// actionSheet.addAction(UIAlertAction(title: NSLocalizedString("ONBOARDING_VERIFICATION_RESEND_CODE_BY_SMS_BUTTON", +// comment: "Label for the 'resend code by SMS' button in the 'onboarding verification' view."), +// style: .default) { _ in +// self.onboardingController.tryToRegister(fromViewController: self, smsVerification: true) +// }) +// actionSheet.addAction(UIAlertAction(title: NSLocalizedString("ONBOARDING_VERIFICATION_RESEND_CODE_BY_VOICE_BUTTON", +// comment: "Label for the 'resend code by voice' button in the 'onboarding verification' view."), +// style: .default) { _ in +// self.onboardingController.tryToRegister(fromViewController: self, smsVerification: false) +// }) +// actionSheet.addAction(OWSAlerts.cancelAction) +// +// self.present(actionSheet, animated: true) +// } +// +// private func tryToVerify() { +// Logger.info("") +// +// guard onboardingCodeView.isComplete else { +// return +// } +// +// setHasInvalidCode(false) +// +// onboardingController.tryToVerify(fromViewController: self, verificationCode: onboardingCodeView.verificationCode, pin: nil, isInvalidCodeCallback: { +// self.setHasInvalidCode(true) +// }) +// } +// +// private func setHasInvalidCode(_ value: Bool) { +// onboardingCodeView.setHasError(value) +// errorLabel.isHidden = !value +// } +//} +// +//// MARK: - +// +//extension OnboardingProfileViewController: OnboardingCodeViewDelegate { +// public func codeViewDidChange() { +// AssertIsOnMainThread() +// +// setHasInvalidCode(false) +// +// tryToVerify() +// } +} + +// MARK: - + +extension OnboardingProfileViewController: UITextFieldDelegate { + public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + // // TODO: Fix auto-format of phone numbers. + // ViewControllerUtils.phoneNumber(textField, shouldChangeCharactersIn: range, replacementString: string, countryCode: countryCode) + // + // isPhoneNumberInvalid = false + // updateValidationWarnings() + + // Inform our caller that we took care of performing the change. + return true + } + + public func textFieldShouldReturn(_ textField: UITextField) -> Bool { + // parseAndTryToRegister() + return false + } +} + +// MARK: - + +extension OnboardingProfileViewController: AvatarViewHelperDelegate { + public func avatarActionSheetTitle() -> String? { + return nil + } + + public func avatarDidChange(_ image: UIImage) { + AssertIsOnMainThread() + + let maxDiameter = CGFloat(kOWSProfileManager_MaxAvatarDiameter) + avatar = image.resizedImage(toFillPixelSize: CGSize(width: maxDiameter, + height: maxDiameter)) + + updateAvatarView() + } + + public func fromViewController() -> UIViewController { + return self + } + + public func hasClearAvatarAction() -> Bool { + return avatar != nil + } + + public func clearAvatar() { + avatar = nil + + updateAvatarView() + } + + public func clearAvatarActionLabel() -> String { + return NSLocalizedString("PROFILE_VIEW_CLEAR_AVATAR", comment: "Label for action that clear's the user's profile avatar") + } +} diff --git a/Signal/src/ViewControllers/Registration/OnboardingVerificationViewController.swift b/Signal/src/ViewControllers/Registration/OnboardingVerificationViewController.swift index a19f2227e..48cb07bba 100644 --- a/Signal/src/ViewControllers/Registration/OnboardingVerificationViewController.swift +++ b/Signal/src/ViewControllers/Registration/OnboardingVerificationViewController.swift @@ -133,14 +133,8 @@ private class OnboardingCodeView: UIView { digitView.addSubview(digitLabel) digitLabel.autoCenterInSuperview() - let strokeView = UIView.container() - if hasStroke { - strokeView.backgroundColor = Theme.primaryColor - digitView.addSubview(strokeView) - strokeView.autoPinWidthToSuperview() - strokeView.autoPinEdge(toSuperviewEdge: .bottom) - strokeView.autoSetDimension(.height, toSize: 1) - } + let strokeColor = (hasStroke ? Theme.primaryColor : UIColor.clear) + let strokeView = digitView.addBottomStroke(color: strokeColor, strokeWidth: 1) let vMargin: CGFloat = 4 let cellHeight: CGFloat = digitLabel.font.lineHeight + vMargin * 2 diff --git a/Signal/src/ViewControllers/ThreadSettings/UpdateGroupViewController.m b/Signal/src/ViewControllers/ThreadSettings/UpdateGroupViewController.m index 1820ad17c..1baa19bec 100644 --- a/Signal/src/ViewControllers/ThreadSettings/UpdateGroupViewController.m +++ b/Signal/src/ViewControllers/ThreadSettings/UpdateGroupViewController.m @@ -485,7 +485,7 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - AvatarViewHelperDelegate -- (NSString *)avatarActionSheetTitle +- (nullable NSString *)avatarActionSheetTitle { return NSLocalizedString( @"NEW_GROUP_ADD_PHOTO_ACTION", @"Action Sheet title prompting the user for a group avatar"); diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 5bd79ce6d..f61ad1a2c 100644 --- a/Signal/translations/en.lproj/Localizable.strings +++ b/Signal/translations/en.lproj/Localizable.strings @@ -1529,6 +1529,15 @@ /* Label indicating that the phone number is invalid in the 'onboarding phone number' view. */ "ONBOARDING_PHONE_NUMBER_VALIDATION_WARNING" = "Invalid number"; +/* Explanation in the 'onboarding profile' view. */ +"ONBOARDING_PROFILE_EXPLANATION" = "Signal profiles are end-to-end encrypted and the Signal service never has access to this information."; + +/* Placeholder text for the profile name in the 'onboarding profile' view. */ +"ONBOARDING_PROFILE_NAME_PLACEHOLDER" = "Your Name"; + +/* Title of the 'onboarding profile' view. */ +"ONBOARDING_PROFILE_TITLE" = "Set up your profile"; + /* Link to the 'terms and privacy policy' in the 'onboarding splash' view. */ "ONBOARDING_SPLASH_TERM_AND_PRIVACY_POLICY" = "Terms & Privacy Policy";