mirror of
https://github.com/Ferrite-iOS/Ferrite.git
synced 2026-01-11 20:10:27 +00:00
Plugins: Unify settings
Plugin settings used to only be available for installed sources. Change this to display info about an installed plugin and add settings depending on the plugin type. For example, a source will have additional settings specified by its own views. Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
parent
9f83ebfce0
commit
39a705717e
9 changed files with 275 additions and 219 deletions
|
|
@ -93,6 +93,10 @@
|
|||
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 */; };
|
||||
0C8DC35229CE287E008A83AD /* PluginInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8DC35129CE287E008A83AD /* PluginInfoView.swift */; };
|
||||
0C8DC35429CE2AB5008A83AD /* SourceSettingsBaseUrlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8DC35329CE2AB5008A83AD /* SourceSettingsBaseUrlView.swift */; };
|
||||
0C8DC35629CE2ABF008A83AD /* SourceSettingsApiView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8DC35529CE2ABF008A83AD /* SourceSettingsApiView.swift */; };
|
||||
0C8DC35829CE2ACA008A83AD /* SourceSettingsMethodView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C8DC35729CE2ACA008A83AD /* SourceSettingsMethodView.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 */; };
|
||||
|
|
@ -228,6 +232,10 @@
|
|||
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>"; };
|
||||
0C871BDE29994D9D005279AC /* FilterLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterLabelView.swift; sourceTree = "<group>"; };
|
||||
0C8DC35129CE287E008A83AD /* PluginInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginInfoView.swift; sourceTree = "<group>"; };
|
||||
0C8DC35329CE2AB5008A83AD /* SourceSettingsBaseUrlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceSettingsBaseUrlView.swift; sourceTree = "<group>"; };
|
||||
0C8DC35529CE2ABF008A83AD /* SourceSettingsApiView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceSettingsApiView.swift; sourceTree = "<group>"; };
|
||||
0C8DC35729CE2ACA008A83AD /* SourceSettingsMethodView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceSettingsMethodView.swift; sourceTree = "<group>"; };
|
||||
0C95D8D728A55B03005E22B3 /* DefaultActionPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultActionPickerView.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>"; };
|
||||
|
|
@ -417,6 +425,7 @@
|
|||
0C0D50E6288DFF850035ECC8 /* PluginAggregateView.swift */,
|
||||
0C5005512992B6750064606A /* PluginTagsView.swift */,
|
||||
0CD5F1FC299C083B00476DDB /* PluginPickerView.swift */,
|
||||
0C8DC35129CE287E008A83AD /* PluginInfoView.swift */,
|
||||
);
|
||||
path = Plugin;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -475,6 +484,9 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
0C733286289C4C820058D1FE /* SourceSettingsView.swift */,
|
||||
0C8DC35329CE2AB5008A83AD /* SourceSettingsBaseUrlView.swift */,
|
||||
0C8DC35529CE2ABF008A83AD /* SourceSettingsApiView.swift */,
|
||||
0C8DC35729CE2ACA008A83AD /* SourceSettingsMethodView.swift */,
|
||||
);
|
||||
path = Source;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -773,6 +785,7 @@
|
|||
0C6771FA29B3D1AE005D38D2 /* KodiModels.swift in Sources */,
|
||||
0C0D50E5288DFE7F0035ECC8 /* SourceModels.swift in Sources */,
|
||||
0CB6516528C5A5D700DCA721 /* InlinedList.swift in Sources */,
|
||||
0C8DC35429CE2AB5008A83AD /* SourceSettingsBaseUrlView.swift in Sources */,
|
||||
0C32FB532890D19D002BD219 /* AboutView.swift in Sources */,
|
||||
0CB6516328C5A57300DCA721 /* ConditionalId.swift in Sources */,
|
||||
0C70E40628C40C4E00A5C72D /* NotificationCenter.swift in Sources */,
|
||||
|
|
@ -824,6 +837,7 @@
|
|||
0CA148E1288903F000DE2211 /* Collection.swift in Sources */,
|
||||
0C750744289B003E004B3906 /* SourceRssParser+CoreDataClass.swift in Sources */,
|
||||
0C794B69289DACC800DD1CC8 /* InstalledPluginButtonView.swift in Sources */,
|
||||
0C8DC35829CE2ACA008A83AD /* SourceSettingsMethodView.swift in Sources */,
|
||||
0C5708EB29B8F89300BE07F9 /* SettingsLogView.swift in Sources */,
|
||||
0C79DC082899AF3C003F1C5A /* SourceSeedLeech+CoreDataProperties.swift in Sources */,
|
||||
0C6771FE29B521F1005D38D2 /* SettingsDebridInfoView.swift in Sources */,
|
||||
|
|
@ -869,7 +883,9 @@
|
|||
0C6771F429B3B4FD005D38D2 /* KodiWrapper.swift in Sources */,
|
||||
0C3E00D0296F4DB200ECECB2 /* ActionModels.swift in Sources */,
|
||||
0C44E2AD28D51C63007711AE /* BackupManager.swift in Sources */,
|
||||
0C8DC35229CE287E008A83AD /* PluginInfoView.swift in Sources */,
|
||||
0C422E80293542F300486D65 /* PremiumizeModels.swift in Sources */,
|
||||
0C8DC35629CE2ABF008A83AD /* SourceSettingsApiView.swift in Sources */,
|
||||
0C4CFC4D28970C8B00AD9FAD /* SourceComplexQuery+CoreDataClass.swift in Sources */,
|
||||
0C0974B029CCAAAF006DE7A3 /* OperatingSystemVersion.swift in Sources */,
|
||||
0CA148E8288903F000DE2211 /* RealDebridWrapper.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -58,11 +58,6 @@ public class NavigationViewModel: ObservableObject {
|
|||
|
||||
@Published var selectedTab: ViewTab = .search
|
||||
|
||||
// TODO: Maybe move these to their own StateObjects?
|
||||
// Used between SourceListView and SourceSettingsView
|
||||
@Published var showSourceSettings: Bool = false
|
||||
var selectedSource: Source?
|
||||
|
||||
// Used between service views and editor views in Settings
|
||||
@Published var selectedPluginList: PluginList?
|
||||
@Published var selectedKodiServer: KodiServer?
|
||||
|
|
|
|||
|
|
@ -10,10 +10,11 @@ import SwiftUI
|
|||
struct InstalledPluginButtonView<P: Plugin>: View {
|
||||
let backgroundContext = PersistenceController.shared.backgroundContext
|
||||
|
||||
@EnvironmentObject var navModel: NavigationViewModel
|
||||
|
||||
@ObservedObject var installedPlugin: P
|
||||
|
||||
@Binding var showPluginOptions: Bool
|
||||
@Binding var selectedPlugin: P?
|
||||
|
||||
var body: some View {
|
||||
Toggle(isOn: Binding<Bool>(
|
||||
get: { installedPlugin.enabled },
|
||||
|
|
@ -42,14 +43,12 @@ struct InstalledPluginButtonView<P: Plugin>: View {
|
|||
.padding(.vertical, 2)
|
||||
}
|
||||
.contextMenu {
|
||||
if let installedSource = installedPlugin as? Source {
|
||||
Button {
|
||||
navModel.selectedSource = installedSource
|
||||
navModel.showSourceSettings.toggle()
|
||||
} label: {
|
||||
Text("Settings")
|
||||
Image(systemName: "gear")
|
||||
}
|
||||
Button {
|
||||
selectedPlugin = installedPlugin
|
||||
showPluginOptions.toggle()
|
||||
} label: {
|
||||
Text("Options")
|
||||
Image(systemName: "gear")
|
||||
}
|
||||
|
||||
if #available(iOS 15.0, *) {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ struct PluginAggregateView<P: Plugin, PJ: PluginJson>: View {
|
|||
|
||||
@State private var sourcePredicate: NSPredicate?
|
||||
|
||||
@State private var showPluginOptions = false
|
||||
@State private var selectedPlugin: P?
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
DynamicFetchRequest(predicate: sourcePredicate) { (installedPlugins: FetchedResults<P>) in
|
||||
|
|
@ -49,7 +52,11 @@ struct PluginAggregateView<P: Plugin, PJ: PluginJson>: View {
|
|||
if !installedPlugins.isEmpty {
|
||||
Section(header: InlineHeader("Installed")) {
|
||||
ForEach(installedPlugins, id: \.self) { installedPlugin in
|
||||
InstalledPluginButtonView(installedPlugin: installedPlugin)
|
||||
InstalledPluginButtonView(
|
||||
installedPlugin: installedPlugin,
|
||||
showPluginOptions: $showPluginOptions,
|
||||
selectedPlugin: $selectedPlugin
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -82,11 +89,8 @@ struct PluginAggregateView<P: Plugin, PJ: PluginJson>: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $navModel.showSourceSettings) {
|
||||
if String(describing: P.self) == "Source" {
|
||||
SourceSettingsView()
|
||||
.environmentObject(navModel)
|
||||
}
|
||||
.sheet(isPresented: $showPluginOptions) {
|
||||
PluginInfoView(selectedPlugin: $selectedPlugin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
78
Ferrite/Views/ComponentViews/Plugin/PluginInfoView.swift
Normal file
78
Ferrite/Views/ComponentViews/Plugin/PluginInfoView.swift
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// PluginInfoView.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 3/24/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct PluginInfoView<P: Plugin>: View {
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
|
||||
@Binding var selectedPlugin: P?
|
||||
|
||||
@FetchRequest(
|
||||
entity: PluginList.entity(),
|
||||
sortDescriptors: []
|
||||
) var pluginLists: FetchedResults<PluginList>
|
||||
|
||||
var body: some View {
|
||||
NavView {
|
||||
List {
|
||||
if let selectedPlugin {
|
||||
Section(header: InlineHeader("Info")) {
|
||||
VStack(alignment: .leading) {
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
HStack(spacing: 5) {
|
||||
Text(selectedPlugin.name)
|
||||
|
||||
Text("v\(selectedPlugin.version)")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Text("by \(selectedPlugin.author)")
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
Group {
|
||||
Text("ID: \(selectedPlugin.id)")
|
||||
|
||||
if let pluginList = pluginLists.first(where: { $0.id == selectedPlugin.listId })
|
||||
{
|
||||
Text("List: \(pluginList.name)")
|
||||
Text("List ID: \(pluginList.id.uuidString)")
|
||||
} else {
|
||||
Text("No plugin list found. This source should be removed.")
|
||||
}
|
||||
}
|
||||
.foregroundColor(.secondary)
|
||||
.font(.caption)
|
||||
}
|
||||
|
||||
if let tags = selectedPlugin.getTags(), !tags.isEmpty {
|
||||
PluginTagsView(tags: tags)
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 2)
|
||||
}
|
||||
|
||||
if let selectedSource = selectedPlugin as? Source {
|
||||
SourceSettingsView(selectedSource: selectedSource)
|
||||
}
|
||||
}
|
||||
}
|
||||
.listStyle(.insetGrouped)
|
||||
.onDisappear {
|
||||
PersistenceController.shared.save()
|
||||
}
|
||||
.navigationTitle("Plugin Options")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button("Done") {
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// SourceSettingsApiView.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 3/24/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SourceSettingsApiView: View {
|
||||
@ObservedObject var selectedSourceApi: SourceApi
|
||||
|
||||
@State private var tempClientId: String = ""
|
||||
@State private var tempClientSecret: String = ""
|
||||
|
||||
enum Field {
|
||||
case secure, plain
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Section(
|
||||
header: InlineHeader("API credentials"),
|
||||
footer: Text("Grab the required API credentials from the website. A client secret can be an API token.")
|
||||
) {
|
||||
if let clientId = selectedSourceApi.clientId, clientId.dynamic {
|
||||
TextField("Client ID", text: $tempClientId, onEditingChanged: { isFocused in
|
||||
if !isFocused {
|
||||
clientId.value = tempClientId
|
||||
clientId.timeStamp = Date()
|
||||
}
|
||||
})
|
||||
.autocorrectionDisabled(true)
|
||||
.autocapitalization(.none)
|
||||
.backport.onAppear {
|
||||
tempClientId = clientId.value ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
if let clientSecret = selectedSourceApi.clientSecret, clientSecret.dynamic {
|
||||
TextField("Token", text: $tempClientSecret, onEditingChanged: { isFocused in
|
||||
if !isFocused {
|
||||
clientSecret.value = tempClientSecret
|
||||
clientSecret.timeStamp = Date()
|
||||
}
|
||||
})
|
||||
.autocorrectionDisabled(true)
|
||||
.autocapitalization(.none)
|
||||
.backport.onAppear {
|
||||
tempClientSecret = clientSecret.value ?? ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// SourceSettingsBaseUrlView.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 3/24/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SourceSettingsBaseUrlView: View {
|
||||
@ObservedObject var selectedSource: Source
|
||||
|
||||
@State private var tempBaseUrl: String = ""
|
||||
var body: some View {
|
||||
Section(
|
||||
header: InlineHeader("Base URL"),
|
||||
footer: Text("Enter the base URL of your server.")
|
||||
) {
|
||||
TextField("https://...", text: $tempBaseUrl, onEditingChanged: { isFocused in
|
||||
if !isFocused {
|
||||
if tempBaseUrl.last == "/" {
|
||||
selectedSource.baseUrl = String(tempBaseUrl.dropLast())
|
||||
} else {
|
||||
selectedSource.baseUrl = tempBaseUrl
|
||||
}
|
||||
}
|
||||
})
|
||||
.keyboardType(.URL)
|
||||
.autocorrectionDisabled(true)
|
||||
.autocapitalization(.none)
|
||||
.backport.onAppear {
|
||||
tempBaseUrl = selectedSource.baseUrl ?? ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
//
|
||||
// SourceSettingsMethodView.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 3/24/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SourceSettingsMethodView: View {
|
||||
@ObservedObject var selectedSource: Source
|
||||
|
||||
var body: some View {
|
||||
Section(header: InlineHeader("Fetch method")) {
|
||||
if selectedSource.jsonParser != nil {
|
||||
Button {
|
||||
selectedSource.preferredParser = SourcePreferredParser.siteApi.rawValue
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Website API")
|
||||
Spacer()
|
||||
if SourcePreferredParser.siteApi.rawValue == selectedSource.preferredParser {
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if selectedSource.rssParser != nil {
|
||||
Button {
|
||||
selectedSource.preferredParser = SourcePreferredParser.rss.rawValue
|
||||
} label: {
|
||||
HStack {
|
||||
Text("RSS")
|
||||
Spacer()
|
||||
if SourcePreferredParser.rss.rawValue == selectedSource.preferredParser {
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if selectedSource.htmlParser != nil {
|
||||
Button {
|
||||
selectedSource.preferredParser = SourcePreferredParser.scraping.rawValue
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Web scraping")
|
||||
Spacer()
|
||||
if SourcePreferredParser.scraping.rawValue == selectedSource.preferredParser {
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.backport.tint(.primary)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,207 +8,19 @@
|
|||
import SwiftUI
|
||||
|
||||
struct SourceSettingsView: View {
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
|
||||
@EnvironmentObject var navModel: NavigationViewModel
|
||||
|
||||
@FetchRequest(
|
||||
entity: PluginList.entity(),
|
||||
sortDescriptors: []
|
||||
) var pluginLists: FetchedResults<PluginList>
|
||||
|
||||
var body: some View {
|
||||
NavView {
|
||||
List {
|
||||
if let selectedSource = navModel.selectedSource {
|
||||
Section(header: InlineHeader("Info")) {
|
||||
VStack(alignment: .leading) {
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
HStack {
|
||||
Text(selectedSource.name)
|
||||
|
||||
Text("v\(selectedSource.version)")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Text("by \(selectedSource.author)")
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
Group {
|
||||
Text("ID: \(selectedSource.id)")
|
||||
|
||||
if let pluginList = pluginLists.first(where: { $0.id == selectedSource.listId })
|
||||
{
|
||||
Text("List: \(pluginList.name)")
|
||||
Text("List ID: \(pluginList.id.uuidString)")
|
||||
} else {
|
||||
Text("No plugin list found. This source should be removed.")
|
||||
}
|
||||
}
|
||||
.foregroundColor(.secondary)
|
||||
.font(.caption)
|
||||
}
|
||||
|
||||
if let tags = selectedSource.getTags(), !tags.isEmpty {
|
||||
PluginTagsView(tags: tags)
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 2)
|
||||
}
|
||||
|
||||
if selectedSource.dynamicBaseUrl {
|
||||
SourceSettingsBaseUrlView(selectedSource: selectedSource)
|
||||
}
|
||||
|
||||
if let sourceApi = selectedSource.api,
|
||||
sourceApi.clientId?.dynamic ?? false || sourceApi.clientSecret?.dynamic ?? false
|
||||
{
|
||||
SourceSettingsApiView(selectedSourceApi: sourceApi)
|
||||
}
|
||||
|
||||
SourceSettingsMethodView(selectedSource: selectedSource)
|
||||
}
|
||||
}
|
||||
.listStyle(.insetGrouped)
|
||||
.onDisappear {
|
||||
PersistenceController.shared.save()
|
||||
}
|
||||
.navigationTitle("Source Settings")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button("Done") {
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SourceSettingsBaseUrlView: View {
|
||||
@ObservedObject var selectedSource: Source
|
||||
|
||||
@State private var tempBaseUrl: String = ""
|
||||
var body: some View {
|
||||
Section(
|
||||
header: InlineHeader("Base URL"),
|
||||
footer: Text("Enter the base URL of your server.")
|
||||
) {
|
||||
TextField("https://...", text: $tempBaseUrl, onEditingChanged: { isFocused in
|
||||
if !isFocused {
|
||||
if tempBaseUrl.last == "/" {
|
||||
selectedSource.baseUrl = String(tempBaseUrl.dropLast())
|
||||
} else {
|
||||
selectedSource.baseUrl = tempBaseUrl
|
||||
}
|
||||
}
|
||||
})
|
||||
.keyboardType(.URL)
|
||||
.autocorrectionDisabled(true)
|
||||
.autocapitalization(.none)
|
||||
.backport.onAppear {
|
||||
tempBaseUrl = selectedSource.baseUrl ?? ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SourceSettingsApiView: View {
|
||||
@ObservedObject var selectedSourceApi: SourceApi
|
||||
|
||||
@State private var tempClientId: String = ""
|
||||
@State private var tempClientSecret: String = ""
|
||||
|
||||
enum Field {
|
||||
case secure, plain
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Section(
|
||||
header: InlineHeader("API credentials"),
|
||||
footer: Text("Grab the required API credentials from the website. A client secret can be an API token.")
|
||||
) {
|
||||
if let clientId = selectedSourceApi.clientId, clientId.dynamic {
|
||||
TextField("Client ID", text: $tempClientId, onEditingChanged: { isFocused in
|
||||
if !isFocused {
|
||||
clientId.value = tempClientId
|
||||
clientId.timeStamp = Date()
|
||||
}
|
||||
})
|
||||
.autocorrectionDisabled(true)
|
||||
.autocapitalization(.none)
|
||||
.backport.onAppear {
|
||||
tempClientId = clientId.value ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
if let clientSecret = selectedSourceApi.clientSecret, clientSecret.dynamic {
|
||||
TextField("Token", text: $tempClientSecret, onEditingChanged: { isFocused in
|
||||
if !isFocused {
|
||||
clientSecret.value = tempClientSecret
|
||||
clientSecret.timeStamp = Date()
|
||||
}
|
||||
})
|
||||
.autocorrectionDisabled(true)
|
||||
.autocapitalization(.none)
|
||||
.backport.onAppear {
|
||||
tempClientSecret = clientSecret.value ?? ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SourceSettingsMethodView: View {
|
||||
@ObservedObject var selectedSource: Source
|
||||
|
||||
var body: some View {
|
||||
Section(header: InlineHeader("Fetch method")) {
|
||||
if selectedSource.jsonParser != nil {
|
||||
Button {
|
||||
selectedSource.preferredParser = SourcePreferredParser.siteApi.rawValue
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Website API")
|
||||
Spacer()
|
||||
if SourcePreferredParser.siteApi.rawValue == selectedSource.preferredParser {
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if selectedSource.rssParser != nil {
|
||||
Button {
|
||||
selectedSource.preferredParser = SourcePreferredParser.rss.rawValue
|
||||
} label: {
|
||||
HStack {
|
||||
Text("RSS")
|
||||
Spacer()
|
||||
if SourcePreferredParser.rss.rawValue == selectedSource.preferredParser {
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if selectedSource.htmlParser != nil {
|
||||
Button {
|
||||
selectedSource.preferredParser = SourcePreferredParser.scraping.rawValue
|
||||
} label: {
|
||||
HStack {
|
||||
Text("Web scraping")
|
||||
Spacer()
|
||||
if SourcePreferredParser.scraping.rawValue == selectedSource.preferredParser {
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if selectedSource.dynamicBaseUrl {
|
||||
SourceSettingsBaseUrlView(selectedSource: selectedSource)
|
||||
}
|
||||
.backport.tint(.primary)
|
||||
|
||||
if let sourceApi = selectedSource.api,
|
||||
sourceApi.clientId?.dynamic ?? false || sourceApi.clientSecret?.dynamic ?? false
|
||||
{
|
||||
SourceSettingsApiView(selectedSourceApi: sourceApi)
|
||||
}
|
||||
|
||||
SourceSettingsMethodView(selectedSource: selectedSource)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue