Skip to content

Commit fc794dd

Browse files
authored
[Gravatar] - Add Gravatar info UI in user profile settings (#22563)
* Add Gravatar info cell * Update font * Disable drag interaction * Update spacing * Update link color * Remove cardView * Cleanup * Replace some logos * Add notice text * Add notice message * Revert Localizable.strings * Remove Copyright comments * Add an extra check to detect if the value has changed on the multiline text * Revert irrelevant project file changes * Add "adjustsFontForContentSizeCategory = true" and make the font calculated value in GravatarRowInfo * Remove isUserInteractionEnabled * Replace zPosition setting with bringSubviewToFront * Fix typo (Rename GravatarInterceptingLogosView as GravatarIntersectingLogosView) * Revert irrelevant project file changes (Every time I rename a file I get these changes I don't know why) --------- Co-authored-by: Pinar Olguc <pinar.olguc@automattic.com>
1 parent 5fba062 commit fc794dd

13 files changed

Lines changed: 261 additions & 6 deletions

File tree

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import Foundation
2+
import WordPressShared
3+
import DesignSystem
4+
5+
struct GravatarInfoRow: ImmuTableRow {
6+
var action: ImmuTableAction?
7+
8+
func configureCell(_ cell: UITableViewCell) {
9+
guard let cell = cell as? GravatarInfoCell else { return }
10+
cell.applyColors()
11+
cell.selectionStyle = .none
12+
}
13+
14+
static let cell = ImmuTableCell.class(GravatarInfoCell.self)
15+
}
16+
17+
class GravatarInfoCell: WPTableViewCellDefault {
18+
19+
private enum Constants {
20+
static let externalLinkLogo: UIImage? = UIImage(named: "icon-post-actionbar-view")?.withRenderingMode(.alwaysTemplate)
21+
static let infoText = NSLocalizedString("Gravatar keeps your profile information safe and up to date, automatically syncing any updates made here with your Gravatar profile.", comment: "This text is shown in the profile editing page to let the user know about Gravatar.")
22+
static let linkText = NSLocalizedString("Learn more on Gravatar.com", comment: "This is a link that takes the user to the external Gravatar website")
23+
static var font: UIFont { TextStyle.caption.uiFont }
24+
static let gravatarLink = "https://gravatar.com"
25+
}
26+
27+
private var infoLabel: UILabel = {
28+
let label = UILabel()
29+
label.translatesAutoresizingMaskIntoConstraints = false
30+
label.font = Constants.font
31+
label.text = Constants.infoText
32+
label.adjustsFontForContentSizeCategory = true
33+
label.numberOfLines = 0
34+
return label
35+
}()
36+
37+
private var linkTextView: UITextView = {
38+
let text = UITextView()
39+
text.translatesAutoresizingMaskIntoConstraints = false
40+
text.isEditable = false
41+
text.isScrollEnabled = false
42+
text.isSelectable = true
43+
text.backgroundColor = .clear
44+
text.textContainerInset = .zero
45+
text.textContainer.lineFragmentPadding = 0
46+
text.textDragInteraction?.isEnabled = false
47+
text.adjustsFontForContentSizeCategory = true
48+
return text
49+
}()
50+
51+
private var logosView: GravatarIntersectingLogosView = {
52+
let view = GravatarIntersectingLogosView(frame: .zero)
53+
view.translatesAutoresizingMaskIntoConstraints = false
54+
return view
55+
}()
56+
57+
private lazy var verticalStackView: UIStackView = {
58+
let stackView = UIStackView(arrangedSubviews: [logosView, infoLabel, linkTextView])
59+
stackView.translatesAutoresizingMaskIntoConstraints = false
60+
stackView.axis = .vertical
61+
stackView.spacing = Length.Padding.split
62+
stackView.alignment = .leading
63+
return stackView
64+
}()
65+
66+
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
67+
super.init(style: style, reuseIdentifier: reuseIdentifier)
68+
contentView.addSubview(verticalStackView)
69+
NSLayoutConstraint.activate([
70+
verticalStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: Length.Padding.double),
71+
verticalStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -Length.Padding.double),
72+
verticalStackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: Length.Padding.double),
73+
verticalStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -Length.Padding.double),
74+
])
75+
applyColors()
76+
}
77+
78+
required init?(coder aDecoder: NSCoder) {
79+
fatalError("init(coder:) has not been implemented")
80+
}
81+
82+
func applyColors() {
83+
infoLabel.textColor = UIColor.DS.Foreground.primary
84+
linkTextView.tintColor = UIColor.primary
85+
86+
let infoText = NSMutableAttributedString(string: Constants.linkText,
87+
attributes: [.underlineStyle: NSUnderlineStyle.single.rawValue,
88+
.font: Constants.font])
89+
if let linkImage = Constants.externalLinkLogo {
90+
let imageAttachment = NSTextAttachment()
91+
imageAttachment.image = linkImage
92+
imageAttachment.bounds = CGRect(x: 0, y: (Constants.font.capHeight - linkImage.size.height).rounded() / 2, width: linkImage.size.width, height: linkImage.size.height)
93+
94+
let imageString = NSAttributedString(attachment: imageAttachment)
95+
infoText.append(NSAttributedString(string: " "))
96+
infoText.append(imageString)
97+
}
98+
99+
infoText.addAttributes([.link: Constants.gravatarLink], range: NSRange(location: 0, length: infoText.length))
100+
linkTextView.attributedText = infoText
101+
}
102+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import Foundation
2+
3+
class GravatarIntersectingLogosView: UIView {
4+
5+
private enum Constants {
6+
static let wordpressLogo = UIImage(named: "wordpress-circular")
7+
static let jetpackLogo = UIImage(named: "jetpack-circular")
8+
static let gravatarLogo = UIImage(named: "gravatar-circular")
9+
static let intersectionAmount: CGFloat = 12.0
10+
static let logoSize: CGSize = .init(width: 30, height: 30)
11+
}
12+
13+
private lazy var appLogoImageView: UIImageView = {
14+
let imageView = UIImageView()
15+
imageView.translatesAutoresizingMaskIntoConstraints = false
16+
imageView.image = appLogo
17+
imageView.contentMode = .scaleAspectFit
18+
imageView.widthAnchor.constraint(equalToConstant: Constants.logoSize.width).isActive = true
19+
imageView.heightAnchor.constraint(equalToConstant: Constants.logoSize.height).isActive = true
20+
return imageView
21+
}()
22+
23+
private lazy var gravatarLogoImageView: UIImageView = {
24+
let imageView = UIImageView()
25+
imageView.translatesAutoresizingMaskIntoConstraints = false
26+
imageView.image = Constants.gravatarLogo
27+
imageView.contentMode = .scaleAspectFit
28+
imageView.widthAnchor.constraint(equalToConstant: Constants.logoSize.width).isActive = true
29+
imageView.heightAnchor.constraint(equalToConstant: Constants.logoSize.height).isActive = true
30+
return imageView
31+
}()
32+
33+
private var appLogo: UIImage? {
34+
return AppConfiguration.isJetpack ? Constants.jetpackLogo : Constants.wordpressLogo
35+
}
36+
37+
override init(frame: CGRect) {
38+
super.init(frame: frame)
39+
addSubview(appLogoImageView)
40+
addSubview(gravatarLogoImageView)
41+
bringSubviewToFront(gravatarLogoImageView)
42+
NSLayoutConstraint.activate([
43+
appLogoImageView.leadingAnchor.constraint(equalTo: leadingAnchor),
44+
appLogoImageView.topAnchor.constraint(equalTo: topAnchor),
45+
appLogoImageView.bottomAnchor.constraint(equalTo: bottomAnchor),
46+
gravatarLogoImageView.leadingAnchor.constraint(equalTo: appLogoImageView.trailingAnchor, constant: -Constants.intersectionAmount),
47+
gravatarLogoImageView.topAnchor.constraint(equalTo: topAnchor),
48+
gravatarLogoImageView.bottomAnchor.constraint(equalTo: bottomAnchor),
49+
gravatarLogoImageView.trailingAnchor.constraint(equalTo: trailingAnchor)
50+
])
51+
}
52+
53+
required init?(coder: NSCoder) {
54+
fatalError("init(coder:) has not been implemented")
55+
}
56+
}

