Skip to content

Commit 4416fb9

Browse files
authored
Merge pull request #19264 from wordpress-mobile/task/jetpack-landing-screen-animation
New Jetpack prologue screen - prompts with animation
2 parents 064eb1b + bbe8d15 commit 4416fb9

File tree

14 files changed

+482
-34
lines changed

14 files changed

+482
-34
lines changed

WordPress/Classes/ViewRelated/Feature Highlight/Tooltip.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ final class Tooltip: UIView {
397397
}
398398
}
399399

400-
private extension String {
400+
extension String {
401401
private func size(withMaxWidth maxWidth: CGFloat, font: UIFont) -> CGRect {
402402
let constraintRect = CGSize(width: maxWidth, height: .greatestFiniteMagnitude)
403403
let boundingBox = self.boundingRect(

WordPress/Jetpack/Classes/JetpackAuthenticationManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import WordPressAuthenticator
22

33
struct JetpackAuthenticationManager: AuthenticationHandler {
4-
var statusBarStyle: UIStatusBarStyle = .lightContent
4+
var statusBarStyle: UIStatusBarStyle = FeatureFlag.newLandingScreen.enabled ? .default : .lightContent
55
var prologueViewController: UIViewController? = JetpackPrologueViewController()
66
var buttonViewTopShadowImage: UIImage? = UIImage()
77
var prologueButtonsBackgroundColor: UIColor? = JetpackPrologueStyleGuide.backgroundColor

WordPress/Jetpack/Classes/NUX/JetpackPrologueStyleGuide.swift

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,45 @@ import WordPressAuthenticator
55
/// The colors in here intentionally do not support light or dark modes since they're the same on both.
66
///
77
struct JetpackPrologueStyleGuide {
8-
static let backgroundColor = UIColor(red: 0.00, green: 0.11, blue: 0.18, alpha: 1.00)
8+
// Background colors
9+
// old
10+
static let oldBackgroundColor = UIColor(red: 0.00, green: 0.11, blue: 0.18, alpha: 1.00)
11+
// new
12+
static let newBackgroundColor = UIColor(light: .muriel(color: .jetpackGreen, .shade5), dark: .muriel(color: .jetpackGreen, .shade100))
13+
// combined
14+
static let backgroundColor = FeatureFlag.newLandingScreen.enabled ? newBackgroundColor : oldBackgroundColor
15+
16+
// Continue with WordPress button colors
17+
// old
18+
static let oldContinueHighlightedFillColor = UIColor(red: 1.00, green: 1.00, blue: 1.00, alpha: 0.90)
19+
// new
20+
static let newContinueFillColor = UIColor.muriel(color: .jetpackGreen, .shade50)
21+
static let newContinueHighlightedFillColor = UIColor.muriel(color: .jetpackGreen, .shade90)
22+
23+
// combined
24+
static let continueFillColor = FeatureFlag.newLandingScreen.enabled ? newContinueFillColor : .white
25+
static let continueHighlightedFillColor = FeatureFlag.newLandingScreen.enabled ? newContinueHighlightedFillColor : oldContinueHighlightedFillColor
26+
static let continueTextColor = FeatureFlag.newLandingScreen.enabled ? .white : oldBackgroundColor
27+
static let continueHighlightedTextColor = FeatureFlag.newLandingScreen.enabled ? whiteWithAlpha07 : oldBackgroundColor
28+
29+
30+
// Enter your site address button
31+
// old
32+
static let oldSiteBorderColor = UIColor(red: 1.00, green: 1.00, blue: 1.00, alpha: 0.40)
33+
static let oldSiteHighlightedBorderColor = UIColor(red: 1.00, green: 1.00, blue: 1.00, alpha: 0.20)
34+
// combined
35+
static let siteFillColor = FeatureFlag.newLandingScreen.enabled ? .white : oldBackgroundColor
36+
static let siteBorderColor = FeatureFlag.newLandingScreen.enabled ? .white : oldSiteBorderColor
37+
static let siteTextColor = FeatureFlag.newLandingScreen.enabled ? UIColor.black : UIColor.white
38+
static let siteHighlightedFillColor = FeatureFlag.newLandingScreen.enabled ? whiteWithAlpha07 : oldBackgroundColor
39+
static let siteHighlightedBorderColor = FeatureFlag.newLandingScreen.enabled ? whiteWithAlpha07 : oldSiteHighlightedBorderColor
40+
static let siteHighlightedTextColor = FeatureFlag.newLandingScreen.enabled ? .black : whiteWithAlpha07
41+
42+
// Color used in both old and versions
43+
static let whiteWithAlpha07 = UIColor.white.withAlphaComponent(0.7)
44+
45+
46+
947

1048
struct Title {
1149
static let font: UIFont = WPStyleGuide.fontForTextStyle(.title3, fontWeight: .semibold)
@@ -22,28 +60,27 @@ struct JetpackPrologueStyleGuide {
2260
]
2361
}
2462

25-
static let continueButtonStyle = NUXButtonStyle(normal: .init(backgroundColor: .white,
26-
borderColor: .white,
27-
titleColor: Self.backgroundColor),
28-
29-
highlighted: .init(backgroundColor: UIColor(red: 1.00, green: 1.00, blue: 1.00, alpha: 0.90),
30-
borderColor: UIColor(red: 1.00, green: 1.00, blue: 1.00, alpha: 0.90),
31-
titleColor: Self.backgroundColor),
63+
static let continueButtonStyle = NUXButtonStyle(normal: .init(backgroundColor: continueFillColor,
64+
borderColor: continueFillColor,
65+
titleColor: continueTextColor),
66+
highlighted: .init(backgroundColor: continueHighlightedFillColor,
67+
borderColor: continueHighlightedFillColor,
68+
titleColor: continueHighlightedTextColor),
3269

33-
disabled: .init(backgroundColor: .white,
34-
borderColor: .white,
35-
titleColor: Self.backgroundColor))
70+
disabled: .init(backgroundColor: .white,
71+
borderColor: .white,
72+
titleColor: backgroundColor))
3673

37-
static let siteAddressButtonStyle = NUXButtonStyle(normal: .init(backgroundColor: Self.backgroundColor,
38-
borderColor: UIColor(red: 1.00, green: 1.00, blue: 1.00, alpha: 0.40),
39-
titleColor: .white),
74+
static let siteAddressButtonStyle = NUXButtonStyle(normal: .init(backgroundColor: siteFillColor,
75+
borderColor: siteBorderColor,
76+
titleColor: siteTextColor),
4077

41-
highlighted: .init(backgroundColor: Self.backgroundColor,
42-
borderColor: UIColor(red: 1.00, green: 1.00, blue: 1.00, alpha: 0.20),
43-
titleColor: UIColor.white.withAlphaComponent(0.7)),
78+
highlighted: .init(backgroundColor: siteHighlightedFillColor,
79+
borderColor: siteHighlightedBorderColor,
80+
titleColor: siteHighlightedTextColor),
4481

4582
disabled: .init(backgroundColor: .white,
4683
borderColor: .white,
47-
titleColor: Self.backgroundColor))
84+
titleColor: backgroundColor))
4885

4986
}

WordPress/Jetpack/Classes/NUX/JetpackPrologueViewController.swift

Lines changed: 75 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,37 +12,90 @@ class JetpackPrologueViewController: UIViewController {
1212
return view
1313
}()
1414

15-
var gradientLayer: CALayer = {
15+
private lazy var jetpackAnimatedView: UIView = {
16+
let viewModel = JetpackPromptsViewModel()
17+
let jetpackAnimatedView = UIView.embedSwiftUIView(JetpackLandingScreenView(viewModel: viewModel))
18+
jetpackAnimatedView.translatesAutoresizingMaskIntoConstraints = false
19+
return jetpackAnimatedView
20+
}()
21+
22+
private lazy var logoImageView: UIImageView = {
23+
let imageView = UIImageView(image: UIImage(named: "jetpack-logo"))
24+
imageView.translatesAutoresizingMaskIntoConstraints = false
25+
return imageView
26+
}()
27+
28+
private lazy var gradientLayer: CALayer = {
29+
makeGradientLayer()
30+
}()
31+
32+
private func makeGradientLayer() -> CAGradientLayer {
1633
let gradientLayer = CAGradientLayer()
1734

1835
// Start color is the background color with no alpha because if we use clear it will fade to black
1936
// instead of just disappearing
2037
let startColor = JetpackPrologueStyleGuide.backgroundColor.withAlphaComponent(0)
38+
let midTopColor = JetpackPrologueStyleGuide.backgroundColor.withAlphaComponent(0.9)
39+
let midBottomColor = JetpackPrologueStyleGuide.backgroundColor.withAlphaComponent(0.2)
2140
let endColor = JetpackPrologueStyleGuide.backgroundColor
2241

23-
gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
24-
gradientLayer.locations = [0.0, 0.9]
42+
gradientLayer.colors = FeatureFlag.newLandingScreen.enabled ?
43+
[endColor.cgColor, midTopColor.cgColor, midBottomColor.cgColor, startColor.cgColor] :
44+
[startColor.cgColor, endColor.cgColor]
45+
46+
gradientLayer.locations = FeatureFlag.newLandingScreen.enabled ? [0.0, 0.2, 0.5, 1.0] : [0.0, 0.9]
2547

2648
return gradientLayer
27-
}()
49+
}
2850

2951
override func viewDidLoad() {
3052
super.viewDidLoad()
3153

3254
view.backgroundColor = JetpackPrologueStyleGuide.backgroundColor
55+
56+
guard FeatureFlag.newLandingScreen.enabled else {
57+
loadOldPrologueView()
58+
return
59+
}
60+
loadNewPrologueView()
61+
}
62+
63+
private func loadNewPrologueView() {
64+
// hide old view unused elements
65+
stackView.isHidden = true
66+
titleLabel.isHidden = true
67+
// complex gradient background
68+
if let backgroundImage = UIImage(named: "JPBackground") {
69+
view.layer.contents = backgroundImage.cgImage
70+
}
71+
// animated view
72+
73+
view.addSubview(jetpackAnimatedView)
74+
view.pinSubviewToAllEdges(jetpackAnimatedView)
75+
// Jetpack logo with parallax
76+
view.addSubview(logoImageView)
77+
addParallax(to: logoImageView)
78+
// linear gradient above the animated view
79+
view.layer.insertSublayer(gradientLayer, above: jetpackAnimatedView.layer)
80+
// constraints
81+
NSLayoutConstraint.activate([
82+
logoImageView.widthAnchor.constraint(equalToConstant: 72),
83+
logoImageView.heightAnchor.constraint(equalTo: logoImageView.widthAnchor),
84+
logoImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
85+
logoImageView.topAnchor.constraint(equalTo: view.topAnchor, constant: 72)
86+
])
87+
}
88+
89+
private func loadOldPrologueView() {
3390
view.addSubview(starFieldView)
3491
view.layer.addSublayer(gradientLayer)
35-
3692
titleLabel.text = NSLocalizedString("Site security and performance\nfrom your pocket", comment: "Prologue title label, the \n force splits it into 2 lines.")
3793
titleLabel.textColor = JetpackPrologueStyleGuide.Title.textColor
3894
titleLabel.font = JetpackPrologueStyleGuide.Title.font
39-
4095
// Move the layers to appear below everything else
4196
starFieldView.layer.zPosition = Constants.starLayerPosition
4297
gradientLayer.zPosition = Constants.gradientLayerPosition
43-
44-
addParallax()
45-
98+
addParallax(to: stackView)
4699
updateLabel(for: traitCollection)
47100
}
48101

@@ -57,18 +110,26 @@ class JetpackPrologueViewController: UIViewController {
57110
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
58111
super.traitCollectionDidChange(previousTraitCollection)
59112

60-
updateLabel(for: traitCollection)
113+
guard FeatureFlag.newLandingScreen.enabled,
114+
previousTraitCollection?.userInterfaceStyle != traitCollection.userInterfaceStyle else {
115+
updateLabel(for: traitCollection)
116+
return
117+
}
118+
gradientLayer.removeFromSuperlayer()
119+
gradientLayer = makeGradientLayer()
120+
view.layer.insertSublayer(gradientLayer, above: jetpackAnimatedView.layer)
61121
}
62122

63123
override func viewDidLayoutSubviews() {
64124
super.viewDidLayoutSubviews()
65-
66-
starFieldView.frame = view.bounds
125+
if !FeatureFlag.newLandingScreen.enabled {
126+
starFieldView.frame = view.bounds
127+
}
67128
gradientLayer.frame = view.bounds
68129
}
69130

70131
/// Slightly moves the logo / text when moving the device
71-
private func addParallax() {
132+
private func addParallax(to view: UIView) {
72133
let amount = Constants.parallaxAmount
73134

74135
let horizontal = UIInterpolatingMotionEffect(keyPath: "center.x", type: .tiltAlongHorizontalAxis)
@@ -82,7 +143,7 @@ class JetpackPrologueViewController: UIViewController {
82143
let group = UIMotionEffectGroup()
83144
group.motionEffects = [horizontal, vertical]
84145

85-
stackView.addMotionEffect(group)
146+
view.addMotionEffect(group)
86147
}
87148

88149
private struct Constants {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import SwiftUI
2+
3+
struct JetpackPrompt: Identifiable {
4+
var id = UUID()
5+
let index: Int
6+
let text: String
7+
let color: Color
8+
var frameHeight: CGFloat
9+
var initialOffset: CGFloat
10+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import SwiftUI
2+
3+
/// A type that contains the configuration of the animated prompts in the Jetpack prologue screen
4+
struct JetpackPromptsConfiguration {
5+
6+
let size: CGSize
7+
let frameHeight: CGFloat
8+
let fontSize: CGFloat
9+
let totalHeight: CGFloat
10+
let hiddenRows: Int
11+
let maximumOffset: CGFloat
12+
13+
let prompts: [JetpackPrompt]
14+
15+
init(size: CGSize) {
16+
self.size = size
17+
18+
var prompts = [JetpackPrompt]()
19+
20+
let frameHeight = ceil(size.height / CGFloat(Constants.visibleRows)) + JetpackPromptView.totalVerticalPadding
21+
self.frameHeight = frameHeight
22+
23+
let fontSize = floor((frameHeight - JetpackPromptView.totalVerticalPadding) * Constants.fontScaleFactor)
24+
self.fontSize = fontSize
25+
// sum of all the offsets + frame height of the last element = total height
26+
var cumulatedOffset: CGFloat = 0
27+
28+
for row in Constants.prompts.enumerated() {
29+
let textHeight = row.element.height(withMaxWidth: size.width,
30+
font: UIFont.systemFont(ofSize: fontSize).bold())
31+
// double the frame height if the text spans over 2 rows
32+
let actualFrameHeight = textHeight > frameHeight ? 2 * frameHeight : frameHeight
33+
34+
prompts.append(JetpackPrompt(index: row.offset,
35+
text: row.element,
36+
color: row.offset % 2 == 0 ? Constants.evenColor : Constants.oddColor,
37+
frameHeight: actualFrameHeight,
38+
initialOffset: cumulatedOffset))
39+
cumulatedOffset += actualFrameHeight
40+
}
41+
42+
self.prompts = prompts
43+
self.totalHeight = cumulatedOffset
44+
self.hiddenRows = Int(totalHeight / frameHeight) - Constants.visibleRows
45+
self.maximumOffset = size.height + CGFloat(self.hiddenRows / 2) * frameHeight
46+
}
47+
}
48+
49+
/// Text and constants
50+
private extension JetpackPromptsConfiguration {
51+
52+
enum Constants {
53+
// alternate colors in rows
54+
static let evenColor = Color(UIColor(light: .muriel(color: .jetpackGreen, .shade60),
55+
dark: .muriel(color: .jetpackGreen, .shade40)))
56+
static let oddColor = Color(UIColor(light: .muriel(color: .jetpackGreen, .shade20),
57+
dark: .muriel(color: .jetpackGreen, .shade5)))
58+
59+
static let visibleRows = prompts.count / 2
60+
// Font size is calculated as a percentage of frame height
61+
static let fontScaleFactor: CGFloat = 0.8
62+
63+
static let basePrompts = [
64+
NSLocalizedString("jetpack.prologue.prompt.watchStats",
65+
value: "Watch your stats",
66+
comment: "Watch your stats prompt for the jetpack prologue"),
67+
NSLocalizedString("jetpack.prologue.prompt.checkNotifications",
68+
value: "Check notifications",
69+
comment: "Check notifications prompt for the jetpack prologue"),
70+
NSLocalizedString("jetpack.prologue.prompt.buildSite",
71+
value: "Build a site",
72+
comment: "Build a site prompt for the jetpack prologue"),
73+
NSLocalizedString("jetpack.prologue.prompt.respondComments",
74+
value: "Respond to comments",
75+
comment: "Respond to comments prompt for the jetpack prologue"),
76+
NSLocalizedString("jetpack.prologue.prompt.restoreBackup",
77+
value: "Restore a backup",
78+
comment: "Restore a backup prompt for the jetpack prologue"),
79+
NSLocalizedString("jetpack.prologue.prompt.searchPlugins",
80+
value: "Search for plugins",
81+
comment: "Search for plugins prompt for the jetpack prologue"),
82+
NSLocalizedString("jetpack.prologue.prompt.fbShare",
83+
value: "Share on Facebook",
84+
comment: "Share on Facebook prompt for the jetpack prologue"),
85+
NSLocalizedString("jetpack.prologue.prompt.fixSecurity",
86+
value: "Fix a security issue",
87+
comment: "Fix a security issue prompt for the jetpack prologue"),
88+
NSLocalizedString("jetpack.prologue.prompt.postPhoto",
89+
value: "Post a photo",
90+
comment: "Post a photo prompt for the jetpack prologue"),
91+
NSLocalizedString("jetpack.prologue.prompt.addAuthor",
92+
value: "Add an author",
93+
comment: "Add an author prompt for the jetpack prologue"),
94+
NSLocalizedString("jetpack.prologue.prompt.searchPosts",
95+
value: "Search for posts or sites",
96+
comment: "Search for posts or sites prompt for the jetpack prologue"),
97+
NSLocalizedString("jetpack.prologue.prompt.writeBlog",
98+
value: "Write a blog",
99+
comment: "Write a blog prompt for the jetpack prologue"),
100+
NSLocalizedString("jetpack.prologue.prompt.readArticle",
101+
value: "Read an article",
102+
comment: "Read an article prompt for the jetpack prologue")
103+
]
104+
105+
// Duplicate the array to have enough cells for the rotation
106+
static let prompts = basePrompts + basePrompts
107+
}
108+
}

0 commit comments

Comments
 (0)