diff --git a/Tests/KeystoneTests/Tests/Jetpack/JetpackBrandingMenuCardPresenterTests.swift b/Tests/KeystoneTests/Tests/Jetpack/JetpackBrandingMenuCardPresenterTests.swift index ab1237e3e213..a1b1859e46bf 100644 --- a/Tests/KeystoneTests/Tests/Jetpack/JetpackBrandingMenuCardPresenterTests.swift +++ b/Tests/KeystoneTests/Tests/Jetpack/JetpackBrandingMenuCardPresenterTests.swift @@ -60,7 +60,6 @@ final class JetpackBrandingMenuCardPresenterTests: CoreDataTestCase { config.description, "Unlock your site’s full potential. Get Stats, Reader, Notifications and more with Jetpack." ) - XCTAssertEqual(config.type, .expanded) } func testNoCardConfigOutsideSelfHostedPhase() { 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 0ca553a27860..e81e687b7a2a 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Posts/BlogDashboardCardFrameView.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Posts/BlogDashboardCardFrameView.swift @@ -129,7 +129,12 @@ class BlogDashboardCardFrameView: UIView { if !ellipsisButton.isHidden { mainStackViewTrailingConstraint?.constant = -Constants.mainStackViewTrailingPadding if #available(iOS 26, *) { - buttonContainerStackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 5, leading: 0, bottom: 0, trailing: 0) + buttonContainerStackView.directionalLayoutMargins = NSDirectionalEdgeInsets( + top: 5, + leading: 0, + bottom: 0, + trailing: 0 + ) buttonContainerStackView.isLayoutMarginsRelativeArrangement = true } } @@ -182,8 +187,14 @@ class BlogDashboardCardFrameView: UIView { addSubview(buttonContainerStackView) NSLayoutConstraint.activate([ - buttonContainerStackView.topAnchor.constraint(equalTo: topAnchor, constant: Constants.buttonContainerStackViewPadding), - buttonContainerStackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -Constants.buttonContainerStackViewPadding) + buttonContainerStackView.topAnchor.constraint( + equalTo: topAnchor, + constant: Constants.buttonContainerStackViewPadding + ), + buttonContainerStackView.trailingAnchor.constraint( + equalTo: trailingAnchor, + constant: -Constants.buttonContainerStackViewPadding + ) ]) buttonContainerStackView.addArrangedSubviews([ @@ -191,10 +202,6 @@ class BlogDashboardCardFrameView: UIView { ]) } - func removeButtonContainerStackView() { - buttonContainerStackView.removeFromSuperview() - } - /// Adds the "more" button with the given actions to the corner of the cell. func addMoreMenu(items: [UIMenuElement], card: DashboardCard) { self.addMoreMenu(items: items, card: card as BlogDashboardAnalyticPropertiesProviding) @@ -211,9 +218,9 @@ class BlogDashboardCardFrameView: UIView { private func updateEllipsisButtonState() { ellipsisButton.isHidden = onEllipsisButtonTap == nil - let headerPadding = ellipsisButton.isHidden ? - Constants.headerPaddingWithEllipsisButtonHidden : - Constants.headerPaddingWithEllipsisButtonShown + let headerPadding = + ellipsisButton.isHidden + ? Constants.headerPaddingWithEllipsisButtonHidden : Constants.headerPaddingWithEllipsisButtonShown headerStackView.directionalLayoutMargins = headerPadding } @@ -246,8 +253,7 @@ class BlogDashboardCardFrameView: UIView { @objc private func headerTapped() { if let onHeaderTap { onHeaderTap() - } - else { + } else { onViewTap?() } } @@ -255,18 +261,31 @@ class BlogDashboardCardFrameView: UIView { private static func titleAttributedText(title: String, hint: String?, font: UIFont?) -> NSAttributedString { let titleString = NSMutableAttributedString(string: title) if let hint, let range = title.nsRange(of: hint) { - titleString.addAttributes([ - .foregroundColor: UIAppColor.primary, - .font: font as Any - ], range: range) + titleString.addAttributes( + [ + .foregroundColor: UIAppColor.primary, + .font: font as Any + ], + range: range + ) } return titleString } private enum Constants { static let bottomPadding: CGFloat = 8 - static let headerPaddingWithEllipsisButtonHidden = NSDirectionalEdgeInsets(top: 12, leading: 16, bottom: 8, trailing: 16) - static let headerPaddingWithEllipsisButtonShown = NSDirectionalEdgeInsets(top: 12, leading: 16, bottom: 8, trailing: 8) + static let headerPaddingWithEllipsisButtonHidden = NSDirectionalEdgeInsets( + top: 12, + leading: 16, + bottom: 8, + trailing: 16 + ) + static let headerPaddingWithEllipsisButtonShown = NSDirectionalEdgeInsets( + top: 12, + leading: 16, + bottom: 8, + trailing: 8 + ) static let headerHorizontalSpacing: CGFloat = 5 static let cornerRadius = DesignConstants.radius(.large) static let buttonContainerStackViewPadding: CGFloat = 8 @@ -274,6 +293,9 @@ class BlogDashboardCardFrameView: UIView { } private enum Strings { - static let ellipsisButtonAccessibilityLabel = NSLocalizedString("More", comment: "Accessibility label for more button in dashboard quick start card.") + static let ellipsisButtonAccessibilityLabel = NSLocalizedString( + "More", + comment: "Accessibility label for more button in dashboard quick start card." + ) } } diff --git a/WordPress/Classes/ViewRelated/Jetpack/Branding/JetpackBrandedScreen.swift b/WordPress/Classes/ViewRelated/Jetpack/Branding/JetpackBrandedScreen.swift index 49863e1923f9..5ff5401f7402 100644 --- a/WordPress/Classes/ViewRelated/Jetpack/Branding/JetpackBrandedScreen.swift +++ b/WordPress/Classes/ViewRelated/Jetpack/Branding/JetpackBrandedScreen.swift @@ -1,17 +1,6 @@ import Foundation -protocol JetpackBrandedScreen { - - /// Name to use when constructing phase three branding text. - /// Set to `nil` to fallback to the default branding text. - var featureName: String? { get } - - /// Whether the feature is in plural form. - /// Used to decide on using "is" or "are" when constructing the branding text. - var isPlural: Bool { get } -} - -enum JetpackBannerScreen: String, JetpackBrandedScreen { +enum JetpackBannerScreen: String { case activityLog = "activity_log" case backup case menus @@ -22,59 +11,9 @@ enum JetpackBannerScreen: String, JetpackBrandedScreen { case scan case stats case themes - - var featureName: String? { - switch self { - case .activityLog: - return NSLocalizedString("Activity", comment: "Noun. Name of the Activity Log feature") - case .backup: - return NSLocalizedString("Backup", comment: "Noun. Name of the Backup feature") - case .menus: - return NSLocalizedString("Menus", comment: "Noun. Name of the Menus feature") - case .notifications: - return NSLocalizedString("Notifications", comment: "Noun. Name of the Notifications feature") - case .people: - return nil - case .reader: - return NSLocalizedString("Reader", comment: "Noun. Name of the Reader feature") - case .readerSearch: - return NSLocalizedString("Reader", comment: "Noun. Name of the Reader feature") - case .scan: - return NSLocalizedString("Scan", comment: "Noun. Name of the Scan feature") - case .stats: - return NSLocalizedString("Stats", comment: "Noun. Abbreviation of Statistics. Name of the Stats feature") - case .themes: - return NSLocalizedString("Themes", comment: "Noun. Name of the Themes feature") - } - } - - var isPlural: Bool { - switch self { - case .activityLog: - fallthrough - case .backup: - fallthrough - case .reader: - fallthrough - case .people: - fallthrough - case .scan: - fallthrough - case .readerSearch: - return false - case .notifications: - fallthrough - case .menus: - fallthrough - case .themes: - fallthrough - case .stats: - return true - } - } } -enum JetpackBadgeScreen: String, JetpackBrandedScreen { +enum JetpackBadgeScreen: String { case activityDetail = "activity_detail" case appSettings = "app_settings" case home @@ -83,46 +22,4 @@ enum JetpackBadgeScreen: String, JetpackBrandedScreen { case person case readerDetail = "reader_detail" case sharing - - var featureName: String? { - switch self { - case .appSettings: - fallthrough - case .home: - fallthrough - case .person: - fallthrough - case .me: - return nil - case .activityDetail: - return NSLocalizedString("Activity", comment: "Noun. Name of the Activity Log feature") - case .notificationsSettings: - return NSLocalizedString("Notifications", comment: "Noun. Name of the Notifications feature") - case .readerDetail: - return NSLocalizedString("Reader", comment: "Noun. Name of the Reader feature") - case .sharing: - return NSLocalizedString("Sharing", comment: "Noun. Name of the Social Sharing feature") - } - } - - var isPlural: Bool { - switch self { - case .appSettings: - fallthrough - case .home: - fallthrough - case .me: - fallthrough - case .activityDetail: - fallthrough - case .readerDetail: - fallthrough - case .person: - fallthrough - case .sharing: - return false - case .notificationsSettings: - return true - } - } } diff --git a/WordPress/Classes/ViewRelated/Jetpack/Branding/Menu Card/JetpackBrandingMenuCardCell.swift b/WordPress/Classes/ViewRelated/Jetpack/Branding/Menu Card/JetpackBrandingMenuCardCell.swift index ccf036c8e828..a893bf65cfb9 100644 --- a/WordPress/Classes/ViewRelated/Jetpack/Branding/Menu Card/JetpackBrandingMenuCardCell.swift +++ b/WordPress/Classes/ViewRelated/Jetpack/Branding/Menu Card/JetpackBrandingMenuCardCell.swift @@ -12,13 +12,8 @@ public class JetpackBrandingMenuCardCell: UITableViewCell { /// Sets the animation based on the language orientation private var animation: LottieAnimation? { - traitCollection.layoutDirection == .leftToRight ? - LottieAnimation.named(Constants.animationLtr) : - LottieAnimation.named(Constants.animationRtl) - } - - private var cardType: JetpackBrandingMenuCardPresenter.Config.CardType { - config?.type ?? .expanded + traitCollection.layoutDirection == .leftToRight + ? LottieAnimation.named(Constants.animationLtr) : LottieAnimation.named(Constants.animationRtl) } // MARK: Lazy Loading General Views @@ -38,7 +33,7 @@ public class JetpackBrandingMenuCardCell: UITableViewCell { return stackView }() - // MARK: Lazy Loading Expanded Card Views + // MARK: Lazy Loading Card Views private lazy var logosSuperview: UIView = { let view = UIView() @@ -59,7 +54,7 @@ public class JetpackBrandingMenuCardCell: UITableViewCell { view.animation = animation // Height Constraint - view.heightAnchor.constraint(equalToConstant: Metrics.Expanded.animationsViewHeight).isActive = true + view.heightAnchor.constraint(equalToConstant: Metrics.animationsViewHeight).isActive = true // Width constraint to achieve aspect ratio let animationSize = animation?.size ?? .init(width: 1, height: 1) @@ -105,37 +100,6 @@ public class JetpackBrandingMenuCardCell: UITableViewCell { return button }() - // MARK: Lazy Loading Compact Card Views - - private lazy var jetpackIconImageView: UIImageView = { - let imageView = UIImageView() - let image = UIImage(named: Constants.jetpackIcon) - imageView.translatesAutoresizingMaskIntoConstraints = false - imageView.image = image - imageView.heightAnchor.constraint(equalToConstant: Metrics.Compact.logoImageViewSize).isActive = true - imageView.widthAnchor.constraint(equalToConstant: Metrics.Compact.logoImageViewSize).isActive = true - return imageView - }() - - private lazy var ellipsisButton: UIButton = { - let button = UIButton(type: .custom) - var configuration = UIButton.Configuration.plain() - configuration.image = UIImage.gridicon(.ellipsis) - configuration.baseForegroundColor = Metrics.Compact.ellipsisButtonColor - configuration.contentInsets = Metrics.Compact.ellipsisButtonPadding - button.configuration = configuration - button.isAccessibilityElement = true - button.accessibilityLabel = Strings.ellipsisButtonAccessibilityLabel - button.accessibilityTraits = .button - button.setContentHuggingPriority(.defaultHigh, for: .horizontal) - button.showsMenuAsPrimaryAction = true - button.menu = contextMenu - button.on([.touchUpInside, .menuActionTriggered]) { [weak self] _ in - self?.presenter?.trackContextualMenuAccessed() - } - return button - }() - // MARK: Initializers public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { @@ -177,33 +141,27 @@ public class JetpackBrandingMenuCardCell: UITableViewCell { } private func setupContent() { - containerStackView.addArrangedSubviews(stackViewSubviews) + containerStackView.addArrangedSubviews([logosSuperview, label, learnMoreSuperview]) logosAnimationView.currentProgress = 1.0 label.text = config?.description } private func applyStyles() { - containerStackView.axis = stackViewAxis - containerStackView.spacing = stackViewSpacing - containerStackView.directionalLayoutMargins = stackViewLayoutMargins - label.font = labelFont - label.textColor = labelTextColor - label.numberOfLines = labelNumberOfLines + containerStackView.axis = .vertical + containerStackView.spacing = Metrics.spacing + containerStackView.directionalLayoutMargins = Metrics.containerMargins + label.font = Metrics.labelFont + label.textColor = Metrics.labelTextColor + label.numberOfLines = 0 } private func configureCardFrame() { - if cardType == .expanded { - cardFrameView.configureButtonContainerStackView() - cardFrameView.onEllipsisButtonTap = { [weak self] in - self?.presenter?.trackContextualMenuAccessed() - } - cardFrameView.ellipsisButton.showsMenuAsPrimaryAction = true - cardFrameView.ellipsisButton.menu = contextMenu - } - else { - cardFrameView.removeButtonContainerStackView() - cardFrameView.onEllipsisButtonTap = nil + cardFrameView.configureButtonContainerStackView() + cardFrameView.onEllipsisButtonTap = { [weak self] in + self?.presenter?.trackContextualMenuAccessed() } + cardFrameView.ellipsisButton.showsMenuAsPrimaryAction = true + cardFrameView.ellipsisButton.menu = contextMenu } // MARK: Actions @@ -213,9 +171,11 @@ public class JetpackBrandingMenuCardCell: UITableViewCell { return } - JetpackFeaturesRemovalCoordinator.presentOverlayIfNeeded(in: viewController, - source: .card, - blog: viewController.blog) + JetpackFeaturesRemovalCoordinator.presentOverlayIfNeeded( + in: viewController, + source: .card, + blog: viewController.blog + ) presenter?.trackLinkTapped() } } @@ -228,7 +188,7 @@ private extension JetpackBrandingMenuCardCell { // Defines the structure of the contextual menu items. private var contextMenuItems: [MenuItem] { - return [.remindLater(remindMeLaterTapped), .hide(hideThisTapped)] + [.remindLater(remindMeLaterTapped), .hide(hideThisTapped)] } // MARK: Menu Creation @@ -251,103 +211,20 @@ private extension JetpackBrandingMenuCardCell { } } -private extension JetpackBrandingMenuCardCell { - var stackViewAxis: NSLayoutConstraint.Axis { - switch cardType { - case .compact: - return .horizontal - case .expanded: - return .vertical - } - } - - var stackViewSpacing: CGFloat { - switch cardType { - case .compact: - return Metrics.Compact.spacing - case .expanded: - return Metrics.Expanded.spacing - } - } - - var stackViewLayoutMargins: NSDirectionalEdgeInsets { - switch cardType { - case .compact: - return Metrics.Compact.containerMargins - case .expanded: - return Metrics.Expanded.containerMargins - } - } - - var stackViewSubviews: [UIView] { - switch cardType { - case .compact: - return [jetpackIconImageView, label, ellipsisButton] - case .expanded: - return [logosSuperview, label, learnMoreSuperview] - } - } - - var labelFont: UIFont { - switch cardType { - case .compact: - return Metrics.Compact.labelFont - case .expanded: - return Metrics.Expanded.labelFont - } - } - - var labelTextColor: UIColor { - switch cardType { - case .compact: - return Metrics.Compact.labelTextColor - case .expanded: - return Metrics.Expanded.labelTextColor - } - } - - var labelNumberOfLines: Int { - switch cardType { - case .compact: - return 1 - case .expanded: - return 0 - } - } -} - private extension JetpackBrandingMenuCardCell { enum Metrics { // General - enum Expanded { - static let spacing: CGFloat = 10 - static let containerMargins = NSDirectionalEdgeInsets(top: 20, leading: 20, bottom: 12, trailing: 20) - static let animationsViewHeight: CGFloat = 32 - static var labelFont: UIFont { - let maximumFontPointSize: CGFloat = 16 - let fontDescriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body) - let font = UIFont(descriptor: fontDescriptor, size: min(fontDescriptor.pointSize, maximumFontPointSize)) - return UIFontMetrics.default.scaledFont(for: font) - } - static let labelTextColor: UIColor = .label + static let spacing: CGFloat = 10 + static let containerMargins = NSDirectionalEdgeInsets(top: 20, leading: 20, bottom: 12, trailing: 20) + static let animationsViewHeight: CGFloat = 32 + static var labelFont: UIFont { + let maximumFontPointSize: CGFloat = 16 + let fontDescriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body) + let font = UIFont(descriptor: fontDescriptor, size: min(fontDescriptor.pointSize, maximumFontPointSize)) + return UIFontMetrics.default.scaledFont(for: font) } - - enum Compact { - static let spacing: CGFloat = 15 - static let containerMargins = NSDirectionalEdgeInsets(top: 15, leading: 20, bottom: 7, trailing: 12) - static let logoImageViewSize: CGFloat = 24 - static let ellipsisButtonPadding = NSDirectionalEdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 8) - static let ellipsisButtonColor = UIAppColor.gray(.shade20) - static var labelFont: UIFont { - let maximumFontPointSize: CGFloat = 17 - let fontDescriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body) - let font = UIFont(descriptor: fontDescriptor, size: min(fontDescriptor.pointSize, maximumFontPointSize)) - return UIFontMetrics.default.scaledFont(for: font) - } - static let labelTextColor: UIColor = UIAppColor.jetpackGreen(.shade40) - } - + static let labelTextColor: UIColor = .label static let cardFrameConstraintPriority = UILayoutPriority(999) // Learn more button @@ -361,22 +238,24 @@ private extension JetpackBrandingMenuCardCell { static let analyticsSource = "jetpack_menu_card" static let remindMeLaterSystemImageName = "alarm" static let hideThisLaterSystemImageName = "eye.slash" - static let jetpackIcon = "icon-jetpack" } enum Strings { - static let learnMoreButtonText = NSLocalizedString("jetpack.menuCard.learnMore", - value: "Learn more", - comment: "Title of a button that displays a blog post in a web view.") - static let remindMeLaterMenuItemTitle = NSLocalizedString("jetpack.menuCard.remindLater", - value: "Remind me later", - comment: "Menu item title to hide the card for now and show it later.") - static let hideCardMenuItemTitle = NSLocalizedString("jetpack.menuCard.hide", - value: "Hide this", - comment: "Menu item title to hide the card.") - static let ellipsisButtonAccessibilityLabel = NSLocalizedString("ellipsisButton.AccessibilityLabel", - value: "More", - comment: "Accessibility label for more button in dashboard quick start card.") + static let learnMoreButtonText = NSLocalizedString( + "jetpack.menuCard.learnMore", + value: "Learn more", + comment: "Title of a button that displays a blog post in a web view." + ) + static let remindMeLaterMenuItemTitle = NSLocalizedString( + "jetpack.menuCard.remindLater", + value: "Remind me later", + comment: "Menu item title to hide the card for now and show it later." + ) + static let hideCardMenuItemTitle = NSLocalizedString( + "jetpack.menuCard.hide", + value: "Hide this", + comment: "Menu item title to hide the card." + ) } enum MenuItem { @@ -404,7 +283,7 @@ private extension JetpackBrandingMenuCardCell { var toAction: UIAction { switch self { case .remindLater(let handler), - .hide(let handler): + .hide(let handler): return UIAction(title: title, image: image, attributes: []) { _ in handler() } diff --git a/WordPress/Classes/ViewRelated/Jetpack/Branding/Menu Card/JetpackBrandingMenuCardPresenter.swift b/WordPress/Classes/ViewRelated/Jetpack/Branding/Menu Card/JetpackBrandingMenuCardPresenter.swift index c539568d0c42..79e9fbf7a957 100644 --- a/WordPress/Classes/ViewRelated/Jetpack/Branding/Menu Card/JetpackBrandingMenuCardPresenter.swift +++ b/WordPress/Classes/ViewRelated/Jetpack/Branding/Menu Card/JetpackBrandingMenuCardPresenter.swift @@ -5,13 +5,7 @@ import WordPressShared class JetpackBrandingMenuCardPresenter { struct Config { - - enum CardType { - case compact, expanded - } - let description: String - let type: CardType } // MARK: Private Variables @@ -46,7 +40,7 @@ class JetpackBrandingMenuCardPresenter { guard phase == .selfHosted else { return nil } - return .init(description: Strings.selfHostedPhaseDescription, type: .expanded) + return .init(description: Strings.selfHostedPhaseDescription) } func shouldShowTopCard() -> Bool {