Filter statuses on profiles
This commit is contained in:
parent
8ad48784d9
commit
e1886509d3
|
@ -14,6 +14,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
|
||||||
|
|
||||||
weak var owner: ProfileViewController?
|
weak var owner: ProfileViewController?
|
||||||
let mastodonController: MastodonController
|
let mastodonController: MastodonController
|
||||||
|
let filterer: Filterer
|
||||||
private(set) var accountID: String!
|
private(set) var accountID: String!
|
||||||
let kind: Kind
|
let kind: Kind
|
||||||
var initialHeaderMode: HeaderMode?
|
var initialHeaderMode: HeaderMode?
|
||||||
|
@ -38,6 +39,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
|
||||||
self.kind = kind
|
self.kind = kind
|
||||||
self.owner = owner
|
self.owner = owner
|
||||||
self.mastodonController = owner.mastodonController
|
self.mastodonController = owner.mastodonController
|
||||||
|
self.filterer = Filterer(mastodonController: mastodonController, context: .account)
|
||||||
|
|
||||||
super.init(nibName: nil, bundle: nil)
|
super.init(nibName: nil, bundle: nil)
|
||||||
|
|
||||||
|
@ -67,7 +69,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
|
||||||
config.topSeparatorVisibility = .hidden
|
config.topSeparatorVisibility = .hidden
|
||||||
config.bottomSeparatorVisibility = .hidden
|
config.bottomSeparatorVisibility = .hidden
|
||||||
}
|
}
|
||||||
if case .status(_, _, _) = item {
|
if case .status(_, _, _, _) = item {
|
||||||
config.topSeparatorInsets = TimelineStatusCollectionViewCell.separatorInsets
|
config.topSeparatorInsets = TimelineStatusCollectionViewCell.separatorInsets
|
||||||
config.bottomSeparatorInsets = TimelineStatusCollectionViewCell.separatorInsets
|
config.bottomSeparatorInsets = TimelineStatusCollectionViewCell.separatorInsets
|
||||||
}
|
}
|
||||||
|
@ -110,10 +112,10 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
|
||||||
|
|
||||||
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
|
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
|
||||||
collectionView.register(ProfileHeaderCollectionViewCell.self, forCellWithReuseIdentifier: "headerCell")
|
collectionView.register(ProfileHeaderCollectionViewCell.self, forCellWithReuseIdentifier: "headerCell")
|
||||||
let statusCell = UICollectionView.CellRegistration<TimelineStatusCollectionViewCell, (String, CollapseState, Bool)> { [unowned self] cell, indexPath, item in
|
let statusCell = UICollectionView.CellRegistration<TimelineStatusCollectionViewCell, (String, CollapseState, Filterer.Result, Bool)> { [unowned self] cell, indexPath, item in
|
||||||
cell.delegate = self
|
cell.delegate = self
|
||||||
cell.showPinned = item.2
|
cell.showPinned = item.3
|
||||||
cell.updateUI(statusID: item.0, state: item.1, filterResult: .allow)
|
cell.updateUI(statusID: item.0, state: item.1, filterResult: item.2)
|
||||||
}
|
}
|
||||||
return UICollectionViewDiffableDataSource(collectionView: collectionView) { [unowned self] collectionView, indexPath, itemIdentifier in
|
return UICollectionViewDiffableDataSource(collectionView: collectionView) { [unowned self] collectionView, indexPath, itemIdentifier in
|
||||||
switch itemIdentifier {
|
switch itemIdentifier {
|
||||||
|
@ -139,8 +141,14 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
|
||||||
self.headerCell = cell
|
self.headerCell = cell
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
case .status(id: let id, state: let state, pinned: let pinned):
|
case .status(id: let id, collapseState: let collapseState, filterState: let filterState, pinned: let pinned):
|
||||||
return collectionView.dequeueConfiguredReusableCell(using: statusCell, for: indexPath, item: (id, state, 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:
|
case .loadingIndicator:
|
||||||
return loadingIndicatorCell(for: indexPath)
|
return loadingIndicatorCell(for: indexPath)
|
||||||
case .confirmLoadMore:
|
case .confirmLoadMore:
|
||||||
|
@ -225,7 +233,7 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
|
||||||
var snapshot = dataSource.snapshot()
|
var snapshot = dataSource.snapshot()
|
||||||
let existingPinned = snapshot.itemIdentifiers(inSection: .pinned)
|
let existingPinned = snapshot.itemIdentifiers(inSection: .pinned)
|
||||||
let items = statuses.map {
|
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
|
// try to keep the existing status state
|
||||||
if let existing = existingPinned.first(where: { $0 == item }) {
|
if let existing = existingPinned.first(where: { $0 == item }) {
|
||||||
return existing
|
return existing
|
||||||
|
@ -288,19 +296,19 @@ extension ProfileStatusesViewController {
|
||||||
typealias TimelineItem = String
|
typealias TimelineItem = String
|
||||||
|
|
||||||
case header(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 loadingIndicator
|
||||||
case confirmLoadMore
|
case confirmLoadMore
|
||||||
|
|
||||||
static func fromTimelineItem(_ item: String) -> Self {
|
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 {
|
static func ==(lhs: Item, rhs: Item) -> Bool {
|
||||||
switch (lhs, rhs) {
|
switch (lhs, rhs) {
|
||||||
case let (.header(a), .header(b)):
|
case let (.header(a), .header(b)):
|
||||||
return a == 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
|
return a == b && ap == bp
|
||||||
case (.loadingIndicator, .loadingIndicator):
|
case (.loadingIndicator, .loadingIndicator):
|
||||||
return true
|
return true
|
||||||
|
@ -316,7 +324,7 @@ extension ProfileStatusesViewController {
|
||||||
case .header(let id):
|
case .header(let id):
|
||||||
hasher.combine(0)
|
hasher.combine(0)
|
||||||
hasher.combine(id)
|
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(1)
|
||||||
hasher.combine(id)
|
hasher.combine(id)
|
||||||
hasher.combine(pinned)
|
hasher.combine(pinned)
|
||||||
|
@ -338,7 +346,7 @@ extension ProfileStatusesViewController {
|
||||||
|
|
||||||
var isSelectable: Bool {
|
var isSelectable: Bool {
|
||||||
switch self {
|
switch self {
|
||||||
case .status(id: _, state: _, pinned: _):
|
case .status(_, _, _, _):
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
|
@ -445,11 +453,20 @@ extension ProfileStatusesViewController: UICollectionViewDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
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
|
return
|
||||||
}
|
}
|
||||||
let status = mastodonController.persistentContainer.status(for: id)!
|
if filterState.isWarning {
|
||||||
selected(status: status.reblog?.id ?? id, state: state.copy())
|
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? {
|
func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
|
||||||
|
|
|
@ -144,7 +144,11 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
||||||
return UICollectionViewDiffableDataSource(collectionView: collectionView) { [unowned self] collectionView, indexPath, itemIdentifier in
|
return UICollectionViewDiffableDataSource(collectionView: collectionView) { [unowned self] collectionView, indexPath, itemIdentifier in
|
||||||
switch itemIdentifier {
|
switch itemIdentifier {
|
||||||
case .status(id: let id, collapseState: let state, filterState: let filterState):
|
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)
|
let result = filterState.resolveFor(status: status, resolver: filterer)
|
||||||
switch result {
|
switch result {
|
||||||
case .allow, .warn(_):
|
case .allow, .warn(_):
|
||||||
|
@ -785,7 +789,6 @@ extension TimelineViewController: UICollectionViewDelegate {
|
||||||
case .publicTimelineDescription:
|
case .publicTimelineDescription:
|
||||||
removeTimelineDescriptionCell()
|
removeTimelineDescriptionCell()
|
||||||
case .status(id: let id, collapseState: let collapseState, filterState: let filterState):
|
case .status(id: let id, collapseState: let collapseState, filterState: let filterState):
|
||||||
let status = mastodonController.persistentContainer.status(for: id)!
|
|
||||||
if filterState.isWarning {
|
if filterState.isWarning {
|
||||||
filterState.setResult(.allow)
|
filterState.setResult(.allow)
|
||||||
collectionView.deselectItem(at: indexPath, animated: true)
|
collectionView.deselectItem(at: indexPath, animated: true)
|
||||||
|
@ -793,6 +796,7 @@ extension TimelineViewController: UICollectionViewDelegate {
|
||||||
snapshot.reconfigureItems([item])
|
snapshot.reconfigureItems([item])
|
||||||
dataSource.apply(snapshot, animatingDifferences: true)
|
dataSource.apply(snapshot, animatingDifferences: true)
|
||||||
} else {
|
} 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
|
// 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())
|
selected(status: status.reblog?.id ?? id, state: collapseState.copy())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue