PostCreateView.swift (3617B)
1 import SwiftUI 2 3 struct PostCreateView: View { 4 @EnvironmentObject private var app: AppState 5 @State private var text: String = "" 6 @State private var isPosting = false 7 @State private var resultMessage: String? 8 @State private var showResult = false 9 @FocusState private var focused: Bool 10 11 private var isConnected: Bool { app.relayConnectedCount > 0 } 12 private var canPost: Bool { 13 isConnected && !isPosting && !text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty 14 } 15 16 var body: some View { 17 Form { 18 Section { 19 ZStack(alignment: .topLeading) { 20 TextEditor(text: $text) 21 .frame(minHeight: 160) 22 .focused($focused) 23 .submitLabel(.send) 24 if text.isEmpty { 25 Text("What's happening?") 26 .foregroundStyle(.secondary) 27 .padding(.top, 8) 28 .padding(.leading, 5) 29 .allowsHitTesting(false) 30 } 31 } 32 HStack { 33 Text("\(text.count)") 34 .font(.footnote) 35 .foregroundStyle(.secondary) 36 Spacer() 37 Button { 38 post() 39 } label: { 40 if isPosting { 41 ProgressView() 42 } else { 43 Text("Post") 44 .fontWeight(.semibold) 45 } 46 } 47 .buttonStyle(.borderedProminent) 48 .disabled(!canPost) 49 } 50 } footer: { 51 VStack(alignment: .leading, spacing: 4) { 52 if !isConnected { 53 Text("No relays connected. Configure and connect to post.") 54 .foregroundStyle(.red) 55 } 56 if let e = app.relayLastError { 57 Text(e).foregroundStyle(.secondary) 58 } 59 } 60 } 61 } 62 .listStyle(.insetGrouped) 63 .inlineNavigationTitle("Compose") 64 .toolbar { 65 ToolbarItemGroup(placement: .keyboard) { 66 Spacer() 67 Button("Done") { focused = false } 68 } 69 } 70 .alert("Post Result", isPresented: $showResult) { 71 Button("OK", role: .cancel) { } 72 } message: { 73 Text(resultMessage ?? "") 74 } 75 .onAppear { focused = true } 76 } 77 78 private func post() { 79 guard let service = app.runtimeService else { return } 80 let content = text.trimmingCharacters(in: .whitespacesAndNewlines) 81 guard !content.isEmpty else { return } 82 isPosting = true 83 resultMessage = nil 84 Task { 85 do { 86 let id = try await service.nostrPostTextNote(content: content) 87 await MainActor.run { 88 resultMessage = "Posted kind:1 event: \(id.rawValue)" 89 showResult = true 90 text = "" 91 isPosting = false 92 app.refresh() 93 } 94 } catch { 95 await MainActor.run { 96 resultMessage = "Failed to post: \(error.fieldRuntimeMessage)" 97 showResult = true 98 isPosting = false 99 } 100 } 101 } 102 } 103 }