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 <bdashore3@proton.me>
This commit is contained in:
kingbri 2023-03-01 18:31:29 -05:00
parent 0661ed66f3
commit f622b7af05
19 changed files with 301 additions and 299 deletions

View file

@ -81,8 +81,7 @@
0C84F4842895BFED0074B7C9 /* SourceHtmlParser+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C84F47C2895BFED0074B7C9 /* SourceHtmlParser+CoreDataClass.swift */; }; 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 */; }; 0C84F4852895BFED0074B7C9 /* SourceHtmlParser+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C84F47D2895BFED0074B7C9 /* SourceHtmlParser+CoreDataProperties.swift */; };
0C871BDF29994D9D005279AC /* FilterLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C871BDE29994D9D005279AC /* FilterLabelView.swift */; }; 0C871BDF29994D9D005279AC /* FilterLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C871BDE29994D9D005279AC /* FilterLabelView.swift */; };
0C95D8D828A55B03005E22B3 /* DefaultActionsPickerViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C95D8D728A55B03005E22B3 /* DefaultActionsPickerViews.swift */; }; 0C95D8D828A55B03005E22B3 /* DefaultActionPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C95D8D728A55B03005E22B3 /* DefaultActionPickerView.swift */; };
0C95D8DA28A55BB6005E22B3 /* SettingsModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C95D8D928A55BB6005E22B3 /* SettingsModels.swift */; };
0CA05457288EE58200850554 /* SettingsPluginListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA05456288EE58200850554 /* SettingsPluginListView.swift */; }; 0CA05457288EE58200850554 /* SettingsPluginListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA05456288EE58200850554 /* SettingsPluginListView.swift */; };
0CA05459288EE9E600850554 /* PluginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA05458288EE9E600850554 /* PluginManager.swift */; }; 0CA05459288EE9E600850554 /* PluginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA05458288EE9E600850554 /* PluginManager.swift */; };
0CA0545B288EEA4E00850554 /* PluginListEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CA0545A288EEA4E00850554 /* PluginListEditorView.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 = "<group>"; }; 0C84F47C2895BFED0074B7C9 /* SourceHtmlParser+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceHtmlParser+CoreDataClass.swift"; sourceTree = "<group>"; };
0C84F47D2895BFED0074B7C9 /* SourceHtmlParser+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceHtmlParser+CoreDataProperties.swift"; sourceTree = "<group>"; }; 0C84F47D2895BFED0074B7C9 /* SourceHtmlParser+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceHtmlParser+CoreDataProperties.swift"; sourceTree = "<group>"; };
0C871BDE29994D9D005279AC /* FilterLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterLabelView.swift; sourceTree = "<group>"; }; 0C871BDE29994D9D005279AC /* FilterLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterLabelView.swift; sourceTree = "<group>"; };
0C95D8D728A55B03005E22B3 /* DefaultActionsPickerViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultActionsPickerViews.swift; sourceTree = "<group>"; }; 0C95D8D728A55B03005E22B3 /* DefaultActionPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultActionPickerView.swift; sourceTree = "<group>"; };
0C95D8D928A55BB6005E22B3 /* SettingsModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsModels.swift; sourceTree = "<group>"; };
0CA05456288EE58200850554 /* SettingsPluginListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPluginListView.swift; sourceTree = "<group>"; }; 0CA05456288EE58200850554 /* SettingsPluginListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsPluginListView.swift; sourceTree = "<group>"; };
0CA05458288EE9E600850554 /* PluginManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginManager.swift; sourceTree = "<group>"; }; 0CA05458288EE9E600850554 /* PluginManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginManager.swift; sourceTree = "<group>"; };
0CA0545A288EEA4E00850554 /* PluginListEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginListEditorView.swift; sourceTree = "<group>"; }; 0CA0545A288EEA4E00850554 /* PluginListEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginListEditorView.swift; sourceTree = "<group>"; };
@ -346,7 +344,6 @@
0C422E7F293542F300486D65 /* PremiumizeModels.swift */, 0C422E7F293542F300486D65 /* PremiumizeModels.swift */,
0C0167DB29293FA900B65783 /* RealDebridModels.swift */, 0C0167DB29293FA900B65783 /* RealDebridModels.swift */,
0C41BC6428C2AEB900B47DD6 /* SearchModels.swift */, 0C41BC6428C2AEB900B47DD6 /* SearchModels.swift */,
0C95D8D928A55BB6005E22B3 /* SettingsModels.swift */,
0C0D50E4288DFE7F0035ECC8 /* SourceModels.swift */, 0C0D50E4288DFE7F0035ECC8 /* SourceModels.swift */,
0C3E00D7296F5B9A00ECECB2 /* PluginModels.swift */, 0C3E00D7296F5B9A00ECECB2 /* PluginModels.swift */,
); );
@ -438,7 +435,7 @@
0C44E2AE28D52E8A007711AE /* BackupsView.swift */, 0C44E2AE28D52E8A007711AE /* BackupsView.swift */,
0CA05456288EE58200850554 /* SettingsPluginListView.swift */, 0CA05456288EE58200850554 /* SettingsPluginListView.swift */,
0CA0545A288EEA4E00850554 /* PluginListEditorView.swift */, 0CA0545A288EEA4E00850554 /* PluginListEditorView.swift */,
0C95D8D728A55B03005E22B3 /* DefaultActionsPickerViews.swift */, 0C95D8D728A55B03005E22B3 /* DefaultActionPickerView.swift */,
0C10848A28BD9A38008F0BA6 /* SettingsAppVersionView.swift */, 0C10848A28BD9A38008F0BA6 /* SettingsAppVersionView.swift */,
); );
path = Settings; path = Settings;
@ -763,7 +760,7 @@
0CC389532970AD900066D06F /* Action+CoreDataClass.swift in Sources */, 0CC389532970AD900066D06F /* Action+CoreDataClass.swift in Sources */,
0C03EB72296F619900162E9A /* PluginList+CoreDataProperties.swift in Sources */, 0C03EB72296F619900162E9A /* PluginList+CoreDataProperties.swift in Sources */,
0C572D4C2993FC2A003EEC05 /* ViewDidAppearHandler.swift in Sources */, 0C572D4C2993FC2A003EEC05 /* ViewDidAppearHandler.swift in Sources */,
0C95D8D828A55B03005E22B3 /* DefaultActionsPickerViews.swift in Sources */, 0C95D8D828A55B03005E22B3 /* DefaultActionPickerView.swift in Sources */,
0C44E2AF28D52E8A007711AE /* BackupsView.swift in Sources */, 0C44E2AF28D52E8A007711AE /* BackupsView.swift in Sources */,
0CA148E1288903F000DE2211 /* Collection.swift in Sources */, 0CA148E1288903F000DE2211 /* Collection.swift in Sources */,
0C750744289B003E004B3906 /* SourceRssParser+CoreDataClass.swift in Sources */, 0C750744289B003E004B3906 /* SourceRssParser+CoreDataClass.swift in Sources */,
@ -778,7 +775,6 @@
0CA148D8288903F000DE2211 /* ActionChoiceView.swift in Sources */, 0CA148D8288903F000DE2211 /* ActionChoiceView.swift in Sources */,
0C41BC6528C2AEB900B47DD6 /* SearchModels.swift in Sources */, 0C41BC6528C2AEB900B47DD6 /* SearchModels.swift in Sources */,
0C68135028BC1A2D00FAD890 /* GithubWrapper.swift in Sources */, 0C68135028BC1A2D00FAD890 /* GithubWrapper.swift in Sources */,
0C95D8DA28A55BB6005E22B3 /* SettingsModels.swift in Sources */,
0CA148E3288903F000DE2211 /* Task.swift in Sources */, 0CA148E3288903F000DE2211 /* Task.swift in Sources */,
0CA148E7288903F000DE2211 /* ToastViewModel.swift in Sources */, 0CA148E7288903F000DE2211 /* ToastViewModel.swift in Sources */,
0C6C7C9D29315292002DF910 /* AllDebridModels.swift in Sources */, 0C6C7C9D29315292002DF910 /* AllDebridModels.swift in Sources */,

