Search: Completely revamp it! close #78 #90

This commit is contained in:
Thomas Ricouard 2023-01-21 07:51:15 +01:00
parent eb1925b5d5
commit 7b25240f59
7 changed files with 44 additions and 51 deletions

View File

@ -180,6 +180,8 @@
"explore.search.message-%@" = "Hier kannst du alles auf %@ suchen";
"explore.search.prompt" = "Suche nach Usern, Posts und Hashtags";
"explore.search.title" = "Durchsuche deine Instanz";
"explore.search.empty.message" = "This query returned no search results, please try another one.";
"explore.search.empty.title" = "No search results";
"explore.section.posts" = "Posts";
"explore.section.suggested-users" = "Vorgeschlagene Nutzer:innen";
"explore.section.tags" = "Tags";

View File

@ -180,6 +180,8 @@
"explore.search.message-%@" = "From this screen you can search anything on %@";
"explore.search.prompt" = "Search users, posts and tags";
"explore.search.title" = "Search your instance";
"explore.search.empty.message" = "This query returned no search results, please try another one.";
"explore.search.empty.title" = "No search results";
"explore.section.posts" = "Posts";
"explore.section.suggested-users" = "Suggested Users";
"explore.section.tags" = "Tags";

View File

@ -179,6 +179,8 @@
"explore.search.message-%@" = "Desde esta pantalla puedes buscar cualquier cosa en %@";
"explore.search.prompt" = "Busca usuarios, publicaciones y etiquetas";
"explore.search.title" = "Busca en tu instancia";
"explore.search.empty.message" = "This query returned no search results, please try another one.";
"explore.search.empty.title" = "No search results";
"explore.section.posts" = "Publicaciones";
"explore.section.suggested-users" = "Sugerencias de usuarios";
"explore.section.tags" = "Etiquetas";

View File

@ -180,6 +180,8 @@
"explore.search.message-%@" = "Vanaf dit scherm kan je naar alles zoeken op %@";
"explore.search.prompt" = "Zoek gebruikers, posts en hashtags";
"explore.search.title" = "Doorzoek je instantie";
"explore.search.empty.message" = "This query returned no search results, please try another one.";
"explore.search.empty.title" = "No search results";
"explore.section.posts" = "Posts";
"explore.section.suggested-users" = "Gesuggereerde Gebruikers";
"explore.section.tags" = "Hashtags";

View File

@ -18,19 +18,28 @@ public struct ExploreView: View {
public var body: some View {
List {
if !viewModel.searchQuery.isEmpty {
if let results = viewModel.results[viewModel.searchQuery] {
if !viewModel.isLoaded {
loadingView
} else if !viewModel.searchQuery.isEmpty {
if viewModel.isSearching {
HStack { }
.listRowBackground(theme.secondaryBackgroundColor)
.listRowSeparator(.hidden)
} else if let results = viewModel.results[viewModel.searchQuery], !results.isEmpty {
makeSearchResultsView(results: results)
} else {
loadingView
EmptyView(iconName: "magnifyingglass",
title: "explore.search.empty.title",
message: "explore.search.empty.message")
.listRowBackground(theme.secondaryBackgroundColor)
.listRowSeparator(.hidden)
}
} else if !viewModel.isLoaded {
loadingView
} else if viewModel.allSectionsEmpty {
EmptyView(iconName: "magnifyingglass",
title: "explore.search.title",
message: "explore.search.message-\(client.server)")
.listRowBackground(theme.secondaryBackgroundColor)
.listRowSeparator(.hidden)
} else {
if !viewModel.trendingTags.isEmpty {
trendingTagsSection
@ -60,12 +69,7 @@ public struct ExploreView: View {
.background(theme.secondaryBackgroundColor)
.navigationTitle("explore.navigation-title")
.searchable(text: $viewModel.searchQuery,
tokens: $viewModel.tokens,
suggestedTokens: $viewModel.suggestedToken,
prompt: Text("explore.search.prompt"),
token: { token in
Text(token.rawValue)
})
prompt: Text("explore.search.prompt"))
}
private var loadingView: some View {

View File

@ -18,36 +18,18 @@ class ExploreViewModel: ObservableObject {
}
}
enum Token: String, Identifiable {
case user = "@user"
case statuses = "@posts"
case tag = "#hashtag"
var id: String {
rawValue
}
var apiType: String {
switch self {
case .user:
return "accounts"
case .tag:
return "hashtags"
case .statuses:
return "statuses"
}
}
}
var allSectionsEmpty: Bool {
trendingLinks.isEmpty && trendingTags.isEmpty && trendingStatuses.isEmpty && suggestedAccounts.isEmpty
}
@Published var tokens: [Token] = []
@Published var suggestedToken: [Token] = []
@Published var searchQuery = ""
@Published var searchQuery = "" {
didSet {
isSearching = true
}
}
@Published var results: [String: SearchResults] = [:]
@Published var isLoaded = false
@Published var isSearching = false
@Published var suggestedAccounts: [Account] = []
@Published var suggestedAccountsRelationShips: [Relationship] = []
@Published var trendingTags: [Tag] = []
@ -60,19 +42,9 @@ class ExploreViewModel: ObservableObject {
init() {
$searchQuery
.removeDuplicates()
.debounce(for: .milliseconds(500), scheduler: DispatchQueue.main)
.debounce(for: .milliseconds(250), scheduler: DispatchQueue.main)
.sink(receiveValue: { [weak self] _ in
guard let self else { return }
if self.searchQuery.starts(with: "@") {
self.suggestedToken = [.user, .statuses]
} else if self.searchQuery.starts(with: "#") {
self.suggestedToken = [.tag]
} else {
self.suggestedToken = []
}
self.search()
self?.search()
})
.store(in: &cancellables)
}
@ -115,22 +87,27 @@ class ExploreViewModel: ObservableObject {
func search() {
guard !searchQuery.isEmpty else { return }
isSearching = true
searchTask?.cancel()
searchTask = nil
searchTask = Task {
guard let client else { return }
do {
let apiType = tokens.first?.apiType
var results: SearchResults = try await client.get(endpoint: Search.search(query: searchQuery,
type: apiType,
type: nil,
offset: nil,
following: nil),
forceVersion: .v2)
let relationships: [Relationship] =
try await client.get(endpoint: Accounts.relationships(ids: results.accounts.map { $0.id }))
results.relationships = relationships
self.results[searchQuery] = results
} catch {}
withAnimation {
self.results[searchQuery] = results
isSearching = false
}
} catch {
isSearching = false
}
}
}
}

View File

@ -9,4 +9,8 @@ public struct SearchResults: Decodable {
public var relationships: [Relationship] = []
public let statuses: [Status]
public let hashtags: [Tag]
public var isEmpty: Bool {
accounts.isEmpty && statuses.isEmpty && hashtags.isEmpty
}
}