Ferrite: Add updater
Updates are sent via an alert on starting the app. This can be disabled in the settings menu. A full version struct has been completed for flexible comparisons. Version history can also be viewed in settings in case a user wants to download an earlier version of the app. Updates track Github releases. Signed-off-by: kingbri <bdashore3@gmail.com>
This commit is contained in:
parent
1bf64a8934
commit
664c57b751
11 changed files with 236 additions and 43 deletions
|
|
@ -9,6 +9,7 @@
|
|||
/* Begin PBXBuildFile section */
|
||||
0C0D50E5288DFE7F0035ECC8 /* SourceModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0D50E4288DFE7F0035ECC8 /* SourceModels.swift */; };
|
||||
0C0D50E7288DFF850035ECC8 /* SourcesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C0D50E6288DFF850035ECC8 /* SourcesView.swift */; };
|
||||
0C10848B28BD9A38008F0BA6 /* SettingsAppVersionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C10848A28BD9A38008F0BA6 /* SettingsAppVersionView.swift */; };
|
||||
0C31133C28B1ABFA004DCB0D /* SourceJsonParser+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C31133A28B1ABFA004DCB0D /* SourceJsonParser+CoreDataClass.swift */; };
|
||||
0C31133D28B1ABFA004DCB0D /* SourceJsonParser+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C31133B28B1ABFA004DCB0D /* SourceJsonParser+CoreDataProperties.swift */; };
|
||||
0C32FB532890D19D002BD219 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C32FB522890D19D002BD219 /* AboutView.swift */; };
|
||||
|
|
@ -21,11 +22,14 @@
|
|||
0C60B1EF28A1A00000E3FD7E /* SearchProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C60B1EE28A1A00000E3FD7E /* SearchProgressView.swift */; };
|
||||
0C64A4B4288903680079976D /* Base32 in Frameworks */ = {isa = PBXBuildFile; productRef = 0C64A4B3288903680079976D /* Base32 */; };
|
||||
0C64A4B7288903880079976D /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0C64A4B6288903880079976D /* KeychainSwift */; };
|
||||
0C68135028BC1A2D00FAD890 /* GithubWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C68134F28BC1A2D00FAD890 /* GithubWrapper.swift */; };
|
||||
0C68135228BC1A7C00FAD890 /* GithubModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C68135128BC1A7C00FAD890 /* GithubModels.swift */; };
|
||||
0C733287289C4C820058D1FE /* SourceSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C733286289C4C820058D1FE /* SourceSettingsView.swift */; };
|
||||
0C7376F028A97D1400D60918 /* SwiftUIX in Frameworks */ = {isa = PBXBuildFile; productRef = 0C7376EF28A97D1400D60918 /* SwiftUIX */; };
|
||||
0C7506D728B1AC9A008BEE38 /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 0C7506D628B1AC9A008BEE38 /* SwiftyJSON */; };
|
||||
0C750744289B003E004B3906 /* SourceRssParser+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C750742289B003E004B3906 /* SourceRssParser+CoreDataClass.swift */; };
|
||||
0C750745289B003E004B3906 /* SourceRssParser+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C750743289B003E004B3906 /* SourceRssParser+CoreDataProperties.swift */; };
|
||||
0C78041D28BFB3EA001E8CA3 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C78041C28BFB3EA001E8CA3 /* String.swift */; };
|
||||
0C794B67289DACB600DD1CC8 /* SourceUpdateButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C794B66289DACB600DD1CC8 /* SourceUpdateButtonView.swift */; };
|
||||
0C794B69289DACC800DD1CC8 /* InstalledSourceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C794B68289DACC800DD1CC8 /* InstalledSourceView.swift */; };
|
||||
0C794B6B289DACF100DD1CC8 /* SourceCatalogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C794B6A289DACF100DD1CC8 /* SourceCatalogView.swift */; };
|
||||
|
|
@ -76,6 +80,7 @@
|
|||
/* Begin PBXFileReference section */
|
||||
0C0D50E4288DFE7F0035ECC8 /* SourceModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceModels.swift; sourceTree = "<group>"; };
|
||||
0C0D50E6288DFF850035ECC8 /* SourcesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourcesView.swift; sourceTree = "<group>"; };
|
||||
0C10848A28BD9A38008F0BA6 /* SettingsAppVersionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAppVersionView.swift; sourceTree = "<group>"; };
|
||||
0C31133A28B1ABFA004DCB0D /* SourceJsonParser+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceJsonParser+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
0C31133B28B1ABFA004DCB0D /* SourceJsonParser+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceJsonParser+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
0C32FB522890D19D002BD219 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -85,9 +90,12 @@
|
|||
0C4CFC4828970C8B00AD9FAD /* SourceComplexQuery+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceComplexQuery+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
0C57D4CB289032ED008534E8 /* SearchResultRDView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultRDView.swift; sourceTree = "<group>"; };
|
||||
0C60B1EE28A1A00000E3FD7E /* SearchProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchProgressView.swift; sourceTree = "<group>"; };
|
||||
0C68134F28BC1A2D00FAD890 /* GithubWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GithubWrapper.swift; sourceTree = "<group>"; };
|
||||
0C68135128BC1A7C00FAD890 /* GithubModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GithubModels.swift; sourceTree = "<group>"; };
|
||||
0C733286289C4C820058D1FE /* SourceSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceSettingsView.swift; sourceTree = "<group>"; };
|
||||
0C750742289B003E004B3906 /* SourceRssParser+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceRssParser+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
0C750743289B003E004B3906 /* SourceRssParser+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceRssParser+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
0C78041C28BFB3EA001E8CA3 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
|
||||
0C794B66289DACB600DD1CC8 /* SourceUpdateButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceUpdateButtonView.swift; sourceTree = "<group>"; };
|
||||
0C794B68289DACC800DD1CC8 /* InstalledSourceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstalledSourceView.swift; sourceTree = "<group>"; };
|
||||
0C794B6A289DACF100DD1CC8 /* SourceCatalogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SourceCatalogView.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -180,6 +188,7 @@
|
|||
0CA148C4288903F000DE2211 /* RealDebridModels.swift */,
|
||||
0C0D50E4288DFE7F0035ECC8 /* SourceModels.swift */,
|
||||
0C95D8D928A55BB6005E22B3 /* SettingsModels.swift */,
|
||||
0C68135128BC1A7C00FAD890 /* GithubModels.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -201,6 +210,7 @@
|
|||
0CA05456288EE58200850554 /* SettingsSourceListView.swift */,
|
||||
0CA0545A288EEA4E00850554 /* SourceListEditorView.swift */,
|
||||
0C95D8D728A55B03005E22B3 /* DefaultActionsPickerViews.swift */,
|
||||
0C10848A28BD9A38008F0BA6 /* SettingsAppVersionView.swift */,
|
||||
);
|
||||
path = SettingsViews;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -251,6 +261,7 @@
|
|||
0CA148CB288903F000DE2211 /* Task.swift */,
|
||||
0C32FB542890D1BF002BD219 /* UIApplication.swift */,
|
||||
0C7D11FD28AA03FE00ED92DB /* View.swift */,
|
||||
0C78041C28BFB3EA001E8CA3 /* String.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -301,6 +312,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
0CA148D0288903F000DE2211 /* RealDebridWrapper.swift */,
|
||||
0C68134F28BC1A2D00FAD890 /* GithubWrapper.swift */,
|
||||
);
|
||||
path = API;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -444,9 +456,11 @@
|
|||
0CA148DD288903F000DE2211 /* ScrapingViewModel.swift in Sources */,
|
||||
0CA148D8288903F000DE2211 /* MagnetChoiceView.swift in Sources */,
|
||||
0C84F4862895BFED0074B7C9 /* SourceList+CoreDataClass.swift in Sources */,
|
||||
0C68135028BC1A2D00FAD890 /* GithubWrapper.swift in Sources */,
|
||||
0C95D8DA28A55BB6005E22B3 /* SettingsModels.swift in Sources */,
|
||||
0CA148E3288903F000DE2211 /* Task.swift in Sources */,
|
||||
0CA148E7288903F000DE2211 /* ToastViewModel.swift in Sources */,
|
||||
0C68135228BC1A7C00FAD890 /* GithubModels.swift in Sources */,
|
||||
0CFEFCFD288A006200B3F490 /* GroupBoxStyle.swift in Sources */,
|
||||
0C79DC072899AF3C003F1C5A /* SourceSeedLeech+CoreDataClass.swift in Sources */,
|
||||
0C794B67289DACB600DD1CC8 /* SourceUpdateButtonView.swift in Sources */,
|
||||
|
|
@ -458,7 +472,9 @@
|
|||
0CA05459288EE9E600850554 /* SourceManager.swift in Sources */,
|
||||
0C84F4772895BE680074B7C9 /* FerriteDB.xcdatamodeld in Sources */,
|
||||
0C733287289C4C820058D1FE /* SourceSettingsView.swift in Sources */,
|
||||
0C10848B28BD9A38008F0BA6 /* SettingsAppVersionView.swift in Sources */,
|
||||
0CA05457288EE58200850554 /* SettingsSourceListView.swift in Sources */,
|
||||
0C78041D28BFB3EA001E8CA3 /* String.swift in Sources */,
|
||||
0C31133C28B1ABFA004DCB0D /* SourceJsonParser+CoreDataClass.swift in Sources */,
|
||||
0CA148DE288903F000DE2211 /* RealDebridModels.swift in Sources */,
|
||||
0CBC76FF288DAAD00054BE44 /* NavigationViewModel.swift in Sources */,
|
||||
|
|
|
|||
28
Ferrite/API/GithubWrapper.swift
Normal file
28
Ferrite/API/GithubWrapper.swift
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
//
|
||||
// GithubWrapper.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 8/28/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class Github {
|
||||
public func fetchLatestRelease() async throws -> GithubRelease? {
|
||||
let url = URL(string: "https://api.github.com/repos/bdashore3/Ferrite/releases/latest")!
|
||||
|
||||
let (data, _) = try await URLSession.shared.data(from: url)
|
||||
|
||||
let rawResponse = try JSONDecoder().decode(GithubRelease.self, from: data)
|
||||
return rawResponse
|
||||
}
|
||||
|
||||
public func fetchReleases() async throws -> [GithubRelease]? {
|
||||
let url = URL(string: "https://api.github.com/repos/bdashore3/Ferrite/releases")!
|
||||
|
||||
let (data, _) = try await URLSession.shared.data(from: url)
|
||||
|
||||
let rawResponse = try JSONDecoder().decode([GithubRelease].self, from: data)
|
||||
return rawResponse
|
||||
}
|
||||
}
|
||||
42
Ferrite/Extensions/String.swift
Normal file
42
Ferrite/Extensions/String.swift
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// String.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 8/31/22.
|
||||
//
|
||||
// From https://stackoverflow.com/a/59307884
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension String {
|
||||
private func compare(toVersion targetVersion: String) -> ComparisonResult {
|
||||
let versionDelimiter = "."
|
||||
var result: ComparisonResult = .orderedSame
|
||||
var versionComponents = components(separatedBy: versionDelimiter)
|
||||
var targetComponents = targetVersion.components(separatedBy: versionDelimiter)
|
||||
|
||||
while versionComponents.count < targetComponents.count {
|
||||
versionComponents.append("0")
|
||||
}
|
||||
|
||||
while targetComponents.count < versionComponents.count {
|
||||
targetComponents.append("0")
|
||||
}
|
||||
|
||||
for (version, target) in zip(versionComponents, targetComponents) {
|
||||
result = version.compare(target, options: .numeric)
|
||||
if result != .orderedSame {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
static func ==(lhs: String, rhs: String) -> Bool { lhs.compare(toVersion: rhs) == .orderedSame }
|
||||
static func <(lhs: String, rhs: String) -> Bool { lhs.compare(toVersion: rhs) == .orderedAscending }
|
||||
static func <=(lhs: String, rhs: String) -> Bool { lhs.compare(toVersion: rhs) != .orderedDescending }
|
||||
static func >(lhs: String, rhs: String) -> Bool { lhs.compare(toVersion: rhs) == .orderedDescending }
|
||||
static func >=(lhs: String, rhs: String) -> Bool { lhs.compare(toVersion: rhs) != .orderedAscending }
|
||||
}
|
||||
18
Ferrite/Models/GithubModels.swift
Normal file
18
Ferrite/Models/GithubModels.swift
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// GithubModels.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 8/28/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct GithubRelease: Codable, Hashable {
|
||||
let htmlUrl: String
|
||||
let tagName: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case htmlUrl = "html_url"
|
||||
case tagName = "tag_name"
|
||||
}
|
||||
}
|
||||
|
|
@ -26,6 +26,7 @@ public class DebridManager: ObservableObject {
|
|||
UserDefaults.standard.set(realDebridEnabled, forKey: "RealDebrid.Enabled")
|
||||
}
|
||||
}
|
||||
|
||||
@Published var realDebridAuthProcessing: Bool = false
|
||||
@Published var realDebridAuthUrl: String = ""
|
||||
|
||||
|
|
|
|||
|
|
@ -897,7 +897,7 @@ class ScrapingViewModel: ObservableObject {
|
|||
responseArray.append("client ID")
|
||||
}
|
||||
|
||||
if clientIdReset && clientSecretReset {
|
||||
if clientIdReset, clientSecretReset {
|
||||
responseArray.append("and")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,26 +59,7 @@ public class SourceManager: ObservableObject {
|
|||
return true
|
||||
}
|
||||
|
||||
var splitCurrentVersion = UIApplication.shared.appVersion
|
||||
.split(separator: ".")
|
||||
.map { Int($0) ?? 0 }
|
||||
|
||||
if splitCurrentVersion.count < 3 {
|
||||
splitCurrentVersion += [Int](repeating: 0, count: 3 - splitCurrentVersion.count)
|
||||
}
|
||||
|
||||
var splitMinVersion = minVersion
|
||||
.split(separator: ".")
|
||||
.map { Int($0) ?? 0 }
|
||||
|
||||
if splitMinVersion.count < 3 {
|
||||
splitMinVersion += [Int](repeating: 0, count: 3 - splitMinVersion.count)
|
||||
}
|
||||
|
||||
let combined = zip(splitCurrentVersion, splitMinVersion)
|
||||
return combined.allSatisfy({ part, minPart in
|
||||
part >= minPart
|
||||
})
|
||||
return UIApplication.shared.appVersion >= minVersion
|
||||
}
|
||||
|
||||
// Fetches sources using the background context
|
||||
|
|
|
|||
|
|
@ -13,6 +13,13 @@ struct MainView: View {
|
|||
@EnvironmentObject var toastModel: ToastViewModel
|
||||
@EnvironmentObject var debridManager: DebridManager
|
||||
|
||||
@AppStorage("Updates.AutomaticNotifs") var autoUpdateNotifs = true
|
||||
|
||||
@State private var showUpdateAlert = false
|
||||
@State private var releaseVersionString: String = ""
|
||||
@State private var releaseUrlString: String = ""
|
||||
@State private var viewTask: Task<Void, Never>?
|
||||
|
||||
var body: some View {
|
||||
TabView(selection: $navModel.selectedTab) {
|
||||
ContentView()
|
||||
|
|
@ -33,6 +40,45 @@ struct MainView: View {
|
|||
}
|
||||
.tag(ViewTab.settings)
|
||||
}
|
||||
.alert(isPresented: $showUpdateAlert) {
|
||||
Alert(
|
||||
title: Text("Update available"),
|
||||
message: Text("Ferrite \(releaseVersionString) can be downloaded. \n\n This alert can be disabled in Settings."),
|
||||
primaryButton: .default(Text("Download")) {
|
||||
guard let releaseUrl = URL(string: releaseUrlString) else {
|
||||
return
|
||||
}
|
||||
|
||||
UIApplication.shared.open(releaseUrl)
|
||||
},
|
||||
secondaryButton: .cancel()
|
||||
)
|
||||
}
|
||||
.onAppear {
|
||||
if autoUpdateNotifs {
|
||||
viewTask = Task {
|
||||
do {
|
||||
guard let latestRelease = try await Github().fetchLatestRelease() else {
|
||||
toastModel.updateToastDescription("Github error: No releases found")
|
||||
return
|
||||
}
|
||||
|
||||
let releaseVersion = String(latestRelease.tagName.dropFirst())
|
||||
if releaseVersion > UIApplication.shared.appVersion {
|
||||
print("Greater")
|
||||
releaseVersionString = latestRelease.tagName
|
||||
releaseUrlString = latestRelease.htmlUrl
|
||||
showUpdateAlert.toggle()
|
||||
}
|
||||
} catch {
|
||||
toastModel.updateToastDescription("Github error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
viewTask?.cancel()
|
||||
}
|
||||
.overlay {
|
||||
VStack {
|
||||
Spacer()
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ struct SettingsView: View {
|
|||
|
||||
let backgroundContext = PersistenceController.shared.backgroundContext
|
||||
|
||||
@AppStorage("Updates.AutomaticNotifs") var autoUpdateNotifs = true
|
||||
|
||||
@AppStorage("Actions.DefaultDebrid") var defaultDebridAction: DefaultDebridActionType = .none
|
||||
@AppStorage("Actions.DefaultMagnet") var defaultMagnetAction: DefaultMagnetActionType = .none
|
||||
|
||||
|
|
@ -92,9 +94,15 @@ struct SettingsView: View {
|
|||
)
|
||||
}
|
||||
|
||||
Section(header: Text("Updates")) {
|
||||
Toggle(isOn: $autoUpdateNotifs) {
|
||||
Text("Show update alerts")
|
||||
}
|
||||
NavigationLink("Version history", destination: SettingsAppVersionView())
|
||||
}
|
||||
|
||||
Section {
|
||||
ListRowLinkView(text: "Report issues", link: "https://github.com/bdashore3/Ferrite/issues")
|
||||
|
||||
NavigationLink("About", destination: AboutView())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
50
Ferrite/Views/SettingsViews/SettingsAppVersionView.swift
Normal file
50
Ferrite/Views/SettingsViews/SettingsAppVersionView.swift
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// SettingsAppVersionView.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 8/29/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SettingsAppVersionView: View {
|
||||
@EnvironmentObject var toastModel: ToastViewModel
|
||||
|
||||
@State private var viewTask: Task<Void, Never>?
|
||||
@State private var releases: [GithubRelease] = []
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section(header: Text("GitHub links")) {
|
||||
ForEach(releases, id: \.self) { release in
|
||||
ListRowLinkView(text: release.tagName, link: release.htmlUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
viewTask = Task {
|
||||
do {
|
||||
if let fetchedReleases = try await Github().fetchReleases() {
|
||||
releases = fetchedReleases
|
||||
} else {
|
||||
toastModel.updateToastDescription("Github error: No releases found")
|
||||
}
|
||||
} catch {
|
||||
toastModel.updateToastDescription("Github error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
viewTask?.cancel()
|
||||
}
|
||||
.listStyle(.insetGrouped)
|
||||
.navigationTitle("Version history")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
}
|
||||
|
||||
struct SettingsAppVersionView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
SettingsAppVersionView()
|
||||
}
|
||||
}
|
||||
|
|
@ -22,31 +22,33 @@ struct SettingsSourceListView: View {
|
|||
|
||||
var body: some View {
|
||||
List {
|
||||
ForEach(sourceLists, id: \.self) { sourceList in
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
Text(sourceList.name)
|
||||
Section(header: Text("List information")) {
|
||||
ForEach(sourceLists, id: \.self) { sourceList in
|
||||
VStack(alignment: .leading, spacing: 5) {
|
||||
Text(sourceList.name)
|
||||
|
||||
Text(sourceList.author)
|
||||
.foregroundColor(.gray)
|
||||
Text(sourceList.author)
|
||||
.foregroundColor(.gray)
|
||||
|
||||
Text("ID: \(sourceList.id)")
|
||||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
.contextMenu {
|
||||
Button {
|
||||
navModel.selectedSourceList = sourceList
|
||||
presentSourceSheet.toggle()
|
||||
} label: {
|
||||
Text("Edit")
|
||||
Image(systemName: "pencil")
|
||||
Text("ID: \(sourceList.id)")
|
||||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
.contextMenu {
|
||||
Button {
|
||||
navModel.selectedSourceList = sourceList
|
||||
presentSourceSheet.toggle()
|
||||
} label: {
|
||||
Text("Edit")
|
||||
Image(systemName: "pencil")
|
||||
}
|
||||
|
||||
Button {
|
||||
PersistenceController.shared.delete(sourceList, context: backgroundContext)
|
||||
} label: {
|
||||
Text("Remove")
|
||||
Image(systemName: "trash")
|
||||
Button {
|
||||
PersistenceController.shared.delete(sourceList, context: backgroundContext)
|
||||
} label: {
|
||||
Text("Remove")
|
||||
Image(systemName: "trash")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -61,6 +63,7 @@ struct SettingsSourceListView: View {
|
|||
}
|
||||
}
|
||||
.navigationTitle("Source lists")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button {
|
||||
|
|
|
|||
Loading…
Reference in a new issue