Add more lists setttings

This commit is contained in:
Thomas Ricouard 2023-11-28 09:18:52 +01:00
parent d2f7ab1464
commit 2e2a9f5f14
12 changed files with 1665 additions and 98 deletions

View file

@ -82,6 +82,9 @@ extension View {
case let .mentionStatusEditor(account, visibility):
StatusEditorView(mode: .mention(account: account, visibility: visibility))
.withEnvironments()
case .listCreate:
ListCreateView()
.withEnvironments()
case let .listEdit(list):
ListEditView(list: list)
.withEnvironments()

View file

@ -143,6 +143,11 @@ struct TimelineTab: View {
Label(list.title, systemImage: "list.bullet")
}
}
Button {
routerPath.presentedSheet = .listCreate
} label: {
Label("account.list.create", systemImage: "plus")
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -22,8 +22,6 @@ public struct AccountDetailView: View {
@State private var viewModel: AccountDetailViewModel
@State private var isCurrentUser: Bool = false
@State private var isCreateListAlertPresented: Bool = false
@State private var createListTitle: String = ""
@State private var showBlockConfirmation: Bool = false
@State private var isEditingAccount: Bool = false
@ -262,7 +260,7 @@ public struct AccountDetailView: View {
}
}
Button("account.list.create") {
isCreateListAlertPresented = true
routerPath.presentedSheet = .listCreate
}
.tint(theme.tintColor)
.buttonStyle(.borderless)
@ -271,23 +269,6 @@ public struct AccountDetailView: View {
.task {
await currentAccount.fetchLists()
}
.alert("account.list.create", isPresented: $isCreateListAlertPresented) {
TextField("account.list.name", text: $createListTitle)
Button("action.cancel") {
isCreateListAlertPresented = false
createListTitle = ""
}
Button("account.list.create.confirm") {
guard !createListTitle.isEmpty else { return }
isCreateListAlertPresented = false
Task {
await currentAccount.createList(title: createListTitle)
createListTitle = ""
}
}
} message: {
Text("account.list.create.description")
}
}
@ViewBuilder

View file

@ -87,15 +87,7 @@ import Observation
tags = []
}
}
public func createList(title: String) async {
guard let client else { return }
do {
let list: Models.List = try await client.post(endpoint: Lists.createList(title: title))
lists.append(list)
} catch {}
}
public func deleteList(list: Models.List) async {
guard let client else { return }
lists.removeAll(where: { $0.id == list.id })

View file

@ -42,6 +42,7 @@ public enum SheetDestination: Identifiable {
case replyToStatusEditor(status: Status)
case quoteStatusEditor(status: Status)
case mentionStatusEditor(account: Account, visibility: Models.Visibility)
case listCreate
case listEdit(list: Models.List)
case listAddAccount(account: Account)
case addAccount
@ -59,6 +60,8 @@ public enum SheetDestination: Identifiable {
case .editStatusEditor, .newStatusEditor, .replyToStatusEditor, .quoteStatusEditor,
.mentionStatusEditor, .settings, .accountPushNotficationsSettings:
"statusEditor"
case .listCreate:
"listCreate"
case .listEdit:
"listEdit"
case .listAddAccount:

View file

@ -12,9 +12,6 @@ public struct ListAddAccountView: View {
@Environment(CurrentAccount.self) private var currentAccount
@State private var viewModel: ListAddAccountViewModel
@State private var isCreateListAlertPresented: Bool = false
@State private var createListTitle: String = ""
public init(account: Account) {
_viewModel = .init(initialValue: .init(account: account))
}
@ -40,10 +37,6 @@ public struct ListAddAccountView: View {
}
.listRowBackground(theme.primaryBackgroundColor)
}
Button("lists.create") {
isCreateListAlertPresented = true
}
.listRowBackground(theme.primaryBackgroundColor)
}
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
@ -56,23 +49,6 @@ public struct ListAddAccountView: View {
}
}
}
.alert("lists.create", isPresented: $isCreateListAlertPresented) {
TextField("lists.name", text: $createListTitle)
Button("action.cancel") {
isCreateListAlertPresented = false
createListTitle = ""
}
Button("lists.create.confirm") {
guard !createListTitle.isEmpty else { return }
isCreateListAlertPresented = false
Task {
await currentAccount.createList(title: createListTitle)
createListTitle = ""
}
}
} message: {
Text("lists.name.message")
}
}
.task {
viewModel.client = client

View file

@ -0,0 +1,65 @@
import DesignSystem
import EmojiText
import Models
import Network
import SwiftUI
import Env
@MainActor
public struct ListCreateView: View {
@Environment(\.dismiss) private var dismiss
@Environment(Theme.self) private var theme
@Environment(Client.self) private var client
@Environment(CurrentAccount.self) private var currentAccount
@State private var title = ""
@State private var repliesPolicy: Models.List.RepliesPolicy = .list
@State private var isExclusive: Bool = false
@State private var isSaving: Bool = false
public init() { }
public var body: some View {
NavigationStack {
Form {
Section("lists.edit.settings") {
TextField("list.edit.title", text: $title)
Picker("list.edit.repliesPolicy",
selection: $repliesPolicy) {
ForEach(Models.List.RepliesPolicy.allCases) { policy in
Text(policy.title)
.tag(policy)
}
}
Toggle("list.edit.isExclusive", isOn: $isExclusive)
}
.listRowBackground(theme.primaryBackgroundColor)
}
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.toolbar {
ToolbarItem {
Button {
Task {
isSaving = true
let _: Models.List = try await client.post(endpoint: Lists.createList(title: title,
repliesPolicy: repliesPolicy,
exclusive: isExclusive ))
await currentAccount.fetchLists()
isSaving = false
dismiss()
}
} label: {
if isSaving {
ProgressView()
} else {
Text("lists.create.confirm")
}
}
}
}
.navigationTitle(title)
.navigationBarTitleDisplayMode(.inline)
}
}
}

View file

@ -18,7 +18,29 @@ public struct ListEditView: View {
public var body: some View {
NavigationStack {
List {
Form {
Section("lists.edit.settings") {
TextField("list.edit.title", text: $viewModel.title) {
Task { await viewModel.update() }
}
Picker("list.edit.repliesPolicy",
selection: $viewModel.repliesPolicy) {
ForEach(Models.List.RepliesPolicy.allCases) { policy in
Text(policy.title)
.tag(policy)
}
}
Toggle("list.edit.isExclusive", isOn: $viewModel.isExclusive)
}
.listRowBackground(theme.primaryBackgroundColor)
.disabled(viewModel.isUpdating)
.onChange(of: viewModel.repliesPolicy) { _, _ in
Task { await viewModel.update() }
}
.onChange(of: viewModel.isExclusive) { _, _ in
Task { await viewModel.update() }
}
Section("lists.edit.users-in-list") {
if viewModel.isLoadingAccounts {
HStack {
@ -26,7 +48,6 @@ public struct ListEditView: View {
ProgressView()
Spacer()
}
.listRowBackground(theme.primaryBackgroundColor)
} else {
ForEach(viewModel.accounts) { account in
HStack {
@ -41,7 +62,6 @@ public struct ListEditView: View {
.font(.scaledFootnote)
}
}
.listRowBackground(theme.primaryBackgroundColor)
}.onDelete { indexes in
if let index = indexes.first {
Task {
@ -52,6 +72,7 @@ public struct ListEditView: View {
}
}
}
.listRowBackground(theme.primaryBackgroundColor)
}
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)

View file

@ -3,18 +3,28 @@ import Models
import Network
import Observation
import SwiftUI
import Env
@MainActor
@Observable public class ListEditViewModel {
let list: Models.List
var list: Models.List
var client: Client?
var isLoadingAccounts: Bool = true
var accounts: [Account] = []
var title: String
var repliesPolicy: Models.List.RepliesPolicy
var isExclusive: Bool
var isUpdating: Bool = false
init(list: Models.List) {
self.list = list
self.title = list.title
self.repliesPolicy = list.repliesPolicy
self.isExclusive = list.exclusive
}
func fetchAccounts() async {
@ -27,7 +37,27 @@ import SwiftUI
isLoadingAccounts = false
}
}
func update() async {
guard let client else { return }
do {
isUpdating = true
let list: Models.List = try await client.put(endpoint:
Lists.updateList(id: list.id,
title: title,
repliesPolicy: repliesPolicy,
exclusive: isExclusive ))
self.list = list
self.title = list.title
self.repliesPolicy = list.repliesPolicy
self.isExclusive = list.exclusive
self.isUpdating = false
await CurrentAccount.shared.fetchLists()
} catch {
isUpdating = false
}
}
func delete(account: Account) async {
guard let client else { return }
do {
@ -38,3 +68,16 @@ import SwiftUI
} catch {}
}
}
extension Models.List.RepliesPolicy {
var title: LocalizedStringKey {
switch self {
case .followed:
return "list.repliesPolicy.followed"
case .list:
return "list.repliesPolicy.list"
case .none:
return "list.repliesPolicy.none"
}
}
}

View file

@ -3,7 +3,16 @@ import Foundation
public struct List: Codable, Identifiable, Equatable, Hashable {
public let id: String
public let title: String
public let repliesPolicy: String
public let repliesPolicy: RepliesPolicy
public let exclusive: Bool
public enum RepliesPolicy: String, Sendable, Codable, CaseIterable, Identifiable {
public var id: String {
rawValue
}
case followed, list, `none`
}
}
extension List: Sendable {}

View file

@ -1,9 +1,11 @@
import Foundation
import Models
public enum Lists: Endpoint {
case lists
case list(id: String)
case createList(title: String)
case createList(title: String, repliesPolicy: List.RepliesPolicy, exclusive: Bool)
case updateList(id: String, title: String, repliesPolicy: List.RepliesPolicy, exclusive: Bool)
case accounts(listId: String)
case updateAccounts(listId: String, accounts: [String])
@ -11,7 +13,7 @@ public enum Lists: Endpoint {
switch self {
case .lists, .createList:
"lists"
case let .list(id):
case let .list(id), let .updateList(id, _, _, _):
"lists/\(id)"
case let .accounts(listId):
"lists/\(listId)/accounts"
@ -24,8 +26,11 @@ public enum Lists: Endpoint {
switch self {
case .accounts:
return [.init(name: "limit", value: String(0))]
case let .createList(title):
return [.init(name: "title", value: title)]
case let .createList(title, repliesPolicy, exclusive),
let .updateList(_, title, repliesPolicy, exclusive):
return [.init(name: "title", value: title),
.init(name: "replies_policy", value: repliesPolicy.rawValue),
.init(name: "exclusive", value: exclusive ? "true" : "false")]
case let .updateAccounts(_, accounts):
var params: [URLQueryItem] = []
for account in accounts {