Fetch filters and store in CoreData

This commit is contained in:
Shadowfacts 2022-11-30 22:16:33 -05:00
parent 0247c50650
commit c9fa11cc3b
6 changed files with 119 additions and 1 deletions

View File

@ -8,7 +8,7 @@
import Foundation
public class Filter: Decodable {
public struct Filter: Decodable {
public let id: String
public let phrase: String
private let context: [String]
@ -51,6 +51,7 @@ extension Filter {
case notifications
case `public`
case thread
case account
}
}

View File

@ -52,6 +52,7 @@
D61F759229365C6C00C0B37F /* CollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61F759129365C6C00C0B37F /* CollectionViewController.swift */; };
D61F75942936F0DA00C0B37F /* FollowedHashtag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61F75932936F0DA00C0B37F /* FollowedHashtag.swift */; };
D61F75962937037800C0B37F /* ToggleFollowHashtagService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61F75952937037800C0B37F /* ToggleFollowHashtagService.swift */; };
D61F759B29384F9C00C0B37F /* FilterMO.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61F759A29384F9C00C0B37F /* FilterMO.swift */; };
D620483423D3801D008A63EF /* LinkTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483323D3801D008A63EF /* LinkTextView.swift */; };
D620483623D38075008A63EF /* ContentTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483523D38075008A63EF /* ContentTextView.swift */; };
D620483823D38190008A63EF /* StatusContentTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483723D38190008A63EF /* StatusContentTextView.swift */; };
@ -420,6 +421,7 @@
D61F759129365C6C00C0B37F /* CollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewController.swift; sourceTree = "<group>"; };
D61F75932936F0DA00C0B37F /* FollowedHashtag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowedHashtag.swift; sourceTree = "<group>"; };
D61F75952937037800C0B37F /* ToggleFollowHashtagService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleFollowHashtagService.swift; sourceTree = "<group>"; };
D61F759A29384F9C00C0B37F /* FilterMO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterMO.swift; sourceTree = "<group>"; };
D620483323D3801D008A63EF /* LinkTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkTextView.swift; sourceTree = "<group>"; };
D620483523D38075008A63EF /* ContentTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentTextView.swift; sourceTree = "<group>"; };
D620483723D38190008A63EF /* StatusContentTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusContentTextView.swift; sourceTree = "<group>"; };
@ -886,6 +888,7 @@
D6B9366E2828452F00237D0E /* SavedHashtag.swift */,
D6B9366C2828444F00237D0E /* SavedInstance.swift */,
D61F75932936F0DA00C0B37F /* FollowedHashtag.swift */,
D61F759A29384F9C00C0B37F /* FilterMO.swift */,
D60E2F2D244248BF005F8713 /* MastodonCachePersistentStore.swift */,
D6B936702829F72900237D0E /* NSManagedObjectContext+Helpers.swift */,
);
@ -1824,6 +1827,7 @@
D69693FA25859A8000F4E116 /* ComposeSceneDelegate.swift in Sources */,
D6A3BC8A2321F79B00FD64D5 /* AccountTableViewCell.swift in Sources */,
D66A77BB233838DC0058F1EC /* UIFont+Traits.swift in Sources */,
D61F759B29384F9C00C0B37F /* FilterMO.swift in Sources */,
D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */,
D64AAE9126C80DC600FC57FB /* ToastView.swift in Sources */,
D62275AA24F1E01C00B82A16 /* ComposeTextView.swift in Sources */,

View File

