Skip to content

Commit b0c9df4

Browse files
author
Emily Laguna
authored
Merge pull request #18385 from wordpress-mobile/try/personalized-questions-concept
Add Onboarding Personalization Questions
2 parents da25f3e + 3af8245 commit b0c9df4

26 files changed

Lines changed: 1150 additions & 38 deletions

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* [*] Quick Start: Updated the Stats tour. The tour can now be accessed from either the dashboard or the menu tab. [#18413]
66
* [*] Quick Start: Updated the Reader tour. The tour now highlights the Discover tab and guides users to follow topics via the Settings screen. [#18450]
77
* [*] [internal] Quick Start: Refactored some code related to the tasks displayed in the Quick Start Card and the Quick Start modal. It should have no visible changes but could cause regressions. [#18395]
8+
* [**] We'll now ask users logging in which area of the app they'd like to focus on to build towards a more personalized experience. [#18385]
89

910
19.7
1011
-----

WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,16 @@ import Foundation
338338
// Quick Start
339339
case quickStartStarted
340340

341+
// Onboarding Question Prompt
342+
case onboardingQuestionsDisplayed
343+
case onboardingQuestionsItemSelected
344+
case onboardingQuestionsSkipped
345+
346+
// Onboarding Enable Notifications Prompt
347+
case onboardingEnableNotificationsDisplayed
348+
case onboardingEnableNotificationsSkipped
349+
case onboardingEnableNotificationsEnableTapped
350+
341351
/// A String that represents the event
342352
var value: String {
343353
switch self {
@@ -895,6 +905,21 @@ import Foundation
895905
case .enhancedSiteCreationIntentQuestionExperiment:
896906
return "enhanced_site_creation_intent_question_experiment"
897907

908+
// Onboarding Question Prompt
909+
case .onboardingQuestionsDisplayed:
910+
return "onboarding_questions_displayed"
911+
case .onboardingQuestionsItemSelected:
912+
return "onboarding_questions_item_selected"
913+
case .onboardingQuestionsSkipped:
914+
return "onboarding_questions_skipped"
915+
916+
// Onboarding Enable Notifications Prompt
917+
case .onboardingEnableNotificationsDisplayed:
918+
return "onboarding_enable_notifications_displayed"
919+
case .onboardingEnableNotificationsSkipped:
920+
return "onboarding_enable_notifications_skipped"
921+
case .onboardingEnableNotificationsEnableTapped:
922+
return "onboarding_enable_notifications_enable_tapped"
898923
// Site Name
899924
case .enhancedSiteCreationSiteNameCanceled:
900925
return "enhanced_site_creation_site_name_canceled"
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import Foundation
2+
3+
extension MySiteViewController {
4+
func startObservingOnboardingPrompt() {
5+
NotificationCenter.default.addObserver(self, selector: #selector(onboardingPromptWasDismissed(_:)), name: .onboardingPromptWasDismissed, object: nil)
6+
}
7+
8+
@objc func onboardingPromptWasDismissed(_ notification: NSNotification) {
9+
guard
10+
let userInfo = notification.userInfo,
11+
let option = userInfo["option"] as? OnboardingOption
12+
else {
13+
return
14+
}
15+
16+
switch option {
17+
case .stats:
18+
// Show the stats view for the current blog
19+
if let blog = blog {
20+
StatsViewController.show(for: blog, from: self)
21+
}
22+
case .writing:
23+
// Open the editor
24+
let controller = tabBarController as? WPTabBarController
25+
controller?.showPostTab(completion: {
26+
self.startAlertTimer()
27+
})
28+
29+
case .showMeAround:
30+
// Start the quick start
31+
if let blog = blog {
32+
let type: QuickStartType = FeatureFlag.quickStartForExistingUsers.enabled ? .existingSite : .newSite
33+
QuickStartTourGuide.shared.setup(for: blog, type: type)
34+
}
35+
36+
case .skip, .reader, .notifications:
37+
// Skip: Do nothing
38+
// Reader and notifications will be handled by:
39+
// WPAuthenticationManager.handleOnboardingQuestionsWillDismiss
40+
break
41+
}
42+
}
43+
}

WordPress/Classes/ViewRelated/Blog/My Site/MySiteViewController.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ class MySiteViewController: UIViewController, NoResultsViewHost {
155155
subscribeToModelChanges()
156156
subscribeToContentSizeCategory()
157157
startObservingQuickStart()
158+
startObservingOnboardingPrompt()
158159
}
159160

160161
override func viewWillAppear(_ animated: Bool) {
@@ -268,8 +269,7 @@ class MySiteViewController: UIViewController, NoResultsViewHost {
268269
segmentedControlContainerView.isHidden = hideSegmentedControl
269270

270271
if !hideSegmentedControl && switchTabsIfNeeded {
271-
segmentedControl.selectedSegmentIndex = mySiteSettings.defaultSection.rawValue
272-
segmentedControlValueChanged()
272+
switchTab(to: mySiteSettings.defaultSection)
273273
}
274274
}
275275

@@ -478,6 +478,13 @@ class MySiteViewController: UIViewController, NoResultsViewHost {
478478
}
479479
}
480480

481+
/// Changes between the site menu and dashboard
482+
/// - Parameter section: The section to switch to
483+
func switchTab(to section: Section) {
484+
segmentedControl.selectedSegmentIndex = section.rawValue
485+
segmentedControlValueChanged()
486+
}
487+
481488
// MARK: - Child VC logic
482489

483490
private func embedChildInStackView(_ child: UIViewController) {
@@ -765,8 +772,7 @@ class MySiteViewController: UIViewController, NoResultsViewHost {
765772
}
766773

767774
if !blog.isAccessibleThroughWPCom() && self.isShowingDashboard {
768-
self.segmentedControl.selectedSegmentIndex = Section.siteMenu.rawValue
769-
self.segmentedControlValueChanged()
775+
self.switchTab(to: .siteMenu)
770776
}
771777

772778
self.updateNavigationTitle(for: blog)
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
import UIKit
2+
3+
class OnboardingEnableNotificationsViewController: UIViewController {
4+
@IBOutlet weak var titleLabel: UILabel!
5+
@IBOutlet weak var subTitleLabel: UILabel!
6+
@IBOutlet weak var detailView: UIView!
7+
8+
let option: OnboardingOption
9+
let coordinator: OnboardingQuestionsCoordinator
10+
11+
init(with coordinator: OnboardingQuestionsCoordinator, option: OnboardingOption) {
12+
self.coordinator = coordinator
13+
self.option = option
14+
15+
super.init(nibName: nil, bundle: nil)
16+
}
17+
18+
required convenience init?(coder: NSCoder) {
19+
self.init(with: OnboardingQuestionsCoordinator(), option: .notifications)
20+
}
21+
22+
override func viewDidLoad() {
23+
super.viewDidLoad()
24+
25+
navigationController?.navigationBar.isHidden = true
26+
navigationController?.delegate = self
27+
28+
applyStyles()
29+
updateContent()
30+
31+
coordinator.notificationsDisplayed(option: option)
32+
}
33+
34+
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
35+
return [.portrait, .portraitUpsideDown]
36+
}
37+
}
38+
39+
// MARK: - IBAction's
40+
extension OnboardingEnableNotificationsViewController {
41+
@IBAction func enableButtonTapped(_ sender: Any) {
42+
coordinator.notificationsEnabledTapped(selection: option)
43+
}
44+
45+
@IBAction func skipButtonTapped(_ sender: Any) {
46+
coordinator.notificationsSkipped(selection: option)
47+
}
48+
}
49+
50+
// MARK: - Trait Collection Handling
51+
extension OnboardingEnableNotificationsViewController {
52+
func updateContent(for traitCollection: UITraitCollection) {
53+
let contentSize = traitCollection.preferredContentSizeCategory
54+
55+
// Hide the detail image if the text is too large
56+
detailView.isHidden = contentSize.isAccessibilityCategory
57+
}
58+
59+
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
60+
super.traitCollectionDidChange(previousTraitCollection)
61+
62+
updateContent(for: traitCollection)
63+
}
64+
}
65+
66+
// MARK: - UINavigation Controller Delegate
67+
extension OnboardingEnableNotificationsViewController: UINavigationControllerDelegate {
68+
func navigationControllerSupportedInterfaceOrientations(_ navigationController: UINavigationController) -> UIInterfaceOrientationMask {
69+
return supportedInterfaceOrientations
70+
}
71+
72+
func navigationControllerPreferredInterfaceOrientationForPresentation(_ navigationController: UINavigationController) -> UIInterfaceOrientation {
73+
return .portrait
74+
}
75+
}
76+
77+
// MARK: - Private Helpers
78+
private extension OnboardingEnableNotificationsViewController {
79+
func applyStyles() {
80+
navigationController?.navigationBar.isHidden = true
81+
82+
titleLabel.font = WPStyleGuide.serifFontForTextStyle(.title1, fontWeight: .semibold)
83+
titleLabel.textColor = .text
84+
85+
subTitleLabel.font = .preferredFont(forTextStyle: .title3)
86+
subTitleLabel.textColor = .secondaryLabel
87+
}
88+
89+
func updateContent() {
90+
let text: String
91+
let notificationContent: UnifiedPrologueNotificationsContent?
92+
93+
switch option {
94+
case .stats:
95+
text = StatsStrings.subTitle
96+
notificationContent = .init(topElementTitle: StatsStrings.notificationTopTitle,
97+
middleElementTitle: StatsStrings.notificationMiddleTitle,
98+
bottomElementTitle: StatsStrings.notificationBottomTitle,
99+
topImage: "view-milestone-1k",
100+
middleImage: "traffic-surge-icon")
101+
case .writing:
102+
text = WritingStrings.subTitle
103+
notificationContent = nil
104+
105+
case .notifications, .showMeAround, .skip:
106+
text = DefaultStrings.subTitle
107+
notificationContent = nil
108+
109+
case .reader:
110+
text = ReaderStrings.subTitle
111+
notificationContent = .init(topElementTitle: ReaderStrings.notificationTopTitle,
112+
middleElementTitle: ReaderStrings.notificationMiddleTitle,
113+
bottomElementTitle: ReaderStrings.notificationBottomTitle)
114+
}
115+
116+
117+
subTitleLabel.text = text
118+
119+
// Convert the image view to a UIView and embed it
120+
let imageView = UIView.embedSwiftUIView(UnifiedPrologueNotificationsContentView(notificationContent))
121+
imageView.frame.size.width = detailView.frame.width
122+
detailView.addSubview(imageView)
123+
imageView.pinSubviewToAllEdges(detailView)
124+
}
125+
}
126+
127+
// MARK: - Constants / Strings
128+
private struct StatsStrings {
129+
static let subTitle = NSLocalizedString("Know when your site is getting more traffic, new followers, or when it passes a new milestone!", comment: "Subtitle giving the user more context about why to enable notifications.")
130+
131+
static let notificationTopTitle = NSLocalizedString("Congratulations! Your site passed *1000 all-time* views!", comment: "Example notification content displayed on the Enable Notifications prompt that is personalized based on a users selection. Words marked between * characters will be displayed as bold text.")
132+
static let notificationMiddleTitle = NSLocalizedString("Your site appears to be getting *more traffic* than usual!", comment: "Example notification content displayed on the Enable Notifications prompt that is personalized based on a users selection. Words marked between * characters will be displayed as bold text.")
133+
static let notificationBottomTitle = NSLocalizedString("*Johann Brandt* is now following your site!", comment: "Example notification content displayed on the Enable Notifications prompt that is personalized based on a users selection. Words marked between * characters will be displayed as bold text.")
134+
}
135+
136+
private struct WritingStrings {
137+
static let subTitle = NSLocalizedString("Stay in touch with your audience with like and comment notifications.", comment: "Subtitle giving the user more context about why to enable notifications.")
138+
}
139+
140+
private struct DefaultStrings {
141+
static let subTitle = NSLocalizedString("Stay in touch with like and comment notifications.", comment: "Subtitle giving the user more context about why to enable notifications.")
142+
}
143+
144+
private struct ReaderStrings {
145+
static let subTitle = NSLocalizedString("Know when your favorite authors post new content.", comment: "Subtitle giving the user more context about why to enable notifications.")
146+
static let notificationTopTitle = NSLocalizedString("*Madison Ruiz* added a new post to their site", comment: "Example notification content displayed on the Enable Notifications prompt that is personalized based on a users selection. Words marked between * characters will be displayed as bold text.")
147+
static let notificationMiddleTitle = NSLocalizedString("You received *50 likes* on your comment", comment: "Example notification content displayed on the Enable Notifications prompt that is personalized based on a users selection. Words marked between * characters will be displayed as bold text.")
148+
static let notificationBottomTitle = NSLocalizedString("*Johann Brandt* responded to your comment", comment: "Example notification content displayed on the Enable Notifications prompt that is personalized based on a users selection. Words marked between * characters will be displayed as bold text.")
149+
}

0 commit comments

Comments
 (0)