From 7b25240f59eeb27d96bcc145b9028fbb29dec31b Mon Sep 17 00:00:00 2001 From: Thomas Ricouard Date: Sat, 21 Jan 2023 07:51:15 +0100 Subject: [PATCH] Search: Completely revamp it! close #78 #90 --- .../Localization/de.lproj/Localizable.strings | 2 + .../Localization/en.lproj/Localizable.strings | 2 + .../Localization/es.lproj/Localizable.strings | 2 + .../Localization/nl.lproj/Localizable.strings | 2 + .../Explore/Sources/Explore/ExploreView.swift | 26 +++++---- .../Sources/Explore/ExploreViewModel.swift | 57 ++++++------------- .../Models/Sources/Models/SearchResults.swift | 4 ++ 7 files changed, 44 insertions(+), 51 deletions(-) diff --git a/IceCubesApp/Resources/Localization/de.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/de.lproj/Localizable.strings index c5857d2d..7c469f1e 100644 --- a/IceCubesApp/Resources/Localization/de.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/de.lproj/Localizable.strings @@ -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"; diff --git a/IceCubesApp/Resources/Localization/en.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/en.lproj/Localizable.strings index ac894b5a..21ddaeb9 100644 --- a/IceCubesApp/Resources/Localization/en.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/en.lproj/Localizable.strings @@ -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"; diff --git a/IceCubesApp/Resources/Localization/es.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/es.lproj/Localizable.strings index 97fa1ede..0c896154 100644 --- a/IceCubesApp/Resources/Localization/es.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/es.lproj/Localizable.strings @@ -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"; diff --git a/IceCubesApp/Resources/Localization/nl.lproj/Localizable.strings b/IceCubesApp/Resources/Localization/nl.lproj/Localizable.strings index a740514d..e1ec1f0e 100644 --- a/IceCubesApp/Resources/Localization/nl.lproj/Localizable.strings +++ b/IceCubesApp/Resources/Localization/nl.lproj/Localizable.strings @@ -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"; diff --git a/Packages/Explore/Sources/Explore/ExploreView.swift b/Packages/Explore/Sources/Explore/ExploreView.swift index 4cc0b87b..4b41d58b 100644 --- a/Packages/Explore/Sources/Explore/ExploreView.swift +++ b/Packages/Explore/Sources/Explore/ExploreView.swift @@ -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 { diff --git a/Packages/Explore/Sources/Explore/ExploreViewModel.swift b/Packages/Explore/Sources/Explore/ExploreViewModel.swift index 8fbe1566..c9f6b438 100644 --- a/Packages/Explore/Sources/Explore/ExploreViewModel.swift +++ b/Packages/Explore/Sources/Explore/ExploreViewModel.swift @@ -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 + } } } } diff --git a/Packages/Models/Sources/Models/SearchResults.swift b/Packages/Models/Sources/Models/SearchResults.swift index 6c79a62e..d1e63d4a 100644 --- a/Packages/Models/Sources/Models/SearchResults.swift +++ b/Packages/Models/Sources/Models/SearchResults.swift @@ -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 + } }