View file

@ -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
}

View file

@ -8,16 +8,16 @@
import SwiftUI import SwiftUI
@MainActor @MainActor
class NavigationViewModel: ObservableObject { public class NavigationViewModel: ObservableObject {
var toastModel: ToastViewModel? var toastModel: ToastViewModel?
// Used between SearchResultsView and MagnetChoiceView // Used between SearchResultsView and MagnetChoiceView
enum ChoiceSheetType: Identifiable { public enum ChoiceSheetType: Identifiable {
var id: Int { public var id: Int {
hashValue hashValue
} }
case magnet case action
case batch case batch
case activity case activity
} }
@ -66,82 +66,4 @@ class NavigationViewModel: ObservableObject {
@Published var libraryPickerSelection: LibraryPickerSegment = .bookmarks @Published var libraryPickerSelection: LibraryPickerSegment = .bookmarks
@Published var pluginPickerSelection: PluginPickerSegment = .sources @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")
}
}
*/
}
} }

View file

@ -14,6 +14,8 @@ public class PluginManager: ObservableObject {
@Published var availableSources: [SourceJson] = [] @Published var availableSources: [SourceJson] = []
@Published var availableActions: [ActionJson] = [] @Published var availableActions: [ActionJson] = []
@Published var showBrokenDefaultActionAlert = false
@MainActor @MainActor
public func fetchPluginsFromUrl() async { public func fetchPluginsFromUrl() async {
let pluginListRequest = PluginList.fetchRequest() 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 // The iOS version of Ferrite only runs deeplink actions
@MainActor @MainActor
public func runDeeplinkAction(_ action: Action, urlString: String?) { public func runDeeplinkAction(_ action: Action, urlString: String?) {

View file

@ -10,6 +10,7 @@ import SwiftUI
struct AllDebridCloudView: View { struct AllDebridCloudView: View {
@EnvironmentObject var debridManager: DebridManager @EnvironmentObject var debridManager: DebridManager
@EnvironmentObject var navModel: NavigationViewModel @EnvironmentObject var navModel: NavigationViewModel
@EnvironmentObject var pluginManager: PluginManager
@Binding var searchText: String @Binding var searchText: String
@ -38,7 +39,10 @@ struct AllDebridCloudView: View {
if !debridManager.downloadUrl.isEmpty { if !debridManager.downloadUrl.isEmpty {
historyInfo.url = debridManager.downloadUrl historyInfo.url = debridManager.downloadUrl
PersistenceController.shared.createHistory(historyInfo, performSave: true) PersistenceController.shared.createHistory(historyInfo, performSave: true)
navModel.runDebridAction(urlString: debridManager.downloadUrl) pluginManager.runDebridAction(
urlString: debridManager.downloadUrl,
currentChoiceSheet: &navModel.currentChoiceSheet
)
} }
} }
} else { } else {

View file

@ -11,6 +11,7 @@ import SwiftUIX
struct PremiumizeCloudView: View { struct PremiumizeCloudView: View {
@EnvironmentObject var debridManager: DebridManager @EnvironmentObject var debridManager: DebridManager
@EnvironmentObject var navModel: NavigationViewModel @EnvironmentObject var navModel: NavigationViewModel
@EnvironmentObject var pluginManager: PluginManager
@Binding var searchText: String @Binding var searchText: String
@ -38,7 +39,10 @@ struct PremiumizeCloudView: View {
performSave: true performSave: true
) )
navModel.runDebridAction(urlString: debridManager.downloadUrl) pluginManager.runDebridAction(
urlString: debridManager.downloadUrl,
currentChoiceSheet: &navModel.currentChoiceSheet
)
} }
} }
} }

View file

@ -10,6 +10,7 @@ import SwiftUI
struct RealDebridCloudView: View { struct RealDebridCloudView: View {
@EnvironmentObject var navModel: NavigationViewModel @EnvironmentObject var navModel: NavigationViewModel
@EnvironmentObject var debridManager: DebridManager @EnvironmentObject var debridManager: DebridManager
@EnvironmentObject var pluginManager: PluginManager
@Binding var searchText: String @Binding var searchText: String
@ -35,7 +36,10 @@ struct RealDebridCloudView: View {
performSave: true performSave: true
) )
navModel.runDebridAction(urlString: debridManager.downloadUrl) pluginManager.runDebridAction(
urlString: debridManager.downloadUrl,
currentChoiceSheet: &navModel.currentChoiceSheet
)
} }
.backport.tint(.primary) .backport.tint(.primary)
} }
@ -72,7 +76,10 @@ struct RealDebridCloudView: View {
historyInfo.url = debridManager.downloadUrl historyInfo.url = debridManager.downloadUrl
PersistenceController.shared.createHistory(historyInfo, performSave: true) PersistenceController.shared.createHistory(historyInfo, performSave: true)
navModel.runDebridAction(urlString: debridManager.downloadUrl) pluginManager.runDebridAction(
urlString: debridManager.downloadUrl,
currentChoiceSheet: &navModel.currentChoiceSheet
)
} }
} }
} else { } else {

View file

@ -11,6 +11,7 @@ struct HistoryButtonView: View {
@EnvironmentObject var toastModel: ToastViewModel @EnvironmentObject var toastModel: ToastViewModel
@EnvironmentObject var navModel: NavigationViewModel @EnvironmentObject var navModel: NavigationViewModel
@EnvironmentObject var debridManager: DebridManager @EnvironmentObject var debridManager: DebridManager
@EnvironmentObject var pluginManager: PluginManager
let entry: HistoryEntry let entry: HistoryEntry
@ -23,14 +24,20 @@ struct HistoryButtonView: View {
if url.starts(with: "https://") { if url.starts(with: "https://") {
Task { Task {
debridManager.downloadUrl = url debridManager.downloadUrl = url
navModel.runDebridAction(urlString: url) pluginManager.runDebridAction(
urlString: url,
currentChoiceSheet: &navModel.currentChoiceSheet
)
if navModel.currentChoiceSheet != .magnet { if navModel.currentChoiceSheet != .action {
debridManager.downloadUrl = "" debridManager.downloadUrl = ""
} }
} }
} else { } else {
navModel.runMagnetAction(magnet: Magnet(hash: nil, link: url)) pluginManager.runMagnetAction(
urlString: url,
currentChoiceSheet: &navModel.currentChoiceSheet
)
} }
} else { } else {
toastModel.updateToastDescription("URL invalid. Cannot load this history entry. Please delete it.") toastModel.updateToastDescription("URL invalid. Cannot load this history entry. Please delete it.")

View file

@ -12,6 +12,11 @@ struct SourceSettingsView: View {
@EnvironmentObject var navModel: NavigationViewModel @EnvironmentObject var navModel: NavigationViewModel
@FetchRequest(
entity: PluginList.entity(),
sortDescriptors: []
) var pluginLists: FetchedResults<PluginList>
var body: some View { var body: some View {
NavView { NavView {
List { List {
@ -32,10 +37,12 @@ struct SourceSettingsView: View {
Group { Group {
Text("ID: \(selectedSource.id)") Text("ID: \(selectedSource.id)")
if let listId = selectedSource.listId { if let pluginList = pluginLists.first(where: { $0.id == selectedSource.listId })
Text("List ID: \(listId)") {
Text("List: \(pluginList.name)")
Text("List ID: \(pluginList.id.uuidString)")
} else { } else {
Text("No list ID found. This source should be removed.") Text("No plugin list found. This source should be removed.")
} }
} }
.foregroundColor(.secondary) .foregroundColor(.secondary)

View file

@ -12,6 +12,7 @@ struct SearchResultButtonView: View {
@EnvironmentObject var navModel: NavigationViewModel @EnvironmentObject var navModel: NavigationViewModel
@EnvironmentObject var debridManager: DebridManager @EnvironmentObject var debridManager: DebridManager
@EnvironmentObject var pluginManager: PluginManager
var result: SearchResult var result: SearchResult
@ -42,9 +43,12 @@ struct SearchResultButtonView: View {
performSave: true 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 = "" debridManager.downloadUrl = ""
} }
} }
@ -68,7 +72,10 @@ struct SearchResultButtonView: View {
performSave: true performSave: true
) )
navModel.runMagnetAction(magnet: result.magnet) pluginManager.runMagnetAction(
urlString: result.magnet.link,
currentChoiceSheet: &navModel.currentChoiceSheet
)
} }
} }
} label: { } label: {

View file

@ -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<Action>
@FetchRequest(
entity: PluginList.entity(),
sortDescriptors: []
) var pluginLists: FetchedResults<PluginList>
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<PluginList>
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)
}
}

View file

@ -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"
}
}
}

