Search users in list edit

This commit is contained in:
Thomas Ricouard 2023-11-28 14:16:04 +01:00
parent ab07fb5906
commit f2606b4614
3 changed files with 287 additions and 27 deletions

View file

@ -1,6 +1,22 @@
{
"sourceLanguage" : "en",
"strings" : {
"" : {
"localizations" : {
"be" : {
"stringUnit" : {
"state" : "translated",
"value" : ""
}
},
"eu" : {
"stringUnit" : {
"state" : "translated",
"value" : ""
}
}
}
},
" ⸱ " : {
"localizations" : {
"be" : {
@ -31939,6 +31955,125 @@
}
}
},
"lists.edit.users-search" : {
"extractionState" : "manual",
"localizations" : {
"be" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
},
"ca" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
},
"en-GB" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
},
"es" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
},
"eu" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
},
"nb" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "Search users"
}
}
}
},
"lists.name" : {
"extractionState" : "manual",
"localizations" : {

View file

@ -3,6 +3,7 @@ import EmojiText
import Models
import Network
import SwiftUI
import Account
@MainActor
public struct ListEditView: View {
@ -42,38 +43,28 @@ public struct ListEditView: View {
}
Section("lists.edit.users-in-list") {
if viewModel.isLoadingAccounts {
HStack {
Spacer()
ProgressView()
Spacer()
}
} else {
ForEach(viewModel.accounts) { account in
HStack {
AvatarView(account.avatar)
VStack(alignment: .leading) {
EmojiTextApp(.init(stringValue: account.safeDisplayName),
emojis: account.emojis)
.emojiSize(Font.scaledBodyFont.emojiSize)
.emojiBaselineOffset(Font.scaledBodyFont.emojiBaselineOffset)
Text("@\(account.acct)")
.foregroundColor(.gray)
.font(.scaledFootnote)
}
}
}.onDelete { indexes in
if let index = indexes.first {
Task {
let account = viewModel.accounts[index]
await viewModel.delete(account: account)
}
HStack {
TextField("lists.edit.users-search",
text: $viewModel.searchUserQuery)
if !viewModel.searchUserQuery.isEmpty {
Button {
viewModel.searchUserQuery = ""
} label: {
Image(systemName: "xmark.circle")
}
}
}
.id("stableId")
if !viewModel.searchUserQuery.isEmpty {
searchAccountsView
} else {
listAccountsView
}
}
.listRowBackground(theme.primaryBackgroundColor)
.disabled(viewModel.isUpdating)
}
.scrollDismissesKeyboard(.immediately)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.toolbar {
@ -91,6 +82,98 @@ public struct ListEditView: View {
await viewModel.fetchAccounts()
}
}
.task(id: viewModel.searchUserQuery) {
do {
viewModel.isSearching = true
try await Task.sleep(for: .milliseconds(150))
await viewModel.searchUsers()
} catch {}
}
}
}
private var loadingView: some View {
HStack {
Spacer()
ProgressView()
Spacer()
}
.id(UUID())
}
@ViewBuilder
private var searchAccountsView: some View {
if viewModel.isSearching {
loadingView
} else {
ForEach(viewModel.searchedAccounts) { account in
HStack {
AvatarView(account.avatar)
VStack(alignment: .leading) {
EmojiTextApp(.init(stringValue: account.safeDisplayName),
emojis: account.emojis)
.emojiSize(Font.scaledBodyFont.emojiSize)
.emojiBaselineOffset(Font.scaledBodyFont.emojiBaselineOffset)
Text("@\(account.acct)")
.foregroundColor(.gray)
.font(.scaledFootnote)
.lineLimit(1)
}
Spacer()
if let relationship = viewModel.searchedRelationships[account.id] {
if relationship.following {
Toggle("", isOn: .init(get: {
viewModel.accounts.contains(where: { $0.id == account.id })
}, set: { addedToList in
Task {
if addedToList {
await viewModel.add(account: account)
} else {
await viewModel.delete(account: account)
}
}
}))
} else {
FollowButton(viewModel: .init(accountId: account.id,
relationship: relationship,
shouldDisplayNotify: false,
relationshipUpdated: { relationship in
viewModel.searchedRelationships[account.id] = relationship
}))
}
}
}
}
}
}
@ViewBuilder
private var listAccountsView: some View {
if viewModel.isLoadingAccounts {
loadingView
} else {
ForEach(viewModel.accounts) { account in
HStack {
AvatarView(account.avatar)
VStack(alignment: .leading) {
EmojiTextApp(.init(stringValue: account.safeDisplayName),
emojis: account.emojis)
.emojiSize(Font.scaledBodyFont.emojiSize)
.emojiBaselineOffset(Font.scaledBodyFont.emojiBaselineOffset)
Text("@\(account.acct)")
.foregroundColor(.gray)
.font(.scaledFootnote)
}
}
}.onDelete { indexes in
if let index = indexes.first {
Task {
let account = viewModel.accounts[index]
await viewModel.delete(account: account)
}
}
}
}
}
}

View file

@ -19,6 +19,11 @@ import Env
var isExclusive: Bool
var isUpdating: Bool = false
var searchUserQuery: String = ""
var searchedAccounts: [Account] = []
var searchedRelationships: [String: Relationship] = [:]
var isSearching: Bool = false
init(list: Models.List) {
self.list = list
@ -58,14 +63,51 @@ import Env
}
}
func add(account: Account) async {
guard let client else { return }
do {
isUpdating = true
let response = try await client.post(endpoint: Lists.updateAccounts(listId: list.id, accounts: [account.id]))
if response?.statusCode == 200 {
accounts.append(account)
}
isUpdating = false
} catch {
isUpdating = false
}
}
func delete(account: Account) async {
guard let client else { return }
do {
isUpdating = true
let response = try await client.delete(endpoint: Lists.updateAccounts(listId: list.id, accounts: [account.id]))
if response?.statusCode == 200 {
accounts.removeAll(where: { $0.id == account.id })
}
} catch {}
isUpdating = false
} catch {
isUpdating = false
}
}
func searchUsers() async {
guard let client, !searchUserQuery.isEmpty else { return }
do {
isSearching = true
let results: SearchResults = try await client.get(endpoint: Search.search(query: searchUserQuery,
type: nil,
offset: nil,
following: nil),
forceVersion: .v2)
let relationships: [Relationship] =
try await client.get(endpoint: Accounts.relationships(ids: results.accounts.map(\.id)))
searchedRelationships = relationships.reduce(into: [String: Relationship]()) {
$0[$1.id] = $1
}
searchedAccounts = results.accounts
isSearching = false
} catch { }
}
}