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 {
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)")
}
}
}
// Check if underlying type is Source or Action
func fetchFilteredPlugins<P: Plugin, PJ: PluginJson>(installedPlugins: FetchedResults<P>, searchText: String) -> [PJ] {
let availablePlugins: [PJ] = fetchCastedPlugins(PJ.self)
// forType required to guide generic inferences
func fetchFilteredPlugins<P: Plugin, PJ: PluginJson>(
forType: PJ.Type,
installedPlugins: FetchedResults<P>,
searchText: String
) -> [PJ] {
let availablePlugins: [PJ] = fetchCastedPlugins(forType)
return availablePlugins
.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] = []
let availablePlugins: [PJ] = fetchCastedPlugins(PJ.self)
let availablePlugins: [PJ] = fetchCastedPlugins(forType)
for plugin in installedPlugins {
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
{

View file

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

View file

@ -4,7 +4,6 @@
//
// Created by Brian Dashore on 7/24/22.
//
import SwiftUI
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 isSearching = false
@State private var filteredUpdatedPlugins: [PJ] = []
@State private var filteredAvailablePlugins: [PJ] = []
@State private var sourcePredicate: NSPredicate?
var body: some View {
DynamicFetchRequest(predicate: sourcePredicate) { (installedPlugins: FetchedResults<P>) in
List {
if !filteredUpdatedPlugins.isEmpty {
if
let filteredUpdatedPlugins = pluginManager.fetchUpdatedPlugins(
forType: PJ.self,
installedPlugins: installedPlugins,
searchText: searchText
),
!filteredUpdatedPlugins.isEmpty
{
Section(header: InlineHeader("Updates")) {
ForEach(filteredUpdatedPlugins, id: \.self) { (updatedPlugin: PJ) in
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")) {
ForEach(filteredAvailablePlugins, id: \.self) { availablePlugin in
if !installedPlugins.contains(where: {
availablePlugin.name == $0.name &&
availablePlugin.listId == $0.listId &&
availablePlugin.author == $0.author
}) {
PluginCatalogButtonView(availablePlugin: availablePlugin, doUpsert: false)
}
PluginCatalogButtonView(availablePlugin: availablePlugin, doUpsert: false)
}
}
}
@ -65,18 +70,8 @@ struct PluginListView<P: Plugin, PJ: PluginJson>: View {
.environmentObject(navModel)
}
}
.backport.onAppear {
filteredAvailablePlugins = pluginManager.fetchFilteredPlugins(installedPlugins: installedPlugins, searchText: searchText)
filteredUpdatedPlugins = pluginManager.fetchUpdatedPlugins(installedPlugins: installedPlugins, searchText: searchText)
}
.onChange(of: searchText) { _ in
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)
}
}
}