diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 06d6073cb3ab..9eef518266f9 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -3,7 +3,6 @@ * [**] [internal] Refactor updating account related Core Data operations, which ususally happens during log in and out of the app. [#20394] * [***] [internal] Refactor uploading photos (from the device photo, the Free Photo library, and other sources) to the WordPress Media Library. Affected areas are where you can choose a photo and upload, including the "Media" screen, adding images to a post, updating site icon, etc. [#20322] * [**] [WordPress-only] Warns user about sites with only individual plugins not supporting core app features and offers the option to switch to the Jetpack app. [#20408] -* [**] Add a "Personalize Home Tab" button to the bottom of the Home tab that allows changing cards visibility. [#20369] 22.0 ----- diff --git a/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift b/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift index 4a012e9dfdb2..5a094b521e41 100644 --- a/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift +++ b/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift @@ -329,6 +329,8 @@ import Foundation // My Site Dashboard case dashboardCardShown case dashboardCardItemTapped + case dashboardCardContextualMenuAccessed + case dashboardCardHideTapped case mySiteTabTapped case mySiteSiteMenuShown case mySiteDashboardShown @@ -1033,6 +1035,10 @@ import Foundation return "my_site_dashboard_card_shown" case .dashboardCardItemTapped: return "my_site_dashboard_card_item_tapped" + case .dashboardCardContextualMenuAccessed: + return "my_site_dashboard_contextual_menu_accessed" + case .dashboardCardHideTapped: + return "my_site_dashboard_card_hide_tapped" case .mySiteTabTapped: return "my_site_tab_tapped" case .mySiteSiteMenuShown: diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Blaze/BlazeCardView.swift b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Blaze/BlazeCardView.swift index 24c48cbae22e..84a6dd156149 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Blaze/BlazeCardView.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Blaze/BlazeCardView.swift @@ -126,7 +126,7 @@ extension BlazeCardView { comment: "Description for the Blaze dashboard card.") static let hideThis = NSLocalizedString("blaze.dashboard.card.menu.hide", value: "Hide this", - comment: "Title for a menu action in the context menu on the Jetpack install card.") + comment: "Title for a menu action in the context menu on the Blaze card.") } } diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Blaze/DashboardBlazeCardCell.swift b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Blaze/DashboardBlazeCardCell.swift index fac42f823c48..9f286cb096f6 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Blaze/DashboardBlazeCardCell.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Blaze/DashboardBlazeCardCell.swift @@ -19,10 +19,12 @@ class DashboardBlazeCardCell: DashboardCollectionViewCell { } let onEllipsisTap: () -> Void = { [weak self] in + BlogDashboardAnalytics.trackContextualMenuAccessed(for: .blaze) BlazeEventsTracker.trackContextualMenuAccessed(for: .dashboardCard) } let onHideThisTap: UIActionHandler = { [weak self] _ in + BlogDashboardAnalytics.trackHideTapped(for: .blaze) BlazeEventsTracker.trackHideThisTapped(for: .dashboardCard) BlazeHelper.hideBlazeCard(for: self?.blog) } diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Posts/BlogDashboardCardFrameView.swift b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Posts/BlogDashboardCardFrameView.swift index 90518f15ded9..542c39799b8c 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Posts/BlogDashboardCardFrameView.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Posts/BlogDashboardCardFrameView.swift @@ -25,16 +25,6 @@ class BlogDashboardCardFrameView: UIView { return topStackView }() - /// Card's icon image view - private lazy var iconImageView: UIImageView = { - let iconImageView = UIImageView(image: UIImage.gridicon(.posts, size: Constants.iconSize).withRenderingMode(.alwaysTemplate)) - iconImageView.tintColor = .label - iconImageView.frame = CGRect(x: 0, y: 0, width: Constants.iconSize.width, height: Constants.iconSize.height) - iconImageView.setContentHuggingPriority(.defaultHigh, for: .horizontal) - iconImageView.isAccessibilityElement = false - return iconImageView - }() - /// Card's title private lazy var titleLabel: UILabel = { let titleLabel = UILabel() @@ -46,24 +36,12 @@ class BlogDashboardCardFrameView: UIView { return titleLabel }() - /// Chevron displayed in case there's any action associated - private lazy var chevronImageView: UIImageView = { - let chevronImage = UIImage.gridicon(.chevronRight, size: Constants.iconSize).withRenderingMode(.alwaysTemplate) - let chevronImageView = UIImageView(image: chevronImage.imageFlippedForRightToLeftLayoutDirection()) - chevronImageView.frame = CGRect(x: 0, y: 0, width: Constants.iconSize.width, height: Constants.iconSize.height) - chevronImageView.tintColor = .listIcon - chevronImageView.setContentHuggingPriority(.defaultHigh, for: .horizontal) - chevronImageView.isAccessibilityElement = false - chevronImageView.isHidden = true - chevronImageView.setContentHuggingPriority(.defaultHigh, for: .horizontal) - return chevronImageView - }() - /// Ellipsis Button displayed on the top right corner of the view. /// Displayed only when an associated action is set private(set) lazy var ellipsisButton: UIButton = { let button = UIButton(type: .custom) - button.setImage(UIImage.gridicon(.ellipsis).imageWithTintColor(.listIcon), for: .normal) + button.setImage(UIImage(named: "more-horizontal-mobile"), for: .normal) + button.tintColor = UIColor.listIcon button.contentEdgeInsets = Constants.ellipsisButtonPadding button.isAccessibilityElement = true button.accessibilityLabel = Strings.ellipsisButtonAccessibilityLabel @@ -104,19 +82,10 @@ class BlogDashboardCardFrameView: UIView { } } - /// The icon to be displayed at the header - var icon: UIImage? { - didSet { - iconImageView.image = icon?.withRenderingMode(.alwaysTemplate) - iconImageView.isHidden = icon == nil - } - } - /// Closure to be called when anywhere in the view is tapped. /// If set, the chevron image is displayed. var onViewTap: (() -> Void)? { didSet { - updateChevronImageState() addViewTapGestureIfNeeded() } } @@ -126,10 +95,8 @@ class BlogDashboardCardFrameView: UIView { /// If set, the chevron image is displayed. var onHeaderTap: (() -> Void)? { didSet { - updateChevronImageState() addHeaderTapGestureIfNeeded() } - } /// Closure to be called when the ellipsis button is tapped.. @@ -163,11 +130,6 @@ class BlogDashboardCardFrameView: UIView { } } - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - updateColors() - } - /// Add a subview inside the card frame func add(subview: UIView) { mainStackView.addArrangedSubview(subview) @@ -179,7 +141,7 @@ class BlogDashboardCardFrameView: UIView { headerStackView.isHidden = true buttonContainerStackView.isHidden = false - if !ellipsisButton.isHidden || !chevronImageView.isHidden { + if !ellipsisButton.isHidden { mainStackViewTrailingConstraint?.constant = -Constants.mainStackViewTrailingPadding } } @@ -228,34 +190,17 @@ class BlogDashboardCardFrameView: UIView { buttonContainerStackView.removeFromSuperview() } - private func updateColors() { - ellipsisButton.setImage(UIImage.gridicon(.ellipsis).imageWithTintColor(.listIcon), for: .normal) - } - - private func updateChevronImageState() { - chevronImageView.isHidden = onViewTap == nil && onHeaderTap == nil - assertOnTapRecognitionCorrectUsage() - } - private func updateEllipsisButtonState() { ellipsisButton.isHidden = onEllipsisButtonTap == nil let headerPadding = ellipsisButton.isHidden ? Constants.headerPaddingWithEllipsisButtonHidden : Constants.headerPaddingWithEllipsisButtonShown headerStackView.layoutMargins = headerPadding - assertOnTapRecognitionCorrectUsage() - } - - /// Only one of two types of action should be associated with the card. - /// Either ellipsis button tap, or view/header tap - private func assertOnTapRecognitionCorrectUsage() { - let bothTypesUsed = (onViewTap != nil || onHeaderTap != nil) && onEllipsisButtonTap != nil - assert(!bothTypesUsed, "Using onViewTap or onHeaderTap alongside onEllipsisButtonTap is not supported and will result in unexpected behavior.") } private func addHeaderTapGestureIfNeeded() { // Reset any previously added gesture recognizers - headerStackView.gestureRecognizers?.forEach {headerStackView.removeGestureRecognizer($0)} + headerStackView.gestureRecognizers?.forEach { headerStackView.removeGestureRecognizer($0) } // Add gesture recognizer if needed if onHeaderTap != nil { @@ -266,7 +211,7 @@ class BlogDashboardCardFrameView: UIView { private func addViewTapGestureIfNeeded() { // Reset any previously added gesture recognizers - self.gestureRecognizers?.forEach {self.removeGestureRecognizer($0)} + self.gestureRecognizers?.forEach { self.removeGestureRecognizer($0) } // Add gesture recognizer if needed if onViewTap != nil { diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Posts/DashboardPostsListCardCell.swift b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Posts/DashboardPostsListCardCell.swift index 2dc355cd48df..0f8127af752d 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Posts/DashboardPostsListCardCell.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Posts/DashboardPostsListCardCell.swift @@ -16,7 +16,7 @@ class DashboardPostsListCardCell: UICollectionViewCell, Reusable { // MARK: Views - private var frameView: BlogDashboardCardFrameView? + private let frameView = BlogDashboardCardFrameView() lazy var tableView: UITableView = { let tableView = PostCardTableView() @@ -68,14 +68,9 @@ class DashboardPostsListCardCell: UICollectionViewCell, Reusable { } private func addSubviews() { - let frameView = BlogDashboardCardFrameView() - frameView.icon = UIImage.gridicon(.posts, size: Constants.iconSize) frameView.translatesAutoresizingMaskIntoConstraints = false - frameView.add(subview: tableView) - self.frameView = frameView - contentView.addSubview(frameView) contentView.pinSubviewToAllEdges(frameView, priority: Constants.constraintPriority) } @@ -104,23 +99,37 @@ extension DashboardPostsListCardCell { assertionFailure("Cell used with wrong card type") return } + addContextMenu(card: cardType, blog: blog) + viewModel = PostsCardViewModel(blog: blog, status: status, view: self) viewModel?.viewDidLoad() tableView.dataSource = viewModel?.diffableDataSource viewModel?.refresh() } + private func addContextMenu(card: DashboardCard, blog: Blog) { + guard FeatureFlag.personalizeHomeTab.enabled else { return } + + frameView.onEllipsisButtonTap = { + BlogDashboardAnalytics.trackContextualMenuAccessed(for: card) + } + frameView.ellipsisButton.showsMenuAsPrimaryAction = true + frameView.ellipsisButton.menu = UIMenu(title: "", options: .displayInline, children: [ + BlogDashboardHelpers.makeHideCardAction(for: card, siteID: blog.dotComID?.intValue ?? 0) + ]) + } + private func configureDraftsList(blog: Blog) { - frameView?.title = Strings.draftsTitle - frameView?.titleHint = Strings.draftsTitleHint - frameView?.onHeaderTap = { [weak self] in + frameView.title = Strings.draftsTitle + frameView.titleHint = Strings.draftsTitleHint + frameView.onHeaderTap = { [weak self] in self?.presentPostList(with: .draft) } } private func configureScheduledList(blog: Blog) { - frameView?.title = Strings.scheduledTitle - frameView?.onHeaderTap = { [weak self] in + frameView.title = Strings.scheduledTitle + frameView.onHeaderTap = { [weak self] in self?.presentPostList(with: .scheduled) } } diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Prompts/DashboardPromptsCardCell.swift b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Prompts/DashboardPromptsCardCell.swift index 6a59bc919863..0442d14e52f6 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Prompts/DashboardPromptsCardCell.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Prompts/DashboardPromptsCardCell.swift @@ -11,12 +11,13 @@ class DashboardPromptsCardCell: UICollectionViewCell, Reusable { let frameView = BlogDashboardCardFrameView() frameView.translatesAutoresizingMaskIntoConstraints = false frameView.title = Strings.cardFrameTitle - frameView.icon = Style.frameIconImage // NOTE: Remove the logic when support for iOS 14 is dropped if #available (iOS 15.0, *) { // assign an empty closure so the button appears. - frameView.onEllipsisButtonTap = {} + frameView.onEllipsisButtonTap = { + BlogDashboardAnalytics.trackContextualMenuAccessed(for: .prompts) + } frameView.ellipsisButton.showsMenuAsPrimaryAction = true frameView.ellipsisButton.menu = contextMenu } else { @@ -24,6 +25,7 @@ class DashboardPromptsCardCell: UICollectionViewCell, Reusable { // iOS 13 doesn't support showing UIMenu programmatically. // iOS 14 doesn't support `UIDeferredMenuElement.uncached`. frameView.onEllipsisButtonTap = { [weak self] in + BlogDashboardAnalytics.trackContextualMenuAccessed(for: .prompts) self?.showMenuSheet() } } @@ -505,6 +507,7 @@ private extension DashboardPromptsCardCell { return } WPAnalytics.track(.promptsDashboardCardMenuRemove) + BlogDashboardAnalytics.trackHideTapped(for: .prompts) let service = BlogDashboardPersonalizationService(siteID: siteID) service.setEnabled(false, for: .prompts) let notice = Notice(title: Strings.promptRemovedTitle, message: Strings.promptRemovedSubtitle, feedbackType: .success, actionTitle: Strings.undoSkipTitle) { _ in diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Quick Start/DashboardQuickStartCardCell.swift b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Quick Start/DashboardQuickStartCardCell.swift index 38a689f9bb7b..fdc9eafeed2f 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Quick Start/DashboardQuickStartCardCell.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Quick Start/DashboardQuickStartCardCell.swift @@ -51,7 +51,6 @@ final class DashboardQuickStartCardCell: UICollectionViewCell, Reusable, BlogDas fallthrough case .newSite: - cardFrameView.icon = UIImage.gridicon(.listOrdered, size: Metrics.iconSize) configureOnEllipsisButtonTap(sourceRect: cardFrameView.ellipsisButton.frame) cardFrameView.showHeader() diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Stats/DashboardStatsCardCell.swift b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Stats/DashboardStatsCardCell.swift index 9d4e54f731f3..0b371d238a24 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Stats/DashboardStatsCardCell.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Stats/DashboardStatsCardCell.swift @@ -6,7 +6,7 @@ class DashboardStatsCardCell: UICollectionViewCell, Reusable { // MARK: Private Variables private var viewModel: DashboardStatsViewModel? - private var frameView: BlogDashboardCardFrameView? + private let frameView = BlogDashboardCardFrameView() private var nudgeView: DashboardStatsNudgeView? private var statsStackView: DashboardStatsStackView? @@ -39,11 +39,8 @@ class DashboardStatsCardCell: UICollectionViewCell, Reusable { } private func addSubviews() { - let frameView = BlogDashboardCardFrameView() frameView.title = Strings.statsTitle frameView.titleHint = Strings.statsTitleHint - frameView.icon = UIImage.gridicon(.statsAlt, size: Constants.iconSize) - self.frameView = frameView let statsStackview = DashboardStatsStackView() frameView.add(subview: statsStackview) @@ -74,11 +71,20 @@ extension DashboardStatsCardCell: BlogDashboardCardConfigurable { } private func configureCard(for blog: Blog, in viewController: UIViewController) { - - frameView?.onViewTap = { [weak self] in + frameView.onViewTap = { [weak self] in self?.showStats(for: blog, from: viewController) } + if FeatureFlag.personalizeHomeTab.enabled { + frameView.onEllipsisButtonTap = { + BlogDashboardAnalytics.trackContextualMenuAccessed(for: .todaysStats) + } + frameView.ellipsisButton.showsMenuAsPrimaryAction = true + frameView.ellipsisButton.menu = UIMenu(title: "", options: .displayInline, children: [ + BlogDashboardHelpers.makeHideCardAction(for: .todaysStats, siteID: blog.dotComID?.intValue ?? 0) + ]) + } + statsStackView?.views = viewModel?.todaysViews statsStackView?.visitors = viewModel?.todaysVisitors statsStackView?.likes = viewModel?.todaysLikes diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Helpers/BlogDashboardAnalytics.swift b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Helpers/BlogDashboardAnalytics.swift index 53078b8fa6da..b3e90f6d86bc 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Helpers/BlogDashboardAnalytics.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Helpers/BlogDashboardAnalytics.swift @@ -30,4 +30,12 @@ class BlogDashboardAnalytics { } } } + + static func trackContextualMenuAccessed(for card: DashboardCard) { + WPAnalytics.track(.dashboardCardContextualMenuAccessed, properties: ["card": card.rawValue]) + } + + static func trackHideTapped(for card: DashboardCard) { + WPAnalytics.track(.dashboardCardHideTapped, properties: ["card": card.rawValue]) + } } diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Helpers/BlogDashboardHelpers.swift b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Helpers/BlogDashboardHelpers.swift new file mode 100644 index 000000000000..beb295506968 --- /dev/null +++ b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Helpers/BlogDashboardHelpers.swift @@ -0,0 +1,19 @@ +import Foundation + +struct BlogDashboardHelpers { + static func makeHideCardAction(for card: DashboardCard, siteID: Int) -> UIAction { + UIAction( + title: Strings.hideThis, + image: UIImage(systemName: "minus.circle"), + attributes: [.destructive], + handler: { _ in + BlogDashboardAnalytics.trackHideTapped(for: card) + BlogDashboardPersonalizationService(siteID: siteID) + .setEnabled(false, for: card) + }) + } + + private enum Strings { + static let hideThis = NSLocalizedString("blogDashboard.contextMenu.hideThis", value: "Hide this", comment: "Title for the context menu action that hides the dashboard card.") + } +} diff --git a/WordPress/Classes/ViewRelated/Jetpack/Install/View/JetpackRemoteInstallCardView.swift b/WordPress/Classes/ViewRelated/Jetpack/Install/View/JetpackRemoteInstallCardView.swift index 7c177bc2c21e..6260de7578f3 100644 --- a/WordPress/Classes/ViewRelated/Jetpack/Install/View/JetpackRemoteInstallCardView.swift +++ b/WordPress/Classes/ViewRelated/Jetpack/Install/View/JetpackRemoteInstallCardView.swift @@ -72,7 +72,6 @@ class JetpackRemoteInstallCardView: UIView { private lazy var cardFrameView: BlogDashboardCardFrameView = { let frameView = BlogDashboardCardFrameView() frameView.translatesAutoresizingMaskIntoConstraints = false - frameView.icon = .none frameView.onEllipsisButtonTap = {} frameView.ellipsisButton.showsMenuAsPrimaryAction = true frameView.ellipsisButton.menu = contextMenu diff --git a/WordPress/Resources/AppImages.xcassets/more-horizontal-mobile.imageset/Contents.json b/WordPress/Resources/AppImages.xcassets/more-horizontal-mobile.imageset/Contents.json new file mode 100644 index 000000000000..08a009949956 --- /dev/null +++ b/WordPress/Resources/AppImages.xcassets/more-horizontal-mobile.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "more-horizontal-mobile.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/WordPress/Resources/AppImages.xcassets/more-horizontal-mobile.imageset/more-horizontal-mobile.svg b/WordPress/Resources/AppImages.xcassets/more-horizontal-mobile.imageset/more-horizontal-mobile.svg new file mode 100644 index 000000000000..f8af22821782 --- /dev/null +++ b/WordPress/Resources/AppImages.xcassets/more-horizontal-mobile.imageset/more-horizontal-mobile.svg @@ -0,0 +1,3 @@ + + + diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index a4628f94790d..c70f5af44ce1 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -13559,6 +13559,7 @@ isa = PBXGroup; children = ( 8B15D27328009EBF0076628A /* BlogDashboardAnalytics.swift */, + 0C35FFF029CB81F700D224EB /* BlogDashboardHelpers.swift */, 8BC81D6427CFC0DA0057F790 /* BlogDashboardState.swift */, 80EF9285280D272E0064A971 /* DashboardPostsSyncManager.swift */, ); @@ -21336,6 +21337,7 @@ B538F3891EF46EC8001003D5 /* UnknownEditorViewController.swift in Sources */, 937D9A1119F838C2007B9D5F /* AccountToAccount22to23.swift in Sources */, 400A2C772217A8A0000A8A59 /* VisitsSummaryStatsRecordValue+CoreDataClass.swift in Sources */, + 0C35FFF129CB81F700D224EB /* BlogDashboardHelpers.swift in Sources */, D8212CB320AA6861008E8AE8 /* ReaderFollowAction.swift in Sources */, F5D399302541F25B0058D0AB /* SheetActions.swift in Sources */, 93F7214F271831820021A09F /* SiteStatsPinnedItemStore.swift in Sources */, @@ -24972,6 +24974,7 @@ FABB256B2602FC2C00C8785C /* InteractivePostView.swift in Sources */, FABB256C2602FC2C00C8785C /* LinearGradientView.swift in Sources */, FABB256D2602FC2C00C8785C /* WizardStep.swift in Sources */, + 0C35FFF229CB81F700D224EB /* BlogDashboardHelpers.swift in Sources */, C395FB242821FE4B00AE7C11 /* SiteDesignSection.swift in Sources */, 837B49D8283C2AE80061A657 /* BloggingPromptSettings+CoreDataClass.swift in Sources */, FABB256E2602FC2C00C8785C /* PostPostViewController.swift in Sources */,