Plugins: Add website and about properties
These will serve as descriptions for a plugin which will be displayed in the Plugin Info screen. website has also replaced baseUrl and dynamicWebsite has replaced dynamicBaseUrl Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
parent
51366f3215
commit
d918039810
14 changed files with 162 additions and 73 deletions
|
|
@ -137,6 +137,8 @@
|
|||
0CBC7705288DE7F40054BE44 /* PersistenceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CBC7704288DE7F40054BE44 /* PersistenceController.swift */; };
|
||||
0CC389532970AD900066D06F /* Action+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CC389512970AD900066D06F /* Action+CoreDataClass.swift */; };
|
||||
0CC389542970AD900066D06F /* Action+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CC389522970AD900066D06F /* Action+CoreDataProperties.swift */; };
|
||||
0CD4030A29DA01B6008D9F03 /* PluginInfoMetaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD4030929DA01B6008D9F03 /* PluginInfoMetaView.swift */; };
|
||||
0CD4030C29DA0222008D9F03 /* PluginInfoAboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD4030B29DA0222008D9F03 /* PluginInfoAboutView.swift */; };
|
||||
0CD4CAC628C980EB0046E1DC /* HistoryActionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD4CAC528C980EB0046E1DC /* HistoryActionsView.swift */; };
|
||||
0CD5E78928CD932B001BF684 /* DisabledAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD5E78828CD932B001BF684 /* DisabledAppearance.swift */; };
|
||||
0CD5F1FB299BEFBE00476DDB /* CustomScopeBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD5F1FA299BEFBE00476DDB /* CustomScopeBar.swift */; };
|
||||
|
|
@ -277,6 +279,8 @@
|
|||
0CC389512970AD900066D06F /* Action+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Action+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
0CC389522970AD900066D06F /* Action+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Action+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
0CC6E4D428A45BA000AF2BCC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
0CD4030929DA01B6008D9F03 /* PluginInfoMetaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginInfoMetaView.swift; sourceTree = "<group>"; };
|
||||
0CD4030B29DA0222008D9F03 /* PluginInfoAboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginInfoAboutView.swift; sourceTree = "<group>"; };
|
||||
0CD4CAC528C980EB0046E1DC /* HistoryActionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryActionsView.swift; sourceTree = "<group>"; };
|
||||
0CD5E78828CD932B001BF684 /* DisabledAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisabledAppearance.swift; sourceTree = "<group>"; };
|
||||
0CD5F1FA299BEFBE00476DDB /* CustomScopeBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomScopeBar.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -420,6 +424,7 @@
|
|||
0C3E00D4296F560800ECECB2 /* Plugin */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0CD4030829DA01A3008D9F03 /* Info */,
|
||||
0C44E2AA28D4E09B007711AE /* Buttons */,
|
||||
0C794B65289DAC9F00DD1CC8 /* Source */,
|
||||
0C0D50E6288DFF850035ECC8 /* PluginAggregateView.swift */,
|
||||
|
|
@ -665,6 +670,15 @@
|
|||
path = DataManagement;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0CD4030829DA01A3008D9F03 /* Info */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0CD4030929DA01B6008D9F03 /* PluginInfoMetaView.swift */,
|
||||
0CD4030B29DA0222008D9F03 /* PluginInfoAboutView.swift */,
|
||||
);
|
||||
path = Info;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
|
|
@ -794,8 +808,10 @@
|
|||
0CD5E78928CD932B001BF684 /* DisabledAppearance.swift in Sources */,
|
||||
0CA148DB288903F000DE2211 /* NavView.swift in Sources */,
|
||||
0CA3B23C28C2AA5600616D3A /* Bookmark+CoreDataClass.swift in Sources */,
|
||||
0CD4030A29DA01B6008D9F03 /* PluginInfoMetaView.swift in Sources */,
|
||||
0C750745289B003E004B3906 /* SourceRssParser+CoreDataProperties.swift in Sources */,
|
||||
0CA3FB2028B91D9500FA10A8 /* IndeterminateProgressView.swift in Sources */,
|
||||
0CD4030C29DA0222008D9F03 /* PluginInfoAboutView.swift in Sources */,
|
||||
0C50055A2992BA6A0064606A /* PluginTag+CoreDataClass.swift in Sources */,
|
||||
0C1A3E5229C8A7F500DA9730 /* SettingsModels.swift in Sources */,
|
||||
0CBC7705288DE7F40054BE44 /* PersistenceController.swift in Sources */,
|
||||
|
|
@ -1129,7 +1145,7 @@
|
|||
repositoryURL = "https://github.com/siteline/SwiftUI-Introspect/";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 0.2.2;
|
||||
minimumVersion = 0.2.3;
|
||||
};
|
||||
};
|
||||
0C4CFC442897030D00AD9FAD /* XCRemoteSwiftPackageReference "Regex" */ = {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ public extension Action {
|
|||
@NSManaged var name: String
|
||||
@NSManaged var deeplink: String?
|
||||
@NSManaged var version: Int16
|
||||
@NSManaged var about: String?
|
||||
@NSManaged var website: String?
|
||||
@NSManaged var requires: [String]
|
||||
@NSManaged var author: String
|
||||
@NSManaged var enabled: Bool
|
||||
|
|
|
|||
|
|
@ -15,9 +15,10 @@ public extension Source {
|
|||
}
|
||||
|
||||
@NSManaged var id: UUID
|
||||
@NSManaged var baseUrl: String?
|
||||
@NSManaged var about: String?
|
||||
@NSManaged var website: String?
|
||||
@NSManaged var dynamicWebsite: Bool
|
||||
@NSManaged var fallbackUrls: [String]?
|
||||
@NSManaged var dynamicBaseUrl: Bool
|
||||
@NSManaged var enabled: Bool
|
||||
@NSManaged var name: String
|
||||
@NSManaged var author: String
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21513" systemVersion="22D49" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="Action" representedClassName="Action" syncable="YES">
|
||||
<attribute name="about" optional="YES" attributeType="String"/>
|
||||
<attribute name="author" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="deeplink" optional="YES" attributeType="String"/>
|
||||
<attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
|
|
@ -9,6 +10,7 @@
|
|||
<attribute name="name" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="requires" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromData" customClassName="[String]"/>
|
||||
<attribute name="version" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="website" optional="YES" attributeType="String"/>
|
||||
<relationship name="tags" optional="YES" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="PluginTag" inverseName="parentAction" inverseEntity="PluginTag"/>
|
||||
</entity>
|
||||
<entity name="Bookmark" representedClassName="Bookmark" syncable="YES">
|
||||
|
|
@ -53,9 +55,9 @@
|
|||
<relationship name="parentSource" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Source" inverseName="tags" inverseEntity="Source"/>
|
||||
</entity>
|
||||
<entity name="Source" representedClassName="Source" syncable="YES">
|
||||
<attribute name="about" optional="YES" attributeType="String"/>
|
||||
<attribute name="author" attributeType="String" defaultValueString=""/>
|
||||
<attribute name="baseUrl" optional="YES" attributeType="String"/>
|
||||
<attribute name="dynamicBaseUrl" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="dynamicWebsite" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="enabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="fallbackUrls" optional="YES" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromData" customClassName="[String]"/>
|
||||
<attribute name="id" attributeType="UUID" usesScalarValueType="NO"/>
|
||||
|
|
@ -64,6 +66,7 @@
|
|||
<attribute name="preferredParser" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="trackers" optional="YES" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromData" customClassName="[String]"/>
|
||||
<attribute name="version" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
||||
<attribute name="website" optional="YES" attributeType="String"/>
|
||||
<relationship name="api" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceApi" inverseName="parentSource" inverseEntity="SourceApi"/>
|
||||
<relationship name="htmlParser" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceHtmlParser" inverseName="parentSource" inverseEntity="SourceHtmlParser"/>
|
||||
<relationship name="jsonParser" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="SourceJsonParser" inverseName="parentSource" inverseEntity="SourceJsonParser"/>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ public struct ActionJson: Codable, Hashable, PluginJson {
|
|||
public let name: String
|
||||
public let version: Int16
|
||||
let minVersion: String?
|
||||
let about: String?
|
||||
let website: String?
|
||||
let requires: [ActionRequirement]
|
||||
let deeplink: [DeeplinkActionJson]?
|
||||
public let author: String?
|
||||
|
|
@ -21,6 +23,8 @@ public struct ActionJson: Codable, Hashable, PluginJson {
|
|||
public init(name: String,
|
||||
version: Int16,
|
||||
minVersion: String?,
|
||||
about: String?,
|
||||
website: String?,
|
||||
requires: [ActionRequirement],
|
||||
deeplink: [DeeplinkActionJson]?,
|
||||
author: String?,
|
||||
|
|
@ -31,6 +35,8 @@ public struct ActionJson: Codable, Hashable, PluginJson {
|
|||
self.name = name
|
||||
self.version = version
|
||||
self.minVersion = minVersion
|
||||
self.about = about
|
||||
self.website = website
|
||||
self.requires = requires
|
||||
self.deeplink = deeplink
|
||||
self.author = author
|
||||
|
|
@ -44,6 +50,8 @@ public struct ActionJson: Codable, Hashable, PluginJson {
|
|||
name = try container.decode(String.self, forKey: .name)
|
||||
version = try container.decode(Int16.self, forKey: .version)
|
||||
minVersion = try container.decodeIfPresent(String.self, forKey: .minVersion)
|
||||
about = try container.decodeIfPresent(String.self, forKey: .about)
|
||||
website = try container.decodeIfPresent(String.self, forKey: .website)
|
||||
requires = try container.decode([ActionRequirement].self, forKey: .requires)
|
||||
author = try container.decodeIfPresent(String.self, forKey: .author)
|
||||
listId = nil
|
||||
|
|
|
|||
|
|
@ -16,9 +16,10 @@ public struct SourceJson: Codable, Hashable, Sendable, PluginJson {
|
|||
public let name: String
|
||||
public let version: Int16
|
||||
let minVersion: String?
|
||||
let baseUrl: String?
|
||||
let about: String?
|
||||
let website: String?
|
||||
let dynamicWebsite: Bool?
|
||||
let fallbackUrls: [String]?
|
||||
let dynamicBaseUrl: Bool?
|
||||
let trackers: [String]?
|
||||
let api: SourceApiJson?
|
||||
let jsonParser: SourceJsonParserJson?
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ public protocol Plugin: ObservableObject, NSManagedObject {
|
|||
var name: String { get set }
|
||||
var version: Int16 { get set }
|
||||
var author: String { get set }
|
||||
var about: String? { get set }
|
||||
var website: String? { get set }
|
||||
var enabled: Bool { get set }
|
||||
var tags: NSOrderedSet? { get set }
|
||||
func getTags() -> [PluginTagJson]
|
||||
|
|
|
|||
|
|
@ -124,9 +124,10 @@ public class PluginManager: ObservableObject {
|
|||
name: inputJson.name,
|
||||
version: inputJson.version,
|
||||
minVersion: inputJson.minVersion,
|
||||
baseUrl: inputJson.baseUrl,
|
||||
about: inputJson.about,
|
||||
website: inputJson.website,
|
||||
dynamicWebsite: inputJson.dynamicWebsite,
|
||||
fallbackUrls: inputJson.fallbackUrls,
|
||||
dynamicBaseUrl: inputJson.dynamicBaseUrl,
|
||||
trackers: inputJson.trackers,
|
||||
api: inputJson.api,
|
||||
jsonParser: inputJson.jsonParser,
|
||||
|
|
@ -154,6 +155,8 @@ public class PluginManager: ObservableObject {
|
|||
name: inputJson.name,
|
||||
version: inputJson.version,
|
||||
minVersion: inputJson.minVersion,
|
||||
about: inputJson.about,
|
||||
website: inputJson.website,
|
||||
requires: inputJson.requires,
|
||||
deeplink: filteredDeeplinks,
|
||||
author: pluginList.author,
|
||||
|
|
@ -409,6 +412,8 @@ public class PluginManager: ObservableObject {
|
|||
newAction.id = UUID()
|
||||
newAction.name = actionJson.name
|
||||
newAction.version = actionJson.version
|
||||
newAction.website = actionJson.website
|
||||
newAction.about = actionJson.about
|
||||
newAction.author = actionJson.author ?? "Unknown"
|
||||
newAction.listId = actionJson.listId
|
||||
newAction.requires = actionJson.requires.map(\.rawValue)
|
||||
|
|
@ -448,9 +453,9 @@ public class PluginManager: ObservableObject {
|
|||
let backgroundContext = PersistenceController.shared.backgroundContext
|
||||
|
||||
// If there's no base URL and it isn't dynamic, return before any transactions occur
|
||||
let dynamicBaseUrl = sourceJson.dynamicBaseUrl ?? false
|
||||
if !dynamicBaseUrl, sourceJson.baseUrl == nil {
|
||||
await logManager?.error("Not adding this source because base URL parameters are malformed. Please contact the source dev.")
|
||||
let dynamicWebsite = sourceJson.dynamicWebsite ?? false
|
||||
if !dynamicWebsite, sourceJson.website == nil {
|
||||
await logManager?.error("Not adding this source because website parameters are malformed. Please contact the source dev.")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -472,9 +477,10 @@ public class PluginManager: ObservableObject {
|
|||
newSource.id = UUID()
|
||||
newSource.name = sourceJson.name
|
||||
newSource.version = sourceJson.version
|
||||
newSource.dynamicBaseUrl = dynamicBaseUrl
|
||||
newSource.baseUrl = sourceJson.baseUrl
|
||||
newSource.fallbackUrls = dynamicBaseUrl ? nil : sourceJson.fallbackUrls
|
||||
newSource.about = sourceJson.about
|
||||
newSource.website = sourceJson.website
|
||||
newSource.dynamicWebsite = dynamicWebsite
|
||||
newSource.fallbackUrls = dynamicWebsite ? nil : sourceJson.fallbackUrls
|
||||
newSource.author = sourceJson.author ?? "Unknown"
|
||||
newSource.listId = sourceJson.listId
|
||||
newSource.trackers = sourceJson.trackers
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ class ScrapingViewModel: ObservableObject {
|
|||
}
|
||||
|
||||
func executeParser(source: Source) async -> SearchRequestResult? {
|
||||
guard let baseUrl = source.baseUrl else {
|
||||
guard let website = source.website else {
|
||||
await logManager?.error("Scraping: The base URL could not be found for source \(source.name)")
|
||||
|
||||
return nil
|
||||
|
|
@ -167,7 +167,7 @@ class ScrapingViewModel: ObservableObject {
|
|||
}
|
||||
|
||||
let data = await handleUrls(
|
||||
baseUrl: baseUrl,
|
||||
website: website,
|
||||
replacedSearchUrl: replacedSearchUrl,
|
||||
fallbackUrls: source.fallbackUrls,
|
||||
sourceName: source.name
|
||||
|
|
@ -176,7 +176,7 @@ class ScrapingViewModel: ObservableObject {
|
|||
if let data,
|
||||
let html = String(data: data, encoding: .utf8)
|
||||
{
|
||||
return await scrapeHtml(source: source, baseUrl: baseUrl, html: html)
|
||||
return await scrapeHtml(source: source, website: website, html: html)
|
||||
}
|
||||
}
|
||||
case .rss:
|
||||
|
|
@ -194,7 +194,7 @@ class ScrapingViewModel: ObservableObject {
|
|||
)
|
||||
} else {
|
||||
data = await handleUrls(
|
||||
baseUrl: baseUrl,
|
||||
website: website,
|
||||
replacedSearchUrl: replacedSearchUrl,
|
||||
fallbackUrls: source.fallbackUrls,
|
||||
sourceName: source.name
|
||||
|
|
@ -220,7 +220,7 @@ class ScrapingViewModel: ObservableObject {
|
|||
replacement: "{clientId}",
|
||||
searchUrl: replacedSearchUrl,
|
||||
apiUrl: sourceApi.apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
website: website,
|
||||
sourceName: source.name)
|
||||
{
|
||||
replacedSearchUrl = newSearchUrl
|
||||
|
|
@ -233,7 +233,7 @@ class ScrapingViewModel: ObservableObject {
|
|||
replacement: "{secret}",
|
||||
searchUrl: replacedSearchUrl,
|
||||
apiUrl: sourceApi.apiUrl,
|
||||
baseUrl: baseUrl,
|
||||
website: website,
|
||||
sourceName: source.name)
|
||||
{
|
||||
replacedSearchUrl = newSearchUrl
|
||||
|
|
@ -241,9 +241,9 @@ class ScrapingViewModel: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
let passedUrl = source.api?.apiUrl ?? baseUrl
|
||||
let passedUrl = source.api?.apiUrl ?? website
|
||||
let data = await handleUrls(
|
||||
baseUrl: passedUrl,
|
||||
website: passedUrl,
|
||||
replacedSearchUrl: replacedSearchUrl,
|
||||
fallbackUrls: source.fallbackUrls,
|
||||
sourceName: source.name
|
||||
|
|
@ -261,8 +261,8 @@ class ScrapingViewModel: ObservableObject {
|
|||
}
|
||||
|
||||
// Checks the base URL for any website data then iterates through the fallback URLs
|
||||
func handleUrls(baseUrl: String, replacedSearchUrl: String?, fallbackUrls: [String]?, sourceName: String) async -> Data? {
|
||||
let fetchUrl = baseUrl + (replacedSearchUrl.map { $0 } ?? "")
|
||||
func handleUrls(website: String, replacedSearchUrl: String?, fallbackUrls: [String]?, sourceName: String) async -> Data? {
|
||||
let fetchUrl = website + (replacedSearchUrl.map { $0 } ?? "")
|
||||
if let data = await fetchWebsiteData(urlString: fetchUrl, sourceName: sourceName) {
|
||||
return data
|
||||
}
|
||||
|
|
@ -283,7 +283,7 @@ class ScrapingViewModel: ObservableObject {
|
|||
replacement: String,
|
||||
searchUrl: String,
|
||||
apiUrl: String?,
|
||||
baseUrl: String,
|
||||
website: String,
|
||||
sourceName: String) async -> String?
|
||||
{
|
||||
// Is the credential expired
|
||||
|
|
@ -302,7 +302,7 @@ class ScrapingViewModel: ObservableObject {
|
|||
credential.value == nil || isExpired,
|
||||
let credentialUrl = credential.urlString,
|
||||
let newValue = await fetchApiCredential(
|
||||
urlString: (apiUrl ?? baseUrl) + credentialUrl,
|
||||
urlString: (apiUrl ?? website) + credentialUrl,
|
||||
credential: credential,
|
||||
sourceName: sourceName
|
||||
)
|
||||
|
|
@ -733,7 +733,7 @@ class ScrapingViewModel: ObservableObject {
|
|||
}
|
||||
|
||||
// HTML scraper
|
||||
public func scrapeHtml(source: Source, baseUrl: String, html: String) async -> SearchRequestResult? {
|
||||
public func scrapeHtml(source: Source, website: String, html: String) async -> SearchRequestResult? {
|
||||
guard let htmlParser = source.htmlParser else {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -783,7 +783,7 @@ class ScrapingViewModel: ObservableObject {
|
|||
continue
|
||||
}
|
||||
|
||||
let replacedMagnetUrl = externalMagnetUrl.starts(with: "/") ? baseUrl + externalMagnetUrl : externalMagnetUrl
|
||||
let replacedMagnetUrl = externalMagnetUrl.starts(with: "/") ? website + externalMagnetUrl : externalMagnetUrl
|
||||
guard
|
||||
let data = await fetchWebsiteData(urlString: replacedMagnetUrl, sourceName: source.name),
|
||||
let magnetHtml = String(data: data, encoding: .utf8)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// PluginInfoAboutView.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 4/2/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct PluginInfoAboutView<P: Plugin>: View {
|
||||
@ObservedObject var selectedPlugin: P
|
||||
|
||||
var body: some View {
|
||||
Section(header: InlineHeader("Description")) {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
if let pluginAbout = selectedPlugin.about {
|
||||
if pluginAbout.last == "\n" {
|
||||
Text(pluginAbout.dropLast())
|
||||
} else {
|
||||
Text(pluginAbout)
|
||||
}
|
||||
}
|
||||
|
||||
if let pluginWebsite = selectedPlugin.website {
|
||||
Link("Website", destination: URL(string: pluginWebsite) ?? URL(string: "https://kingbri.dev/ferrite")!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// PluginInfoMetaView.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 4/2/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct PluginInfoMetaView<P: Plugin>: View {
|
||||
@ObservedObject var selectedPlugin: P
|
||||
|
||||
@FetchRequest(
|
||||
entity: PluginList.entity(),
|
||||
sortDescriptors: []
|
||||
) var pluginLists: FetchedResults<PluginList>
|
||||
|
||||
var body: some View {
|
||||
Section(header: InlineHeader("Metadata")) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,48 +12,14 @@ struct PluginInfoView<P: Plugin>: View {
|
|||
|
||||
@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)
|
||||
PluginInfoMetaView(selectedPlugin: selectedPlugin)
|
||||
|
||||
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 selectedPlugin.about != nil || selectedPlugin.website != nil {
|
||||
PluginInfoAboutView(selectedPlugin: selectedPlugin)
|
||||
}
|
||||
|
||||
if let selectedSource = selectedPlugin as? Source {
|
||||
|
|
|
|||
|
|
@ -10,18 +10,18 @@ import SwiftUI
|
|||
struct SourceSettingsBaseUrlView: View {
|
||||
@ObservedObject var selectedSource: Source
|
||||
|
||||
@State private var tempBaseUrl: String = ""
|
||||
@State private var tempSite: 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
|
||||
TextField("https://...", text: $tempSite, onEditingChanged: { isFocused in
|
||||
if !isFocused {
|
||||
if tempBaseUrl.last == "/" {
|
||||
selectedSource.baseUrl = String(tempBaseUrl.dropLast())
|
||||
if tempSite.last == "/" {
|
||||
selectedSource.website = String(tempSite.dropLast())
|
||||
} else {
|
||||
selectedSource.baseUrl = tempBaseUrl
|
||||
selectedSource.website = tempSite
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -29,7 +29,7 @@ struct SourceSettingsBaseUrlView: View {
|
|||
.autocorrectionDisabled(true)
|
||||
.autocapitalization(.none)
|
||||
.onAppear {
|
||||
tempBaseUrl = selectedSource.baseUrl ?? ""
|
||||
tempSite = selectedSource.website ?? ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ struct SourceSettingsView: View {
|
|||
@ObservedObject var selectedSource: Source
|
||||
|
||||
var body: some View {
|
||||
if selectedSource.dynamicBaseUrl {
|
||||
if selectedSource.dynamicWebsite {
|
||||
SourceSettingsBaseUrlView(selectedSource: selectedSource)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue