d26b9ab3b6
- Library: NewPostOptions factories for boost/poll/vote/migration/reaction; PostWriter emits new properties; ProfileWriter for header manipulation; OrgSocialPollVote model; RelayClient.fetchPollVotes/fetchGroupList/fetchGroupPostURLs - Tests: ProfileWriterTests (14), SpecialPostTests (17) — 117 total, all passing - App: ComposeView/ViewModel support poll options, poll end date, scheduled posts; PostRowView shows boost confirm, poll with vote bars, reaction/migration/visibility badges, mood; BoostViewModel, EditProfileViewModel with async load, GroupsViewModel; EditProfileView, GroupsView with group post list; ProfileView toolbar with follow/unfollow and edit button, pinned post section; full uploader support across all views; Groups tab in RootView
85 lines
3.1 KiB
Swift
85 lines
3.1 KiB
Swift
import SwiftUI
|
|
|
|
struct EditProfileView: View {
|
|
@Environment(\.dismiss) private var dismiss
|
|
@State private var viewModel = EditProfileViewModel()
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
Form {
|
|
Section("Identity") {
|
|
field("Nick", placeholder: "@username", text: $viewModel.nick)
|
|
field("Name / Title", placeholder: "My Feed", text: $viewModel.title)
|
|
field("Bio", placeholder: "A short description", text: $viewModel.description)
|
|
}
|
|
|
|
Section("Appearance") {
|
|
field("Avatar URL", placeholder: "https://example.com/avatar.jpg", text: $viewModel.avatar)
|
|
}
|
|
|
|
Section("Info") {
|
|
field("Location", placeholder: "City, Country", text: $viewModel.location)
|
|
field("Birthday", placeholder: "YYYY-MM-DD", text: $viewModel.birthday)
|
|
field("Languages", placeholder: "en es", text: $viewModel.language)
|
|
}
|
|
|
|
Section {
|
|
TextEditor(text: $viewModel.linksText)
|
|
.frame(minHeight: 60)
|
|
} header: {
|
|
Text("Links (one URL per line)")
|
|
}
|
|
|
|
Section {
|
|
TextEditor(text: $viewModel.contactsText)
|
|
.frame(minHeight: 60)
|
|
} header: {
|
|
Text("Contacts (one per line, e.g. mailto:)")
|
|
}
|
|
|
|
if let error = viewModel.errorMessage {
|
|
Section {
|
|
Label(error, systemImage: "exclamationmark.triangle.fill")
|
|
.foregroundStyle(.red).font(.subheadline)
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle("Edit Profile")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.task { await viewModel.load() }
|
|
.toolbar {
|
|
ToolbarItem(placement: .cancellationAction) {
|
|
Button("Cancel") { dismiss() }
|
|
}
|
|
ToolbarItem(placement: .confirmationAction) {
|
|
if viewModel.isSaving {
|
|
ProgressView()
|
|
} else {
|
|
Button("Save") {
|
|
Task {
|
|
await viewModel.save()
|
|
if viewModel.saved { dismiss() }
|
|
}
|
|
}
|
|
.fontWeight(.semibold)
|
|
.disabled(viewModel.nick.isEmpty || viewModel.title.isEmpty)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func field(_ label: String, placeholder: String, text: Binding<String>) -> some View {
|
|
HStack {
|
|
Text(label)
|
|
Spacer()
|
|
TextField(placeholder, text: text)
|
|
.autocorrectionDisabled()
|
|
.textInputAutocapitalization(.never)
|
|
.multilineTextAlignment(.trailing)
|
|
.foregroundStyle(.secondary)
|
|
.frame(maxWidth: 220)
|
|
}
|
|
}
|
|
}
|