import SwiftUI struct AddContactOrChannelScreen: View { @Environment(\.router) var router @EnvironmentObject var clientsStore: ClientsStore @EnvironmentObject var rostersStore: RostersStore enum Field { case account case contact } @FocusState private var focus: Field? @State private var ownerCredentials: Credentials? @State private var contactJID: String = "" var body: some View { ZStack { // Background color Color.Material.Background.light .ignoresSafeArea() // Content VStack(spacing: 0) { // Header SharedNavigationBar( leftButton: .init( image: Image(systemName: "xmark"), action: { router.dismissScreen() } ), centerText: .init(text: L10n.Contacts.Add.title), rightButton: .init( image: Image(systemName: "plus.viewfinder"), action: { print("Scan QR-code") } ) ) // Content VStack(spacing: 16) { // Explanation text Text(L10n.Contacts.Add.explanation) .font(.body3) .foregroundColor(.Material.Shape.separator) .multilineTextAlignment(.center) .padding(.top, 16) // Account selector HStack(spacing: 0) { Text("Use account:") .font(.body2) .foregroundColor(.Material.Text.main) .frame(alignment: .leading) Spacer() } UniversalInputCollection.DropDownMenu( prompt: "Use account", elements: activeClientsCredentials, selected: $ownerCredentials, focus: $focus, fieldType: .account ) // Contact text input HStack(spacing: 0) { Text("Contact JID:") .font(.body2) .foregroundColor(.Material.Text.main) .frame(alignment: .leading) Spacer() } UniversalInputCollection.TextField( prompt: "Contact or channel JID", text: $contactJID, focus: $focus, fieldType: .contact, contentType: .emailAddress, keyboardType: .emailAddress, submitLabel: .done, action: { focus = .account } ) // Save button Button { Task { await save() } } label: { Text(L10n.Global.save) } .buttonStyle(PrimaryButtonStyle()) .disabled(!inputValid) .padding(.top) Spacer() } .padding(.horizontal, 32) } } .onAppear { if let exists = activeClientsCredentials.first { ownerCredentials = exists } } } private var activeClientsCredentials: [Credentials] { clientsStore.clients .filter { $0.state != .disabled } .map { $0.credentials } } private var inputValid: Bool { ownerCredentials != nil && !contactJID.isEmpty && UniversalInputCollection.Validators.isEmail(contactJID) } private func save() async { guard let ownerCredentials = ownerCredentials else { return } router.showModal { LoadingScreen() } defer { router.dismissModal() } let result = await rostersStore.addRoster(ownerCredentials, contactJID: contactJID, name: nil, groups: []) switch result { case .success: router.dismissScreen() case .connectionError: showErrorAlert(subtitle: L10n.Contacts.Add.connectionError) case .serverError: showErrorAlert(subtitle: L10n.Contacts.Add.serverError) } } private func showErrorAlert(subtitle: String) { router.showAlert( .alert, title: L10n.Global.Error.title, subtitle: subtitle ) { Button(L10n.Global.ok, role: .cancel) {} } } }