IceCubesApp/Packages/Lists/Sources/Lists/Edit/ListEditView.swift

196 lines
5.8 KiB
Swift
Raw Normal View History

2023-12-18 07:22:59 +00:00
import Account
import DesignSystem
import EmojiText
2023-01-17 10:36:01 +00:00
import Models
import Network
import SwiftUI
2023-09-18 19:03:52 +00:00
@MainActor
public struct ListEditView: View {
@Environment(\.dismiss) private var dismiss
2023-09-18 19:03:52 +00:00
@Environment(Theme.self) private var theme
@Environment(Client.self) private var client
2023-01-17 10:36:01 +00:00
@State private var viewModel: ListEditViewModel
2023-01-17 10:36:01 +00:00
public init(list: Models.List) {
_viewModel = .init(initialValue: .init(list: list))
}
2023-01-17 10:36:01 +00:00
public var body: some View {
NavigationStack {
2023-11-28 08:18:52 +00:00
Form {
Section("lists.edit.settings") {
TextField("list.edit.title", text: $viewModel.title) {
2023-12-18 07:22:59 +00:00
Task { await viewModel.update() }
2023-11-28 08:18:52 +00:00
}
Picker("list.edit.repliesPolicy",
2023-12-18 07:22:59 +00:00
selection: $viewModel.repliesPolicy)
{
2023-11-28 08:18:52 +00:00
ForEach(Models.List.RepliesPolicy.allCases) { policy in
Text(policy.title)
.tag(policy)
}
}
Toggle("list.edit.isExclusive", isOn: $viewModel.isExclusive)
}
2024-02-06 14:17:20 +00:00
#if !os(visionOS)
2023-11-28 08:18:52 +00:00
.listRowBackground(theme.primaryBackgroundColor)
2024-02-06 14:17:20 +00:00
#endif
2023-11-28 08:18:52 +00:00
.disabled(viewModel.isUpdating)
.onChange(of: viewModel.repliesPolicy) { _, _ in
Task { await viewModel.update() }
}
.onChange(of: viewModel.isExclusive) { _, _ in
Task { await viewModel.update() }
}
2023-12-18 07:22:59 +00:00
Section("lists.edit.users-in-list") {
2023-11-28 13:16:04 +00:00
HStack {
TextField("lists.edit.users-search",
text: $viewModel.searchUserQuery)
if !viewModel.searchUserQuery.isEmpty {
Button {
viewModel.searchUserQuery = ""
} label: {
Image(systemName: "xmark.circle")
}
}
}
2023-11-28 13:16:04 +00:00
.id("stableId")
if !viewModel.searchUserQuery.isEmpty {
searchAccountsView
} else {
listAccountsView
}
}
2024-02-06 14:17:20 +00:00
#if !os(visionOS)
2023-11-28 08:18:52 +00:00
.listRowBackground(theme.primaryBackgroundColor)
2024-02-06 14:17:20 +00:00
#endif
2023-11-28 13:16:04 +00:00
.disabled(viewModel.isUpdating)
}
#if !os(visionOS)
2023-11-28 13:16:04 +00:00
.scrollDismissesKeyboard(.immediately)
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
2024-02-06 14:17:20 +00:00
#endif
.toolbar {
ToolbarItem {
Button {
Task {
await viewModel.update()
dismiss()
}
} label: {
if viewModel.isUpdating {
ProgressView()
} else {
Text("action.done")
}
}
}
}
.navigationTitle(viewModel.list.title)
.navigationBarTitleDisplayMode(.inline)
.onAppear {
viewModel.client = client
Task {
await viewModel.fetchAccounts()
}
}
2023-11-28 13:16:04 +00:00
.task(id: viewModel.searchUserQuery) {
do {
viewModel.isSearching = true
try await Task.sleep(for: .milliseconds(150))
await viewModel.searchUsers()
} catch {}
}
}
}
2023-12-18 07:22:59 +00:00
2023-11-28 13:16:04 +00:00
private var loadingView: some View {
HStack {
Spacer()
ProgressView()
Spacer()
}
.id(UUID())
}
2023-12-18 07:22:59 +00:00
2023-11-28 13:16:04 +00:00
@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)
2024-02-05 07:55:24 +00:00
.emojiText.size(Font.scaledBodyFont.emojiSize)
.emojiText.baselineOffset(Font.scaledBodyFont.emojiBaselineOffset)
2023-11-28 13:16:04 +00:00
Text("@\(account.acct)")
2023-12-04 14:49:44 +00:00
.foregroundStyle(.secondary)
2023-11-28 13:16:04 +00:00
.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
2023-12-18 07:22:59 +00:00
viewModel.searchedRelationships[account.id] = relationship
}))
2023-11-28 13:16:04 +00:00
}
}
}
}
}
}
2023-12-18 07:22:59 +00:00
2023-11-28 13:16:04 +00:00
@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)
2024-02-05 07:55:24 +00:00
.emojiText.size(Font.scaledBodyFont.emojiSize)
.emojiText.baselineOffset(Font.scaledBodyFont.emojiBaselineOffset)
2023-11-28 13:16:04 +00:00
Text("@\(account.acct)")
2023-12-04 14:49:44 +00:00
.foregroundStyle(.secondary)
2023-11-28 13:16:04 +00:00
.font(.scaledFootnote)
2023-11-28 13:32:35 +00:00
.lineLimit(1)
2023-11-28 13:16:04 +00:00
}
}
}.onDelete { indexes in
if let index = indexes.first {
Task {
let account = viewModel.accounts[index]
await viewModel.delete(account: account)
}
}
}
}
}
}