diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift
index 75c9b4d60..fce1705c6 100644
--- a/apps/ios/Shared/Model/SimpleXAPI.swift
+++ b/apps/ios/Shared/Model/SimpleXAPI.swift
@@ -220,7 +220,7 @@ func loadChat(chat: Chat, search: String = "") {
}
}
-func apiSendMessage(type: ChatType, id: Int64, file: String?, quotedItemId: Int64?, msg: MsgContent) async throws -> ChatItem {
+func apiSendMessage(type: ChatType, id: Int64, file: String?, quotedItemId: Int64?, msg: MsgContent) async -> ChatItem? {
let chatModel = ChatModel.shared
let cmd: ChatCommand = .apiSendMessage(type: type, id: id, file: file, quotedItemId: quotedItemId, msg: msg)
let r: ChatResponse
@@ -233,14 +233,27 @@ func apiSendMessage(type: ChatType, id: Int64, file: String?, quotedItemId: Int6
chatModel.messageDelivery[cItem.id] = endTask
return cItem
}
+ if !networkErrorAlert(r) {
+ sendMessageErrorAlert(r)
+ }
endTask()
+ return nil
} else {
r = await chatSendCmd(cmd, bgDelay: msgDelay)
if case let .newChatItem(aChatItem) = r {
return aChatItem.chatItem
}
+ sendMessageErrorAlert(r)
+ return nil
}
- throw r
+}
+
+private func sendMessageErrorAlert(_ r: ChatResponse) {
+ logger.error("apiSendMessage error: \(String(describing: r))")
+ AlertManager.shared.showAlertMsg(
+ title: "Error sending message",
+ message: "Error: \(String(describing: r))"
+ )
}
func apiUpdateChatItem(type: ChatType, id: Int64, itemId: Int64, msg: MsgContent) async throws -> ChatItem {
@@ -335,13 +348,14 @@ func apiGroupMemberInfo(_ groupId: Int64, _ groupMemberId: Int64) async throws -
throw r
}
-func apiAddContact() throws -> String {
+func apiAddContact() -> String? {
let r = chatSendCmdSync(.addContact, bgTask: false)
if case let .invitation(connReqInvitation) = r { return connReqInvitation }
- throw r
+ connectionErrorAlert(r)
+ return nil
}
-func apiConnect(connReq: String) async throws -> ConnReqType? {
+func apiConnect(connReq: String) async -> ConnReqType? {
let r = await chatSendCmd(.connect(connReq: connReq))
let am = AlertManager.shared
switch r {
@@ -359,18 +373,6 @@ func apiConnect(connReq: String) async throws -> ConnReqType? {
message: "Please check that you used the correct link or ask your contact to send you another one."
)
return nil
- case .chatCmdError(.errorAgent(.BROKER(.TIMEOUT))):
- am.showAlertMsg(
- title: "Connection timeout",
- message: "Please check your network connection and try again."
- )
- return nil
- case .chatCmdError(.errorAgent(.BROKER(.NETWORK))):
- am.showAlertMsg(
- title: "Connection error",
- message: "Please check your network connection and try again."
- )
- return nil
case .chatCmdError(.errorAgent(.SMP(.AUTH))):
am.showAlertMsg(
title: "Connection error (AUTH)",
@@ -384,10 +386,19 @@ func apiConnect(connReq: String) async throws -> ConnReqType? {
message: "It seems like you are already connected via this link. If it is not the case, there was an error (\(responseError(r)))."
)
return nil
- } else {
- throw r
}
- default: throw r
+ default: ()
+ }
+ connectionErrorAlert(r)
+ return nil
+}
+
+private func connectionErrorAlert(_ r: ChatResponse) {
+ if !networkErrorAlert(r) {
+ AlertManager.shared.showAlertMsg(
+ title: "Connection error",
+ message: "Error: \(String(describing: r))"
+ )
}
}
@@ -404,8 +415,20 @@ func deleteChat(_ chat: Chat) async {
let cInfo = chat.chatInfo
try await apiDeleteChat(type: cInfo.chatType, id: cInfo.apiId)
DispatchQueue.main.async { ChatModel.shared.removeChat(cInfo.id) }
- } catch {
+ } catch let error {
logger.error("deleteChat apiDeleteChat error: \(responseError(error))")
+ switch error as? ChatResponse {
+ case let .chatCmdError(.error(.contactGroups(contact, groupNames))):
+ AlertManager.shared.showAlertMsg(
+ title: "Can't delete contact!",
+ message: "Contact \(contact.displayName) cannot be deleted, they are a member of the group(s) \(groupNames.joined(separator: ", "))."
+ )
+ default:
+ AlertManager.shared.showAlertMsg(
+ title: "Error deleting chat!",
+ message: "Error: \(responseError(error))"
+ )
+ }
}
}
@@ -469,10 +492,24 @@ func apiGetUserAddress() throws -> String? {
}
}
-func apiAcceptContactRequest(contactReqId: Int64) async throws -> Contact {
+func apiAcceptContactRequest(contactReqId: Int64) async -> Contact? {
let r = await chatSendCmd(.apiAcceptContact(contactReqId: contactReqId))
+ let am = AlertManager.shared
+
if case let .acceptingContactRequest(contact) = r { return contact }
- throw r
+ if case .chatCmdError(.errorAgent(.SMP(.AUTH))) = r {
+ am.showAlertMsg(
+ title: "Connection error (AUTH)",
+ message: "Sender may have deleted the connection request."
+ )
+ } else if !networkErrorAlert(r) {
+ logger.error("apiAcceptContactRequest error: \(String(describing: r))")
+ am.showAlertMsg(
+ title: "Error accepting contact request",
+ message: "Error: \(String(describing: r))"
+ )
+ }
+ return nil
}
func apiRejectContactRequest(contactReqId: Int64) async throws {
@@ -486,27 +523,54 @@ func apiChatRead(type: ChatType, id: Int64, itemRange: (Int64, Int64)) async thr
}
func receiveFile(fileId: Int64) async {
- do {
- let chatItem = try await apiReceiveFile(fileId: fileId)
+ if let chatItem = await apiReceiveFile(fileId: fileId) {
DispatchQueue.main.async { chatItemSimpleUpdate(chatItem) }
- } catch let error {
- logger.error("receiveFile error: \(responseError(error))")
}
}
-func apiReceiveFile(fileId: Int64) async throws -> AChatItem {
+func apiReceiveFile(fileId: Int64) async -> AChatItem? {
let r = await chatSendCmd(.receiveFile(fileId: fileId))
+ let am = AlertManager.shared
if case let .rcvFileAccepted(chatItem) = r { return chatItem }
- throw r
+ if case .rcvFileAcceptedSndCancelled = r {
+ am.showAlertMsg(
+ title: "Cannot receive file",
+ message: "Sender cancelled file transfer."
+ )
+ } else if !networkErrorAlert(r) {
+ logger.error("apiReceiveFile error: \(String(describing: r))")
+ am.showAlertMsg(
+ title: "Error receiving file",
+ message: "Error: \(String(describing: r))"
+ )
+ }
+ return nil
+}
+
+func networkErrorAlert(_ r: ChatResponse) -> Bool {
+ let am = AlertManager.shared
+ switch r {
+ case .chatCmdError(.errorAgent(.BROKER(.TIMEOUT))):
+ am.showAlertMsg(
+ title: "Connection timeout",
+ message: "Please check your network connection and try again."
+ )
+ return true
+ case .chatCmdError(.errorAgent(.BROKER(.NETWORK))):
+ am.showAlertMsg(
+ title: "Connection error",
+ message: "Please check your network connection and try again."
+ )
+ return true
+ default:
+ return false
+ }
}
func acceptContactRequest(_ contactRequest: UserContactRequest) async {
- do {
- let contact = try await apiAcceptContactRequest(contactReqId: contactRequest.apiId)
+ if let contact = await apiAcceptContactRequest(contactReqId: contactRequest.apiId) {
let chat = Chat(chatInfo: ChatInfo.direct(contact: contact), chatItems: [])
DispatchQueue.main.async { ChatModel.shared.replaceChat(contactRequest.id, chat) }
- } catch let error {
- logger.error("acceptContactRequest error: \(responseError(error))")
}
}
diff --git a/apps/ios/Shared/Views/Chat/ChatInfoView.swift b/apps/ios/Shared/Views/Chat/ChatInfoView.swift
index 667ae0e59..3f5274b17 100644
--- a/apps/ios/Shared/Views/Chat/ChatInfoView.swift
+++ b/apps/ios/Shared/Views/Chat/ChatInfoView.swift
@@ -56,10 +56,18 @@ struct ChatInfoView: View {
enum ChatInfoViewAlert: Identifiable {
case deleteContactAlert
+ case contactGroupsAlert(groupNames: [GroupName])
case clearChatAlert
case networkStatusAlert
- var id: ChatInfoViewAlert { get { self } }
+ var id: String {
+ switch self {
+ case .deleteContactAlert: return "deleteContactAlert"
+ case .contactGroupsAlert: return "contactGroupsAlert"
+ case .clearChatAlert: return "clearChatAlert"
+ case .networkStatusAlert: return "networkStatusAlert"
+ }
+ }
}
var body: some View {
@@ -111,6 +119,7 @@ struct ChatInfoView: View {
.alert(item: $alert) { alertItem in
switch(alertItem) {
case .deleteContactAlert: return deleteContactAlert()
+ case let .contactGroupsAlert(groupNames): return contactGroupsAlert(groupNames)
case .clearChatAlert: return clearChatAlert()
case .networkStatusAlert: return networkStatusAlert()
}
@@ -221,6 +230,9 @@ struct ChatInfoView: View {
}
} catch let error {
logger.error("deleteContactAlert apiDeleteChat error: \(error.localizedDescription)")
+ if case let .chatCmdError(.error(.contactGroups(_, groupNames))) = error as? ChatResponse {
+ alert = .contactGroupsAlert(groupNames: groupNames)
+ }
}
}
},
@@ -228,6 +240,13 @@ struct ChatInfoView: View {
)
}
+ private func contactGroupsAlert(_ groupNames: [GroupName]) -> Alert {
+ Alert(
+ title: Text("Can't delete contact!"),
+ message: Text("Contact \(contact.displayName) cannot be deleted, they are a member of the group(s) \(groupNames.joined(separator: ", ")).")
+ )
+ }
+
private func clearChatAlert() -> Alert {
Alert(
title: Text("Clear conversation?"),
diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift
index c97719dc1..eb0120204 100644
--- a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift
+++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift
@@ -284,10 +284,10 @@ struct ComposeView: View {
logger.debug("ChatView sendMessage")
Task {
logger.debug("ChatView sendMessage: in Task")
- do {
- switch composeState.contextItem {
- case let .editingItem(chatItem: ei):
- if let oldMsgContent = ei.content.msgContent {
+ switch composeState.contextItem {
+ case let .editingItem(chatItem: ei):
+ if let oldMsgContent = ei.content.msgContent {
+ do {
let chatItem = try await apiUpdateChatItem(
type: chat.chatInfo.chatType,
id: chat.chatInfo.apiId,
@@ -297,52 +297,53 @@ struct ComposeView: View {
DispatchQueue.main.async {
let _ = self.chatModel.upsertChatItem(self.chat.chatInfo, chatItem)
}
- }
- default:
- var mc: MsgContent? = nil
- var file: String? = nil
- switch (composeState.preview) {
- case .noPreview:
- mc = .text(composeState.message)
- case .linkPreview:
- mc = checkLinkPreview()
- case let .imagePreview(imagePreview: image):
- if let uiImage = chosenImage,
- let savedFile = saveImage(uiImage) {
- mc = .image(text: composeState.message, image: image)
- file = savedFile
- }
- case .filePreview:
- if let fileURL = chosenFile,
- let savedFile = saveFileFromURL(fileURL) {
- mc = .file(composeState.message)
- file = savedFile
- }
- }
-
- var quotedItemId: Int64? = nil
- switch (composeState.contextItem) {
- case let .quotedItem(chatItem: quotedItem):
- quotedItemId = quotedItem.id
- default:
- quotedItemId = nil
- }
- if let mc = mc {
- let chatItem = try await apiSendMessage(
- type: chat.chatInfo.chatType,
- id: chat.chatInfo.apiId,
- file: file,
- quotedItemId: quotedItemId,
- msg: mc
- )
- chatModel.addChatItem(chat.chatInfo, chatItem)
+ } catch {
+ clearState()
+ logger.error("ChatView.sendMessage error: \(error.localizedDescription)")
+ AlertManager.shared.showAlertMsg(title: "Error updating message", message: "Error: \(responseError(error))")
}
}
- clearState()
- } catch {
- clearState()
- logger.error("ChatView.sendMessage error: \(error.localizedDescription)")
+ default:
+ var mc: MsgContent? = nil
+ var file: String? = nil
+ switch (composeState.preview) {
+ case .noPreview:
+ mc = .text(composeState.message)
+ case .linkPreview:
+ mc = checkLinkPreview()
+ case let .imagePreview(imagePreview: image):
+ if let uiImage = chosenImage,
+ let savedFile = saveImage(uiImage) {
+ mc = .image(text: composeState.message, image: image)
+ file = savedFile
+ }
+ case .filePreview:
+ if let fileURL = chosenFile,
+ let savedFile = saveFileFromURL(fileURL) {
+ mc = .file(composeState.message)
+ file = savedFile
+ }
+ }
+
+ var quotedItemId: Int64? = nil
+ switch (composeState.contextItem) {
+ case let .quotedItem(chatItem: quotedItem):
+ quotedItemId = quotedItem.id
+ default:
+ quotedItemId = nil
+ }
+ if let mc = mc,
+ let chatItem = await apiSendMessage(
+ type: chat.chatInfo.chatType,
+ id: chat.chatInfo.apiId,
+ file: file,
+ quotedItemId: quotedItemId,
+ msg: mc
+ ) {
+ chatModel.addChatItem(chat.chatInfo, chatItem)
+ }
}
+ clearState()
}
}
diff --git a/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift b/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift
index 2ab0fc516..1ada8c240 100644
--- a/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift
+++ b/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift
@@ -128,7 +128,14 @@ struct AddGroupMembersView: View {
await MainActor.run { dismiss() }
if let cb = addedMembersCb { cb(selectedContacts) }
} catch {
- alert = .error(title: "Error adding member(s)", error: responseError(error))
+ switch error as? ChatResponse {
+ case .chatCmdError(.errorAgent(.BROKER(.TIMEOUT))):
+ alert = .error(title: "Connection timeout", error: "Please check your network connection and try again.")
+ case .chatCmdError(.errorAgent(.BROKER(.NETWORK))):
+ alert = .error(title: "Connection error", error: "Please check your network connection and try again.")
+ default:
+ alert = .error(title: "Error adding member(s)", error: responseError(error))
+ }
}
}
}
diff --git a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift
index 4e19412bd..226aabeb7 100644
--- a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift
+++ b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift
@@ -345,9 +345,15 @@ func joinGroup(_ groupId: Int64) {
await deleteGroup()
}
} catch let error {
- let err = responseError(error)
- AlertManager.shared.showAlert(Alert(title: Text("Error joining group"), message: Text(err)))
- logger.error("apiJoinGroup error: \(err)")
+ switch error as? ChatResponse {
+ case .chatCmdError(.errorAgent(.BROKER(.TIMEOUT))):
+ AlertManager.shared.showAlertMsg(title: "Connection timeout", message: "Please check your network connection and try again.")
+ case .chatCmdError(.errorAgent(.BROKER(.NETWORK))):
+ AlertManager.shared.showAlertMsg(title: "Connection error", message: "Please check your network connection and try again.")
+ default:
+ logger.error("apiJoinGroup error: \(responseError(error))")
+ AlertManager.shared.showAlertMsg(title: "Error joining group", message: "\(responseError(error))")
+ }
}
func deleteGroup() async {
diff --git a/apps/ios/Shared/Views/NewChat/NewChatButton.swift b/apps/ios/Shared/Views/NewChat/NewChatButton.swift
index e4571cd09..2fa981caf 100644
--- a/apps/ios/Shared/Views/NewChat/NewChatButton.swift
+++ b/apps/ios/Shared/Views/NewChat/NewChatButton.swift
@@ -47,14 +47,9 @@ struct NewChatButton: View {
}
func addContactAction() {
- do {
- connReq = try apiAddContact()
+ if let cReq = apiAddContact() {
+ connReq = cReq
actionSheet = .createLink
- } catch {
- DispatchQueue.global().async {
- connectionErrorAlert(error)
- }
- logger.error("NewChatButton.addContactAction apiAddContact error: \(error.localizedDescription)")
}
}
}
@@ -66,28 +61,19 @@ enum ConnReqType: Equatable {
func connectViaLink(_ connectionLink: String, _ dismiss: DismissAction? = nil) {
Task {
- do {
- let res = try await apiConnect(connReq: connectionLink)
+ if let connReqType = await apiConnect(connReq: connectionLink) {
DispatchQueue.main.async {
dismiss?()
- if let connReqType = res {
- connectionReqSentAlert(connReqType)
- }
+ connectionReqSentAlert(connReqType)
}
- } catch {
- logger.error("connectViaLink apiConnect error: \(responseError(error))")
+ } else {
DispatchQueue.main.async {
dismiss?()
- connectionErrorAlert(error)
}
}
}
}
-func connectionErrorAlert(_ error: Error) {
- AlertManager.shared.showAlertMsg(title: "Connection error", message: "Error: \(responseError(error))")
-}
-
func connectionReqSentAlert(_ type: ConnReqType) {
AlertManager.shared.showAlertMsg(
title: "Connection request sent!",
diff --git a/apps/ios/Shared/Views/Onboarding/MakeConnection.swift b/apps/ios/Shared/Views/Onboarding/MakeConnection.swift
index d3b46c991..81bee84d9 100644
--- a/apps/ios/Shared/Views/Onboarding/MakeConnection.swift
+++ b/apps/ios/Shared/Views/Onboarding/MakeConnection.swift
@@ -112,14 +112,9 @@ struct MakeConnection: View {
}
private func addContactAction() {
- do {
- connReq = try apiAddContact()
+ if let cReq = apiAddContact() {
+ connReq = cReq
actionSheet = .createLink
- } catch {
- DispatchQueue.global().async {
- connectionErrorAlert(error)
- }
- logger.error("NewChatButton.addContactAction apiAddContact error: \(error.localizedDescription)")
}
}
diff --git a/apps/ios/Shared/Views/UserSettings/UserAddress.swift b/apps/ios/Shared/Views/UserSettings/UserAddress.swift
index 16e4994d3..c21bfbf6b 100644
--- a/apps/ios/Shared/Views/UserSettings/UserAddress.swift
+++ b/apps/ios/Shared/Views/UserSettings/UserAddress.swift
@@ -7,10 +7,23 @@
//
import SwiftUI
+import SimpleXChat
struct UserAddress: View {
@EnvironmentObject var chatModel: ChatModel
- @State private var deleteAddressAlert = false
+ @State private var alert: UserAddressAlert?
+
+ private enum UserAddressAlert: Identifiable {
+ case deleteAddress
+ case error(title: LocalizedStringKey, error: String = "")
+
+ var id: String {
+ switch self {
+ case .deleteAddress: return "deleteAddress"
+ case let .error(title, _): return "error \(title)"
+ }
+ }
+ }
var body: some View {
ScrollView {
@@ -27,28 +40,10 @@ struct UserAddress: View {
}
.padding()
- Button(role: .destructive) { deleteAddressAlert = true } label: {
+ Button(role: .destructive) { alert = .deleteAddress } label: {
Label("Delete address", systemImage: "trash")
}
.padding()
- .alert(isPresented: $deleteAddressAlert) {
- Alert(
- title: Text("Delete address?"),
- message: Text("All your contacts will remain connected"),
- primaryButton: .destructive(Text("Delete")) {
- Task {
- do {
- try await apiDeleteUserAddress()
- DispatchQueue.main.async {
- chatModel.userAddress = nil
- }
- } catch let error {
- logger.error("UserAddress apiDeleteUserAddress: \(error.localizedDescription)")
- }
- }
- }, secondaryButton: .cancel()
- )
- }
}
.frame(maxWidth: .infinity)
} else {
@@ -61,6 +56,14 @@ struct UserAddress: View {
}
} catch let error {
logger.error("UserAddress apiCreateUserAddress: \(error.localizedDescription)")
+ switch error as? ChatResponse {
+ case .chatCmdError(.errorAgent(.BROKER(.TIMEOUT))):
+ alert = .error(title: "Connection timeout", error: "Please check your network connection and try again.")
+ case .chatCmdError(.errorAgent(.BROKER(.NETWORK))):
+ alert = .error(title: "Connection error", error: "Please check your network connection and try again.")
+ default:
+ alert = .error(title: "Error creating address", error: "Error: \(responseError(error))")
+ }
}
}
} label: { Label("Create address", systemImage: "qrcode") }
@@ -69,6 +72,29 @@ struct UserAddress: View {
}
.padding()
.frame(maxHeight: .infinity, alignment: .top)
+ .alert(item: $alert) { alert in
+ switch alert {
+ case .deleteAddress:
+ return Alert(
+ title: Text("Delete address?"),
+ message: Text("All your contacts will remain connected"),
+ primaryButton: .destructive(Text("Delete")) {
+ Task {
+ do {
+ try await apiDeleteUserAddress()
+ DispatchQueue.main.async {
+ chatModel.userAddress = nil
+ }
+ } catch let error {
+ logger.error("UserAddress apiDeleteUserAddress: \(error.localizedDescription)")
+ }
+ }
+ }, secondaryButton: .cancel()
+ )
+ case let .error(title, error):
+ return Alert(title: Text(title), message: Text("\(error)"))
+ }
+ }
}
}
}
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 bfca67bef..73768a7d8 100644
--- a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff
+++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff
@@ -308,6 +308,11 @@
Calls
No comment provided by engineer.
+
+ Can't delete contact!
+ Can't delete contact!
+ No comment provided by engineer.
+
Can't invite contact!
Can't invite contact!
@@ -328,6 +333,11 @@
Cannot access keychain to save database password
No comment provided by engineer.
+
+ Cannot receive file
+ Cannot receive file
+ No comment provided by engineer.
+
Change database passphrase?
Change database passphrase?
@@ -498,6 +508,11 @@
Connection timeout
No comment provided by engineer.
+
+ Contact %@ cannot be deleted, they are a member of the group(s) %@.
+ Contact %@ cannot be deleted, they are a member of the group(s) %@.
+ No comment provided by engineer.
+
Contact already exists
Contact already exists
@@ -911,6 +926,11 @@
Enter passphrase…
No comment provided by engineer.
+
+ Error accepting contact request
+ Error accepting contact request
+ No comment provided by engineer.
+
Error accessing database file
Error accessing database file
@@ -921,6 +941,11 @@
Error adding member(s)
No comment provided by engineer.
+
+ Error creating address
+ Error creating address
+ No comment provided by engineer.
+
Error creating group
Error creating group
@@ -931,6 +956,11 @@
Error deleting chat database
No comment provided by engineer.
+
+ Error deleting chat!
+ Error deleting chat!
+ No comment provided by engineer.
+
Error deleting database
Error deleting database
@@ -971,6 +1001,11 @@
Error joining group
No comment provided by engineer.
+
+ Error receiving file
+ Error receiving file
+ No comment provided by engineer.
+
Error saving ICE servers
Error saving ICE servers
@@ -986,6 +1021,11 @@
Error saving group profile
No comment provided by engineer.
+
+ Error sending message
+ Error sending message
+ No comment provided by engineer.
+
Error starting chat
Error starting chat
@@ -996,6 +1036,11 @@
Error stopping chat
No comment provided by engineer.
+
+ Error updating message
+ Error updating message
+ No comment provided by engineer.
+
Error updating settings
Error updating settings
@@ -1928,6 +1973,16 @@ We will be adding server redundancy to prevent lost messages.
Send notifications:
No comment provided by engineer.
+
+ Sender cancelled file transfer.
+ Sender cancelled file transfer.
+ No comment provided by engineer.
+
+
+ Sender may have deleted the connection request.
+ Sender may have deleted the connection request.
+ No comment provided by engineer.
+
Sending via
Sending via
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 6cb28901b..a608cf4e4 100644
--- a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff
+++ b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff
@@ -308,6 +308,11 @@
Звонки
No comment provided by engineer.
+
+ Can't delete contact!
+ Невозможно удалить контакт!
+ No comment provided by engineer.
+
Can't invite contact!
Нельзя пригласить контакт!
@@ -328,6 +333,11 @@
Ошибка доступа к Keychain при сохранении пароля
No comment provided by engineer.
+
+ Cannot receive file
+ Невозможно получить файл
+ No comment provided by engineer.
+
Change database passphrase?
Поменять пароль базы данных?
@@ -498,6 +508,11 @@
Превышено время соединения
No comment provided by engineer.
+
+ Contact %@ cannot be deleted, they are a member of the group(s) %@.
+ Контакт %@ не может быть удален, так как является членом групп(ы) %@.
+ No comment provided by engineer.
+
Contact already exists
Существующий контакт
@@ -911,6 +926,11 @@
Введите пароль…
No comment provided by engineer.
+
+ Error accepting contact request
+ Ошибка при принятии запроса на соединение
+ No comment provided by engineer.
+
Error accessing database file
Ошибка при доступе к данным чата
@@ -921,6 +941,11 @@
Ошибка при добавлении членов группы
No comment provided by engineer.
+
+ Error creating address
+ Ошибка при создании адреса
+ No comment provided by engineer.
+
Error creating group
Ошибка при создании группы
@@ -931,6 +956,11 @@
Ошибка при удалении данных чата
No comment provided by engineer.
+
+ Error deleting chat!
+ Ошибка при удалении чата!
+ No comment provided by engineer.
+
Error deleting database
Ошибка при удалении данных чата
@@ -971,6 +1001,11 @@
Ошибка приглашения
No comment provided by engineer.
+
+ Error receiving file
+ Ошибка при получении файла
+ No comment provided by engineer.
+
Error saving ICE servers
Ошибка при сохранении ICE серверов
@@ -986,6 +1021,11 @@
Ошибка при сохранении профиля группы
No comment provided by engineer.
+
+ Error sending message
+ Ошибка при отправке сообщения
+ No comment provided by engineer.
+
Error starting chat
Ошибка при запуске чата
@@ -996,6 +1036,11 @@
Ошибка при остановке чата
No comment provided by engineer.
+
+ Error updating message
+ Ошибка при обновлении сообщения
+ No comment provided by engineer.
+
Error updating settings
Ошибка при сохранении настроек сети
@@ -1928,6 +1973,16 @@ We will be adding server redundancy to prevent lost messages.
Отправлять уведомления:
No comment provided by engineer.
+
+ Sender cancelled file transfer.
+ Отправитель отменил передачу файла.
+ No comment provided by engineer.
+
+
+ Sender may have deleted the connection request.
+ Отправитель мог удалить запрос на соединение.
+ No comment provided by engineer.
+
Sending via
Отправка через
diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift
index 6d548e86f..de3311725 100644
--- a/apps/ios/SimpleXChat/APITypes.swift
+++ b/apps/ios/SimpleXChat/APITypes.swift
@@ -302,6 +302,7 @@ public enum ChatResponse: Decodable, Error {
case groupUpdated(toGroup: GroupInfo)
// receiving file events
case rcvFileAccepted(chatItem: AChatItem)
+ case rcvFileAcceptedSndCancelled(rcvFileTransfer: RcvFileTransfer)
case rcvFileStart(chatItem: AChatItem)
case rcvFileComplete(chatItem: AChatItem)
// sending file events
@@ -392,6 +393,7 @@ public enum ChatResponse: Decodable, Error {
case .groupRemoved: return "groupRemoved"
case .groupUpdated: return "groupUpdated"
case .rcvFileAccepted: return "rcvFileAccepted"
+ case .rcvFileAcceptedSndCancelled: return "rcvFileAcceptedSndCancelled"
case .rcvFileStart: return "rcvFileStart"
case .rcvFileComplete: return "rcvFileComplete"
case .sndFileStart: return "sndFileStart"
@@ -484,6 +486,7 @@ public enum ChatResponse: Decodable, Error {
case let .groupRemoved(groupInfo): return String(describing: groupInfo)
case let .groupUpdated(toGroup): return String(describing: toGroup)
case let .rcvFileAccepted(chatItem): return String(describing: chatItem)
+ case .rcvFileAcceptedSndCancelled: return noDetails
case let .rcvFileStart(chatItem): return String(describing: chatItem)
case let .rcvFileComplete(chatItem): return String(describing: chatItem)
case let .sndFileStart(chatItem, _): return String(describing: chatItem)
diff --git a/apps/ios/ru.lproj/Localizable.strings b/apps/ios/ru.lproj/Localizable.strings
index 181355ce0..60219eb96 100644
--- a/apps/ios/ru.lproj/Localizable.strings
+++ b/apps/ios/ru.lproj/Localizable.strings
@@ -221,6 +221,9 @@
/* No comment provided by engineer. */
"Calls" = "Звонки";
+/* No comment provided by engineer. */
+"Can't delete contact!" = "Невозможно удалить контакт!";
+
/* No comment provided by engineer. */
"Can't invite contact!" = "Нельзя пригласить контакт!";
@@ -233,6 +236,9 @@
/* No comment provided by engineer. */
"Cannot access keychain to save database password" = "Ошибка доступа к Keychain при сохранении пароля";
+/* No comment provided by engineer. */
+"Cannot receive file" = "Невозможно получить файл";
+
/* No comment provided by engineer. */
"Change database passphrase?" = "Поменять пароль базы данных?";
@@ -374,6 +380,9 @@
/* connection information */
"connection:%@" = "connection:%@";
+/* No comment provided by engineer. */
+"Contact %@ cannot be deleted, they are a member of the group(s) %@." = "Контакт %@ не может быть удален, так как является членом групп(ы) %@.";
+
/* No comment provided by engineer. */
"Contact already exists" = "Существующий контакт";
@@ -650,18 +659,27 @@
/* No comment provided by engineer. */
"error" = "ошибка";
+/* No comment provided by engineer. */
+"Error accepting contact request" = "Ошибка при принятии запроса на соединение";
+
/* No comment provided by engineer. */
"Error accessing database file" = "Ошибка при доступе к данным чата";
/* No comment provided by engineer. */
"Error adding member(s)" = "Ошибка при добавлении членов группы";
+/* No comment provided by engineer. */
+"Error creating address" = "Ошибка при создании адреса";
+
/* No comment provided by engineer. */
"Error creating group" = "Ошибка при создании группы";
/* No comment provided by engineer. */
"Error deleting chat database" = "Ошибка при удалении данных чата";
+/* No comment provided by engineer. */
+"Error deleting chat!" = "Ошибка при удалении чата!";
+
/* No comment provided by engineer. */
"Error deleting database" = "Ошибка при удалении данных чата";
@@ -686,6 +704,9 @@
/* No comment provided by engineer. */
"Error joining group" = "Ошибка приглашения";
+/* No comment provided by engineer. */
+"Error receiving file" = "Ошибка при получении файла";
+
/* No comment provided by engineer. */
"Error saving group profile" = "Ошибка при сохранении профиля группы";
@@ -695,12 +716,18 @@
/* No comment provided by engineer. */
"Error saving SMP servers" = "Ошибка при сохранении SMP серверов";
+/* No comment provided by engineer. */
+"Error sending message" = "Ошибка при отправке сообщения";
+
/* No comment provided by engineer. */
"Error starting chat" = "Ошибка при запуске чата";
/* No comment provided by engineer. */
"Error stopping chat" = "Ошибка при остановке чата";
+/* No comment provided by engineer. */
+"Error updating message" = "Ошибка при обновлении сообщения";
+
/* No comment provided by engineer. */
"Error updating settings" = "Ошибка при сохранении настроек сети";
@@ -1340,6 +1367,12 @@
/* No comment provided by engineer. */
"Send notifications:" = "Отправлять уведомления:";
+/* No comment provided by engineer. */
+"Sender cancelled file transfer." = "Отправитель отменил передачу файла.";
+
+/* No comment provided by engineer. */
+"Sender may have deleted the connection request." = "Отправитель мог удалить запрос на соединение.";
+
/* No comment provided by engineer. */
"Sending via" = "Отправка через";
diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs
index 06ecffdd8..ae62ccec4 100644
--- a/src/Simplex/Chat.hs
+++ b/src/Simplex/Chat.hs
@@ -271,7 +271,6 @@ processChatCommand = \case
setupSndFileTransfer :: Contact -> m (Maybe (FileInvitation, CIFile 'MDSnd))
setupSndFileTransfer ct = forM file_ $ \file -> do
(fileSize, chSize) <- checkSndFile file
- -- [async agent commands] keep command synchronous, but process error
(agentConnId, fileConnReq) <- withAgent $ \a -> createConnection a True SCMInvitation
let fileName = takeFileName file
fileInvitation = FileInvitation {fileName, fileSize, fileConnReq = Just fileConnReq}
@@ -765,7 +764,6 @@ processChatCommand = \case
case contactMember contact members of
Nothing -> do
gVar <- asks idsDrg
- -- [async agent commands] keep command synchronous, but process error
(agentConnId, cReq) <- withAgent $ \a -> createConnection a True SCMInvitation
member <- withStore $ \db -> createNewContactMember db gVar user groupId contact memRole agentConnId cReq
sendInvitation member cReq
@@ -778,7 +776,6 @@ processChatCommand = \case
APIJoinGroup groupId -> withUser $ \user@User {userId} -> do
ReceivedGroupInvitation {fromMember, connRequest, groupInfo = g@GroupInfo {membership}} <- withStore $ \db -> getGroupInvitation db user groupId
withChatLock . procCmd $ do
- -- [async agent commands] keep command synchronous, but process error
agentConnId <- withAgent $ \a -> joinConnection a True connRequest . directMessage $ XGrpAcpt (memberId (membership :: GroupMember))
withStore' $ \db -> do
createMemberConnection db userId fromMember agentConnId
@@ -1131,7 +1128,6 @@ acceptFileReceive user@User {userId} RcvFileTransfer {fileId, fileInvitation = F
case fileConnReq of
-- direct file protocol
Just connReq -> do
- -- [async agent commands] keep command synchronous, but process error
agentConnId <- withAgent $ \a -> joinConnection a True connReq . directMessage $ XFileAcpt fName
filePath <- getRcvFilePath filePath_ fName
withStore $ \db -> acceptRcvFileTransfer db user fileId agentConnId ConnJoined filePath