mirror of
https://github.com/Ferrite-iOS/Ferrite.git
synced 2026-01-11 20:10:27 +00:00
Searching: Cleanup existing searches
If a user searched after cancelling the search the first time, the first search would still continue. Assign the search task to navigation view and automatically cancel it and dismiss the searchbar when the user switches to a different tab. Also add a ProgressView to show which source is being parsed. Signed-off-by: kingbri <bdashore3@gmail.com>
This commit is contained in:
parent
6a90dab386
commit
c82cb1819d
7 changed files with 89 additions and 20 deletions
|
|
@ -16,6 +16,7 @@
|
|||
0C4CFC4D28970C8B00AD9FAD /* SourceComplexQuery+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C4CFC4728970C8B00AD9FAD /* SourceComplexQuery+CoreDataClass.swift */; };
|
||||
0C4CFC4E28970C8B00AD9FAD /* SourceComplexQuery+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C4CFC4828970C8B00AD9FAD /* SourceComplexQuery+CoreDataProperties.swift */; };
|
||||
0C57D4CC289032ED008534E8 /* SearchResultRDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C57D4CB289032ED008534E8 /* SearchResultRDView.swift */; };
|
||||
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 */; };
|
||||
0C733287289C4C820058D1FE /* SourceSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C733286289C4C820058D1FE /* SourceSettingsView.swift */; };
|
||||
|
|
@ -75,6 +76,7 @@
|
|||
0C4CFC4728970C8B00AD9FAD /* SourceComplexQuery+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SourceComplexQuery+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
|
@ -251,6 +253,7 @@
|
|||
0CA148BD288903F000DE2211 /* MagnetChoiceView.swift */,
|
||||
0C0D50E6288DFF850035ECC8 /* SourcesView.swift */,
|
||||
0C32FB522890D19D002BD219 /* AboutView.swift */,
|
||||
0C60B1EE28A1A00000E3FD7E /* SearchProgressView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -395,6 +398,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0C0D50E5288DFE7F0035ECC8 /* SourceModels.swift in Sources */,
|
||||
0C60B1EF28A1A00000E3FD7E /* SearchProgressView.swift in Sources */,
|
||||
0C32FB532890D19D002BD219 /* AboutView.swift in Sources */,
|
||||
0C84F4832895BFED0074B7C9 /* Source+CoreDataProperties.swift in Sources */,
|
||||
0CA148DB288903F000DE2211 /* NavView.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,12 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
enum ViewTab {
|
||||
case search
|
||||
case sources
|
||||
case settings
|
||||
}
|
||||
|
||||
class NavigationViewModel: ObservableObject {
|
||||
// Used between SearchResultsView and MagnetChoiceView
|
||||
enum ChoiceSheetType: Identifiable {
|
||||
|
|
@ -20,6 +26,9 @@ class NavigationViewModel: ObservableObject {
|
|||
|
||||
@Published var currentChoiceSheet: ChoiceSheetType?
|
||||
|
||||
@Published var selectedTab: ViewTab = .search
|
||||
@Published var showSearchProgress: Bool = false
|
||||
|
||||
// Used between SourceListView and SourceSettingsView
|
||||
@Published var showSourceSettings: Bool = false
|
||||
@Published var selectedSource: Source?
|
||||
|
|
|
|||
|
|
@ -27,14 +27,21 @@ class ScrapingViewModel: ObservableObject {
|
|||
var toastModel: ToastViewModel?
|
||||
let byteCountFormatter: ByteCountFormatter = .init()
|
||||
|
||||
@Published var runningSearchTask: Task<Void, Error>?
|
||||
@Published var searchResults: [SearchResult] = []
|
||||
@Published var searchText: String = ""
|
||||
@Published var selectedSearchResult: SearchResult?
|
||||
@Published var filteredSource: Source?
|
||||
@Published var currentSourceName: String?
|
||||
|
||||
@MainActor
|
||||
public func scanSources(sources: [Source]) async {
|
||||
if sources.isEmpty {
|
||||
Task { @MainActor in
|
||||
toastModel?.toastType = .info
|
||||
toastModel?.toastDescription = "There are no sources to search!"
|
||||
}
|
||||
|
||||
print("Sources empty")
|
||||
return
|
||||
}
|
||||
|
|
@ -43,6 +50,8 @@ class ScrapingViewModel: ObservableObject {
|
|||
|
||||
for source in sources {
|
||||
if source.enabled {
|
||||
currentSourceName = source.name
|
||||
|
||||
// Default to HTML scraping
|
||||
let preferredParser = SourcePreferredParser(rawValue: source.preferredParser) ?? .none
|
||||
|
||||
|
|
@ -97,6 +106,11 @@ class ScrapingViewModel: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
// If the task is cancelled, return
|
||||
if let searchTask = runningSearchTask, searchTask.isCancelled {
|
||||
return
|
||||
}
|
||||
|
||||
searchResults = tempResults
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +129,15 @@ class ScrapingViewModel: ObservableObject {
|
|||
let html = String(data: data, encoding: .ascii)
|
||||
return html
|
||||
} catch {
|
||||
toastModel?.toastDescription = "Error in fetching data \(error)"
|
||||
let error = error as NSError
|
||||
|
||||
switch error.code {
|
||||
case -999:
|
||||
toastModel?.toastType = .info
|
||||
toastModel?.toastDescription = "Search cancelled"
|
||||
default:
|
||||
toastModel?.toastDescription = "Error in fetching data \(error)"
|
||||
}
|
||||
print("Error in fetching data \(error)")
|
||||
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import SwiftUI
|
|||
struct ContentView: View {
|
||||
@EnvironmentObject var scrapingModel: ScrapingViewModel
|
||||
@EnvironmentObject var debridManager: DebridManager
|
||||
@EnvironmentObject var navigationModel: NavigationViewModel
|
||||
@EnvironmentObject var navModel: NavigationViewModel
|
||||
|
||||
@AppStorage("RealDebrid.Enabled") var realDebridEnabled = false
|
||||
|
||||
|
|
@ -71,17 +71,21 @@ struct ContentView: View {
|
|||
}
|
||||
.searchable(text: $scrapingModel.searchText)
|
||||
.onSubmit(of: .search) {
|
||||
Task {
|
||||
scrapingModel.runningSearchTask = Task {
|
||||
navModel.showSearchProgress = true
|
||||
|
||||
await scrapingModel.scanSources(sources: sources.compactMap { $0 })
|
||||
|
||||
if realDebridEnabled {
|
||||
await debridManager.populateDebridHashes(scrapingModel.searchResults)
|
||||
}
|
||||
|
||||
navModel.showSearchProgress = false
|
||||
}
|
||||
}
|
||||
.navigationTitle("Search")
|
||||
}
|
||||
.sheet(item: $navigationModel.currentChoiceSheet) { item in
|
||||
.sheet(item: $navModel.currentChoiceSheet) { item in
|
||||
Group {
|
||||
switch item {
|
||||
case .magnet:
|
||||
|
|
|
|||
|
|
@ -7,36 +7,29 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
enum Tab {
|
||||
case search
|
||||
case sources
|
||||
case settings
|
||||
}
|
||||
|
||||
struct MainView: View {
|
||||
@EnvironmentObject var navModel: NavigationViewModel
|
||||
@EnvironmentObject var toastModel: ToastViewModel
|
||||
|
||||
@State private var tabSelection: Tab = .search
|
||||
|
||||
var body: some View {
|
||||
TabView(selection: $tabSelection) {
|
||||
TabView(selection: $navModel.selectedTab) {
|
||||
ContentView()
|
||||
.tabItem {
|
||||
Label("Search", systemImage: "magnifyingglass")
|
||||
}
|
||||
.tag(Tab.search)
|
||||
.tag(ViewTab.search)
|
||||
|
||||
SourcesView()
|
||||
.tabItem {
|
||||
Label("Sources", systemImage: "doc.text")
|
||||
}
|
||||
.tag(Tab.sources)
|
||||
.tag(ViewTab.sources)
|
||||
|
||||
SettingsView()
|
||||
.tabItem {
|
||||
Label("Settings", systemImage: "gear")
|
||||
}
|
||||
.tag(Tab.settings)
|
||||
.tag(ViewTab.settings)
|
||||
}
|
||||
.overlay {
|
||||
VStack {
|
||||
|
|
|
|||
20
Ferrite/Views/SearchProgressView.swift
Normal file
20
Ferrite/Views/SearchProgressView.swift
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// SearchProgressView.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 8/8/22.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SearchProgressView: View {
|
||||
var body: some View {
|
||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
||||
}
|
||||
}
|
||||
|
||||
struct SearchProgressView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
SearchProgressView()
|
||||
}
|
||||
}
|
||||
|
|
@ -9,10 +9,11 @@ import SwiftUI
|
|||
|
||||
struct SearchResultsView: View {
|
||||
@Environment(\.isSearching) var isSearching
|
||||
@Environment(\.dismissSearch) var dismissSearch
|
||||
|
||||
@EnvironmentObject var scrapingModel: ScrapingViewModel
|
||||
@EnvironmentObject var debridManager: DebridManager
|
||||
@EnvironmentObject var navigationModel: NavigationViewModel
|
||||
@EnvironmentObject var navModel: NavigationViewModel
|
||||
|
||||
@AppStorage("RealDebrid.Enabled") var realDebridEnabled = false
|
||||
|
||||
|
|
@ -28,14 +29,14 @@ struct SearchResultsView: View {
|
|||
case .full:
|
||||
Task {
|
||||
await debridManager.fetchRdDownload(searchResult: result)
|
||||
navigationModel.currentChoiceSheet = .magnet
|
||||
navModel.currentChoiceSheet = .magnet
|
||||
}
|
||||
case .partial:
|
||||
if debridManager.setSelectedRdResult(result: result) {
|
||||
navigationModel.currentChoiceSheet = .batch
|
||||
navModel.currentChoiceSheet = .batch
|
||||
}
|
||||
case .none:
|
||||
navigationModel.currentChoiceSheet = .magnet
|
||||
navModel.currentChoiceSheet = .magnet
|
||||
}
|
||||
} label: {
|
||||
Text(result.title)
|
||||
|
|
@ -50,7 +51,23 @@ struct SearchResultsView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.overlay {
|
||||
if scrapingModel.searchResults.isEmpty, navModel.showSearchProgress {
|
||||
VStack(spacing: 5) {
|
||||
ProgressView()
|
||||
Text("Loading \(scrapingModel.currentSourceName ?? "")")
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: navModel.selectedTab) { tab in
|
||||
// Cancel the search if tab is switched
|
||||
if tab != .search, isSearching {
|
||||
scrapingModel.runningSearchTask?.cancel()
|
||||
dismissSearch()
|
||||
}
|
||||
}
|
||||
.onChange(of: isSearching) { changed in
|
||||
// Clear the results array on cancel
|
||||
if !changed {
|
||||
scrapingModel.searchResults = []
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue