From 0ceeaa117d4858794343b731106d614545a02c86 Mon Sep 17 00:00:00 2001 From: kean Date: Fri, 20 Jun 2025 11:24:03 -0400 Subject: [PATCH 1/3] Remove FilterBarView --- .../Activity/Filter/FilterBarView.swift | 73 ------------------- 1 file changed, 73 deletions(-) delete mode 100644 WordPress/Classes/ViewRelated/Activity/Filter/FilterBarView.swift diff --git a/WordPress/Classes/ViewRelated/Activity/Filter/FilterBarView.swift b/WordPress/Classes/ViewRelated/Activity/Filter/FilterBarView.swift deleted file mode 100644 index 8c3b7f8d6b7d..000000000000 --- a/WordPress/Classes/ViewRelated/Activity/Filter/FilterBarView.swift +++ /dev/null @@ -1,73 +0,0 @@ -import UIKit -import WordPressShared - -class FilterBarView: UIScrollView { - let filterStackView = UIStackView() - - override init(frame: CGRect) { - super.init(frame: frame) - - filterStackView.alignment = .center - filterStackView.spacing = Constants.filterStackViewSpacing - filterStackView.translatesAutoresizingMaskIntoConstraints = false - - let filterIcon = UIImageView(image: UIImage.gridicon(.filter)) - filterIcon.tintColor = .secondaryLabel - filterIcon.heightAnchor.constraint(equalToConstant: Constants.filterHeightAnchor).isActive = true - - filterStackView.addArrangedSubview(filterIcon) - - canCancelContentTouches = true - showsHorizontalScrollIndicator = false - addSubview(filterStackView) - - NSLayoutConstraint.activate([ - filterStackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: Constants.filterBarHorizontalPadding), - filterStackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -1 * Constants.filterBarHorizontalPadding), - filterStackView.topAnchor.constraint(equalTo: topAnchor, constant: Constants.filterBarVerticalPadding), - filterStackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: Constants.filterBarVerticalPadding), - heightAnchor.constraint(equalTo: filterStackView.heightAnchor, constant: 2 * Constants.filterBarVerticalPadding) - ]) - - // Ensure that the stackview is right aligned in RTL layouts - if userInterfaceLayoutDirection() == .rightToLeft { - transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi)) - filterStackView.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi)) - } - } - - override func didMoveToSuperview() { - super.didMoveToSuperview() - - guard let superview else { - return - } - - let separator = UIView() - separator.translatesAutoresizingMaskIntoConstraints = false - superview.addSubview(separator) - NSLayoutConstraint.activate([ - separator.bottomAnchor.constraint(equalTo: bottomAnchor), - separator.trailingAnchor.constraint(equalTo: superview.trailingAnchor), - separator.leadingAnchor.constraint(equalTo: superview.leadingAnchor), - separator.heightAnchor.constraint(equalToConstant: 1) - ]) - WPStyleGuide.applyBorderStyle(separator) - separator.layer.zPosition = 10 - } - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - - func add(button chip: FilterChipButton) { - filterStackView.addArrangedSubview(chip) - } - - private enum Constants { - static let filterHeightAnchor: CGFloat = 24 - static let filterStackViewSpacing: CGFloat = 8 - static let filterBarHorizontalPadding: CGFloat = 16 - static let filterBarVerticalPadding: CGFloat = 8 - } -} From 94809f00fcae0af08eb767cc02bc6540a779fe3d Mon Sep 17 00:00:00 2001 From: kean Date: Fri, 20 Jun 2025 11:24:40 -0400 Subject: [PATCH 2/3] Remove FilterChipButton --- .../Activity/Filter/FilterChipButton.swift | 102 ------------------ 1 file changed, 102 deletions(-) delete mode 100644 WordPress/Classes/ViewRelated/Activity/Filter/FilterChipButton.swift diff --git a/WordPress/Classes/ViewRelated/Activity/Filter/FilterChipButton.swift b/WordPress/Classes/ViewRelated/Activity/Filter/FilterChipButton.swift deleted file mode 100644 index cf6ac1a72435..000000000000 --- a/WordPress/Classes/ViewRelated/Activity/Filter/FilterChipButton.swift +++ /dev/null @@ -1,102 +0,0 @@ -import Foundation -import WordPressShared - -/// A button that represents a filter chip -/// -class FilterChipButton: UIView { - /// The title of the button - var title: String? { - didSet { - mainButton.setTitle(title, for: .normal) - } - } - - let mainButton = UIButton(type: .system) - let resetButton = UIButton(type: .system) - - /// Callback called when the button is tapped - var tapped: (() -> Void)? - - /// Callback called when the reset ("X") is tapped - var resetTapped: (() -> Void)? - - override init(frame: CGRect) { - super.init(frame: frame) - - let stackView = UIStackView() - stackView.axis = .horizontal - - stackView.addArrangedSubview(mainButton) - - resetButton.widthAnchor.constraint(greaterThanOrEqualToConstant: Constants.minResetButtonWidth).isActive = true - resetButton.imageEdgeInsets = Constants.resetImageInsets - resetButton.isHidden = true - stackView.addArrangedSubview(resetButton) - - mainButton.addTarget(self, action: #selector(mainButtonTapped), for: .touchUpInside) - resetButton.addTarget(self, action: #selector(resetButtonTapped), for: .touchUpInside) - - addSubview(stackView) - pinSubviewToAllEdges(stackView) - stackView.translatesAutoresizingMaskIntoConstraints = false - - layer.borderWidth = Constants.borderWidth - layer.cornerRadius = Constants.cornerRadius - - mainButton.titleLabel?.font = WPStyleGuide.fontForTextStyle(.callout) - mainButton.setTitleColor(.label, for: .normal) - mainButton.heightAnchor.constraint(greaterThanOrEqualToConstant: Constants.minButtonHeight).isActive = true - mainButton.contentEdgeInsets = Constants.buttonContentInset - - applyColors() - } - - required init?(coder: NSCoder) { - super.init(coder: coder) - } - - /// Enables the reset button - func enableResetButton() { - resetButton.isHidden = false - mainButton.contentEdgeInsets = Constants.buttonContentInsetWithResetEnabled - } - - /// Disables the reset button - func disableResetButton() { - resetButton.isHidden = true - mainButton.contentEdgeInsets = Constants.buttonContentInset - UIAccessibility.post(notification: .layoutChanged, argument: mainButton) - } - - @objc private func mainButtonTapped() { - tapped?() - } - - @objc private func resetButtonTapped() { - resetTapped?() - } - - private func applyColors() { - layer.borderColor = UIColor.quaternaryLabel.cgColor - resetButton.setImage(UIImage.gridicon(.crossCircle), for: .normal) - resetButton.tintColor = .secondaryLabel - } - - private enum Constants { - static let minResetButtonWidth: CGFloat = 32 - static let resetImageInsets = UIEdgeInsets(top: 8, left: 6, bottom: 8, right: 10).flippedForRightToLeft - static let borderWidth: CGFloat = 1 - static let cornerRadius: CGFloat = 16 - static let minButtonHeight: CGFloat = 32 - static let buttonContentInset = UIEdgeInsets(top: 0, left: 12, bottom: 0, right: 12).flippedForRightToLeft - static let buttonContentInsetWithResetEnabled = UIEdgeInsets(top: 0, left: 12, bottom: 0, right: 0).flippedForRightToLeft - } - - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) { - applyColors() - } - } -} From 4e976a9aa71e84b3d79ef6147e5b524ed2eede62 Mon Sep 17 00:00:00 2001 From: kean Date: Fri, 20 Jun 2025 11:28:43 -0400 Subject: [PATCH 3/3] Remove ActivityDetailViewController --- .../ActivityDetailViewController.storyboard | 221 ------------ .../ActivityDetailViewController.swift | 320 ------------------ .../Activity/RewindStatus+multiSite.swift | 18 - 3 files changed, 559 deletions(-) delete mode 100644 WordPress/Classes/ViewRelated/Activity/ActivityDetailViewController.storyboard delete mode 100644 WordPress/Classes/ViewRelated/Activity/ActivityDetailViewController.swift delete mode 100644 WordPress/Classes/ViewRelated/Activity/RewindStatus+multiSite.swift diff --git a/WordPress/Classes/ViewRelated/Activity/ActivityDetailViewController.storyboard b/WordPress/Classes/ViewRelated/Activity/ActivityDetailViewController.storyboard deleted file mode 100644 index 357e53ea78bf..000000000000 --- a/WordPress/Classes/ViewRelated/Activity/ActivityDetailViewController.storyboard +++ /dev/null @@ -1,221 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/WordPress/Classes/ViewRelated/Activity/ActivityDetailViewController.swift b/WordPress/Classes/ViewRelated/Activity/ActivityDetailViewController.swift deleted file mode 100644 index 32888c85140c..000000000000 --- a/WordPress/Classes/ViewRelated/Activity/ActivityDetailViewController.swift +++ /dev/null @@ -1,320 +0,0 @@ -import UIKit -import Gridicons -import WordPressUI - -protocol ActivityPresenter: AnyObject { - func presentDetailsFor(activity: FormattableActivity) - func presentBackupOrRestoreFor(activity: Activity, from sender: UIButton) - func presentRestoreFor(activity: Activity, from: String?) - func presentBackupFor(activity: Activity, from: String?) -} - -class ActivityDetailViewController: UIViewController, StoryboardLoadable { - - // MARK: - StoryboardLoadable Protocol - - static var defaultStoryboardName = defaultControllerID - - // MARK: - Properties - - var formattableActivity: FormattableActivity? { - didSet { - setupActivity() - setupRouter() - } - } - var site: JetpackSiteRef? - - var rewindStatus: RewindStatus? - - weak var presenter: ActivityPresenter? - - @IBOutlet private var imageView: CircularImageView! - - @IBOutlet private var roleLabel: UILabel! - @IBOutlet private var nameLabel: UILabel! - - @IBOutlet private var timeLabel: UILabel! - @IBOutlet private var dateLabel: UILabel! - - @IBOutlet weak var textView: UITextView! { - didSet { - textView.delegate = self - } - } - - @IBOutlet weak var jetpackBadgeView: UIView! - - //TODO: remove! - @IBOutlet private var textLabel: UILabel! - @IBOutlet private var summaryLabel: UILabel! - - @IBOutlet private var headerStackView: UIStackView! - @IBOutlet private var rewindStackView: UIStackView! - @IBOutlet private var backupStackView: UIStackView! - @IBOutlet private var contentStackView: UIStackView! - @IBOutlet private var containerView: UIView! - - @IBOutlet weak var warningButton: MultilineButton! - - @IBOutlet private var bottomConstaint: NSLayoutConstraint! - - @IBOutlet private var rewindButton: UIButton! - @IBOutlet private var backupButton: UIButton! - - private var activity: Activity? - - var router: ActivityContentRouter? - - override func viewDidLoad() { - super.viewDidLoad() - - setupLabelStyles() - setupViews() - setupText() - setupAccesibility() - hideRestoreIfNeeded() - showWarningIfNeeded() - WPAnalytics.track(.activityLogDetailViewed, withProperties: ["source": presentedFrom()]) - } - - @IBAction func rewindButtonTapped(sender: UIButton) { - guard let activity else { - return - } - presenter?.presentRestoreFor(activity: activity, from: "\(presentedFrom())/detail") - } - - @IBAction func backupButtonTapped(sender: UIButton) { - guard let activity else { - return - } - presenter?.presentBackupFor(activity: activity, from: "\(presentedFrom())/detail") - } - - @IBAction func warningTapped(_ sender: Any) { - guard let url = URL(string: Constants.supportUrl) else { - return - } - - let navController = UINavigationController(rootViewController: WebViewControllerFactory.controller(url: url, source: "activity_detail_warning")) - - present(navController, animated: true) - } - - private func setupLabelStyles() { - nameLabel.textColor = .label - nameLabel.font = UIFont.systemFont(ofSize: UIFont.preferredFont(forTextStyle: .footnote).pointSize, - weight: .semibold) - textLabel.textColor = .label - summaryLabel.textColor = .secondaryLabel - - roleLabel.textColor = .secondaryLabel - dateLabel.textColor = .secondaryLabel - timeLabel.textColor = .secondaryLabel - - rewindButton.setTitleColor(UIAppColor.primary, for: .normal) - rewindButton.setTitleColor(UIAppColor.primaryDark, for: .highlighted) - - backupButton.setTitleColor(UIAppColor.primary, for: .normal) - backupButton.setTitleColor(UIAppColor.primaryDark, for: .highlighted) - } - - private func setupViews() { - guard let activity else { - return - } - - view.backgroundColor = .systemBackground - containerView.backgroundColor = .systemBackground - - textLabel.isHidden = true - textView.textContainerInset = .zero - textView.textContainer.lineFragmentPadding = 0 - - if activity.isRewindable { - bottomConstaint.constant = 0 - rewindStackView.isHidden = false - backupStackView.isHidden = false - } - - if let avatar = activity.actor?.avatarURL, let avatarURL = URL(string: avatar) { - imageView.backgroundColor = UIAppColor.neutral(.shade20) - imageView.downloadImage(from: avatarURL, placeholderImage: .gridicon(.user, size: Constants.gridiconSize)) - } else if let iconType = activity.gridiconType { - imageView.contentMode = .center - imageView.backgroundColor = activity.statusColor - imageView.image = .gridicon(iconType, size: Constants.gridiconSize) - } else { - imageView.isHidden = true - } - - rewindButton.naturalContentHorizontalAlignment = .leading - rewindButton.setImage(.gridicon(.history, size: Constants.gridiconSize), for: .normal) - - backupButton.naturalContentHorizontalAlignment = .leading - backupButton.setImage(.gridicon(.cloudDownload, size: Constants.gridiconSize), for: .normal) - - let attributedTitle = StringHighlighter.highlightString(RewindStatus.Strings.multisiteNotAvailableSubstring, - inString: RewindStatus.Strings.multisiteNotAvailable) - - warningButton.setAttributedTitle(attributedTitle, for: .normal) - warningButton.setTitleColor(.systemGray, for: .normal) - warningButton.titleLabel?.numberOfLines = 0 - warningButton.titleLabel?.lineBreakMode = .byWordWrapping - warningButton.naturalContentHorizontalAlignment = .leading - warningButton.backgroundColor = view.backgroundColor - setupJetpackBadge() - } - - private func setupJetpackBadge() { - guard JetpackBrandingVisibility.all.enabled else { - return - } - jetpackBadgeView.isHidden = false - let textProvider = JetpackBrandingTextProvider(screen: JetpackBadgeScreen.activityDetail) - let jetpackBadgeButton = JetpackButton(style: .badge, title: textProvider.brandingText()) - jetpackBadgeButton.translatesAutoresizingMaskIntoConstraints = false - jetpackBadgeButton.addTarget(self, action: #selector(jetpackButtonTapped), for: .touchUpInside) - jetpackBadgeView.addSubview(jetpackBadgeButton) - NSLayoutConstraint.activate([ - jetpackBadgeButton.centerXAnchor.constraint(equalTo: jetpackBadgeView.centerXAnchor), - jetpackBadgeButton.topAnchor.constraint(equalTo: jetpackBadgeView.topAnchor, constant: Constants.jetpackBadgeTopInset), - jetpackBadgeButton.bottomAnchor.constraint(equalTo: jetpackBadgeView.bottomAnchor) - ]) - jetpackBadgeView.backgroundColor = .systemGroupedBackground - } - - @objc private func jetpackButtonTapped() { - JetpackBrandingCoordinator.presentOverlay(from: self) - JetpackBrandingAnalyticsHelper.trackJetpackPoweredBadgeTapped(screen: .activityDetail) - } - - private func setupText() { - guard let activity, let site else { - return - } - - title = NSLocalizedString("Event", comment: "Title for the activity detail view") - nameLabel.text = activity.actor?.displayName - roleLabel.text = activity.actor?.role.localizedCapitalized - - textView.attributedText = formattableActivity?.formattedContent(using: ActivityContentStyles()) - summaryLabel.text = activity.summary - - rewindButton.setTitle(NSLocalizedString("Restore", comment: "Title for button allowing user to restore their Jetpack site"), - for: .normal) - backupButton.setTitle(NSLocalizedString("Download backup", comment: "Title for button allowing user to backup their Jetpack site"), - for: .normal) - - let dateFormatter = ActivityDateFormatting.longDateFormatter(for: site, withTime: false) - dateLabel.text = dateFormatter.string(from: activity.published) - - let timeFormatter = DateFormatter() - timeFormatter.dateStyle = .none - timeFormatter.timeStyle = .short - timeFormatter.timeZone = dateFormatter.timeZone - - timeLabel.text = timeFormatter.string(from: activity.published) - } - - private func setupAccesibility() { - guard let activity else { - return - } - - contentStackView.isAccessibilityElement = true - contentStackView.accessibilityTraits = UIAccessibilityTraits.staticText - contentStackView.accessibilityLabel = "\(activity.text), \(activity.summary)" - textLabel.isAccessibilityElement = false - summaryLabel.isAccessibilityElement = false - - if traitCollection.preferredContentSizeCategory.isAccessibilityCategory { - headerStackView.axis = .vertical - - dateLabel.textAlignment = .center - timeLabel.textAlignment = .center - } else { - headerStackView.axis = .horizontal - - if view.effectiveUserInterfaceLayoutDirection == .leftToRight { - // swiftlint:disable:next inverse_text_alignment - dateLabel.textAlignment = .right - // swiftlint:disable:next inverse_text_alignment - timeLabel.textAlignment = .right - } else { - // swiftlint:disable:next natural_text_alignment - dateLabel.textAlignment = .left - // swiftlint:disable:next natural_text_alignment - timeLabel.textAlignment = .left - } - } - } - - private func hideRestoreIfNeeded() { - guard let isRestoreActive = rewindStatus?.isActive() else { - return - } - - rewindStackView.isHidden = !isRestoreActive - } - - private func showWarningIfNeeded() { - guard let isMultiSite = rewindStatus?.isMultisite() else { - return - } - - warningButton.isHidden = !isMultiSite - } - - func setupRouter() { - guard let activity = formattableActivity else { - router = nil - return - } - let coordinator = DefaultContentCoordinator(controller: self, context: ContextManager.shared.mainContext) - router = ActivityContentRouter( - activity: activity, - coordinator: coordinator) - } - - func setupActivity() { - activity = formattableActivity?.activity - } - - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - if previousTraitCollection?.preferredContentSizeCategory != traitCollection.preferredContentSizeCategory { - setupLabelStyles() - setupAccesibility() - } - } - - private func presentedFrom() -> String { - if presenter is ActivityLogsViewController { - return "activity_log" - } else if presenter is BackupsViewController { - return "backup" - } else if presenter is DashboardActivityLogCardCell { - return "dashboard" - } else { - return "unknown" - } - } - - private enum Constants { - static let gridiconSize: CGSize = CGSize(width: 24, height: 24) - static let supportUrl = "https://jetpack.com/support/backup/" - // the distance ought to be 30, and the stackView spacing is 16, thus the top inset is 14. - static let jetpackBadgeTopInset: CGFloat = 14 - } -} - -// MARK: - UITextViewDelegate - -extension ActivityDetailViewController: UITextViewDelegate { - func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool { - router?.routeTo(URL) - return false - } -} diff --git a/WordPress/Classes/ViewRelated/Activity/RewindStatus+multiSite.swift b/WordPress/Classes/ViewRelated/Activity/RewindStatus+multiSite.swift deleted file mode 100644 index 0109881caa22..000000000000 --- a/WordPress/Classes/ViewRelated/Activity/RewindStatus+multiSite.swift +++ /dev/null @@ -1,18 +0,0 @@ -import WordPressKit - -extension RewindStatus { - func isMultisite() -> Bool { - reason == "multisite_not_supported" - } - - func isActive() -> Bool { - state == .active - } - - enum Strings { - static let multisiteNotAvailable = String(format: Self.multisiteNotAvailableFormat, - Self.multisiteNotAvailableSubstring) - static let multisiteNotAvailableFormat = NSLocalizedString("Jetpack Backup for Multisite installations provides downloadable backups, no one-click restores. For more information %1$@.", comment: "Message for Jetpack users that have multisite WP installation, thus Restore is not available. %1$@ is a placeholder for the string 'visit our documentation page'.") - static let multisiteNotAvailableSubstring = NSLocalizedString("visit our documentation page", comment: "Portion of a message for Jetpack users that have multisite WP installation, thus Restore is not available. This part is a link, colored with a different color.") - } -}