Implement profile updating redesign

pull/68/head
Niels Andriesse 5 years ago
parent 83fe454b07
commit 7ea5e5bd46

@ -1,7 +1,9 @@
final class TextField : UITextField {
private let usesDefaultHeight: Bool
init(placeholder: String) {
init(placeholder: String, usesDefaultHeight: Bool = true) {
self.usesDefaultHeight = usesDefaultHeight
super.init(frame: CGRect.zero)
self.placeholder = placeholder
setUpStyle()
@ -24,17 +26,27 @@ final class TextField : UITextField {
attributedPlaceholder = placeholder
tintColor = Colors.accent
keyboardAppearance = .dark
set(.height, to: Values.textFieldHeight)
if usesDefaultHeight {
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)
if usesDefaultHeight {
return bounds.insetBy(dx: Values.largeSpacing, dy: Values.largeSpacing)
} else {
return bounds.insetBy(dx: Values.mediumSpacing, dy: Values.smallSpacing)
}
}
override func editingRect(forBounds bounds: CGRect) -> CGRect {
return bounds.insetBy(dx: Values.largeSpacing, dy: Values.largeSpacing)
if usesDefaultHeight {
return bounds.insetBy(dx: Values.largeSpacing, dy: Values.largeSpacing)
} else {
return bounds.insetBy(dx: Values.mediumSpacing, dy: Values.smallSpacing)
}
}
}

@ -1,5 +1,8 @@
final class SettingsVC : UIViewController {
final class SettingsVC : UIViewController, AvatarViewHelperDelegate {
private var profilePictureToBeUploaded: UIImage?
private var displayNameToBeUploaded: String?
private var isEditingDisplayName = false { didSet { handleIsEditingDisplayNameChanged() } }
private lazy var userHexEncodedPublicKey: String = {
if let masterHexEncodedPublicKey = UserDefaults.standard.string(forKey: "masterDeviceHexEncodedPublicKey") {
@ -22,11 +25,24 @@ final class SettingsVC : UIViewController {
return result
}()
private lazy var profilePictureUtilities: AvatarViewHelper = {
let result = AvatarViewHelper()
result.delegate = self
return result
}()
private lazy var displayNameLabel: UILabel = {
let result = UILabel()
result.textColor = Colors.text
result.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
result.lineBreakMode = .byTruncatingTail
result.textAlignment = .center
return result
}()
private lazy var displayNameTextField: TextField = {
let result = TextField(placeholder: NSLocalizedString("Enter a display name", comment: ""), usesDefaultHeight: false)
result.textAlignment = .center
return result
}()
@ -50,15 +66,10 @@ final class SettingsVC : UIViewController {
navigationBar.isTranslucent = false
navigationBar.barTintColor = Colors.navigationBarBackground
// Set up navigation bar buttons
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
closeButton.tintColor = Colors.text
navigationItem.leftBarButtonItem = closeButton
let backButton = UIBarButtonItem(title: NSLocalizedString("Back", comment: ""), style: .plain, target: nil, action: nil)
backButton.tintColor = Colors.text
navigationItem.backBarButtonItem = backButton
let qrCodeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "QRCodeFilled"), style: .plain, target: self, action: #selector(showQRCode))
qrCodeButton.tintColor = Colors.text
navigationItem.rightBarButtonItem = qrCodeButton
updateNavigationBarButtons()
// Customize title
let titleLabel = UILabel()
titleLabel.text = NSLocalizedString("Settings", comment: "")
@ -66,12 +77,24 @@ final class SettingsVC : UIViewController {
titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
navigationItem.titleView = titleLabel
// Set up profile picture view
let profilePictureTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(showEditProfilePictureUI))
profilePictureView.addGestureRecognizer(profilePictureTapGestureRecognizer)
profilePictureView.hexEncodedPublicKey = userHexEncodedPublicKey
profilePictureView.update()
// Set up display name label
displayNameLabel.text = OWSProfileManager.shared().profileName(forRecipientId: userHexEncodedPublicKey)
// Set up display name container
let displayNameContainer = UIView()
displayNameContainer.addSubview(displayNameLabel)
displayNameLabel.pin(to: displayNameContainer)
displayNameContainer.addSubview(displayNameTextField)
displayNameTextField.pin(to: displayNameContainer)
displayNameContainer.set(.height, to: 40)
displayNameTextField.alpha = 0
let displayNameLabelTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(showEditDisplayNameUI))
displayNameContainer.addGestureRecognizer(displayNameLabelTapGestureRecognizer)
// Set up header view
let headerStackView = UIStackView(arrangedSubviews: [ profilePictureView, displayNameLabel ])
let headerStackView = UIStackView(arrangedSubviews: [ profilePictureView, displayNameContainer ])
headerStackView.axis = .vertical
headerStackView.spacing = Values.smallSpacing
headerStackView.alignment = .center
@ -164,12 +187,6 @@ final class SettingsVC : UIViewController {
return result
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
profilePictureView.update()
displayNameLabel.text = OWSProfileManager.shared().profileName(forRecipientId: userHexEncodedPublicKey)
}
// MARK: General
@objc private func enableCopyButton() {
copyButton.isUserInteractionEnabled = true
@ -178,6 +195,91 @@ final class SettingsVC : UIViewController {
}, completion: nil)
}
func avatarActionSheetTitle() -> String? {
return NSLocalizedString("Update Profile Picture", comment: "")
}
func fromViewController() -> UIViewController {
return self
}
func hasClearAvatarAction() -> Bool {
return true
}
func clearAvatarActionLabel() -> String {
return NSLocalizedString("Clear", comment: "")
}
// MARK: Updating
private func handleIsEditingDisplayNameChanged() {
updateNavigationBarButtons()
UIView.animate(withDuration: 0.25) {
self.displayNameLabel.alpha = self.isEditingDisplayName ? 0 : 1
self.displayNameTextField.alpha = self.isEditingDisplayName ? 1 : 0
}
if isEditingDisplayName {
displayNameTextField.becomeFirstResponder()
} else {
displayNameTextField.resignFirstResponder()
}
}
private func updateNavigationBarButtons() {
if isEditingDisplayName {
let cancelButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(handleCancelDisplayNameEditingButtonTapped))
cancelButton.tintColor = Colors.text
navigationItem.leftBarButtonItem = cancelButton
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handleSaveDisplayNameButtonTapped))
doneButton.tintColor = Colors.text
navigationItem.rightBarButtonItem = doneButton
} else {
let closeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "X"), style: .plain, target: self, action: #selector(close))
closeButton.tintColor = Colors.text
navigationItem.leftBarButtonItem = closeButton
let qrCodeButton = UIBarButtonItem(image: #imageLiteral(resourceName: "QRCodeFilled"), style: .plain, target: self, action: #selector(showQRCode))
qrCodeButton.tintColor = Colors.text
navigationItem.rightBarButtonItem = qrCodeButton
}
}
func avatarDidChange(_ image: UIImage) {
let maxSize = Int(kOWSProfileManager_MaxAvatarDiameter)
profilePictureToBeUploaded = image.resizedImage(toFillPixelSize: CGSize(width: maxSize, height: maxSize))
updateProfile(isUpdatingDisplayName: false, isUpdatingProfilePicture: true)
}
func clearAvatar() {
profilePictureToBeUploaded = nil
updateProfile(isUpdatingDisplayName: false, isUpdatingProfilePicture: true)
}
private func updateProfile(isUpdatingDisplayName: Bool, isUpdatingProfilePicture: Bool) {
let displayName = displayNameToBeUploaded ?? OWSProfileManager.shared().profileName(forRecipientId: userHexEncodedPublicKey)
let profilePicture = profilePictureToBeUploaded ?? OWSProfileManager.shared().profileAvatar(forRecipientId: userHexEncodedPublicKey)
ModalActivityIndicatorViewController.present(fromViewController: navigationController!, canCancel: false) { [weak self] modalActivityIndicator in
OWSProfileManager.shared().updateLocalProfileName(displayName, avatarImage: profilePicture, success: {
DispatchQueue.main.async {
modalActivityIndicator.dismiss {
guard let self = self else { return }
self.profilePictureView.update()
self.displayNameLabel.text = displayName
self.profilePictureToBeUploaded = nil
self.displayNameToBeUploaded = nil
}
}
}, failure: {
DispatchQueue.main.async {
modalActivityIndicator.dismiss {
let alert = UIAlertController(title: NSLocalizedString("Couldn't Update Profile", comment: ""), message: NSLocalizedString("Please check your internet connection and try again", comment: ""), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil)
}
}
})
}
}
// MARK: Interaction
@objc private func close() {
dismiss(animated: true, completion: nil)
@ -188,6 +290,41 @@ final class SettingsVC : UIViewController {
navigationController!.pushViewController(qrCodeVC, animated: true)
}
@objc private func handleCancelDisplayNameEditingButtonTapped() {
isEditingDisplayName = false
}
@objc private func handleSaveDisplayNameButtonTapped() {
func showError(title: String, message: String = "") {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
presentAlert(alert)
}
let displayName = displayNameTextField.text!.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
guard !displayName.isEmpty else {
return showError(title: NSLocalizedString("Please pick a display name", comment: ""))
}
let allowedCharacters = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_ ")
let hasInvalidCharacters = !displayName.allSatisfy { $0.unicodeScalars.allSatisfy { allowedCharacters.contains($0) } }
guard !hasInvalidCharacters else {
return showError(title: NSLocalizedString("Please pick a display name that consists of only a-z, A-Z, 0-9 and _ characters", comment: ""))
}
guard !OWSProfileManager.shared().isProfileNameTooLong(displayName) else {
return showError(title: NSLocalizedString("Please pick a shorter display name", comment: ""))
}
isEditingDisplayName = false
displayNameToBeUploaded = displayName
updateProfile(isUpdatingDisplayName: true, isUpdatingProfilePicture: false)
}
@objc private func showEditProfilePictureUI() {
profilePictureUtilities.showChangeAvatarUI()
}
@objc private func showEditDisplayNameUI() {
isEditingDisplayName = true
}
@objc private func copyPublicKey() {
UIPasteboard.general.string = userHexEncodedPublicKey
copyButton.isUserInteractionEnabled = false

@ -146,10 +146,10 @@ import SignalMessaging
private func createViews() {
view.backgroundColor = UIColor.black
view.backgroundColor = .black
let contentView = UIView()
contentView.backgroundColor = UIColor.black
contentView.backgroundColor = .black
self.view.addSubview(contentView)
contentView.autoPinEdgesToSuperviewEdges()
@ -186,14 +186,14 @@ import SignalMessaging
layer.path = path.cgPath
layer.fillRule = .evenOdd
layer.fillColor = UIColor.black.cgColor
layer.opacity = 0.7
layer.opacity = 0.75
}
maskingView.autoPinEdgesToSuperviewEdges()
let titleLabel = UILabel()
titleLabel.textColor = UIColor.white
titleLabel.textColor = Colors.text
titleLabel.textAlignment = .center
titleLabel.font = UIFont.ows_mediumFont(withSize: ScaleFromIPhone5(16))
titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
titleLabel.text = NSLocalizedString("CROP_SCALE_IMAGE_VIEW_TITLE",
comment: "Title for the 'crop/scale image' dialog.")
contentView.addSubview(titleLabel)
@ -439,6 +439,7 @@ import SignalMessaging
let cancelButton = createButton(title: CommonStrings.cancelButton,
action: #selector(cancelPressed))
cancelButton.titleLabel!.font = .systemFont(ofSize: 18) // Match iOS UI
buttonRow.addSubview(cancelButton)
cancelButton.autoPinEdge(toSuperviewEdge: .top)
cancelButton.autoPinEdge(toSuperviewEdge: .bottom)
@ -446,6 +447,7 @@ import SignalMessaging
let doneButton = createButton(title: CommonStrings.doneButton,
action: #selector(donePressed))
doneButton.titleLabel!.font = .systemFont(ofSize: 18) // Match iOS UI
buttonRow.addSubview(doneButton)
doneButton.autoPinEdge(toSuperviewEdge: .top)
doneButton.autoPinEdge(toSuperviewEdge: .bottom)

@ -2720,3 +2720,7 @@
"Unlock Loki Messenger's screen using Touch ID, Face ID, or your iOS device passcode. You can still receive message notifications while Screen Lock is enabled. Loki Messenger's notification settings allow you to customize the information that is displayed." = "Unlock Loki Messenger's screen using Touch ID, Face ID, or your iOS device passcode. You can still receive message notifications while Screen Lock is enabled. Loki Messenger's notification settings allow you to customize the information that is displayed.";
"Sound" = "Sound";
"Content" = "Content";
"Update Profile Picture" = "Update Profile Picture";
"Couldn't Update Profile Picture" = "Couldn't Update Profile Picture";
"Clear" = "Clear";
"Enter a display name" = "Enter a display name";

@ -27,12 +27,12 @@
+ (void)setupSignalAppearence
{
UINavigationBar.appearance.barTintColor = Theme.navbarBackgroundColor;
UINavigationBar.appearance.tintColor = Theme.navbarIconColor;
UINavigationBar.appearance.barTintColor = [UIColor colorWithRGBHex:0x161616]; // Colors.navigationBarBackground
UINavigationBar.appearance.tintColor = [UIColor colorWithRGBHex:0xFFFFFF]; // Colors.text
UIToolbar.appearance.barTintColor = Theme.navbarBackgroundColor;
UIToolbar.appearance.tintColor = Theme.navbarIconColor;
UIBarButtonItem.appearance.tintColor = Theme.navbarIconColor;
UIBarButtonItem.appearance.tintColor = [UIColor colorWithRGBHex:0xFFFFFF]; // Colors.text
// Using the keyboardAppearance causes crashes due to a bug in UIKit.
// UITextField.appearance.keyboardAppearance = (Theme.isDarkThemeEnabled
@ -42,11 +42,11 @@
// ? UIKeyboardAppearanceDark
// : UIKeyboardAppearanceDefault);
[[UISwitch appearance] setOnTintColor:[UIColor ows_materialBlueColor]];
[[UISwitch appearance] setOnTintColor:[UIColor colorWithRGBHex:0x00F782]]; // Colors.accent
[[UIToolbar appearance] setTintColor:[UIColor ows_materialBlueColor]];
// If we set NSShadowAttributeName, the NSForegroundColorAttributeName value is ignored.
UINavigationBar.appearance.titleTextAttributes = @{ NSForegroundColorAttributeName : Theme.navbarTitleColor };
UINavigationBar.appearance.titleTextAttributes = @{ NSForegroundColorAttributeName : [UIColor colorWithRGBHex:0xFFFFFF] }; // Colors.text
}
@end

Loading…
Cancel
Save