Skip to content

Commit d0a97b0

Browse files
authored
[Jetpack Content Migration Flow] Add Help Screen (#19618)
1 parent 784c2a4 commit d0a97b0

File tree

12 files changed

+267
-49
lines changed

12 files changed

+267
-49
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import UIKit
2+
3+
struct MeHeaderViewConfiguration {
4+
5+
let gravatarEmail: String?
6+
let username: String
7+
let displayName: String
8+
}
9+
10+
extension MeHeaderViewConfiguration {
11+
12+
init(account: WPAccount) {
13+
self.init(
14+
gravatarEmail: account.email,
15+
username: account.username,
16+
displayName: account.displayName
17+
)
18+
}
19+
}
20+
21+
extension MeHeaderView {
22+
23+
func update(with configuration: Configuration) {
24+
self.gravatarEmail = configuration.gravatarEmail
25+
self.username = configuration.username
26+
self.displayName = configuration.displayName
27+
}
28+
29+
typealias Configuration = MeHeaderViewConfiguration
30+
}

WordPress/Classes/ViewRelated/Site Creation/Shared/UITableView+Header.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@ extension UITableView {
4242
withHorizontalFittingPriority: .required,
4343
verticalFittingPriority: .fittingSizeLevel
4444
)
45-
tableHeaderView.frame = CGRect(origin: .zero, size: size)
46-
self.tableHeaderView = tableHeaderView
45+
let newFrame = CGRect(origin: .zero, size: size)
46+
if tableHeaderView.frame.height != newFrame.height {
47+
tableHeaderView.frame = newFrame
48+
self.tableHeaderView = tableHeaderView
49+
}
4750
}
4851
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import UIKit
2+
3+
struct LogOutActionHandler {
4+
5+
func logOut(with viewController: UIViewController) {
6+
let alert = UIAlertController(title: logOutAlertTitle, message: nil, preferredStyle: .alert)
7+
alert.addActionWithTitle(Strings.alertCancelAction, style: .cancel)
8+
alert.addActionWithTitle(Strings.alertLogoutAction, style: .destructive) { [weak viewController] _ in
9+
viewController?.dismiss(animated: true) {
10+
AccountHelper.logOutDefaultWordPressComAccount()
11+
}
12+
}
13+
viewController.present(alert, animated: true)
14+
}
15+
16+
private var logOutAlertTitle: String {
17+
let context = ContextManager.sharedInstance().mainContext
18+
let count = AbstractPost.countLocalPosts(in: context)
19+
20+
guard count > 0 else {
21+
return Strings.alertDefaultTitle
22+
}
23+
24+
let format = count > 1 ? Strings.alertUnsavedTitlePlural : Strings.alertUnsavedTitleSingular
25+
return String(format: format, count)
26+
}
27+
28+
29+
private struct Strings {
30+
static let alertDefaultTitle = AppConstants.Logout.alertTitle
31+
static let alertUnsavedTitleSingular = NSLocalizedString("You have changes to %d post that hasn't been uploaded to your site. Logging out now will delete those changes. Log out anyway?",
32+
comment: "Warning displayed before logging out. The %d placeholder will contain the number of local posts (SINGULAR!)")
33+
static let alertUnsavedTitlePlural = NSLocalizedString("You have changes to %d posts that haven’t been uploaded to your site. Logging out now will delete those changes. Log out anyway?",
34+
comment: "Warning displayed before logging out. The %d placeholder will contain the number of local posts (PLURAL!)")
35+
static let alertCancelAction = NSLocalizedString("Cancel", comment: "Verb. A button title. Tapping cancels an action.")
36+
static let alertLogoutAction = NSLocalizedString("Log Out", comment: "Button for confirming logging out from WordPress.com account")
37+
}
38+
}

