mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-25 17:51:01 +00:00
Add more lists setttings
This commit is contained in:
parent
d2f7ab1464
commit
2e2a9f5f14
12 changed files with 1665 additions and 98 deletions
|
@ -82,6 +82,9 @@ extension View {
|
||||||
case let .mentionStatusEditor(account, visibility):
|
case let .mentionStatusEditor(account, visibility):
|
||||||
StatusEditorView(mode: .mention(account: account, visibility: visibility))
|
StatusEditorView(mode: .mention(account: account, visibility: visibility))
|
||||||
.withEnvironments()
|
.withEnvironments()
|
||||||
|
case .listCreate:
|
||||||
|
ListCreateView()
|
||||||
|
.withEnvironments()
|
||||||
case let .listEdit(list):
|
case let .listEdit(list):
|
||||||
ListEditView(list: list)
|
ListEditView(list: list)
|
||||||
.withEnvironments()
|
.withEnvironments()
|
||||||
|
|
|
@ -143,6 +143,11 @@ struct TimelineTab: View {
|
||||||
Label(list.title, systemImage: "list.bullet")
|
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
|
@ -22,8 +22,6 @@ public struct AccountDetailView: View {
|
||||||
|
|
||||||
@State private var viewModel: AccountDetailViewModel
|
@State private var viewModel: AccountDetailViewModel
|
||||||
@State private var isCurrentUser: Bool = false
|
@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 showBlockConfirmation: Bool = false
|
||||||
|
|
||||||
@State private var isEditingAccount: Bool = false
|
@State private var isEditingAccount: Bool = false
|
||||||
|
@ -262,7 +260,7 @@ public struct AccountDetailView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button("account.list.create") {
|
Button("account.list.create") {
|
||||||
isCreateListAlertPresented = true
|
routerPath.presentedSheet = .listCreate
|
||||||
}
|
}
|
||||||
.tint(theme.tintColor)
|
.tint(theme.tintColor)
|
||||||
.buttonStyle(.borderless)
|
.buttonStyle(.borderless)
|
||||||
|
@ -271,23 +269,6 @@ public struct AccountDetailView: View {
|
||||||
.task {
|
.task {
|
||||||
await currentAccount.fetchLists()
|
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
|
@ViewBuilder
|
||||||
|
|
|
@ -88,14 +88,6 @@ import Observation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
public func deleteList(list: Models.List) async {
|
||||||
guard let client else { return }
|
guard let client else { return }
|
||||||
lists.removeAll(where: { $0.id == list.id })
|
lists.removeAll(where: { $0.id == list.id })
|
||||||
|
|
|
@ -42,6 +42,7 @@ public enum SheetDestination: Identifiable {
|
||||||
case replyToStatusEditor(status: Status)
|
case replyToStatusEditor(status: Status)
|
||||||
case quoteStatusEditor(status: Status)
|
case quoteStatusEditor(status: Status)
|
||||||
case mentionStatusEditor(account: Account, visibility: Models.Visibility)
|
case mentionStatusEditor(account: Account, visibility: Models.Visibility)
|
||||||
|
case listCreate
|
||||||
case listEdit(list: Models.List)
|
case listEdit(list: Models.List)
|
||||||
case listAddAccount(account: Account)
|
case listAddAccount(account: Account)
|
||||||
case addAccount
|
case addAccount
|
||||||
|
@ -59,6 +60,8 @@ public enum SheetDestination: Identifiable {
|
||||||
case .editStatusEditor, .newStatusEditor, .replyToStatusEditor, .quoteStatusEditor,
|
case .editStatusEditor, .newStatusEditor, .replyToStatusEditor, .quoteStatusEditor,
|
||||||
.mentionStatusEditor, .settings, .accountPushNotficationsSettings:
|
.mentionStatusEditor, .settings, .accountPushNotficationsSettings:
|
||||||
"statusEditor"
|
"statusEditor"
|
||||||
|
case .listCreate:
|
||||||
|
"listCreate"
|
||||||
case .listEdit:
|
case .listEdit:
|
||||||
"listEdit"
|
"listEdit"
|
||||||
case .listAddAccount:
|
case .listAddAccount:
|
||||||
|
|
|
@ -12,9 +12,6 @@ public struct ListAddAccountView: View {
|
||||||
@Environment(CurrentAccount.self) private var currentAccount
|
@Environment(CurrentAccount.self) private var currentAccount
|
||||||
@State private var viewModel: ListAddAccountViewModel
|
@State private var viewModel: ListAddAccountViewModel
|
||||||
|
|
||||||
@State private var isCreateListAlertPresented: Bool = false
|
|
||||||
@State private var createListTitle: String = ""
|
|
||||||
|
|
||||||
public init(account: Account) {
|
public init(account: Account) {
|
||||||
_viewModel = .init(initialValue: .init(account: account))
|
_viewModel = .init(initialValue: .init(account: account))
|
||||||
}
|
}
|
||||||
|
@ -40,10 +37,6 @@ public struct ListAddAccountView: View {
|
||||||
}
|
}
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
}
|
}
|
||||||
Button("lists.create") {
|
|
||||||
isCreateListAlertPresented = true
|
|
||||||
}
|
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
|
||||||
}
|
}
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.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 {
|
.task {
|
||||||
viewModel.client = client
|
viewModel.client = client
|
||||||
|
|
65
Packages/Lists/Sources/Lists/Create/ListCreateView.swift
Normal file
65
Packages/Lists/Sources/Lists/Create/ListCreateView.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,29 @@ public struct ListEditView: View {
|
||||||
|
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
NavigationStack {
|
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") {
|
Section("lists.edit.users-in-list") {
|
||||||
if viewModel.isLoadingAccounts {
|
if viewModel.isLoadingAccounts {
|
||||||
HStack {
|
HStack {
|
||||||
|
@ -26,7 +48,6 @@ public struct ListEditView: View {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
|
||||||
} else {
|
} else {
|
||||||
ForEach(viewModel.accounts) { account in
|
ForEach(viewModel.accounts) { account in
|
||||||
HStack {
|
HStack {
|
||||||
|
@ -41,7 +62,6 @@ public struct ListEditView: View {
|
||||||
.font(.scaledFootnote)
|
.font(.scaledFootnote)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.listRowBackground(theme.primaryBackgroundColor)
|
|
||||||
}.onDelete { indexes in
|
}.onDelete { indexes in
|
||||||
if let index = indexes.first {
|
if let index = indexes.first {
|
||||||
Task {
|
Task {
|
||||||
|
@ -52,6 +72,7 @@ public struct ListEditView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.listRowBackground(theme.primaryBackgroundColor)
|
||||||
}
|
}
|
||||||
.scrollContentBackground(.hidden)
|
.scrollContentBackground(.hidden)
|
||||||
.background(theme.secondaryBackgroundColor)
|
.background(theme.secondaryBackgroundColor)
|
||||||
|
|
|
@ -3,18 +3,28 @@ import Models
|
||||||
import Network
|
import Network
|
||||||
import Observation
|
import Observation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import Env
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
@Observable public class ListEditViewModel {
|
@Observable public class ListEditViewModel {
|
||||||
let list: Models.List
|
var list: Models.List
|
||||||
|
|
||||||
var client: Client?
|
var client: Client?
|
||||||
|
|
||||||
var isLoadingAccounts: Bool = true
|
var isLoadingAccounts: Bool = true
|
||||||
var accounts: [Account] = []
|
var accounts: [Account] = []
|
||||||
|
|
||||||
|
var title: String
|
||||||
|
var repliesPolicy: Models.List.RepliesPolicy
|
||||||
|
var isExclusive: Bool
|
||||||
|
|
||||||
|
var isUpdating: Bool = false
|
||||||
|
|
||||||
init(list: Models.List) {
|
init(list: Models.List) {
|
||||||
self.list = list
|
self.list = list
|
||||||
|
self.title = list.title
|
||||||
|
self.repliesPolicy = list.repliesPolicy
|
||||||
|
self.isExclusive = list.exclusive
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchAccounts() async {
|
func fetchAccounts() async {
|
||||||
|
@ -28,6 +38,26 @@ import SwiftUI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
func delete(account: Account) async {
|
||||||
guard let client else { return }
|
guard let client else { return }
|
||||||
do {
|
do {
|
||||||
|
@ -38,3 +68,16 @@ import SwiftUI
|
||||||
} catch {}
|
} 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,16 @@ import Foundation
|
||||||
public struct List: Codable, Identifiable, Equatable, Hashable {
|
public struct List: Codable, Identifiable, Equatable, Hashable {
|
||||||
public let id: String
|
public let id: String
|
||||||
public let title: 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 {}
|
extension List: Sendable {}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import Models
|
||||||
|
|
||||||
public enum Lists: Endpoint {
|
public enum Lists: Endpoint {
|
||||||
case lists
|
case lists
|
||||||
case list(id: String)
|
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 accounts(listId: String)
|
||||||
case updateAccounts(listId: String, accounts: [String])
|
case updateAccounts(listId: String, accounts: [String])
|
||||||
|
|
||||||
|
@ -11,7 +13,7 @@ public enum Lists: Endpoint {
|
||||||
switch self {
|
switch self {
|
||||||
case .lists, .createList:
|
case .lists, .createList:
|
||||||
"lists"
|
"lists"
|
||||||
case let .list(id):
|
case let .list(id), let .updateList(id, _, _, _):
|
||||||
"lists/\(id)"
|
"lists/\(id)"
|
||||||
case let .accounts(listId):
|
case let .accounts(listId):
|
||||||
"lists/\(listId)/accounts"
|
"lists/\(listId)/accounts"
|
||||||
|
@ -24,8 +26,11 @@ public enum Lists: Endpoint {
|
||||||
switch self {
|
switch self {
|
||||||
case .accounts:
|
case .accounts:
|
||||||
return [.init(name: "limit", value: String(0))]
|
return [.init(name: "limit", value: String(0))]
|
||||||
case let .createList(title):
|
case let .createList(title, repliesPolicy, exclusive),
|
||||||
return [.init(name: "title", value: title)]
|
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):
|
case let .updateAccounts(_, accounts):
|
||||||
var params: [URLQueryItem] = []
|
var params: [URLQueryItem] = []
|
||||||
for account in accounts {
|
for account in accounts {
|
||||||
|
|
Loading…
Reference in a new issue