Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ class WelcomeSplitViewContent: SplitViewDisplayable {
init(addSite: @escaping (AddSiteMenuViewModel.Selection) -> Void) {
supplementary = UINavigationController(rootViewController: UnifiedPrologueViewController())

let addSiteViewModel = AddSiteMenuViewModel(context: .shared, onSelection: addSite)
let noSitesViewModel = NoSitesViewModel(appUIType: JetpackFeaturesRemovalCoordinator.currentAppUIType, account: nil)
let noSiteView = NoSitesView(addSiteViewModel: addSiteViewModel, viewModel: noSitesViewModel)
let noSitesVC = UIHostingController(rootView: noSiteView)
noSitesVC.view.backgroundColor = .systemBackground
secondary = UINavigationController(rootViewController: noSitesVC)
if let account = try? WPAccount.lookupDefaultWordPressComAccount(in: ContextManager.shared.mainContext) {
let noSiteView = NoSitesView(account: account, appUIType: JetpackFeaturesRemovalCoordinator.currentAppUIType, onSelection: addSite)
let noSitesVC = UIHostingController(rootView: noSiteView)
noSitesVC.view.backgroundColor = .systemBackground
secondary = UINavigationController(rootViewController: noSitesVC)
} else {
// This branch should never execute, because we only show the "Welcome" screen when the WP.com account does not have any sites.
secondary = UINavigationController()
}
}

func displayed(in splitVC: UISplitViewController) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,25 +131,7 @@ final class MySiteViewController: UIViewController, UIScrollViewDelegate, NoSite
private var noSitesScrollView: UIScrollView?
private var noSitesRefreshControl: UIRefreshControl?

private lazy var noSitesViewController: UIHostingController = {
let addSiteViewModel = AddSiteMenuViewModel { [weak self] in
switch $0 {
case .dotCom:
self?.launchSiteCreationFromNoSites()
RootViewCoordinator.shared.isSiteCreationActive = true
case .selfHosted:
self?.launchLoginForSelfHostedSite()
}
}
let noSitesViewModel = NoSitesViewModel(
appUIType: JetpackFeaturesRemovalCoordinator.currentAppUIType,
account: self.viewModel.defaultAccount
)
let noSiteView = NoSitesView(addSiteViewModel: addSiteViewModel, viewModel: noSitesViewModel)
let hostingVC = UIHostingController(rootView: noSiteView)
hostingVC.view.backgroundColor = .clear
return hostingVC
}()
private var noSitesViewController: UIHostingController<NoSitesView>?

private var isNavigationBarHidden = false

Expand Down Expand Up @@ -178,6 +160,10 @@ final class MySiteViewController: UIViewController, UIScrollViewDelegate, NoSite
}

configureNavBarAppearance(animated: animated)

if let account = self.viewModel.defaultAccount {
AccountService(coreDataStack: ContextManager.shared).updateUserDetails(for: account, success: nil, failure: nil)
}
}

