1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import SwiftUI
struct ReportView: View {
@Environment(AuthManager.self) private var auth
@Environment(\.dismiss) private var dismiss
let client: XRPCClient
let subjectUri: String
let subjectCid: String
@Environment(LabelDefinitionsCache.self) private var labelDefsCache
@State private var selectedLabel = ""
private var labelDefs: [LabelDefinition] {
labelDefsCache.definitions
}
@State private var reason = ""
@State private var isSubmitting = false
@State private var isSubmitted = false
@State private var error: String?
var body: some View {
NavigationStack {
Group {
if isSubmitted {
ContentUnavailableView(
"Report submitted",
systemImage: "checkmark.circle",
description: Text("Thank you. Your report has been submitted for review.")
)
} else {
Form {
Section("Category") {
Picker("Category", selection: $selectedLabel) {
ForEach(labelDefs) { def in
Text(def.displayName).tag(def.identifier)
}
}
.pickerStyle(.inline)
.labelsHidden()
.onAppear {
if selectedLabel.isEmpty {
selectedLabel = labelDefs.first?.identifier ?? ""
}
}
}
Section("Details (optional)") {
TextField("Provide additional context...", text: $reason, axis: .vertical)
.lineLimit(3 ... 6)
}
if let error {
Section {
Text(error)
.foregroundStyle(.red)
.font(.subheadline)
}
}
}
}
}
.navigationTitle(isSubmitted ? "" : "Report content")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button(isSubmitted ? "Done" : "Cancel") {
dismiss()
}
}
if !isSubmitted {
ToolbarItem(placement: .confirmationAction) {
Button("Submit") {
Task { await submit() }
}
.disabled(selectedLabel.isEmpty || isSubmitting)
}
}
}
}
}
private func submit() async {
guard !selectedLabel.isEmpty, !isSubmitting else { return }
isSubmitting = true
error = nil
do {
try await client.createReport(
subjectUri: subjectUri,
subjectCid: subjectCid,
label: selectedLabel,
reason: reason.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? nil : reason.trimmingCharacters(in: .whitespacesAndNewlines),
auth: auth.authContext()
)
isSubmitted = true
} catch {
self.error = "Failed to submit report. Please try again."
}
isSubmitting = false
}
}
#Preview {
ReportView(
client: .preview,
subjectUri: "at://did:plc:preview/social.grain.gallery/r1",
subjectCid: "cid"
)
.previewEnvironments()
}