Library: Add searching and cleanup
Add a searchbar to filter through various library entries so it's easier to find items. Also add fixes for < iOS 16 devices and fix up searchbar constraints. Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
parent
2258036f7b
commit
e8f62e3cdc
13 changed files with 214 additions and 122 deletions
|
|
@ -32,4 +32,6 @@ public class Application {
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
let osVersion: OperatingSystemVersion = ProcessInfo().operatingSystemVersion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -544,6 +544,9 @@ public class DebridManager: ObservableObject {
|
|||
} else {
|
||||
throw RealDebrid.RDError.FailedRequest(description: "Could not fetch your file from RealDebrid's cache or API")
|
||||
}
|
||||
|
||||
// Fetch one more time to add updated data into the RD cloud cache
|
||||
await fetchRdCloud(bypassTTL: true)
|
||||
} catch {
|
||||
switch error {
|
||||
case RealDebrid.RDError.EmptyTorrents:
|
||||
|
|
@ -640,6 +643,9 @@ public class DebridManager: ObservableObject {
|
|||
} else {
|
||||
throw AllDebrid.ADError.FailedRequest(description: "Could not fetch your file from AllDebrid's cache or API")
|
||||
}
|
||||
|
||||
// Fetch one more time to add updated data into the AD cloud cache
|
||||
await fetchAdCloud(bypassTTL: true)
|
||||
} catch {
|
||||
await sendDebridError(error, prefix: "AllDebrid download error", cancelString: "Download cancelled")
|
||||
}
|
||||
|
|
@ -650,7 +656,6 @@ public class DebridManager: ObservableObject {
|
|||
if bypassTTL || Date().timeIntervalSince1970 > allDebridCloudTTL {
|
||||
do {
|
||||
allDebridCloudMagnets = try await allDebrid.userMagnets()
|
||||
realDebridCloudDownloads = try await realDebrid.userDownloads()
|
||||
|
||||
// 5 minutes
|
||||
allDebridCloudTTL = Date().timeIntervalSince1970 + 300
|
||||
|
|
@ -685,6 +690,9 @@ public class DebridManager: ObservableObject {
|
|||
throw Premiumize.PMError.FailedRequest(description: "There were no items or files found!")
|
||||
}
|
||||
|
||||
// Fetch one more time to add updated data into the PM cloud cache
|
||||
await fetchPmCloud(bypassTTL: true)
|
||||
|
||||
// Add a PM transfer if the item exists
|
||||
if let premiumizeItem = selectedPremiumizeItem {
|
||||
try await premiumize.createTransfer(magnet: premiumizeItem.magnet)
|
||||
|
|
|
|||
|
|
@ -15,14 +15,15 @@ struct BookmarksView: View {
|
|||
|
||||
let backgroundContext = PersistenceController.shared.backgroundContext
|
||||
|
||||
var bookmarks: FetchedResults<Bookmark>
|
||||
@Binding var searchText: String
|
||||
|
||||
@State private var viewTask: Task<Void, Never>?
|
||||
@State private var bookmarkPredicate: NSPredicate?
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
if !bookmarks.isEmpty {
|
||||
List {
|
||||
DynamicFetchRequest(predicate: bookmarkPredicate) { (bookmarks: FetchedResults<Bookmark>) in
|
||||
List {
|
||||
if !bookmarks.isEmpty {
|
||||
ForEach(bookmarks, id: \.self) { bookmark in
|
||||
SearchResultButtonView(result: bookmark.toSearchResult(), existingBookmark: bookmark)
|
||||
}
|
||||
|
|
@ -30,43 +31,53 @@ struct BookmarksView: View {
|
|||
for index in offsets {
|
||||
if let bookmark = bookmarks[safe: index] {
|
||||
PersistenceController.shared.delete(bookmark, context: backgroundContext)
|
||||
|
||||
|
||||
NotificationCenter.default.post(name: .didDeleteBookmark, object: bookmark)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onMove { source, destination in
|
||||
var changedBookmarks = bookmarks.map { $0 }
|
||||
|
||||
|
||||
changedBookmarks.move(fromOffsets: source, toOffset: destination)
|
||||
|
||||
|
||||
for reverseIndex in stride(from: changedBookmarks.count - 1, through: 0, by: -1) {
|
||||
changedBookmarks[reverseIndex].orderNum = Int16(reverseIndex)
|
||||
}
|
||||
|
||||
|
||||
PersistenceController.shared.save()
|
||||
}
|
||||
}
|
||||
.inlinedList()
|
||||
.listStyle(.insetGrouped)
|
||||
}
|
||||
.inlinedList()
|
||||
.listStyle(.insetGrouped)
|
||||
.onAppear {
|
||||
if debridManager.enabledDebrids.count > 0 {
|
||||
viewTask = Task {
|
||||
let magnets = bookmarks.compactMap {
|
||||
if let magnetHash = $0.magnetHash {
|
||||
return Magnet(hash: magnetHash, link: $0.magnetLink)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
await debridManager.populateDebridIA(magnets)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
viewTask?.cancel()
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
if debridManager.enabledDebrids.count > 0 {
|
||||
viewTask = Task {
|
||||
let magnets = bookmarks.compactMap {
|
||||
if let magnetHash = $0.magnetHash {
|
||||
return Magnet(hash: magnetHash, link: $0.magnetLink)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
await debridManager.populateDebridIA(magnets)
|
||||
}
|
||||
}
|
||||
applyPredicate()
|
||||
}
|
||||
.onDisappear {
|
||||
viewTask?.cancel()
|
||||
.onChange(of: searchText) { _ in
|
||||
applyPredicate()
|
||||
}
|
||||
}
|
||||
|
||||
func applyPredicate() {
|
||||
bookmarkPredicate = searchText.isEmpty ? nil : NSPredicate(format: "title CONTAINS[cd] %@", searchText)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,11 +11,15 @@ struct AllDebridCloudView: View {
|
|||
@EnvironmentObject var debridManager: DebridManager
|
||||
@EnvironmentObject var navModel: NavigationViewModel
|
||||
|
||||
@Binding var searchText: String
|
||||
|
||||
@State private var viewTask: Task<Void, Never>?
|
||||
|
||||
var body: some View {
|
||||
DisclosureGroup("Magnets") {
|
||||
ForEach(debridManager.allDebridCloudMagnets, id: \.id) { magnet in
|
||||
ForEach(debridManager.allDebridCloudMagnets.filter {
|
||||
searchText.isEmpty ? true : $0.filename.lowercased().contains(searchText.lowercased())
|
||||
}, id: \.id) { magnet in
|
||||
Button {
|
||||
if magnet.status == "Ready" && !magnet.links.isEmpty {
|
||||
navModel.resultFromCloud = true
|
||||
|
|
@ -38,8 +42,9 @@ struct AllDebridCloudView: View {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
debridManager.clearIAValues()
|
||||
let magnet = Magnet(hash: magnet.hash, link: nil)
|
||||
|
||||
// Do not clear old IA values
|
||||
await debridManager.populateDebridIA([magnet])
|
||||
|
||||
if debridManager.selectDebridResult(magnet: magnet) {
|
||||
|
|
@ -85,9 +90,3 @@ struct AllDebridCloudView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AllDebridCloudView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
AllDebridCloudView()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,15 @@ struct PremiumizeCloudView: View {
|
|||
@EnvironmentObject var debridManager: DebridManager
|
||||
@EnvironmentObject var navModel: NavigationViewModel
|
||||
|
||||
@Binding var searchText: String
|
||||
|
||||
@State private var viewTask: Task<Void, Never>?
|
||||
|
||||
var body: some View {
|
||||
DisclosureGroup("Items") {
|
||||
ForEach(debridManager.premiumizeCloudItems, id: \.id) { item in
|
||||
ForEach(debridManager.premiumizeCloudItems.filter {
|
||||
searchText.isEmpty ? true : $0.name.lowercased().contains(searchText.lowercased())
|
||||
}, id: \.id) { item in
|
||||
Button(item.name) {
|
||||
Task {
|
||||
navModel.resultFromCloud = true
|
||||
|
|
@ -60,9 +64,3 @@ struct PremiumizeCloudView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PremiumizeCloudView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
PremiumizeCloudView()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,12 +11,16 @@ struct RealDebridCloudView: View {
|
|||
@EnvironmentObject var navModel: NavigationViewModel
|
||||
@EnvironmentObject var debridManager: DebridManager
|
||||
|
||||
@Binding var searchText: String
|
||||
|
||||
@State private var viewTask: Task<Void, Never>?
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
DisclosureGroup("Downloads") {
|
||||
ForEach(debridManager.realDebridCloudDownloads, id: \.self) { downloadResponse in
|
||||
ForEach(debridManager.realDebridCloudDownloads.filter {
|
||||
searchText.isEmpty ? true : $0.filename.lowercased().contains(searchText.lowercased())
|
||||
}, id: \.self) { downloadResponse in
|
||||
Button(downloadResponse.filename) {
|
||||
navModel.resultFromCloud = true
|
||||
navModel.selectedTitle = downloadResponse.filename
|
||||
|
|
@ -46,7 +50,9 @@ struct RealDebridCloudView: View {
|
|||
}
|
||||
|
||||
DisclosureGroup("Torrents") {
|
||||
ForEach(debridManager.realDebridCloudTorrents, id: \.self) { torrentResponse in
|
||||
ForEach(debridManager.realDebridCloudTorrents.filter {
|
||||
searchText.isEmpty ? true : $0.filename.lowercased().contains(searchText.lowercased())
|
||||
}, id: \.self) { torrentResponse in
|
||||
Button {
|
||||
if torrentResponse.status == "downloaded" && !torrentResponse.links.isEmpty {
|
||||
navModel.resultFromCloud = true
|
||||
|
|
@ -69,8 +75,9 @@ struct RealDebridCloudView: View {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
debridManager.clearIAValues()
|
||||
let magnet = Magnet(hash: torrentResponse.hash, link: nil)
|
||||
|
||||
// Do not clear old IA values
|
||||
await debridManager.populateDebridIA([magnet])
|
||||
|
||||
if debridManager.selectDebridResult(magnet: magnet) {
|
||||
|
|
@ -119,9 +126,3 @@ struct RealDebridCloudView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct RealDebridCloudView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
RealDebridCloudView()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,30 +10,21 @@ import SwiftUI
|
|||
struct DebridCloudView: View {
|
||||
@EnvironmentObject var debridManager: DebridManager
|
||||
|
||||
@Binding var searchText: String
|
||||
|
||||
var body: some View {
|
||||
NavView {
|
||||
VStack {
|
||||
List {
|
||||
switch debridManager.selectedDebridType {
|
||||
case .realDebrid:
|
||||
RealDebridCloudView()
|
||||
case .premiumize:
|
||||
PremiumizeCloudView()
|
||||
case .allDebrid:
|
||||
AllDebridCloudView()
|
||||
case .none:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
.inlinedList()
|
||||
.listStyle(.grouped)
|
||||
List {
|
||||
switch debridManager.selectedDebridType {
|
||||
case .realDebrid:
|
||||
RealDebridCloudView(searchText: $searchText)
|
||||
case .premiumize:
|
||||
PremiumizeCloudView(searchText: $searchText)
|
||||
case .allDebrid:
|
||||
AllDebridCloudView(searchText: $searchText)
|
||||
case .none:
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DebridCloudView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
DebridCloudView()
|
||||
.listStyle(.plain)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ struct HistoryButtonView: View {
|
|||
toastModel.updateToastDescription("URL invalid. Cannot load this history entry. Please delete it.")
|
||||
}
|
||||
} label: {
|
||||
VStack(alignment: .leading) {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
VStack(alignment: .leading) {
|
||||
Text(entry.name ?? "Unknown title")
|
||||
.font(entry.subName == nil ? .body : .subheadline)
|
||||
|
|
|
|||
|
|
@ -10,44 +10,89 @@ import SwiftUI
|
|||
struct HistoryView: View {
|
||||
@EnvironmentObject var navModel: NavigationViewModel
|
||||
|
||||
let backgroundContext = PersistenceController.shared.backgroundContext
|
||||
|
||||
var history: FetchedResults<History>
|
||||
var formatter: DateFormatter = .init()
|
||||
|
||||
@State private var historyIndex = 0
|
||||
@Binding var searchText: String
|
||||
|
||||
init(history: FetchedResults<History>) {
|
||||
self.history = history
|
||||
|
||||
formatter.dateStyle = .medium
|
||||
formatter.timeStyle = .none
|
||||
}
|
||||
|
||||
func groupedEntries(_ result: FetchedResults<History>) -> [[History]] {
|
||||
Dictionary(grouping: result) { (element: History) in
|
||||
element.dateString ?? ""
|
||||
}.values.sorted { $0[0].date ?? Date() > $1[0].date ?? Date() }
|
||||
}
|
||||
@State private var historyPredicate: NSPredicate?
|
||||
|
||||
var body: some View {
|
||||
if !history.isEmpty {
|
||||
DynamicFetchRequest(predicate: historyPredicate) { (allEntries: FetchedResults<HistoryEntry>) in
|
||||
List {
|
||||
ForEach(groupedEntries(history), id: \.self) { (section: [History]) in
|
||||
Section(header: Text(formatter.string(from: section[0].date ?? Date()))) {
|
||||
ForEach(section, id: \.self) { history in
|
||||
ForEach(history.entryArray) { entry in
|
||||
HistoryButtonView(entry: entry)
|
||||
}
|
||||
.onDelete { offsets in
|
||||
removeEntry(at: offsets, from: history)
|
||||
}
|
||||
}
|
||||
if !history.isEmpty {
|
||||
ForEach(groupedHistory(history), id: \.self) { historyGroup in
|
||||
HistorySectionView(allEntries: allEntries, historyGroup: historyGroup)
|
||||
}
|
||||
}
|
||||
}
|
||||
.listStyle(.insetGrouped)
|
||||
}
|
||||
.onAppear {
|
||||
applyPredicate()
|
||||
}
|
||||
.onChange(of: searchText) { _ in
|
||||
applyPredicate()
|
||||
}
|
||||
}
|
||||
|
||||
func applyPredicate() {
|
||||
if searchText.isEmpty {
|
||||
historyPredicate = nil
|
||||
} else {
|
||||
let namePredicate = NSPredicate(format: "name CONTAINS[cd] %@", searchText.lowercased())
|
||||
let subNamePredicate = NSPredicate(format: "subName CONTAINS[cd] %@", searchText.lowercased())
|
||||
historyPredicate = NSCompoundPredicate(type: .or, subpredicates: [namePredicate, subNamePredicate])
|
||||
}
|
||||
}
|
||||
|
||||
func groupedHistory(_ result: FetchedResults<History>) -> [[History]] {
|
||||
return Dictionary(grouping: result) { (element: History) in
|
||||
element.dateString ?? ""
|
||||
}
|
||||
.values
|
||||
.sorted { $0[0].date ?? Date() > $1[0].date ?? Date() }
|
||||
}
|
||||
}
|
||||
|
||||
struct HistorySectionView: View {
|
||||
let backgroundContext = PersistenceController.shared.backgroundContext
|
||||
|
||||
var formatter: DateFormatter = .init()
|
||||
var allEntries: FetchedResults<HistoryEntry>
|
||||
var historyGroup: [History]
|
||||
|
||||
init(allEntries: FetchedResults<HistoryEntry>, historyGroup: [History]) {
|
||||
self.allEntries = allEntries
|
||||
self.historyGroup = historyGroup
|
||||
|
||||
formatter.dateStyle = .medium
|
||||
formatter.timeStyle = .none
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if compareGroup(historyGroup) > 0 {
|
||||
Section(header: Text(formatter.string(from: historyGroup[0].date ?? Date()))) {
|
||||
ForEach(historyGroup, id: \.self) { history in
|
||||
ForEach(history.entryArray.filter { allEntries.contains($0) }, id: \.self) { entry in
|
||||
HistoryButtonView(entry: entry)
|
||||
}
|
||||
.onDelete { offsets in
|
||||
removeEntry(at: offsets, from: history)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func compareGroup(_ group: [History]) -> Int {
|
||||
var totalCount = 0
|
||||
for history in group {
|
||||
totalCount += history.entryArray.reduce(0, { result, item in
|
||||
result + (allEntries.contains { $0.name == item.name || (item.subName.map { return !$0.isEmpty } ?? false && $0.subName == item.subName) } ? 1 : 0)
|
||||
})
|
||||
}
|
||||
|
||||
return totalCount
|
||||
}
|
||||
|
||||
func removeEntry(at offsets: IndexSet, from history: History) {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ struct ContentView: View {
|
|||
sortDescriptors: []
|
||||
) var sources: FetchedResults<Source>
|
||||
|
||||
@AppStorage("Behavior.AutocorrectSearch") var autocorrectSearch = true
|
||||
|
||||
@State private var selectedSource: Source? {
|
||||
didSet {
|
||||
scrapingModel.filteredSource = selectedSource
|
||||
|
|
@ -72,7 +74,9 @@ struct ContentView: View {
|
|||
SearchResultsView()
|
||||
}
|
||||
.navigationTitle("Search")
|
||||
.navigationBarTitleDisplayMode(navModel.isSearching ? .inline : .large)
|
||||
.navigationBarTitleDisplayMode(
|
||||
navModel.isSearching && Application.shared.osVersion.majorVersion > 14 ? .inline : .large
|
||||
)
|
||||
.navigationSearchBar {
|
||||
SearchBar("Search",
|
||||
text: $scrapingModel.searchText,
|
||||
|
|
@ -114,8 +118,8 @@ struct ContentView: View {
|
|||
}
|
||||
.introspectSearchController { searchController in
|
||||
searchController.hidesNavigationBarDuringPresentation = false
|
||||
searchController.searchBar.autocorrectionType = .no
|
||||
searchController.searchBar.autocapitalizationType = .none
|
||||
searchController.searchBar.autocorrectionType = autocorrectSearch ? .default : .no
|
||||
searchController.searchBar.autocapitalizationType = autocorrectSearch ? .sentences : .none
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
//
|
||||
|
||||
import SwiftUI
|
||||
import SwiftUIX
|
||||
|
||||
struct LibraryView: View {
|
||||
enum LibraryPickerSegment {
|
||||
|
|
@ -19,9 +20,7 @@ struct LibraryView: View {
|
|||
|
||||
@FetchRequest(
|
||||
entity: Bookmark.entity(),
|
||||
sortDescriptors: [
|
||||
NSSortDescriptor(keyPath: \Bookmark.orderNum, ascending: true)
|
||||
]
|
||||
sortDescriptors: []
|
||||
) var bookmarks: FetchedResults<Bookmark>
|
||||
|
||||
@FetchRequest(
|
||||
|
|
@ -31,11 +30,15 @@ struct LibraryView: View {
|
|||
]
|
||||
) var history: FetchedResults<History>
|
||||
|
||||
@State private var historyEmpty = true
|
||||
@AppStorage("Behavior.AutocorrectSearch") var autocorrectSearch = true
|
||||
|
||||
@State private var selectedSegment: LibraryPickerSegment = .bookmarks
|
||||
@State private var editMode: EditMode = .inactive
|
||||
|
||||
@State private var searchText: String = ""
|
||||
@State private var isEditingSearch = false
|
||||
@State private var isSearching = false
|
||||
|
||||
var body: some View {
|
||||
NavView {
|
||||
VStack {
|
||||
|
|
@ -48,19 +51,34 @@ struct LibraryView: View {
|
|||
}
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
.padding()
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 5)
|
||||
|
||||
switch selectedSegment {
|
||||
case .bookmarks:
|
||||
BookmarksView(bookmarks: bookmarks)
|
||||
BookmarksView(searchText: $searchText)
|
||||
case .history:
|
||||
HistoryView(history: history)
|
||||
HistoryView(history: history, searchText: $searchText)
|
||||
case .debridCloud:
|
||||
DebridCloudView()
|
||||
DebridCloudView(searchText: $searchText)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.navigationSearchBar {
|
||||
SearchBar("Search", text: $searchText, isEditing: $isEditingSearch, onCommit: {
|
||||
isSearching = true
|
||||
})
|
||||
.showsCancelButton(isEditingSearch || isSearching)
|
||||
.onCancel {
|
||||
searchText = ""
|
||||
isSearching = false
|
||||
}
|
||||
}
|
||||
.introspectSearchController { searchController in
|
||||
searchController.searchBar.autocorrectionType = autocorrectSearch ? .default : .no
|
||||
searchController.searchBar.autocapitalizationType = autocorrectSearch ? .sentences : .none
|
||||
}
|
||||
.overlay {
|
||||
switch selectedSegment {
|
||||
case .bookmarks:
|
||||
|
|
@ -80,7 +98,8 @@ struct LibraryView: View {
|
|||
.navigationTitle("Library")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
HStack {
|
||||
HStack(spacing: Application.shared.osVersion.majorVersion > 14 ? 10 : 18) {
|
||||
Spacer()
|
||||
EditButton()
|
||||
|
||||
switch selectedSegment {
|
||||
|
|
@ -90,6 +109,7 @@ struct LibraryView: View {
|
|||
HistoryActionsView()
|
||||
}
|
||||
}
|
||||
.animation(.none)
|
||||
}
|
||||
}
|
||||
.environment(\.editMode, $editMode)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ struct SettingsView: View {
|
|||
|
||||
let backgroundContext = PersistenceController.shared.backgroundContext
|
||||
|
||||
@AppStorage("Behavior.AutocorrectSearch") var autocorrectSearch = true
|
||||
|
||||
@AppStorage("Updates.AutomaticNotifs") var autoUpdateNotifs = true
|
||||
|
||||
@AppStorage("Actions.DefaultDebrid") var defaultDebridAction: DefaultDebridActionType = .none
|
||||
|
|
@ -76,6 +78,12 @@ struct SettingsView: View {
|
|||
}
|
||||
}
|
||||
|
||||
Section(header: Text("Behavior")) {
|
||||
Toggle(isOn: $autocorrectSearch) {
|
||||
Text("Autocorrect search")
|
||||
}
|
||||
}
|
||||
|
||||
Section(header: Text("Source management")) {
|
||||
NavigationLink("Source lists", destination: SettingsSourceListView())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,13 +15,11 @@ struct SourcesView: View {
|
|||
|
||||
let backgroundContext = PersistenceController.shared.backgroundContext
|
||||
|
||||
@FetchRequest(
|
||||
entity: Source.entity(),
|
||||
sortDescriptors: []
|
||||
) var sources: FetchedResults<Source>
|
||||
@AppStorage("Behavior.AutocorrectSearch") var autocorrectSearch = true
|
||||
|
||||
@State private var checkedForSources = false
|
||||
@State private var isEditing = false
|
||||
@State private var isEditingSearch = false
|
||||
@State private var isSearching = false
|
||||
|
||||
@State private var viewTask: Task<Void, Never>? = nil
|
||||
@State private var searchText: String = ""
|
||||
|
|
@ -35,7 +33,7 @@ struct SourcesView: View {
|
|||
ZStack {
|
||||
if !checkedForSources {
|
||||
ProgressView()
|
||||
} else if sources.isEmpty, sourceManager.availableSources.isEmpty {
|
||||
} else if installedSources.isEmpty, sourceManager.availableSources.isEmpty {
|
||||
EmptyInstructionView(title: "No Sources", message: "Add a source list in Settings")
|
||||
} else {
|
||||
List {
|
||||
|
|
@ -119,11 +117,18 @@ struct SourcesView: View {
|
|||
}
|
||||
.navigationTitle("Sources")
|
||||
.navigationSearchBar {
|
||||
SearchBar("Search", text: $searchText, isEditing: $isEditing)
|
||||
.showsCancelButton(isEditing)
|
||||
.onCancel {
|
||||
searchText = ""
|
||||
}
|
||||
SearchBar("Search", text: $searchText, isEditing: $isEditingSearch, onCommit: {
|
||||
isSearching = true
|
||||
})
|
||||
.showsCancelButton(isEditingSearch || isSearching)
|
||||
.onCancel {
|
||||
searchText = ""
|
||||
isSearching = false
|
||||
}
|
||||
}
|
||||
.introspectSearchController { searchController in
|
||||
searchController.searchBar.autocorrectionType = autocorrectSearch ? .default : .no
|
||||
searchController.searchBar.autocapitalizationType = autocorrectSearch ? .sentences : .none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue