Plugins: Fix list sync in view

Plugin entries were not syncing properly inside the plugins view.
Instead of adding events to update filtered lists, make it so that
these filtered lists are updated on state changes.

This is the intended method of reactive programming and removes
complexity from filtering logic.

Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
kingbri 2023-03-02 11:06:57 -05:00
parent f622b7af05
commit 282783c460
4 changed files with 34 additions and 35 deletions

View file

@ -11,8 +11,4 @@ extension Notification.Name {
static var didDeleteBookmark: Notification.Name { static var didDeleteBookmark: Notification.Name {
Notification.Name("Deleted bookmark") Notification.Name("Deleted bookmark")
} }
static var didDeletePlugin: Notification.Name {
Notification.Name("Deleted plugin")
}
} }

View file

@ -89,12 +89,16 @@ public class PluginManager: ObservableObject {
} }
print("Plugin fetch error: \(error)") print("Plugin fetch error: \(error)")
} }
} }
// Check if underlying type is Source or Action // forType required to guide generic inferences
func fetchFilteredPlugins<P: Plugin, PJ: PluginJson>(installedPlugins: FetchedResults<P>, searchText: String) -> [PJ] { func fetchFilteredPlugins<P: Plugin, PJ: PluginJson>(
let availablePlugins: [PJ] = fetchCastedPlugins(PJ.self) forType: PJ.Type,
installedPlugins: FetchedResults<P>,
searchText: String
) -> [PJ] {
let availablePlugins: [PJ] = fetchCastedPlugins(forType)
return availablePlugins return availablePlugins
.filter { availablePlugin in .filter { availablePlugin in
@ -112,13 +116,19 @@ public class PluginManager: ObservableObject {
} }
} }
func fetchUpdatedPlugins<P: Plugin, PJ: PluginJson>(installedPlugins: FetchedResults<P>, searchText: String) -> [PJ] { func fetchUpdatedPlugins<P: Plugin, PJ: PluginJson>(
forType: PJ.Type,
installedPlugins: FetchedResults<P>,
searchText: String
) -> [PJ] {
var updatedPlugins: [PJ] = [] var updatedPlugins: [PJ] = []
let availablePlugins: [PJ] = fetchCastedPlugins(PJ.self) let availablePlugins: [PJ] = fetchCastedPlugins(forType)
for plugin in installedPlugins { for plugin in installedPlugins {
if let availablePlugin = availablePlugins.first(where: { if let availablePlugin = availablePlugins.first(where: {
plugin.listId == $0.listId && plugin.name == $0.name && plugin.author == $0.author plugin.listId == $0.listId &&
plugin.name == $0.name &&
plugin.author == $0.author
}), }),
availablePlugin.version > plugin.version availablePlugin.version > plugin.version
{ {

View file

@ -54,7 +54,6 @@ struct InstalledPluginButtonView<P: Plugin>: View {
if #available(iOS 15.0, *) { if #available(iOS 15.0, *) {
Button(role: .destructive) { Button(role: .destructive) {
PersistenceController.shared.delete(installedPlugin, context: backgroundContext) PersistenceController.shared.delete(installedPlugin, context: backgroundContext)
NotificationCenter.default.post(name: .didDeletePlugin, object: nil)
} label: { } label: {
Text("Remove") Text("Remove")
Image(systemName: "trash") Image(systemName: "trash")
@ -62,7 +61,6 @@ struct InstalledPluginButtonView<P: Plugin>: View {
} else { } else {
Button { Button {
PersistenceController.shared.delete(installedPlugin, context: backgroundContext) PersistenceController.shared.delete(installedPlugin, context: backgroundContext)
NotificationCenter.default.post(name: .didDeletePlugin, object: nil)
} label: { } label: {
Text("Remove") Text("Remove")
Image(systemName: "trash") Image(systemName: "trash")

View file

@ -4,7 +4,6 @@
// //
// Created by Brian Dashore on 7/24/22. // Created by Brian Dashore on 7/24/22.
// //
import SwiftUI import SwiftUI
struct PluginListView<P: Plugin, PJ: PluginJson>: View { struct PluginListView<P: Plugin, PJ: PluginJson>: View {
@ -20,14 +19,19 @@ struct PluginListView<P: Plugin, PJ: PluginJson>: View {
@State private var isEditingSearch = false @State private var isEditingSearch = false
@State private var isSearching = false @State private var isSearching = false
@State private var filteredUpdatedPlugins: [PJ] = []
@State private var filteredAvailablePlugins: [PJ] = []
@State private var sourcePredicate: NSPredicate? @State private var sourcePredicate: NSPredicate?
var body: some View { var body: some View {
DynamicFetchRequest(predicate: sourcePredicate) { (installedPlugins: FetchedResults<P>) in DynamicFetchRequest(predicate: sourcePredicate) { (installedPlugins: FetchedResults<P>) in
List { List {
if !filteredUpdatedPlugins.isEmpty { if
let filteredUpdatedPlugins = pluginManager.fetchUpdatedPlugins(
forType: PJ.self,
installedPlugins: installedPlugins,
searchText: searchText
),
!filteredUpdatedPlugins.isEmpty
{
Section(header: InlineHeader("Updates")) { Section(header: InlineHeader("Updates")) {
ForEach(filteredUpdatedPlugins, id: \.self) { (updatedPlugin: PJ) in ForEach(filteredUpdatedPlugins, id: \.self) { (updatedPlugin: PJ) in
PluginCatalogButtonView(availablePlugin: updatedPlugin, doUpsert: true) PluginCatalogButtonView(availablePlugin: updatedPlugin, doUpsert: true)
@ -43,16 +47,17 @@ struct PluginListView<P: Plugin, PJ: PluginJson>: View {
} }
} }
if !filteredAvailablePlugins.isEmpty { if
let filteredAvailablePlugins = pluginManager.fetchFilteredPlugins(
forType: PJ.self,
installedPlugins: installedPlugins,
searchText: searchText
),
!filteredAvailablePlugins.isEmpty
{
Section(header: InlineHeader("Catalog")) { Section(header: InlineHeader("Catalog")) {
ForEach(filteredAvailablePlugins, id: \.self) { availablePlugin in ForEach(filteredAvailablePlugins, id: \.self) { availablePlugin in
if !installedPlugins.contains(where: { PluginCatalogButtonView(availablePlugin: availablePlugin, doUpsert: false)
availablePlugin.name == $0.name &&
availablePlugin.listId == $0.listId &&
availablePlugin.author == $0.author
}) {
PluginCatalogButtonView(availablePlugin: availablePlugin, doUpsert: false)
}
} }
} }
} }
@ -65,18 +70,8 @@ struct PluginListView<P: Plugin, PJ: PluginJson>: View {
.environmentObject(navModel) .environmentObject(navModel)
} }
} }
.backport.onAppear {
filteredAvailablePlugins = pluginManager.fetchFilteredPlugins(installedPlugins: installedPlugins, searchText: searchText)
filteredUpdatedPlugins = pluginManager.fetchUpdatedPlugins(installedPlugins: installedPlugins, searchText: searchText)
}
.onChange(of: searchText) { _ in .onChange(of: searchText) { _ in
sourcePredicate = searchText.isEmpty ? nil : NSPredicate(format: "name CONTAINS[cd] %@", searchText) sourcePredicate = searchText.isEmpty ? nil : NSPredicate(format: "name CONTAINS[cd] %@", searchText)
filteredAvailablePlugins = pluginManager.fetchFilteredPlugins(installedPlugins: installedPlugins, searchText: searchText)
filteredUpdatedPlugins = pluginManager.fetchUpdatedPlugins(installedPlugins: installedPlugins, searchText: searchText)
}
.onReceive(NotificationCenter.default.publisher(for: .didDeletePlugin)) { _ in
filteredAvailablePlugins = pluginManager.fetchFilteredPlugins(installedPlugins: installedPlugins, searchText: searchText)
filteredUpdatedPlugins = pluginManager.fetchUpdatedPlugins(installedPlugins: installedPlugins, searchText: searchText)
} }
} }
} }