mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-28 19:11:30 +00:00
Filter management
This commit is contained in:
parent
b073f31e77
commit
cd59aeab0e
8 changed files with 72 additions and 31 deletions
|
@ -142,6 +142,20 @@ extension ContentDatabase {
|
||||||
.publisher(in: databaseQueue)
|
.publisher(in: databaseQueue)
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func activeFiltersObservation(date: Date) -> AnyPublisher<[Filter], Error> {
|
||||||
|
ValueObservation.tracking(Filter.filter(Column("expiresAt") == nil || Column("expiresAt") > date).fetchAll)
|
||||||
|
.removeDuplicates()
|
||||||
|
.publisher(in: databaseQueue)
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
func expiredFiltersObservation(date: Date) -> AnyPublisher<[Filter], Error> {
|
||||||
|
ValueObservation.tracking(Filter.filter(Column("expiresAt") < date).fetchAll)
|
||||||
|
.removeDuplicates()
|
||||||
|
.publisher(in: databaseQueue)
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension ContentDatabase {
|
private extension ContentDatabase {
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
"preferences.notification-types.reblog" = "Reblog";
|
"preferences.notification-types.reblog" = "Reblog";
|
||||||
"preferences.notification-types.mention" = "Mention";
|
"preferences.notification-types.mention" = "Mention";
|
||||||
"preferences.notification-types.poll" = "Poll";
|
"preferences.notification-types.poll" = "Poll";
|
||||||
|
"filters.active" = "Active";
|
||||||
|
"filters.expired" = "Expired";
|
||||||
"filter.add-new" = "Add New Filter";
|
"filter.add-new" = "Add New Filter";
|
||||||
"filter.edit" = "Edit Filter";
|
"filter.edit" = "Edit Filter";
|
||||||
"filter.keyword-or-phrase" = "Keyword or phrase";
|
"filter.keyword-or-phrase" = "Keyword or phrase";
|
||||||
|
|
|
@ -78,17 +78,9 @@ private extension FilterEndpoint {
|
||||||
"whole_word": wholeWord]
|
"whole_word": wholeWord]
|
||||||
|
|
||||||
if let expiresIn = expiresIn {
|
if let expiresIn = expiresIn {
|
||||||
params["expires_in"] = Self.dateFormatter.string(from: expiresIn)
|
params["expires_in"] = Int(expiresIn.timeIntervalSinceNow)
|
||||||
}
|
}
|
||||||
|
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
|
|
||||||
static let dateFormatter: DateFormatter = {
|
|
||||||
let dateFormatter = DateFormatter()
|
|
||||||
|
|
||||||
dateFormatter.dateFormat = MastodonAPI.dateFormat
|
|
||||||
|
|
||||||
return dateFormatter
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -143,8 +143,12 @@ extension IdentityService {
|
||||||
.eraseToAnyPublisher()
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
func filtersObservation() -> AnyPublisher<[Filter], Error> {
|
func activeFiltersObservation(date: Date) -> AnyPublisher<[Filter], Error> {
|
||||||
contentDatabase.filtersObservation()
|
contentDatabase.activeFiltersObservation(date: date)
|
||||||
|
}
|
||||||
|
|
||||||
|
func expiredFiltersObservation(date: Date) -> AnyPublisher<[Filter], Error> {
|
||||||
|
contentDatabase.expiredFiltersObservation(date: date)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updatePreferences(_ preferences: Identity.Preferences) -> AnyPublisher<Never, Error> {
|
func updatePreferences(_ preferences: Identity.Preferences) -> AnyPublisher<Never, Error> {
|
||||||
|
|
|
@ -7,10 +7,12 @@ class EditFilterViewModel: ObservableObject {
|
||||||
@Published var filter: Filter
|
@Published var filter: Filter
|
||||||
@Published var saving = false
|
@Published var saving = false
|
||||||
@Published var alertItem: AlertItem?
|
@Published var alertItem: AlertItem?
|
||||||
var date: Date
|
|
||||||
let dateRange: ClosedRange<Date>
|
|
||||||
let saveCompleted: AnyPublisher<Void, Never>
|
let saveCompleted: AnyPublisher<Void, Never>
|
||||||
|
|
||||||
|
var date: Date {
|
||||||
|
didSet { filter.expiresAt = date }
|
||||||
|
}
|
||||||
|
|
||||||
private let identityService: IdentityService
|
private let identityService: IdentityService
|
||||||
private let saveCompletedInput = PassthroughSubject<Void, Never>()
|
private let saveCompletedInput = PassthroughSubject<Void, Never>()
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
@ -18,8 +20,7 @@ class EditFilterViewModel: ObservableObject {
|
||||||
init(filter: Filter, identityService: IdentityService) {
|
init(filter: Filter, identityService: IdentityService) {
|
||||||
self.filter = filter
|
self.filter = filter
|
||||||
self.identityService = identityService
|
self.identityService = identityService
|
||||||
date = Calendar.autoupdatingCurrent.date(byAdding: .minute, value: 30, to: Date()) ?? Date()
|
date = filter.expiresAt ?? Date()
|
||||||
dateRange = date...(Calendar.autoupdatingCurrent.date(byAdding: .day, value: 7, to: date) ?? Date())
|
|
||||||
saveCompleted = saveCompletedInput.eraseToAnyPublisher()
|
saveCompleted = saveCompletedInput.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,8 @@ import Foundation
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
class FiltersViewModel: ObservableObject {
|
class FiltersViewModel: ObservableObject {
|
||||||
@Published var filters = [Filter]()
|
@Published var activeFilters = [Filter]()
|
||||||
|
@Published var expiredFilters = [Filter]()
|
||||||
@Published var alertItem: AlertItem?
|
@Published var alertItem: AlertItem?
|
||||||
|
|
||||||
private let identityService: IdentityService
|
private let identityService: IdentityService
|
||||||
|
@ -13,9 +14,15 @@ class FiltersViewModel: ObservableObject {
|
||||||
init(identityService: IdentityService) {
|
init(identityService: IdentityService) {
|
||||||
self.identityService = identityService
|
self.identityService = identityService
|
||||||
|
|
||||||
identityService.filtersObservation()
|
let now = Date()
|
||||||
|
|
||||||
|
identityService.activeFiltersObservation(date: now)
|
||||||
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
.assign(to: &$filters)
|
.assign(to: &$activeFilters)
|
||||||
|
|
||||||
|
identityService.expiredFiltersObservation(date: now)
|
||||||
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
|
.assign(to: &$expiredFilters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +34,13 @@ extension FiltersViewModel {
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func delete(filter: Filter) {
|
||||||
|
identityService.deleteFilter(id: filter.id)
|
||||||
|
.assignErrorsToAlertItem(to: \.alertItem, on: self)
|
||||||
|
.sink { _ in }
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
|
|
||||||
func editFilterViewModel(filter: Filter) -> EditFilterViewModel {
|
func editFilterViewModel(filter: Filter) -> EditFilterViewModel {
|
||||||
EditFilterViewModel(filter: filter, identityService: identityService)
|
EditFilterViewModel(filter: filter, identityService: identityService)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,17 +16,11 @@ struct EditFilterView: View {
|
||||||
if viewModel.isNew || viewModel.filter.expiresAt == nil {
|
if viewModel.isNew || viewModel.filter.expiresAt == nil {
|
||||||
Toggle("filter.never-expires", isOn: .init(
|
Toggle("filter.never-expires", isOn: .init(
|
||||||
get: { viewModel.filter.expiresAt == nil },
|
get: { viewModel.filter.expiresAt == nil },
|
||||||
set: {
|
set: { viewModel.filter.expiresAt = $0 ? nil : viewModel.date }))
|
||||||
if $0 {
|
|
||||||
viewModel.filter.expiresAt = nil
|
|
||||||
} else {
|
|
||||||
viewModel.filter.expiresAt = viewModel.date
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if viewModel.filter.expiresAt != nil {
|
if viewModel.filter.expiresAt != nil {
|
||||||
DatePicker(selection: $viewModel.date, in: viewModel.dateRange) {
|
DatePicker(selection: $viewModel.date, in: Date()...) {
|
||||||
Text("filter.expire-after")
|
Text("filter.expire-after")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,26 @@ struct FiltersView: View {
|
||||||
Label("add", systemImage: "plus.circle")
|
Label("add", systemImage: "plus.circle")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Section {
|
section(title: "filters.active", filters: viewModel.activeFilters)
|
||||||
ForEach(viewModel.filters) { filter in
|
section(title: "filters.expired", filters: viewModel.expiredFilters)
|
||||||
|
}
|
||||||
|
.navigationTitle("preferences.filters")
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItem(placement: ToolbarItemPlacement.navigationBarTrailing) {
|
||||||
|
EditButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.alertItem($viewModel.alertItem)
|
||||||
|
.onAppear(perform: viewModel.refreshFilters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension FiltersView {
|
||||||
|
@ViewBuilder
|
||||||
|
func section(title: LocalizedStringKey, filters: [Filter]) -> some View {
|
||||||
|
if !filters.isEmpty {
|
||||||
|
Section(header: Text(title)) {
|
||||||
|
ForEach(filters) { filter in
|
||||||
NavigationLink(destination: EditFilterView(
|
NavigationLink(destination: EditFilterView(
|
||||||
viewModel: viewModel.editFilterViewModel(filter: filter))) {
|
viewModel: viewModel.editFilterViewModel(filter: filter))) {
|
||||||
HStack {
|
HStack {
|
||||||
|
@ -25,11 +43,13 @@ struct FiltersView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onDelete {
|
||||||
|
guard let index = $0.first else { return }
|
||||||
|
|
||||||
|
viewModel.delete(filter: filters[index])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle("preferences.filters")
|
|
||||||
.alertItem($viewModel.alertItem)
|
|
||||||
.onAppear(perform: viewModel.refreshFilters)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue