parent
eb1925b5d5
commit
7b25240f59
|
@ -180,6 +180,8 @@
|
||||||
"explore.search.message-%@" = "Hier kannst du alles auf %@ suchen";
|
"explore.search.message-%@" = "Hier kannst du alles auf %@ suchen";
|
||||||
"explore.search.prompt" = "Suche nach Usern, Posts und Hashtags";
|
"explore.search.prompt" = "Suche nach Usern, Posts und Hashtags";
|
||||||
"explore.search.title" = "Durchsuche deine Instanz";
|
"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.posts" = "Posts";
|
||||||
"explore.section.suggested-users" = "Vorgeschlagene Nutzer:innen";
|
"explore.section.suggested-users" = "Vorgeschlagene Nutzer:innen";
|
||||||
"explore.section.tags" = "Tags";
|
"explore.section.tags" = "Tags";
|
||||||
|
|
|
@ -180,6 +180,8 @@
|
||||||
"explore.search.message-%@" = "From this screen you can search anything on %@";
|
"explore.search.message-%@" = "From this screen you can search anything on %@";
|
||||||
"explore.search.prompt" = "Search users, posts and tags";
|
"explore.search.prompt" = "Search users, posts and tags";
|
||||||
"explore.search.title" = "Search your instance";
|
"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.posts" = "Posts";
|
||||||
"explore.section.suggested-users" = "Suggested Users";
|
"explore.section.suggested-users" = "Suggested Users";
|
||||||
"explore.section.tags" = "Tags";
|
"explore.section.tags" = "Tags";
|
||||||
|
|
|
@ -179,6 +179,8 @@
|
||||||
"explore.search.message-%@" = "Desde esta pantalla puedes buscar cualquier cosa en %@";
|
"explore.search.message-%@" = "Desde esta pantalla puedes buscar cualquier cosa en %@";
|
||||||
"explore.search.prompt" = "Busca usuarios, publicaciones y etiquetas";
|
"explore.search.prompt" = "Busca usuarios, publicaciones y etiquetas";
|
||||||
"explore.search.title" = "Busca en tu instancia";
|
"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.posts" = "Publicaciones";
|
||||||
"explore.section.suggested-users" = "Sugerencias de usuarios";
|
"explore.section.suggested-users" = "Sugerencias de usuarios";
|
||||||
"explore.section.tags" = "Etiquetas";
|
"explore.section.tags" = "Etiquetas";
|
||||||
|
|
|
@ -180,6 +180,8 @@
|
||||||
"explore.search.message-%@" = "Vanaf dit scherm kan je naar alles zoeken op %@";
|
"explore.search.message-%@" = "Vanaf dit scherm kan je naar alles zoeken op %@";
|
||||||
"explore.search.prompt" = "Zoek gebruikers, posts en hashtags";
|
"explore.search.prompt" = "Zoek gebruikers, posts en hashtags";
|
||||||
"explore.search.title" = "Doorzoek je instantie";
|
"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.posts" = "Posts";
|
||||||
"explore.section.suggested-users" = "Gesuggereerde Gebruikers";
|
"explore.section.suggested-users" = "Gesuggereerde Gebruikers";
|
||||||
"explore.section.tags" = "Hashtags";
|
"explore.section.tags" = "Hashtags";
|
||||||
|
|
|
@ -18,19 +18,28 @@ public struct ExploreView: View {
|
||||||
|
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
List {
|
List {
|
||||||
if !viewModel.searchQuery.isEmpty {
|
if !viewModel.isLoaded {
|
||||||
if let results = viewModel.results[viewModel.searchQuery] {
|
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)
|
makeSearchResultsView(results: results)
|
||||||
} else {
|
} 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 {
|
} else if viewModel.allSectionsEmpty {
|
||||||
EmptyView(iconName: "magnifyingglass",
|
EmptyView(iconName: "magnifyingglass",
|
||||||
title: "explore.search.title",
|
title: "explore.search.title",
|
||||||
message: "explore.search.message-\(client.server)")
|
message: "explore.search.message-\(client.server)")
|
||||||
.listRowBackground(theme.secondaryBackgroundColor)
|
.listRowBackground(theme.secondaryBackgroundColor)
|
||||||
|
.listRowSeparator(.hidden)
|
||||||
} else {
|
} else {
|
||||||
if !viewModel.trendingTags.isEmpty {
|
if !viewModel.trendingTags.isEmpty {
|
||||||
trendingTagsSection
|
trendingTagsSection
|
||||||
|
@ -60,12 +69,7 @@ public struct ExploreView: View {
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
.navigationTitle("explore.navigation-title")
|
.navigationTitle("explore.navigation-title")
|
||||||
.searchable(text: $viewModel.searchQuery,
|
.searchable(text: $viewModel.searchQuery,
|
||||||
tokens: $viewModel.tokens,
|
prompt: Text("explore.search.prompt"))
|
||||||
suggestedTokens: $viewModel.suggestedToken,
|
|
||||||
prompt: Text("explore.search.prompt"),
|
|
||||||
token: { token in
|
|
||||||
Text(token.rawValue)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private var loadingView: some View {
|
private var loadingView: some View {
|
||||||
|
|
|
@ -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 {
|
var allSectionsEmpty: Bool {
|
||||||
trendingLinks.isEmpty && trendingTags.isEmpty && trendingStatuses.isEmpty && suggestedAccounts.isEmpty
|
trendingLinks.isEmpty && trendingTags.isEmpty && trendingStatuses.isEmpty && suggestedAccounts.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
@Published var tokens: [Token] = []
|
@Published var searchQuery = "" {
|
||||||
@Published var suggestedToken: [Token] = []
|
didSet {
|
||||||
@Published var searchQuery = ""
|
isSearching = true
|
||||||
|
}
|
||||||
|
}
|
||||||
@Published var results: [String: SearchResults] = [:]
|
@Published var results: [String: SearchResults] = [:]
|
||||||
@Published var isLoaded = false
|
@Published var isLoaded = false
|
||||||
|
@Published var isSearching = false
|
||||||
@Published var suggestedAccounts: [Account] = []
|
@Published var suggestedAccounts: [Account] = []
|
||||||
@Published var suggestedAccountsRelationShips: [Relationship] = []
|
@Published var suggestedAccountsRelationShips: [Relationship] = []
|
||||||
@Published var trendingTags: [Tag] = []
|
@Published var trendingTags: [Tag] = []
|
||||||
|
@ -60,19 +42,9 @@ class ExploreViewModel: ObservableObject {
|
||||||
init() {
|
init() {
|
||||||
$searchQuery
|
$searchQuery
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.debounce(for: .milliseconds(500), scheduler: DispatchQueue.main)
|
.debounce(for: .milliseconds(250), scheduler: DispatchQueue.main)
|
||||||
.sink(receiveValue: { [weak self] _ in
|
.sink(receiveValue: { [weak self] _ in
|
||||||
guard let self else { return }
|
self?.search()
|
||||||
|
|
||||||
if self.searchQuery.starts(with: "@") {
|
|
||||||
self.suggestedToken = [.user, .statuses]
|
|
||||||
} else if self.searchQuery.starts(with: "#") {
|
|
||||||
self.suggestedToken = [.tag]
|
|
||||||
} else {
|
|
||||||
self.suggestedToken = []
|
|
||||||
}
|
|
||||||
|
|
||||||
self.search()
|
|
||||||
})
|
})
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
@ -115,22 +87,27 @@ class ExploreViewModel: ObservableObject {
|
||||||
|
|
||||||
func search() {
|
func search() {
|
||||||
guard !searchQuery.isEmpty else { return }
|
guard !searchQuery.isEmpty else { return }
|
||||||
|
isSearching = true
|
||||||
searchTask?.cancel()
|
searchTask?.cancel()
|
||||||
searchTask = nil
|
searchTask = nil
|
||||||
searchTask = Task {
|
searchTask = Task {
|
||||||
guard let client else { return }
|
guard let client else { return }
|
||||||
do {
|
do {
|
||||||
let apiType = tokens.first?.apiType
|
|
||||||
var results: SearchResults = try await client.get(endpoint: Search.search(query: searchQuery,
|
var results: SearchResults = try await client.get(endpoint: Search.search(query: searchQuery,
|
||||||
type: apiType,
|
type: nil,
|
||||||
offset: nil,
|
offset: nil,
|
||||||
following: nil),
|
following: nil),
|
||||||
forceVersion: .v2)
|
forceVersion: .v2)
|
||||||
let relationships: [Relationship] =
|
let relationships: [Relationship] =
|
||||||
try await client.get(endpoint: Accounts.relationships(ids: results.accounts.map { $0.id }))
|
try await client.get(endpoint: Accounts.relationships(ids: results.accounts.map { $0.id }))
|
||||||
results.relationships = relationships
|
results.relationships = relationships
|
||||||
self.results[searchQuery] = results
|
withAnimation {
|
||||||
} catch {}
|
self.results[searchQuery] = results
|
||||||
|
isSearching = false
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
isSearching = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,4 +9,8 @@ public struct SearchResults: Decodable {
|
||||||
public var relationships: [Relationship] = []
|
public var relationships: [Relationship] = []
|
||||||
public let statuses: [Status]
|
public let statuses: [Status]
|
||||||
public let hashtags: [Tag]
|
public let hashtags: [Tag]
|
||||||
|
|
||||||
|
public var isEmpty: Bool {
|
||||||
|
accounts.isEmpty && statuses.isEmpty && hashtags.isEmpty
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue