[ADD] Load and show trusted sources with option to add them to the app

This commit is contained in:
Fabian Thies
2023-01-31 22:32:11 +01:00
parent 77465cebd0
commit e5369524ce

View File

@@ -25,7 +25,10 @@ struct SourcesView: View {
NSSortDescriptor(keyPath: \Source.identifier, ascending: true) NSSortDescriptor(keyPath: \Source.identifier, ascending: true)
]) ])
var installedSources: FetchedResults<Source> var installedSources: FetchedResults<Source>
@State var trustedSources: [Source] = []
@State private var isLoadingTrustedSources: Bool = false
@State private var sourcesFetchContext: NSManagedObjectContext?
@State var isShowingAddSourceAlert = false @State var isShowingAddSourceAlert = false
@State var sourceToConfirm: FetchedSource? @State var sourceToConfirm: FetchedSource?
@@ -52,7 +55,7 @@ struct SourcesView: View {
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
.padding() .padding()
.tintedBackground(.accentColor) .tintedBackground(.accentColor)
.clipShape(RoundedRectangle(cornerRadius: 30, style: .circular)) .clipShape(RoundedRectangle(cornerRadius: 24, style: .circular))
.if(source.identifier != Source.altStoreIdentifier) { view in .if(source.identifier != Source.altStoreIdentifier) { view in
view.contextMenu(ContextMenu(menuItems: { view.contextMenu(ContextMenu(menuItems: {
SwiftUI.Button { SwiftUI.Button {
@@ -66,16 +69,58 @@ struct SourcesView: View {
} }
// Trusted Sources // Trusted Sources
LazyVStack(alignment: .leading) { LazyVStack(alignment: .leading, spacing: 16) {
Text(L10n.SourcesView.trustedSources) HStack(spacing: 4) {
.font(.title3) Text(L10n.SourcesView.trustedSources)
.bold() .font(.title3)
.bold()
Image(systemSymbol: .shieldLefthalfFill)
.foregroundColor(.accentColor)
}
Text(L10n.SourcesView.reviewedText) Text(L10n.SourcesView.reviewedText)
.font(.callout) .font(.callout)
.foregroundColor(.secondary) .foregroundColor(.secondary)
if self.isLoadingTrustedSources {
ProgressView()
.progressViewStyle(CircularProgressViewStyle())
.frame(maxWidth: .infinity)
} else {
ForEach(self.trustedSources, id: \.sourceURL) { source in
HStack {
VStack(alignment: .leading) {
Text(source.name)
.bold()
Text(source.sourceURL.absoluteString)
.font(.callout)
.foregroundColor(.secondary)
}
Spacer()
if self.installedSources.contains(where: { $0.sourceURL == source.sourceURL }) {
Image(systemSymbol: .checkmarkCircle)
.foregroundColor(.accentColor)
} else {
SwiftUI.Button {
self.fetchSource(with: source.sourceURL.absoluteString)
} label: {
Text("ADD")
.bold()
}
.buttonStyle(PillButtonStyle(tintColor: Asset.accentColor.color, progress: nil))
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
.tintedBackground(.accentColor)
.clipShape(RoundedRectangle(cornerRadius: 24, style: .circular))
}
}
} }
} }
.padding() .padding()
@@ -111,6 +156,7 @@ struct SourcesView: View {
} }
} }
} }
.onAppear(perform: self.fetchTrustedSources)
} }
@@ -120,12 +166,14 @@ struct SourcesView: View {
guard let url = URL(string: urlText) else { guard let url = URL(string: urlText) else {
return return
} }
AppManager.shared.fetchSource(sourceURL: url) { result in let context = DatabaseManager.shared.persistentContainer.newBackgroundSavingViewContext()
AppManager.shared.fetchSource(sourceURL: url, managedObjectContext: context) { result in
switch result { switch result {
case let .success(source): case let .success(source):
self.sourceToConfirm = FetchedSource(source: source) self.sourceToConfirm = FetchedSource(source: source, context: context)
case let .failure(error): case let .failure(error):
print(error) print(error)
} }
@@ -133,11 +181,12 @@ struct SourcesView: View {
} }
func addSource(_ source: FetchedSource) { func addSource(_ source: FetchedSource) {
source.context?.perform { source.context.perform {
do { do {
try source.context?.save() try source.context.save()
} catch { } catch {
print(error) print(error)
NotificationManager.shared.reportError(error: error)
} }
} }
@@ -157,88 +206,74 @@ struct SourcesView: View {
} }
} }
func fetchTrustedSources() {
// func fetchTrustedSources() { self.isLoadingTrustedSources = true
// func finish(_ result: Result<[Source], Error>)
// { AppManager.shared.fetchTrustedSources { result in
// self.fetchTrustedSourcesResult = result.map { _ in () }
// switch result {
// DispatchQueue.main.async { case .success(let trustedSources):
// do // Cache trusted source IDs.
// { UserDefaults.shared.trustedSourceIDs = trustedSources.map { $0.identifier }
// let sources = try result.get()
// print("Fetched trusted sources:", sources.map { $0.identifier }) // Don't show sources without a sourceURL.
// let featuredSourceURLs = trustedSources.compactMap { $0.sourceURL }
// let sectionUpdate = RSTCellContentChange(type: .update, sectionIndex: 0)
// self.trustedSourcesDataSource.setItems(sources, with: [sectionUpdate]) // This context is never saved, but keeps the managed sources alive.
// } let context = DatabaseManager.shared.persistentContainer.newBackgroundSavingViewContext()
// catch self.sourcesFetchContext = context
// {
// print("Error fetching trusted sources:", error) let dispatchGroup = DispatchGroup()
//
// let sectionUpdate = RSTCellContentChange(type: .update, sectionIndex: 0) var sourcesByURL = [URL: Source]()
// self.trustedSourcesDataSource.setItems([], with: [sectionUpdate]) var errors: [(error: Error, sourceURL: URL)] = []
// }
// } for sourceURL in featuredSourceURLs {
// } dispatchGroup.enter()
//
// self.fetchTrustedSourcesOperation = AppManager.shared.fetchTrustedSources { result in AppManager.shared.fetchSource(sourceURL: sourceURL, managedObjectContext: context) { result in
// switch result defer {
// { dispatchGroup.leave()
// case .failure(let error): finish(.failure(error)) }
// case .success(let trustedSources):
// // Cache trusted source IDs. // Serialize access to sourcesByURL.
// UserDefaults.shared.trustedSourceIDs = trustedSources.map { $0.identifier } context.performAndWait {
// switch result
// // Don't show sources without a sourceURL. {
// let featuredSourceURLs = trustedSources.compactMap { $0.sourceURL } case .failure(let error): errors.append((error, sourceURL))
// case .success(let source): sourcesByURL[source.sourceURL] = source
// // This context is never saved, but keeps the managed sources alive. }
// let context = DatabaseManager.shared.persistentContainer.newBackgroundSavingViewContext() }
// self._fetchTrustedSourcesContext = context }
// }
// let dispatchGroup = DispatchGroup()
// dispatchGroup.notify(queue: .main) {
// var sourcesByURL = [URL: Source]() if let (error, _) = errors.first {
// var fetchError: Error? NotificationManager.shared.reportError(error: error)
// } else {
// for sourceURL in featuredSourceURLs let sources = featuredSourceURLs.compactMap { sourcesByURL[$0] }
// { self.trustedSources = sources
// dispatchGroup.enter() }
//
// AppManager.shared.fetchSource(sourceURL: sourceURL, managedObjectContext: context) { result in self.isLoadingTrustedSources = false
// // Serialize access to sourcesByURL. }
// context.performAndWait {
// switch result case .failure(let error):
// { NotificationManager.shared.reportError(error: error)
// case .failure(let error): fetchError = error self.isLoadingTrustedSources = false
// case .success(let source): sourcesByURL[source.sourceURL] = source }
// } }
// }
// dispatchGroup.leave()
// }
// }
// }
//
// dispatchGroup.notify(queue: .main) {
// if let error = fetchError
// {
// finish(.failure(error))
// }
// else
// {
// let sources = featuredSourceURLs.compactMap { sourcesByURL[$0] }
// finish(.success(sources))
// }
// }
// }
// }
// }
} }
struct SourcesListView_Previews: PreviewProvider { struct SourcesView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
SourcesView() Color.clear
.sheet(isPresented: .constant(true)) {
NavigationView {
SourcesView()
}
}
} }
} }
@@ -252,16 +287,13 @@ extension Source: Identifiable {
struct FetchedSource: Identifiable { struct FetchedSource: Identifiable {
let source: Source let source: Source
let context: NSManagedObjectContext? let context: NSManagedObjectContext
var id: String { var id: String {
source.identifier source.identifier
} }
init?(source: Source) { init(source: Source, context: NSManagedObjectContext) {
guard let context = source.managedObjectContext else{
return nil
}
self.source = source self.source = source
self.context = context self.context = context
} }