WordPress/Classes/ViewRelated/Me/My Profile/MyProfileViewController.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ private class MyProfileController: SettingsController {
7272
let title = NSLocalizedString("My Profile", comment: "My Profile view title")
7373

7474
var immuTableRows: [ImmuTableRow.Type] {
75-
return [EditableTextRow.self]
75+
return [EditableTextRow.self,
76+
GravatarInfoRow.self]
7677
}
7778

7879
// MARK: - Initialization
@@ -146,13 +147,16 @@ private class MyProfileController: SettingsController {
146147
service: service)),
147148
fieldName: "about_me")
148149

150+
let gravatarInfoRow = GravatarInfoRow()
151+
149152
return ImmuTable(sections: [
150153
ImmuTableSection(rows: [
151154
firstNameRow,
152155
lastNameRow,
153156
displayNameRow,
154157
aboutMeRow
155-
])
158+
]),
159+
ImmuTableSection(rows: [gravatarInfoRow])
156160
])
157161
}
158162

WordPress/Classes/ViewRelated/Tools/SettingsCommon.swift

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import UIKit
2+
import WordPressFlux
23
import WordPressKit
34
import CocoaLumberjack
45

@@ -56,6 +57,9 @@ extension SettingsController {
5657

5758
let change = changeType(value)
5859
service.saveChange(change)
60+
if change.isGravatarField {
61+
sendGravatarSyncNotice()
62+
}
5963
DDLogDebug("\(title) changed: \(value)")
6064

6165
trackChangeIfNeeded(row)
@@ -75,11 +79,14 @@ extension SettingsController {
7579

7680
controller.title = title
7781
controller.onValueChanged = {
78-
value in
79-
80-
let change = changeType(value)
82+
newValue in
83+
guard value != newValue else { return }
84+
let change = changeType(newValue)
8185
service.saveChange(change)
82-
DDLogDebug("\(title) changed: \(value)")
86+
if change.isGravatarField {
87+
sendGravatarSyncNotice()
88+
}
89+
DDLogDebug("\(title) changed: \(newValue)")
8390

8491
trackChangeIfNeeded(row)
8592
}
@@ -95,4 +102,22 @@ extension SettingsController {
95102

96103
WPAnalytics.trackSettingsChange(trackingKey, fieldName: fieldName)
97104
}
105+
106+
private func sendGravatarSyncNotice() {
107+
let noticeMessage = NSLocalizedString("Updates might take some time to sync with your Gravatar profile.", comment: "A notice message that's shown after the user updates their profile information.")
108+
ActionDispatcher.dispatch(NoticeAction.post(Notice(title: noticeMessage)))
109+
}
110+
}
111+
112+
extension AccountSettingsChange {
113+
114+
// These are the fields that are in sync with user's Gravatar account
115+
var isGravatarField: Bool {
116+
switch self {
117+
case .firstName, .lastName, .aboutMe, .displayName:
118+
return true
119+
default:
120+
return false
121+
}
122+
}
98123
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "jetpack-circular.pdf",
5+
"idiom" : "universal"
6+
}
7+
],
8+
"info" : {
9+
"author" : "xcode",
10+
"version" : 1
11+
}
12+
}
Binary file not shown.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "gravatar-logo.pdf",
5+
"idiom" : "universal"
6+
}
7+
],
8+
"info" : {
9+
"author" : "xcode",
10+
"version" : 1
11+
}
12+
}
Binary file not shown.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "wordpress-logo-light 1.pdf",
5+
"idiom" : "universal"
6+
},
7+
{
8+
"appearances" : [
9+
{
10+
"appearance" : "luminosity",
11+
"value" : "light"
12+
}
13+
],
14+
"filename" : "wordpress-logo-light.pdf",
15+
"idiom" : "universal"
16+
},
17+
{
18+
"appearances" : [
19+
{
20+
"appearance" : "luminosity",
21+
"value" : "dark"
22+
}
23+
],
24+
"filename" : "wordpress-logo-darkpdf.pdf",
25+
"idiom" : "universal"
26+
}
27+
],
28+
"info" : {
29+
"author" : "xcode",
30+
"version" : 1
31+
}
32+
}
Binary file not shown.

0 commit comments

Comments
 (0)