Settings: Fix Default actions and Kodi
Fix how default actions are picked and add in default app actions as options for both debrid and magnet defaults. Kodi shows the action choice sheet with the DisclosureGroup dropped down. The new Kodi server framework also wasn't implemented in the Kodi wrapper. Fix that. Finally, add some iOS 14 fixes and repair the autocorrect search setting to actually work. Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
parent
edfba1c62e
commit
6e95c6072c
20 changed files with 197 additions and 118 deletions
|
|
@ -16,6 +16,8 @@
|
|||
0C0D50E7288DFF850035ECC8 /* PluginAggregateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0D50E6288DFF850035ECC8 /* PluginAggregateView.swift */; };
|
||||
0C10848B28BD9A38008F0BA6 /* SettingsAppVersionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C10848A28BD9A38008F0BA6 /* SettingsAppVersionView.swift */; };
|
||||
0C12D43D28CC332A000195BF /* HistoryButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C12D43C28CC332A000195BF /* HistoryButtonView.swift */; };
|
||||
0C1A3E5229C8A7F500DA9730 /* SettingsModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C1A3E5129C8A7F500DA9730 /* SettingsModels.swift */; };
|
||||
0C1A3E5629C9488C00DA9730 /* CodableWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C1A3E5529C9488C00DA9730 /* CodableWrapper.swift */; };
|
||||
0C2886D22960AC2800D6FC16 /* DebridCloudView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C2886D12960AC2800D6FC16 /* DebridCloudView.swift */; };
|
||||
0C2886D72960C50900D6FC16 /* RealDebridCloudView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C2886D62960C50900D6FC16 /* RealDebridCloudView.swift */; };
|
||||
0C2D9653299316CC00A504B6 /* Tag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C2D9652299316CC00A504B6 /* Tag.swift */; };
|
||||
|
|
@ -153,6 +155,8 @@
|
|||
0C0D50E6288DFF850035ECC8 /* PluginAggregateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginAggregateView.swift; sourceTree = "<group>"; };
|
||||
0C10848A28BD9A38008F0BA6 /* SettingsAppVersionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAppVersionView.swift; sourceTree = "<group>"; };
|
||||
0C12D43C28CC332A000195BF /* HistoryButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryButtonView.swift; sourceTree = "<group>"; };
|
||||
0C1A3E5129C8A7F500DA9730 /* SettingsModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsModels.swift; sourceTree = "<group>"; };
|
||||
0C1A3E5529C9488C00DA9730 /* CodableWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableWrapper.swift; sourceTree = "<group>"; };
|
||||
0C2886D12960AC2800D6FC16 /* DebridCloudView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebridCloudView.swift; sourceTree = "<group>"; };
|
||||
0C2886D62960C50900D6FC16 /* RealDebridCloudView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealDebridCloudView.swift; sourceTree = "<group>"; };
|
||||
0C2D9652299316CC00A504B6 /* Tag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tag.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -369,6 +373,7 @@
|
|||
0C0D50E4288DFE7F0035ECC8 /* SourceModels.swift */,
|
||||
0C3E00D7296F5B9A00ECECB2 /* PluginModels.swift */,
|
||||
0C6771F929B3D1AE005D38D2 /* KodiModels.swift */,
|
||||
0C1A3E5129C8A7F500DA9730 /* SettingsModels.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -414,12 +419,13 @@
|
|||
path = Plugin;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0C44E2A628D4DDC6007711AE /* Classes */ = {
|
||||
0C44E2A628D4DDC6007711AE /* Utils */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0C44E2A728D4DDDC007711AE /* Application.swift */,
|
||||
0C1A3E5529C9488C00DA9730 /* CodableWrapper.swift */,
|
||||
);
|
||||
path = Classes;
|
||||
path = Utils;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0C44E2A928D4DFC4007711AE /* Modifiers */ = {
|
||||
|
|
@ -494,7 +500,7 @@
|
|||
0C0D50E3288DFE6E0035ECC8 /* Models */,
|
||||
0CA148EF2889061600DE2211 /* ViewModels */,
|
||||
0CA148EE2889061200DE2211 /* Views */,
|
||||
0C44E2A628D4DDC6007711AE /* Classes */,
|
||||
0C44E2A628D4DDC6007711AE /* Utils */,
|
||||
0C5005552992B9C20064606A /* Protocols */,
|
||||
0CA148C8288903F000DE2211 /* Extensions */,
|
||||
0CA148C5288903F000DE2211 /* Preview Content */,
|
||||
|
|
@ -775,6 +781,7 @@
|
|||
0C750745289B003E004B3906 /* SourceRssParser+CoreDataProperties.swift in Sources */,
|
||||
0CA3FB2028B91D9500FA10A8 /* IndeterminateProgressView.swift in Sources */,
|
||||
0C50055A2992BA6A0064606A /* PluginTag+CoreDataClass.swift in Sources */,
|
||||
0C1A3E5229C8A7F500DA9730 /* SettingsModels.swift in Sources */,
|
||||
0CBC7705288DE7F40054BE44 /* PersistenceController.swift in Sources */,
|
||||
0C0755C8293425B500ECA142 /* DebridManagerModels.swift in Sources */,
|
||||
0C31133D28B1ABFA004DCB0D /* SourceJsonParser+CoreDataProperties.swift in Sources */,
|
||||
|
|
@ -828,6 +835,7 @@
|
|||
0C68135028BC1A2D00FAD890 /* GithubWrapper.swift in Sources */,
|
||||
0CA148E3288903F000DE2211 /* Task.swift in Sources */,
|
||||
0CA148E7288903F000DE2211 /* LoggingManager.swift in Sources */,
|
||||
0C1A3E5629C9488C00DA9730 /* CodableWrapper.swift in Sources */,
|
||||
0C6771FC29B3E0DB005D38D2 /* HybridSecureField.swift in Sources */,
|
||||
0C6C7C9D29315292002DF910 /* AllDebridModels.swift in Sources */,
|
||||
0C6C7C9B2931521B002DF910 /* AllDebridWrapper.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -94,27 +94,21 @@ public class Kodi {
|
|||
}
|
||||
}
|
||||
|
||||
public func sendVideoUrl(urlString: String) async throws {
|
||||
guard let baseUrl = UserDefaults.standard.string(forKey: "ExternalServices.KodiUrl") else {
|
||||
throw KodiError.InvalidBaseUrl
|
||||
}
|
||||
|
||||
public func sendVideoUrl(urlString: String, server: KodiServer) async throws {
|
||||
if URL(string: urlString) == nil {
|
||||
throw KodiError.InvalidPlaybackUrl
|
||||
}
|
||||
let username = UserDefaults.standard.string(forKey: "ExternalServices.KodiUsername")
|
||||
let password = UserDefaults.standard.string(forKey: "ExternalServices.KodiPassword")
|
||||
|
||||
let requestBody = RPCPayload(
|
||||
method: "Player.Open",
|
||||
params: Params(item: Item(file: urlString))
|
||||
)
|
||||
|
||||
var request = URLRequest(url: URL(string: "\(baseUrl)/jsonrpc")!)
|
||||
var request = URLRequest(url: URL(string: "\(server.urlString)/jsonrpc")!)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
if let username, let password {
|
||||
if let username = server.username, let password = server.password {
|
||||
request.setValue("Basic \(Data("\(username):\(password)".utf8).base64EncodedString())", forHTTPHeaderField: "Authorization")
|
||||
}
|
||||
|
||||
|
|
|
|||
19
Ferrite/Models/SettingsModels.swift
Normal file
19
Ferrite/Models/SettingsModels.swift
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// SettingsModels.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 3/20/23.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum DefaultAction: Codable, CaseIterable, Hashable {
|
||||
static var allCases: [DefaultAction] {
|
||||
[.none, .share, .kodi, .custom(name: "", listId: "")]
|
||||
}
|
||||
|
||||
case none
|
||||
case share
|
||||
case kodi
|
||||
case custom(name: String, listId: String)
|
||||
}
|
||||
36
Ferrite/Utils/CodableWrapper.swift
Normal file
36
Ferrite/Utils/CodableWrapper.swift
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// CodableWrapper.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 3/20/23.
|
||||
//
|
||||
// From https://forums.swift.org/t/rawrepresentable-conformance-leads-to-crash/51912/4
|
||||
// Prevents recursion when using Codable with RawRepresentable without needing manual conformance
|
||||
|
||||
import Foundation
|
||||
|
||||
struct CodableWrapper<Value: Codable> {
|
||||
var value: Value
|
||||
}
|
||||
|
||||
extension CodableWrapper: RawRepresentable {
|
||||
var rawValue: String {
|
||||
guard
|
||||
let data = try? JSONEncoder().encode(value),
|
||||
let string = String(data: data, encoding: .utf8)
|
||||
else {
|
||||
return ""
|
||||
}
|
||||
return string
|
||||
}
|
||||
|
||||
init?(rawValue: String) {
|
||||
guard
|
||||
let data = rawValue.data(using: .utf8),
|
||||
let decoded = try? JSONDecoder().decode(Value.self, from: data)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
value = decoded
|
||||
}
|
||||
}
|
||||
|
|
@ -48,7 +48,7 @@ public class NavigationViewModel: ObservableObject {
|
|||
@Published var selectedTitle: String = ""
|
||||
@Published var selectedBatchTitle: String = ""
|
||||
|
||||
@Published var hideNavigationBar = false
|
||||
@Published var kodiExpanded: Bool = false
|
||||
|
||||
@Published var currentChoiceSheet: ChoiceSheetType?
|
||||
var activityItems: [Any] = []
|
||||
|
|
|
|||
|
|
@ -258,57 +258,51 @@ public class PluginManager: ObservableObject {
|
|||
}
|
||||
|
||||
@MainActor
|
||||
public func runDebridAction(urlString: String?, navModel: NavigationViewModel) {
|
||||
public func runDefaultAction(urlString: String?, navModel: NavigationViewModel) {
|
||||
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 {
|
||||
navModel.currentChoiceSheet = .action
|
||||
UserDefaults.standard.set(nil, forKey: "Actions.DefaultDebridName")
|
||||
UserDefaults.standard.set(nil, forKey: "Action.DefaultDebridList")
|
||||
|
||||
actionErrorAlertMessage =
|
||||
"The default action could not be run. The action choice sheet has been opened. \n\n" +
|
||||
"Please check your default actions in Settings"
|
||||
showActionErrorAlert.toggle()
|
||||
}
|
||||
} else {
|
||||
navModel.currentChoiceSheet = .action
|
||||
guard let urlString else {
|
||||
logManager?.error("Default action: Could not run because the URL is invalid")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
public func runMagnetAction(urlString: String?, navModel: NavigationViewModel) {
|
||||
let context = PersistenceController.shared.backgroundContext
|
||||
let defaultsKey: String
|
||||
// Assume this is a magnet link
|
||||
if urlString.starts(with: "magnet") {
|
||||
defaultsKey = "Actions.DefaultMagnet"
|
||||
} else {
|
||||
defaultsKey = "Actions.DefaultDebrid"
|
||||
}
|
||||
|
||||
if
|
||||
let defaultMagnetActionName = UserDefaults.standard.string(forKey: "Actions.DefaultMagnetName"),
|
||||
let defaultMagnetActionList = UserDefaults.standard.string(forKey: "Actions.DefaultMagnetList")
|
||||
let rawValue = UserDefaults.standard.string(forKey: defaultsKey),
|
||||
let defaultAction = CodableWrapper<DefaultAction>(rawValue: rawValue)?.value
|
||||
{
|
||||
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 {
|
||||
switch defaultAction {
|
||||
case .none:
|
||||
navModel.currentChoiceSheet = .action
|
||||
UserDefaults.standard.set(nil, forKey: "Actions.DefaultMagnetName")
|
||||
UserDefaults.standard.set(nil, forKey: "Actions.DefaultMagnetList")
|
||||
case .share:
|
||||
navModel.activityItems = [urlString]
|
||||
navModel.currentChoiceSheet = .activity
|
||||
case .kodi:
|
||||
navModel.kodiExpanded = true
|
||||
navModel.currentChoiceSheet = .action
|
||||
case .custom(let name, let listId):
|
||||
let actionFetchRequest = Action.fetchRequest()
|
||||
actionFetchRequest.fetchLimit = 1
|
||||
actionFetchRequest.predicate = NSPredicate(format: "name == %@ AND listId == %@", name, listId)
|
||||
|
||||
actionErrorAlertMessage =
|
||||
"The default action could not be run. The action choice sheet has been opened. \n\n" +
|
||||
"Please check your default actions in Settings"
|
||||
showActionErrorAlert.toggle()
|
||||
if let fetchedAction = try? context.fetch(actionFetchRequest).first {
|
||||
runDeeplinkAction(fetchedAction, urlString: urlString)
|
||||
} else {
|
||||
navModel.currentChoiceSheet = .action
|
||||
UserDefaults.standard.set(CodableWrapper<DefaultAction>(value: .none).rawValue, forKey: "Actions.DefaultDebrid")
|
||||
|
||||
actionErrorAlertMessage =
|
||||
"The default action could not be run. The action choice sheet has been opened. \n\n" +
|
||||
"Please check your default actions in Settings"
|
||||
showActionErrorAlert.toggle()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
navModel.currentChoiceSheet = .action
|
||||
|
|
@ -340,7 +334,7 @@ public class PluginManager: ObservableObject {
|
|||
}
|
||||
|
||||
@MainActor
|
||||
public func sendToKodi(urlString: String?) async {
|
||||
public func sendToKodi(urlString: String?, server: KodiServer) async {
|
||||
guard let urlString else {
|
||||
actionErrorAlertMessage = "Could not send URL to Kodi since there is no playback URL to send"
|
||||
showActionErrorAlert.toggle()
|
||||
|
|
@ -351,7 +345,7 @@ public class PluginManager: ObservableObject {
|
|||
}
|
||||
|
||||
do {
|
||||
try await kodi.sendVideoUrl(urlString: urlString)
|
||||
try await kodi.sendVideoUrl(urlString: urlString, server: server)
|
||||
|
||||
actionSuccessAlertMessage = "Your URL should be playing on Kodi"
|
||||
showActionSuccessAlert.toggle()
|
||||
|
|
|
|||
|
|
@ -12,18 +12,10 @@ struct CustomScopeBarModifier<V: View>: ViewModifier {
|
|||
let hostingContent: V
|
||||
@State private var hostingController: UIHostingController<V>?
|
||||
|
||||
// Don't use AppStorage since it causes a view update
|
||||
var autocorrectSearch: Bool {
|
||||
UserDefaults.standard.bool(forKey: "Behavior.AutocorrectSearch")
|
||||
}
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
if #available(iOS 15, *) {
|
||||
content
|
||||
.backport.introspectSearchController { searchController in
|
||||
searchController.searchBar.autocorrectionType = autocorrectSearch ? .default : .no
|
||||
searchController.searchBar.autocapitalizationType = autocorrectSearch ? .sentences : .none
|
||||
|
||||
// MARK: One-time setup
|
||||
|
||||
guard hostingController == nil else { return }
|
||||
|
|
@ -64,10 +56,6 @@ struct CustomScopeBarModifier<V: View>: ViewModifier {
|
|||
content
|
||||
Spacer()
|
||||
}
|
||||
.backport.introspectSearchController { searchController in
|
||||
searchController.searchBar.autocorrectionType = autocorrectSearch ? .default : .no
|
||||
searchController.searchBar.autocapitalizationType = autocorrectSearch ? .sentences : .none
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ struct AllDebridCloudView: View {
|
|||
if !debridManager.downloadUrl.isEmpty {
|
||||
historyInfo.url = debridManager.downloadUrl
|
||||
PersistenceController.shared.createHistory(historyInfo, performSave: true)
|
||||
pluginManager.runDebridAction(
|
||||
pluginManager.runDefaultAction(
|
||||
urlString: debridManager.downloadUrl,
|
||||
navModel: navModel
|
||||
)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ struct PremiumizeCloudView: View {
|
|||
performSave: true
|
||||
)
|
||||
|
||||
pluginManager.runDebridAction(
|
||||
pluginManager.runDefaultAction(
|
||||
urlString: debridManager.downloadUrl,
|
||||
navModel: navModel
|
||||
)
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ struct RealDebridCloudView: View {
|
|||
performSave: true
|
||||
)
|
||||
|
||||
pluginManager.runDebridAction(
|
||||
pluginManager.runDefaultAction(
|
||||
urlString: debridManager.downloadUrl,
|
||||
navModel: navModel
|
||||
)
|
||||
|
|
@ -76,7 +76,7 @@ struct RealDebridCloudView: View {
|
|||
historyInfo.url = debridManager.downloadUrl
|
||||
PersistenceController.shared.createHistory(historyInfo, performSave: true)
|
||||
|
||||
pluginManager.runDebridAction(
|
||||
pluginManager.runDefaultAction(
|
||||
urlString: debridManager.downloadUrl,
|
||||
navModel: navModel
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ struct HistoryButtonView: View {
|
|||
if url.starts(with: "https://") {
|
||||
Task {
|
||||
debridManager.downloadUrl = url
|
||||
pluginManager.runDebridAction(
|
||||
pluginManager.runDefaultAction(
|
||||
urlString: url,
|
||||
navModel: navModel
|
||||
)
|
||||
|
|
@ -34,7 +34,7 @@ struct HistoryButtonView: View {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
pluginManager.runMagnetAction(
|
||||
pluginManager.runDefaultAction(
|
||||
urlString: url,
|
||||
navModel: navModel
|
||||
)
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ struct PluginAggregateView<P: Plugin, PJ: PluginJson>: View {
|
|||
.onChange(of: installedPlugins.count) { newCount in
|
||||
pluginsEmpty = newCount == 0
|
||||
}
|
||||
.id(UUID())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ struct SearchResultButtonView: View {
|
|||
performSave: true
|
||||
)
|
||||
|
||||
pluginManager.runDebridAction(
|
||||
pluginManager.runDefaultAction(
|
||||
urlString: debridManager.downloadUrl,
|
||||
navModel: navModel
|
||||
)
|
||||
|
|
@ -72,7 +72,7 @@ struct SearchResultButtonView: View {
|
|||
performSave: true
|
||||
)
|
||||
|
||||
pluginManager.runMagnetAction(
|
||||
pluginManager.runDefaultAction(
|
||||
urlString: result.magnet.link,
|
||||
navModel: navModel
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ struct DefaultActionPickerView: View {
|
|||
@EnvironmentObject var logManager: LoggingManager
|
||||
|
||||
let actionRequirement: ActionRequirement
|
||||
@Binding var defaultActionName: String?
|
||||
@Binding var defaultActionList: String?
|
||||
|
||||
@Binding var defaultAction: DefaultAction
|
||||
|
||||
@FetchRequest(
|
||||
entity: Action.entity(),
|
||||
|
|
@ -24,19 +24,25 @@ struct DefaultActionPickerView: View {
|
|||
sortDescriptors: []
|
||||
) var pluginLists: FetchedResults<PluginList>
|
||||
|
||||
@FetchRequest(
|
||||
entity: KodiServer.entity(),
|
||||
sortDescriptors: []
|
||||
) var kodiServers: FetchedResults<KodiServer>
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
UserChoiceButton(
|
||||
defaultActionName: $defaultActionName,
|
||||
defaultActionList: $defaultActionList,
|
||||
pluginLists: pluginLists
|
||||
)
|
||||
DefaultChoiceButton(defaultAction: $defaultAction, selectedOption: .none)
|
||||
DefaultChoiceButton(defaultAction: $defaultAction, selectedOption: .share)
|
||||
|
||||
if actionRequirement == .debrid && !kodiServers.isEmpty {
|
||||
DefaultChoiceButton(defaultAction: $defaultAction, selectedOption: .kodi)
|
||||
}
|
||||
|
||||
// Handle custom here
|
||||
ForEach(actions.filter { $0.requires.contains(actionRequirement.rawValue) }, id: \.id) { action in
|
||||
Button {
|
||||
if let actionListId = action.listId?.uuidString {
|
||||
defaultActionName = action.name
|
||||
defaultActionList = actionListId
|
||||
defaultAction = .custom(name: action.name, listId: actionListId)
|
||||
} else {
|
||||
logManager.error(
|
||||
"Default action: This action doesn't have a corresponding plugin list! Please uninstall the action"
|
||||
|
|
@ -63,9 +69,9 @@ struct DefaultActionPickerView: View {
|
|||
Spacer()
|
||||
|
||||
if
|
||||
let defaultActionList,
|
||||
action.listId?.uuidString == defaultActionList,
|
||||
action.name == defaultActionName
|
||||
case let .custom(name, listId) = defaultAction,
|
||||
action.listId?.uuidString == listId,
|
||||
action.name == name
|
||||
{
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.blue)
|
||||
|
|
@ -77,28 +83,24 @@ struct DefaultActionPickerView: View {
|
|||
}
|
||||
.listStyle(.insetGrouped)
|
||||
.inlinedList(inset: -20)
|
||||
.navigationTitle("Default \(actionRequirement == .debrid ? "debrid" : "magnet") action")
|
||||
.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>
|
||||
private struct DefaultChoiceButton: View {
|
||||
@Binding var defaultAction: DefaultAction
|
||||
let selectedOption: DefaultAction
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
defaultActionName = nil
|
||||
defaultActionList = nil
|
||||
defaultAction = selectedOption
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Let me choose")
|
||||
Text(fetchButtonName())
|
||||
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 }) {
|
||||
if defaultAction == selectedOption {
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
|
|
@ -106,4 +108,18 @@ private struct UserChoiceButton: View {
|
|||
}
|
||||
.backport.tint(.primary)
|
||||
}
|
||||
|
||||
func fetchButtonName() -> String {
|
||||
switch selectedOption {
|
||||
case .none:
|
||||
return "Let me choose"
|
||||
case .share:
|
||||
return "Share link"
|
||||
case .kodi:
|
||||
return "Open in Kodi"
|
||||
case .custom(_, _):
|
||||
// This should not be called
|
||||
return "Custom button"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ struct SettingsDebridInfoView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.listStyle(.insetGrouped)
|
||||
.navigationTitle(debridType.toString())
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ struct ContentView: View {
|
|||
@EnvironmentObject var pluginManager: PluginManager
|
||||
@EnvironmentObject var logManager: LoggingManager
|
||||
|
||||
@AppStorage("Behavior.AutocorrectSearch") var autocorrectSearch: Bool = false
|
||||
@AppStorage("Behavior.UsesRandomSearchText") var usesRandomSearchText: Bool = false
|
||||
|
||||
@State private var isEditingSearch = false
|
||||
|
|
@ -110,6 +111,11 @@ struct ContentView: View {
|
|||
searchBarText = getSearchBarText()
|
||||
}
|
||||
}
|
||||
.autocorrectionDisabled(!autocorrectSearch)
|
||||
.backport.introspectSearchController { searchController in
|
||||
// TODO: Replace with SwiftUI autocapitalization modifier
|
||||
searchController.searchBar.autocapitalizationType = .none
|
||||
}
|
||||
.navigationSearchBarHiddenWhenScrolling(false)
|
||||
.customScopeBar {
|
||||
SearchFilterHeaderView()
|
||||
|
|
|
|||
|
|
@ -27,14 +27,12 @@ struct SettingsView: View {
|
|||
|
||||
@AppStorage("Updates.AutomaticNotifs") var autoUpdateNotifs = true
|
||||
|
||||
@AppStorage("Actions.DefaultDebridName") var defaultDebridActionName: String?
|
||||
@AppStorage("Actions.DefaultDebridList") var defaultDebridActionList: String?
|
||||
|
||||
@AppStorage("Actions.DefaultMagnetName") var defaultMagnetActionName: String?
|
||||
@AppStorage("Actions.DefaultMagnetList") var defaultMagnetActionList: String?
|
||||
@AppStorage("Actions.DefaultMagnet") var defaultMagnetAction: CodableWrapper<DefaultAction> = .init(value: .none)
|
||||
@AppStorage("Actions.DefaultDebrid") var defaultDebridAction: CodableWrapper<DefaultAction> = .init(value: .none)
|
||||
|
||||
@AppStorage("Debug.ShowErrorToasts") var showErrorToasts = true
|
||||
|
||||
|
||||
var body: some View {
|
||||
NavView {
|
||||
Form {
|
||||
|
|
@ -85,17 +83,26 @@ struct SettingsView: View {
|
|||
NavigationLink(
|
||||
destination: DefaultActionPickerView(
|
||||
actionRequirement: .debrid,
|
||||
defaultActionName: $defaultDebridActionName,
|
||||
defaultActionList: $defaultDebridActionList
|
||||
defaultAction: $defaultDebridAction.value
|
||||
),
|
||||
label: {
|
||||
HStack {
|
||||
Text("Debrid action")
|
||||
Spacer()
|
||||
|
||||
// TODO: Maybe make this check for nil list as well?
|
||||
Text(defaultDebridActionName.map { $0 } ?? "User choice")
|
||||
.foregroundColor(.secondary)
|
||||
Group {
|
||||
switch defaultDebridAction.value {
|
||||
case .none:
|
||||
Text("User choice")
|
||||
case .share:
|
||||
Text("Share")
|
||||
case .kodi:
|
||||
Text("Kodi")
|
||||
case .custom(let name, _):
|
||||
Text(name)
|
||||
}
|
||||
}
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
@ -104,17 +111,26 @@ struct SettingsView: View {
|
|||
NavigationLink(
|
||||
destination: DefaultActionPickerView(
|
||||
actionRequirement: .magnet,
|
||||
defaultActionName: $defaultMagnetActionName,
|
||||
defaultActionList: $defaultMagnetActionList
|
||||
defaultAction: $defaultMagnetAction.value
|
||||
),
|
||||
label: {
|
||||
HStack {
|
||||
Text("Magnet action")
|
||||
Spacer()
|
||||
|
||||
// TODO: Maybe make this check for nil list as well?
|
||||
Text(defaultMagnetActionName.map { $0 } ?? "User choice")
|
||||
.foregroundColor(.secondary)
|
||||
Group {
|
||||
switch defaultMagnetAction.value {
|
||||
case .none:
|
||||
Text("User choice")
|
||||
case .share:
|
||||
Text("Share")
|
||||
case .kodi:
|
||||
Text("Kodi")
|
||||
case .custom(let name, _):
|
||||
Text(name)
|
||||
}
|
||||
}
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -57,11 +57,11 @@ struct ActionChoiceView: View {
|
|||
}
|
||||
|
||||
if !kodiServers.isEmpty {
|
||||
DisclosureGroup("Open in Kodi") {
|
||||
DisclosureGroup("Open in Kodi", isExpanded: $navModel.kodiExpanded) {
|
||||
ForEach(kodiServers, id: \.self) { server in
|
||||
Button {
|
||||
Task {
|
||||
await pluginManager.sendToKodi(urlString: debridManager.downloadUrl)
|
||||
await pluginManager.sendToKodi(urlString: debridManager.downloadUrl, server: server)
|
||||
}
|
||||
} label: {
|
||||
KodiServerView(server: server)
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ struct BatchChoiceView: View {
|
|||
PersistenceController.shared.createHistory(selectedHistoryInfo, performSave: true)
|
||||
}
|
||||
|
||||
pluginManager.runDebridAction(
|
||||
pluginManager.runDefaultAction(
|
||||
urlString: debridManager.downloadUrl,
|
||||
navModel: navModel
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue