Search results are now displayed

This commit is contained in:
Lumaa 2024-02-11 12:03:21 +01:00
parent 015b60b8f1
commit a5021e9fc9
6 changed files with 209 additions and 10 deletions

View File

@ -52,6 +52,7 @@
B9D9C6C32B6A576C00C26A41 /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9D9C6C22B6A576C00C26A41 /* NotificationsView.swift */; };
B9D9C6C52B6A587700C26A41 /* NotificationRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9D9C6C42B6A587700C26A41 /* NotificationRow.swift */; };
B9D9C6C72B6A590F00C26A41 /* ProfilePicture.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9D9C6C62B6A590F00C26A41 /* ProfilePicture.swift */; };
B9DC69292B78D9A500E625B9 /* SearchResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9DC69282B78D9A500E625B9 /* SearchResultView.swift */; };
B9EBE8562B47256900FB594D /* PostAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9EBE8552B47256900FB594D /* PostAttachment.swift */; };
B9EBE8582B474FD600FB594D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9EBE8572B474FD600FB594D /* AppDelegate.swift */; };
B9F8FA162B5D3AC30044DAB4 /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9F8FA152B5D3AC30044DAB4 /* SafariView.swift */; };
@ -151,6 +152,7 @@
B9D9C6C22B6A576C00C26A41 /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = "<group>"; };
B9D9C6C42B6A587700C26A41 /* NotificationRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationRow.swift; sourceTree = "<group>"; };
B9D9C6C62B6A590F00C26A41 /* ProfilePicture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfilePicture.swift; sourceTree = "<group>"; };
B9DC69282B78D9A500E625B9 /* SearchResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultView.swift; sourceTree = "<group>"; };
B9EBE8552B47256900FB594D /* PostAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostAttachment.swift; sourceTree = "<group>"; };
B9EBE8572B474FD600FB594D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
B9F8FA152B5D3AC30044DAB4 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = "<group>"; };
@ -352,6 +354,7 @@
B9D9C6C62B6A590F00C26A41 /* ProfilePicture.swift */,
B97491E22B6E96700098BC48 /* SymbolWidth.swift */,
B915C4412B6F908C00042DDB /* ProfileView.swift */,
B9DC69282B78D9A500E625B9 /* SearchResultView.swift */,
);
path = Components;
sourceTree = "<group>";
@ -532,6 +535,7 @@
B999DE602B76FB3E00509868 /* ContactRow.swift in Sources */,
B999DE5C2B76F8CB00509868 /* ContactsView.swift in Sources */,
B9842C0E2B2F21B700D9F3C1 /* CompactPostView.swift in Sources */,
B9DC69292B78D9A500E625B9 /* SearchResultView.swift in Sources */,
B98BC7492B46CEDA00595441 /* AppearenceView.swift in Sources */,
B9D9C6C52B6A587700C26A41 /* NotificationRow.swift in Sources */,
B9FB94992B2EEB9400D81C07 /* AddInstanceView.swift in Sources */,

View File

@ -0,0 +1,131 @@
//Made by Lumaa
import SwiftUI
struct SearchResultView: View {
@EnvironmentObject private var navigator: Navigator
@Environment(AccountManager.self) private var accountManager: AccountManager
var searchResults: SearchResults
var query: String
var body: some View {
if !searchResults.isEmpty {
VStack(alignment: .leading) {
if !searchResults.accounts.isEmpty {
Text("discovery.results.users")
.multilineTextAlignment(.leading)
.font(.title.bold())
.padding(.horizontal)
accountsView
}
if searchResults.statuses.count > 0 {
Text("discovery.results.posts")
.multilineTextAlignment(.leading)
.font(.title.bold())
.padding(.horizontal)
statusesView
}
if searchResults.hashtags.count > 0 {
Text("discovery.results.tags")
.multilineTextAlignment(.leading)
.font(.title.bold())
.padding(.horizontal)
tagsView
}
}
} else {
ContentUnavailableView.search(text: query)
}
}
var accountsView: some View {
ScrollView(.horizontal, showsIndicators: false) {
LazyHStack(spacing: 10) {
ForEach(searchResults.accounts) { account in
VStack {
ProfilePicture(url: account.avatar, size: 64)
Text(account.displayName?.replacing(/:.+:/, with: "") ?? account.username)
.font(.subheadline.bold())
.foregroundStyle(Color(uiColor: UIColor.label))
.lineLimit(1)
Text("@\(account.username)")
.font(.caption)
.foregroundStyle(Color.gray)
Spacer()
Button {
guard let client = accountManager.getClient() else { return }
Task {
do {
let better: Account = try await client.get(endpoint: Accounts.accounts(id: account.id))
navigator.navigate(to: .account(acc: better))
} catch {
print(error)
}
}
} label: {
Text("account.view")
}
.buttonStyle(LargeButton(filled: true, height: 7.5))
}
.padding(.vertical)
.frame(width: 200)
.background(Color.gray.opacity(0.2))
.clipShape(RoundedRectangle(cornerRadius: 15.0))
}
.scrollTargetLayout()
}
}
.padding(.horizontal)
.scrollClipDisabled()
.defaultScrollAnchor(.leading)
}
var tagsView: some View {
VStack(spacing: 7.5) {
ForEach(searchResults.hashtags) { tag in
HStack {
VStack(alignment: .leading) {
Text("#\(tag.name)")
.multilineTextAlignment(.leading)
.lineLimit(1)
.bold()
Text("tag.posts-\(tag.totalUses)")
.multilineTextAlignment(.leading)
.lineLimit(1)
.foregroundStyle(Color.gray)
}
Spacer()
Button {
// do stuff
} label: {
Text("tag.read")
}
.buttonStyle(LargeButton(filled: true, height: 7.5))
}
.padding()
}
}
}
var statusesView: some View {
VStack(spacing: 7.5) {
ForEach(searchResults.statuses) { status in
CompactPostView(status: status)
.padding(.vertical)
}
}
}
}