override func viewWillDisappear(_ animated: Bool) {
Expand Down Expand Up @@ -228,7 +214,7 @@ final class MySiteViewController: UIViewController, UIScrollViewDelegate, NoSite

private func subscribeToWillEnterForeground() {
NotificationCenter.default.addObserver(self,
selector: #selector(displayOverlayIfNeeded),
selector: #selector(willEnterForeground),
name: UIApplication.willEnterForegroundNotification,
object: nil)
}
Expand Down Expand Up @@ -460,6 +446,8 @@ final class MySiteViewController: UIViewController, UIScrollViewDelegate, NoSite
// MARK: - No Sites UI logic

private func hideNoSites() {
guard let noSitesViewController else { return }

// Only track if the no sites view is currently visible
if noSitesViewController.view.superview != nil {
WPAnalytics.track(.mySiteNoSitesViewHidden)
Expand All @@ -473,23 +461,50 @@ final class MySiteViewController: UIViewController, UIScrollViewDelegate, NoSite
}

private func showNoSites() {
guard AccountHelper.isLoggedIn else {
guard let account = self.viewModel.defaultAccount else {
WordPressAppDelegate.shared?.windowManager.showFullscreenSignIn()
return
}

guard noSitesViewController.view.superview == nil else {
if let noSitesViewController, noSitesViewController.rootView.account.objectID != self.viewModel.defaultAccount?.objectID {
self.hideNoSites()
}

let viewController: UIHostingController<NoSitesView>
if let noSitesViewController, noSitesViewController.rootView.account == self.viewModel.defaultAccount?.objectID {
viewController = noSitesViewController
} else {
let view = NoSitesView(account: account, appUIType: JetpackFeaturesRemovalCoordinator.currentAppUIType) { [weak self] in
switch $0 {
case .dotCom:
if self?.viewModel.defaultAccount?.needsEmailVerification == true {
self?.presentNeedsEmailVerificationAlert()
return
}

self?.launchSiteCreationFromNoSites()
RootViewCoordinator.shared.isSiteCreationActive = true
case .selfHosted:
self?.launchLoginForSelfHostedSite()
}
}
viewController = UIHostingController(rootView: view)
viewController.view.backgroundColor = .clear
self.noSitesViewController = viewController
}

guard viewController.view.superview == nil else {
return
}

makeNoSitesScrollView()
configureNoSitesView()
addNoSitesViewAndConfigureConstraints()
configureNoSitesView(viewController)
addNoSitesViewAndConfigureConstraints(viewController)
createButtonCoordinator?.removeCreateButton()
}

private func trackNoSitesVisibleIfNeeded() {
guard noSitesViewController.view.superview != nil else {
guard let noSitesViewController, noSitesViewController.view.superview != nil else {
return
}

Expand All @@ -512,11 +527,11 @@ final class MySiteViewController: UIViewController, UIScrollViewDelegate, NoSite
noSitesScrollView = scrollView
}

private func configureNoSitesView() {
private func configureNoSitesView(_ noSitesViewController: UIHostingController<NoSitesView>) {
noSitesViewController.rootView.delegate = self
}

private func addNoSitesViewAndConfigureConstraints() {
private func addNoSitesViewAndConfigureConstraints(_ noSitesViewController: UIHostingController<NoSitesView>) {
guard let scrollView = noSitesScrollView else {
return
}
Expand Down Expand Up @@ -560,6 +575,7 @@ final class MySiteViewController: UIViewController, UIScrollViewDelegate, NoSite
bottomAnchor: view.safeAreaLayoutGuide.bottomAnchor)

if let blog,
let noSitesViewController,
noSitesViewController.view.superview == nil {
createButtonCoordinator?.showCreateButton(for: blog)
}
Expand Down Expand Up @@ -597,6 +613,16 @@ final class MySiteViewController: UIViewController, UIScrollViewDelegate, NoSite
})
}

private func presentNeedsEmailVerificationAlert() {
let alert = UIAlertController(
title: NSLocalizedString("noSites.verifyEmail.title", value: "Verify Your Email", comment: "Title for email verification alert"),
message: NSLocalizedString("noSites.verifyEmail.message", value: "You need to verify your email to create a new site.", comment: "Message for email verification alert"),
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: SharedStrings.Button.close, style: .cancel, handler: nil))
present(alert, animated: true)
}

@objc
private func showAddSelfHostedSite() {
WordPressAuthenticator.showLoginForSelfHostedSite(self)
Expand Down Expand Up @@ -880,6 +906,14 @@ extension MySiteViewController: BlogDetailsPresentationDelegate {
// MARK: Jetpack Features Removal

private extension MySiteViewController {
@objc func willEnterForeground() {
displayOverlayIfNeeded()

if let account = self.viewModel.defaultAccount {
AccountService(coreDataStack: ContextManager.shared).updateUserDetails(for: account, success: nil, failure: nil)
}
}

@objc func displayOverlayIfNeeded() {
if isViewOnScreen() && !RootViewCoordinator.shared.isSiteCreationActive {
let didReloadUI = RootViewCoordinator.shared.reloadUIIfNeeded(blog: self.blog)
Expand Down
43 changes: 30 additions & 13 deletions WordPress/Classes/ViewRelated/Blog/My Site/NoSitesView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ protocol NoSitesViewDelegate: AnyObject {
}

struct NoSitesView: View {
let addSiteViewModel: AddSiteMenuViewModel
let viewModel: NoSitesViewModel
@ObservedObject var account: WPAccount

let appUIType: RootViewCoordinator.AppUIType?
let onSelection: (AddSiteMenuViewModel.Selection) -> Void
weak var delegate: NoSitesViewDelegate?

@Environment(\.horizontalSizeClass) var horizontalSizeClass
Expand All @@ -19,8 +21,22 @@ struct NoSitesView: View {
emptyStateView
.frame(maxHeight: .infinity, alignment: .center)

if account.verificationStatus != .verified {
Group {
VerifyEmailView(fillVerticalSpace: false)
.background(
RoundedRectangle(cornerRadius: 8)
.fill(.white)
)
.frame(maxWidth: .infinity, alignment: .leading)
}
.padding(.horizontal, .DS.Padding.medium)
.padding(.bottom, .DS.Padding.medium)
}

let viewModel = NoSitesViewModel(appUIType: appUIType, account: account)
if viewModel.isShowingAccountAndSettings, horizontalSizeClass == .compact {
accountAndSettingsButton
accountAndSettingsButton(viewModel: viewModel)
.padding(.horizontal, .DS.Padding.large)
.padding(.bottom, .DS.Padding.medium)
}
Expand All @@ -36,12 +52,13 @@ struct NoSitesView: View {
Text(Strings.description)
} actions: {
VStack(spacing: 20) {
ForEach(addSiteViewModel.actions) { action in
let actions = AddSiteMenuViewModel(onSelection: onSelection).actions
ForEach(actions, id: \.id) { action in
let button = Button(action.title) {
WPAnalytics.track(.mySiteNoSitesViewActionTapped)
action.handler()
}
if action.id == addSiteViewModel.actions.first?.id {
WPAnalytics.track(.mySiteNoSitesViewActionTapped)
action.handler()
}
if action.id == actions.first?.id {
button.buttonStyle(.primary)
} else {
button
Expand All @@ -51,13 +68,13 @@ struct NoSitesView: View {
}
}

private var accountAndSettingsButton: some View {
private func accountAndSettingsButton(viewModel: NoSitesViewModel) -> some View {
Button {
delegate?.didTapAccountAndSettingsButton()
} label: {
HStack(alignment: .center, spacing: .DS.Padding.double) {
makeGravatarIcon(size: 40)
accountAndSettingsStackView
makeGravatarIcon(size: 40, viewModel: viewModel)
accountAndSettingsStackView(viewModel: viewModel)
Spacer()
Image(systemName: "chevron.forward")
.tint(.secondary)
Expand All @@ -69,7 +86,7 @@ struct NoSitesView: View {
}
}

private var accountAndSettingsStackView: some View {
private func accountAndSettingsStackView(viewModel: NoSitesViewModel) -> some View {
VStack(alignment: .leading) {
Text(viewModel.displayName)
.foregroundColor(.primary)
Expand All @@ -84,7 +101,7 @@ struct NoSitesView: View {
}
}

private func makeGravatarIcon(size: CGFloat) -> some View {
private func makeGravatarIcon(size: CGFloat, viewModel: NoSitesViewModel) -> some View {
AsyncImage(url: viewModel.gravatarURL) { phase in
switch phase {
case .success(let image):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ struct AddSiteMenuViewModel {
}

struct Action: Identifiable {
let id = UUID()
var id: Selection {
selection
}

let selection: Selection
let title: String
let handler: () -> Void

Expand All @@ -85,14 +89,14 @@ struct AddSiteMenuViewModel {
let canAddSelfHostedSite = AppConfiguration.showAddSelfHostedSiteButton

var actions: [Action] = []
if defaultAccount != nil {
actions.append(Action(title: Strings.createDotComSite) {
if let defaultAccount {
actions.append(Action(selection: .dotCom, title: Strings.createDotComSite) {
onSelection(.dotCom)
})
}

if canAddSelfHostedSite {
actions.append(Action(title: Strings.addSelfHostedSite) {
actions.append(Action(selection: .selfHosted, title: Strings.addSelfHostedSite) {
onSelection(.selfHosted)
})
}
Expand Down
22 changes: 18 additions & 4 deletions WordPress/Classes/ViewRelated/Me/Me Main/MeViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class MeViewController: UITableViewController {
}

ImmuTable.registerRows([
VerifyEmailRow.self,
NavigationItemRow.self,
IndicatorNavigationItemRow.self,
ButtonRow.self,
Expand All @@ -47,6 +48,7 @@ class MeViewController: UITableViewController {
WPStyleGuide.configureAutomaticHeightRows(for: tableView)

NotificationCenter.default.addObserver(self, selector: #selector(MeViewController.accountDidChange), name: NSNotification.Name.WPAccountDefaultWordPressComAccountChanged, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(MeViewController.refreshAccountDetailsAndSettings), name: UIApplication.didBecomeActiveNotification, object: nil)

WPStyleGuide.configureColors(view: view, tableView: tableView)
tableView.accessibilityIdentifier = "Me Table"
Expand Down Expand Up @@ -114,9 +116,13 @@ class MeViewController: UITableViewController {

fileprivate func tableViewModel(with account: WPAccount?) -> ImmuTable {
let accessoryType: UITableViewCell.AccessoryType = .disclosureIndicator

let loggedIn = account != nil

var verificationSection: ImmuTableSection?
if let account, account.verificationStatus == .unverified {
verificationSection = ImmuTableSection(rows: [VerifyEmailRow()])
}

let myProfile = NavigationItemRow(
title: RowTitles.myProfile,
icon: UIImage(named: "site-menu-people")?.withRenderingMode(.alwaysTemplate),
Expand Down Expand Up @@ -162,7 +168,14 @@ class MeViewController: UITableViewController {

let shouldShowQRLoginRow = AppConfiguration.qrLoginEnabled && !(account?.settings?.twoStepEnabled ?? false)

var sections: [ImmuTableSection] = [
var sections: [ImmuTableSection] = []

// Add verification section first if it exists
if let verificationSection {
sections.append(verificationSection)
}

sections.append(contentsOf: [
ImmuTableSection(rows: {
var rows: [ImmuTableRow] = [appSettingsRow]
if loggedIn {
Expand All @@ -176,7 +189,7 @@ class MeViewController: UITableViewController {
return rows
}()),
ImmuTableSection(rows: [helpAndSupportIndicator]),
]
])

#if IS_JETPACK
if RemoteFeatureFlag.domainManagement.enabled() && loggedIn && !isSidebarModeEnabled {
Expand Down Expand Up @@ -405,6 +418,7 @@ class MeViewController: UITableViewController {
}
return false
}

guard let sections = handler?.viewModel.sections,
let section = sections.firstIndex(where: { $0.rows.contains(where: matchRow) }),
let row = sections[section].rows.firstIndex(where: matchRow) else {
Expand Down Expand Up @@ -432,7 +446,7 @@ class MeViewController: UITableViewController {
return try? WPAccount.lookupDefaultWordPressComAccount(in: ContextManager.shared.mainContext)
}

fileprivate func refreshAccountDetailsAndSettings() {
@objc fileprivate func refreshAccountDetailsAndSettings() {
guard let account = defaultAccount(), let api = account.wordPressComRestApi else {
reloadViewModel()
return
Expand Down
Loading