diff --git a/WordPress/Classes/Utility/ImmuTable.swift b/WordPress/Classes/Utility/ImmuTable.swift index 33f30d6a01b9..eba71e9826b5 100644 --- a/WordPress/Classes/Utility/ImmuTable.swift +++ b/WordPress/Classes/Utility/ImmuTable.swift @@ -256,7 +256,7 @@ open class ImmuTableViewHandler: NSObject, UITableViewDataSource, UITableViewDel /// An ImmuTable object representing the table structure. open var viewModel = ImmuTable.Empty { didSet { - if target.isViewLoaded { + if target.isViewLoaded && automaticallyReloadTableView { target.tableView.reloadData() } } @@ -265,6 +265,9 @@ open class ImmuTableViewHandler: NSObject, UITableViewDataSource, UITableViewDel /// Configure the handler to automatically deselect any cell after tapping it. @objc var automaticallyDeselectCells = false + /// Automatically reload table view when view model changes + @objc var automaticallyReloadTableView = true + // MARK: UITableViewDataSource open func numberOfSections(in tableView: UITableView) -> Int { diff --git a/WordPress/Classes/ViewRelated/Stats/Insights/Insights Management/InsightsManagementViewController.swift b/WordPress/Classes/ViewRelated/Stats/Insights/Insights Management/InsightsManagementViewController.swift index 1a3929480ac6..a54909936d9d 100644 --- a/WordPress/Classes/ViewRelated/Stats/Insights/Insights Management/InsightsManagementViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/Insights/Insights Management/InsightsManagementViewController.swift @@ -21,6 +21,10 @@ class InsightsManagementViewController: UITableViewController { } } + private var insightsInactive: [StatSection] { + InsightsManagementViewController.allInsights.filter({ !self.insightsShown.contains($0) }) + } + private var hasChanges: Bool { return insightsShown != originalInsightsShown } @@ -30,7 +34,9 @@ class InsightsManagementViewController: UITableViewController { private lazy var saveButton = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(saveTapped)) private lazy var tableHandler: ImmuTableViewHandler = { - return ImmuTableViewHandler(takeOver: self) + let handler = ImmuTableViewHandler(takeOver: self) + handler.automaticallyReloadTableView = false + return handler }() // MARK: - Init @@ -214,6 +220,7 @@ private extension InsightsManagementViewController { func reloadViewModel() { tableHandler.viewModel = tableViewModel() + tableView.reloadData() } func tableViewModel() -> ImmuTable { @@ -250,7 +257,7 @@ private extension InsightsManagementViewController { } func inactiveCardsSection() -> ImmuTableSection { - let rows = InsightsManagementViewController.allInsights.filter({ !self.insightsShown.contains($0) }) + let rows = insightsInactive guard rows.count > 0 else { return ImmuTableSection(headerText: TextContent.inactiveCardsHeader, rows: [inactivePlaceholderRow]) @@ -259,7 +266,7 @@ private extension InsightsManagementViewController { return ImmuTableSection(headerText: TextContent.inactiveCardsHeader, rows: rows.map { return AddInsightStatRow(title: $0.insightManagementTitle, - enabled: true, + enabled: false, action: rowActionFor($0)) } ) } @@ -305,11 +312,67 @@ private extension InsightsManagementViewController { func toggleRow(for statSection: StatSection) { if let index = insightsShown.firstIndex(of: statSection) { insightsShown.remove(at: index) - } else { + moveRowToInactive(at: index, statSection: statSection) + } else if let inactiveIndex = insightsInactive.firstIndex(of: statSection) { insightsShown.append(statSection) + moveRowToActive(at: inactiveIndex, statSection: statSection) } + } - reloadViewModel() + // Animates the movement of a row from the inactive to active section, supports accessibility + func moveRowToActive(at index: Int, statSection: StatSection) { + tableHandler.viewModel = tableViewModel() + + let origin = IndexPath(row: index, section: 1) + let row = insightsShown.firstIndex(of: statSection) ?? (insightsShown.count - 1) + let destination = IndexPath(row: row, section: 0) + + tableView.performBatchUpdates { + tableView.moveRow(at: origin, to: destination) + + /// Account for placeholder cell addition to inactive section + if insightsInactive.isEmpty { + tableView.insertRows(at: [.init(row: 0, section: 1)], with: .none) + } + + /// Account for placeholder cell removal from active section + if insightsShown.count == 1 { + tableView.deleteRows(at: [.init(row: 0, section: 0)], with: .automatic) + } + } + + /// Reload the data of the row to update the accessibility information + if let cell = tableView.cellForRow(at: destination), insightsShown.count > 0 { + tableHandler.viewModel.rowAtIndexPath(destination).configureCell(cell) + } + } + + // Animates the movement of a row from the active to inactive section, supports accessibility + func moveRowToInactive(at index: Int, statSection: StatSection) { + tableHandler.viewModel = tableViewModel() + + let origin = IndexPath(row: index, section: 0) + let row = insightsInactive.firstIndex(of: statSection) ?? 0 + let destination = IndexPath(row: row, section: 1) + + tableView.performBatchUpdates { + tableView.moveRow(at: origin, to: destination) + + /// Account for placeholder cell addition to active section + if insightsShown.isEmpty { + tableView.insertRows(at: [.init(row: 0, section: 0)], with: .none) + } + + /// Account for placeholder cell removal from inactive section + if insightsInactive.count == 1 { + tableView.deleteRows(at: [.init(row: 0, section: 1)], with: .automatic) + } + } + + /// Reload the data of the row to update the accessibility information + if let cell = tableView.cellForRow(at: destination), insightsInactive.count > 0 { + tableHandler.viewModel.rowAtIndexPath(destination).configureCell(cell) + } } var placeholderRow: ImmuTableRow { diff --git a/WordPress/Classes/ViewRelated/Views/NoResultsViewController.swift b/WordPress/Classes/ViewRelated/Views/NoResultsViewController.swift index 49442b6563a1..739c00ffe99f 100644 --- a/WordPress/Classes/ViewRelated/Views/NoResultsViewController.swift +++ b/WordPress/Classes/ViewRelated/Views/NoResultsViewController.swift @@ -588,11 +588,11 @@ private extension NoResultsViewController { view.accessibilityLabel = titleLabel.text view.accessibilityTraits = .staticText } else { - view.accessibilityElements = [noResultsView!, actionButton!] + view.accessibilityElements = [labelStackView!, actionButton!] - noResultsView.isAccessibilityElement = true - noResultsView.accessibilityTraits = .staticText - noResultsView.accessibilityLabel = [ + labelStackView.accessibilityTraits = .staticText + labelStackView.isAccessibilityElement = true + labelStackView.accessibilityLabel = [ titleLabel.text, subtitleTextView.isHidden ? nil : subtitleTextView.attributedText.string ].compactMap { $0 }.joined(separator: ". ")