@ -50,6 +50,7 @@ class MastodonController: ObservableObject {
@Published private(set) var lists: [List] = []
@Published private(set) var customEmojis: [Emoji]?
@Published private(set) var followedHashtags: [FollowedHashtag] = []
@Published private(set) var filters: [FilterMO] = []
private var cancellables = Set<AnyCancellable>()
@ -154,6 +155,7 @@ class MastodonController: ObservableObject {
_ = try await (ownAccount, ownInstance)
loadLists()
async let _ = await loadFilters()
}
func getOwnAccount(completion: ((Result<Account, Client.Error>) -> Void)? = nil) {
@ -350,6 +352,20 @@ class MastodonController: ObservableObject {
followedHashtags = (try? persistentContainer.viewContext.fetch(FollowedHashtag.fetchRequest())) ?? []
}
@MainActor
func loadFilters() async {
filters = (try? persistentContainer.viewContext.fetch(FilterMO.fetchRequest())) ?? []
let req = Client.getFilters()
if let (filters, _) = try? await run(req) {
self.persistentContainer.updateFilters(filters) {
if case .success(let filters) = $0 {
self.filters = filters
}
}
}
}
}
private struct ListComparator: SortComparator {

View File

@ -0,0 +1,66 @@
//
// FilterMO.swift
// Tusker
//
// Created by Shadowfacts on 11/30/22.
// Copyright © 2022 Shadowfacts. All rights reserved.
//
import Foundation
import CoreData
import Pachyderm
@objc(FilterMO)
public final class FilterMO: NSManagedObject {
@nonobjc public class func fetchRequest() -> NSFetchRequest<FilterMO> {
return NSFetchRequest(entityName: "Filter")
}
@NSManaged public var id: String
@NSManaged public var phrase: String
@NSManaged private var context: String
@NSManaged public var expiresAt: Date?
@NSManaged public var irreversible: Bool
@NSManaged public var wholeWord: Bool
private var _contexts: [Filter.Context]?
var contexts: [Filter.Context] {
get {
if let _contexts {
return _contexts
} else {
_contexts = context.split(separator: ",").compactMap { .init(rawValue: String($0)) }
return _contexts!
}
}
set {
_contexts = newValue
context = newValue.map(\.rawValue).joined(separator: ",")
}
}
public override func didChangeValue(forKey key: String) {
super.didChangeValue(forKey: key)
if key == "context" {
_contexts = nil
}
}
}
extension FilterMO {
convenience init(apiFilter filter: Filter, context: NSManagedObjectContext) {
self.init(context: context)
self.updateFrom(apiFilter: filter)
}
func updateFrom(apiFilter filter: Filter) {
self.id = filter.id
self.phrase = filter.phrase
self.contexts = filter.contexts
self.expiresAt = filter.expiresAt
self.irreversible = filter.irreversible
self.wholeWord = filter.wholeWord
}
}

View File

@ -299,6 +299,29 @@ class MastodonCachePersistentStore: NSPersistentContainer {
}
}
func updateFilters(_ filters: [Filter], completion: @escaping (Result<[FilterMO], Error>) -> Void) {
viewContext.perform {
do {
var all = try self.viewContext.fetch(FilterMO.fetchRequest())
let toDelete = all.filter { existing in !filters.contains(where: { $0.id == existing.id }) }.map(\.objectID)
if !toDelete.isEmpty {
try self.viewContext.execute(NSBatchDeleteRequest(objectIDs: toDelete))
}
for filter in filters where !all.contains(where: { $0.id == filter.id }) {
let mo = FilterMO(apiFilter: filter, context: self.viewContext)
all.append(mo)
}
self.save(context: self.viewContext)
completion(.success(all))
} catch {
completion(.failure(error))
}
}
}
@objc private func managedObjectsDidChange(_ notification: Foundation.Notification) {
let changes = hasChangedSavedHashtagsOrInstances(notification)
if changes.hashtags {

View File

@ -28,6 +28,14 @@
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="Filter" representedClassName="FilterMO" syncable="YES">
<attribute name="context" attributeType="String"/>
<attribute name="expiresAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="id" attributeType="String"/>
<attribute name="irreversible" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="phrase" attributeType="String"/>
<attribute name="wholeWord" attributeType="Boolean" usesScalarValueType="YES"/>
</entity>
<entity name="FollowedHashtag" representedClassName="FollowedHashtag" syncable="YES">
<attribute name="name" attributeType="String"/>
<attribute name="url" attributeType="URI"/>