gitbay/Views/Discovery/ProfileEditSheet.swift
100 lines · 3681 bytes
1import SwiftUI
2
3/// `profile set`, or `org profile` for an organization. Description and
4/// website are single lines; links are capped at five, matching the
5/// server. The about text is a file and is edited from the profile
6/// screen, not here.
7struct ProfileEditSheet: View {
8
9 let model: ProfileViewModel
10 let profile: ProfileViewModel.Profile
11
12 @Environment(\.dismiss) private var dismiss
13 @State private var edit: ProfileEdit
14
15 init(model: ProfileViewModel, profile: ProfileViewModel.Profile) {
16 self.model = model
17 self.profile = profile
18 _edit = State(initialValue: ProfileEdit(from: profile))
19 }
20
21 private var localError: String? { edit.validationError }
22
23 var body: some View {
24 NavigationStack {
25 Form {
26 Section("Description") {
27 TextField("Description", text: $edit.description)
28 .accessibilityIdentifier("profile-edit-description")
29 }
30 Section("Website") {
31 TextField("https://example.com", text: $edit.website)
32 .keyboardType(.URL)
33 .autocorrectionDisabled()
34 .textInputAutocapitalization(.never)
35 .accessibilityIdentifier("profile-edit-website")
36 }
37 linksSection
38 if let error = localError ?? model.actionError {
39 Section {
40 GBNotice(error, .gbWarn)
41 }
42 }
43 }
44 .navigationTitle("Edit Profile")
45 .navigationBarTitleDisplayMode(.inline)
46 .toolbar {
47 ToolbarItem(placement: .cancellationAction) {
48 Button("Cancel") { dismiss() }
49 }
50 ToolbarItem(placement: .confirmationAction) {
51 if model.working {
52 ProgressView()
53 } else {
54 Button("Save") { save() }
55 .disabled(localError != nil)
56 .accessibilityIdentifier("profile-edit-save")
57 }
58 }
59 }
60 .interactiveDismissDisabled(model.working)
61 }
62 }
63
64 private var linksSection: some View {
65 Section {
66 ForEach($edit.links) { $link in
67 let index = edit.links.firstIndex { $0.id == link.id } ?? 0
68 VStack(alignment: .leading, spacing: 4) {
69 TextField("Label", text: $link.label)
70 .autocorrectionDisabled()
71 .accessibilityIdentifier("profile-edit-link-label-\(index)")
72 TextField("https://example.com", text: $link.url)
73 .keyboardType(.URL)
74 .autocorrectionDisabled()
75 .textInputAutocapitalization(.never)
76 .accessibilityIdentifier("profile-edit-link-url-\(index)")
77 }
78 }
79 .onDelete { edit.links.remove(atOffsets: $0) }
80 if edit.links.count < 5 {
81 Button {
82 edit.links.append(.init(label: "", url: ""))
83 } label: {
84 Label("Add Link", systemImage: "plus.circle")
85 }
86 .accessibilityIdentifier("profile-edit-add-link")
87 }
88 } header: {
89 Text("Links")
90 } footer: {
91 Text("Up to 5 links.")
92 }
93 }
94
95 private func save() {
96 Task {
97 if await model.saveProfile(edit) { dismiss() }
98 }
99 }
100}