From 774087d67eb231cb603abc73069cd74f4aafefe3 Mon Sep 17 00:00:00 2001 From: Fuwn Date: Sat, 1 Mar 2025 19:53:45 -0800 Subject: feat: Development commit --- Sora/Data/Either.swift | 4 +++ Sora/Views/Post/Grid/PostGridView.swift | 23 +++++++++++-- Sora/Views/SearchSuggestionsView.swift | 57 +++++++++++++++++++++++++-------- 3 files changed, 69 insertions(+), 15 deletions(-) create mode 100644 Sora/Data/Either.swift diff --git a/Sora/Data/Either.swift b/Sora/Data/Either.swift new file mode 100644 index 0000000..2c2dd22 --- /dev/null +++ b/Sora/Data/Either.swift @@ -0,0 +1,4 @@ +enum Either: Hashable { + case left(Left) + case right(Right) +} diff --git a/Sora/Views/Post/Grid/PostGridView.swift b/Sora/Views/Post/Grid/PostGridView.swift index d5d583b..dc6546d 100644 --- a/Sora/Views/Post/Grid/PostGridView.swift +++ b/Sora/Views/Post/Grid/PostGridView.swift @@ -31,9 +31,9 @@ struct PostGridView: View { } .searchable(text: $manager.searchText, prompt: "Tags") .searchSuggestions { - if settings.searchSuggestionsMode == .tags { + if settings.searchSuggestionsMode != .disabled { SearchSuggestionsView( - tags: manager.allTags, + items: searchSuggestionsItems(), searchText: $manager.searchText ) } @@ -127,4 +127,23 @@ struct PostGridView: View { } } } + + private func searchSuggestionsItems() -> [Either] { + let items: [Either] + + switch settings.searchSuggestionsMode { + case .tags: + items = manager.allTags + .map { Either.left($0) } + + case .history: + items = settings.searchHistory + .map { Either.right($0) } + + case .disabled: + items = [] + } + + return items + } } diff --git a/Sora/Views/SearchSuggestionsView.swift b/Sora/Views/SearchSuggestionsView.swift index 2a1a8d6..244de49 100644 --- a/Sora/Views/SearchSuggestionsView.swift +++ b/Sora/Views/SearchSuggestionsView.swift @@ -1,29 +1,60 @@ import SwiftUI struct SearchSuggestionsView: View { - var tags: [BooruTag] + var items: [Either] @Binding var searchText: String private var lastSearchTag: String { String(searchText.split(separator: " ").last ?? "").lowercased() } - private var filteredTags: [BooruTag] { + private var filteredItems: [Either] { guard !lastSearchTag.isEmpty else { return [] } - return tags.filter { $0.name.lowercased().contains(lastSearchTag) } + var seen = Set() + + return items.filter { item in + switch item { + case .left(let tag): + return tag.name.lowercased().contains(lastSearchTag) + && seen.insert(tag.name.lowercased()).inserted + + case .right(let query): + let newTags = query.tags.filter { tag in + tag.lowercased().contains(lastSearchTag) + && seen.insert(tag.lowercased()).inserted + } + + return !newTags.isEmpty + } + } } var body: some View { - ForEach( - tags.filter { tag in - tag.name.lowercased().contains(lastSearchTag) - } - ) { suggestion in - Button { - if let range = searchText.range(of: lastSearchTag, options: .backwards) { - searchText.replaceSubrange(range, with: suggestion.name) + ForEach(filteredItems, id: \.self) { item in + switch item { + case .left(let tag): + Button { + if let range = searchText.range(of: lastSearchTag, options: .backwards) { + searchText.replaceSubrange(range, with: tag.name) + } + } label: { + Text(tag.name) + } + + case .right(let query): + Button { + if let range = searchText.range(of: lastSearchTag, options: .backwards), + let matchingTag = query.tags.first(where: { $0.lowercased().contains(lastSearchTag) }) + { + searchText.replaceSubrange(range, with: matchingTag) + } + } label: { + if let matchingTag = query.tags.first(where: { $0.lowercased().contains(lastSearchTag) }) + { + Text(matchingTag) + } else { + Text(query.tags.first ?? "") + } } - } label: { - Text(suggestion.name) } } } -- cgit v1.2.3