WordPress/Classes/ViewRelated/Support/SupportTableViewController.swift

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ class SupportTableViewController: UITableViewController {
55

66
// MARK: - Properties
77

8+
/// Configures the appearance of the support screen.
9+
let configuration: Configuration
10+
811
var sourceTag: WordPressSupportSourceTag?
912

1013
// If set, the Zendesk views will be shown from this view instead of in the navigation controller.
@@ -21,7 +24,8 @@ class SupportTableViewController: UITableViewController {
2124

2225
// MARK: - Init
2326

24-
override init(style: UITableView.Style) {
27+
init(configuration: Configuration = .init(), style: UITableView.Style = .grouped) {
28+
self.configuration = configuration
2529
super.init(style: style)
2630
}
2731

@@ -46,6 +50,11 @@ class SupportTableViewController: UITableViewController {
4650
ZendeskUtils.fetchUserInformation()
4751
}
4852

53+
override func viewDidLayoutSubviews() {
54+
super.viewDidLayoutSubviews()
55+
self.tableView.sizeToFitHeaderView()
56+
}
57+
4958
override func viewWillAppear(_ animated: Bool) {
5059
super.viewWillAppear(animated)
5160
reloadViewModel()
@@ -78,7 +87,6 @@ class SupportTableViewController: UITableViewController {
7887
dismissTapped?()
7988
dismiss(animated: true)
8089
}
81-
8290
}
8391

8492
// MARK: - Private Extension
@@ -107,14 +115,18 @@ private extension SupportTableViewController {
107115
NavigationItemRow.self,
108116
TextRow.self,
109117
HelpRow.self,
118+
DestructiveButtonRow.self,
110119
SupportEmailRow.self],
111120
tableView: tableView)
112121
tableHandler = ImmuTableViewHandler(takeOver: self)
113122
reloadViewModel()
114123
WPStyleGuide.configureColors(view: view, tableView: tableView)
115-
// remove empty cells
116-
tableView.tableFooterView = UIView()
117-
124+
tableView.tableFooterView = UIView() // remove empty cells
125+
if let headerConfig = configuration.meHeaderConfiguration {
126+
let headerView = MeHeaderView()
127+
headerView.update(with: headerConfig)
128+
tableView.tableHeaderView = headerView
129+
}
118130
registerObservers()
119131
}
120132

@@ -144,19 +156,34 @@ private extension SupportTableViewController {
144156
footerText: LocalizedText.helpFooter)
145157

146158
// Information Section
147-
let versionRow = TextRow(title: LocalizedText.version, value: Bundle.main.shortVersionString())
148-
let switchRow = SwitchRow(title: LocalizedText.extraDebug,
149-
value: userDefaults.bool(forKey: UserDefaultsKeys.extraDebug),
150-
onChange: extraDebugToggled())
151-
let logsRow = NavigationItemRow(title: LocalizedText.activityLogs, action: activityLogsSelected(), accessibilityIdentifier: "activity-logs-button")
159+
var informationSection: ImmuTableSection?
160+
if configuration.showsLogsSection {
161+
let versionRow = TextRow(title: LocalizedText.version, value: Bundle.main.shortVersionString())
162+
let switchRow = SwitchRow(title: LocalizedText.extraDebug,
163+
value: userDefaults.bool(forKey: UserDefaultsKeys.extraDebug),
164+
onChange: extraDebugToggled())
165+
let logsRow = NavigationItemRow(title: LocalizedText.activityLogs, action: activityLogsSelected(), accessibilityIdentifier: "activity-logs-button")
166+
informationSection = ImmuTableSection(
167+
headerText: nil,
168+
rows: [versionRow, switchRow, logsRow],
169+
footerText: LocalizedText.informationFooter
170+
)
171+
}
152172

153-
let informationSection = ImmuTableSection(
154-
headerText: nil,
155-
rows: [versionRow, switchRow, logsRow],
156-
footerText: LocalizedText.informationFooter)
173+
// Log out Section
174+
var logOutSections: ImmuTableSection?
175+
if configuration.showsLogOutButton {
176+
let logOutRow = DestructiveButtonRow(
177+
title: LocalizedText.logOutButtonTitle,
178+
action: logOutTapped(),
179+
accessibilityIdentifier: ""
180+
)
181+
logOutSections = .init(headerText: LocalizedText.wpAccount, optionalRows: [logOutRow])
182+
}
157183

158184
// Create and return table
159-
return ImmuTable(sections: [helpSection, informationSection])
185+
let sections = [helpSection, informationSection, logOutSections].compactMap { $0 }
186+
return ImmuTable(sections: sections)
160187
}
161188

162189
@objc func refreshNotificationIndicator(_ notification: Foundation.Notification) {
@@ -274,6 +301,17 @@ private extension SupportTableViewController {
274301
}
275302
}
276303

304+
private func logOutTapped() -> ImmuTableAction {
305+
return { [weak self] row in
306+
guard let self else {
307+
return
308+
}
309+
self.tableView.deselectSelectedRowWithAnimation(true)
310+
let actionHandler = LogOutActionHandler()
311+
actionHandler.logOut(with: self)
312+
}
313+
}
314+
277315
// MARK: - ImmuTableRow Struct
278316

279317
struct HelpRow: ImmuTableRow {
@@ -345,6 +383,8 @@ private extension SupportTableViewController {
345383
static let contactEmail = NSLocalizedString("Contact Email", comment: "Support email label.")
346384
static let contactEmailAccessibilityHint = NSLocalizedString("Shows a dialog for changing the Contact Email.", comment: "Accessibility hint describing what happens if the Contact Email button is tapped.")
347385
static let emailNotSet = NSLocalizedString("Not Set", comment: "Display value for Support email field if there is no user email address.")
386+
static let wpAccount = NSLocalizedString("WordPress.com Account", comment: "WordPress.com sign-out section header title")
387+
static let logOutButtonTitle = NSLocalizedString("Log Out", comment: "Button for confirming logging out from WordPress.com account")
348388
}
349389

350390
// MARK: - User Defaults Keys
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import Foundation
2+
3+
struct SupportTableViewControllerConfiguration {
4+
5+
// MARK: Properties
6+
7+
var meHeaderConfiguration: MeHeaderView.Configuration?
8+
var showsLogOutButton: Bool = false
9+
var showsLogsSection: Bool = true
10+
11+
// MARK: Default Configurations
12+
13+
static func currentAccountConfiguration() -> Self {
14+
var config = Self.init()
15+
if let account = Self.makeAccount() {
16+
config.meHeaderConfiguration = .init(account: account)
17+
config.showsLogOutButton = true
18+
config.showsLogsSection = false
19+
}
20+
return config
21+
}
22+
23+
private static func makeAccount() -> WPAccount? {
24+
let context = ContextManager.shared.mainContext
25+
do {
26+
return try WPAccount.lookupDefaultWordPressComAccount(in: context)
27+
} catch {
28+
DDLogError("Account lookup failed with error: \(error)")
29+
return nil
30+
}
31+
}
32+
}
33+
34+
extension SupportTableViewController {
35+
36+
typealias Configuration = SupportTableViewControllerConfiguration
37+
}

WordPress/Jetpack/Classes/ViewRelated/WordPress-to-Jetpack Migration/Common/MigrationDependencyContainer.swift

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,30 @@ struct MigrationViewControllerFactory {
4444
}
4545
}
4646

47-
private func makeWelcomeViewModel() -> MigrationWelcomeViewModel {
48-
MigrationWelcomeViewModel(account: makeAccount(), coordinator: coordinator)
47+
// MARK: - View Controllers
48+
49+
private func makeWelcomeViewModel(handlers: ActionHandlers) -> MigrationWelcomeViewModel {
50+
let primaryHandler = { () -> Void in handlers.primary?() }
51+
let secondaryHandler = { () -> Void in handlers.secondary?() }
52+
53+
let actions = MigrationActionsViewConfiguration(
54+
step: .welcome,
55+
primaryHandler: primaryHandler,
56+
secondaryHandler: secondaryHandler
57+
)
58+
59+
return .init(account: makeAccount(), actions: actions)
4960
}
5061

5162
private func makeWelcomeViewController() -> UIViewController {
52-
MigrationWelcomeViewController(viewModel: makeWelcomeViewModel())
63+
let handlers = ActionHandlers()
64+
let viewModel = makeWelcomeViewModel(handlers: handlers)
65+
66+
let viewController = MigrationWelcomeViewController(viewModel: viewModel)
67+
handlers.primary = { [weak coordinator] in coordinator?.transitionToNextStep() }
68+
handlers.secondary = makeSupportViewControllerRouter(with: viewController)
69+
70+
return viewController
5371
}
5472

5573
private func makeNotificationsViewModel() -> MigrationNotificationsViewModel {
@@ -67,4 +85,20 @@ struct MigrationViewControllerFactory {
6785
private func makeDoneViewController() -> UIViewController {
6886
MigrationDoneViewController(viewModel: makeDoneViewModel())
6987
}
88+
89+
// MARK: - Routers
90+
91+
private func makeSupportViewControllerRouter(with presenter: UIViewController) -> () -> Void {
92+
return { [weak presenter] in
93+
let destination = SupportTableViewController(configuration: .currentAccountConfiguration(), style: .insetGrouped)
94+
presenter?.present(UINavigationController(rootViewController: destination), animated: true)
95+
}
96+
}
97+
98+
// MARK: - Types
99+
100+
private class ActionHandlers {
101+
var primary: (() -> Void)?
102+
var secondary: (() -> Void)?
103+
}
70104
}

WordPress/Jetpack/Classes/ViewRelated/WordPress-to-Jetpack Migration/Common/Navigation/MigrationNavigationController.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,22 @@ class MigrationNavigationController: UINavigationController {
1010
private var cancellable: AnyCancellable?
1111

1212
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
13+
if let presentedViewController {
14+
return presentedViewController.supportedInterfaceOrientations
15+
}
1316
if WPDeviceIdentification.isiPhone() {
1417
return .portrait
1518
} else {
1619
return .allButUpsideDown
1720
}
1821
}
1922

23+
// Force portrait orientation for migration view controllers only
2024
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
21-
.portrait
25+
if let presentedViewController {
26+
return presentedViewController.preferredInterfaceOrientationForPresentation
27+
}
28+
return .portrait
2229
}
2330

2431
init(coordinator: MigrationFlowCoordinator, factory: MigrationViewControllerFactory) {

WordPress/Jetpack/Classes/ViewRelated/WordPress-to-Jetpack Migration/Welcome/MigrationWelcomeViewController.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ final class MigrationWelcomeViewController: UIViewController {
6565
}
6666

6767
private func setupNavigationBar() {
68-
self.navigationItem.rightBarButtonItem = UIBarButtonItem(email: viewModel.gravatarEmail)
68+
self.navigationItem.rightBarButtonItem = UIBarButtonItem(email: viewModel.gravatarEmail) { [weak self] () -> Void in
69+
self?.viewModel.configuration.actionsConfiguration.secondaryHandler?()
70+
}
6971
}
7072

7173
private func setupBottomSheet() {

0 commit comments

Comments
 (0)