From 7e3d53b6212114bb8948dd2a7f68b6f10e679f3a Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 21 Nov 2022 08:37:13 +0000 Subject: [PATCH] ios: advanced server configuration (#1388) * ios: advanced server configuration * UI is mostly working, QR code scan * refactor * error alerts * fixes * remove old view * rename view * translations * only show valid QR code, spinner during server test * update tested status on edit * space wtf Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com> * moar space * translation Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com> * translations * translations Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com> --- apps/ios/Shared/Model/ChatModel.swift | 3 +- apps/ios/Shared/Model/SimpleXAPI.swift | 10 +- .../Chat/Group/GroupMemberInfoView.swift | 4 +- apps/ios/Shared/Views/NewChat/QRCode.swift | 55 ++-- .../UserSettings/NetworkAndServers.swift | 9 +- .../Views/UserSettings/RTCServers.swift | 1 + .../Views/UserSettings/SMPServerView.swift | 115 +++++++-- .../Views/UserSettings/SMPServers.swift | 178 ------------- .../Views/UserSettings/SMPServersView.swift | 242 ++++++++++++++---- .../Views/UserSettings/ScanSMPServer.swift | 60 +++++ .../de.xcloc/Localized Contents/de.xliff | 159 ++++++++++-- .../en.xcloc/Localized Contents/en.xliff | 159 ++++++++++-- .../ru.xcloc/Localized Contents/ru.xliff | 164 ++++++++++-- apps/ios/SimpleX.xcodeproj/project.pbxproj | 8 +- apps/ios/SimpleXChat/API.swift | 20 ++ apps/ios/SimpleXChat/APITypes.swift | 70 +++-- apps/ios/SimpleXChat/SimpleX.h | 1 + apps/ios/de.lproj/Localizable.strings | 93 ++++++- apps/ios/ru.lproj/Localizable.strings | 93 ++++++- src/Simplex/Chat/View.hs | 2 +- 20 files changed, 1043 insertions(+), 403 deletions(-) delete mode 100644 apps/ios/Shared/Views/UserSettings/SMPServers.swift create mode 100644 apps/ios/Shared/Views/UserSettings/ScanSMPServer.swift diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index 894a79a03b..e76c935a92 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -31,7 +31,8 @@ final class ChatModel: ObservableObject { // items in the terminal view @Published var terminalItems: [TerminalItem] = [] @Published var userAddress: UserContactLink? - @Published var userSMPServers: [String]? + @Published var userSMPServers: [ServerCfg]? + @Published var presetSMPServers: [String]? @Published var chatItemTTL: ChatItemTTL = .none @Published var appOpenUrl: URL? @Published var deviceToken: DeviceToken? diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 4ee70bc0bc..c96b702c50 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -309,19 +309,19 @@ func apiDeleteToken(token: DeviceToken) async throws { try await sendCommandOkResp(.apiDeleteToken(token: token)) } -func getUserSMPServers() throws -> [String] { +func getUserSMPServers() throws -> ([ServerCfg], [String]) { let r = chatSendCmdSync(.getUserSMPServers) - if case let .userSMPServers(smpServers, _) = r { return smpServers.map { $0.server } } + if case let .userSMPServers(smpServers, presetServers) = r { return (smpServers, presetServers) } throw r } -func setUserSMPServers(smpServers: [String]) async throws { +func setUserSMPServers(smpServers: [ServerCfg]) async throws { try await sendCommandOkResp(.setUserSMPServers(smpServers: smpServers)) } func testSMPServer(smpServer: String) async throws -> Result<(), SMPTestFailure> { let r = await chatSendCmd(.testSMPServer(smpServer: smpServer)) - if case let .sMPTestResult(testFailure) = r { + if case let .smpTestResult(testFailure) = r { if let t = testFailure { return .failure(t) } @@ -843,7 +843,7 @@ func startChat() throws { let justStarted = try apiStartChat() if justStarted { m.userAddress = try apiGetUserAddress() - m.userSMPServers = try getUserSMPServers() + (m.userSMPServers, m.presetSMPServers) = try getUserSMPServers() m.chatItemTTL = try getChatItemTTL() let chats = try apiGetChats() m.chats = chats.map { Chat.init($0) } diff --git a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift index a9aa29bf64..3bc082e6b8 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift @@ -131,7 +131,9 @@ struct GroupMemberInfoView: View { } if let chat = chat { dismissAllSheets(animated: true) - chatModel.chatId = chat.id + DispatchQueue.main.async { + chatModel.chatId = chat.id + } } } label: { Label("Send direct message", systemImage: "message") diff --git a/apps/ios/Shared/Views/NewChat/QRCode.swift b/apps/ios/Shared/Views/NewChat/QRCode.swift index 4d9b7835b0..27f014fe5a 100644 --- a/apps/ios/Shared/Views/NewChat/QRCode.swift +++ b/apps/ios/Shared/Views/NewChat/QRCode.swift @@ -9,6 +9,25 @@ import SwiftUI import CoreImage.CIFilterBuiltins +struct MutableQRCode: View { + @Binding var uri: String + @State private var image: UIImage? + + var body: some View { + ZStack { + if let image = image { + qrCodeImage(image) + } + } + .onAppear { + image = generateImage(uri) + } + .onChange(of: uri) { _ in + image = generateImage(uri) + } + } +} + struct QRCode: View { let uri: String @State private var image: UIImage? @@ -16,32 +35,32 @@ struct QRCode: View { var body: some View { ZStack { if let image = image { - Image(uiImage: image) - .resizable() - .interpolation(.none) - .aspectRatio(1, contentMode: .fit) - .textSelection(.enabled) + qrCodeImage(image) } } .onAppear { - generateImage() + image = image ?? generateImage(uri) } } +} - private func generateImage() { - guard image == nil else { return } +private func qrCodeImage(_ image: UIImage) -> some View { + Image(uiImage: image) + .resizable() + .interpolation(.none) + .aspectRatio(1, contentMode: .fit) + .textSelection(.enabled) +} - let context = CIContext() - let filter = CIFilter.qrCodeGenerator() - filter.message = Data(uri.utf8) - - guard - let outputImage = filter.outputImage, - let cgImage = context.createCGImage(outputImage, from: outputImage.extent) - else { return } - - self.image = UIImage(cgImage: cgImage) +private func generateImage(_ uri: String) -> UIImage? { + let context = CIContext() + let filter = CIFilter.qrCodeGenerator() + filter.message = Data(uri.utf8) + if let outputImage = filter.outputImage, + let cgImage = context.createCGImage(outputImage, from: outputImage.extent) { + return UIImage(cgImage: cgImage) } + return nil } struct QRCode_Previews: PreviewProvider { diff --git a/apps/ios/Shared/Views/UserSettings/NetworkAndServers.swift b/apps/ios/Shared/Views/UserSettings/NetworkAndServers.swift index c4ba096fa3..16d3c9cd0c 100644 --- a/apps/ios/Shared/Views/UserSettings/NetworkAndServers.swift +++ b/apps/ios/Shared/Views/UserSettings/NetworkAndServers.swift @@ -33,18 +33,11 @@ struct NetworkAndServers: View { VStack { List { Section { - NavigationLink { - SMPServers() - .navigationTitle("Your SMP servers") - } label: { - Text("SMP servers") - } - NavigationLink { SMPServersView() .navigationTitle("Your SMP servers") } label: { - Text("SMP servers (new)") + Text("SMP servers") } Picker("Use .onion hosts", selection: $onionHosts) { diff --git a/apps/ios/Shared/Views/UserSettings/RTCServers.swift b/apps/ios/Shared/Views/UserSettings/RTCServers.swift index 99685e6b1b..db36babbdb 100644 --- a/apps/ios/Shared/Views/UserSettings/RTCServers.swift +++ b/apps/ios/Shared/Views/UserSettings/RTCServers.swift @@ -10,6 +10,7 @@ import SwiftUI private let howToUrl = URL(string: "https://github.com/simplex-chat/simplex-chat/blob/stable/docs/WEBRTC.md#configure-mobile-apps")! +let serversFont = Font.custom("Menlo", size: 14) struct RTCServers: View { @State private var userRTCServers: [String] = [] diff --git a/apps/ios/Shared/Views/UserSettings/SMPServerView.swift b/apps/ios/Shared/Views/UserSettings/SMPServerView.swift index 66043fe3ca..47ed5a6223 100644 --- a/apps/ios/Shared/Views/UserSettings/SMPServerView.swift +++ b/apps/ios/Shared/Views/UserSettings/SMPServerView.swift @@ -10,13 +10,46 @@ import SwiftUI import SimpleXChat struct SMPServerView: View { - @State var server: ServerCfg + @Environment(\.dismiss) var dismiss: DismissAction + @Binding var server: ServerCfg + @State var serverToEdit: ServerCfg + @State private var showTestFailure = false + @State private var testing = false + @State private var testFailure: SMPTestFailure? var body: some View { - if server.preset { - presetServer() - } else { - customServer() + ZStack { + if server.preset { + presetServer() + } else { + customServer() + } + if testing { + ProgressView().scaleEffect(2) + } + } + .navigationBarBackButtonHidden(true) + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button { + server = serverToEdit + dismiss() + } label: { + HStack { + Image(systemName: "chevron.left") + Text("Your SMP servers") + } + } + } + } + .alert(isPresented: $showTestFailure) { + Alert( + title: Text("Server test failed!"), + message: Text(testFailure?.localizedDescription ?? "") + ) + } + .onChange(of: serverToEdit.server) { _ in + serverToEdit.tested = serverToEdit.server == server.server ? server.tested : nil } } @@ -24,47 +57,66 @@ struct SMPServerView: View { return VStack { List { Section("Preset server address") { - Text(server.server) + Text(serverToEdit.server) + .textSelection(.enabled) } - useServerSection() + useServerSection(true) } } } private func customServer() -> some View { VStack { + let valid = parseServerAddress(serverToEdit.server)?.valid == true List { - Section("Your server address") { - TextEditor(text: $server.server) + Section { + TextEditor(text: $serverToEdit.server) .multilineTextAlignment(.leading) .autocorrectionDisabled(true) .autocapitalization(.none) + .allowsTightening(true) .lineLimit(10) - .frame(height: 108) + .frame(height: 144) .padding(-6) + } header: { + HStack { + Text("Your server address") + if !valid { + Spacer() + Image(systemName: "exclamationmark.circle").foregroundColor(.red) + } + } } - useServerSection() - Section("Add to another device") { - QRCode(uri: server.server) - .listRowInsets(EdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 12)) + useServerSection(valid) + if valid { + Section("Add to another device") { + MutableQRCode(uri: $serverToEdit.server) + .listRowInsets(EdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 12)) + } } } } } - private func useServerSection() -> some View { + private func useServerSection(_ valid: Bool) -> some View { Section("Use server") { HStack { Button("Test server") { - Task { await testServerConnection(server: $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: server) - } - Toggle("Enabled", isOn: $server.enabled) - Button("Remove server", role: .destructive) { - + showTestStatus(server: serverToEdit) } + Toggle("Use for new connections", isOn: $serverToEdit.enabled) } } } @@ -82,24 +134,33 @@ struct SMPServerView: View { } } -func testServerConnection(server: Binding) async { +func testServerConnection(server: Binding) async -> SMPTestFailure? { do { let r = try await testSMPServer(smpServer: server.wrappedValue.server) - await MainActor.run { + switch r { - case .success: server.wrappedValue.tested = true - case .failure: server.wrappedValue.tested = false + 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 { server.wrappedValue.tested = false } + return nil } } +func serverHostname(_ srv: ServerCfg) -> String { + parseServerAddress(srv.server)?.hostnames.first ?? srv.server +} + struct SMPServerView_Previews: PreviewProvider { static var previews: some View { - SMPServerView(server: ServerCfg.sampleData.custom) + SMPServerView(server: Binding.constant(ServerCfg.sampleData.custom), serverToEdit: ServerCfg.sampleData.custom) } } diff --git a/apps/ios/Shared/Views/UserSettings/SMPServers.swift b/apps/ios/Shared/Views/UserSettings/SMPServers.swift deleted file mode 100644 index 886cc7a1c0..0000000000 --- a/apps/ios/Shared/Views/UserSettings/SMPServers.swift +++ /dev/null @@ -1,178 +0,0 @@ -// -// SMPServers.swift -// SimpleX -// -// Created by Efim Poberezkin on 02.03.2022. -// Copyright © 2022 SimpleX Chat. All rights reserved. -// - -import SwiftUI -import SimpleXChat - -let serversFont = Font.custom("Menlo", size: 14) - -private let howToUrl = URL(string: "https://github.com/simplex-chat/simplexmq#using-smp-server-and-smp-agent")! - -struct SMPServers: View { - @EnvironmentObject var chatModel: ChatModel - @State var isUserSMPServers = false - @State var isUserSMPServersToggle = false - @State var editSMPServers = true - @State var userSMPServersStr = "" - @State var showBadServersAlert = false - @State var showResetServersAlert = false - @FocusState private var keyboardVisible: Bool - - var body: some View { - List { - Section { - Toggle("Configure SMP servers", isOn: $isUserSMPServersToggle) - .onChange(of: isUserSMPServersToggle) { _ in - if (isUserSMPServersToggle) { - isUserSMPServers = true - } else { - let servers = chatModel.userSMPServers ?? [] - if (!servers.isEmpty) { - showResetServersAlert = true - } else { - isUserSMPServers = false - userSMPServersStr = "" - } - } - } - .alert(isPresented: $showResetServersAlert) { - Alert( - title: Text("Use SimpleX Chat servers?"), - message: Text("Saved SMP servers will be removed"), - primaryButton: .destructive(Text("Confirm")) { - saveSMPServers(smpServers: []) - isUserSMPServers = false - userSMPServersStr = "" - }, secondaryButton: .cancel() { - withAnimation() { - isUserSMPServersToggle = true - } - } - ) - } - } header: { - Text("") - } footer: { - if !isUserSMPServers { - Text("Using SimpleX Chat servers.") - } - } - - if isUserSMPServers { - Section { - if editSMPServers { - TextEditor(text: $userSMPServersStr) - .focused($keyboardVisible) - .font(serversFont) - .disableAutocorrection(true) - .textInputAutocapitalization(.never) - .padding(.horizontal, -5) - .padding(.top, -8) - .frame(height: 160, alignment: .topLeading) - .frame(maxWidth: .infinity, alignment: .leading) - } else { - ScrollView { - Text(userSMPServersStr) - .font(serversFont) - .frame(minHeight: 0, alignment: .topLeading) - .textSelection(.enabled) - .frame(maxWidth: .infinity, alignment: .leading) - } - .frame(height: 160) - } - } header: { - Text("SMP servers (one per line)") - } footer: { - HStack(spacing: 20) { - if editSMPServers { - Button("Cancel") { - initialize() - } - Button("Save") { - saveUserSMPServers() - } - .alert(isPresented: $showBadServersAlert) { - Alert(title: Text("Error saving SMP servers"), message: Text("Make sure SMP server addresses are in correct format, line separated and are not duplicated.")) - } - Spacer() - howToButton() - } else { - Button("Edit") { - editSMPServers = true - } - Spacer() - howToButton() - } - } - .font(.body) - } - } - } - .onAppear { initialize() } - } - - func initialize() { - let userSMPServers = chatModel.userSMPServers ?? [] - isUserSMPServers = !userSMPServers.isEmpty - isUserSMPServersToggle = isUserSMPServers - editSMPServers = !isUserSMPServers - userSMPServersStr = isUserSMPServers ? userSMPServers.joined(separator: "\n") : "" - } - - func saveUserSMPServers() { - let userSMPServers = userSMPServersStr.components(separatedBy: "\n") - saveSMPServers(smpServers: userSMPServers) - } - - func saveSMPServers(smpServers: [String]) { - Task { - do { - try await setUserSMPServers(smpServers: smpServers) - DispatchQueue.main.async { - chatModel.userSMPServers = smpServers - if smpServers.isEmpty { - isUserSMPServers = false - editSMPServers = true - } else { - editSMPServers = false - } - } - } catch { - let err = error.localizedDescription - logger.error("SMPServers.saveServers setUserSMPServers error: \(err)") - DispatchQueue.main.async { - showBadServersAlert = true - } - } - } - } - - func howToButton() -> some View { - Button { - DispatchQueue.main.async { - UIApplication.shared.open(howToUrl) - } - } label: { - HStack{ - Text("How to") - Image(systemName: "arrow.up.right.circle") - } - } - } -} - -// TODO preview doesn't work -struct SMPServers_Previews: PreviewProvider { - static var previews: some View { - let chatModel = ChatModel() - chatModel.currentUser = User.sampleData - chatModel.userSMPServers = [] - return SMPServers() - .environmentObject(chatModel) - } -} diff --git a/apps/ios/Shared/Views/UserSettings/SMPServersView.swift b/apps/ios/Shared/Views/UserSettings/SMPServersView.swift index fb4cd2f1ed..1d24c97bfc 100644 --- a/apps/ios/Shared/Views/UserSettings/SMPServersView.swift +++ b/apps/ios/Shared/Views/UserSettings/SMPServersView.swift @@ -9,20 +9,43 @@ import SwiftUI import SimpleXChat +private let howToUrl = URL(string: "https://github.com/simplex-chat/simplexmq#using-smp-server-and-smp-agent")! + struct SMPServersView: View { - @Environment(\.editMode) var editMode - @State var servers: [ServerCfg] = [ - ServerCfg.sampleData.preset, - ServerCfg.sampleData.custom, - ServerCfg.sampleData.untested, - ] - @State var showAddServer = false - @State var showSaveAlert = false + @EnvironmentObject private var m: ChatModel + @Environment(\.editMode) private var editMode + @State private var servers = ChatModel.shared.userSMPServers ?? [] + @State private var selectedServer: String? = nil + @State private var showAddServer = false + @State private var showScanSMPServer = false + @State private var testing = false + @State private var alert: SMPServerAlert? = nil var body: some View { + ZStack { + smpServersView() + if testing { + ProgressView().scaleEffect(2) + } + } + } + + enum SMPServerAlert: Identifiable { + case testsFailed(failures: [String: SMPTestFailure]) + case error(title: LocalizedStringKey, error: String = "") + + var id: String { + switch self { + case .testsFailed: return "testsFailed" + case let .error(title, _): return "error \(title)" + } + } + } + + private func smpServersView() -> some View { List { Section("SMP servers") { - ForEach(servers) { srv in + ForEach($servers) { srv in smpServerView(srv) } .onMove { indexSet, offset in @@ -31,68 +54,189 @@ struct SMPServersView: View { .onDelete { indexSet in servers.remove(atOffsets: indexSet) } - if isEditing { - Button("Add server…") { - showAddServer = true - } + Button("Add server…") { + showAddServer = true } } - } - .onChange(of: isEditing) { value in - if value == false { - showSaveAlert = true + + Section { + Button("Reset") { servers = m.userSMPServers ?? [] } + .disabled(servers == m.userSMPServers || testing) + Button("Test servers", action: testServers) + .disabled(testing) + Button("Save servers", action: saveSMPServers) + .disabled(saveDisabled) + howToButton() } } .toolbar { EditButton() } .confirmationDialog("Add server…", isPresented: $showAddServer, titleVisibility: .hidden) { - Button("Scan server QR code") { - } - Button("Add preset servers") { - } Button("Enter server manually") { servers.append(ServerCfg.empty) + selectedServer = servers.last?.id } + Button("Scan server QR code") { showScanSMPServer = true } + Button("Add preset servers", action: addAllPresets) + .disabled(hasAllPresets()) } - .confirmationDialog("Save servers?", isPresented: $showSaveAlert, titleVisibility: .visible) { - Button("Test & save servers") { - for i in 0.. some View { - NavigationLink { - SMPServerView(server: srv) - .navigationBarTitle("Server") + private func smpServerView(_ server: Binding) -> some View { + let srv = server.wrappedValue + return NavigationLink(tag: srv.id, selection: $selectedServer) { + SMPServerView(server: server, serverToEdit: srv) + .navigationBarTitle(srv.preset ? "Preset server" : "Your server") .navigationBarTitleDisplayMode(.large) } label: { - let v = Text(srv.server) + let address = parseServerAddress(srv.server) HStack { - showTestStatus(server: srv) - .frame(width: 16, alignment: .center) - .padding(.trailing, 4) + Group { + if let address = address { + if !address.valid { + invalidServer() + } else if !uniqueAddress(srv, address) { + Image(systemName: "exclamationmark.circle").foregroundColor(.red) + } else if !srv.enabled { + Image(systemName: "slash.circle").foregroundColor(.secondary) + } else { + showTestStatus(server: srv) + } + } else { + invalidServer() + } + } + .frame(width: 16, alignment: .center) + .padding(.trailing, 4) + + let v = Text(address?.hostnames.first ?? srv.server).lineLimit(1) if srv.enabled { v } else { - (v + Text(" (disabled)")).foregroundColor(.secondary) + v.foregroundColor(.secondary) + } + } + } + } + + func howToButton() -> some View { + Button { + DispatchQueue.main.async { + UIApplication.shared.open(howToUrl) + } + } label: { + HStack { + Text("How to use your servers") + Image(systemName: "arrow.up.right.circle") + } + } + } + + private func invalidServer() -> some View { + Image(systemName: "exclamationmark.circle").foregroundColor(.red) + } + + private func uniqueAddress(_ s: ServerCfg, _ address: ServerAddress) -> Bool { + servers.allSatisfy { srv in + address.hostnames.allSatisfy { host in + srv.id == s.id || !srv.server.contains(host) + } + } + } + + private func hasAllPresets() -> Bool { + m.presetSMPServers?.allSatisfy { hasPreset($0) } ?? true + } + + private func addAllPresets() { + for srv in m.presetSMPServers ?? [] { + if !hasPreset(srv) { + servers.append(ServerCfg(server: srv, preset: true, tested: nil, enabled: true)) + } + } + } + + private func hasPreset(_ srv: String) -> Bool { + servers.contains(where: { $0.server == srv }) + } + + private func testServers() { + resetTestStatus() + testing = true + Task { + let fs = await runServersTest() + await MainActor.run { + testing = false + if !fs.isEmpty { + alert = .testsFailed(failures: fs) + } + } + } + } + + private func resetTestStatus() { + for i in 0.. [String: SMPTestFailure] { + var fs: [String: SMPTestFailure] = [:] + for i in 0..) { + switch resp { + case let .success(r): + if parseServerAddress(r.string) != nil { + servers.append(ServerCfg(server: r.string, preset: false, tested: nil, enabled: true)) + dismiss() + } else { + showAddressError = true + } + case let .failure(e): + logger.error("ConnectContactView.processQRCode QR code error: \(e.localizedDescription)") + dismiss() + } + } +} + +struct ScanSMPServer_Previews: PreviewProvider { + static var previews: some View { + ScanSMPServer(servers: Binding.constant([])) + } +} diff --git a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff index f01578bd1b..1f48139265 100644 --- a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff +++ b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff @@ -27,6 +27,16 @@ (kann kopiert werden) No comment provided by engineer. + + Possibly, certificate fingerprint in server address is incorrect + ***Possibly, certificate fingerprint in server address is incorrect + server test error + + + Server requires authorization to create queues, check password + ***Server requires authorization to create queues, check password + server test error + !1 colored! !1 farbig! @@ -258,6 +268,21 @@ Anfragen annehmen No comment provided by engineer. + + Add preset servers + ***Add preset servers + No comment provided by engineer. + + + Add server… + ***Add server… + No comment provided by engineer. + + + Add to another device + ***Add to another device + No comment provided by engineer. + Advanced network settings Erweiterte Netzwerkeinstellungen @@ -478,6 +503,11 @@ Chats No comment provided by engineer. + + Check server address and try again. + ***Check server address and try again. + No comment provided by engineer. + Choose file Datei auswählen @@ -513,11 +543,6 @@ ICE-Server konfigurieren No comment provided by engineer. - - Configure SMP servers - SMP-Server konfigurieren - No comment provided by engineer. - Confirm Bestätigen @@ -531,7 +556,7 @@ Connect Verbinden - No comment provided by engineer. + server test step Connect via contact link? @@ -678,6 +703,11 @@ Erstellen Sie einen einmaligen Einladungslink No comment provided by engineer. + + Create queue + ***Create queue + server test step + Create secret group Geheime Gruppe erstellen @@ -931,6 +961,11 @@ Die ausstehende Verbindung löschen? No comment provided by engineer. + + Delete queue + ***Delete queue + server test step + Develop Entwicklung @@ -961,6 +996,11 @@ SimpleX Sperre deaktivieren authentication reason + + Disconnect + ***Disconnect + server test step + Display name Angezeigter Name @@ -1076,6 +1116,16 @@ Passwort eingeben… No comment provided by engineer. + + Enter server manually + ***Enter server manually + No comment provided by engineer. + + + Error + ***Error + No comment provided by engineer. + Error accepting contact request Fehler beim Annehmen der Kontaktanfrage @@ -1356,6 +1406,11 @@ ***Group preferences No comment provided by engineer. + + Group profile + ***Group profile + No comment provided by engineer. + Group profile is stored on members' devices, not on the servers. Das Gruppenprofil wird nur auf den Mitglieds-Systemen gespeichtert und nicht auf den Servern. @@ -1406,6 +1461,11 @@ Markdowns verwenden No comment provided by engineer. + + How to use your servers + ***How to use your servers + No comment provided by engineer. + ICE servers (one per line) ICE-Server (einer pro Zeile) @@ -1518,6 +1578,11 @@ Ungültiger Verbindungslink No comment provided by engineer. + + Invalid server address! + ***Invalid server address! + No comment provided by engineer. + Invitation expired! Einladung abgelaufen! @@ -1628,11 +1693,6 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Stellen Sie eine private Verbindung her No comment provided by engineer. - - Make sure SMP server addresses are in correct format, line separated and are not duplicated. - Stellen Sie sicher, dass die SMP-Server Adressen das richtige Format haben, zeilenweise angeordnet und nicht kopiert sind. - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. Stellen Sie sicher, dass die WebRTC ICE-Server Adressen das richtige Format haben, zeilenweise angeordnet und nicht kopiert sind. @@ -1968,9 +2028,14 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Bitte bewahren Sie das Passwort sicher auf, Sie können es NICHT mehr ändern, wenn Sie es vergessen haben oder verlieren. No comment provided by engineer. - - Preferences - ***Preferences + + Preset server + ***Preset server + No comment provided by engineer. + + + Preset server address + ***Preset server address No comment provided by engineer. @@ -2158,11 +2223,6 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v SMP-Server No comment provided by engineer. - - SMP servers (one per line) - SMP-Server (einer pro Zeile) - No comment provided by engineer. - Save Speichern @@ -2203,9 +2263,9 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Passwort im Schlüsselbund speichern No comment provided by engineer. - - Saved SMP servers will be removed - Gespeicherte SMP-Server werden entfernt + + Save servers + ***Save servers No comment provided by engineer. @@ -2218,11 +2278,21 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v QR-Code scannen No comment provided by engineer. + + Scan server QR code + ***Scan server QR code + No comment provided by engineer. + Search Suche No comment provided by engineer. + + Secure queue + ***Secure queue + server test step + Send direct message Direktnachricht senden @@ -2268,6 +2338,11 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Datei-Ereignis wurde gesendet notification + + Server test failed! + ***Server test failed! + No comment provided by engineer. + Servers Server @@ -2448,6 +2523,26 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Tippen, um einen neuen Chat zu starten No comment provided by engineer. + + Test failed at step %@. + ***Test failed at step %@. + server test failure + + + Test server + ***Test server + No comment provided by engineer. + + + Test servers + ***Test servers + No comment provided by engineer. + + + Tests failed! + ***Tests failed! + No comment provided by engineer. + Thank you for installing SimpleX Chat! Vielen Dank, dass Sie SimpleX Chat installiert haben! @@ -2697,6 +2792,16 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Verwenden Sie Chat No comment provided by engineer. + + Use for new connections + ***Use for new connections + No comment provided by engineer. + + + Use server + ***Use server + No comment provided by engineer. + Using .onion hosts requires compatible VPN provider. Für die Nutzung von .onion-Hosts sind kompatible VPN-Anbieter erforderlich. @@ -3031,6 +3136,16 @@ SimpleX-Server können Ihr Profil nicht einsehen. Ihr Zufallsprofil No comment provided by engineer. + + Your server + ***Your server + No comment provided by engineer. + + + Your server address + ***Your server address + No comment provided by engineer. + Your settings Meine Einstellungen diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff index 0d3432d87d..4e8ce45f63 100644 --- a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff +++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff @@ -27,6 +27,16 @@ (can be copied) No comment provided by engineer. + + Possibly, certificate fingerprint in server address is incorrect + Possibly, certificate fingerprint in server address is incorrect + server test error + + + Server requires authorization to create queues, check password + Server requires authorization to create queues, check password + server test error + !1 colored! !1 colored! @@ -258,6 +268,21 @@ Accept requests No comment provided by engineer. + + Add preset servers + Add preset servers + No comment provided by engineer. + + + Add server… + Add server… + No comment provided by engineer. + + + Add to another device + Add to another device + No comment provided by engineer. + Advanced network settings Advanced network settings @@ -478,6 +503,11 @@ Chats No comment provided by engineer. + + Check server address and try again. + Check server address and try again. + No comment provided by engineer. + Choose file Choose file @@ -513,11 +543,6 @@ Configure ICE servers No comment provided by engineer. - - Configure SMP servers - Configure SMP servers - No comment provided by engineer. - Confirm Confirm @@ -531,7 +556,7 @@ Connect Connect - No comment provided by engineer. + server test step Connect via contact link? @@ -678,6 +703,11 @@ Create one-time invitation link No comment provided by engineer. + + Create queue + Create queue + server test step + Create secret group Create secret group @@ -931,6 +961,11 @@ Delete pending connection? No comment provided by engineer. + + Delete queue + Delete queue + server test step + Develop Develop @@ -961,6 +996,11 @@ Disable SimpleX Lock authentication reason + + Disconnect + Disconnect + server test step + Display name Display name @@ -1076,6 +1116,16 @@ Enter passphrase… No comment provided by engineer. + + Enter server manually + Enter server manually + No comment provided by engineer. + + + Error + Error + No comment provided by engineer. + Error accepting contact request Error accepting contact request @@ -1356,6 +1406,11 @@ Group preferences No comment provided by engineer. + + Group profile + Group profile + No comment provided by engineer. + Group profile is stored on members' devices, not on the servers. Group profile is stored on members' devices, not on the servers. @@ -1406,6 +1461,11 @@ How to use markdown No comment provided by engineer. + + How to use your servers + How to use your servers + No comment provided by engineer. + ICE servers (one per line) ICE servers (one per line) @@ -1518,6 +1578,11 @@ Invalid connection link No comment provided by engineer. + + Invalid server address! + Invalid server address! + No comment provided by engineer. + Invitation expired! Invitation expired! @@ -1628,11 +1693,6 @@ We will be adding server redundancy to prevent lost messages. Make a private connection No comment provided by engineer. - - Make sure SMP server addresses are in correct format, line separated and are not duplicated. - Make sure SMP server addresses are in correct format, line separated and are not duplicated. - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. @@ -1968,9 +2028,14 @@ We will be adding server redundancy to prevent lost messages. Please store passphrase securely, you will NOT be able to change it if you lose it. No comment provided by engineer. - - Preferences - Preferences + + Preset server + Preset server + No comment provided by engineer. + + + Preset server address + Preset server address No comment provided by engineer. @@ -2158,11 +2223,6 @@ We will be adding server redundancy to prevent lost messages. SMP servers No comment provided by engineer. - - SMP servers (one per line) - SMP servers (one per line) - No comment provided by engineer. - Save Save @@ -2203,9 +2263,9 @@ We will be adding server redundancy to prevent lost messages. Save passphrase in Keychain No comment provided by engineer. - - Saved SMP servers will be removed - Saved SMP servers will be removed + + Save servers + Save servers No comment provided by engineer. @@ -2218,11 +2278,21 @@ We will be adding server redundancy to prevent lost messages. Scan QR code No comment provided by engineer. + + Scan server QR code + Scan server QR code + No comment provided by engineer. + Search Search No comment provided by engineer. + + Secure queue + Secure queue + server test step + Send direct message Send direct message @@ -2268,6 +2338,11 @@ We will be adding server redundancy to prevent lost messages. Sent file event notification + + Server test failed! + Server test failed! + No comment provided by engineer. + Servers Servers @@ -2448,6 +2523,26 @@ We will be adding server redundancy to prevent lost messages. Tap to start a new chat No comment provided by engineer. + + Test failed at step %@. + Test failed at step %@. + server test failure + + + Test server + Test server + No comment provided by engineer. + + + Test servers + Test servers + No comment provided by engineer. + + + Tests failed! + Tests failed! + No comment provided by engineer. + Thank you for installing SimpleX Chat! Thank you for installing SimpleX Chat! @@ -2697,6 +2792,16 @@ To connect, please ask your contact to create another connection link and check Use chat No comment provided by engineer. + + Use for new connections + Use for new connections + No comment provided by engineer. + + + Use server + Use server + No comment provided by engineer. + Using .onion hosts requires compatible VPN provider. Using .onion hosts requires compatible VPN provider. @@ -3031,6 +3136,16 @@ SimpleX servers cannot see your profile. Your random profile No comment provided by engineer. + + Your server + Your server + No comment provided by engineer. + + + Your server address + Your server address + No comment provided by engineer. + Your settings Your settings diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff index a5f1c75edb..cce3a8c2d7 100644 --- a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff @@ -27,6 +27,16 @@ (можно скопировать) No comment provided by engineer. + + Possibly, certificate fingerprint in server address is incorrect + Возможно, хэш сертификата в адресе сервера неверный + server test error + + + Server requires authorization to create queues, check password + Сервер требует авторизации для создания очередей, проверьте пароль + server test error + !1 colored! !1 цвет! @@ -258,6 +268,21 @@ Принимать запросы No comment provided by engineer. + + Add preset servers + Добавить серверы по умолчанию + No comment provided by engineer. + + + Add server… + Добавить сервер… + No comment provided by engineer. + + + Add to another device + Добавить на другое устройство + No comment provided by engineer. + Advanced network settings Настройки сети @@ -478,6 +503,11 @@ Чаты No comment provided by engineer. + + Check server address and try again. + Проверьте адрес сервера и попробуйте снова. + No comment provided by engineer. + Choose file Выбрать файл @@ -513,11 +543,6 @@ Настройка ICE серверов No comment provided by engineer. - - Configure SMP servers - Настройка SMP серверов - No comment provided by engineer. - Confirm Подтвердить @@ -531,7 +556,7 @@ Connect Соединиться - No comment provided by engineer. + server test step Connect via contact link? @@ -678,6 +703,11 @@ Создать ссылку-приглашение No comment provided by engineer. + + Create queue + Создание очереди + server test step + Create secret group Создать скрытую группу @@ -931,6 +961,11 @@ Удалить ожидаемое соединение? No comment provided by engineer. + + Delete queue + Удаление очереди + server test step + Develop Для разработчиков @@ -961,6 +996,11 @@ Отключить блокировку SimpleX authentication reason + + Disconnect + Разрыв соединения + server test step + Display name Имя профиля @@ -1076,6 +1116,16 @@ Введите пароль… No comment provided by engineer. + + Enter server manually + Ввести сервер вручную + No comment provided by engineer. + + + Error + Ошибка + No comment provided by engineer. + Error accepting contact request Ошибка при принятии запроса на соединение @@ -1356,6 +1406,11 @@ Предпочтения группы No comment provided by engineer. + + Group profile + Профиль группы + No comment provided by engineer. + Group profile is stored on members' devices, not on the servers. Профиль группы хранится на устройствах членов, а не на серверах. @@ -1406,6 +1461,11 @@ Как форматировать No comment provided by engineer. + + How to use your servers + Как использовать серверы + No comment provided by engineer. + ICE servers (one per line) ICE серверы (один на строке) @@ -1518,6 +1578,11 @@ Ошибка в ссылке контакта No comment provided by engineer. + + Invalid server address! + Ошибка в адресе сервера! + No comment provided by engineer. + Invitation expired! Приглашение истекло! @@ -1628,11 +1693,6 @@ We will be adding server redundancy to prevent lost messages. Добавьте контакт No comment provided by engineer. - - Make sure SMP server addresses are in correct format, line separated and are not duplicated. - Пожалуйста, проверьте, что адреса SMP серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется. - No comment provided by engineer. - Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. Пожалуйста, проверьте, что адреса WebRTC ICE серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется. @@ -1968,9 +2028,14 @@ We will be adding server redundancy to prevent lost messages. Пожалуйста, надежно сохраните пароль, вы НЕ сможете его поменять, если потеряете. No comment provided by engineer. - - Preferences - Предпочтения + + Preset server + Сервер по умолчанию + No comment provided by engineer. + + + Preset server address + Адрес сервера по умолчанию No comment provided by engineer. @@ -2158,11 +2223,6 @@ We will be adding server redundancy to prevent lost messages. SMP серверы No comment provided by engineer. - - SMP servers (one per line) - SMP серверы (один на строке) - No comment provided by engineer. - Save Сохранить @@ -2203,9 +2263,9 @@ We will be adding server redundancy to prevent lost messages. Сохранить пароль в Keychain No comment provided by engineer. - - Saved SMP servers will be removed - Сохраненные SMP серверы будут удалены + + Save servers + Сохранить серверы No comment provided by engineer. @@ -2218,11 +2278,21 @@ We will be adding server redundancy to prevent lost messages. Сканировать QR код No comment provided by engineer. + + Scan server QR code + Сканировать QR код сервера + No comment provided by engineer. + Search Поиск No comment provided by engineer. + + Secure queue + Защита очереди + server test step + Send direct message Отправить сообщение @@ -2268,6 +2338,11 @@ We will be adding server redundancy to prevent lost messages. Отправка файла notification + + Server test failed! + Ошибка теста сервера! + No comment provided by engineer. + Servers Серверы @@ -2448,6 +2523,26 @@ We will be adding server redundancy to prevent lost messages. Нажмите, чтобы начать чат No comment provided by engineer. + + Test failed at step %@. + Ошибка теста на шаге %@. + server test failure + + + Test server + Тестировать сервер + No comment provided by engineer. + + + Test servers + Тестировать серверы + No comment provided by engineer. + + + Tests failed! + Ошибка тестов! + No comment provided by engineer. + Thank you for installing SimpleX Chat! Спасибо, что установили SimpleX Chat! @@ -2697,6 +2792,16 @@ To connect, please ask your contact to create another connection link and check Использовать чат No comment provided by engineer. + + Use for new connections + Использовать для новых соединений + No comment provided by engineer. + + + Use server + Использовать сервер + No comment provided by engineer. + Using .onion hosts requires compatible VPN provider. Для использования .onion хостов требуется совместимый VPN провайдер. @@ -2932,6 +3037,11 @@ To connect, please ask your contact to create another connection link and check Ваши SMP серверы No comment provided by engineer. + + Your SMP servers + Ваши SMP серверы + No comment provided by engineer. + Your SimpleX contact address Ваш SimpleX адрес @@ -3031,6 +3141,16 @@ SimpleX серверы не могут получить доступ к ваше Ваш случайный профиль No comment provided by engineer. + + Your server + Ваш сервер + No comment provided by engineer. + + + Your server address + Адрес вашего сервер + No comment provided by engineer. + Your settings Настройки diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 3f1119315c..00bf9bff89 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -60,6 +60,7 @@ 5C93293929241CDA0090FFF9 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C93293429241CD90090FFF9 /* libgmpxx.a */; }; 5C93293A29241CDA0090FFF9 /* libHSsimplex-chat-4.2.1-HrC8bBnv4qm8Sq6Eue57W1.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C93293529241CD90090FFF9 /* libHSsimplex-chat-4.2.1-HrC8bBnv4qm8Sq6Eue57W1.a */; }; 5C93293B29241CDA0090FFF9 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C93293629241CDA0090FFF9 /* libgmp.a */; }; + 5C9329412929248A0090FFF9 /* ScanSMPServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9329402929248A0090FFF9 /* ScanSMPServer.swift */; }; 5C971E1D27AEBEF600C8A3CE /* ChatInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C971E1C27AEBEF600C8A3CE /* ChatInfoView.swift */; }; 5C971E2127AEBF8300C8A3CE /* ChatInfoImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C971E2027AEBF8300C8A3CE /* ChatInfoImage.swift */; }; 5C9A5BDB2871E05400A5B906 /* SetNotificationsMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9A5BDA2871E05400A5B906 /* SetNotificationsMode.swift */; }; @@ -128,7 +129,6 @@ 5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59CF286477B400863A68 /* ChatArchiveView.swift */; }; 5CFE0921282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; }; 5CFE0922282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; }; - 640F50E327CF991C001E05C2 /* SMPServers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640F50E227CF991C001E05C2 /* SMPServers.swift */; }; 6432857C2925443C00FBE5C8 /* GroupPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6432857B2925443C00FBE5C8 /* GroupPreferencesView.swift */; }; 6440CA00288857A10062C672 /* CIEventView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6440C9FF288857A10062C672 /* CIEventView.swift */; }; 6440CA03288AECA70062C672 /* AddGroupMembersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6440CA02288AECA70062C672 /* AddGroupMembersView.swift */; }; @@ -260,6 +260,7 @@ 5C93293429241CD90090FFF9 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; 5C93293529241CD90090FFF9 /* libHSsimplex-chat-4.2.1-HrC8bBnv4qm8Sq6Eue57W1.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.2.1-HrC8bBnv4qm8Sq6Eue57W1.a"; sourceTree = ""; }; 5C93293629241CDA0090FFF9 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; + 5C9329402929248A0090FFF9 /* ScanSMPServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanSMPServer.swift; sourceTree = ""; }; 5C971E1C27AEBEF600C8A3CE /* ChatInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatInfoView.swift; sourceTree = ""; }; 5C971E2027AEBF8300C8A3CE /* ChatInfoImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatInfoImage.swift; sourceTree = ""; }; 5C9A5BDA2871E05400A5B906 /* SetNotificationsMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetNotificationsMode.swift; sourceTree = ""; }; @@ -332,7 +333,6 @@ 5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToAppGroupView.swift; sourceTree = ""; }; 5CFA59CF286477B400863A68 /* ChatArchiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatArchiveView.swift; sourceTree = ""; }; 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ZoomableScrollView.swift; path = Shared/Views/ZoomableScrollView.swift; sourceTree = SOURCE_ROOT; }; - 640F50E227CF991C001E05C2 /* SMPServers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SMPServers.swift; sourceTree = ""; }; 6432857B2925443C00FBE5C8 /* GroupPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupPreferencesView.swift; sourceTree = ""; }; 6440C9FF288857A10062C672 /* CIEventView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIEventView.swift; sourceTree = ""; }; 6440CA02288AECA70062C672 /* AddGroupMembersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddGroupMembersView.swift; sourceTree = ""; }; @@ -590,9 +590,9 @@ 5CCA7DF22905735700C8FEBA /* AcceptRequestsView.swift */, 5CB924E027A867BA00ACCCDD /* UserProfile.swift */, 5C577F7C27C83AA10006112D /* MarkdownHelp.swift */, - 640F50E227CF991C001E05C2 /* SMPServers.swift */, 5C93292E29239A170090FFF9 /* SMPServersView.swift */, 5C93293029239BED0090FFF9 /* SMPServerView.swift */, + 5C9329402929248A0090FFF9 /* ScanSMPServer.swift */, 5CB2084E28DA4B4800D024EC /* RTCServers.swift */, 5C3F1D592844B4DE00EC8A82 /* ExperimentalFeaturesView.swift */, 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */, @@ -927,7 +927,6 @@ 5C00164428A26FBC0094D739 /* ContextMenu.swift in Sources */, 5C3A88D127DF57800060F1C2 /* FramedItemView.swift in Sources */, 5CB924E427A8683A00ACCCDD /* UserAddress.swift in Sources */, - 640F50E327CF991C001E05C2 /* SMPServers.swift in Sources */, 64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */, 5CB0BA90282713D900B3292C /* SimpleXInfo.swift in Sources */, 5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */, @@ -988,6 +987,7 @@ 6440CA00288857A10062C672 /* CIEventView.swift in Sources */, 5CB0BA92282713FD00B3292C /* CreateProfile.swift in Sources */, 5C5F2B7027EBC704006A9D5F /* ProfileImage.swift in Sources */, + 5C9329412929248A0090FFF9 /* ScanSMPServer.swift in Sources */, 64AA1C6C27F3537400AC7277 /* DeletedItemView.swift in Sources */, 5C029EA82837DBB3004A9677 /* CICallItemView.swift in Sources */, 5CE4407227ADB1D0007B033A /* Emoji.swift in Sources */, diff --git a/apps/ios/SimpleXChat/API.swift b/apps/ios/SimpleXChat/API.swift index 4eadb4c1a5..42ed1fbd31 100644 --- a/apps/ios/SimpleXChat/API.swift +++ b/apps/ios/SimpleXChat/API.swift @@ -89,6 +89,26 @@ struct ParsedMarkdown: Decodable { var formattedText: [FormattedText]? } +public func parseServerAddress(_ s: String) -> ServerAddress? { + var c = s.cString(using: .utf8)! + if let cjson = chat_parse_server(&c) { + if let d = fromCString(cjson).data(using: .utf8) { + do { + let r = try jsonDecoder.decode(ParsedServerAddress.self, from: d) + return r.serverAddress + } catch { + logger.error("parseServerAddress jsonDecoder.decode error: \(error.localizedDescription)") + } + } + } + return nil +} + +struct ParsedServerAddress: Decodable { + var serverAddress: ServerAddress? + var parseError: String +} + private func fromCString(_ c: UnsafeMutablePointer) -> String { let s = String.init(cString: c) free(c) diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index 83c1dd2d74..7c54711c8a 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -47,7 +47,7 @@ public enum ChatCommand { case apiDeleteGroupLink(groupId: Int64) case apiGetGroupLink(groupId: Int64) case getUserSMPServers - case setUserSMPServers(smpServers: [String]) + case setUserSMPServers(smpServers: [ServerCfg]) case testSMPServer(smpServer: String) case apiSetChatItemTTL(seconds: Int64?) case apiGetChatItemTTL @@ -127,7 +127,7 @@ public enum ChatCommand { case let .apiDeleteGroupLink(groupId): return "/_delete link #\(groupId)" case let .apiGetGroupLink(groupId): return "/_get link #\(groupId)" case .getUserSMPServers: return "/smp" - case let .setUserSMPServers(smpServers): return "/smp \(smpServersStr(smpServers: smpServers))" + case let .setUserSMPServers(smpServers): return "/_smp \(smpServersStr(smpServers: smpServers))" case let .testSMPServer(smpServer): return "/smp test \(smpServer)" case let .apiSetChatItemTTL(seconds): return "/_ttl \(chatItemTTLStr(seconds: seconds))" case .apiGetChatItemTTL: return "/ttl" @@ -252,8 +252,8 @@ public enum ChatCommand { "\(type.rawValue)\(id)" } - func smpServersStr(smpServers: [String]) -> String { - smpServers.isEmpty ? "default" : smpServers.joined(separator: ";") + func smpServersStr(smpServers: [ServerCfg]) -> String { + smpServers.isEmpty ? "default" : encodeJSON(SMPServersConfig(smpServers: smpServers)) } func chatItemTTLStr(seconds: Int64?) -> String { @@ -295,7 +295,7 @@ public enum ChatResponse: Decodable, Error { case apiChats(chats: [ChatData]) case apiChat(chat: ChatData) case userSMPServers(smpServers: [ServerCfg], presetSMPServers: [String]) - case sMPTestResult(smpTestFailure: SMPTestFailure?) + case smpTestResult(smpTestFailure: SMPTestFailure?) case chatItemTTL(chatItemTTL: Int64?) case networkConfig(networkConfig: NetCfg) case contactInfo(contact: Contact, connectionStats: ConnectionStats, customUserProfile: Profile?) @@ -398,7 +398,7 @@ public enum ChatResponse: Decodable, Error { case .apiChats: return "apiChats" case .apiChat: return "apiChat" case .userSMPServers: return "userSMPServers" - case .sMPTestResult: return "smpTestResult" + case .smpTestResult: return "smpTestResult" case .chatItemTTL: return "chatItemTTL" case .networkConfig: return "networkConfig" case .contactInfo: return "contactInfo" @@ -500,8 +500,8 @@ public enum ChatResponse: Decodable, Error { case .chatSuspended: return noDetails case let .apiChats(chats): return String(describing: chats) case let .apiChat(chat): return String(describing: chat) - case let .userSMPServers(smpServers, _): return String(describing: smpServers) - case let .sMPTestResult(smpTestFailure): return String(describing: smpTestFailure) + case let .userSMPServers(smpServers, presetServers): return "smpServers: \(String(describing: smpServers))\npresetServers: \(String(describing: presetServers))" + case let .smpTestResult(smpTestFailure): return String(describing: smpTestFailure) case let .chatItemTTL(chatItemTTL): return String(describing: chatItemTTL) case let .networkConfig(networkConfig): return String(describing: networkConfig) case let .contactInfo(contact, connectionStats, customUserProfile): return "contact: \(String(describing: contact))\nconnectionStats: \(String(describing: connectionStats))\ncustomUserProfile: \(String(describing: customUserProfile))" @@ -635,11 +635,16 @@ public struct DBEncryptionConfig: Codable { public var newKey: String } -public struct ServerCfg: Identifiable, Decodable { +struct SMPServersConfig: Encodable { + var smpServers: [ServerCfg] +} + +public struct ServerCfg: Identifiable, Equatable, Codable { public var server: String public var preset: Bool public var tested: Bool? public var enabled: Bool + var createdAt = Date() // public var sendEnabled: Bool // can we potentially want to prevent sending on the servers we use to receive? // Even if we don't see the use case, it's probably better to allow it in the model // In any case, "trusted/known" servers are out of scope of this change @@ -651,9 +656,17 @@ public struct ServerCfg: Identifiable, Decodable { self.enabled = enabled } - public var id: String { server } + public static func == (l: ServerCfg, r: ServerCfg) -> Bool { + l.server == r.server && l.preset == r.preset && l.tested == r.tested && l.enabled == r.enabled + } - public static var empty = ServerCfg(server: "", preset: false, tested: false, enabled: true) + public var id: String { "\(server) \(createdAt)" } + + public static var empty = ServerCfg(server: "", preset: false, tested: nil, enabled: true) + + public var isEmpty: Bool { + server.trimmingCharacters(in: .whitespaces) == "" + } public struct SampleData { public var preset: ServerCfg @@ -663,27 +676,34 @@ public struct ServerCfg: Identifiable, Decodable { public static var sampleData = SampleData( preset: ServerCfg( - server: "smp://0YuTwO05YJWS8rkjn9eLJDjQhFKvIYd8d4xG8X1blIU=@smp8.simplex.im,beccx4yfxxbvyhqypaavemqurytl6hozr47wfc7uuecacjqdvwpw2xid.onion", + server: "smp://abcd@smp8.simplex.im", preset: true, tested: true, enabled: true ), custom: ServerCfg( - server: "smp://SkIkI6EPd2D63F4xFKfHk7I1UGZVNn6k1QWZ5rcyr6w=@smp9.simplex.im,jssqzccmrcws6bhmn77vgmhfjmhwlyr3u7puw4erkyoosywgl67slqqd.onion", + server: "smp://abcd@smp9.simplex.im", preset: false, tested: false, enabled: false ), untested: ServerCfg( - server: "smp://6iIcWT_dF2zN_w5xzZEY7HI2Prbh3ldP07YTyDexPjE=@smp10.simplex.im,rb2pbttocvnbrngnwziclp2f4ckjq65kebafws6g4hy22cdaiv5dwjqd.onion", + server: "smp://abcd@smp10.simplex.im", preset: false, tested: nil, enabled: true ) ) + + enum CodingKeys: CodingKey { + case server + case preset + case tested + case enabled + } } -public enum SMPTestStep: String, Decodable { +public enum SMPTestStep: String, Decodable, Equatable { case connect case createQueue case secureQueue @@ -701,24 +721,28 @@ public enum SMPTestStep: String, Decodable { } } -public struct SMPTestFailure: Decodable, Error { +public struct SMPTestFailure: Decodable, Error, Equatable { var testStep: SMPTestStep var testError: AgentErrorType - var localizedDescription: String { - let err = String.localizedStringWithFormat(NSLocalizedString("Test failed at step %@", comment: "server test failure"), testStep.text) + public static func == (l: SMPTestFailure, r: SMPTestFailure) -> Bool { + l.testStep == r.testStep + } + + public var localizedDescription: String { + let err = String.localizedStringWithFormat(NSLocalizedString("Test failed at step %@.", comment: "server test failure"), testStep.text) switch testError { case .SMP(.AUTH): - return err + "," + NSLocalizedString("Server requires authentication to create queues, check password", comment: "server test error") + return err + " " + NSLocalizedString("Server requires authorization to create queues, check password", comment: "server test error") case .BROKER(.NETWORK): - return err + "," + NSLocalizedString("Possibly, certificate fingerprint in server address is incorrect", comment: "server test error") + return err + " " + NSLocalizedString("Possibly, certificate fingerprint in server address is incorrect", comment: "server test error") default: return err } } } -public struct ServerAddress { +public struct ServerAddress: Decodable { public var hostnames: [String] public var port: String public var keyHash: String @@ -735,6 +759,10 @@ public struct ServerAddress { "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: "", diff --git a/apps/ios/SimpleXChat/SimpleX.h b/apps/ios/SimpleXChat/SimpleX.h index edd9d46bbc..56df7589a7 100644 --- a/apps/ios/SimpleXChat/SimpleX.h +++ b/apps/ios/SimpleXChat/SimpleX.h @@ -21,3 +21,4 @@ extern char *chat_send_cmd(chat_ctrl ctl, char *cmd); extern char *chat_recv_msg(chat_ctrl ctl); extern char *chat_recv_msg_wait(chat_ctrl ctl, int wait); extern char *chat_parse_markdown(char *str); +extern char *chat_parse_server(char *str); diff --git a/apps/ios/de.lproj/Localizable.strings b/apps/ios/de.lproj/Localizable.strings index 6570a7e7a2..dca89cde25 100644 --- a/apps/ios/de.lproj/Localizable.strings +++ b/apps/ios/de.lproj/Localizable.strings @@ -173,6 +173,15 @@ /* call status */ "accepted call" = "Anruf angenommen"; +/* No comment provided by engineer. */ +"Add preset servers" = "***Add preset servers"; + +/* No comment provided by engineer. */ +"Add server…" = "***Add server…"; + +/* No comment provided by engineer. */ +"Add to another device" = "***Add to another device"; + /* member role */ "admin" = "Admin"; @@ -347,6 +356,9 @@ /* No comment provided by engineer. */ "Chats" = "Chats"; +/* No comment provided by engineer. */ +"Check server address and try again." = "***Check server address and try again."; + /* No comment provided by engineer. */ "Choose file" = "Datei auswählen"; @@ -374,16 +386,13 @@ /* No comment provided by engineer. */ "Configure ICE servers" = "ICE-Server konfigurieren"; -/* No comment provided by engineer. */ -"Configure SMP servers" = "SMP-Server konfigurieren"; - /* No comment provided by engineer. */ "Confirm" = "Bestätigen"; /* No comment provided by engineer. */ "Confirm new passphrase…" = "Neues Passwort bestätigen…"; -/* No comment provided by engineer. */ +/* server test step */ "Connect" = "Verbinden"; /* No comment provided by engineer. */ @@ -512,6 +521,9 @@ /* No comment provided by engineer. */ "Create one-time invitation link" = "Erstellen Sie einen einmaligen Einladungslink"; +/* server test step */ +"Create queue" = "***Create queue"; + /* No comment provided by engineer. */ "Create secret group" = "Geheime Gruppe erstellen"; @@ -665,6 +677,9 @@ /* No comment provided by engineer. */ "Delete pending connection?" = "Die ausstehende Verbindung löschen?"; +/* server test step */ +"Delete queue" = "***Delete queue"; + /* deleted chat item */ "deleted" = "Gelöscht"; @@ -692,6 +707,9 @@ /* authentication reason */ "Disable SimpleX Lock" = "SimpleX Sperre deaktivieren"; +/* server test step */ +"Disconnect" = "***Disconnect"; + /* No comment provided by engineer. */ "Display name" = "Angezeigter Name"; @@ -773,9 +791,15 @@ /* No comment provided by engineer. */ "Enter passphrase…" = "Passwort eingeben…"; +/* No comment provided by engineer. */ +"Enter server manually" = "***Enter server manually"; + /* No comment provided by engineer. */ "error" = "Fehler"; +/* No comment provided by engineer. */ +"Error" = "***Error"; + /* No comment provided by engineer. */ "Error accepting contact request" = "Fehler beim Annehmen der Kontaktanfrage"; @@ -947,6 +971,9 @@ /* No comment provided by engineer. */ "Group preferences" = "***Group preferences"; +/* No comment provided by engineer. */ +"Group profile" = "***Group profile"; + /* No comment provided by engineer. */ "Group profile is stored on members' devices, not on the servers." = "Das Gruppenprofil wird nur auf den Mitglieds-Systemen gespeichtert und nicht auf den Servern."; @@ -980,6 +1007,9 @@ /* No comment provided by engineer. */ "How to use markdown" = "Markdowns verwenden"; +/* No comment provided by engineer. */ +"How to use your servers" = "***How to use your servers"; + /* No comment provided by engineer. */ "ICE servers (one per line)" = "ICE-Server (einer pro Zeile)"; @@ -1058,6 +1088,9 @@ /* No comment provided by engineer. */ "Invalid connection link" = "Ungültiger Verbindungslink"; +/* No comment provided by engineer. */ +"Invalid server address!" = "***Invalid server address!"; + /* No comment provided by engineer. */ "Invitation expired!" = "Einladung abgelaufen!"; @@ -1148,9 +1181,6 @@ /* No comment provided by engineer. */ "Make a private connection" = "Stellen Sie eine private Verbindung her"; -/* No comment provided by engineer. */ -"Make sure SMP server addresses are in correct format, line separated and are not duplicated." = "Stellen Sie sicher, dass die SMP-Server Adressen das richtige Format haben, zeilenweise angeordnet und nicht kopiert sind."; - /* No comment provided by engineer. */ "Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." = "Stellen Sie sicher, dass die WebRTC ICE-Server Adressen das richtige Format haben, zeilenweise angeordnet und nicht kopiert sind."; @@ -1391,8 +1421,14 @@ /* No comment provided by engineer. */ "Please store passphrase securely, you will NOT be able to change it if you lose it." = "Bitte bewahren Sie das Passwort sicher auf, Sie können es NICHT mehr ändern, wenn Sie es vergessen haben oder verlieren."; +/* server test error */ +"Possibly, certificate fingerprint in server address is incorrect" = "***Possibly, certificate fingerprint in server address is incorrect"; + /* No comment provided by engineer. */ -"Preferences" = "***Preferences"; +"Preset server" = "***Preset server"; + +/* No comment provided by engineer. */ +"Preset server address" = "***Preset server address"; /* No comment provided by engineer. */ "Privacy & security" = "Datenschutz & Sicherheit"; @@ -1545,7 +1581,7 @@ "Save passphrase in Keychain" = "Passwort im Schlüsselbund speichern"; /* No comment provided by engineer. */ -"Saved SMP servers will be removed" = "Gespeicherte SMP-Server werden entfernt"; +"Save servers" = "***Save servers"; /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Gespeicherte WebRTC ICE-Server werden entfernt"; @@ -1553,6 +1589,9 @@ /* No comment provided by engineer. */ "Scan QR code" = "QR-Code scannen"; +/* No comment provided by engineer. */ +"Scan server QR code" = "***Scan server QR code"; + /* No comment provided by engineer. */ "Search" = "Suche"; @@ -1562,6 +1601,9 @@ /* No comment provided by engineer. */ "secret" = "geheim"; +/* server test step */ +"Secure queue" = "***Secure queue"; + /* No comment provided by engineer. */ "Send direct message" = "Direktnachricht senden"; @@ -1589,6 +1631,12 @@ /* notification */ "Sent file event" = "Datei-Ereignis wurde gesendet"; +/* server test error */ +"Server requires authorization to create queues, check password" = "***Server requires authorization to create queues, check password"; + +/* No comment provided by engineer. */ +"Server test failed!" = "***Server test failed!"; + /* No comment provided by engineer. */ "Servers" = "Server"; @@ -1640,9 +1688,6 @@ /* No comment provided by engineer. */ "SMP servers" = "SMP-Server"; -/* No comment provided by engineer. */ -"SMP servers (one per line)" = "SMP-Server (einer pro Zeile)"; - /* notification title */ "Somebody" = "Jemand"; @@ -1709,6 +1754,18 @@ /* No comment provided by engineer. */ "TCP_KEEPINTVL" = "TCP_KEEPINTVL"; +/* server test failure */ +"Test failed at step %@." = "***Test failed at step %@."; + +/* No comment provided by engineer. */ +"Test server" = "***Test server"; + +/* No comment provided by engineer. */ +"Test servers" = "***Test servers"; + +/* No comment provided by engineer. */ +"Tests failed!" = "***Tests failed!"; + /* No comment provided by engineer. */ "Thank you for installing SimpleX Chat!" = "Vielen Dank, dass Sie SimpleX Chat installiert haben!"; @@ -1862,6 +1919,12 @@ /* No comment provided by engineer. */ "Use chat" = "Verwenden Sie Chat"; +/* No comment provided by engineer. */ +"Use for new connections" = "***Use for new connections"; + +/* No comment provided by engineer. */ +"Use server" = "***Use server"; + /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "Verwenden Sie SimpleX Chat Server?"; @@ -2117,6 +2180,12 @@ /* No comment provided by engineer. */ "Your random profile" = "Ihr Zufallsprofil"; +/* No comment provided by engineer. */ +"Your server" = "***Your server"; + +/* No comment provided by engineer. */ +"Your server address" = "***Your server address"; + /* No comment provided by engineer. */ "Your settings" = "Meine Einstellungen"; diff --git a/apps/ios/ru.lproj/Localizable.strings b/apps/ios/ru.lproj/Localizable.strings index aa42f56efc..31eede69e8 100644 --- a/apps/ios/ru.lproj/Localizable.strings +++ b/apps/ios/ru.lproj/Localizable.strings @@ -173,6 +173,15 @@ /* call status */ "accepted call" = " принятый звонок"; +/* No comment provided by engineer. */ +"Add preset servers" = "Добавить серверы по умолчанию"; + +/* No comment provided by engineer. */ +"Add server…" = "Добавить сервер…"; + +/* No comment provided by engineer. */ +"Add to another device" = "Добавить на другое устройство"; + /* member role */ "admin" = "админ"; @@ -347,6 +356,9 @@ /* No comment provided by engineer. */ "Chats" = "Чаты"; +/* No comment provided by engineer. */ +"Check server address and try again." = "Проверьте адрес сервера и попробуйте снова."; + /* No comment provided by engineer. */ "Choose file" = "Выбрать файл"; @@ -374,16 +386,13 @@ /* No comment provided by engineer. */ "Configure ICE servers" = "Настройка ICE серверов"; -/* No comment provided by engineer. */ -"Configure SMP servers" = "Настройка SMP серверов"; - /* No comment provided by engineer. */ "Confirm" = "Подтвердить"; /* No comment provided by engineer. */ "Confirm new passphrase…" = "Подтвердите новый пароль…"; -/* No comment provided by engineer. */ +/* server test step */ "Connect" = "Соединиться"; /* No comment provided by engineer. */ @@ -512,6 +521,9 @@ /* No comment provided by engineer. */ "Create one-time invitation link" = "Создать ссылку-приглашение"; +/* server test step */ +"Create queue" = "Создание очереди"; + /* No comment provided by engineer. */ "Create secret group" = "Создать скрытую группу"; @@ -665,6 +677,9 @@ /* No comment provided by engineer. */ "Delete pending connection?" = "Удалить ожидаемое соединение?"; +/* server test step */ +"Delete queue" = "Удаление очереди"; + /* deleted chat item */ "deleted" = "удалено"; @@ -692,6 +707,9 @@ /* authentication reason */ "Disable SimpleX Lock" = "Отключить блокировку SimpleX"; +/* server test step */ +"Disconnect" = "Разрыв соединения"; + /* No comment provided by engineer. */ "Display name" = "Имя профиля"; @@ -773,9 +791,15 @@ /* No comment provided by engineer. */ "Enter passphrase…" = "Введите пароль…"; +/* No comment provided by engineer. */ +"Enter server manually" = "Ввести сервер вручную"; + /* No comment provided by engineer. */ "error" = "ошибка"; +/* No comment provided by engineer. */ +"Error" = "Ошибка"; + /* No comment provided by engineer. */ "Error accepting contact request" = "Ошибка при принятии запроса на соединение"; @@ -947,6 +971,9 @@ /* No comment provided by engineer. */ "Group preferences" = "Предпочтения группы"; +/* No comment provided by engineer. */ +"Group profile" = "Профиль группы"; + /* No comment provided by engineer. */ "Group profile is stored on members' devices, not on the servers." = "Профиль группы хранится на устройствах членов, а не на серверах."; @@ -980,6 +1007,9 @@ /* No comment provided by engineer. */ "How to use markdown" = "Как форматировать"; +/* No comment provided by engineer. */ +"How to use your servers" = "Как использовать серверы"; + /* No comment provided by engineer. */ "ICE servers (one per line)" = "ICE серверы (один на строке)"; @@ -1058,6 +1088,9 @@ /* No comment provided by engineer. */ "Invalid connection link" = "Ошибка в ссылке контакта"; +/* No comment provided by engineer. */ +"Invalid server address!" = "Ошибка в адресе сервера!"; + /* No comment provided by engineer. */ "Invitation expired!" = "Приглашение истекло!"; @@ -1148,9 +1181,6 @@ /* No comment provided by engineer. */ "Make a private connection" = "Добавьте контакт"; -/* No comment provided by engineer. */ -"Make sure SMP server addresses are in correct format, line separated and are not duplicated." = "Пожалуйста, проверьте, что адреса SMP серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется."; - /* No comment provided by engineer. */ "Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." = "Пожалуйста, проверьте, что адреса WebRTC ICE серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется."; @@ -1391,8 +1421,14 @@ /* No comment provided by engineer. */ "Please store passphrase securely, you will NOT be able to change it if you lose it." = "Пожалуйста, надежно сохраните пароль, вы НЕ сможете его поменять, если потеряете."; +/* server test error */ +"Possibly, certificate fingerprint in server address is incorrect" = "Возможно, хэш сертификата в адресе сервера неверный"; + /* No comment provided by engineer. */ -"Preferences" = "Предпочтения"; +"Preset server" = "Сервер по умолчанию"; + +/* No comment provided by engineer. */ +"Preset server address" = "Адрес сервера по умолчанию"; /* No comment provided by engineer. */ "Privacy & security" = "Конфиденциальность"; @@ -1545,7 +1581,7 @@ "Save passphrase in Keychain" = "Сохранить пароль в Keychain"; /* No comment provided by engineer. */ -"Saved SMP servers will be removed" = "Сохраненные SMP серверы будут удалены"; +"Save servers" = "Сохранить серверы"; /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Сохраненные WebRTC ICE серверы будут удалены"; @@ -1553,6 +1589,9 @@ /* No comment provided by engineer. */ "Scan QR code" = "Сканировать QR код"; +/* No comment provided by engineer. */ +"Scan server QR code" = "Сканировать QR код сервера"; + /* No comment provided by engineer. */ "Search" = "Поиск"; @@ -1562,6 +1601,9 @@ /* No comment provided by engineer. */ "secret" = "секрет"; +/* server test step */ +"Secure queue" = "Защита очереди"; + /* No comment provided by engineer. */ "Send direct message" = "Отправить сообщение"; @@ -1589,6 +1631,12 @@ /* notification */ "Sent file event" = "Отправка файла"; +/* server test error */ +"Server requires authorization to create queues, check password" = "Сервер требует авторизации для создания очередей, проверьте пароль"; + +/* No comment provided by engineer. */ +"Server test failed!" = "Ошибка теста сервера!"; + /* No comment provided by engineer. */ "Servers" = "Серверы"; @@ -1640,9 +1688,6 @@ /* No comment provided by engineer. */ "SMP servers" = "SMP серверы"; -/* No comment provided by engineer. */ -"SMP servers (one per line)" = "SMP серверы (один на строке)"; - /* notification title */ "Somebody" = "Контакт"; @@ -1709,6 +1754,18 @@ /* No comment provided by engineer. */ "TCP_KEEPINTVL" = "TCP_KEEPINTVL"; +/* server test failure */ +"Test failed at step %@." = "Ошибка теста на шаге %@."; + +/* No comment provided by engineer. */ +"Test server" = "Тестировать сервер"; + +/* No comment provided by engineer. */ +"Test servers" = "Тестировать серверы"; + +/* No comment provided by engineer. */ +"Tests failed!" = "Ошибка тестов!"; + /* No comment provided by engineer. */ "Thank you for installing SimpleX Chat!" = "Спасибо, что установили SimpleX Chat!"; @@ -1862,6 +1919,12 @@ /* No comment provided by engineer. */ "Use chat" = "Использовать чат"; +/* No comment provided by engineer. */ +"Use for new connections" = "Использовать для новых соединений"; + +/* No comment provided by engineer. */ +"Use server" = "Использовать сервер"; + /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "Использовать серверы предосталенные SimpleX Chat?"; @@ -2117,6 +2180,12 @@ /* No comment provided by engineer. */ "Your random profile" = "Ваш случайный профиль"; +/* No comment provided by engineer. */ +"Your server" = "Ваш сервер"; + +/* No comment provided by engineer. */ +"Your server address" = "Адрес вашего сервер"; + /* No comment provided by engineer. */ "Your settings" = "Настройки"; diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 916bff024f..641da2467f 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -645,7 +645,7 @@ viewSMPTestResult :: Maybe SMPTestFailure -> [StyledString] viewSMPTestResult = \case Just SMPTestFailure {testStep, testError} -> result - <> ["Server requires authentication to create queues, check password" | testStep == TSCreateQueue && testError == SMP SMP.AUTH] + <> ["Server requires authorization to create queues, check password" | testStep == TSCreateQueue && testError == SMP SMP.AUTH] <> ["Possibly, certificate fingerprint in server address is incorrect" | testStep == TSConnect && testError == BROKER NETWORK] where result = ["SMP server test failed at " <> plain (drop 2 $ show testStep) <> ", error: " <> plain (strEncode testError)]