View File

@ -125,4 +125,4 @@ public extension Relationship {
}
}
extension Relationship: Sendable {}
extension Relationship: Sendable, Equatable {}

View File

@ -19,6 +19,12 @@ public struct SearchResults: Decodable {
extension SearchResults: Sendable {}
extension SearchResults: Equatable {
public static func == (lhs: SearchResults, rhs: SearchResults) -> Bool {
return lhs.statuses == rhs.statuses && lhs.accounts == rhs.accounts && lhs.relationships == rhs.relationships && lhs.hashtags == rhs.hashtags
}
}
public enum Search: Endpoint {
case search(query: String, type: String?, offset: Int?, following: Bool?)
case accountsSearch(query: String, type: String?, offset: Int?, following: Bool?)

View File

@ -532,6 +532,36 @@
}
}
},
"discovery.results.posts" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Found posts"
}
}
}
},
"discovery.results.tags" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Found tags"
}
}
}
},
"discovery.results.users" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Found users"
}
}
}
},
"discovery.search.posts" : {
"localizations" : {
"en" : {
@ -564,6 +594,16 @@
}
}
},
"discovery.search.tags" : {
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Tags"
}
}
}
},
"discovery.search.users" : {
"localizations" : {
"en" : {
@ -1697,4 +1737,4 @@
}
},
"version" : "1.0"
}
}

View File

@ -9,8 +9,9 @@ struct DiscoveryView: View {
@State private var searchQuery: String = ""
@State private var results: [String : SearchResults] = [:]
@State private var querying: Bool = false
let allTokens = [Token(id: "accounts", name: String(localized: "discovery.search.users")), Token(id: "statuses", name: String(localized: "discovery.search.posts")), Token(id: "hashtags", name: String(localized: "discovery.search.tags"))]
let allTokens = [Token(id: "accounts", name: String(localized: "discovery.search.users"), image: "person.3.fill"), Token(id: "statuses", name: String(localized: "discovery.search.posts"), image: "note.text"), Token(id: "hashtags", name: String(localized: "discovery.search.tags"), image: "tag.fill")]
@State private var currentTokens = [Token]()
@State private var suggestedAccounts: [Account] = []
@ -19,11 +20,23 @@ struct DiscoveryView: View {
@State private var trendingStatuses: [Status] = []
@State private var trendingLinks: [Card] = []
// TODO: "Read" button + search with scopes
// TODO: "Read" button
var body: some View {
NavigationStack(path: $navigator.path) {
ScrollView {
if results != [:] && !querying {
SearchResultView(searchResults: results[searchQuery] ?? .init(accounts: [], statuses: [], hashtags: []), query: searchQuery)
.environmentObject(navigator)
Spacer()
.foregroundStyle(Color.white)
.padding()
} else if querying {
ProgressView()
.progressViewStyle(.circular)
}
VStack(alignment: .leading) {
Text("discovery.suggested.users")
.multilineTextAlignment(.leading)
@ -41,20 +54,24 @@ struct DiscoveryView: View {
}
}
.submitLabel(.search)
.onSubmit {
Task {
.task(id: searchQuery) {
if !searchQuery.isEmpty {
querying = true
await search()
querying = false
} else {
querying = false
results = [:]
}
}
.onChange(of: currentTokens) { old, new in
guard new.count > 1 else { return }
let oldToken = old.first ?? allTokens[0]
let newToken = new.last ?? allTokens[1]
currentTokens.removeAll(where: { $0 == oldToken })
currentTokens = [newToken]
}
.searchable(text: $searchQuery, tokens: $currentTokens, suggestedTokens: .constant(allTokens), prompt: Text("discovery.search.prompt")) { token in
Text(token.name)
Label(token.name, systemImage: token.image)
}
.withAppRouter(navigator)
.navigationTitle(Text("discovery"))
@ -146,7 +163,7 @@ struct DiscoveryView: View {
guard let client = accountManager.getClient(), !searchQuery.isEmpty else { return }
do {
try await Task.sleep(for: .milliseconds(250))
var results: SearchResults = try await client.get(endpoint: Search.search(query: searchQuery, type: nil, offset: nil, following: nil), forceVersion: .v2)
var results: SearchResults = try await client.get(endpoint: Search.search(query: searchQuery, type: currentTokens.first?.id, offset: nil, following: nil), forceVersion: .v2)
// let relationships: [Relationship] = try await client.get(endpoint: Accounts.relationships(ids: results.accounts.map(\.id)))
// results.relationships = relationships
withAnimation {
@ -190,5 +207,6 @@ struct DiscoveryView: View {
struct Token: Identifiable, Equatable {
var id: String
var name: String
var image: String
}
}