Files
simplex-chat/apps/ios/Shared/Views/UserSettings/NetworkAndServers/NewServerView.swift
2026-02-19 10:58:16 +00:00

158 lines
5.8 KiB
Swift

//
// NewServerView.swift
// SimpleX (iOS)
//
// Created by spaced4ndy on 13.11.2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
// Spec: spec/architecture.md
import SwiftUI
import SimpleXChat
struct NewServerView: View {
@Environment(\.dismiss) var dismiss: DismissAction
@EnvironmentObject var theme: AppTheme
@Binding var userServers: [UserOperatorServers]
@Binding var serverErrors: [UserServersError]
@State private var serverToEdit: UserServer = .empty
@State private var showTestFailure = false
@State private var testing = false
@State private var testFailure: ProtocolTestFailure?
var body: some View {
ZStack {
customServer()
if testing {
ProgressView().scaleEffect(2)
}
}
.modifier(BackButton(disabled: Binding.constant(false)) {
addServer(serverToEdit, $userServers, $serverErrors, dismiss)
})
.alert(isPresented: $showTestFailure) {
Alert(
title: Text("Server test failed!"),
message: Text(testFailure?.localizedDescription ?? "")
)
}
}
// TODO Possibly refactor - similar functions in ProtocolServerView
private func customServer() -> some View {
VStack {
let serverAddress = parseServerAddress(serverToEdit.server)
let valid = serverAddress?.valid == true
List {
Section {
TextEditor(text: $serverToEdit.server)
.multilineTextAlignment(.leading)
.autocorrectionDisabled(true)
.autocapitalization(.none)
.allowsTightening(true)
.lineLimit(10)
.frame(height: 144)
.padding(-6)
} header: {
HStack {
Text("Your server address")
.foregroundColor(theme.colors.secondary)
if !valid {
Spacer()
Image(systemName: "exclamationmark.circle").foregroundColor(.red)
}
}
}
useServerSection(valid)
if valid {
Section(header: Text("Add to another device").foregroundColor(theme.colors.secondary)) {
MutableQRCode(uri: $serverToEdit.server, small: true)
.listRowInsets(EdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 12))
}
}
}
}
}
private func useServerSection(_ valid: Bool) -> some View {
Section(header: Text("Use server").foregroundColor(theme.colors.secondary)) {
HStack {
Button("Test server") {
testing = true
serverToEdit.tested = nil
Task {
if let f = await testServerConnection(server: $serverToEdit) {
showTestFailure = true
testFailure = f
}
await MainActor.run { testing = false }
}
}
.disabled(!valid || testing)
Spacer()
showTestStatus(server: serverToEdit)
}
Toggle("Use for new connections", isOn: $serverToEdit.enabled)
}
}
}
func serverProtocolAndOperator(_ server: UserServer, _ userServers: [UserOperatorServers]) -> (ServerProtocol, ServerOperator?)? {
if let serverAddress = parseServerAddress(server.server) {
let serverProtocol = serverAddress.serverProtocol
let hostnames = serverAddress.hostnames
let matchingOperator = userServers.compactMap { $0.operator }.first { op in
op.serverDomains.contains { domain in
hostnames.contains { hostname in
hostname.hasSuffix(domain)
}
}
}
return (serverProtocol, matchingOperator)
} else {
return nil
}
}
func addServer(
_ server: UserServer,
_ userServers: Binding<[UserOperatorServers]>,
_ serverErrors: Binding<[UserServersError]>,
_ dismiss: DismissAction
) {
if let (serverProtocol, matchingOperator) = serverProtocolAndOperator(server, userServers.wrappedValue) {
if let i = userServers.wrappedValue.firstIndex(where: { $0.operator?.operatorId == matchingOperator?.operatorId }) {
switch serverProtocol {
case .smp: userServers[i].wrappedValue.smpServers.append(server)
case .xftp: userServers[i].wrappedValue.xftpServers.append(server)
}
validateServers_(userServers, serverErrors)
dismiss()
if let op = matchingOperator {
showAlert(
NSLocalizedString("Operator server", comment: "alert title"),
message: String.localizedStringWithFormat(NSLocalizedString("Server added to operator %@.", comment: "alert message"), op.tradeName)
)
}
} else { // Shouldn't happen
dismiss()
showAlert(NSLocalizedString("Error adding server", comment: "alert title"))
}
} else {
dismiss()
if server.server.trimmingCharacters(in: .whitespaces) != "" {
showAlert(
NSLocalizedString("Invalid server address!", comment: "alert title"),
message: NSLocalizedString("Check server address and try again.", comment: "alert title")
)
}
}
}
#Preview {
NewServerView(
userServers: Binding.constant([UserOperatorServers.sampleDataNilOperator]),
serverErrors: Binding.constant([])
)
}