View file

@ -30,12 +30,13 @@ struct SettingsPluginListView: View {
VStack(alignment: .leading, spacing: 5) { VStack(alignment: .leading, spacing: 5) {
Text(pluginList.name) Text(pluginList.name)
Text(pluginList.author) Group {
.foregroundColor(.gray) Text(pluginList.author)
Text("ID: \(pluginList.id)") Text("ID: \(pluginList.id)")
.font(.caption) .font(.caption)
.foregroundColor(.gray) }
.foregroundColor(.secondary)
} }
.padding(.vertical, 2) .padding(.vertical, 2)
.contextMenu { .contextMenu {

View file

@ -44,6 +44,22 @@ struct LibraryView: View {
DebridCloudView(searchText: $searchText) 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") .navigationTitle("Library")
.toolbar { .toolbar {
ToolbarItem(placement: .navigationBarTrailing) { ToolbarItem(placement: .navigationBarTrailing) {
@ -83,22 +99,6 @@ struct LibraryView: View {
} }
.environment(\.editMode, $editMode) .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 .onChange(of: navModel.libraryPickerSelection) { _ in
editMode = .inactive editMode = .inactive
} }

View file

@ -51,7 +51,7 @@ struct MainView: View {
} }
.sheet(item: $navModel.currentChoiceSheet) { item in .sheet(item: $navModel.currentChoiceSheet) { item in
switch item { switch item {
case .magnet: case .action:
ActionChoiceView() ActionChoiceView()
.environmentObject(debridManager) .environmentObject(debridManager)
.environmentObject(scrapingModel) .environmentObject(scrapingModel)
@ -139,7 +139,9 @@ struct MainView: View {
.backport.alert( .backport.alert(
isPresented: $showUpdateAlert, isPresented: $showUpdateAlert,
title: "Update available", 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: [ buttons: [
.init("Download") { .init("Download") {
guard let releaseUrl = URL(string: releaseUrlString) else { guard let releaseUrl = URL(string: releaseUrlString) else {

View file

@ -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 { .backport.onAppear {
viewTask = Task { viewTask = Task {
await pluginManager.fetchPluginsFromUrl() await pluginManager.fetchPluginsFromUrl()
@ -71,22 +87,6 @@ struct PluginsView: View {
.environmentObject(navModel) .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()
}
}
} }
} }

View file

@ -20,8 +20,11 @@ struct SettingsView: View {
@AppStorage("Updates.AutomaticNotifs") var autoUpdateNotifs = true @AppStorage("Updates.AutomaticNotifs") var autoUpdateNotifs = true
@AppStorage("Actions.DefaultDebrid") var defaultDebridAction: DefaultDebridActionType = .none @AppStorage("Actions.DefaultDebridName") var defaultDebridActionName: String?
@AppStorage("Actions.DefaultMagnet") var defaultMagnetAction: DefaultMagnetActionType = .none @AppStorage("Actions.DefaultDebridList") var defaultDebridActionList: String?
@AppStorage("Actions.DefaultMagnetName") var defaultMagnetActionName: String?
@AppStorage("Actions.DefaultMagnetList") var defaultMagnetActionList: String?
var body: some View { var body: some View {
NavView { NavView {
@ -94,50 +97,40 @@ struct SettingsView: View {
} }
Section(header: InlineHeader("Default actions")) { Section(header: InlineHeader("Default actions")) {
if debridManager.enabledDebrids.count > 0 { //if debridManager.enabledDebrids.count > 0 {
NavigationLink( NavigationLink(
destination: DebridActionPickerView(), destination: DefaultActionPickerView(
actionRequirement: .debrid,
defaultActionName: $defaultDebridActionName,
defaultActionList: $defaultDebridActionList
),
label: { label: {
HStack { HStack {
Text("Default debrid action") Text("Debrid action")
Spacer() Spacer()
Group {
switch defaultDebridAction { // TODO: Maybe make this check for nil list as well?
case .none: Text(defaultDebridActionName.map { $0 } ?? "User choice")
Text("User choice") .foregroundColor(.secondary)
case .outplayer:
Text("Outplayer")
case .vlc:
Text("VLC")
case .infuse:
Text("Infuse")
case .shareDownload:
Text("Share")
}
}
.foregroundColor(.gray)
} }
} }
) )
} //}
NavigationLink( NavigationLink(
destination: MagnetActionPickerView(), destination: DefaultActionPickerView(
actionRequirement: .magnet,
defaultActionName: $defaultMagnetActionName,
defaultActionList: $defaultMagnetActionList
),
label: { label: {
HStack { HStack {
Text("Default magnet action") Text("Magnet action")
Spacer() Spacer()
Group {
switch defaultMagnetAction { // TODO: Maybe make this check for nil list as well?
case .none: Text(defaultMagnetActionName.map { $0 } ?? "User choice")
Text("User choice") .foregroundColor(.secondary)
case .webtor:
Text("Webtor")
case .shareMagnet:
Text("Share")
}
}
.foregroundColor(.gray)
} }
} }
) )

View file

@ -112,6 +112,13 @@ struct ActionChoiceView: View {
AppActivityView(activityItems: navModel.activityItems) 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 { .onDisappear {
debridManager.downloadUrl = "" debridManager.downloadUrl = ""
navModel.selectedTitle = "" navModel.selectedTitle = ""

View file

@ -11,6 +11,7 @@ struct BatchChoiceView: View {
@EnvironmentObject var debridManager: DebridManager @EnvironmentObject var debridManager: DebridManager
@EnvironmentObject var scrapingModel: ScrapingViewModel @EnvironmentObject var scrapingModel: ScrapingViewModel
@EnvironmentObject var navModel: NavigationViewModel @EnvironmentObject var navModel: NavigationViewModel
@EnvironmentObject var pluginManager: PluginManager
let backgroundContext = PersistenceController.shared.backgroundContext let backgroundContext = PersistenceController.shared.backgroundContext
@ -83,7 +84,10 @@ struct BatchChoiceView: View {
PersistenceController.shared.createHistory(selectedHistoryInfo, performSave: true) PersistenceController.shared.createHistory(selectedHistoryInfo, performSave: true)
} }
navModel.runDebridAction(urlString: debridManager.downloadUrl, nil) pluginManager.runDebridAction(
urlString: debridManager.downloadUrl,
currentChoiceSheet: &navModel.currentChoiceSheet
)
} }
debridManager.clearSelectedDebridItems() debridManager.clearSelectedDebridItems()