forked from shadowfacts/Tusker
Fetch filters and store in CoreData
This commit is contained in:
parent
0247c50650
commit
c9fa11cc3b
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class Filter: Decodable {
|
public struct Filter: Decodable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let phrase: String
|
public let phrase: String
|
||||||
private let context: [String]
|
private let context: [String]
|
||||||
|
@ -51,6 +51,7 @@ extension Filter {
|
||||||
case notifications
|
case notifications
|
||||||
case `public`
|
case `public`
|
||||||
case thread
|
case thread
|
||||||
|
case account
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
D61F759229365C6C00C0B37F /* CollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61F759129365C6C00C0B37F /* CollectionViewController.swift */; };
|
D61F759229365C6C00C0B37F /* CollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61F759129365C6C00C0B37F /* CollectionViewController.swift */; };
|
||||||
D61F75942936F0DA00C0B37F /* FollowedHashtag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61F75932936F0DA00C0B37F /* FollowedHashtag.swift */; };
|
D61F75942936F0DA00C0B37F /* FollowedHashtag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61F75932936F0DA00C0B37F /* FollowedHashtag.swift */; };
|
||||||
D61F75962937037800C0B37F /* ToggleFollowHashtagService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61F75952937037800C0B37F /* ToggleFollowHashtagService.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 */; };
|
D620483423D3801D008A63EF /* LinkTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483323D3801D008A63EF /* LinkTextView.swift */; };
|
||||||
D620483623D38075008A63EF /* ContentTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483523D38075008A63EF /* ContentTextView.swift */; };
|
D620483623D38075008A63EF /* ContentTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483523D38075008A63EF /* ContentTextView.swift */; };
|
||||||
D620483823D38190008A63EF /* StatusContentTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483723D38190008A63EF /* StatusContentTextView.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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
D620483723D38190008A63EF /* StatusContentTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusContentTextView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -886,6 +888,7 @@
|
||||||
D6B9366E2828452F00237D0E /* SavedHashtag.swift */,
|
D6B9366E2828452F00237D0E /* SavedHashtag.swift */,
|
||||||
D6B9366C2828444F00237D0E /* SavedInstance.swift */,
|
D6B9366C2828444F00237D0E /* SavedInstance.swift */,
|
||||||
D61F75932936F0DA00C0B37F /* FollowedHashtag.swift */,
|
D61F75932936F0DA00C0B37F /* FollowedHashtag.swift */,
|
||||||
|
D61F759A29384F9C00C0B37F /* FilterMO.swift */,
|
||||||
D60E2F2D244248BF005F8713 /* MastodonCachePersistentStore.swift */,
|
D60E2F2D244248BF005F8713 /* MastodonCachePersistentStore.swift */,
|
||||||
D6B936702829F72900237D0E /* NSManagedObjectContext+Helpers.swift */,
|
D6B936702829F72900237D0E /* NSManagedObjectContext+Helpers.swift */,
|
||||||
);
|
);
|
||||||
|
@ -1824,6 +1827,7 @@
|
||||||
D69693FA25859A8000F4E116 /* ComposeSceneDelegate.swift in Sources */,
|
D69693FA25859A8000F4E116 /* ComposeSceneDelegate.swift in Sources */,
|
||||||
D6A3BC8A2321F79B00FD64D5 /* AccountTableViewCell.swift in Sources */,
|
D6A3BC8A2321F79B00FD64D5 /* AccountTableViewCell.swift in Sources */,
|
||||||
D66A77BB233838DC0058F1EC /* UIFont+Traits.swift in Sources */,
|
D66A77BB233838DC0058F1EC /* UIFont+Traits.swift in Sources */,
|
||||||
|
D61F759B29384F9C00C0B37F /* FilterMO.swift in Sources */,
|
||||||
D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */,
|
D68FEC4F232C5BC300C84F23 /* SegmentedPageViewController.swift in Sources */,
|
||||||
D64AAE9126C80DC600FC57FB /* ToastView.swift in Sources */,
|
D64AAE9126C80DC600FC57FB /* ToastView.swift in Sources */,
|
||||||
D62275AA24F1E01C00B82A16 /* ComposeTextView.swift in Sources */,
|
D62275AA24F1E01C00B82A16 /* ComposeTextView.swift in Sources */,
|
||||||
|
|
|
@ -50,6 +50,7 @@ class MastodonController: ObservableObject {
|
||||||
@Published private(set) var lists: [List] = []
|
@Published private(set) var lists: [List] = []
|
||||||
@Published private(set) var customEmojis: [Emoji]?
|
@Published private(set) var customEmojis: [Emoji]?
|
||||||
@Published private(set) var followedHashtags: [FollowedHashtag] = []
|
@Published private(set) var followedHashtags: [FollowedHashtag] = []
|
||||||
|
@Published private(set) var filters: [FilterMO] = []
|
||||||
|
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
@ -154,6 +155,7 @@ class MastodonController: ObservableObject {
|
||||||
_ = try await (ownAccount, ownInstance)
|
_ = try await (ownAccount, ownInstance)
|
||||||
|
|
||||||
loadLists()
|
loadLists()
|
||||||
|
async let _ = await loadFilters()
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOwnAccount(completion: ((Result<Account, Client.Error>) -> Void)? = nil) {
|
func getOwnAccount(completion: ((Result<Account, Client.Error>) -> Void)? = nil) {
|
||||||
|
@ -350,6 +352,20 @@ class MastodonController: ObservableObject {
|
||||||
followedHashtags = (try? persistentContainer.viewContext.fetch(FollowedHashtag.fetchRequest())) ?? []
|
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 {
|
private struct ListComparator: SortComparator {
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) {
|
@objc private func managedObjectsDidChange(_ notification: Foundation.Notification) {
|
||||||
let changes = hasChangedSavedHashtagsOrInstances(notification)
|
let changes = hasChangedSavedHashtagsOrInstances(notification)
|
||||||
if changes.hashtags {
|
if changes.hashtags {
|
||||||
|
|
|
@ -28,6 +28,14 @@
|
||||||
</uniquenessConstraint>
|
</uniquenessConstraint>
|
||||||
</uniquenessConstraints>
|
</uniquenessConstraints>
|
||||||
</entity>
|
</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">
|
<entity name="FollowedHashtag" representedClassName="FollowedHashtag" syncable="YES">
|
||||||
<attribute name="name" attributeType="String"/>
|
<attribute name="name" attributeType="String"/>
|
||||||
<attribute name="url" attributeType="URI"/>
|
<attribute name="url" attributeType="URI"/>
|
||||||
|
|
Loading…
Reference in New Issue