parent
01467574d0
commit
ce708e2d16
|
@ -45,9 +45,12 @@ class Filterer {
|
|||
var htmlConverter = HTMLConverter()
|
||||
private var hasSetup = false
|
||||
private var matchers = [(NSRegularExpression, Result)]()
|
||||
private var allFiltersObserver: AnyCancellable?
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var filterObservers = Set<AnyCancellable>()
|
||||
|
||||
private var hideReblogsInTimelines: Bool
|
||||
private var hideRepliesInTimelines: Bool
|
||||
|
||||
// the generation is incremented when the matchers change, to indicate that older cached FilterStates
|
||||
// are no longer valid, without needing to go through and update each of them
|
||||
private var generation = 0
|
||||
|
@ -55,13 +58,37 @@ class Filterer {
|
|||
init(mastodonController: MastodonController, context: FilterV1.Context) {
|
||||
self.mastodonController = mastodonController
|
||||
self.context = context
|
||||
self.hideReblogsInTimelines = Preferences.shared.hideReblogsInTimelines
|
||||
self.hideRepliesInTimelines = Preferences.shared.hideRepliesInTimelines
|
||||
|
||||
allFiltersObserver = mastodonController.$filters
|
||||
mastodonController.$filters
|
||||
.sink { [unowned self] in
|
||||
if self.hasSetup {
|
||||
self.setupFilters(filters: $0)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
if context == .home {
|
||||
Preferences.shared.$hideReblogsInTimelines
|
||||
.sink { [unowned self] newValue in
|
||||
if newValue != hideReblogsInTimelines {
|
||||
self.hideReblogsInTimelines = newValue
|
||||
self.generation += 1
|
||||
self.filtersChanged?(true)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
Preferences.shared.$hideRepliesInTimelines
|
||||
.sink { [unowned self] newValue in
|
||||
if newValue != hideRepliesInTimelines {
|
||||
self.hideRepliesInTimelines = newValue
|
||||
self.generation += 1
|
||||
self.filtersChanged?(true)
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
}
|
||||
|
||||
private func setupFilters(filters: [FilterMO]) {
|
||||
|
@ -86,7 +113,7 @@ class Filterer {
|
|||
}
|
||||
|
||||
if hasSetup {
|
||||
var allMatch: Bool = false
|
||||
var allMatch: Bool = true
|
||||
var actionsChanged: Bool = false
|
||||
if matchers.count != oldMatchers.count {
|
||||
allMatch = false
|
||||
|
@ -114,12 +141,13 @@ class Filterer {
|
|||
}
|
||||
|
||||
// Use a closure for the status in case the result is cached and we don't need to look it up
|
||||
func resolve(state: FilterState, status: () -> StatusMO) -> (Filterer.Result, NSAttributedString?) {
|
||||
func resolve(state: FilterState, status: () -> (StatusMO, Bool)) -> (Filterer.Result, NSAttributedString?) {
|
||||
switch state.state {
|
||||
case .known(_, generation: let knownGen) where knownGen < generation:
|
||||
fallthrough
|
||||
case .unknown:
|
||||
let (result, attributedString) = doResolve(status: status())
|
||||
let (status, isReblog) = status()
|
||||
let (result, attributedString) = doResolve(status: status, isReblog: isReblog)
|
||||
state.state = .known(result, generation: generation)
|
||||
return (result, attributedString)
|
||||
case .known(let result, _):
|
||||
|
@ -140,10 +168,19 @@ class Filterer {
|
|||
}
|
||||
}
|
||||
|
||||
private func doResolve(status: StatusMO) -> (Result, NSAttributedString?) {
|
||||
private func doResolve(status: StatusMO, isReblog: Bool) -> (Result, NSAttributedString?) {
|
||||
if !hasSetup {
|
||||
setupFilters(filters: mastodonController.filters)
|
||||
}
|
||||
if context == .home {
|
||||
if hideReblogsInTimelines,
|
||||
isReblog {
|
||||
return (.hide, nil)
|
||||
} else if hideRepliesInTimelines,
|
||||
status.inReplyToID != nil {
|
||||
return (.hide, nil)
|
||||
}
|
||||
}
|
||||
if matchers.isEmpty {
|
||||
return (.allow, nil)
|
||||
}
|
||||
|
|
|
@ -70,6 +70,8 @@ class Preferences: Codable, ObservableObject {
|
|||
self.oppositeCollapseKeywords = try container.decodeIfPresent([String].self, forKey: .oppositeCollapseKeywords) ?? []
|
||||
self.confirmBeforeReblog = try container.decodeIfPresent(Bool.self, forKey: .confirmBeforeReblog) ?? false
|
||||
self.timelineStateRestoration = try container.decodeIfPresent(Bool.self, forKey: .timelineStateRestoration) ?? true
|
||||
self.hideReblogsInTimelines = try container.decodeIfPresent(Bool.self, forKey: .hideReblogsInTimelines) ?? false
|
||||
self.hideRepliesInTimelines = try container.decodeIfPresent(Bool.self, forKey: .hideRepliesInTimelines) ?? false
|
||||
|
||||
self.showFavoriteAndReblogCounts = try container.decode(Bool.self, forKey: .showFavoriteAndReblogCounts)
|
||||
self.defaultNotificationsMode = try container.decode(NotificationsMode.self, forKey: .defaultNotificationsType)
|
||||
|
@ -115,6 +117,8 @@ class Preferences: Codable, ObservableObject {
|
|||
try container.encode(oppositeCollapseKeywords, forKey: .oppositeCollapseKeywords)
|
||||
try container.encode(confirmBeforeReblog, forKey: .confirmBeforeReblog)
|
||||
try container.encode(timelineStateRestoration, forKey: .timelineStateRestoration)
|
||||
try container.encode(hideReblogsInTimelines, forKey: .hideReblogsInTimelines)
|
||||
try container.encode(hideRepliesInTimelines, forKey: .hideRepliesInTimelines)
|
||||
|
||||
try container.encode(showFavoriteAndReblogCounts, forKey: .showFavoriteAndReblogCounts)
|
||||
try container.encode(defaultNotificationsMode, forKey: .defaultNotificationsType)
|
||||
|
@ -169,6 +173,8 @@ class Preferences: Codable, ObservableObject {
|
|||
@Published var oppositeCollapseKeywords: [String] = []
|
||||
@Published var confirmBeforeReblog = false
|
||||
@Published var timelineStateRestoration = true
|
||||
@Published var hideReblogsInTimelines = false
|
||||
@Published var hideRepliesInTimelines = false
|
||||
|
||||
// MARK: Digital Wellness
|
||||
@Published var showFavoriteAndReblogCounts = true
|
||||
|
@ -216,6 +222,8 @@ class Preferences: Codable, ObservableObject {
|
|||
case oppositeCollapseKeywords
|
||||
case confirmBeforeReblog
|
||||
case timelineStateRestoration
|
||||
case hideReblogsInTimelines
|
||||
case hideRepliesInTimelines
|
||||
|
||||
case showFavoriteAndReblogCounts
|
||||
case defaultNotificationsType
|
||||
|
|
|
@ -81,7 +81,7 @@ class TrendingStatusesViewController: UIViewController {
|
|||
return UICollectionViewDiffableDataSource(collectionView: collectionView) { [unowned self] collectionView, indexPath, itemIdentifier in
|
||||
switch itemIdentifier {
|
||||
case .status(id: let id, let collapseState, let filterState):
|
||||
let (result, attributedString) = self.filterer.resolve(state: filterState, status: { mastodonController.persistentContainer.status(for: id)! })
|
||||
let (result, attributedString) = self.filterer.resolve(state: filterState, status: { (mastodonController.persistentContainer.status(for: id)!, false) })
|
||||
switch result {
|
||||
case .allow, .warn(_):
|
||||
return collectionView.dequeueConfiguredReusableCell(using: statusCell, for: indexPath, item: (id, collapseState, result, attributedString))
|
||||
|
|
|
@ -22,6 +22,7 @@ struct FiltersView: View {
|
|||
|
||||
struct FiltersList: View {
|
||||
@EnvironmentObject private var mastodonController: MastodonController
|
||||
@ObservedObject private var preferences = Preferences.shared
|
||||
@FetchRequest(sortDescriptors: []) private var filters: FetchedResults<FilterMO>
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var deletionError: (any Error)?
|
||||
|
@ -49,6 +50,17 @@ struct FiltersList: View {
|
|||
|
||||
private var navigationBody: some View {
|
||||
List {
|
||||
Section {
|
||||
Toggle(isOn: $preferences.hideReblogsInTimelines) {
|
||||
Text("Hide Reblogs")
|
||||
}
|
||||
Toggle(isOn: $preferences.hideRepliesInTimelines) {
|
||||
Text("Hide Replies")
|
||||
}
|
||||
} header: {
|
||||
Text("Home Timeline")
|
||||
}
|
||||
|
||||
Section {
|
||||
NavigationLink {
|
||||
EditFilterView(filter: EditedFilter(), create: true, originallyExpired: false)
|
||||
|
|
|
@ -35,6 +35,12 @@ struct BehaviorPrefsView: View {
|
|||
Toggle(isOn: $preferences.timelineStateRestoration) {
|
||||
Text("Maintain Position Across App Launches")
|
||||
}
|
||||
Toggle(isOn: $preferences.hideReblogsInTimelines) {
|
||||
Text("Hide Reblogs")
|
||||
}
|
||||
Toggle(isOn: $preferences.hideRepliesInTimelines) {
|
||||
Text("Hide Replies")
|
||||
}
|
||||
} header: {
|
||||
Text("Timeline")
|
||||
}
|
||||
|
|
|
@ -261,7 +261,11 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
|
|||
let status = {
|
||||
let status = self.mastodonController.persistentContainer.status(for: statusID)!
|
||||
// if the status is a reblog of another one, filter based on that one
|
||||
return status.reblog ?? status
|
||||
if let reblogged = status.reblog {
|
||||
return (reblogged, true)
|
||||
} else {
|
||||
return (status, false)
|
||||
}
|
||||
}
|
||||
return filterer.resolve(state: state, status: status)
|
||||
}
|
||||
|
|
|
@ -354,7 +354,11 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
|||
let status = {
|
||||
let status = self.mastodonController.persistentContainer.status(for: statusID)!
|
||||
// if the status is a reblog of another one, filter based on that one
|
||||
return status.reblog ?? status
|
||||
if let reblogged = status.reblog {
|
||||
return (reblogged, true)
|
||||
} else {
|
||||
return (status, false)
|
||||
}
|
||||
}
|
||||
return filterer.resolve(state: state, status: status)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue