From f622b7af05a37997a7d316d48b03c83b0cada97f Mon Sep 17 00:00:00 2001 From: kingbri Date: Wed, 1 Mar 2023 18:31:29 -0500 Subject: [PATCH] Actions: Fix default action settings Since actions use a new API, update default actions to use the same API rather than the legacy models. If an action is removed, a prompt will tell the user to change their default debrid/magnet action and default to the choice sheet. Also add extra UI fixes and cleanup. Signed-off-by: kingbri --- Ferrite.xcodeproj/project.pbxproj | 12 +- Ferrite/Models/SettingsModels.swift | 32 ----- Ferrite/ViewModels/NavigationViewModel.swift | 86 +------------- Ferrite/ViewModels/PluginManager.swift | 54 +++++++++ .../Library/Cloud/AllDebridCloudView.swift | 6 +- .../Library/Cloud/PremiumizeCloudView.swift | 6 +- .../Library/Cloud/RealDebridCloudView.swift | 11 +- .../Library/HistoryButtonView.swift | 13 ++- .../Plugin/Source/SourceSettingsView.swift | 13 ++- .../SearchResult/SearchResultButtonView.swift | 13 ++- .../Settings/DefaultActionPickerView.swift | 109 ++++++++++++++++++ .../Settings/DefaultActionsPickerViews.swift | 90 --------------- .../Settings/SettingsPluginListView.swift | 11 +- Ferrite/Views/LibraryView.swift | 32 ++--- Ferrite/Views/MainView.swift | 6 +- Ferrite/Views/PluginsView.swift | 32 ++--- Ferrite/Views/SettingsView.swift | 61 +++++----- .../Views/SheetViews/ActionChoiceView.swift | 7 ++ .../Views/SheetViews/BatchChoiceView.swift | 6 +- 19 files changed, 301 insertions(+), 299 deletions(-) delete mode 100644 Ferrite/Models/SettingsModels.swift create mode 100644 Ferrite/Views/ComponentViews/Settings/DefaultActionPickerView.swift delete mode 100644 Ferrite/Views/ComponentViews/Settings/DefaultActionsPickerViews.swift diff --git a/Ferrite.xcodeproj/project.pbxproj b/Ferrite.xcodeproj/project.pbxproj index f6f5a18..e19c7f5 100644 --- a/Ferrite.xcodeproj/project.pbxproj +++ b/Ferrite.xcodeproj/project.pbxproj @@ -81,8 +81,7 @@ 0C84F4842895BFED0074B7C9 /* SourceHtmlParser+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C84F47C2895BFED0074B7C9 /* SourceHtmlParser+CoreDataClass.swift */; }; 0C84F4852895BFED0074B7C9 /* SourceHtmlParser+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C84F47D2895BFED0074B7C9 /* SourceHtmlParser+CoreDataProperties.swift */; }; 0C871BDF29994D9D005279AC /* FilterLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C871BDE29994D9D005279AC /* FilterLabelView.swift */; }; - 0C95D8D828A55B03005E22B3 /* DefaultActionsPickerViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C95D8D728A55B03005E22B3 /* DefaultActionsPickerViews.swift */; }; - 0C95D8DA28A55BB6005E22B3 /* SettingsModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C95D8D928A55BB6005E22B3 /* SettingsModels.swift */; }; + 0C95D8D828A55B03005E22B3 /* DefaultActionPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C95D8D728A55B03005E22B3 /* DefaultActionPickerView.swift */; }; 0CA05457288EE58200850554 /* SettingsPluginListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA05456288EE58200850554 /* SettingsPluginListView.swift */; }; 0CA05459288EE9E600850554 /* PluginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA05458288EE9E600850554 /* PluginManager.swift */; }; 0CA0545B288EEA4E00850554 /* PluginListEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA0545A288EEA4E00850554 /* PluginListEditorView.swift */; }; @@ -204,8 +203,7 @@ 0C84F47C2895BFED0074B7C9 /* SourceHtmlParser+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceHtmlParser+CoreDataClass.swift"; sourceTree = ""; }; 0C84F47D2895BFED0074B7C9 /* SourceHtmlParser+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceHtmlParser+CoreDataProperties.swift"; sourceTree = ""; }; 0C871BDE29994D9D005279AC /* FilterLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterLabelView.swift; sourceTree = ""; }; - 0C95D8D728A55B03005E22B3 /* DefaultActionsPickerViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultActionsPickerViews.swift; sourceTree = ""; }; - 0C95D8D928A55BB6005E22B3 /* SettingsModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsModels.swift; sourceTree = ""; }; + 0C95D8D728A55B03005E22B3 /* DefaultActionPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultActionPickerView.swift; sourceTree = ""; }; 0CA05456288EE58200850554 /* SettingsPluginListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPluginListView.swift; sourceTree = ""; }; 0CA05458288EE9E600850554 /* PluginManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginManager.swift; sourceTree = ""; }; 0CA0545A288EEA4E00850554 /* PluginListEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginListEditorView.swift; sourceTree = ""; }; @@ -346,7 +344,6 @@ 0C422E7F293542F300486D65 /* PremiumizeModels.swift */, 0C0167DB29293FA900B65783 /* RealDebridModels.swift */, 0C41BC6428C2AEB900B47DD6 /* SearchModels.swift */, - 0C95D8D928A55BB6005E22B3 /* SettingsModels.swift */, 0C0D50E4288DFE7F0035ECC8 /* SourceModels.swift */, 0C3E00D7296F5B9A00ECECB2 /* PluginModels.swift */, ); @@ -438,7 +435,7 @@ 0C44E2AE28D52E8A007711AE /* BackupsView.swift */, 0CA05456288EE58200850554 /* SettingsPluginListView.swift */, 0CA0545A288EEA4E00850554 /* PluginListEditorView.swift */, - 0C95D8D728A55B03005E22B3 /* DefaultActionsPickerViews.swift */, + 0C95D8D728A55B03005E22B3 /* DefaultActionPickerView.swift */, 0C10848A28BD9A38008F0BA6 /* SettingsAppVersionView.swift */, ); path = Settings; @@ -763,7 +760,7 @@ 0CC389532970AD900066D06F /* Action+CoreDataClass.swift in Sources */, 0C03EB72296F619900162E9A /* PluginList+CoreDataProperties.swift in Sources */, 0C572D4C2993FC2A003EEC05 /* ViewDidAppearHandler.swift in Sources */, - 0C95D8D828A55B03005E22B3 /* DefaultActionsPickerViews.swift in Sources */, + 0C95D8D828A55B03005E22B3 /* DefaultActionPickerView.swift in Sources */, 0C44E2AF28D52E8A007711AE /* BackupsView.swift in Sources */, 0CA148E1288903F000DE2211 /* Collection.swift in Sources */, 0C750744289B003E004B3906 /* SourceRssParser+CoreDataClass.swift in Sources */, @@ -778,7 +775,6 @@ 0CA148D8288903F000DE2211 /* ActionChoiceView.swift in Sources */, 0C41BC6528C2AEB900B47DD6 /* SearchModels.swift in Sources */, 0C68135028BC1A2D00FAD890 /* GithubWrapper.swift in Sources */, - 0C95D8DA28A55BB6005E22B3 /* SettingsModels.swift in Sources */, 0CA148E3288903F000DE2211 /* Task.swift in Sources */, 0CA148E7288903F000DE2211 /* ToastViewModel.swift in Sources */, 0C6C7C9D29315292002DF910 /* AllDebridModels.swift in Sources */, diff --git a/Ferrite/Models/SettingsModels.swift b/Ferrite/Models/SettingsModels.swift deleted file mode 100644 index 3f20e57..0000000 --- a/Ferrite/Models/SettingsModels.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// SettingsModels.swift -// Ferrite -// -// Created by Brian Dashore on 8/11/22. -// - -import Foundation - -public enum DefaultMagnetActionType: Int, CaseIterable { - // Let the user choose - case none = 0 - - // Open in actions come first - case webtor = 1 - - // Sharing actions come last - case shareMagnet = 2 -} - -public enum DefaultDebridActionType: Int, CaseIterable { - // Let the user choose - case none = 0 - - // Open in actions come first - case outplayer = 1 - case vlc = 2 - case infuse = 3 - - // Sharing actions come last - case shareDownload = 4 -} diff --git a/Ferrite/ViewModels/NavigationViewModel.swift b/Ferrite/ViewModels/NavigationViewModel.swift index aed154a..45e4c57 100644 --- a/Ferrite/ViewModels/NavigationViewModel.swift +++ b/Ferrite/ViewModels/NavigationViewModel.swift @@ -8,16 +8,16 @@ import SwiftUI @MainActor -class NavigationViewModel: ObservableObject { +public class NavigationViewModel: ObservableObject { var toastModel: ToastViewModel? // Used between SearchResultsView and MagnetChoiceView - enum ChoiceSheetType: Identifiable { - var id: Int { + public enum ChoiceSheetType: Identifiable { + public var id: Int { hashValue } - case magnet + case action case batch case activity } @@ -66,82 +66,4 @@ class NavigationViewModel: ObservableObject { @Published var libraryPickerSelection: LibraryPickerSegment = .bookmarks @Published var pluginPickerSelection: PluginPickerSegment = .sources - - @AppStorage("Actions.DefaultDebrid") var defaultDebridAction: DefaultDebridActionType = .none - @AppStorage("Actions.DefaultMagnet") var defaultMagnetAction: DefaultMagnetActionType = .none - - // TODO: Fix for new Actions API - public func runDebridAction(urlString: String, _ action: DefaultDebridActionType? = nil) { - currentChoiceSheet = .magnet - /* - let selectedAction = action ?? defaultDebridAction - - switch selectedAction { - case .none: - currentChoiceSheet = .magnet - case .outplayer: - if let downloadUrl = URL(string: "outplayer://\(urlString)") { - UIApplication.shared.open(downloadUrl) - } else { - toastModel?.updateToastDescription("Could not create an Outplayer URL") - } - case .vlc: - if let downloadUrl = URL(string: "vlc://\(urlString)") { - UIApplication.shared.open(downloadUrl) - } else { - toastModel?.updateToastDescription("Could not create a VLC URL") - } - case .infuse: - if let downloadUrl = URL(string: "infuse://x-callback-url/play?url=\(urlString)") { - UIApplication.shared.open(downloadUrl) - } else { - toastModel?.updateToastDescription("Could not create a Infuse URL") - } - case .shareDownload: - if let downloadUrl = URL(string: urlString), currentChoiceSheet == nil { - activityItems = [downloadUrl] - currentChoiceSheet = .activity - } else { - toastModel?.updateToastDescription("Could not create object for sharing") - } - } - */ - } - - // TODO: Fix for new Actions API - public func runMagnetAction(magnet: Magnet?, _ action: DefaultMagnetActionType? = nil) { - currentChoiceSheet = .magnet - // Fall back to selected magnet if the provided magnet is nil - /* - let magnet = magnet ?? selectedMagnet - guard let magnetLink = magnet?.link else { - toastModel?.updateToastDescription("Could not run your action because the magnet link is invalid.") - print("Magnet action error: The magnet link is invalid.") - - return - } - - let selectedAction = action ?? defaultMagnetAction - - switch selectedAction { - case .none: - currentChoiceSheet = .magnet - case .webtor: - if let url = URL(string: "https://webtor.io/#/show?magnet=\(magnetLink)") { - UIApplication.shared.open(url) - } else { - toastModel?.updateToastDescription("Could not create a WebTor URL") - } - case .shareMagnet: - if let magnetUrl = URL(string: magnetLink), - currentChoiceSheet == nil - { - activityItems = [magnetUrl] - currentChoiceSheet = .activity - } else { - toastModel?.updateToastDescription("Could not create object for sharing") - } - } - */ - } } diff --git a/Ferrite/ViewModels/PluginManager.swift b/Ferrite/ViewModels/PluginManager.swift index 2bac4d6..f1fdd29 100644 --- a/Ferrite/ViewModels/PluginManager.swift +++ b/Ferrite/ViewModels/PluginManager.swift @@ -14,6 +14,8 @@ public class PluginManager: ObservableObject { @Published var availableSources: [SourceJson] = [] @Published var availableActions: [ActionJson] = [] + @Published var showBrokenDefaultActionAlert = false + @MainActor public func fetchPluginsFromUrl() async { let pluginListRequest = PluginList.fetchRequest() @@ -162,6 +164,58 @@ public class PluginManager: ObservableObject { } } + @MainActor + public func runDebridAction(urlString: String?, currentChoiceSheet: inout NavigationViewModel.ChoiceSheetType?) { + let context = PersistenceController.shared.backgroundContext + + if + let defaultDebridActionName = UserDefaults.standard.string(forKey: "Actions.DefaultDebridName"), + let defaultDebridActionList = UserDefaults.standard.string(forKey: "Actions.DefaultDebridList") + { + let actionFetchRequest = Action.fetchRequest() + actionFetchRequest.fetchLimit = 1 + actionFetchRequest.predicate = NSPredicate(format: "name == %@ AND listId == %@", defaultDebridActionName, defaultDebridActionList) + + if let fetchedAction = try? context.fetch(actionFetchRequest).first { + runDeeplinkAction(fetchedAction, urlString: urlString) + } else { + currentChoiceSheet = .action + UserDefaults.standard.set(nil, forKey: "Actions.DefaultDebridName") + UserDefaults.standard.set(nil, forKey: "Action.DefaultDebridList") + + showBrokenDefaultActionAlert.toggle() + } + } else { + currentChoiceSheet = .action + } + } + + @MainActor + public func runMagnetAction(urlString: String?, currentChoiceSheet: inout NavigationViewModel.ChoiceSheetType?) { + let context = PersistenceController.shared.backgroundContext + + if + let defaultMagnetActionName = UserDefaults.standard.string(forKey: "Actions.DefaultMagnetName"), + let defaultMagnetActionList = UserDefaults.standard.string(forKey: "Actions.DefaultMagnetList") + { + let actionFetchRequest = Action.fetchRequest() + actionFetchRequest.fetchLimit = 1 + actionFetchRequest.predicate = NSPredicate(format: "name == %@ AND listId == %@", defaultMagnetActionName, defaultMagnetActionList) + + if let fetchedAction = try? context.fetch(actionFetchRequest).first { + runDeeplinkAction(fetchedAction, urlString: urlString) + } else { + currentChoiceSheet = .action + UserDefaults.standard.set(nil, forKey: "Actions.DefaultMagnetName") + UserDefaults.standard.set(nil, forKey: "Actions.DefaultMagnetList") + + showBrokenDefaultActionAlert.toggle() + } + } else { + currentChoiceSheet = .action + } + } + // The iOS version of Ferrite only runs deeplink actions @MainActor public func runDeeplinkAction(_ action: Action, urlString: String?) { diff --git a/Ferrite/Views/ComponentViews/Library/Cloud/AllDebridCloudView.swift b/Ferrite/Views/ComponentViews/Library/Cloud/AllDebridCloudView.swift index d6d6b77..fc8f0b5 100644 --- a/Ferrite/Views/ComponentViews/Library/Cloud/AllDebridCloudView.swift +++ b/Ferrite/Views/ComponentViews/Library/Cloud/AllDebridCloudView.swift @@ -10,6 +10,7 @@ import SwiftUI struct AllDebridCloudView: View { @EnvironmentObject var debridManager: DebridManager @EnvironmentObject var navModel: NavigationViewModel + @EnvironmentObject var pluginManager: PluginManager @Binding var searchText: String @@ -38,7 +39,10 @@ struct AllDebridCloudView: View { if !debridManager.downloadUrl.isEmpty { historyInfo.url = debridManager.downloadUrl PersistenceController.shared.createHistory(historyInfo, performSave: true) - navModel.runDebridAction(urlString: debridManager.downloadUrl) + pluginManager.runDebridAction( + urlString: debridManager.downloadUrl, + currentChoiceSheet: &navModel.currentChoiceSheet + ) } } } else { diff --git a/Ferrite/Views/ComponentViews/Library/Cloud/PremiumizeCloudView.swift b/Ferrite/Views/ComponentViews/Library/Cloud/PremiumizeCloudView.swift index 8217ea7..760cd10 100644 --- a/Ferrite/Views/ComponentViews/Library/Cloud/PremiumizeCloudView.swift +++ b/Ferrite/Views/ComponentViews/Library/Cloud/PremiumizeCloudView.swift @@ -11,6 +11,7 @@ import SwiftUIX struct PremiumizeCloudView: View { @EnvironmentObject var debridManager: DebridManager @EnvironmentObject var navModel: NavigationViewModel + @EnvironmentObject var pluginManager: PluginManager @Binding var searchText: String @@ -38,7 +39,10 @@ struct PremiumizeCloudView: View { performSave: true ) - navModel.runDebridAction(urlString: debridManager.downloadUrl) + pluginManager.runDebridAction( + urlString: debridManager.downloadUrl, + currentChoiceSheet: &navModel.currentChoiceSheet + ) } } } diff --git a/Ferrite/Views/ComponentViews/Library/Cloud/RealDebridCloudView.swift b/Ferrite/Views/ComponentViews/Library/Cloud/RealDebridCloudView.swift index d826375..9b4700c 100644 --- a/Ferrite/Views/ComponentViews/Library/Cloud/RealDebridCloudView.swift +++ b/Ferrite/Views/ComponentViews/Library/Cloud/RealDebridCloudView.swift @@ -10,6 +10,7 @@ import SwiftUI struct RealDebridCloudView: View { @EnvironmentObject var navModel: NavigationViewModel @EnvironmentObject var debridManager: DebridManager + @EnvironmentObject var pluginManager: PluginManager @Binding var searchText: String @@ -35,7 +36,10 @@ struct RealDebridCloudView: View { performSave: true ) - navModel.runDebridAction(urlString: debridManager.downloadUrl) + pluginManager.runDebridAction( + urlString: debridManager.downloadUrl, + currentChoiceSheet: &navModel.currentChoiceSheet + ) } .backport.tint(.primary) } @@ -72,7 +76,10 @@ struct RealDebridCloudView: View { historyInfo.url = debridManager.downloadUrl PersistenceController.shared.createHistory(historyInfo, performSave: true) - navModel.runDebridAction(urlString: debridManager.downloadUrl) + pluginManager.runDebridAction( + urlString: debridManager.downloadUrl, + currentChoiceSheet: &navModel.currentChoiceSheet + ) } } } else { diff --git a/Ferrite/Views/ComponentViews/Library/HistoryButtonView.swift b/Ferrite/Views/ComponentViews/Library/HistoryButtonView.swift index 799cebe..34f793e 100644 --- a/Ferrite/Views/ComponentViews/Library/HistoryButtonView.swift +++ b/Ferrite/Views/ComponentViews/Library/HistoryButtonView.swift @@ -11,6 +11,7 @@ struct HistoryButtonView: View { @EnvironmentObject var toastModel: ToastViewModel @EnvironmentObject var navModel: NavigationViewModel @EnvironmentObject var debridManager: DebridManager + @EnvironmentObject var pluginManager: PluginManager let entry: HistoryEntry @@ -23,14 +24,20 @@ struct HistoryButtonView: View { if url.starts(with: "https://") { Task { debridManager.downloadUrl = url - navModel.runDebridAction(urlString: url) + pluginManager.runDebridAction( + urlString: url, + currentChoiceSheet: &navModel.currentChoiceSheet + ) - if navModel.currentChoiceSheet != .magnet { + if navModel.currentChoiceSheet != .action { debridManager.downloadUrl = "" } } } else { - navModel.runMagnetAction(magnet: Magnet(hash: nil, link: url)) + pluginManager.runMagnetAction( + urlString: url, + currentChoiceSheet: &navModel.currentChoiceSheet + ) } } else { toastModel.updateToastDescription("URL invalid. Cannot load this history entry. Please delete it.") diff --git a/Ferrite/Views/ComponentViews/Plugin/Source/SourceSettingsView.swift b/Ferrite/Views/ComponentViews/Plugin/Source/SourceSettingsView.swift index b882874..fa30ab4 100644 --- a/Ferrite/Views/ComponentViews/Plugin/Source/SourceSettingsView.swift +++ b/Ferrite/Views/ComponentViews/Plugin/Source/SourceSettingsView.swift @@ -12,6 +12,11 @@ struct SourceSettingsView: View { @EnvironmentObject var navModel: NavigationViewModel + @FetchRequest( + entity: PluginList.entity(), + sortDescriptors: [] + ) var pluginLists: FetchedResults + var body: some View { NavView { List { @@ -32,10 +37,12 @@ struct SourceSettingsView: View { Group { Text("ID: \(selectedSource.id)") - if let listId = selectedSource.listId { - Text("List ID: \(listId)") + if let pluginList = pluginLists.first(where: { $0.id == selectedSource.listId }) + { + Text("List: \(pluginList.name)") + Text("List ID: \(pluginList.id.uuidString)") } else { - Text("No list ID found. This source should be removed.") + Text("No plugin list found. This source should be removed.") } } .foregroundColor(.secondary) diff --git a/Ferrite/Views/ComponentViews/SearchResult/SearchResultButtonView.swift b/Ferrite/Views/ComponentViews/SearchResult/SearchResultButtonView.swift index 5a49bb7..19409b0 100644 --- a/Ferrite/Views/ComponentViews/SearchResult/SearchResultButtonView.swift +++ b/Ferrite/Views/ComponentViews/SearchResult/SearchResultButtonView.swift @@ -12,6 +12,7 @@ struct SearchResultButtonView: View { @EnvironmentObject var navModel: NavigationViewModel @EnvironmentObject var debridManager: DebridManager + @EnvironmentObject var pluginManager: PluginManager var result: SearchResult @@ -42,9 +43,12 @@ struct SearchResultButtonView: View { performSave: true ) - navModel.runDebridAction(urlString: debridManager.downloadUrl) + pluginManager.runDebridAction( + urlString: debridManager.downloadUrl, + currentChoiceSheet: &navModel.currentChoiceSheet + ) - if navModel.currentChoiceSheet != .magnet { + if navModel.currentChoiceSheet != .action { debridManager.downloadUrl = "" } } @@ -68,7 +72,10 @@ struct SearchResultButtonView: View { performSave: true ) - navModel.runMagnetAction(magnet: result.magnet) + pluginManager.runMagnetAction( + urlString: result.magnet.link, + currentChoiceSheet: &navModel.currentChoiceSheet + ) } } } label: { diff --git a/Ferrite/Views/ComponentViews/Settings/DefaultActionPickerView.swift b/Ferrite/Views/ComponentViews/Settings/DefaultActionPickerView.swift new file mode 100644 index 0000000..def50f6 --- /dev/null +++ b/Ferrite/Views/ComponentViews/Settings/DefaultActionPickerView.swift @@ -0,0 +1,109 @@ +// +// DefaultActionsPickerViews.swift +// Ferrite +// +// Created by Brian Dashore on 8/11/22. +// + +import SwiftUI + +struct DefaultActionPickerView: View { + @EnvironmentObject var toastModel: ToastViewModel + + let actionRequirement: ActionRequirement + @Binding var defaultActionName: String? + @Binding var defaultActionList: String? + + @FetchRequest( + entity: Action.entity(), + sortDescriptors: [] + ) var actions: FetchedResults + + @FetchRequest( + entity: PluginList.entity(), + sortDescriptors: [] + ) var pluginLists: FetchedResults + + var body: some View { + List { + UserChoiceButton( + defaultActionName: $defaultActionName, + defaultActionList: $defaultActionList, + pluginLists: pluginLists + ) + + ForEach(actions.filter { $0.requires.contains(actionRequirement.rawValue) }, id: \.id) { action in + Button { + if let actionListId = action.listId?.uuidString { + defaultActionName = action.name + defaultActionList = actionListId + } else { + toastModel.updateToastDescription( + "Default action error: This action doesn't have a corresponding plugin list! Please uninstall the action" + ) + } + } label: { + HStack { + VStack(alignment: .leading, spacing: 5) { + Text(action.name) + + Group { + if let pluginList = pluginLists.first(where: { $0.id == action.listId }) { + Text("List: \(pluginList.name)") + + Text(pluginList.id.uuidString) + .font(.caption) + } else { + Text("No plugin list found") + .font(.caption) + } + } + .foregroundColor(.secondary) + } + Spacer() + + if + let defaultActionList, + action.listId?.uuidString == defaultActionList, + action.name == defaultActionName + { + Image(systemName: "checkmark") + .foregroundColor(.blue) + } + } + } + .backport.tint(.primary) + } + } + .listStyle(.insetGrouped) + .inlinedList(inset: -20) + .navigationTitle("Default \(actionRequirement == .debrid ? "debrid" : "magnet") action") + .navigationBarTitleDisplayMode(.inline) + } +} + +private struct UserChoiceButton: View { + @Binding var defaultActionName: String? + @Binding var defaultActionList: String? + var pluginLists: FetchedResults + + var body: some View { + Button { + defaultActionName = nil + defaultActionList = nil + } label: { + HStack { + Text("Let me choose") + Spacer() + + // Force "Let me choose" if the name OR list ID is nil + // Prevents any mismatches + if defaultActionName == nil || pluginLists.contains(where: { $0.id.uuidString == defaultActionList }) { + Image(systemName: "checkmark") + .foregroundColor(.blue) + } + } + } + .backport.tint(.primary) + } +} diff --git a/Ferrite/Views/ComponentViews/Settings/DefaultActionsPickerViews.swift b/Ferrite/Views/ComponentViews/Settings/DefaultActionsPickerViews.swift deleted file mode 100644 index ab7b52c..0000000 --- a/Ferrite/Views/ComponentViews/Settings/DefaultActionsPickerViews.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// DefaultActionsPickerViews.swift -// Ferrite -// -// Created by Brian Dashore on 8/11/22. -// - -import SwiftUI - -struct MagnetActionPickerView: View { - @AppStorage("Actions.DefaultMagnet") var defaultMagnetAction: DefaultMagnetActionType = .none - - var body: some View { - List { - ForEach(DefaultMagnetActionType.allCases, id: \.self) { action in - Button { - defaultMagnetAction = action - } label: { - HStack { - Text(fetchPickerChoiceName(choice: action)) - Spacer() - if action == defaultMagnetAction { - Image(systemName: "checkmark") - .foregroundColor(.blue) - } - } - } - .backport.tint(.primary) - } - } - .listStyle(.insetGrouped) - .inlinedList(inset: -20) - .navigationTitle("Default magnet action") - .navigationBarTitleDisplayMode(.inline) - } - - func fetchPickerChoiceName(choice: DefaultMagnetActionType) -> String { - switch choice { - case .none: - return "Let me choose" - case .webtor: - return "Open in Webtor" - case .shareMagnet: - return "Share magnet link" - } - } -} - -struct DebridActionPickerView: View { - @AppStorage("Actions.DefaultDebrid") var defaultDebridAction: DefaultDebridActionType = .none - - var body: some View { - List { - ForEach(DefaultDebridActionType.allCases, id: \.self) { action in - Button { - defaultDebridAction = action - } label: { - HStack { - Text(fetchPickerChoiceName(choice: action)) - Spacer() - if action == defaultDebridAction { - Image(systemName: "checkmark") - .foregroundColor(.blue) - } - } - } - .backport.tint(.primary) - } - } - .listStyle(.insetGrouped) - .inlinedList(inset: -20) - .navigationTitle("Default debrid action") - .navigationBarTitleDisplayMode(.inline) - } - - func fetchPickerChoiceName(choice: DefaultDebridActionType) -> String { - switch choice { - case .none: - return "Let me choose" - case .outplayer: - return "Open in Outplayer" - case .vlc: - return "Open in VLC" - case .infuse: - return "Open in Infuse" - case .shareDownload: - return "Share download link" - } - } -} diff --git a/Ferrite/Views/ComponentViews/Settings/SettingsPluginListView.swift b/Ferrite/Views/ComponentViews/Settings/SettingsPluginListView.swift index 1f23b57..fe523ba 100644 --- a/Ferrite/Views/ComponentViews/Settings/SettingsPluginListView.swift +++ b/Ferrite/Views/ComponentViews/Settings/SettingsPluginListView.swift @@ -30,12 +30,13 @@ struct SettingsPluginListView: View { VStack(alignment: .leading, spacing: 5) { Text(pluginList.name) - Text(pluginList.author) - .foregroundColor(.gray) + Group { + Text(pluginList.author) - Text("ID: \(pluginList.id)") - .font(.caption) - .foregroundColor(.gray) + Text("ID: \(pluginList.id)") + .font(.caption) + } + .foregroundColor(.secondary) } .padding(.vertical, 2) .contextMenu { diff --git a/Ferrite/Views/LibraryView.swift b/Ferrite/Views/LibraryView.swift index 6629c4b..ace53a1 100644 --- a/Ferrite/Views/LibraryView.swift +++ b/Ferrite/Views/LibraryView.swift @@ -44,6 +44,22 @@ struct LibraryView: View { DebridCloudView(searchText: $searchText) } } + .overlay { + switch navModel.libraryPickerSelection { + case .bookmarks: + if bookmarks.isEmpty { + EmptyInstructionView(title: "No Bookmarks", message: "Add a bookmark from search results") + } + case .history: + if history.isEmpty { + EmptyInstructionView(title: "No History", message: "Start watching to build history") + } + case .debridCloud: + if debridManager.selectedDebridType == nil { + EmptyInstructionView(title: "Cloud Unavailable", message: "Listing is not available for this service") + } + } + } .navigationTitle("Library") .toolbar { ToolbarItem(placement: .navigationBarTrailing) { @@ -83,22 +99,6 @@ struct LibraryView: View { } .environment(\.editMode, $editMode) } - .overlay { - switch navModel.libraryPickerSelection { - case .bookmarks: - if bookmarks.isEmpty { - EmptyInstructionView(title: "No Bookmarks", message: "Add a bookmark from search results") - } - case .history: - if history.isEmpty { - EmptyInstructionView(title: "No History", message: "Start watching to build history") - } - case .debridCloud: - if debridManager.selectedDebridType == nil { - EmptyInstructionView(title: "Cloud Unavailable", message: "Listing is not available for this service") - } - } - } .onChange(of: navModel.libraryPickerSelection) { _ in editMode = .inactive } diff --git a/Ferrite/Views/MainView.swift b/Ferrite/Views/MainView.swift index 7403048..233a5dc 100644 --- a/Ferrite/Views/MainView.swift +++ b/Ferrite/Views/MainView.swift @@ -51,7 +51,7 @@ struct MainView: View { } .sheet(item: $navModel.currentChoiceSheet) { item in switch item { - case .magnet: + case .action: ActionChoiceView() .environmentObject(debridManager) .environmentObject(scrapingModel) @@ -139,7 +139,9 @@ struct MainView: View { .backport.alert( isPresented: $showUpdateAlert, title: "Update available", - message: "Ferrite \(releaseVersionString) can be downloaded. \n\nThis alert can be disabled in Settings.", + message: + "Ferrite \(releaseVersionString) can be downloaded. \n\n" + + "This alert can be disabled in Settings.", buttons: [ .init("Download") { guard let releaseUrl = URL(string: releaseUrlString) else { diff --git a/Ferrite/Views/PluginsView.swift b/Ferrite/Views/PluginsView.swift index 3f4b179..14b29af 100644 --- a/Ferrite/Views/PluginsView.swift +++ b/Ferrite/Views/PluginsView.swift @@ -44,6 +44,22 @@ struct PluginsView: View { } } } + .overlay { + if checkedForPlugins { + switch navModel.pluginPickerSelection { + case .sources: + if sources.isEmpty && pluginManager.availableSources.isEmpty { + EmptyInstructionView(title: "No Sources", message: "Add a plugin list in Settings") + } + case .actions: + if actions.isEmpty && pluginManager.availableActions.isEmpty { + EmptyInstructionView(title: "No Actions", message: "Add a plugin list in Settings") + } + } + } else { + ProgressView() + } + } .backport.onAppear { viewTask = Task { await pluginManager.fetchPluginsFromUrl() @@ -71,22 +87,6 @@ struct PluginsView: View { .environmentObject(navModel) } } - .overlay { - if checkedForPlugins { - switch navModel.pluginPickerSelection { - case .sources: - if sources.isEmpty && pluginManager.availableSources.isEmpty { - EmptyInstructionView(title: "No Sources", message: "Add a plugin list in Settings") - } - case .actions: - if actions.isEmpty && pluginManager.availableActions.isEmpty { - EmptyInstructionView(title: "No Actions", message: "Add a plugin list in Settings") - } - } - } else { - ProgressView() - } - } } } diff --git a/Ferrite/Views/SettingsView.swift b/Ferrite/Views/SettingsView.swift index 17912cd..1c38209 100644 --- a/Ferrite/Views/SettingsView.swift +++ b/Ferrite/Views/SettingsView.swift @@ -20,8 +20,11 @@ struct SettingsView: View { @AppStorage("Updates.AutomaticNotifs") var autoUpdateNotifs = true - @AppStorage("Actions.DefaultDebrid") var defaultDebridAction: DefaultDebridActionType = .none - @AppStorage("Actions.DefaultMagnet") var defaultMagnetAction: DefaultMagnetActionType = .none + @AppStorage("Actions.DefaultDebridName") var defaultDebridActionName: String? + @AppStorage("Actions.DefaultDebridList") var defaultDebridActionList: String? + + @AppStorage("Actions.DefaultMagnetName") var defaultMagnetActionName: String? + @AppStorage("Actions.DefaultMagnetList") var defaultMagnetActionList: String? var body: some View { NavView { @@ -94,50 +97,40 @@ struct SettingsView: View { } Section(header: InlineHeader("Default actions")) { - if debridManager.enabledDebrids.count > 0 { + //if debridManager.enabledDebrids.count > 0 { NavigationLink( - destination: DebridActionPickerView(), + destination: DefaultActionPickerView( + actionRequirement: .debrid, + defaultActionName: $defaultDebridActionName, + defaultActionList: $defaultDebridActionList + ), label: { HStack { - Text("Default debrid action") + Text("Debrid action") Spacer() - Group { - switch defaultDebridAction { - case .none: - Text("User choice") - case .outplayer: - Text("Outplayer") - case .vlc: - Text("VLC") - case .infuse: - Text("Infuse") - case .shareDownload: - Text("Share") - } - } - .foregroundColor(.gray) + + // TODO: Maybe make this check for nil list as well? + Text(defaultDebridActionName.map { $0 } ?? "User choice") + .foregroundColor(.secondary) } } ) - } + //} NavigationLink( - destination: MagnetActionPickerView(), + destination: DefaultActionPickerView( + actionRequirement: .magnet, + defaultActionName: $defaultMagnetActionName, + defaultActionList: $defaultMagnetActionList + ), label: { HStack { - Text("Default magnet action") + Text("Magnet action") Spacer() - Group { - switch defaultMagnetAction { - case .none: - Text("User choice") - case .webtor: - Text("Webtor") - case .shareMagnet: - Text("Share") - } - } - .foregroundColor(.gray) + + // TODO: Maybe make this check for nil list as well? + Text(defaultMagnetActionName.map { $0 } ?? "User choice") + .foregroundColor(.secondary) } } ) diff --git a/Ferrite/Views/SheetViews/ActionChoiceView.swift b/Ferrite/Views/SheetViews/ActionChoiceView.swift index e24a3e3..d9b4839 100644 --- a/Ferrite/Views/SheetViews/ActionChoiceView.swift +++ b/Ferrite/Views/SheetViews/ActionChoiceView.swift @@ -112,6 +112,13 @@ struct ActionChoiceView: View { AppActivityView(activityItems: navModel.activityItems) } } + .backport.alert( + isPresented: $pluginManager.showBrokenDefaultActionAlert, + title: "Action not found", + message: + "The default action could not be run. The action choice sheet has been opened. \n\n" + + "Please check your default actions in Settings" + ) .onDisappear { debridManager.downloadUrl = "" navModel.selectedTitle = "" diff --git a/Ferrite/Views/SheetViews/BatchChoiceView.swift b/Ferrite/Views/SheetViews/BatchChoiceView.swift index 7497ff2..1721adb 100644 --- a/Ferrite/Views/SheetViews/BatchChoiceView.swift +++ b/Ferrite/Views/SheetViews/BatchChoiceView.swift @@ -11,6 +11,7 @@ struct BatchChoiceView: View { @EnvironmentObject var debridManager: DebridManager @EnvironmentObject var scrapingModel: ScrapingViewModel @EnvironmentObject var navModel: NavigationViewModel + @EnvironmentObject var pluginManager: PluginManager let backgroundContext = PersistenceController.shared.backgroundContext @@ -83,7 +84,10 @@ struct BatchChoiceView: View { PersistenceController.shared.createHistory(selectedHistoryInfo, performSave: true) } - navModel.runDebridAction(urlString: debridManager.downloadUrl, nil) + pluginManager.runDebridAction( + urlString: debridManager.downloadUrl, + currentChoiceSheet: &navModel.currentChoiceSheet + ) } debridManager.clearSelectedDebridItems()