Skip to content

Commit

Permalink
Add empty state view on dashboard (#20385)
Browse files Browse the repository at this point in the history
  • Loading branch information
kean authored Mar 31, 2023
1 parent cf2aab8 commit 10cc23e
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import UIKit

final class BlogDashboardEmptyStateCell: DashboardCollectionViewCell {
// MARK: - Initializers

override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// MARK: - View setup

private func setupView() {
let titleLabel = UILabel()
titleLabel.font = WPStyleGuide.fontForTextStyle(.title2, fontWeight: .regular)
titleLabel.adjustsFontForContentSizeCategory = true
titleLabel.text = Strings.title

let subtitleLabel = UILabel()
subtitleLabel.font = WPStyleGuide.fontForTextStyle(.callout, fontWeight: .regular)
subtitleLabel.adjustsFontForContentSizeCategory = true
subtitleLabel.textColor = .secondaryLabel
subtitleLabel.numberOfLines = 0
subtitleLabel.textAlignment = .center
subtitleLabel.text = Strings.subtitle
subtitleLabel.translatesAutoresizingMaskIntoConstraints = false
subtitleLabel.widthAnchor.constraint(lessThanOrEqualToConstant: 320).isActive = true

let stack = UIStackView(arrangedSubviews: [titleLabel, subtitleLabel])
stack.axis = .vertical
stack.alignment = .center
stack.spacing = 12
stack.translatesAutoresizingMaskIntoConstraints = false

contentView.addSubview(stack)
contentView.pinSubviewToAllEdges(stack, insets: .init(top: 52, left: 16, bottom: 4, right: 16))
}

// MARK: - BlogDashboardCardConfigurable

func configure(blog: Blog, viewController: BlogDashboardViewController?, apiResponse: BlogDashboardRemoteEntity?) {
// Do nothing
}
}

private extension BlogDashboardEmptyStateCell {
enum Strings {
static let title = NSLocalizedString("dasboard.emptyView.title", value: "No cards to display", comment: "Title for an empty state view when no cards are displayed")
static let subtitle = NSLocalizedString("dasboard.emptyView.subtitle", value: "Add cards that fit your needs to see information about your site.", comment: "Title for an empty state view when no cards are displayed")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ enum DashboardCard: String, CaseIterable {
/// Card placeholder for when loading data
case ghost
case failure
/// Empty state when no cards are present
case empty
/// A "Personalize Home Tab" button
case personalize

Expand Down Expand Up @@ -52,6 +54,8 @@ enum DashboardCard: String, CaseIterable {
return DashboardBlazeCardCell.self
case .domainsDashboardCard:
return DashboardDomainsCardCell.self
case .empty:
return BlogDashboardEmptyStateCell.self
case .personalize:
return BlogDashboardPersonalizeCardCell.self
}
Expand Down Expand Up @@ -90,8 +94,10 @@ enum DashboardCard: String, CaseIterable {
return BlazeHelper.shouldShowCard(for: blog)
case .domainsDashboardCard:
return DomainsDashboardCardHelper.shouldShowCard(for: blog)
case .empty:
return false // Controlled manually based on other cards visibility
case .personalize:
return AppConfiguration.isJetpack && FeatureFlag.personalizeHomeTab.enabled
return FeatureFlag.personalizeHomeTab.enabled
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ private func makeKey(for card: DashboardCard) -> String? {
return "prompts-enabled-site-settings"
case .domainsDashboardCard:
return "domains-dashboard-card-enabled-site-settings"
case .quickStart, .jetpackBadge, .jetpackInstall, .nextPost, .createPost, .failure, .ghost, .personalize:
case .quickStart, .jetpackBadge, .jetpackInstall, .nextPost, .createPost, .failure, .ghost, .personalize, .empty:
return nil
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,17 @@ private extension BlogDashboardService {

func parse(_ entity: BlogDashboardRemoteEntity?, blog: Blog, dotComID: Int) -> [DashboardCardModel] {
let personalizationService = BlogDashboardPersonalizationService(repository: repository, siteID: dotComID)
return DashboardCard.allCases.compactMap { card in
var cards: [DashboardCardModel] = DashboardCard.allCases.compactMap { card in
guard personalizationService.isEnabled(card),
card.shouldShow(for: blog, apiResponse: entity) else {
return nil
}
return DashboardCardModel(cardType: card, dotComID: dotComID, entity: entity)
}
if cards.isEmpty || cards.map(\.cardType) == [.personalize] {
cards.insert(DashboardCardModel(cardType: .empty, dotComID: dotComID), at: 0)
}
return cards
}

func decode(_ cardsDictionary: NSDictionary, blog: Blog) -> BlogDashboardRemoteEntity? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ private extension DashboardCard {
return NSLocalizedString("personalizeHome.dashboardCard.draftPosts", value: "Draft posts", comment: "Card title for the pesonalization menu")
case .scheduledPosts:
return NSLocalizedString("personalizeHome.dashboardCard.scheduledPosts", value: "Scheduled posts", comment: "Card title for the pesonalization menu")
case .quickStart, .nextPost, .createPost, .ghost, .failure, .personalize, .jetpackBadge, .jetpackInstall, .domainsDashboardCard:
case .quickStart, .nextPost, .createPost, .ghost, .failure, .personalize, .jetpackBadge, .jetpackInstall, .domainsDashboardCard, .empty:
assertionFailure("\(self) card should not appear in the personalization menus")
return "" // These cards don't appear in the personalization menus
}
Expand Down
11 changes: 11 additions & 0 deletions WordPress/WordPress.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,11 @@
0A9610FA28B2E56300076EBA /* UserSuggestion+Comparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9610F828B2E56300076EBA /* UserSuggestion+Comparable.swift */; };
0A9687BC28B40771009DCD2F /* FullScreenCommentReplyViewModelMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A9687BB28B40771009DCD2F /* FullScreenCommentReplyViewModelMock.swift */; };
0C35FFF429CBA6DA00D224EB /* BlogDashboardPersonalizationViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C35FFF329CBA6DA00D224EB /* BlogDashboardPersonalizationViewModelTests.swift */; };
0C35FFF129CB81F700D224EB /* BlogDashboardHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C35FFF029CB81F700D224EB /* BlogDashboardHelpers.swift */; };
0C35FFF229CB81F700D224EB /* BlogDashboardHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C35FFF029CB81F700D224EB /* BlogDashboardHelpers.swift */; };
0C35FFF429CBA6DA00D224EB /* BlogDashboardPersonalizationViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C35FFF329CBA6DA00D224EB /* BlogDashboardPersonalizationViewModelTests.swift */; };
0C35FFF629CBB5DE00D224EB /* BlogDashboardEmptyStateCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C35FFF529CBB5DE00D224EB /* BlogDashboardEmptyStateCell.swift */; };
0C35FFF729CBB5DE00D224EB /* BlogDashboardEmptyStateCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C35FFF529CBB5DE00D224EB /* BlogDashboardEmptyStateCell.swift */; };
0CB4056B29C78F06008EED0A /* BlogDashboardPersonalizationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB4056A29C78F06008EED0A /* BlogDashboardPersonalizationService.swift */; };
0CB4056C29C78F06008EED0A /* BlogDashboardPersonalizationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB4056A29C78F06008EED0A /* BlogDashboardPersonalizationService.swift */; };
0CB4056E29C7BA63008EED0A /* BlogDashboardPersonalizationServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CB4056D29C7BA63008EED0A /* BlogDashboardPersonalizationServiceTests.swift */; };
Expand Down Expand Up @@ -5924,6 +5929,9 @@
0A9610F828B2E56300076EBA /* UserSuggestion+Comparable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserSuggestion+Comparable.swift"; sourceTree = "<group>"; };
0A9687BB28B40771009DCD2F /* FullScreenCommentReplyViewModelMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenCommentReplyViewModelMock.swift; sourceTree = "<group>"; };
0C35FFF329CBA6DA00D224EB /* BlogDashboardPersonalizationViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlogDashboardPersonalizationViewModelTests.swift; sourceTree = "<group>"; };
0C35FFF029CB81F700D224EB /* BlogDashboardHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlogDashboardHelpers.swift; sourceTree = "<group>"; };
0C35FFF329CBA6DA00D224EB /* BlogDashboardPersonalizationViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlogDashboardPersonalizationViewModelTests.swift; sourceTree = "<group>"; };
0C35FFF529CBB5DE00D224EB /* BlogDashboardEmptyStateCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlogDashboardEmptyStateCell.swift; sourceTree = "<group>"; };
0CB4056A29C78F06008EED0A /* BlogDashboardPersonalizationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlogDashboardPersonalizationService.swift; sourceTree = "<group>"; };
0CB4056D29C7BA63008EED0A /* BlogDashboardPersonalizationServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlogDashboardPersonalizationServiceTests.swift; sourceTree = "<group>"; };
0CB4057029C8DCF4008EED0A /* BlogDashboardPersonalizationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlogDashboardPersonalizationViewModel.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -13430,6 +13438,7 @@
8B4DDF24278F3AF60022494D /* Posts */,
83796698299C048E004A92B9 /* DashboardJetpackInstallCardCell.swift */,
0CB4057B29C8DEE1008EED0A /* BlogDashboardPersonalizeCardCell.swift */,
0C35FFF529CBB5DE00D224EB /* BlogDashboardEmptyStateCell.swift */,
);
path = Cards;
sourceTree = "<group>";
Expand Down Expand Up @@ -21846,6 +21855,7 @@
7E987F562108017B00CAFB88 /* NotificationContentRouter.swift in Sources */,
46E327D124E705C7000944B3 /* PageLayoutService.swift in Sources */,
F574416E242569CA00E150A8 /* Route+Page.swift in Sources */,
0C35FFF629CBB5DE00D224EB /* BlogDashboardEmptyStateCell.swift in Sources */,
177076211EA206C000705A4A /* PlayIconView.swift in Sources */,
57BAD50C225CCE1A006139EC /* WPTabBarController+Swift.swift in Sources */,
E1468DE71E794A4D0044D80F /* LanguageSelectorViewController.swift in Sources */,
Expand Down Expand Up @@ -23804,6 +23814,7 @@
FABB21FE2602FC2C00C8785C /* WPLogger.m in Sources */,
FABB21FF2602FC2C00C8785C /* JetpackBackupStatusCoordinator.swift in Sources */,
3F720C222889B65B00519938 /* JetpackBrandingVisibility.swift in Sources */,
0C35FFF729CBB5DE00D224EB /* BlogDashboardEmptyStateCell.swift in Sources */,
FABB22012602FC2C00C8785C /* RevisionDiff.swift in Sources */,
FABB22022602FC2C00C8785C /* MediaThumbnailCoordinator.swift in Sources */,
FABB22032602FC2C00C8785C /* RevisionDiffsPageManager.swift in Sources */,
Expand Down
25 changes: 25 additions & 0 deletions WordPress/WordPressTest/Dashboard/BlogDashboardServiceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class BlogDashboardServiceTests: CoreDataTestCase {
private var persistenceMock: BlogDashboardPersistenceMock!
private var repositoryMock: InMemoryUserDefaults!
private var postsParserMock: BlogDashboardPostsParserMock!
private let featureFlags = FeatureFlagOverrideStore()

private let wpComID = 123456

Expand All @@ -22,11 +23,15 @@ class BlogDashboardServiceTests: CoreDataTestCase {
repositoryMock = InMemoryUserDefaults()
postsParserMock = BlogDashboardPostsParserMock(managedObjectContext: mainContext)
service = BlogDashboardService(managedObjectContext: mainContext, remoteService: remoteServiceMock, persistence: persistenceMock, repository: repositoryMock, postsParser: postsParserMock)

try? featureFlags.override(FeatureFlag.personalizeHomeTab, withValue: true)
}

override func tearDown() {
super.tearDown()
context = nil

try? featureFlags.override(FeatureFlag.personalizeHomeTab, withValue: FeatureFlag.personalizeHomeTab.originalValue)
}

func testCallServiceWithCorrectIDAndCards() {
Expand Down Expand Up @@ -124,6 +129,26 @@ class BlogDashboardServiceTests: CoreDataTestCase {
waitForExpectations(timeout: 3, handler: nil)
}

func testThatWhenAllCardsAreHiddenEmptyStateIsShown() {
// Given
let personalizationService = BlogDashboardPersonalizationService(repository: repositoryMock, siteID: wpComID)
for card in DashboardCard.personalizableCards {
personalizationService.setEnabled(false, for: card)
}

// When
let expect = expectation(description: "Cards parsed")
remoteServiceMock.respondWith = .withDraftAndSchedulePosts

let blog = newTestBlog(id: wpComID, context: mainContext)
service.fetch(blog: blog) { cards in
// Then empty state is shown
XCTAssertEqual(cards.map(\.cardType), [.empty, .personalize])
expect.fulfill()
}
waitForExpectations(timeout: 3, handler: nil)
}

func testThatPreferencesAreSavedPerSite() {
// When the stats card is hidden for a different site
BlogDashboardPersonalizationService(repository: repositoryMock, siteID: wpComID + 1)
Expand Down

0 comments on commit 10cc23e

Please sign in to comment.