IceCubesApp/Packages/Account/Sources/Account/Edit/EditAccountView.swift

241 lines
7.2 KiB
Swift
Raw Normal View History

2023-01-17 10:36:01 +00:00
import DesignSystem
2023-12-18 07:22:59 +00:00
import Env
2023-01-10 07:24:05 +00:00
import Models
import Network
2024-01-02 20:16:27 +00:00
import NukeUI
2024-02-14 11:48:14 +00:00
import SwiftUI
2023-01-10 07:24:05 +00:00
2023-09-18 19:03:52 +00:00
@MainActor
public struct EditAccountView: View {
2023-01-10 07:24:05 +00:00
@Environment(\.dismiss) private var dismiss
@Environment(Client.self) private var client
2023-09-18 19:03:52 +00:00
@Environment(Theme.self) private var theme
@Environment(UserPreferences.self) private var userPrefs
2023-01-17 10:36:01 +00:00
@State private var viewModel = EditAccountViewModel()
2024-02-14 11:48:14 +00:00
public init() {}
2023-01-17 10:36:01 +00:00
2023-01-10 07:24:05 +00:00
public var body: some View {
2023-12-18 07:22:59 +00:00
NavigationStack {
2023-01-10 07:24:05 +00:00
Form {
if viewModel.isLoading {
loadingSection
} else {
2024-01-02 20:16:27 +00:00
imagesSection
aboutSection
2023-02-24 06:55:24 +00:00
fieldsSection
2023-01-10 07:24:05 +00:00
postSettingsSection
accountSection
}
}
2023-02-24 08:23:16 +00:00
.environment(\.editMode, .constant(.active))
#if !os(visionOS)
2024-02-14 11:48:14 +00:00
.scrollContentBackground(.hidden)
.background(theme.secondaryBackgroundColor)
.scrollDismissesKeyboard(.immediately)
#endif
2024-02-14 11:48:14 +00:00
.navigationTitle("account.edit.navigation-title")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
toolbarContent
}
.alert("account.edit.error.save.title",
isPresented: $viewModel.saveError,
actions: {
Button("alert.button.ok", action: {})
}, message: { Text("account.edit.error.save.message") })
.task {
viewModel.client = client
await viewModel.fetchAccount()
}
2023-01-10 07:24:05 +00:00
}
}
2023-01-17 10:36:01 +00:00
2023-01-10 07:24:05 +00:00
private var loadingSection: some View {
Section {
HStack {
Spacer()
ProgressView()
Spacer()
}
}
#if !os(visionOS)
2023-01-10 07:24:05 +00:00
.listRowBackground(theme.primaryBackgroundColor)
#endif
2023-01-10 07:24:05 +00:00
}
2024-02-14 11:48:14 +00:00
2024-01-02 20:16:27 +00:00
private var imagesSection: some View {
Section {
ZStack(alignment: .center) {
if let header = viewModel.header {
ZStack(alignment: .topLeading) {
2024-01-02 20:16:27 +00:00
LazyImage(url: header) { state in
if let image = state.image {
image
.resizable()
.aspectRatio(contentMode: .fill)
.frame(height: 150)
.clipShape(RoundedRectangle(cornerRadius: 8))
.clipped()
} else {
RoundedRectangle(cornerRadius: 8)
.foregroundStyle(theme.primaryBackgroundColor)
.frame(height: 150)
}
}
.frame(height: 150)
}
}
if let avatar = viewModel.avatar {
ZStack(alignment: .bottomLeading) {
AvatarView(avatar, config: .account)
Menu {
Button("account.edit.avatar") {
viewModel.isChangingAvatar = true
viewModel.isPhotoPickerPresented = true
}
Button("account.edit.header") {
viewModel.isChangingHeader = true
viewModel.isPhotoPickerPresented = true
}
2024-01-02 20:16:27 +00:00
} label: {
Image(systemName: "photo.badge.plus")
.foregroundStyle(.white)
}
.buttonStyle(.borderedProminent)
.clipShape(Circle())
.offset(x: -8, y: 8)
}
}
}
.overlay {
if viewModel.isChangingAvatar || viewModel.isChangingHeader {
ZStack(alignment: .center) {
RoundedRectangle(cornerRadius: 8)
.foregroundStyle(Color.black.opacity(0.40))
ProgressView()
}
}
}
.listRowInsets(EdgeInsets())
}
.listRowBackground(theme.secondaryBackgroundColor)
.photosPicker(isPresented: $viewModel.isPhotoPickerPresented,
selection: $viewModel.mediaPickers,
maxSelectionCount: 1,
matching: .any(of: [.images]),
photoLibrary: .shared())
}
2023-01-17 10:36:01 +00:00
2023-01-10 07:24:05 +00:00
@ViewBuilder
2024-01-02 20:16:27 +00:00
private var aboutSection: some View {
Section("account.edit.display-name") {
TextField("account.edit.display-name", text: $viewModel.displayName)
2023-01-10 07:24:05 +00:00
}
.listRowBackground(theme.primaryBackgroundColor)
Section("account.edit.about") {
TextField("account.edit.about", text: $viewModel.note, axis: .vertical)
2023-01-10 07:24:05 +00:00
.frame(maxHeight: 150)
}
#if !os(visionOS)
2023-01-10 07:24:05 +00:00
.listRowBackground(theme.primaryBackgroundColor)
#endif
2023-01-10 07:24:05 +00:00
}
2023-01-17 10:36:01 +00:00
2023-01-10 07:24:05 +00:00
private var postSettingsSection: some View {
Section("account.edit.post-settings.section-title") {
if !userPrefs.useInstanceContentSettings {
2023-11-19 07:26:07 +00:00
Text("account.edit.post-settings.content-settings-reference")
}
2023-01-10 07:24:05 +00:00
Picker(selection: $viewModel.postPrivacy) {
ForEach(Models.Visibility.supportDefault, id: \.rawValue) { privacy in
Text(privacy.title).tag(privacy)
}
} label: {
Label("account.edit.post-settings.privacy", systemImage: "lock")
2023-01-10 07:24:05 +00:00
}
.pickerStyle(.menu)
Toggle(isOn: $viewModel.isSensitive) {
Label("account.edit.post-settings.sensitive", systemImage: "eye")
2023-01-10 07:24:05 +00:00
}
}
#if !os(visionOS)
2023-01-10 07:24:05 +00:00
.listRowBackground(theme.primaryBackgroundColor)
#endif
2023-01-10 07:24:05 +00:00
}
2023-01-17 10:36:01 +00:00
2023-01-10 07:24:05 +00:00
private var accountSection: some View {
Section("account.edit.account-settings.section-title") {
2023-01-10 07:24:05 +00:00
Toggle(isOn: $viewModel.isLocked) {
Label("account.edit.account-settings.private", systemImage: "lock")
2023-01-10 07:24:05 +00:00
}
Toggle(isOn: $viewModel.isBot) {
Label("account.edit.account-settings.bot", systemImage: "laptopcomputer.trianglebadge.exclamationmark")
2023-01-10 07:24:05 +00:00
}
Toggle(isOn: $viewModel.isDiscoverable) {
Label("account.edit.account-settings.discoverable", systemImage: "magnifyingglass")
2023-01-10 07:24:05 +00:00
}
}
#if !os(visionOS)
2023-01-10 07:24:05 +00:00
.listRowBackground(theme.primaryBackgroundColor)
#endif
2023-01-10 07:24:05 +00:00
}
2023-02-26 05:45:57 +00:00
2023-02-24 06:55:24 +00:00
private var fieldsSection: some View {
Section("account.edit.metadata-section-title") {
ForEach($viewModel.fields) { $field in
VStack(alignment: .leading) {
TextField("account.edit.metadata-name-placeholder", text: $field.name)
2023-02-24 08:37:23 +00:00
.font(.scaledHeadline)
2023-02-24 06:55:24 +00:00
TextField("account.edit.metadata-value-placeholder", text: $field.value)
2024-02-05 07:55:24 +00:00
.emojiText.size(Font.scaledBodyFont.emojiSize)
.emojiText.baselineOffset(Font.scaledBodyFont.emojiBaselineOffset)
2023-02-24 08:37:23 +00:00
.foregroundColor(theme.tintColor)
2023-02-24 06:55:24 +00:00
}
}
2023-02-24 08:23:16 +00:00
.onMove(perform: { indexSet, newOffset in
viewModel.fields.move(fromOffsets: indexSet, toOffset: newOffset)
})
2023-02-24 06:55:24 +00:00
.onDelete { indexes in
if let index = indexes.first {
viewModel.fields.remove(at: index)
}
}
if viewModel.fields.count < 4 {
Button {
withAnimation {
viewModel.fields.append(.init(name: "", value: ""))
}
} label: {
Text("account.edit.add-metadata-button")
.foregroundColor(theme.tintColor)
}
}
}
#if !os(visionOS)
2023-02-24 06:55:24 +00:00
.listRowBackground(theme.primaryBackgroundColor)
#endif
2023-02-24 06:55:24 +00:00
}
2023-01-17 10:36:01 +00:00
2023-01-10 07:24:05 +00:00
@ToolbarContentBuilder
private var toolbarContent: some ToolbarContent {
2024-01-23 07:13:45 +00:00
CancelToolbarItem()
2023-01-17 10:36:01 +00:00
2023-01-10 07:24:05 +00:00
ToolbarItem(placement: .navigationBarTrailing) {
Button {
Task {
await viewModel.save()
dismiss()
}
} label: {
if viewModel.isSaving {
ProgressView()
} else {
2023-02-22 21:12:10 +00:00
Text("action.save").bold()
2023-01-10 07:24:05 +00:00
}
}
}
}
}