From e1886509d332c7b7d5fa57417adc6bbac778bd06 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sat, 3 Dec 2022 23:11:02 -0500 Subject: [PATCH] Filter statuses on profiles --- .../ProfileStatusesViewController.swift | 47 +++++++++++++------ .../Timeline/TimelineViewController.swift | 8 +++- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/Tusker/Screens/Profile/ProfileStatusesViewController.swift b/Tusker/Screens/Profile/ProfileStatusesViewController.swift index 6beccf51..be8e9746 100644 --- a/Tusker/Screens/Profile/ProfileStatusesViewController.swift +++ b/Tusker/Screens/Profile/ProfileStatusesViewController.swift @@ -14,6 +14,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie weak var owner: ProfileViewController? let mastodonController: MastodonController + let filterer: Filterer private(set) var accountID: String! let kind: Kind var initialHeaderMode: HeaderMode? @@ -38,6 +39,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie self.kind = kind self.owner = owner self.mastodonController = owner.mastodonController + self.filterer = Filterer(mastodonController: mastodonController, context: .account) super.init(nibName: nil, bundle: nil) @@ -67,7 +69,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie config.topSeparatorVisibility = .hidden config.bottomSeparatorVisibility = .hidden } - if case .status(_, _, _) = item { + if case .status(_, _, _, _) = item { config.topSeparatorInsets = TimelineStatusCollectionViewCell.separatorInsets config.bottomSeparatorInsets = TimelineStatusCollectionViewCell.separatorInsets } @@ -110,10 +112,10 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie private func createDataSource() -> UICollectionViewDiffableDataSource { collectionView.register(ProfileHeaderCollectionViewCell.self, forCellWithReuseIdentifier: "headerCell") - let statusCell = UICollectionView.CellRegistration { [unowned self] cell, indexPath, item in + let statusCell = UICollectionView.CellRegistration { [unowned self] cell, indexPath, item in cell.delegate = self - cell.showPinned = item.2 - cell.updateUI(statusID: item.0, state: item.1, filterResult: .allow) + cell.showPinned = item.3 + cell.updateUI(statusID: item.0, state: item.1, filterResult: item.2) } return UICollectionViewDiffableDataSource(collectionView: collectionView) { [unowned self] collectionView, indexPath, itemIdentifier in switch itemIdentifier { @@ -139,8 +141,14 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie self.headerCell = cell return cell } - case .status(id: let id, state: let state, pinned: let pinned): - return collectionView.dequeueConfiguredReusableCell(using: statusCell, for: indexPath, item: (id, state, pinned)) + case .status(id: let id, collapseState: let collapseState, filterState: let filterState, pinned: let pinned): + let status = { + let status = self.mastodonController.persistentContainer.status(for: id)! + // if the status is a reblog of another one, filter based on that one + return status.reblog ?? status + } + let result = filterState.resolveFor(status: status, resolver: filterer) + return collectionView.dequeueConfiguredReusableCell(using: statusCell, for: indexPath, item: (id, collapseState, result, pinned)) case .loadingIndicator: return loadingIndicatorCell(for: indexPath) case .confirmLoadMore: @@ -225,7 +233,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie var snapshot = dataSource.snapshot() let existingPinned = snapshot.itemIdentifiers(inSection: .pinned) let items = statuses.map { - let item = Item.status(id: $0.id, state: .unknown, pinned: true) + let item = Item.status(id: $0.id, collapseState: .unknown, filterState: .unknown, pinned: true) // try to keep the existing status state if let existing = existingPinned.first(where: { $0 == item }) { return existing @@ -288,19 +296,19 @@ extension ProfileStatusesViewController { typealias TimelineItem = String case header(String) - case status(id: String, state: CollapseState, pinned: Bool) + case status(id: String, collapseState: CollapseState, filterState: FilterState, pinned: Bool) case loadingIndicator case confirmLoadMore static func fromTimelineItem(_ item: String) -> Self { - return .status(id: item, state: .unknown, pinned: false) + return .status(id: item, collapseState: .unknown, filterState: .unknown, pinned: false) } static func ==(lhs: Item, rhs: Item) -> Bool { switch (lhs, rhs) { case let (.header(a), .header(b)): return a == b - case let (.status(id: a, state: _, pinned: ap), .status(id: b, state: _, pinned: bp)): + case let (.status(id: a, _, _, pinned: ap), .status(id: b, _, _, pinned: bp)): return a == b && ap == bp case (.loadingIndicator, .loadingIndicator): return true @@ -316,7 +324,7 @@ extension ProfileStatusesViewController { case .header(let id): hasher.combine(0) hasher.combine(id) - case .status(id: let id, state: _, pinned: let pinned): + case .status(id: let id, _, _, pinned: let pinned): hasher.combine(1) hasher.combine(id) hasher.combine(pinned) @@ -338,7 +346,7 @@ extension ProfileStatusesViewController { var isSelectable: Bool { switch self { - case .status(id: _, state: _, pinned: _): + case .status(_, _, _, _): return true default: return false @@ -445,11 +453,20 @@ extension ProfileStatusesViewController: UICollectionViewDelegate { } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - guard case .status(id: let id, state: let state, pinned: _) = dataSource.itemIdentifier(for: indexPath) else { + guard let item = dataSource.itemIdentifier(for: indexPath), + case .status(id: let id, collapseState: let collapseState, filterState: let filterState, pinned: _) = item else { return } - let status = mastodonController.persistentContainer.status(for: id)! - selected(status: status.reblog?.id ?? id, state: state.copy()) + if filterState.isWarning { + filterState.setResult(.allow) + collectionView.deselectItem(at: indexPath, animated: true) + var snapshot = dataSource.snapshot() + snapshot.reconfigureItems([item]) + dataSource.apply(snapshot, animatingDifferences: true) + } else { + let status = mastodonController.persistentContainer.status(for: id)! + selected(status: status.reblog?.id ?? id, state: collapseState.copy()) + } } func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { diff --git a/Tusker/Screens/Timeline/TimelineViewController.swift b/Tusker/Screens/Timeline/TimelineViewController.swift index 144bff8a..4b85a795 100644 --- a/Tusker/Screens/Timeline/TimelineViewController.swift +++ b/Tusker/Screens/Timeline/TimelineViewController.swift @@ -144,7 +144,11 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro return UICollectionViewDiffableDataSource(collectionView: collectionView) { [unowned self] collectionView, indexPath, itemIdentifier in switch itemIdentifier { case .status(id: let id, collapseState: let state, filterState: let filterState): - let status = { self.mastodonController.persistentContainer.status(for: id)! } + let status = { + let status = self.mastodonController.persistentContainer.status(for: id)! + // if the status is a reblog of another one, filter based on that one + return status.reblog ?? status + } let result = filterState.resolveFor(status: status, resolver: filterer) switch result { case .allow, .warn(_): @@ -785,7 +789,6 @@ extension TimelineViewController: UICollectionViewDelegate { case .publicTimelineDescription: removeTimelineDescriptionCell() case .status(id: let id, collapseState: let collapseState, filterState: let filterState): - let status = mastodonController.persistentContainer.status(for: id)! if filterState.isWarning { filterState.setResult(.allow) collectionView.deselectItem(at: indexPath, animated: true) @@ -793,6 +796,7 @@ extension TimelineViewController: UICollectionViewDelegate { snapshot.reconfigureItems([item]) dataSource.apply(snapshot, animatingDifferences: true) } else { + let status = mastodonController.persistentContainer.status(for: id)! // if the status in the timeline is a reblog, show the status that it is a reblog of selected(status: status.reblog?.id ?? id, state: collapseState.copy()) }