ios: update servers API/UI (#2149)
* ios: update servers API/UI * fix UI * fix
This commit is contained in:
committed by
GitHub
parent
d250e503b0
commit
5ae0afe1fe
@@ -391,36 +391,22 @@ func apiDeleteToken(token: DeviceToken) async throws {
|
||||
try await sendCommandOkResp(.apiDeleteToken(token: token))
|
||||
}
|
||||
|
||||
func getUserProtocolServers(_ p: ServerProtocol) throws -> ([ServerCfg], [String]) {
|
||||
if case .smp = p {
|
||||
return try getUserSMPServers()
|
||||
}
|
||||
throw RuntimeError("not supported")
|
||||
}
|
||||
|
||||
private func getUserSMPServers() throws -> ([ServerCfg], [String]) {
|
||||
let userId = try currentUserId("getUserSMPServers")
|
||||
let r = chatSendCmdSync(.apiGetUserSMPServers(userId: userId))
|
||||
if case let .userSMPServers(_, smpServers, presetServers) = r { return (smpServers, presetServers) }
|
||||
func getUserProtoServers(_ serverProtocol: ServerProtocol) throws -> UserProtoServers {
|
||||
let userId = try currentUserId("getUserProtoServers")
|
||||
let r = chatSendCmdSync(.apiGetUserProtoServers(userId: userId, serverProtocol: serverProtocol))
|
||||
if case let .userProtoServers(_, servers) = r { return servers }
|
||||
throw r
|
||||
}
|
||||
|
||||
func setUserProtocolServers(_ p: ServerProtocol, servers: [ServerCfg]) async throws {
|
||||
if case .smp = p {
|
||||
return try await setUserSMPServers(smpServers: servers)
|
||||
}
|
||||
throw RuntimeError("not supported")
|
||||
func setUserProtoServers(_ serverProtocol: ServerProtocol, servers: [ServerCfg]) async throws {
|
||||
let userId = try currentUserId("setUserProtoServers")
|
||||
try await sendCommandOkResp(.apiSetUserProtoServers(userId: userId, serverProtocol: serverProtocol, servers: servers))
|
||||
}
|
||||
|
||||
private func setUserSMPServers(smpServers: [ServerCfg]) async throws {
|
||||
let userId = try currentUserId("setUserSMPServers")
|
||||
try await sendCommandOkResp(.apiSetUserSMPServers(userId: userId, smpServers: smpServers))
|
||||
}
|
||||
|
||||
func testSMPServer(smpServer: String) async throws -> Result<(), SMPTestFailure> {
|
||||
let userId = try currentUserId("testSMPServer")
|
||||
let r = await chatSendCmd(.apiTestSMPServer(userId: userId, smpServer: smpServer))
|
||||
if case let .smpTestResult(_, testFailure) = r {
|
||||
func testProtoServer(server: String) async throws -> Result<(), ProtocolTestFailure> {
|
||||
let userId = try currentUserId("testProtoServer")
|
||||
let r = await chatSendCmd(.apiTestProtoServer(userId: userId, server: server))
|
||||
if case let .serverTestResult(_, _, testFailure) = r {
|
||||
if let t = testFailure {
|
||||
return .failure(t)
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ private enum NetworkAlert: Identifiable {
|
||||
|
||||
struct NetworkAndServers: View {
|
||||
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
|
||||
@AppStorage(GROUP_DEFAULT_XFTP_SEND_ENABLED, store: groupDefaults) private var xftpSendEnabled = false
|
||||
@State private var cfgLoaded = false
|
||||
@State private var currentNetCfg = NetCfg.defaults
|
||||
@State private var netCfg = NetCfg.defaults
|
||||
@@ -43,6 +44,15 @@ struct NetworkAndServers: View {
|
||||
Text("SMP servers")
|
||||
}
|
||||
|
||||
if xftpSendEnabled {
|
||||
NavigationLink {
|
||||
ProtocolServersView(serverProtocol: .xftp)
|
||||
.navigationTitle("Your XFTP servers")
|
||||
} label: {
|
||||
Text("XFTP servers")
|
||||
}
|
||||
}
|
||||
|
||||
Picker("Use .onion hosts", selection: $onionHosts) {
|
||||
ForEach(OnionHosts.values, id: \.self) { Text($0.text) }
|
||||
}
|
||||
@@ -62,7 +72,7 @@ struct NetworkAndServers: View {
|
||||
Text("Advanced network settings")
|
||||
}
|
||||
} header: {
|
||||
Text("Messages")
|
||||
Text("Messages & files")
|
||||
} footer: {
|
||||
Text("Using .onion hosts requires compatible VPN provider.")
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ struct ProtocolServerView: View {
|
||||
@State var serverToEdit: ServerCfg
|
||||
@State private var showTestFailure = false
|
||||
@State private var testing = false
|
||||
@State private var testFailure: SMPTestFailure?
|
||||
@State private var testFailure: ProtocolTestFailure?
|
||||
|
||||
var proto: String { serverProtocol.rawValue.uppercased() }
|
||||
|
||||
@@ -60,7 +60,8 @@ struct ProtocolServerView: View {
|
||||
|
||||
private func customServer() -> some View {
|
||||
VStack {
|
||||
let valid = parseServerAddress(serverToEdit.server)?.valid == true
|
||||
let serverAddress = parseServerAddress(serverToEdit.server)
|
||||
let valid = serverAddress?.valid == true && serverAddress?.serverProtocol == serverProtocol
|
||||
List {
|
||||
Section {
|
||||
TextEditor(text: $serverToEdit.server)
|
||||
@@ -147,18 +148,17 @@ struct BackButton: ViewModifier {
|
||||
}
|
||||
}
|
||||
|
||||
func testServerConnection(server: Binding<ServerCfg>) async -> SMPTestFailure? {
|
||||
func testServerConnection(server: Binding<ServerCfg>) async -> ProtocolTestFailure? {
|
||||
do {
|
||||
let r = try await testSMPServer(smpServer: server.wrappedValue.server)
|
||||
|
||||
switch r {
|
||||
case .success:
|
||||
await MainActor.run { server.wrappedValue.tested = true }
|
||||
return nil
|
||||
case let .failure(f):
|
||||
await MainActor.run { server.wrappedValue.tested = false }
|
||||
return f
|
||||
}
|
||||
let r = try await testProtoServer(server: server.wrappedValue.server)
|
||||
switch r {
|
||||
case .success:
|
||||
await MainActor.run { server.wrappedValue.tested = true }
|
||||
return nil
|
||||
case let .failure(f):
|
||||
await MainActor.run { server.wrappedValue.tested = false }
|
||||
return f
|
||||
}
|
||||
} catch let error {
|
||||
logger.error("testServerConnection \(responseError(error))")
|
||||
await MainActor.run {
|
||||
|
||||
@@ -21,7 +21,8 @@ struct ProtocolServersView: View {
|
||||
@State private var servers: [ServerCfg] = []
|
||||
@State private var selectedServer: String? = nil
|
||||
@State private var showAddServer = false
|
||||
@State private var showScanSMPServer = false
|
||||
@State private var showScanProtoServer = false
|
||||
@State private var justOpened = true
|
||||
@State private var testing = false
|
||||
@State private var alert: ServerAlert? = nil
|
||||
@State private var showSaveDialog = false
|
||||
@@ -38,7 +39,7 @@ struct ProtocolServersView: View {
|
||||
}
|
||||
|
||||
enum ServerAlert: Identifiable {
|
||||
case testsFailed(failures: [String: SMPTestFailure])
|
||||
case testsFailed(failures: [String: ProtocolTestFailure])
|
||||
case error(title: LocalizedStringKey, error: LocalizedStringKey = "")
|
||||
|
||||
var id: String {
|
||||
@@ -87,16 +88,17 @@ struct ProtocolServersView: View {
|
||||
servers.append(ServerCfg.empty)
|
||||
selectedServer = servers.last?.id
|
||||
}
|
||||
Button("Scan server QR code") { showScanSMPServer = true }
|
||||
Button("Scan server QR code") { showScanProtoServer = true }
|
||||
Button("Add preset servers", action: addAllPresets)
|
||||
.disabled(hasAllPresets())
|
||||
}
|
||||
.sheet(isPresented: $showScanSMPServer) {
|
||||
.sheet(isPresented: $showScanProtoServer) {
|
||||
ScanProtocolServer(servers: $servers)
|
||||
}
|
||||
.modifier(BackButton {
|
||||
if saveDisabled {
|
||||
dismiss()
|
||||
justOpened = false
|
||||
} else {
|
||||
showSaveDialog = true
|
||||
}
|
||||
@@ -105,6 +107,7 @@ struct ProtocolServersView: View {
|
||||
Button("Save") {
|
||||
saveServers()
|
||||
dismiss()
|
||||
justOpened = false
|
||||
}
|
||||
Button("Exit without saving") { dismiss() }
|
||||
}
|
||||
@@ -125,8 +128,12 @@ struct ProtocolServersView: View {
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
// this condition is needed to prevent re-setting the servers when exiting single server view
|
||||
if !justOpened { return }
|
||||
do {
|
||||
(currServers, presetServers) = try getUserProtocolServers(serverProtocol)
|
||||
let r = try getUserProtoServers(serverProtocol)
|
||||
currServers = r.protoServers
|
||||
presetServers = r.presetServers
|
||||
servers = currServers
|
||||
} catch let error {
|
||||
alert = .error(
|
||||
@@ -134,6 +141,7 @@ struct ProtocolServersView: View {
|
||||
error: "Error: \(responseError(error))"
|
||||
)
|
||||
}
|
||||
justOpened = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +166,7 @@ struct ProtocolServersView: View {
|
||||
let srv = server.wrappedValue
|
||||
return NavigationLink(tag: srv.id, selection: $selectedServer) {
|
||||
ProtocolServerView(
|
||||
serverProtocol: .smp,
|
||||
serverProtocol: serverProtocol,
|
||||
server: server,
|
||||
serverToEdit: srv
|
||||
)
|
||||
@@ -258,8 +266,8 @@ struct ProtocolServersView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func runServersTest() async -> [String: SMPTestFailure] {
|
||||
var fs: [String: SMPTestFailure] = [:]
|
||||
private func runServersTest() async -> [String: ProtocolTestFailure] {
|
||||
var fs: [String: ProtocolTestFailure] = [:]
|
||||
for i in 0..<servers.count {
|
||||
if servers[i].enabled {
|
||||
if let f = await testServerConnection(server: $servers[i]) {
|
||||
@@ -273,7 +281,7 @@ struct ProtocolServersView: View {
|
||||
func saveServers() {
|
||||
Task {
|
||||
do {
|
||||
try await setUserProtocolServers(serverProtocol, servers: servers)
|
||||
try await setUserProtoServers(serverProtocol, servers: servers)
|
||||
await MainActor.run {
|
||||
currServers = servers
|
||||
editMode?.wrappedValue = .inactive
|
||||
|
||||
@@ -99,11 +99,11 @@
|
||||
5CB346E52868AA7F001FD2EF /* SuspendChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB346E42868AA7F001FD2EF /* SuspendChat.swift */; };
|
||||
5CB346E72868D76D001FD2EF /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB346E62868D76D001FD2EF /* NotificationsView.swift */; };
|
||||
5CB346E92869E8BA001FD2EF /* PushEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */; };
|
||||
5CB6348429DCD8130066AD6B /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CB6347F29DCD8130066AD6B /* libgmpxx.a */; };
|
||||
5CB6348529DCD8130066AD6B /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CB6348029DCD8130066AD6B /* libffi.a */; };
|
||||
5CB6348629DCD8130066AD6B /* libHSsimplex-chat-4.6.1.1-mpt0wL13f7GxVMIrCoj3B-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CB6348129DCD8130066AD6B /* libHSsimplex-chat-4.6.1.1-mpt0wL13f7GxVMIrCoj3B-ghc8.10.7.a */; };
|
||||
5CB6348729DCD8130066AD6B /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CB6348229DCD8130066AD6B /* libgmp.a */; };
|
||||
5CB6348829DCD8130066AD6B /* libHSsimplex-chat-4.6.1.1-mpt0wL13f7GxVMIrCoj3B.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CB6348329DCD8130066AD6B /* libHSsimplex-chat-4.6.1.1-mpt0wL13f7GxVMIrCoj3B.a */; };
|
||||
5CB6348E29DE261C0066AD6B /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CB6348929DE261C0066AD6B /* libgmpxx.a */; };
|
||||
5CB6348F29DE261C0066AD6B /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CB6348A29DE261C0066AD6B /* libffi.a */; };
|
||||
5CB6349029DE261C0066AD6B /* libHSsimplex-chat-4.6.1.1-44qOXuEu6Xh4AokU7t538i.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CB6348B29DE261C0066AD6B /* libHSsimplex-chat-4.6.1.1-44qOXuEu6Xh4AokU7t538i.a */; };
|
||||
5CB6349129DE261C0066AD6B /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CB6348C29DE261C0066AD6B /* libgmp.a */; };
|
||||
5CB6349229DE261C0066AD6B /* libHSsimplex-chat-4.6.1.1-44qOXuEu6Xh4AokU7t538i-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CB6348D29DE261C0066AD6B /* libHSsimplex-chat-4.6.1.1-44qOXuEu6Xh4AokU7t538i-ghc8.10.7.a */; };
|
||||
5CB924D727A8563F00ACCCDD /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924D627A8563F00ACCCDD /* SettingsView.swift */; };
|
||||
5CB924E127A867BA00ACCCDD /* UserProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E027A867BA00ACCCDD /* UserProfile.swift */; };
|
||||
5CB924E427A8683A00ACCCDD /* UserAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E327A8683A00ACCCDD /* UserAddress.swift */; };
|
||||
@@ -355,11 +355,11 @@
|
||||
5CB346E42868AA7F001FD2EF /* SuspendChat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuspendChat.swift; sourceTree = "<group>"; };
|
||||
5CB346E62868D76D001FD2EF /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = "<group>"; };
|
||||
5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushEnvironment.swift; sourceTree = "<group>"; };
|
||||
5CB6347F29DCD8130066AD6B /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5CB6348029DCD8130066AD6B /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5CB6348129DCD8130066AD6B /* libHSsimplex-chat-4.6.1.1-mpt0wL13f7GxVMIrCoj3B-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.6.1.1-mpt0wL13f7GxVMIrCoj3B-ghc8.10.7.a"; sourceTree = "<group>"; };
|
||||
5CB6348229DCD8130066AD6B /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5CB6348329DCD8130066AD6B /* libHSsimplex-chat-4.6.1.1-mpt0wL13f7GxVMIrCoj3B.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.6.1.1-mpt0wL13f7GxVMIrCoj3B.a"; sourceTree = "<group>"; };
|
||||
5CB6348929DE261C0066AD6B /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5CB6348A29DE261C0066AD6B /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5CB6348B29DE261C0066AD6B /* libHSsimplex-chat-4.6.1.1-44qOXuEu6Xh4AokU7t538i.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.6.1.1-44qOXuEu6Xh4AokU7t538i.a"; sourceTree = "<group>"; };
|
||||
5CB6348C29DE261C0066AD6B /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5CB6348D29DE261C0066AD6B /* libHSsimplex-chat-4.6.1.1-44qOXuEu6Xh4AokU7t538i-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.6.1.1-44qOXuEu6Xh4AokU7t538i-ghc8.10.7.a"; sourceTree = "<group>"; };
|
||||
5CB924D627A8563F00ACCCDD /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||
5CB924E027A867BA00ACCCDD /* UserProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfile.swift; sourceTree = "<group>"; };
|
||||
5CB924E327A8683A00ACCCDD /* UserAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddress.swift; sourceTree = "<group>"; };
|
||||
@@ -471,13 +471,13 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5CB6348629DCD8130066AD6B /* libHSsimplex-chat-4.6.1.1-mpt0wL13f7GxVMIrCoj3B-ghc8.10.7.a in Frameworks */,
|
||||
5CB6348829DCD8130066AD6B /* libHSsimplex-chat-4.6.1.1-mpt0wL13f7GxVMIrCoj3B.a in Frameworks */,
|
||||
5CB6348429DCD8130066AD6B /* libgmpxx.a in Frameworks */,
|
||||
5CB6348E29DE261C0066AD6B /* libgmpxx.a in Frameworks */,
|
||||
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
|
||||
5CB6349029DE261C0066AD6B /* libHSsimplex-chat-4.6.1.1-44qOXuEu6Xh4AokU7t538i.a in Frameworks */,
|
||||
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
|
||||
5CB6348729DCD8130066AD6B /* libgmp.a in Frameworks */,
|
||||
5CB6348529DCD8130066AD6B /* libffi.a in Frameworks */,
|
||||
5CB6349129DE261C0066AD6B /* libgmp.a in Frameworks */,
|
||||
5CB6349229DE261C0066AD6B /* libHSsimplex-chat-4.6.1.1-44qOXuEu6Xh4AokU7t538i-ghc8.10.7.a in Frameworks */,
|
||||
5CB6348F29DE261C0066AD6B /* libffi.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -536,11 +536,11 @@
|
||||
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5CB6348029DCD8130066AD6B /* libffi.a */,
|
||||
5CB6348229DCD8130066AD6B /* libgmp.a */,
|
||||
5CB6347F29DCD8130066AD6B /* libgmpxx.a */,
|
||||
5CB6348129DCD8130066AD6B /* libHSsimplex-chat-4.6.1.1-mpt0wL13f7GxVMIrCoj3B-ghc8.10.7.a */,
|
||||
5CB6348329DCD8130066AD6B /* libHSsimplex-chat-4.6.1.1-mpt0wL13f7GxVMIrCoj3B.a */,
|
||||
5CB6348A29DE261C0066AD6B /* libffi.a */,
|
||||
5CB6348C29DE261C0066AD6B /* libgmp.a */,
|
||||
5CB6348929DE261C0066AD6B /* libgmpxx.a */,
|
||||
5CB6348D29DE261C0066AD6B /* libHSsimplex-chat-4.6.1.1-44qOXuEu6Xh4AokU7t538i-ghc8.10.7.a */,
|
||||
5CB6348B29DE261C0066AD6B /* libHSsimplex-chat-4.6.1.1-44qOXuEu6Xh4AokU7t538i.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
|
||||
@@ -57,9 +57,9 @@ public enum ChatCommand {
|
||||
case apiGroupLinkMemberRole(groupId: Int64, memberRole: GroupMemberRole)
|
||||
case apiDeleteGroupLink(groupId: Int64)
|
||||
case apiGetGroupLink(groupId: Int64)
|
||||
case apiGetUserSMPServers(userId: Int64)
|
||||
case apiSetUserSMPServers(userId: Int64, smpServers: [ServerCfg])
|
||||
case apiTestSMPServer(userId: Int64, smpServer: String)
|
||||
case apiGetUserProtoServers(userId: Int64, serverProtocol: ServerProtocol)
|
||||
case apiSetUserProtoServers(userId: Int64, serverProtocol: ServerProtocol, servers: [ServerCfg])
|
||||
case apiTestProtoServer(userId: Int64, server: String)
|
||||
case apiSetChatItemTTL(userId: Int64, seconds: Int64?)
|
||||
case apiGetChatItemTTL(userId: Int64)
|
||||
case apiSetNetworkConfig(networkConfig: NetCfg)
|
||||
@@ -158,9 +158,9 @@ public enum ChatCommand {
|
||||
case let .apiGroupLinkMemberRole(groupId, memberRole): return "/_set link role #\(groupId) \(memberRole)"
|
||||
case let .apiDeleteGroupLink(groupId): return "/_delete link #\(groupId)"
|
||||
case let .apiGetGroupLink(groupId): return "/_get link #\(groupId)"
|
||||
case let .apiGetUserSMPServers(userId): return "/_smp \(userId)"
|
||||
case let .apiSetUserSMPServers(userId, smpServers): return "/_smp \(userId) \(smpServersStr(smpServers: smpServers))"
|
||||
case let .apiTestSMPServer(userId, smpServer): return "/_smp test \(userId) \(smpServer)"
|
||||
case let .apiGetUserProtoServers(userId, serverProtocol): return "/_servers \(userId) \(serverProtocol)"
|
||||
case let .apiSetUserProtoServers(userId, serverProtocol, servers): return "/_servers \(userId) \(serverProtocol) \(protoServersStr(servers))"
|
||||
case let .apiTestProtoServer(userId, server): return "/_server test \(userId) \(server)"
|
||||
case let .apiSetChatItemTTL(userId, seconds): return "/_ttl \(userId) \(chatItemTTLStr(seconds: seconds))"
|
||||
case let .apiGetChatItemTTL(userId): return "/_ttl \(userId)"
|
||||
case let .apiSetNetworkConfig(networkConfig): return "/_network \(encodeJSON(networkConfig))"
|
||||
@@ -260,9 +260,9 @@ public enum ChatCommand {
|
||||
case .apiGroupLinkMemberRole: return "apiGroupLinkMemberRole"
|
||||
case .apiDeleteGroupLink: return "apiDeleteGroupLink"
|
||||
case .apiGetGroupLink: return "apiGetGroupLink"
|
||||
case .apiGetUserSMPServers: return "apiGetUserSMPServers"
|
||||
case .apiSetUserSMPServers: return "apiSetUserSMPServers"
|
||||
case .apiTestSMPServer: return "testSMPServer"
|
||||
case .apiGetUserProtoServers: return "apiGetUserProtoServers"
|
||||
case .apiSetUserProtoServers: return "apiSetUserProtoServers"
|
||||
case .apiTestProtoServer: return "apiTestProtoServer"
|
||||
case .apiSetChatItemTTL: return "apiSetChatItemTTL"
|
||||
case .apiGetChatItemTTL: return "apiGetChatItemTTL"
|
||||
case .apiSetNetworkConfig: return "apiSetNetworkConfig"
|
||||
@@ -313,8 +313,8 @@ public enum ChatCommand {
|
||||
"\(type.rawValue)\(id)"
|
||||
}
|
||||
|
||||
func smpServersStr(smpServers: [ServerCfg]) -> String {
|
||||
smpServers.isEmpty ? "default" : encodeJSON(SMPServersConfig(smpServers: smpServers))
|
||||
func protoServersStr(_ servers: [ServerCfg]) -> String {
|
||||
encodeJSON(ProtoServersConfig(servers: servers))
|
||||
}
|
||||
|
||||
func chatItemTTLStr(seconds: Int64?) -> String {
|
||||
@@ -375,8 +375,8 @@ public enum ChatResponse: Decodable, Error {
|
||||
case chatSuspended
|
||||
case apiChats(user: User, chats: [ChatData])
|
||||
case apiChat(user: User, chat: ChatData)
|
||||
case userSMPServers(user: User, smpServers: [ServerCfg], presetSMPServers: [String])
|
||||
case smpTestResult(user: User, smpTestFailure: SMPTestFailure?)
|
||||
case userProtoServers(user: User, servers: UserProtoServers)
|
||||
case serverTestResult(user: User, testServer: String, testFailure: ProtocolTestFailure?)
|
||||
case chatItemTTL(user: User, chatItemTTL: Int64?)
|
||||
case networkConfig(networkConfig: NetCfg)
|
||||
case contactInfo(user: User, contact: Contact, connectionStats: ConnectionStats, customUserProfile: Profile?)
|
||||
@@ -488,8 +488,8 @@ public enum ChatResponse: Decodable, Error {
|
||||
case .chatSuspended: return "chatSuspended"
|
||||
case .apiChats: return "apiChats"
|
||||
case .apiChat: return "apiChat"
|
||||
case .userSMPServers: return "userSMPServers"
|
||||
case .smpTestResult: return "smpTestResult"
|
||||
case .userProtoServers: return "userProtoServers"
|
||||
case .serverTestResult: return "serverTestResult"
|
||||
case .chatItemTTL: return "chatItemTTL"
|
||||
case .networkConfig: return "networkConfig"
|
||||
case .contactInfo: return "contactInfo"
|
||||
@@ -601,8 +601,8 @@ public enum ChatResponse: Decodable, Error {
|
||||
case .chatSuspended: return noDetails
|
||||
case let .apiChats(u, chats): return withUser(u, String(describing: chats))
|
||||
case let .apiChat(u, chat): return withUser(u, String(describing: chat))
|
||||
case let .userSMPServers(u, smpServers, presetServers): return withUser(u, "smpServers: \(String(describing: smpServers))\npresetServers: \(String(describing: presetServers))")
|
||||
case let .smpTestResult(u, smpTestFailure): return withUser(u, String(describing: smpTestFailure))
|
||||
case let .userProtoServers(u, servers): return withUser(u, "servers: \(String(describing: servers))")
|
||||
case let .serverTestResult(u, server, testFailure): return withUser(u, "server: \(server)\result: \(String(describing: testFailure))")
|
||||
case let .chatItemTTL(u, chatItemTTL): return withUser(u, String(describing: chatItemTTL))
|
||||
case let .networkConfig(networkConfig): return String(describing: networkConfig)
|
||||
case let .contactInfo(u, contact, connectionStats, customUserProfile): return withUser(u, "contact: \(String(describing: contact))\nconnectionStats: \(String(describing: connectionStats))\ncustomUserProfile: \(String(describing: customUserProfile))")
|
||||
@@ -760,10 +760,19 @@ struct SMPServersConfig: Encodable {
|
||||
var smpServers: [ServerCfg]
|
||||
}
|
||||
|
||||
public enum ServerProtocol: String {
|
||||
public enum ServerProtocol: String, Decodable {
|
||||
case smp
|
||||
case xftp
|
||||
case ntf
|
||||
}
|
||||
|
||||
public struct ProtoServersConfig: Codable {
|
||||
public var servers: [ServerCfg]
|
||||
}
|
||||
|
||||
public struct UserProtoServers: Decodable {
|
||||
public var serverProtocol: ServerProtocol
|
||||
public var protoServers: [ServerCfg]
|
||||
public var presetServers: [String]
|
||||
}
|
||||
|
||||
public struct ServerCfg: Identifiable, Equatable, Codable {
|
||||
@@ -830,29 +839,39 @@ public struct ServerCfg: Identifiable, Equatable, Codable {
|
||||
}
|
||||
}
|
||||
|
||||
public enum SMPTestStep: String, Decodable, Equatable {
|
||||
public enum ProtocolTestStep: String, Decodable, Equatable {
|
||||
case connect
|
||||
case disconnect
|
||||
case createQueue
|
||||
case secureQueue
|
||||
case deleteQueue
|
||||
case disconnect
|
||||
case createFile
|
||||
case uploadFile
|
||||
case downloadFile
|
||||
case compareFile
|
||||
case deleteFile
|
||||
|
||||
var text: String {
|
||||
switch self {
|
||||
case .connect: return NSLocalizedString("Connect", comment: "server test step")
|
||||
case .disconnect: return NSLocalizedString("Disconnect", comment: "server test step")
|
||||
case .createQueue: return NSLocalizedString("Create queue", comment: "server test step")
|
||||
case .secureQueue: return NSLocalizedString("Secure queue", comment: "server test step")
|
||||
case .deleteQueue: return NSLocalizedString("Delete queue", comment: "server test step")
|
||||
case .disconnect: return NSLocalizedString("Disconnect", comment: "server test step")
|
||||
case .createFile: return NSLocalizedString("Create file", comment: "server test step")
|
||||
case .uploadFile: return NSLocalizedString("Upload file", comment: "server test step")
|
||||
case .downloadFile: return NSLocalizedString("Download file", comment: "server test step")
|
||||
case .compareFile: return NSLocalizedString("Compare file", comment: "server test step")
|
||||
case .deleteFile: return NSLocalizedString("Delete file", comment: "server test step")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct SMPTestFailure: Decodable, Error, Equatable {
|
||||
var testStep: SMPTestStep
|
||||
public struct ProtocolTestFailure: Decodable, Error, Equatable {
|
||||
var testStep: ProtocolTestStep
|
||||
var testError: AgentErrorType
|
||||
|
||||
public static func == (l: SMPTestFailure, r: SMPTestFailure) -> Bool {
|
||||
public static func == (l: ProtocolTestFailure, r: ProtocolTestFailure) -> Bool {
|
||||
l.testStep == r.testStep
|
||||
}
|
||||
|
||||
@@ -861,6 +880,8 @@ public struct SMPTestFailure: Decodable, Error, Equatable {
|
||||
switch testError {
|
||||
case .SMP(.AUTH):
|
||||
return err + " " + NSLocalizedString("Server requires authorization to create queues, check password", comment: "server test error")
|
||||
case .XFTP(.AUTH):
|
||||
return err + " " + NSLocalizedString("Server requires authorization to upload, check password", comment: "server test error")
|
||||
case .BROKER(_, .NETWORK):
|
||||
return err + " " + NSLocalizedString("Possibly, certificate fingerprint in server address is incorrect", comment: "server test error")
|
||||
default:
|
||||
@@ -870,12 +891,14 @@ public struct SMPTestFailure: Decodable, Error, Equatable {
|
||||
}
|
||||
|
||||
public struct ServerAddress: Decodable {
|
||||
public var serverProtocol: ServerProtocol?
|
||||
public var hostnames: [String]
|
||||
public var port: String
|
||||
public var keyHash: String
|
||||
public var basicAuth: String
|
||||
|
||||
public init(hostnames: [String], port: String, keyHash: String, basicAuth: String = "") {
|
||||
public init(serverProtocol: ServerProtocol?, hostnames: [String], port: String, keyHash: String, basicAuth: String = "") {
|
||||
self.serverProtocol = serverProtocol
|
||||
self.hostnames = hostnames
|
||||
self.port = port
|
||||
self.keyHash = keyHash
|
||||
@@ -883,21 +906,25 @@ public struct ServerAddress: Decodable {
|
||||
}
|
||||
|
||||
public var uri: String {
|
||||
"smp://\(keyHash)\(basicAuth == "" ? "" : ":" + basicAuth)@\(hostnames.joined(separator: ","))"
|
||||
"\(serverProtocol?.rawValue ?? "smp")://\(keyHash)\(basicAuth == "" ? "" : ":" + basicAuth)@\(hostnames.joined(separator: ","))"
|
||||
}
|
||||
|
||||
public var valid: Bool {
|
||||
hostnames.count > 0 && Set(hostnames).count == hostnames.count
|
||||
}
|
||||
|
||||
static public var empty = ServerAddress(
|
||||
hostnames: [],
|
||||
port: "",
|
||||
keyHash: "",
|
||||
basicAuth: ""
|
||||
)
|
||||
static func empty(_ serverProtocol: ServerProtocol) -> ServerAddress {
|
||||
ServerAddress(
|
||||
serverProtocol: serverProtocol,
|
||||
hostnames: [],
|
||||
port: "",
|
||||
keyHash: "",
|
||||
basicAuth: ""
|
||||
)
|
||||
}
|
||||
|
||||
static public var sampleData = ServerAddress(
|
||||
serverProtocol: .smp,
|
||||
hostnames: ["smp.simplex.im", "1234.onion"],
|
||||
port: "",
|
||||
keyHash: "LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=",
|
||||
@@ -1242,6 +1269,7 @@ public enum AgentErrorType: Decodable {
|
||||
case CMD(cmdErr: CommandErrorType)
|
||||
case CONN(connErr: ConnectionErrorType)
|
||||
case SMP(smpErr: ProtocolErrorType)
|
||||
case XFTP(xftpErr: XFTPErrorType)
|
||||
case NTF(ntfErr: ProtocolErrorType)
|
||||
case BROKER(brokerAddress: String, brokerErr: BrokerErrorType)
|
||||
case AGENT(agentErr: SMPAgentError)
|
||||
@@ -1283,6 +1311,21 @@ public enum ProtocolErrorType: Decodable {
|
||||
case INTERNAL
|
||||
}
|
||||
|
||||
public enum XFTPErrorType: Decodable {
|
||||
case BLOCK
|
||||
case SESSION
|
||||
case CMD(cmdErr: ProtocolCommandError)
|
||||
case AUTH
|
||||
case SIZE
|
||||
case QUOTA
|
||||
case DIGEST
|
||||
case CRYPTO
|
||||
case NO_FILE
|
||||
case HAS_FILE
|
||||
case FILE_IO
|
||||
case INTERNAL
|
||||
}
|
||||
|
||||
public enum ProtocolCommandError: Decodable {
|
||||
case UNKNOWN
|
||||
case SYNTAX
|
||||
|
||||
Reference in New Issue
Block a user