diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index a9d94be217..c9772090e3 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -43,6 +43,21 @@ private func addTermItem(_ items: inout [TerminalItem], _ item: TerminalItem) { items.append(item) } +class ItemsModel: ObservableObject { + static let shared = ItemsModel() + private let publisher = ObservableObjectPublisher() + private var bag = Set() + var reversedChatItems: [ChatItem] = [] { + willSet { publisher.send() } + } + init() { + publisher + .throttle(for: 0.25, scheduler: DispatchQueue.main, latest: true) + .sink { self.objectWillChange.send() } + .store(in: &bag) + } +} + final class ChatModel: ObservableObject { @Published var onboardingStage: OnboardingStage? @Published var setDeliveryReceipts = false @@ -69,7 +84,6 @@ final class ChatModel: ObservableObject { @Published var networkStatuses: Dictionary = [:] // current chat @Published var chatId: String? - @Published var reversedChatItems: [ChatItem] = [] var chatItemStatuses: Dictionary = [:] @Published var chatToTop: String? @Published var groupMembers: [GMember] = [] @@ -117,6 +131,8 @@ final class ChatModel: ObservableObject { static let shared = ChatModel() + let im = ItemsModel.shared + static var ok: Bool { ChatModel.shared.chatDbStatus == .ok } let ntfEnableLocal = true @@ -343,7 +359,7 @@ final class ChatModel: ObservableObject { var res: Bool if let chat = getChat(cInfo.id) { if let pItem = chat.chatItems.last { - if pItem.id == cItem.id || (chatId == cInfo.id && reversedChatItems.first(where: { $0.id == cItem.id }) == nil) { + if pItem.id == cItem.id || (chatId == cInfo.id && im.reversedChatItems.first(where: { $0.id == cItem.id }) == nil) { chat.chatItems = [cItem] } } else { @@ -373,7 +389,7 @@ final class ChatModel: ObservableObject { if let status = chatItemStatuses.removeValue(forKey: ci.id), case .sndNew = ci.meta.itemStatus { ci.meta.itemStatus = status } - reversedChatItems.insert(ci, at: hasLiveDummy ? 1 : 0) + im.reversedChatItems.insert(ci, at: hasLiveDummy ? 1 : 0) } return true } @@ -397,12 +413,12 @@ final class ChatModel: ObservableObject { } private func _updateChatItem(at i: Int, with cItem: ChatItem) { - reversedChatItems[i] = cItem - reversedChatItems[i].viewTimestamp = .now + im.reversedChatItems[i] = cItem + im.reversedChatItems[i].viewTimestamp = .now } func getChatItemIndex(_ cItem: ChatItem) -> Int? { - reversedChatItems.firstIndex(where: { $0.id == cItem.id }) + im.reversedChatItems.firstIndex(where: { $0.id == cItem.id }) } func removeChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) { @@ -419,7 +435,7 @@ final class ChatModel: ObservableObject { if chatId == cInfo.id { if let i = getChatItemIndex(cItem) { _ = withAnimation { - self.reversedChatItems.remove(at: i) + im.reversedChatItems.remove(at: i) } } } @@ -427,16 +443,16 @@ final class ChatModel: ObservableObject { } func nextChatItemData(_ chatItemId: Int64, previous: Bool, map: @escaping (ChatItem) -> T?) -> T? { - guard var i = reversedChatItems.firstIndex(where: { $0.id == chatItemId }) else { return nil } + guard var i = im.reversedChatItems.firstIndex(where: { $0.id == chatItemId }) else { return nil } if previous { - while i < reversedChatItems.count - 1 { + while i < im.reversedChatItems.count - 1 { i += 1 - if let res = map(reversedChatItems[i]) { return res } + if let res = map(im.reversedChatItems[i]) { return res } } } else { while i > 0 { i -= 1 - if let res = map(reversedChatItems[i]) { return res } + if let res = map(im.reversedChatItems[i]) { return res } } } return nil @@ -467,7 +483,7 @@ final class ChatModel: ObservableObject { func addLiveDummy(_ chatInfo: ChatInfo) -> ChatItem { let cItem = ChatItem.liveDummy(chatInfo.chatType) withAnimation { - reversedChatItems.insert(cItem, at: 0) + im.reversedChatItems.insert(cItem, at: 0) } return cItem } @@ -475,15 +491,15 @@ final class ChatModel: ObservableObject { func removeLiveDummy(animated: Bool = true) { if hasLiveDummy { if animated { - withAnimation { _ = reversedChatItems.removeFirst() } + withAnimation { _ = im.reversedChatItems.removeFirst() } } else { - _ = reversedChatItems.removeFirst() + _ = im.reversedChatItems.removeFirst() } } } private var hasLiveDummy: Bool { - reversedChatItems.first?.isLiveDummy == true + im.reversedChatItems.first?.isLiveDummy == true } func markChatItemsRead(_ cInfo: ChatInfo) { @@ -500,7 +516,7 @@ final class ChatModel: ObservableObject { private func markCurrentChatRead(fromIndex i: Int = 0) { var j = i - while j < reversedChatItems.count { + while j < im.reversedChatItems.count { markChatItemRead_(j) j += 1 } @@ -514,7 +530,7 @@ final class ChatModel: ObservableObject { var unreadBelow = 0 var j = i - 1 while j >= 0 { - if case .rcvNew = self.reversedChatItems[j].meta.itemStatus { + if case .rcvNew = self.im.reversedChatItems[j].meta.itemStatus { unreadBelow += 1 } j -= 1 @@ -549,7 +565,7 @@ final class ChatModel: ObservableObject { // clear current chat if chatId == cInfo.id { chatItemStatuses = [:] - reversedChatItems = [] + im.reversedChatItems = [] } } @@ -557,32 +573,58 @@ final class ChatModel: ObservableObject { if chatId == cInfo.id, let itemIndex = getChatItemIndex(cItem), let chatIndex = getChatIndex(cInfo.id), - reversedChatItems[itemIndex].isRcvNew { + im.reversedChatItems[itemIndex].isRcvNew { await MainActor.run { withTransaction(Transaction()) { // update current chat markChatItemRead_(itemIndex) // update preview - decreaseUnreadCounter(chatIndex) + unreadCollector.decreaseUnreadCounter(chatIndex) } } } } + private let unreadCollector = UnreadCollector() + + class UnreadCollector { + private let subject = PassthroughSubject() + private var bag = Set() + private var dictionary = Dictionary() + + init() { + subject + .debounce(for: 1, scheduler: DispatchQueue.main) + .sink { _ in + self.dictionary.forEach { key, value in + ChatModel.shared.decreaseUnreadCounter(key, by: value) + } + self.dictionary = Dictionary() + } + .store(in: &bag) + } + + // Only call from main thread + func decreaseUnreadCounter(_ chatIndex: Int) { + dictionary[chatIndex] = (dictionary[chatIndex] ?? 0) + 1 + subject.send(chatIndex) + } + } + private func markChatItemRead_(_ i: Int) { - let meta = reversedChatItems[i].meta + let meta = im.reversedChatItems[i].meta if case .rcvNew = meta.itemStatus { - reversedChatItems[i].meta.itemStatus = .rcvRead - reversedChatItems[i].viewTimestamp = .now + im.reversedChatItems[i].meta.itemStatus = .rcvRead + im.reversedChatItems[i].viewTimestamp = .now if meta.itemLive != true, let ttl = meta.itemTimed?.ttl { - reversedChatItems[i].meta.itemTimed?.deleteAt = .now + TimeInterval(ttl) + im.reversedChatItems[i].meta.itemTimed?.deleteAt = .now + TimeInterval(ttl) } } } - func decreaseUnreadCounter(_ chatIndex: Int) { - chats[chatIndex].chatStats.unreadCount = chats[chatIndex].chatStats.unreadCount - 1 - decreaseUnreadCounter(user: currentUser!) + func decreaseUnreadCounter(_ chatIndex: Int, by count: Int = 1) { + chats[chatIndex].chatStats.unreadCount = chats[chatIndex].chatStats.unreadCount - count + decreaseUnreadCounter(user: currentUser!, by: count) } func increaseUnreadCounter(user: any UserLike) { @@ -612,8 +654,8 @@ final class ChatModel: ObservableObject { var ns: [String] = [] if let ciCategory = chatItem.mergeCategory, var i = getChatItemIndex(chatItem) { - while i < reversedChatItems.count { - let ci = reversedChatItems[i] + while i < im.reversedChatItems.count { + let ci = im.reversedChatItems[i] if ci.mergeCategory != ciCategory { break } if let m = ci.memberConnected { ns.append(m.displayName) @@ -628,7 +670,7 @@ final class ChatModel: ObservableObject { // returns the index of the passed item and the next item (it has smaller index) func getNextChatItem(_ ci: ChatItem) -> (Int?, ChatItem?) { if let i = getChatItemIndex(ci) { - (i, i > 0 ? reversedChatItems[i - 1] : nil) + (i, i > 0 ? im.reversedChatItems[i - 1] : nil) } else { (nil, nil) } @@ -638,10 +680,10 @@ final class ChatModel: ObservableObject { // and the previous visible item with another merge category func getPrevShownChatItem(_ ciIndex: Int?, _ ciCategory: CIMergeCategory?) -> (Int?, ChatItem?) { guard var i = ciIndex else { return (nil, nil) } - let fst = reversedChatItems.count - 1 + let fst = im.reversedChatItems.count - 1 while i < fst { i = i + 1 - let ci = reversedChatItems[i] + let ci = im.reversedChatItems[i] if ciCategory == nil || ciCategory != ci.mergeCategory { return (i - 1, ci) } @@ -654,7 +696,7 @@ final class ChatModel: ObservableObject { var prevMember: GroupMember? = nil var memberIds: Set = [] for i in range { - if case let .groupRcv(m) = reversedChatItems[i].chatDir { + if case let .groupRcv(m) = im.reversedChatItems[i].chatDir { if prevMember == nil && m.groupMemberId != member.groupMemberId { prevMember = m } memberIds.insert(m.groupMemberId) } @@ -729,9 +771,9 @@ final class ChatModel: ObservableObject { var i = 0 var totalBelow = 0 var unreadBelow = 0 - while i < reversedChatItems.count - 1 && !itemsInView.contains(reversedChatItems[i].viewId) { + while i < im.reversedChatItems.count - 1 && !itemsInView.contains(im.reversedChatItems[i].viewId) { totalBelow += 1 - if reversedChatItems[i].isRcvNew { + if im.reversedChatItems[i].isRcvNew { unreadBelow += 1 } i += 1 @@ -740,12 +782,12 @@ final class ChatModel: ObservableObject { } func topItemInView(itemsInView: Set) -> ChatItem? { - let maxIx = reversedChatItems.count - 1 + let maxIx = im.reversedChatItems.count - 1 var i = 0 - let inView = { itemsInView.contains(self.reversedChatItems[$0].viewId) } + let inView = { itemsInView.contains(self.im.reversedChatItems[$0].viewId) } while i < maxIx && !inView(i) { i += 1 } while i < maxIx && inView(i) { i += 1 } - return reversedChatItems[min(i - 1, maxIx)] + return im.reversedChatItems[min(i - 1, maxIx)] } func setContactNetworkStatus(_ contact: Contact, _ status: NetworkStatus) { @@ -778,7 +820,7 @@ struct UnreadChatItemCounts: Equatable { var unreadBelow: Int } -final class Chat: ObservableObject, Identifiable { +final class Chat: ObservableObject, Identifiable, ChatLike { @Published var chatInfo: ChatInfo @Published var chatItems: [ChatItem] @Published var chatStats: ChatStats diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 3ffbe9a00d..6d2897338f 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -225,6 +225,15 @@ func apiStartChat(ctrl: chat_ctrl? = nil) throws -> Bool { } } +func apiCheckChatRunning() throws -> Bool { + let r = chatSendCmdSync(.checkChatRunning) + switch r { + case .chatRunning: return true + case .chatStopped: return false + default: throw r + } +} + func apiStopChat() async throws { let r = await chatSendCmd(.apiStopChat) switch r { @@ -325,11 +334,12 @@ func loadChat(chat: Chat, search: String = "") { do { let cInfo = chat.chatInfo let m = ChatModel.shared + let im = ItemsModel.shared m.chatItemStatuses = [:] - m.reversedChatItems = [] + im.reversedChatItems = [] let chat = try apiGetChat(type: cInfo.chatType, id: cInfo.apiId, search: search) m.updateChatInfo(chat.chatInfo) - m.reversedChatItems = chat.chatItems.reversed() + im.reversedChatItems = chat.chatItems.reversed() } catch let error { logger.error("loadChat error: \(responseError(error))") } @@ -421,15 +431,15 @@ func apiChatItemReaction(type: ChatType, id: Int64, itemId: Int64, add: Bool, re throw r } -func apiDeleteChatItem(type: ChatType, id: Int64, itemId: Int64, mode: CIDeleteMode) async throws -> (ChatItem, ChatItem?) { - let r = await chatSendCmd(.apiDeleteChatItem(type: type, id: id, itemId: itemId, mode: mode), bgDelay: msgDelay) - if case let .chatItemDeleted(_, deletedChatItem, toChatItem, _) = r { return (deletedChatItem.chatItem, toChatItem?.chatItem) } +func apiDeleteChatItems(type: ChatType, id: Int64, itemIds: [Int64], mode: CIDeleteMode) async throws -> [ChatItemDeletion] { + let r = await chatSendCmd(.apiDeleteChatItem(type: type, id: id, itemIds: itemIds, mode: mode), bgDelay: msgDelay) + if case let .chatItemsDeleted(_, items, _) = r { return items } throw r } -func apiDeleteMemberChatItem(groupId: Int64, groupMemberId: Int64, itemId: Int64) async throws -> (ChatItem, ChatItem?) { - let r = await chatSendCmd(.apiDeleteMemberChatItem(groupId: groupId, groupMemberId: groupMemberId, itemId: itemId), bgDelay: msgDelay) - if case let .chatItemDeleted(_, deletedChatItem, toChatItem, _) = r { return (deletedChatItem.chatItem, toChatItem?.chatItem) } +func apiDeleteMemberChatItems(groupId: Int64, itemIds: [Int64]) async throws -> [ChatItemDeletion] { + let r = await chatSendCmd(.apiDeleteMemberChatItem(groupId: groupId, itemIds: itemIds), bgDelay: msgDelay) + if case let .chatItemsDeleted(_, items, _) = r { return items } throw r } @@ -1091,62 +1101,6 @@ func deleteRemoteCtrl(_ rcId: Int64) async throws { try await sendCommandOkResp(.deleteRemoteCtrl(remoteCtrlId: rcId)) } -struct ErrorAlert { - var title: LocalizedStringKey - var message: LocalizedStringKey -} - -func getNetworkErrorAlert(_ r: ChatResponse) -> ErrorAlert? { - switch r { - case let .chatCmdError(_, .errorAgent(.BROKER(addr, .TIMEOUT))): - return ErrorAlert(title: "Connection timeout", message: "Please check your network connection with \(serverHostname(addr)) and try again.") - case let .chatCmdError(_, .errorAgent(.BROKER(addr, .NETWORK))): - return ErrorAlert(title: "Connection error", message: "Please check your network connection with \(serverHostname(addr)) and try again.") - case let .chatCmdError(_, .errorAgent(.BROKER(addr, .HOST))): - return ErrorAlert(title: "Connection error", message: "Server address is incompatible with network settings: \(serverHostname(addr)).") - case let .chatCmdError(_, .errorAgent(.BROKER(addr, .TRANSPORT(.version)))): - return ErrorAlert(title: "Connection error", message: "Server version is incompatible with your app: \(serverHostname(addr)).") - case let .chatCmdError(_, .errorAgent(.SMP(serverAddress, .PROXY(proxyErr)))): - return smpProxyErrorAlert(proxyErr, serverAddress) - case let .chatCmdError(_, .errorAgent(.PROXY(proxyServer, relayServer, .protocolError(.PROXY(proxyErr))))): - return proxyDestinationErrorAlert(proxyErr, proxyServer, relayServer) - default: - return nil - } -} - -private func smpProxyErrorAlert(_ proxyErr: ProxyError, _ srvAddr: String) -> ErrorAlert? { - switch proxyErr { - case .BROKER(brokerErr: .TIMEOUT): - return ErrorAlert(title: "Private routing error", message: "Error connecting to forwarding server \(serverHostname(srvAddr)). Please try later.") - case .BROKER(brokerErr: .NETWORK): - return ErrorAlert(title: "Private routing error", message: "Error connecting to forwarding server \(serverHostname(srvAddr)). Please try later.") - case .BROKER(brokerErr: .HOST): - return ErrorAlert(title: "Private routing error", message: "Forwarding server address is incompatible with network settings: \(serverHostname(srvAddr)).") - case .BROKER(brokerErr: .TRANSPORT(.version)): - return ErrorAlert(title: "Private routing error", message: "Forwarding server version is incompatible with network settings: \(serverHostname(srvAddr)).") - default: - return nil - } -} - -private func proxyDestinationErrorAlert(_ proxyErr: ProxyError, _ proxyServer: String, _ relayServer: String) -> ErrorAlert? { - switch proxyErr { - case .BROKER(brokerErr: .TIMEOUT): - return ErrorAlert(title: "Private routing error", message: "Forwarding server \(serverHostname(proxyServer)) failed to connect to destination server \(serverHostname(relayServer)). Please try later.") - case .BROKER(brokerErr: .NETWORK): - return ErrorAlert(title: "Private routing error", message: "Forwarding server \(serverHostname(proxyServer)) failed to connect to destination server \(serverHostname(relayServer)). Please try later.") - case .NO_SESSION: - return ErrorAlert(title: "Private routing error", message: "Forwarding server \(serverHostname(proxyServer)) failed to connect to destination server \(serverHostname(relayServer)). Please try later.") - case .BROKER(brokerErr: .HOST): - return ErrorAlert(title: "Private routing error", message: "Destination server address of \(serverHostname(relayServer)) is incompatible with forwarding server \(serverHostname(proxyServer)) settings.") - case .BROKER(brokerErr: .TRANSPORT(.version)): - return ErrorAlert(title: "Private routing error", message: "Destination server version of \(serverHostname(relayServer)) is incompatible with forwarding server \(serverHostname(proxyServer)).") - default: - return nil - } -} - func networkErrorAlert(_ r: ChatResponse) -> Alert? { if let alert = getNetworkErrorAlert(r) { return mkAlert(title: alert.title, message: alert.message) @@ -1495,15 +1449,16 @@ func startChat(refreshInvitations: Bool = true) throws { logger.debug("startChat") let m = ChatModel.shared try setNetworkConfig(getNetCfg()) - let justStarted = try apiStartChat() + let chatRunning = try apiCheckChatRunning() m.users = try listUsers() - if justStarted { + if !chatRunning { try getUserChatData() NtfManager.shared.setNtfBadgeCount(m.totalUnreadCountForAllUsers()) if (refreshInvitations) { try refreshCallInvitations() } (m.savedToken, m.tokenStatus, m.notificationMode, m.notificationServer) = apiGetNtfToken() + _ = try apiStartChat() // deviceToken is set when AppDelegate.application(didRegisterForRemoteNotificationsWithDeviceToken:) is called, // when it is called before startChat if let token = m.deviceToken { @@ -1791,21 +1746,25 @@ func processReceivedMsg(_ res: ChatResponse) async { m.updateChatItem(r.chatInfo, r.chatReaction.chatItem) } } - case let .chatItemDeleted(user, deletedChatItem, toChatItem, _): + case let .chatItemsDeleted(user, items, _): if !active(user) { - if toChatItem == nil && deletedChatItem.chatItem.isRcvNew && deletedChatItem.chatInfo.ntfsEnabled { - await MainActor.run { - m.decreaseUnreadCounter(user: user) + for item in items { + if item.toChatItem == nil && item.deletedChatItem.chatItem.isRcvNew && item.deletedChatItem.chatInfo.ntfsEnabled { + await MainActor.run { + m.decreaseUnreadCounter(user: user) + } } } return } await MainActor.run { - if let toChatItem = toChatItem { - _ = m.upsertChatItem(toChatItem.chatInfo, toChatItem.chatItem) - } else { - m.removeChatItem(deletedChatItem.chatInfo, deletedChatItem.chatItem) + for item in items { + if let toChatItem = item.toChatItem { + _ = m.upsertChatItem(toChatItem.chatInfo, toChatItem.chatItem) + } else { + m.removeChatItem(item.deletedChatItem.chatInfo, item.deletedChatItem.chatItem) + } } } case let .receivedGroupInvitation(user, groupInfo, _, _): diff --git a/apps/ios/Shared/Model/SuspendChat.swift b/apps/ios/Shared/Model/SuspendChat.swift index 4494adc0e8..92bcdcac53 100644 --- a/apps/ios/Shared/Model/SuspendChat.swift +++ b/apps/ios/Shared/Model/SuspendChat.swift @@ -36,6 +36,18 @@ private func _suspendChat(timeout: Int) { } } +let seSubscriber = seMessageSubscriber { + switch $0 { + case let .state(state): + switch state { + case .inactive: + if AppChatState.shared.value.inactive { activateChat() } + case .sendingMessage: + if AppChatState.shared.value.canSuspend { suspendChat() } + } + } +} + func suspendChat() { suspendLockQueue.sync { _suspendChat(timeout: appSuspendTimeout) diff --git a/apps/ios/Shared/Views/Chat/ChatInfoView.swift b/apps/ios/Shared/Views/Chat/ChatInfoView.swift index f83fca8e2e..4d45db4700 100644 --- a/apps/ios/Shared/Views/Chat/ChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatInfoView.swift @@ -112,7 +112,7 @@ struct ChatInfoView: View { case abortSwitchAddressAlert case syncConnectionForceAlert case queueInfo(info: String) - case error(title: LocalizedStringKey, error: LocalizedStringKey = "") + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIChatFeatureView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIChatFeatureView.swift index c41039a4ef..27d8d9c2de 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIChatFeatureView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIChatFeatureView.swift @@ -11,6 +11,7 @@ import SimpleXChat struct CIChatFeatureView: View { @EnvironmentObject var m: ChatModel + @ObservedObject var im = ItemsModel.shared @ObservedObject var chat: Chat @EnvironmentObject var theme: AppTheme var chatItem: ChatItem @@ -53,8 +54,8 @@ struct CIChatFeatureView: View { var fs: [FeatureInfo] = [] var icons: Set = [] if var i = m.getChatItemIndex(chatItem) { - while i < m.reversedChatItems.count, - let f = featureInfo(m.reversedChatItems[i]) { + while i < im.reversedChatItems.count, + let f = featureInfo(im.reversedChatItems[i]) { if !icons.contains(f.icon) { fs.insert(f, at: 0) icons.insert(f.icon) diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift index 57123d74ba..fcb330c321 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift @@ -14,10 +14,10 @@ struct CIFileView: View { @EnvironmentObject var theme: AppTheme let file: CIFile? let edited: Bool - var smallView: Bool = false + var smallViewSize: CGFloat? var body: some View { - if smallView { + if smallViewSize != nil { fileIndicator() .onTapGesture(perform: fileAction) } else { @@ -201,21 +201,22 @@ struct CIFileView: View { } private func fileIcon(_ icon: String, color: Color = Color(uiColor: .tertiaryLabel), innerIcon: String? = nil, innerIconSize: CGFloat? = nil) -> some View { - ZStack(alignment: .center) { + let size = smallViewSize ?? 30 + return ZStack(alignment: .center) { Image(systemName: icon) .resizable() .aspectRatio(contentMode: .fit) - .frame(width: smallView ? 36 : 30, height: smallView ? 36 : 30) + .frame(width: size, height: size) .foregroundColor(color) if let innerIcon = innerIcon, - let innerIconSize = innerIconSize, (!smallView || file?.showStatusIconInSmallView == true) { + let innerIconSize = innerIconSize, (smallViewSize == nil || file?.showStatusIconInSmallView == true) { Image(systemName: innerIcon) .resizable() .aspectRatio(contentMode: .fit) .frame(maxHeight: 16) .frame(width: innerIconSize, height: innerIconSize) .foregroundColor(.white) - .padding(.top, smallView ? 15 : 12) + .padding(.top, size / 2.5) } } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIRcvDecryptionError.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIRcvDecryptionError.swift index 7023449e9f..1f2e16448d 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIRcvDecryptionError.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIRcvDecryptionError.swift @@ -27,7 +27,7 @@ struct CIRcvDecryptionError: View { case syncNotSupportedContactAlert case syncNotSupportedMemberAlert case decryptionErrorAlert - case error(title: LocalizedStringKey, error: LocalizedStringKey) + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { @@ -62,7 +62,7 @@ struct CIRcvDecryptionError: View { case .syncNotSupportedContactAlert: return Alert(title: Text("Fix not supported by contact"), message: message()) case .syncNotSupportedMemberAlert: return Alert(title: Text("Fix not supported by group member"), message: message()) case .decryptionErrorAlert: return Alert(title: Text("Decryption error"), message: message()) - case let .error(title, error): return Alert(title: Text(title), message: Text(error)) + case let .error(title, error): return mkAlert(title: title, message: error) } } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIVoiceView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIVoiceView.swift index ce4d2a8181..45a20f03bd 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIVoiceView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIVoiceView.swift @@ -20,12 +20,12 @@ struct CIVoiceView: View { @State var playbackTime: TimeInterval? = nil @Binding var allowMenu: Bool - var smallView: Bool = false + var smallViewSize: CGFloat? @State private var seek: (TimeInterval) -> Void = { _ in } var body: some View { Group { - if smallView { + if smallViewSize != nil { HStack(spacing: 10) { player() playerTime() @@ -65,7 +65,12 @@ struct CIVoiceView: View { } private func player() -> some View { - VoiceMessagePlayer( + let sizeMultiplier: CGFloat = if let sz = smallViewSize { + voiceMessageSizeBasedOnSquareSize(sz) / 56 + } else { + 1 + } + return VoiceMessagePlayer( chat: chat, chatItem: chatItem, recordingFile: recordingFile, @@ -76,7 +81,7 @@ struct CIVoiceView: View { playbackState: $playbackState, playbackTime: $playbackTime, allowMenu: $allowMenu, - sizeMultiplier: smallView ? voiceMessageSizeBasedOnSquareSize(36) / 56 : 1 + sizeMultiplier: sizeMultiplier ) } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/FramedItemView.swift b/apps/ios/Shared/Views/Chat/ChatItem/FramedItemView.swift index 2fdd708fdb..258e2e34dc 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/FramedItemView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/FramedItemView.swift @@ -49,7 +49,7 @@ struct FramedItemView: View { if let qi = chatItem.quotedItem { ciQuoteView(qi) .onTapGesture { - if let ci = m.reversedChatItems.first(where: { $0.id == qi.itemId }) { + if let ci = ItemsModel.shared.reversedChatItems.first(where: { $0.id == qi.itemId }) { withAnimation { scrollModel.scrollToItem(id: ci.id) } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/MarkedDeletedItemView.swift b/apps/ios/Shared/Views/Chat/ChatItem/MarkedDeletedItemView.swift index f8bd9156da..25e06b9ea4 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/MarkedDeletedItemView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/MarkedDeletedItemView.swift @@ -35,8 +35,8 @@ struct MarkedDeletedItemView: View { var blockedByAdmin = 0 var deleted = 0 var moderatedBy: Set = [] - while i < m.reversedChatItems.count, - let ci = .some(m.reversedChatItems[i]), + while i < ItemsModel.shared.reversedChatItems.count, + let ci = .some(ItemsModel.shared.reversedChatItems[i]), ci.mergeCategory == ciCategory, let itemDeleted = ci.meta.itemDeleted { switch itemDeleted { diff --git a/apps/ios/Shared/Views/Chat/ChatItemForwardingView.swift b/apps/ios/Shared/Views/Chat/ChatItemForwardingView.swift index 1814419623..0b7de32a88 100644 --- a/apps/ios/Shared/Views/Chat/ChatItemForwardingView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItemForwardingView.swift @@ -22,7 +22,7 @@ struct ChatItemForwardingView: View { @FocusState private var searchFocused @State private var alert: SomeAlert? @State private var hasSimplexLink_: Bool? - private let chatsToForwardTo = filterChatsToForwardTo() + private let chatsToForwardTo = filterChatsToForwardTo(chats: ChatModel.shared.chats) var body: some View { NavigationView { @@ -67,22 +67,6 @@ struct ChatItemForwardingView: View { } } - private func foundChat(_ chat: Chat, _ searchStr: String) -> Bool { - let cInfo = chat.chatInfo - return switch cInfo { - case let .direct(contact): - viewNameContains(cInfo, searchStr) || - contact.profile.displayName.localizedLowercase.contains(searchStr) || - contact.fullName.localizedLowercase.contains(searchStr) - default: - viewNameContains(cInfo, searchStr) - } - - func viewNameContains(_ cInfo: ChatInfo, _ s: String) -> Bool { - cInfo.chatViewName.localizedLowercase.contains(s) - } - } - private func prohibitedByPref(_ chat: Chat) -> Bool { // preference checks should match checks in compose view let simplexLinkProhibited = hasSimplexLink && !chat.groupFeatureEnabled(.simplexLinks) @@ -162,27 +146,6 @@ struct ChatItemForwardingView: View { } } -private func filterChatsToForwardTo() -> [Chat] { - var filteredChats = ChatModel.shared.chats.filter { c in - c.chatInfo.chatType != .local && canForwardToChat(c) - } - if let privateNotes = ChatModel.shared.chats.first(where: { $0.chatInfo.chatType == .local }) { - filteredChats.insert(privateNotes, at: 0) - } - return filteredChats -} - -private func canForwardToChat(_ chat: Chat) -> Bool { - switch chat.chatInfo { - case let .direct(contact): contact.sendMsgEnabled && !contact.nextSendGrpInv - case let .group(groupInfo): groupInfo.sendMsgEnabled - case let .local(noteFolder): noteFolder.sendMsgEnabled - case .contactRequest: false - case .contactConnection: false - case .invalidJSON: false - } -} - #Preview { ChatItemForwardingView( ci: ChatItem.getSample(1, .directSnd, .now, "hello"), @@ -190,3 +153,4 @@ private func canForwardToChat(_ chat: Chat) -> Bool { composeState: Binding.constant(ComposeState(message: "hello")) ).environmentObject(CurrentColors.toAppTheme()) } + diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index 5eb7861bd2..5e2825d30c 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -15,6 +15,7 @@ private let memberImageSize: CGFloat = 34 struct ChatView: View { @EnvironmentObject var chatModel: ChatModel + @ObservedObject var im = ItemsModel.shared @State var theme: AppTheme = buildTheme() @Environment(\.dismiss) var dismiss @Environment(\.colorScheme) var colorScheme @@ -110,7 +111,7 @@ struct ChatView: View { .onChange(of: revealedChatItem) { _ in NotificationCenter.postReverseListNeedsLayout() } - .onChange(of: chatModel.reversedChatItems) { reversedChatItems in + .onChange(of: im.reversedChatItems) { reversedChatItems in if reversedChatItems.count <= loadItemsPerPage && filtered(reversedChatItems).count < 10 { loadChatItems(chat.chatInfo) } @@ -124,7 +125,7 @@ struct ChatView: View { DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) { if chatModel.chatId == nil { chatModel.chatItemStatuses = [:] - chatModel.reversedChatItems = [] + ItemsModel.shared.reversedChatItems = [] chatModel.groupMembers = [] chatModel.groupMembersIndexes.removeAll() chatModel.membersLoaded = false @@ -339,7 +340,7 @@ struct ChatView: View { private func chatItemsList() -> some View { let cInfo = chat.chatInfo - let mergedItems = filtered(chatModel.reversedChatItems) + let mergedItems = filtered(im.reversedChatItems) return GeometryReader { g in ReverseList(items: mergedItems, scrollState: $scrollModel.state) { ci in let voiceNoFrame = voiceWithoutFrame(ci) @@ -372,7 +373,7 @@ struct ChatView: View { loadChat(chat: c) } } - .onChange(of: chatModel.reversedChatItems) { _ in + .onChange(of: im.reversedChatItems) { _ in floatingButtonModel.chatItemsChanged() } } @@ -562,7 +563,7 @@ struct ChatView: View { // Load additional items until the page is +50 large after merging while chatItemsAvailable && filtered(reversedPage).count < loadItemsPerPage { let pagination: ChatPagination = - if let lastItem = reversedPage.last ?? chatModel.reversedChatItems.last { + if let lastItem = reversedPage.last ?? im.reversedChatItems.last { .before(chatItemId: lastItem.id, count: loadItemsPerPage) } else { .last(count: loadItemsPerPage) @@ -580,7 +581,7 @@ struct ChatView: View { if reversedPage.count == 0 { firstPage = true } else { - chatModel.reversedChatItems.append(contentsOf: reversedPage) + im.reversedChatItems.append(contentsOf: reversedPage) } loadingItems = false } @@ -634,11 +635,12 @@ struct ChatView: View { let ciCategory = chatItem.mergeCategory let (prevHidden, prevItem) = m.getPrevShownChatItem(currIndex, ciCategory) let range = itemsRange(currIndex, prevHidden) + let im = ItemsModel.shared Group { if revealed, let range = range { - let items = Array(zip(Array(range), m.reversedChatItems[range])) + let items = Array(zip(Array(range), im.reversedChatItems[range])) ForEach(items, id: \.1.viewId) { (i, ci) in - let prev = i == prevHidden ? prevItem : m.reversedChatItems[i + 1] + let prev = i == prevHidden ? prevItem : im.reversedChatItems[i + 1] chatItemView(ci, nil, prev) } } else { @@ -646,23 +648,39 @@ struct ChatView: View { } } .onAppear { - markRead( - chatItems: range.flatMap { m.reversedChatItems[$0] } - ?? [chatItem] - ) - } - } - - private func markRead(chatItems: Array.SubSequence) { - let unreadItems = chatItems.filter { $0.isRcvNew } - if unreadItems.isEmpty { return } - DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) { - if m.chatId == chat.chatInfo.id { - Task { - for unreadItem in unreadItems { - await apiMarkChatItemRead(chat.chatInfo, unreadItem) + if let range { + if let items = unreadItems(range) { + waitToMarkRead { + for ci in items { + await apiMarkChatItemRead(chat.chatInfo, ci) + } } } + } else if chatItem.isRcvNew { + waitToMarkRead { + await apiMarkChatItemRead(chat.chatInfo, chatItem) + } + } + } + } + + private func unreadItems(_ range: ClosedRange) -> [ChatItem]? { + let im = ItemsModel.shared + let items = range.compactMap { i in + if i >= 0 && i < im.reversedChatItems.count { + let ci = im.reversedChatItems[i] + return if ci.isRcvNew { ci } else { nil } + } else { + return nil + } + } + return if items.isEmpty { nil } else { items } + } + + private func waitToMarkRead(_ op: @Sendable @escaping () async -> Void) { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) { + if m.chatId == chat.chatInfo.id { + Task(operation: op) } } } @@ -1141,7 +1159,7 @@ struct ChatView: View { if let range = itemsRange(currIndex, prevHidden) { var itemIds: [Int64] = [] for i in range { - itemIds.append(m.reversedChatItems[i].id) + itemIds.append(ItemsModel.shared.reversedChatItems[i].id) } showDeleteMessages = true deletingItems = itemIds @@ -1247,24 +1265,20 @@ struct ChatView: View { if itemIds.count > 0 { let chatInfo = chat.chatInfo Task { - var deletedItems: [ChatItem] = [] - for itemId in itemIds { - do { - let (di, _) = try await apiDeleteChatItem( - type: chatInfo.chatType, - id: chatInfo.apiId, - itemId: itemId, - mode: .cidmInternal - ) - deletedItems.append(di) - } catch { - logger.error("ChatView.deleteMessage error: \(error.localizedDescription)") - } - } - await MainActor.run { - for di in deletedItems { - m.removeChatItem(chatInfo, di) + do { + let deletedItems = try await apiDeleteChatItems( + type: chatInfo.chatType, + id: chatInfo.apiId, + itemIds: itemIds, + mode: .cidmInternal + ) + await MainActor.run { + for di in deletedItems { + m.removeChatItem(chatInfo, di.deletedChatItem.chatItem) + } } + } catch { + logger.error("ChatView.deleteMessage error: \(error.localizedDescription)") } } } @@ -1276,29 +1290,28 @@ struct ChatView: View { logger.debug("ChatView deleteMessage: in Task") do { if let di = deletingItem { - var deletedItem: ChatItem - var toItem: ChatItem? - if case .cidmBroadcast = mode, + let r = if case .cidmBroadcast = mode, let (groupInfo, groupMember) = di.memberToModerate(chat.chatInfo) { - (deletedItem, toItem) = try await apiDeleteMemberChatItem( + try await apiDeleteMemberChatItems( groupId: groupInfo.apiId, - groupMemberId: groupMember.groupMemberId, - itemId: di.id + itemIds: [di.id] ) } else { - (deletedItem, toItem) = try await apiDeleteChatItem( + try await apiDeleteChatItems( type: chat.chatInfo.chatType, id: chat.chatInfo.apiId, - itemId: di.id, + itemIds: [di.id], mode: mode ) } - DispatchQueue.main.async { - deletingItem = nil - if let toItem = toItem { - _ = m.upsertChatItem(chat.chatInfo, toItem) - } else { - m.removeChatItem(chat.chatInfo, deletedItem) + if let itemDeletion = r.first { + await MainActor.run { + deletingItem = nil + if let toItem = itemDeletion.toChatItem { + _ = m.upsertChatItem(chat.chatInfo, toItem.chatItem) + } else { + m.removeChatItem(chat.chatInfo, itemDeletion.deletedChatItem.chatItem) + } } } } @@ -1384,7 +1397,7 @@ struct ChatView_Previews: PreviewProvider { static var previews: some View { let chatModel = ChatModel() chatModel.chatId = "@1" - chatModel.reversedChatItems = [ + ItemsModel.shared.reversedChatItems = [ ChatItem.getSample(1, .directSnd, .now, "hello"), ChatItem.getSample(2, .directRcv, .now, "hi"), ChatItem.getSample(3, .directRcv, .now, "hi there"), diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeLinkView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeLinkView.swift index 0fb48033d5..66cb9edcf8 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeLinkView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeLinkView.swift @@ -10,35 +10,6 @@ import SwiftUI import LinkPresentation import SimpleXChat -func getLinkPreview(url: URL, cb: @escaping (LinkPreview?) -> Void) { - logger.debug("getLinkMetadata: fetching URL preview") - LPMetadataProvider().startFetchingMetadata(for: url){ metadata, error in - if let e = error { - logger.error("Error retrieving link metadata: \(e.localizedDescription)") - } - if let metadata = metadata, - let imageProvider = metadata.imageProvider, - imageProvider.canLoadObject(ofClass: UIImage.self) { - imageProvider.loadObject(ofClass: UIImage.self){ object, error in - var linkPreview: LinkPreview? = nil - if let error = error { - logger.error("Couldn't load image preview from link metadata with error: \(error.localizedDescription)") - } else { - if let image = object as? UIImage, - let resized = resizeImageToStrSize(image, maxDataSize: 14000), - let title = metadata.title, - let uri = metadata.originalURL { - linkPreview = LinkPreview(uri: uri, title: title, image: resized) - } - } - cb(linkPreview) - } - } else { - cb(nil) - } - } -} - struct ComposeLinkView: View { @EnvironmentObject var theme: AppTheme let linkPreview: LinkPreview? diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift index a720c3aaaf..9ad6e986bd 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift @@ -67,7 +67,6 @@ struct SendMessageView: View { .fixedSize(horizontal: false, vertical: true) } } - if progressByTimeout { ProgressView() .scaleEffect(1.4) @@ -87,7 +86,7 @@ struct SendMessageView: View { .padding(.vertical, 1) .background(theme.colors.background) .clipShape(composeShape) - .overlay(composeShape.strokeBorder(.secondary, lineWidth: 0.3, antialiased: true)) + .overlay(composeShape.strokeBorder(.secondary, lineWidth: 0.5).opacity(0.7)) } .onChange(of: composeState.message, perform: { text in updateFont(text) }) .onChange(of: composeState.inProgress) { inProgress in @@ -258,6 +257,9 @@ struct SendMessageView: View { var body: some View { Button(action: {}) { Image(systemName: "mic.fill") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) .foregroundColor(theme.colors.primary) } .disabled(disabled) @@ -311,6 +313,9 @@ struct SendMessageView: View { } } label: { Image(systemName: "mic") + .resizable() + .scaledToFit() + .frame(width: 20, height: 20) .foregroundColor(theme.colors.secondary) } .disabled(composeState.inProgress) diff --git a/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift b/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift index 49239c8fa5..dc867b026f 100644 --- a/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift +++ b/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift @@ -35,7 +35,7 @@ struct AddGroupMembersViewCommon: View { private enum AddGroupMembersAlert: Identifiable { case prohibitedToInviteIncognito - case error(title: LocalizedStringKey, error: LocalizedStringKey = "") + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { @@ -122,7 +122,7 @@ struct AddGroupMembersViewCommon: View { message: Text("You're trying to invite contact with whom you've shared an incognito profile to the group in which you're using your main profile") ) case let .error(title, error): - return Alert(title: Text(title), message: Text(error)) + return mkAlert(title: title, message: error) } } .onChange(of: selectedContacts) { _ in diff --git a/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift b/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift index 59a21d2330..f89009f93f 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupChatInfoView.swift @@ -40,7 +40,7 @@ struct GroupChatInfoView: View { case blockForAllAlert(mem: GroupMember) case unblockForAllAlert(mem: GroupMember) case removeMemberAlert(mem: GroupMember) - case error(title: LocalizedStringKey, error: LocalizedStringKey) + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { @@ -158,7 +158,7 @@ struct GroupChatInfoView: View { case let .blockForAllAlert(mem): return blockForAllAlert(groupInfo, mem) case let .unblockForAllAlert(mem): return unblockForAllAlert(groupInfo, mem) case let .removeMemberAlert(mem): return removeMemberAlert(mem) - case let .error(title, error): return Alert(title: Text(title), message: Text(error)) + case let .error(title, error): return mkAlert(title: title, message: error) } } .onAppear { diff --git a/apps/ios/Shared/Views/Chat/Group/GroupLinkView.swift b/apps/ios/Shared/Views/Chat/Group/GroupLinkView.swift index adf5f998a4..93a8be04f4 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupLinkView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupLinkView.swift @@ -22,7 +22,7 @@ struct GroupLinkView: View { private enum GroupLinkAlert: Identifiable { case deleteLink - case error(title: LocalizedStringKey, error: LocalizedStringKey = "") + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { @@ -113,7 +113,7 @@ struct GroupLinkView: View { }, secondaryButton: .cancel() ) case let .error(title, error): - return Alert(title: Text(title), message: Text(error)) + return mkAlert(title: title, message: error) } } .onChange(of: groupLinkMemberRole) { _ in diff --git a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift index 3e4c3c9f6e..12b5bd5a98 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift @@ -37,7 +37,7 @@ struct GroupMemberInfoView: View { case syncConnectionForceAlert case planAndConnectAlert(alert: PlanAndConnectAlert) case queueInfo(info: String) - case error(title: LocalizedStringKey, error: LocalizedStringKey) + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { @@ -237,7 +237,7 @@ struct GroupMemberInfoView: View { case .syncConnectionForceAlert: return syncConnectionForceAlert({ syncMemberConnection(force: true) }) case let .planAndConnectAlert(alert): return planAndConnectAlert(alert, dismiss: true) case let .queueInfo(info): return queueInfoAlert(info) - case let .error(title, error): return Alert(title: Text(title), message: Text(error)) + case let .error(title, error): return mkAlert(title: title, message: error) } } .actionSheet(item: $sheet) { s in planAndConnectActionSheet(s, dismiss: true) } diff --git a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift index 35cb5b3861..add364d9c9 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift @@ -9,25 +9,41 @@ import SwiftUI import SimpleXChat -private let rowHeights: [DynamicTypeSize: CGFloat] = [ - .xSmall: 68, - .small: 72, - .medium: 76, - .large: 80, - .xLarge: 88, - .xxLarge: 94, - .xxxLarge: 104, - .accessibility1: 90, - .accessibility2: 100, - .accessibility3: 120, - .accessibility4: 130, - .accessibility5: 140 +typealias DynamicSizes = ( + rowHeight: CGFloat, + profileImageSize: CGFloat, + mediaSize: CGFloat, + incognitoSize: CGFloat, + chatInfoSize: CGFloat, + unreadCorner: CGFloat, + unreadPadding: CGFloat +) + +private let dynamicSizes: [DynamicTypeSize: DynamicSizes] = [ + .xSmall: (68, 55, 33, 22, 18, 9, 3), + .small: (72, 57, 34, 22, 18, 9, 3), + .medium: (76, 60, 36, 22, 18, 10, 4), + .large: (80, 63, 38, 24, 20, 10, 4), + .xLarge: (88, 67, 41, 24, 20, 10, 4), + .xxLarge: (100, 71, 44, 27, 22, 11, 4), + .xxxLarge: (110, 75, 48, 30, 24, 12, 5), + .accessibility1: (110, 75, 48, 30, 24, 12, 5), + .accessibility2: (114, 75, 48, 30, 24, 12, 5), + .accessibility3: (124, 75, 48, 30, 24, 12, 5), + .accessibility4: (134, 75, 48, 30, 24, 12, 5), + .accessibility5: (144, 75, 48, 30, 24, 12, 5) ] +private let defaultDynamicSizes: DynamicSizes = dynamicSizes[.large]! + +func dynamicSize(_ font: DynamicTypeSize) -> DynamicSizes { + dynamicSizes[font] ?? defaultDynamicSizes +} + struct ChatListNavLink: View { @EnvironmentObject var chatModel: ChatModel @EnvironmentObject var theme: AppTheme - @Environment(\.dynamicTypeSize) private var dynamicTypeSize + @Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize @ObservedObject var chat: Chat @State private var showContactRequestDialog = false @State private var showJoinGroupDialog = false @@ -38,6 +54,8 @@ struct ChatListNavLink: View { @State private var inProgress = false @State private var progressByTimeout = false + var dynamicRowHeight: CGFloat { dynamicSizes[userFont]?.rowHeight ?? 80 } + var body: some View { Group { switch chat.chatInfo { @@ -70,7 +88,7 @@ struct ChatListNavLink: View { Group { if contact.activeConn == nil && contact.profile.contactLink != nil { ChatPreviewView(chat: chat, progressByTimeout: Binding.constant(false)) - .frame(height: rowHeights[dynamicTypeSize]) + .frame(height: dynamicRowHeight) .swipeActions(edge: .trailing, allowsFullSwipe: true) { Button { showDeleteContactActionSheet = true @@ -110,7 +128,7 @@ struct ChatListNavLink: View { } .tint(.red) } - .frame(height: rowHeights[dynamicTypeSize]) + .frame(height: dynamicRowHeight) } } .actionSheet(isPresented: $showDeleteContactActionSheet) { @@ -139,7 +157,7 @@ struct ChatListNavLink: View { switch (groupInfo.membership.memberStatus) { case .memInvited: ChatPreviewView(chat: chat, progressByTimeout: $progressByTimeout) - .frame(height: rowHeights[dynamicTypeSize]) + .frame(height: dynamicRowHeight) .swipeActions(edge: .trailing, allowsFullSwipe: true) { joinGroupButton() if groupInfo.canDelete { @@ -159,7 +177,7 @@ struct ChatListNavLink: View { .disabled(inProgress) case .memAccepted: ChatPreviewView(chat: chat, progressByTimeout: Binding.constant(false)) - .frame(height: rowHeights[dynamicTypeSize]) + .frame(height: dynamicRowHeight) .onTapGesture { AlertManager.shared.showAlert(groupInvitationAcceptedAlert()) } @@ -178,7 +196,7 @@ struct ChatListNavLink: View { label: { ChatPreviewView(chat: chat, progressByTimeout: Binding.constant(false)) }, disabled: !groupInfo.ready ) - .frame(height: rowHeights[dynamicTypeSize]) + .frame(height: dynamicRowHeight) .swipeActions(edge: .leading, allowsFullSwipe: true) { markReadButton() toggleFavoriteButton() @@ -205,7 +223,7 @@ struct ChatListNavLink: View { label: { ChatPreviewView(chat: chat, progressByTimeout: Binding.constant(false)) }, disabled: !noteFolder.ready ) - .frame(height: rowHeights[dynamicTypeSize]) + .frame(height: dynamicRowHeight) .swipeActions(edge: .leading, allowsFullSwipe: true) { markReadButton() } @@ -321,7 +339,7 @@ struct ChatListNavLink: View { } .tint(.red) } - .frame(height: rowHeights[dynamicTypeSize]) + .frame(height: dynamicRowHeight) .onTapGesture { showContactRequestDialog = true } .confirmationDialog("Accept connection request?", isPresented: $showContactRequestDialog, titleVisibility: .visible) { Button("Accept") { Task { await acceptContactRequest(incognito: false, contactRequest: contactRequest) } } @@ -349,7 +367,7 @@ struct ChatListNavLink: View { } .tint(theme.colors.primary) } - .frame(height: rowHeights[dynamicTypeSize]) + .frame(height: dynamicRowHeight) .appSheet(isPresented: $showContactConnectionInfo) { Group { if case let .contactConnection(contactConnection) = chat.chatInfo { @@ -469,7 +487,7 @@ struct ChatListNavLink: View { Text("invalid chat data") .foregroundColor(.red) .padding(4) - .frame(height: rowHeights[dynamicTypeSize]) + .frame(height: dynamicRowHeight) .onTapGesture { showInvalidJSON = true } .appSheet(isPresented: $showInvalidJSON) { invalidJSONView(json) diff --git a/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift b/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift index ce638e8a0a..6db89cd8d2 100644 --- a/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift @@ -12,6 +12,7 @@ import SimpleXChat struct ChatPreviewView: View { @EnvironmentObject var chatModel: ChatModel @EnvironmentObject var theme: AppTheme + @Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize @ObservedObject var chat: Chat @Binding var progressByTimeout: Bool @State var deleting: Bool = false @@ -21,11 +22,14 @@ struct ChatPreviewView: View { @AppStorage(DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS) private var showChatPreviews = true + var dynamicMediaSize: CGFloat { dynamicSize(userFont).mediaSize } + var dynamicChatInfoSize: CGFloat { dynamicSize(userFont).chatInfoSize } + var body: some View { let cItem = chat.chatItems.last return HStack(spacing: 8) { ZStack(alignment: .bottomTrailing) { - ChatInfoImage(chat: chat, size: 63) + ChatInfoImage(chat: chat, size: dynamicSize(userFont).profileImageSize) chatPreviewImageOverlayIcon() .padding([.bottom, .trailing], 1) } @@ -73,7 +77,7 @@ struct ChatPreviewView: View { checkActiveContentPreview(chat, ci, mc) } chatStatusImage() - .padding(.top, 26) + .padding(.top, dynamicChatInfoSize * 1.44) .frame(maxWidth: .infinity, alignment: .trailing) } .frame(maxWidth: .infinity, alignment: .leading) @@ -172,7 +176,7 @@ struct ChatPreviewView: View { private func chatPreviewLayout(_ text: Text?, draft: Bool = false, _ hasFilePreview: Bool = false) -> some View { ZStack(alignment: .topTrailing) { let t = text - .lineLimit(2) + .lineLimit(userFont <= .xxxLarge ? 2 : 1) .multilineTextAlignment(.leading) .frame(maxWidth: .infinity, alignment: .topLeading) .padding(.leading, hasFilePreview ? 0 : 8) @@ -192,22 +196,25 @@ struct ChatPreviewView: View { let s = chat.chatStats if s.unreadCount > 0 || s.unreadChat { unreadCountText(s.unreadCount) - .font(.caption) + .font(userFont <= .xxxLarge ? .caption : .caption2) .foregroundColor(.white) - .padding(.horizontal, 4) - .frame(minWidth: 18, minHeight: 18) + .padding(.horizontal, dynamicSize(userFont).unreadPadding) + .frame(minWidth: dynamicChatInfoSize, minHeight: dynamicChatInfoSize) .background(chat.chatInfo.ntfsEnabled || chat.chatInfo.chatType == .local ? theme.colors.primary : theme.colors.secondary) - .cornerRadius(10) + .cornerRadius(dynamicSize(userFont).unreadCorner) } else if !chat.chatInfo.ntfsEnabled && chat.chatInfo.chatType != .local { Image(systemName: "speaker.slash.fill") + .resizable() + .scaledToFill() + .frame(width: dynamicChatInfoSize, height: dynamicChatInfoSize) .foregroundColor(theme.colors.secondary) } else if chat.chatInfo.chatSettings?.favorite ?? false { Image(systemName: "star.fill") .resizable() .scaledToFill() - .frame(width: 18, height: 18) + .frame(width: dynamicChatInfoSize, height: dynamicChatInfoSize) .padding(.trailing, 1) - .foregroundColor(.secondary.opacity(0.65)) + .foregroundColor(theme.colors.secondary.opacity(0.65)) } else { Color.clear.frame(width: 0) } @@ -293,12 +300,12 @@ struct ChatPreviewView: View { let mc = ci.content.msgContent switch mc { case let .link(_, preview): - smallContentPreview( + smallContentPreview(size: dynamicMediaSize) { ZStack(alignment: .topTrailing) { Image(uiImage: UIImage(base64Encoded: preview.image) ?? UIImage(systemName: "arrow.up.right")!) .resizable() .aspectRatio(contentMode: .fill) - .frame(width: 36, height: 36) + .frame(width: dynamicMediaSize, height: dynamicMediaSize) ZStack { Image(systemName: "arrow.up.right") .resizable() @@ -313,25 +320,25 @@ struct ChatPreviewView: View { .onTapGesture { UIApplication.shared.open(preview.uri) } - ) + } case let .image(_, image): - smallContentPreview( - CIImageView(chatItem: ci, preview: UIImage(base64Encoded: image), maxWidth: 36, smallView: true, showFullScreenImage: $showFullscreenGallery) + smallContentPreview(size: dynamicMediaSize) { + CIImageView(chatItem: ci, preview: UIImage(base64Encoded: image), maxWidth: dynamicMediaSize, smallView: true, showFullScreenImage: $showFullscreenGallery) .environmentObject(ReverseListScrollModel()) - ) + } case let .video(_,image, duration): - smallContentPreview( - CIVideoView(chatItem: ci, preview: UIImage(base64Encoded: image), duration: duration, maxWidth: 36, videoWidth: nil, smallView: true, showFullscreenPlayer: $showFullscreenGallery) + smallContentPreview(size: dynamicMediaSize) { + CIVideoView(chatItem: ci, preview: UIImage(base64Encoded: image), duration: duration, maxWidth: dynamicMediaSize, videoWidth: nil, smallView: true, showFullscreenPlayer: $showFullscreenGallery) .environmentObject(ReverseListScrollModel()) - ) + } case let .voice(_, duration): - smallContentPreviewVoice( - CIVoiceView(chat: chat, chatItem: ci, recordingFile: ci.file, duration: duration, allowMenu: Binding.constant(true), smallView: true) - ) + smallContentPreviewVoice(size: dynamicMediaSize) { + CIVoiceView(chat: chat, chatItem: ci, recordingFile: ci.file, duration: duration, allowMenu: Binding.constant(true), smallViewSize: dynamicMediaSize) + } case .file: - smallContentPreviewFile( - CIFileView(file: ci.file, edited: ci.meta.itemEdited, smallView: true) - ) + smallContentPreviewFile(size: dynamicMediaSize) { + CIFileView(file: ci.file, edited: ci.meta.itemEdited, smallViewSize: dynamicMediaSize) + } default: EmptyView() } } @@ -365,74 +372,70 @@ struct ChatPreviewView: View { } @ViewBuilder private func chatStatusImage() -> some View { + let size = dynamicSize(userFont).incognitoSize switch chat.chatInfo { case let .direct(contact): if contact.active && contact.activeConn != nil { switch (chatModel.contactNetworkStatus(contact)) { - case .connected: incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary) + case .connected: incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary, size: size) case .error: Image(systemName: "exclamationmark.circle") .resizable() .scaledToFit() - .frame(width: 17, height: 17) + .frame(width: dynamicChatInfoSize, height: dynamicChatInfoSize) .foregroundColor(theme.colors.secondary) default: ProgressView() } } else { - incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary) + incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary, size: size) } case .group: if progressByTimeout { ProgressView() } else { - incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary) + incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary, size: size) } default: - incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary) + incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary, size: size) } } } -@ViewBuilder func incognitoIcon(_ incognito: Bool, _ secondaryColor: Color) -> some View { +@ViewBuilder func incognitoIcon(_ incognito: Bool, _ secondaryColor: Color, size: CGFloat) -> some View { if incognito { Image(systemName: "theatermasks") .resizable() .scaledToFit() - .frame(width: 22, height: 22) + .frame(width: size, height: size) .foregroundColor(secondaryColor) } else { EmptyView() } } -func smallContentPreview(_ view: some View) -> some View { - ZStack { - view - .frame(width: 36, height: 36) - } +func smallContentPreview(size: CGFloat, _ view: @escaping () -> some View) -> some View { + view() + .frame(width: size, height: size) .cornerRadius(8) .overlay(RoundedRectangle(cornerSize: CGSize(width: 8, height: 8)) .strokeBorder(.secondary, lineWidth: 0.3, antialiased: true)) - .padding([.top, .leading], 3) + .padding(.vertical, size / 6) + .padding(.leading, 3) .offset(x: 6) } -func smallContentPreviewVoice(_ view: some View) -> some View { - ZStack { - view - .frame(height: voiceMessageSizeBasedOnSquareSize(36)) - } +func smallContentPreviewVoice(size: CGFloat, _ view: @escaping () -> some View) -> some View { + view() + .frame(height: voiceMessageSizeBasedOnSquareSize(size)) + .padding(.vertical, size / 6) .padding(.leading, 8) - .padding(.top, 6) } -func smallContentPreviewFile(_ view: some View) -> some View { - ZStack { - view - .frame(width: 36, height: 36) - } - .padding(.top, 2) +func smallContentPreviewFile(size: CGFloat, _ view: @escaping () -> some View) -> some View { + view() + .frame(width: size, height: size) + .padding(.vertical, size / 7) .padding(.leading, 5) } diff --git a/apps/ios/Shared/Views/ChatList/ContactConnectionInfo.swift b/apps/ios/Shared/Views/ChatList/ContactConnectionInfo.swift index b7e641a338..0f64b632dc 100644 --- a/apps/ios/Shared/Views/ChatList/ContactConnectionInfo.swift +++ b/apps/ios/Shared/Views/ChatList/ContactConnectionInfo.swift @@ -21,7 +21,7 @@ struct ContactConnectionInfo: View { enum CCInfoAlert: Identifiable { case deleteInvitationAlert - case error(title: LocalizedStringKey, error: LocalizedStringKey) + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { @@ -102,7 +102,7 @@ struct ContactConnectionInfo: View { } success: { dismiss() } - case let .error(title, error): return Alert(title: Text(title), message: Text(error)) + case let .error(title, error): return mkAlert(title: title, message: error) } } .onAppear { diff --git a/apps/ios/Shared/Views/ChatList/ContactConnectionView.swift b/apps/ios/Shared/Views/ChatList/ContactConnectionView.swift index bb224b7844..3c7bf97af9 100644 --- a/apps/ios/Shared/Views/ChatList/ContactConnectionView.swift +++ b/apps/ios/Shared/Views/ChatList/ContactConnectionView.swift @@ -13,6 +13,7 @@ struct ContactConnectionView: View { @EnvironmentObject var m: ChatModel @ObservedObject var chat: Chat @EnvironmentObject var theme: AppTheme + @Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize @State private var localAlias = "" @FocusState private var aliasTextFieldFocused: Bool @State private var showContactConnectionInfo = false @@ -62,7 +63,7 @@ struct ContactConnectionView: View { ZStack(alignment: .topTrailing) { Text(contactConnection.description) .frame(maxWidth: .infinity, alignment: .leading) - incognitoIcon(contactConnection.incognito, theme.colors.secondary) + incognitoIcon(contactConnection.incognito, theme.colors.secondary, size: dynamicSize(userFont).incognitoSize) .padding(.top, 26) .frame(maxWidth: .infinity, alignment: .trailing) } diff --git a/apps/ios/Shared/Views/ChatList/ContactRequestView.swift b/apps/ios/Shared/Views/ChatList/ContactRequestView.swift index e36a2f7596..9276bbfc78 100644 --- a/apps/ios/Shared/Views/ChatList/ContactRequestView.swift +++ b/apps/ios/Shared/Views/ChatList/ContactRequestView.swift @@ -12,12 +12,13 @@ import SimpleXChat struct ContactRequestView: View { @EnvironmentObject var chatModel: ChatModel @EnvironmentObject var theme: AppTheme + @Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize var contactRequest: UserContactRequest @ObservedObject var chat: Chat var body: some View { HStack(spacing: 8) { - ChatInfoImage(chat: chat, size: 63) + ChatInfoImage(chat: chat, size: dynamicSize(userFont).profileImageSize) .padding(.leading, 4) VStack(alignment: .leading, spacing: 0) { HStack(alignment: .top) { diff --git a/apps/ios/Shared/Views/Database/DatabaseErrorView.swift b/apps/ios/Shared/Views/Database/DatabaseErrorView.swift index f8d282a6d1..9d71e2a788 100644 --- a/apps/ios/Shared/Views/Database/DatabaseErrorView.swift +++ b/apps/ios/Shared/Views/Database/DatabaseErrorView.swift @@ -64,7 +64,7 @@ struct DatabaseErrorView: View { case let .migrationError(mtrError): titleText("Incompatible database version") fileNameText(dbFile) - Text("Error: ") + Text(DatabaseErrorView.mtrErrorDescription(mtrError)) + Text("Error: ") + Text(mtrErrorDescription(mtrError)) } case let .errorSQL(dbFile, migrationSQLError): titleText("Database error") @@ -105,15 +105,6 @@ struct DatabaseErrorView: View { Text("Migrations: \(ms.joined(separator: ", "))") } - static func mtrErrorDescription(_ err: MTRError) -> LocalizedStringKey { - switch err { - case let .noDown(dbMigrations): - return "database version is newer than the app, but no down migration for: \(dbMigrations.joined(separator: ", "))" - case let .different(appMigration, dbMigration): - return "different migration in the app/database: \(appMigration) / \(dbMigration)" - } - } - private func databaseKeyField(onSubmit: @escaping () -> Void) -> some View { PassphraseField(key: $dbKey, placeholder: "Enter passphrase…", valid: validKey(dbKey), onSubmit: onSubmit) } diff --git a/apps/ios/Shared/Views/Helpers/ChatInfoImage.swift b/apps/ios/Shared/Views/Helpers/ChatInfoImage.swift index 844b5ab4d3..40d62e009b 100644 --- a/apps/ios/Shared/Views/Helpers/ChatInfoImage.swift +++ b/apps/ios/Shared/Views/Helpers/ChatInfoImage.swift @@ -16,18 +16,10 @@ struct ChatInfoImage: View { var color = Color(uiColor: .tertiarySystemGroupedBackground) var body: some View { - var iconName: String - switch chat.chatInfo { - case .direct: iconName = "person.crop.circle.fill" - case .group: iconName = "person.2.circle.fill" - case .local: iconName = "folder.circle.fill" - case .contactRequest: iconName = "person.crop.circle.fill" - default: iconName = "circle.fill" - } let iconColor = if case .local = chat.chatInfo { theme.appColors.primaryVariant2 } else { color } return ProfileImage( imageStr: chat.chatInfo.image, - iconName: iconName, + iconName: chatIconName(chat.chatInfo), size: size, color: iconColor ) diff --git a/apps/ios/Shared/Views/Helpers/VideoUtils.swift b/apps/ios/Shared/Views/Helpers/VideoUtils.swift deleted file mode 100644 index e13893de6e..0000000000 --- a/apps/ios/Shared/Views/Helpers/VideoUtils.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// VideoUtils.swift -// SimpleX (iOS) -// -// Created by Avently on 25.12.2023. -// Copyright © 2023 SimpleX Chat. All rights reserved. -// - -import AVFoundation -import Foundation -import SimpleXChat - -func makeVideoQualityLower(_ input: URL, outputUrl: URL) async -> Bool { - let asset: AVURLAsset = AVURLAsset(url: input, options: nil) - if let s = AVAssetExportSession(asset: asset, presetName: AVAssetExportPreset640x480) { - s.outputURL = outputUrl - s.outputFileType = .mp4 - s.metadataItemFilter = AVMetadataItemFilter.forSharing() - await s.export() - if let err = s.error { - logger.error("Failed to export video with error: \(err)") - } - return s.status == .completed - } - return false -} diff --git a/apps/ios/Shared/Views/LocalAuth/LocalAuthView.swift b/apps/ios/Shared/Views/LocalAuth/LocalAuthView.swift index 9691a9efd3..b75cbf85b4 100644 --- a/apps/ios/Shared/Views/LocalAuth/LocalAuthView.swift +++ b/apps/ios/Shared/Views/LocalAuth/LocalAuthView.swift @@ -64,7 +64,7 @@ struct LocalAuthView: View { deleteAppDatabaseAndFiles() // Clear sensitive data on screen just in case app fails to hide its views while new database is created m.chatId = nil - m.reversedChatItems = [] + ItemsModel.shared.reversedChatItems = [] m.chats = [] m.users = [] _ = kcAppPassword.set(password) diff --git a/apps/ios/Shared/Views/Migration/MigrateToDevice.swift b/apps/ios/Shared/Views/Migration/MigrateToDevice.swift index 107785e336..67ea1008cd 100644 --- a/apps/ios/Shared/Views/Migration/MigrateToDevice.swift +++ b/apps/ios/Shared/Views/Migration/MigrateToDevice.swift @@ -331,7 +331,7 @@ struct MigrateToDevice: View { case let .migrationError(mtrError): ("Incompatible database version", nil, - "\(NSLocalizedString("Error: ", comment: "")) \(DatabaseErrorView.mtrErrorDescription(mtrError))", + "\(NSLocalizedString("Error: ", comment: "")) \(mtrErrorDescription(mtrError))", nil) } default: ("Error", nil, "Unknown error", nil) diff --git a/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift b/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift index 30d200b6e3..be063334d3 100644 --- a/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift +++ b/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift @@ -37,7 +37,7 @@ struct ConnectDesktopView: View { case badInvitationError case badVersionError(version: String?) case desktopDisconnectedError - case error(title: LocalizedStringKey, error: LocalizedStringKey = "") + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { @@ -160,7 +160,7 @@ struct ConnectDesktopView: View { case .desktopDisconnectedError: Alert(title: Text("Connection terminated")) case let .error(title, error): - Alert(title: Text(title), message: Text(error)) + mkAlert(title: title, message: error) } } .interactiveDismissDisabled(m.activeRemoteCtrl) diff --git a/apps/ios/Shared/Views/UserSettings/ProtocolServerView.swift b/apps/ios/Shared/Views/UserSettings/ProtocolServerView.swift index 6433168810..da29dfac29 100644 --- a/apps/ios/Shared/Views/UserSettings/ProtocolServerView.swift +++ b/apps/ios/Shared/Views/UserSettings/ProtocolServerView.swift @@ -175,10 +175,6 @@ func testServerConnection(server: Binding) async -> ProtocolTestFailu } } -func serverHostname(_ srv: String) -> String { - parseServerAddress(srv)?.hostnames.first ?? srv -} - struct ProtocolServerView_Previews: PreviewProvider { static var previews: some View { ProtocolServerView( diff --git a/apps/ios/Shared/Views/UserSettings/UserAddressView.swift b/apps/ios/Shared/Views/UserSettings/UserAddressView.swift index a22a10cd9c..fa95c51d36 100644 --- a/apps/ios/Shared/Views/UserSettings/UserAddressView.swift +++ b/apps/ios/Shared/Views/UserSettings/UserAddressView.swift @@ -30,7 +30,7 @@ struct UserAddressView: View { case deleteAddress case profileAddress(on: Bool) case shareOnCreate - case error(title: LocalizedStringKey, error: LocalizedStringKey = "") + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { @@ -185,7 +185,7 @@ struct UserAddressView: View { }, secondaryButton: .cancel() ) case let .error(title, error): - return Alert(title: Text(title), message: Text(error)) + return mkAlert(title: title, message: error) } } } diff --git a/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift b/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift index 13b9b2b097..160130bccc 100644 --- a/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift +++ b/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift @@ -30,7 +30,7 @@ struct UserProfilesView: View { case hiddenProfilesNotice case muteProfileAlert case activateUserError(error: String) - case error(title: LocalizedStringKey, error: LocalizedStringKey = "") + case error(title: LocalizedStringKey, error: LocalizedStringKey?) var id: String { switch self { @@ -172,7 +172,7 @@ struct UserProfilesView: View { message: Text(err) ) case let .error(title, error): - return Alert(title: Text(title), message: Text(error)) + return mkAlert(title: title, message: error) } } } diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff b/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff index 9ac4d8ced4..2edca32fc5 100644 --- a/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff @@ -1058,6 +1058,10 @@ Блокиран от админ No comment provided by engineer. + + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. И вие, и вашият контакт можете да добавяте реакции към съобщението. @@ -1509,6 +1513,10 @@ This is your own one-time link! Грешка при свързване (AUTH) No comment provided by engineer. + + Connection notifications + No comment provided by engineer. + Connection request sent! Заявката за връзка е изпратена! @@ -2089,10 +2097,18 @@ This cannot be undone! Настолни устройства No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + Destination server error: %@ snd error text + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + Detailed statistics No comment provided by engineer. @@ -2156,6 +2172,10 @@ This cannot be undone! Деактивиране за всички No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message Изчезващо съобщение @@ -2376,6 +2396,10 @@ This cannot be undone! Активирай kод за достъп за самоунищожение set passcode view + + Enabled + No comment provided by engineer. + Enabled for Активирано за @@ -2546,6 +2570,10 @@ This cannot be undone! Грешка при промяна на настройката No comment provided by engineer. + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address Грешка при създаване на адрес @@ -3040,6 +3068,18 @@ This cannot be undone! Препратено от No comment provided by engineer. + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + Forwarding server: %1$@ Destination server error: %2$@ @@ -3851,6 +3891,10 @@ This is your link for group %@! Макс. 30 секунди, получено незабавно. No comment provided by engineer. + + Medium + blur media + Member Член @@ -4287,7 +4331,7 @@ This is your link for group %@! Off Изключено - No comment provided by engineer. + blur media Ok @@ -4638,10 +4682,6 @@ Error: %@ Моля, съхранявайте паролата на сигурно място, НЯМА да можете да я промените, ако я загубите. No comment provided by engineer. - - Please try later. - No comment provided by engineer. - Polish interface Полски интерфейс @@ -5639,10 +5679,6 @@ Enable in *Network & servers* settings. Server version is incompatible with network settings. srv error text - - Server version is incompatible with network settings: %@. - No comment provided by engineer. - Server version is incompatible with your app: %@. No comment provided by engineer. @@ -5899,6 +5935,10 @@ Enable in *Network & servers* settings. Малки групи (максимум 20) No comment provided by engineer. + + Soft + blur media + Some non-fatal errors occurred during import - you may see Chat console for more details. Някои не-фатални грешки са възникнали по време на импортиране - може да видите конзолата за повече подробности. @@ -5997,6 +6037,10 @@ Enable in *Network & servers* settings. Спиране на чата No comment provided by engineer. + + Strong + blur media + Submit Изпрати @@ -8354,4 +8398,154 @@ last received msg: %2$@ + +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Dismiss Sheet + No comment provided by engineer. + + + Encrypted database + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keep Trying + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + No network connection + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Sending File + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Sharing is not supported when passphrase is not stored in KeyChain. + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff b/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff index c37ee1038e..cf88244f88 100644 --- a/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff +++ b/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff @@ -1019,6 +1019,10 @@ Blocked by admin No comment provided by engineer.
+ + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. Vy i váš kontakt můžete přidávat reakce na zprávy. @@ -1448,6 +1452,10 @@ This is your own one-time link! Chyba spojení (AUTH) No comment provided by engineer. + + Connection notifications + No comment provided by engineer. + Connection request sent! Požadavek na připojení byl odeslán! @@ -2011,10 +2019,18 @@ This cannot be undone! Desktop devices No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + Destination server error: %@ snd error text + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + Detailed statistics No comment provided by engineer. @@ -2078,6 +2094,10 @@ This cannot be undone! Vypnout pro všechny No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message Mizící zpráva @@ -2289,6 +2309,10 @@ This cannot be undone! Povolit sebedestrukční heslo set passcode view + + Enabled + No comment provided by engineer. + Enabled for No comment provided by engineer. @@ -2451,6 +2475,10 @@ This cannot be undone! Chyba změny nastavení No comment provided by engineer. + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address Chyba při vytváření adresy @@ -2928,6 +2956,18 @@ This cannot be undone! Forwarded from No comment provided by engineer. + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + Forwarding server: %1$@ Destination server error: %2$@ @@ -3708,6 +3748,10 @@ This is your link for group %@! Max 30 vteřin, přijato okamžitě. No comment provided by engineer. + + Medium + blur media + Member Člen @@ -4125,7 +4169,7 @@ This is your link for group %@! Off Vypnout - No comment provided by engineer. + blur media Ok @@ -4460,10 +4504,6 @@ Error: %@ Heslo uložte bezpečně, v případě jeho ztráty jej NEBUDE možné změnit. No comment provided by engineer. - - Please try later. - No comment provided by engineer. - Polish interface Polské rozhraní @@ -5437,10 +5477,6 @@ Enable in *Network & servers* settings. Server version is incompatible with network settings. srv error text - - Server version is incompatible with network settings: %@. - No comment provided by engineer. - Server version is incompatible with your app: %@. No comment provided by engineer. @@ -5690,6 +5726,10 @@ Enable in *Network & servers* settings. Malé skupiny (max. 20) No comment provided by engineer. + + Soft + blur media + Some non-fatal errors occurred during import - you may see Chat console for more details. Během importu došlo k nezávažným chybám - podrobnosti naleznete v chat konzoli. @@ -5784,6 +5824,10 @@ Enable in *Network & servers* settings. Stopping chat No comment provided by engineer. + + Strong + blur media + Submit Odeslat @@ -8051,4 +8095,154 @@ last received msg: %2$@ + +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Dismiss Sheet + No comment provided by engineer. + + + Encrypted database + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keep Trying + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + No network connection + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Sending File + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Sharing is not supported when passphrase is not stored in KeyChain. + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/cs.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ 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 e3d36cf09e..b495e510f9 100644 --- a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff +++ b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff @@ -590,6 +590,7 @@
Active connections + Aktive Verbindungen No comment provided by engineer. @@ -684,7 +685,7 @@ All chats and messages will be deleted - this cannot be undone! - Alle Chats und Nachrichten werden gelöscht. Dies kann nicht rückgängig gemacht werden! + Es werden alle Chats und Nachrichten gelöscht. Dies kann nicht rückgängig gemacht werden! No comment provided by engineer. @@ -694,7 +695,7 @@ All data is private to your device. - Alle Daten sind auf Ihrem Gerät geschützt. + Alle Daten werden nur auf Ihrem Gerät gespeichert. No comment provided by engineer. @@ -709,7 +710,7 @@ All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you. - Alle Nachrichten werden gelöscht . Dies kann nicht rückgängig gemacht werden! Die Nachrichten werden NUR bei Ihnen gelöscht. + Es werden alle Nachrichten gelöscht. Dies kann nicht rückgängig gemacht werden! Die Nachrichten werden NUR bei Ihnen gelöscht. No comment provided by engineer. @@ -1072,6 +1073,10 @@ wurde vom Administrator blockiert No comment provided by engineer. + + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. Sowohl Sie, als auch Ihr Kontakt können Reaktionen auf Nachrichten geben. @@ -1375,6 +1380,7 @@ Configured %@ servers + Konfigurierte %@ Server No comment provided by engineer. @@ -1536,6 +1542,10 @@ Das ist Ihr eigener Einmal-Link! Verbindungsfehler (AUTH) No comment provided by engineer. + + Connection notifications + No comment provided by engineer. + Connection request sent! Verbindungsanfrage wurde gesendet! @@ -2126,11 +2136,19 @@ Dies kann nicht rückgängig gemacht werden! Desktop-Geräte No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + Destination server error: %@ Zielserver-Fehler: %@ snd error text + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + Detailed statistics Detaillierte Statistiken @@ -2196,6 +2214,10 @@ Dies kann nicht rückgängig gemacht werden! Für Alle deaktivieren No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message Verschwindende Nachricht @@ -2421,6 +2443,10 @@ Dies kann nicht rückgängig gemacht werden! Selbstzerstörungs-Zugangscode aktivieren set passcode view + + Enabled + No comment provided by engineer. + Enabled for Aktiviert für @@ -2591,6 +2617,10 @@ Dies kann nicht rückgängig gemacht werden! Fehler beim Ändern der Einstellung No comment provided by engineer. + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address Fehler beim Erstellen der Adresse @@ -3097,6 +3127,18 @@ Dies kann nicht rückgängig gemacht werden! Weitergeleitet aus No comment provided by engineer. + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + Forwarding server: %1$@ Destination server error: %2$@ @@ -3523,12 +3565,12 @@ Fehler: %2$@ Incompatible database version - Inkompatible Datenbank-Version + Datenbank-Version nicht kompatibel No comment provided by engineer. Incompatible version - Inkompatible Version + Version nicht kompatibel No comment provided by engineer. @@ -3916,6 +3958,10 @@ Das ist Ihr Link für die Gruppe %@! Max. 30 Sekunden, sofort erhalten. No comment provided by engineer. + + Medium + blur media + Member Mitglied @@ -3998,6 +4044,7 @@ Das ist Ihr Link für die Gruppe %@! Message reception + Nachrichtenempfang No comment provided by engineer. @@ -4367,7 +4414,7 @@ Das ist Ihr Link für die Gruppe %@! Off Aus - No comment provided by engineer. + blur media Ok @@ -4551,6 +4598,7 @@ Das ist Ihr Link für die Gruppe %@! Other %@ servers + Andere %@ Server No comment provided by engineer. @@ -4722,10 +4770,6 @@ Fehler: %@ 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. - - Please try later. - No comment provided by engineer. - Polish interface Polnische Bedienoberfläche @@ -4798,6 +4842,7 @@ Fehler: %@ Private routing error + Fehler beim privaten Routing No comment provided by engineer. @@ -4919,7 +4964,7 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Proxied - Proxy + Proxied No comment provided by engineer. @@ -5734,11 +5779,12 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Server address is incompatible with network settings. - Die Server-Adresse ist nicht mit den Netzwerk-Einstellungen kompatibel. + Die Server-Adresse ist nicht mit den Netzwerkeinstellungen kompatibel. srv error text. Server address is incompatible with network settings: %@. + Die Server-Adresse ist nicht mit den Netzwerkeinstellungen kompatibel: %@. No comment provided by engineer. @@ -5763,15 +5809,12 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Server version is incompatible with network settings. - Die Server-Version ist nicht mit den Netzwerk-Einstellungen kompatibel. + Die Server-Version ist nicht mit den Netzwerkeinstellungen kompatibel. srv error text - - Server version is incompatible with network settings: %@. - No comment provided by engineer. - Server version is incompatible with your app: %@. + Die Server-Version ist nicht mit Ihrer App kompatibel: %@. No comment provided by engineer. @@ -5916,6 +5959,7 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Show percentage + Prozentualen Anteil anzeigen No comment provided by engineer. @@ -6033,6 +6077,10 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Kleine Gruppen (max. 20) No comment provided by engineer. + + Soft + blur media + Some non-fatal errors occurred during import - you may see Chat console for more details. Während des Imports sind einige nicht schwerwiegende Fehler aufgetreten - in der Chat-Konsole finden Sie weitere Einzelheiten. @@ -6133,6 +6181,10 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Chat wird beendet No comment provided by engineer. + + Strong + blur media + Submit Bestätigen @@ -6392,12 +6444,12 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain. - Diese Aktion kann nicht rückgängig gemacht werden! Alle empfangenen und gesendeten Dateien und Medien werden gelöscht. Bilder mit niedriger Auflösung bleiben erhalten. + Diese Aktion kann nicht rückgängig gemacht werden! Es werden alle empfangenen und gesendeten Dateien und Medien gelöscht. Bilder mit niedriger Auflösung bleiben erhalten. No comment provided by engineer. This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes. - Diese Aktion kann nicht rückgängig gemacht werden! Alle empfangenen und gesendeten Nachrichten, die über den ausgewählten Zeitraum hinaus gehen, werden gelöscht. Dieser Vorgang kann mehrere Minuten dauern. + Diese Aktion kann nicht rückgängig gemacht werden! Es werden alle empfangenen und gesendeten Nachrichten, die über den ausgewählten Zeitraum hinaus gehen, gelöscht. Dieser Vorgang kann mehrere Minuten dauern. No comment provided by engineer. @@ -8529,4 +8581,154 @@ Zuletzt empfangene Nachricht: %2$@ + +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Dismiss Sheet + No comment provided by engineer. + + + Encrypted database + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keep Trying + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + No network connection + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Sending File + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Sharing is not supported when passphrase is not stored in KeyChain. + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/de.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ 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 a0ec9aca4a..ca5c2670d7 100644 --- a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff +++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff @@ -1073,6 +1073,11 @@ Blocked by admin No comment provided by engineer.
+ + Blur media + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. Both you and your contact can add message reactions. @@ -1538,6 +1543,11 @@ This is your own one-time link! Connection error (AUTH) No comment provided by engineer. + + Connection notifications + Connection notifications + No comment provided by engineer. + Connection request sent! Connection request sent! @@ -2128,11 +2138,21 @@ This cannot be undone! Desktop devices No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + Destination server error: %@ Destination server error: %@ snd error text + + Destination server version of %@ is incompatible with forwarding server %@. + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + Detailed statistics Detailed statistics @@ -2198,6 +2218,11 @@ This cannot be undone! Disable for all No comment provided by engineer. + + Disabled + Disabled + No comment provided by engineer. + Disappearing message Disappearing message @@ -2423,6 +2448,11 @@ This cannot be undone! Enable self-destruct passcode set passcode view + + Enabled + Enabled + No comment provided by engineer. + Enabled for Enabled for @@ -2593,6 +2623,11 @@ This cannot be undone! Error changing setting No comment provided by engineer. + + Error connecting to forwarding server %@. Please try later. + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address Error creating address @@ -3099,6 +3134,21 @@ This cannot be undone! Forwarded from No comment provided by engineer. + + Forwarding server %@ failed to connect to destination server %@. Please try later. + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + Forwarding server: %1$@ Destination server error: %2$@ @@ -3918,6 +3968,11 @@ This is your link for group %@! Max 30 seconds, received instantly. No comment provided by engineer. + + Medium + Medium + blur media + Member Member @@ -4370,7 +4425,7 @@ This is your link for group %@! Off Off - No comment provided by engineer. + blur media Ok @@ -4726,11 +4781,6 @@ Error: %@ Please store passphrase securely, you will NOT be able to change it if you lose it. No comment provided by engineer. - - Please try later. - Please try later. - No comment provided by engineer. - Polish interface Polish interface @@ -5773,11 +5823,6 @@ Enable in *Network & servers* settings. Server version is incompatible with network settings. srv error text - - Server version is incompatible with network settings: %@. - Server version is incompatible with network settings: %@. - No comment provided by engineer. - Server version is incompatible with your app: %@. Server version is incompatible with your app: %@. @@ -6043,6 +6088,11 @@ Enable in *Network & servers* settings. Small groups (max 20) No comment provided by engineer. + + Soft + Soft + blur media + Some non-fatal errors occurred during import - you may see Chat console for more details. Some non-fatal errors occurred during import - you may see Chat console for more details. @@ -6143,6 +6193,11 @@ Enable in *Network & servers* settings. Stopping chat No comment provided by engineer. + + Strong + Strong + blur media + Submit Submit @@ -8539,4 +8594,188 @@ last received msg: %2$@ + +
+ +
+ + + SimpleX SE + SimpleX SE + Bundle display name + + + SimpleX SE + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + %@ + No comment provided by engineer. + + + Cannot access keychain to save database password + Cannot access keychain to save database password + No comment provided by engineer. + + + Comment + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + Database downgrade required + No comment provided by engineer. + + + Database error + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database upgrade required + Database upgrade required + No comment provided by engineer. + + + Dismiss Sheet + Dismiss Sheet + No comment provided by engineer. + + + Encrypted database + Encrypted database + No comment provided by engineer. + + + Error preparing file + Error preparing file + No comment provided by engineer. + + + Error preparing message + Error preparing message + No comment provided by engineer. + + + Error: %@ + Error: %@ + No comment provided by engineer. + + + File error + File error + No comment provided by engineer. + + + Incompatible database version + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + Invalid migration confirmation + No comment provided by engineer. + + + Keep Trying + Keep Trying + No comment provided by engineer. + + + Keychain error + Keychain error + No comment provided by engineer. + + + Large file! + Large file! + No comment provided by engineer. + + + No active profile + No active profile + No comment provided by engineer. + + + No network connection + No network connection + No comment provided by engineer. + + + Ok + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + Open the app to upgrade the database. + No comment provided by engineer. + + + Please create a profile in the SimpleX app + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Sending File + Sending File + No comment provided by engineer. + + + Share + Share + No comment provided by engineer. + + + Sharing is not supported when passphrase is not stored in KeyChain. + Sharing is not supported when passphrase is not stored in KeyChain. + No comment provided by engineer. + + + Unknown database error: %@ + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + Unsupported format + No comment provided by engineer. + + + Wrong database passphrase + Wrong database passphrase + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/en.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff b/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff index 837ea06e96..074d561640 100644 --- a/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff +++ b/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff @@ -554,6 +554,7 @@
Accent + Color No comment provided by engineer. @@ -579,14 +580,17 @@ Acknowledged + Recibido No comment provided by engineer. Acknowledgement errors + Errores de reconocimiento No comment provided by engineer. Active connections + Conexiones activas No comment provided by engineer. @@ -631,14 +635,17 @@ Additional accent + Acento adicional No comment provided by engineer. Additional accent 2 + Color adicional 2 No comment provided by engineer. Additional secondary + Secundario adicional No comment provided by engineer. @@ -668,6 +675,7 @@ Advanced settings + Configuración avanzada No comment provided by engineer. @@ -687,6 +695,7 @@ All data is private to your device. + Todos los datos son privados para tu dispositivo. No comment provided by engineer. @@ -711,6 +720,7 @@ All profiles + Todos los perfiles No comment provided by engineer. @@ -915,6 +925,7 @@ Apply to + Aplicar a No comment provided by engineer. @@ -994,6 +1005,7 @@ Background + Fondo No comment provided by engineer. @@ -1023,6 +1035,7 @@ Black + Negro No comment provided by engineer. @@ -1060,6 +1073,10 @@ Bloqueado por administrador No comment provided by engineer. + + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. Tanto tú como tu contacto podéis añadir reacciones a los mensajes. @@ -1137,6 +1154,7 @@ Cannot forward message + No se puede reenviar el mensaje No comment provided by engineer. @@ -1212,6 +1230,7 @@ Chat colors + Colores del chat No comment provided by engineer. @@ -1261,6 +1280,7 @@ Chat theme + Tema de chat No comment provided by engineer. @@ -1295,14 +1315,17 @@ Chunks deleted + Bloques eliminados No comment provided by engineer. Chunks downloaded + Bloques descargados No comment provided by engineer. Chunks uploaded + Bloques subidos No comment provided by engineer. @@ -1332,6 +1355,7 @@ Color mode + Modo de color No comment provided by engineer. @@ -1346,6 +1370,7 @@ Completed + Completado No comment provided by engineer. @@ -1355,6 +1380,7 @@ Configured %@ servers + %@ servidores configurados No comment provided by engineer. @@ -1463,6 +1489,7 @@ This is your own one-time link! Connected + Conectado No comment provided by engineer. @@ -1472,6 +1499,7 @@ This is your own one-time link! Connected servers + Servidores conectados No comment provided by engineer. @@ -1481,6 +1509,7 @@ This is your own one-time link! Connecting + Conectando No comment provided by engineer. @@ -1513,6 +1542,10 @@ This is your own one-time link! Error de conexión (Autenticación) No comment provided by engineer. + + Connection notifications + No comment provided by engineer. + Connection request sent! ¡Solicitud de conexión enviada! @@ -1530,10 +1563,12 @@ This is your own one-time link! Connection with desktop stopped + La conexión con el escritorio (desktop) se ha parado No comment provided by engineer. Connections + Conexiones No comment provided by engineer. @@ -1593,6 +1628,7 @@ This is your own one-time link! Copy error + Copiar error No comment provided by engineer. @@ -1672,6 +1708,7 @@ This is your own one-time link! Created + Creado No comment provided by engineer. @@ -1711,6 +1748,7 @@ This is your own one-time link! Current profile + Perfil actual No comment provided by engineer. @@ -1725,6 +1763,7 @@ This is your own one-time link! Customize theme + Personalizar tema No comment provided by engineer. @@ -1734,6 +1773,7 @@ This is your own one-time link! Dark mode colors + Colores en modo oscuro No comment provided by engineer. @@ -2043,6 +2083,7 @@ This cannot be undone! Deleted + Eliminado No comment provided by engineer. @@ -2057,6 +2098,7 @@ This cannot be undone! Deletion errors + Errores de borrado No comment provided by engineer. @@ -2094,17 +2136,27 @@ This cannot be undone! Ordenadores No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + Destination server error: %@ Error del servidor de destino: %@ snd error text + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + Detailed statistics + Estadísticas detalladas No comment provided by engineer. Details + Detalles No comment provided by engineer. @@ -2162,6 +2214,10 @@ This cannot be undone! Desactivar para todos No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message Mensaje temporal @@ -2264,6 +2320,7 @@ This cannot be undone! Download errors + Errores en la descarga No comment provided by engineer. @@ -2278,10 +2335,12 @@ This cannot be undone! Downloaded + Descargado No comment provided by engineer. Downloaded files + Archivos descargados No comment provided by engineer. @@ -2384,6 +2443,10 @@ This cannot be undone! Activar código de autodestrucción set passcode view + + Enabled + No comment provided by engineer. + Enabled for Activado para @@ -2554,6 +2617,10 @@ This cannot be undone! Error cambiando configuración No comment provided by engineer. + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address Error al crear dirección @@ -2656,6 +2723,7 @@ This cannot be undone! Error exporting theme: %@ + Error al exportar tema: %@ No comment provided by engineer. @@ -2685,10 +2753,12 @@ This cannot be undone! Error reconnecting server + Error al reconectar con el servidor No comment provided by engineer. Error reconnecting servers + Error al reconectar con los servidores No comment provided by engineer. @@ -2698,6 +2768,7 @@ This cannot be undone! Error resetting statistics + Error al restablecer las estadísticas No comment provided by engineer. @@ -2833,6 +2904,7 @@ This cannot be undone! Errors + Errores No comment provided by engineer. @@ -2862,6 +2934,7 @@ This cannot be undone! Export theme + Exportar tema No comment provided by engineer. @@ -2901,22 +2974,27 @@ This cannot be undone! File error + Error de archivo No comment provided by engineer. File not found - most likely file was deleted or cancelled. + Archivo no encontrado, probablemente haya sido borrado o cancelado. file error text File server error: %@ + Error del servidor de archivos: %@ file error text File status + Estado del archivo No comment provided by engineer. File status: %@ + Estado del archivo: %@ copied message info @@ -3049,6 +3127,18 @@ This cannot be undone! Reenviado por No comment provided by engineer. + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + Forwarding server: %1$@ Destination server error: %2$@ @@ -3110,10 +3200,12 @@ Error: %2$@ Good afternoon! + ¡Buenas tardes! message preview Good morning! + ¡Buenos días! message preview @@ -3398,6 +3490,7 @@ Error: %2$@ Import theme + Importar tema No comment provided by engineer. @@ -3524,6 +3617,7 @@ Error: %2$@ Interface colors + Colores del interfaz No comment provided by engineer. @@ -3864,6 +3958,10 @@ This is your link for group %@! Máximo 30 segundos, recibido al instante. No comment provided by engineer. + + Medium + blur media + Member Miembro @@ -3871,6 +3969,7 @@ This is your link for group %@! Member inactive + Miembro inactivo item status text @@ -3890,6 +3989,7 @@ This is your link for group %@! Menus + Menus No comment provided by engineer. @@ -3914,10 +4014,12 @@ This is your link for group %@! Message forwarded + Mensaje reenviado item status text Message may be delivered later if member becomes active. + El mensaje puede ser entregado más tarde si el miembro vuelve a estar activo. item status description @@ -3942,6 +4044,7 @@ This is your link for group %@! Message reception + Recepción de mensaje No comment provided by engineer. @@ -3961,10 +4064,12 @@ This is your link for group %@! Message status + Estado del mensaje No comment provided by engineer. Message status: %@ + Estado del mensaje: %@ copied message info @@ -3994,10 +4099,12 @@ This is your link for group %@! Messages received + Mensajes recibidos No comment provided by engineer. Messages sent + Mensajes enviados No comment provided by engineer. @@ -4237,6 +4344,7 @@ This is your link for group %@! No direct connection yet, message is forwarded by admin. + Aún no hay conexión directa, el mensaje es reenviado por el administrador. item status description @@ -4256,6 +4364,7 @@ This is your link for group %@! No info, try to reload + No hay información, intenta recargar No comment provided by engineer. @@ -4305,7 +4414,7 @@ This is your link for group %@! Off Desactivado - No comment provided by engineer. + blur media Ok @@ -4444,6 +4553,7 @@ This is your link for group %@! Open server settings + Abrir configuración del servidor No comment provided by engineer. @@ -4488,6 +4598,7 @@ This is your link for group %@! Other %@ servers + Otros servidores %@ No comment provided by engineer. @@ -4557,6 +4668,7 @@ This is your link for group %@! Pending + Pendiente No comment provided by engineer. @@ -4587,6 +4699,8 @@ This is your link for group %@! Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. Please share any other issues with the developers. + Comprueba que el móvil y el ordenador están conectados a la misma red local y que el cortafuegos del ordenador permite la conexión. +Por favor, comparte cualquier otro problema con los desarrolladores. No comment provided by engineer. @@ -4656,10 +4770,6 @@ Error: %@ Guarda la contraseña de forma segura, NO podrás cambiarla si la pierdes. No comment provided by engineer. - - Please try later. - No comment provided by engineer. - Polish interface Interfaz en polaco @@ -4692,6 +4802,7 @@ Error: %@ Previously connected servers + Servidores conectados previamente No comment provided by engineer. @@ -4731,6 +4842,7 @@ Error: %@ Private routing error + Error de enrutamiento privado No comment provided by engineer. @@ -4765,6 +4877,7 @@ Error: %@ Profile theme + Tema del perfil No comment provided by engineer. @@ -4851,10 +4964,12 @@ Actívalo en ajustes de *Servidores y Redes*. Proxied + Con proxy No comment provided by engineer. Proxied servers + Servidores con proxy No comment provided by engineer. @@ -4924,6 +5039,7 @@ Actívalo en ajustes de *Servidores y Redes*. Receive errors + Errores de recepción No comment provided by engineer. @@ -4948,14 +5064,17 @@ Actívalo en ajustes de *Servidores y Redes*. Received messages + Mensajes recibidos No comment provided by engineer. Received reply + Respuesta recibida No comment provided by engineer. Received total + Total recibido No comment provided by engineer. @@ -4990,6 +5109,7 @@ Actívalo en ajustes de *Servidores y Redes*. Reconnect + Reconectar No comment provided by engineer. @@ -4999,18 +5119,22 @@ Actívalo en ajustes de *Servidores y Redes*. Reconnect all servers + Reconectar todos los servidores No comment provided by engineer. Reconnect all servers? + ¿Reconectar todos los servidores? No comment provided by engineer. Reconnect server to force message delivery. It uses additional traffic. + Reconectar el servidor para forzar la entrega de mensajes. Usa tráfico adicional. No comment provided by engineer. Reconnect server? + ¿Reconectar servidor? No comment provided by engineer. @@ -5065,6 +5189,7 @@ Actívalo en ajustes de *Servidores y Redes*. Remove image + Eliminar imagen No comment provided by engineer. @@ -5139,10 +5264,12 @@ Actívalo en ajustes de *Servidores y Redes*. Reset all statistics + Restablecer estadísticas No comment provided by engineer. Reset all statistics? + ¿Restablecer todas las estadísticas? No comment provided by engineer. @@ -5152,6 +5279,7 @@ Actívalo en ajustes de *Servidores y Redes*. Reset to app theme + Restablecer al tema de la aplicación No comment provided by engineer. @@ -5161,6 +5289,7 @@ Actívalo en ajustes de *Servidores y Redes*. Reset to user theme + Restablecer al tema del usuario No comment provided by engineer. @@ -5235,6 +5364,7 @@ Actívalo en ajustes de *Servidores y Redes*. SMP server + Servidor SMP No comment provided by engineer. @@ -5354,10 +5484,12 @@ Actívalo en ajustes de *Servidores y Redes*. Scale + Escala No comment provided by engineer. Scan / Paste link + Escanear / Pegar enlace No comment provided by engineer. @@ -5402,6 +5534,7 @@ Actívalo en ajustes de *Servidores y Redes*. Secondary + Secundario No comment provided by engineer. @@ -5411,6 +5544,7 @@ Actívalo en ajustes de *Servidores y Redes*. Secured + Seguro No comment provided by engineer. @@ -5430,6 +5564,7 @@ Actívalo en ajustes de *Servidores y Redes*. Selected chat preferences prohibit this message. + Las preferencias seleccionadas no permiten este mensaje. No comment provided by engineer. @@ -5484,6 +5619,7 @@ Actívalo en ajustes de *Servidores y Redes*. Send errors + Errores de envío No comment provided by engineer. @@ -5598,6 +5734,7 @@ Actívalo en ajustes de *Servidores y Redes*. Sent directly + Enviado directamente No comment provided by engineer. @@ -5612,6 +5749,7 @@ Actívalo en ajustes de *Servidores y Redes*. Sent messages + Mensajes enviados No comment provided by engineer. @@ -5621,18 +5759,22 @@ Actívalo en ajustes de *Servidores y Redes*. Sent reply + Respuesta enviada No comment provided by engineer. Sent total + Total enviado No comment provided by engineer. Sent via proxy + Enviado mediante proxy No comment provided by engineer. Server address + Dirección del servidor No comment provided by engineer. @@ -5642,6 +5784,7 @@ Actívalo en ajustes de *Servidores y Redes*. Server address is incompatible with network settings: %@. + La dirección del servidor es incompatible con la configuración de la red: %@. No comment provided by engineer. @@ -5661,6 +5804,7 @@ Actívalo en ajustes de *Servidores y Redes*. Server type + Tipo de servidor No comment provided by engineer. @@ -5668,12 +5812,9 @@ Actívalo en ajustes de *Servidores y Redes*. La versión del servidor es incompatible con la configuración de red. srv error text - - Server version is incompatible with network settings: %@. - No comment provided by engineer. - Server version is incompatible with your app: %@. + La versión del servidor es incompatible con tu aplicación: %@. No comment provided by engineer. @@ -5683,10 +5824,12 @@ Actívalo en ajustes de *Servidores y Redes*. Servers info + Info servidores No comment provided by engineer. Servers statistics will be reset - this cannot be undone! + Las estadísticas de los servidores serán restablecidas. ¡No podrá deshacerse! No comment provided by engineer. @@ -5706,6 +5849,7 @@ Actívalo en ajustes de *Servidores y Redes*. Set default theme + Establecer tema predeterminado No comment provided by engineer. @@ -5815,6 +5959,7 @@ Actívalo en ajustes de *Servidores y Redes*. Show percentage + Mostrar porcentaje No comment provided by engineer. @@ -5834,6 +5979,7 @@ Actívalo en ajustes de *Servidores y Redes*. SimpleX + SimpleX No comment provided by engineer. @@ -5913,6 +6059,7 @@ Actívalo en ajustes de *Servidores y Redes*. Size + Tamaño No comment provided by engineer. @@ -5930,6 +6077,10 @@ Actívalo en ajustes de *Servidores y Redes*. Grupos pequeños (máx. 20) No comment provided by engineer. + + Soft + blur media + Some non-fatal errors occurred during import - you may see Chat console for more details. Algunos errores no críticos ocurrieron durante la importación - para más detalles puedes ver la consola de Chat. @@ -5962,10 +6113,12 @@ Actívalo en ajustes de *Servidores y Redes*. Starting from %@. + Iniciado desde %@. No comment provided by engineer. Statistics + Estadísticas No comment provided by engineer. @@ -6028,6 +6181,10 @@ Actívalo en ajustes de *Servidores y Redes*. Parando chat No comment provided by engineer. + + Strong + blur media + Submit Enviar @@ -6035,14 +6192,17 @@ Actívalo en ajustes de *Servidores y Redes*. Subscribed + Suscrito No comment provided by engineer. Subscription errors + Errores de suscripción No comment provided by engineer. Subscriptions ignored + Suscripciones ignoradas No comment provided by engineer. @@ -6127,6 +6287,7 @@ Actívalo en ajustes de *Servidores y Redes*. Temporary file error + Error en archivo temporal No comment provided by engineer. @@ -6268,6 +6429,7 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida. Themes + Temas No comment provided by engineer. @@ -6337,6 +6499,7 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida. This link was used with another mobile device, please create a new link on the desktop. + Este enlace ha sido usado en otro dispositivo móvil, por favor crea un enlace nuevo en el ordenador. No comment provided by engineer. @@ -6346,6 +6509,7 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida. Title + Título No comment provided by engineer. @@ -6417,6 +6581,7 @@ Se te pedirá que completes la autenticación antes de activar esta función.
Total + Total No comment provided by engineer. @@ -6426,6 +6591,7 @@ Se te pedirá que completes la autenticación antes de activar esta función. Transport sessions + Sesiones de transporte No comment provided by engineer. @@ -6622,6 +6788,7 @@ Para conectarte pide a tu contacto que cree otro enlace y comprueba la conexión Upload errors + Errores en subida No comment provided by engineer. @@ -6636,10 +6803,12 @@ Para conectarte pide a tu contacto que cree otro enlace y comprueba la conexión Uploaded + Subido No comment provided by engineer. Uploaded files + Archivos subidos No comment provided by engineer. @@ -6719,6 +6888,7 @@ Para conectarte pide a tu contacto que cree otro enlace y comprueba la conexión User selection + Selección de usuarios No comment provided by engineer. @@ -6858,10 +7028,12 @@ Para conectarte pide a tu contacto que cree otro enlace y comprueba la conexión Wallpaper accent + Color imagen de fondo No comment provided by engineer. Wallpaper background + Color de fondo No comment provided by engineer. @@ -6971,6 +7143,7 @@ Para conectarte pide a tu contacto que cree otro enlace y comprueba la conexión Wrong key or unknown file chunk address - most likely file is deleted. + Clave incorrecta o dirección del bloque del archivo desconocida. Es probable que el archivo se haya eliminado. file error text @@ -6980,6 +7153,7 @@ Para conectarte pide a tu contacto que cree otro enlace y comprueba la conexión XFTP server + Servidor XFTP No comment provided by engineer. @@ -7066,6 +7240,7 @@ Repeat join request? You are not connected to these servers. Private routing is used to deliver messages to them. + No estás conectado a estos servidores. Para enviarles mensajes se usa el enrutamiento privado. No comment provided by engineer. @@ -7105,7 +7280,7 @@ Repeat join request? You can now chat with %@ - Ya puedes enviar mensajes a %@ + Ya puedes chatear con %@ notification body @@ -7476,6 +7651,7 @@ Los servidores SimpleX no pueden ver tu perfil. attempts + intentos No comment provided by engineer. @@ -7670,6 +7846,7 @@ Los servidores SimpleX no pueden ver tu perfil. decryption errors + errores de descifrado No comment provided by engineer. @@ -7724,6 +7901,7 @@ Los servidores SimpleX no pueden ver tu perfil. duplicates + duplicados No comment provided by engineer. @@ -7808,6 +7986,7 @@ Los servidores SimpleX no pueden ver tu perfil. expired + expirado No comment provided by engineer. @@ -7842,6 +8021,7 @@ Los servidores SimpleX no pueden ver tu perfil. inactive + inactivo No comment provided by engineer. @@ -8023,10 +8203,12 @@ Los servidores SimpleX no pueden ver tu perfil. other + otro No comment provided by engineer. other errors + otros errores No comment provided by engineer. @@ -8399,4 +8581,154 @@ last received msg: %2$@ + +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Dismiss Sheet + No comment provided by engineer. + + + Encrypted database + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keep Trying + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + No network connection + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Sending File + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Sharing is not supported when passphrase is not stored in KeyChain. + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/es.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff b/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff index 07673d911c..99ac470f53 100644 --- a/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff +++ b/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff @@ -1013,6 +1013,10 @@ Blocked by admin No comment provided by engineer.
+ + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. Sekä sinä että kontaktisi voivat käyttää viestireaktioita. @@ -1441,6 +1445,10 @@ This is your own one-time link! Yhteysvirhe (AUTH) No comment provided by engineer. + + Connection notifications + No comment provided by engineer. + Connection request sent! Yhteyspyyntö lähetetty! @@ -2004,10 +2012,18 @@ This cannot be undone! Desktop devices No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + Destination server error: %@ snd error text + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + Detailed statistics No comment provided by engineer. @@ -2071,6 +2087,10 @@ This cannot be undone! Poista käytöstä kaikilta No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message Tuhoutuva viesti @@ -2282,6 +2302,10 @@ This cannot be undone! Ota itsetuhoava pääsykoodi käyttöön set passcode view + + Enabled + No comment provided by engineer. + Enabled for No comment provided by engineer. @@ -2443,6 +2467,10 @@ This cannot be undone! Virhe asetuksen muuttamisessa No comment provided by engineer. + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address Virhe osoitteen luomisessa @@ -2918,6 +2946,18 @@ This cannot be undone! Forwarded from No comment provided by engineer. + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + Forwarding server: %1$@ Destination server error: %2$@ @@ -3698,6 +3738,10 @@ This is your link for group %@! Enintään 30 sekuntia, vastaanotetaan välittömästi. No comment provided by engineer. + + Medium + blur media + Member Jäsen @@ -4114,7 +4158,7 @@ This is your link for group %@! Off Pois - No comment provided by engineer. + blur media Ok @@ -4448,10 +4492,6 @@ Error: %@ Säilytä tunnuslause turvallisesti, ET voi muuttaa sitä, jos kadotat sen. No comment provided by engineer. - - Please try later. - No comment provided by engineer. - Polish interface Puolalainen käyttöliittymä @@ -5424,10 +5464,6 @@ Enable in *Network & servers* settings. Server version is incompatible with network settings. srv error text - - Server version is incompatible with network settings: %@. - No comment provided by engineer. - Server version is incompatible with your app: %@. No comment provided by engineer. @@ -5676,6 +5712,10 @@ Enable in *Network & servers* settings. Pienryhmät (max 20) No comment provided by engineer. + + Soft + blur media + Some non-fatal errors occurred during import - you may see Chat console for more details. Tuonnin aikana tapahtui joitakin ei-vakavia virheitä – saatat nähdä Chat-konsolissa lisätietoja. @@ -5770,6 +5810,10 @@ Enable in *Network & servers* settings. Stopping chat No comment provided by engineer. + + Strong + blur media + Submit Lähetä @@ -8035,4 +8079,154 @@ last received msg: %2$@ + +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Dismiss Sheet + No comment provided by engineer. + + + Encrypted database + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keep Trying + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + No network connection + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Sending File + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Sharing is not supported when passphrase is not stored in KeyChain. + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/fi.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff index 5c23f58e9c..3591ab15ab 100644 --- a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff @@ -554,6 +554,7 @@
Accent + Principale No comment provided by engineer. @@ -579,14 +580,17 @@ Acknowledged + Reçu avec accusé de réception No comment provided by engineer. Acknowledgement errors + Erreur d'accusé de réception No comment provided by engineer. Active connections + Connections actives No comment provided by engineer. @@ -631,14 +635,17 @@ Additional accent + Accent additionnel No comment provided by engineer. Additional accent 2 + Accent additionnel 2 No comment provided by engineer. Additional secondary + Accent secondaire No comment provided by engineer. @@ -668,6 +675,7 @@ Advanced settings + Paramètres avancés No comment provided by engineer. @@ -687,6 +695,7 @@ All data is private to your device. + Toutes les données restent confinées dans votre appareil. No comment provided by engineer. @@ -711,6 +720,7 @@ All profiles + Tous les profiles No comment provided by engineer. @@ -915,6 +925,7 @@ Apply to + Appliquer à No comment provided by engineer. @@ -994,6 +1005,7 @@ Background + Fond No comment provided by engineer. @@ -1023,6 +1035,7 @@ Black + Noir No comment provided by engineer. @@ -1060,6 +1073,10 @@ Bloqué par l'administrateur No comment provided by engineer. + + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. Vous et votre contact pouvez ajouter des réactions aux messages. @@ -1137,6 +1154,7 @@ Cannot forward message + Impossible de transférer le message No comment provided by engineer. @@ -1212,6 +1230,7 @@ Chat colors + Couleurs de chat No comment provided by engineer. @@ -1261,6 +1280,7 @@ Chat theme + Thème de chat No comment provided by engineer. @@ -1295,14 +1315,17 @@ Chunks deleted + Chunks supprimés No comment provided by engineer. Chunks downloaded + Chunks téléchargés No comment provided by engineer. Chunks uploaded + Chunks téléversés No comment provided by engineer. @@ -1332,6 +1355,7 @@ Color mode + Mode de couleur No comment provided by engineer. @@ -1346,6 +1370,7 @@ Completed + Complété No comment provided by engineer. @@ -1355,6 +1380,7 @@ Configured %@ servers + %@ serveurs configurés No comment provided by engineer. @@ -1463,6 +1489,7 @@ Il s'agit de votre propre lien unique ! Connected + Connecté No comment provided by engineer. @@ -1472,6 +1499,7 @@ Il s'agit de votre propre lien unique ! Connected servers + Serveurs connectés No comment provided by engineer. @@ -1481,6 +1509,7 @@ Il s'agit de votre propre lien unique ! Connecting + Connexion No comment provided by engineer. @@ -1513,6 +1542,10 @@ Il s'agit de votre propre lien unique ! Erreur de connexion (AUTH) No comment provided by engineer. + + Connection notifications + No comment provided by engineer. + Connection request sent! Demande de connexion envoyée ! @@ -1530,10 +1563,12 @@ Il s'agit de votre propre lien unique ! Connection with desktop stopped + La connexion avec le bureau s'est arrêtée No comment provided by engineer. Connections + Connexions No comment provided by engineer. @@ -1593,6 +1628,7 @@ Il s'agit de votre propre lien unique ! Copy error + Erreur de copie No comment provided by engineer. @@ -1672,6 +1708,7 @@ Il s'agit de votre propre lien unique ! Created + Créé No comment provided by engineer. @@ -1711,6 +1748,7 @@ Il s'agit de votre propre lien unique ! Current profile + Profil actuel No comment provided by engineer. @@ -1725,6 +1763,7 @@ Il s'agit de votre propre lien unique ! Customize theme + Personnaliser le thème No comment provided by engineer. @@ -1734,6 +1773,7 @@ Il s'agit de votre propre lien unique ! Dark mode colors + Couleurs en mode sombre No comment provided by engineer. @@ -2043,6 +2083,7 @@ Cette opération ne peut être annulée ! Deleted + Supprimé No comment provided by engineer. @@ -2057,6 +2098,7 @@ Cette opération ne peut être annulée ! Deletion errors + Erreurs de suppression No comment provided by engineer. @@ -2094,17 +2136,27 @@ Cette opération ne peut être annulée ! Appareils de bureau No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + Destination server error: %@ Erreur du serveur de destination : %@ snd error text + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + Detailed statistics + Statistiques détaillées No comment provided by engineer. Details + Détails No comment provided by engineer. @@ -2162,6 +2214,10 @@ Cette opération ne peut être annulée ! Désactiver pour tous No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message Message éphémère @@ -2264,6 +2320,7 @@ Cette opération ne peut être annulée ! Download errors + Erreurs de téléchargement No comment provided by engineer. @@ -2278,10 +2335,12 @@ Cette opération ne peut être annulée ! Downloaded + Téléchargé No comment provided by engineer. Downloaded files + Fichiers téléchargés No comment provided by engineer. @@ -2384,6 +2443,10 @@ Cette opération ne peut être annulée ! Activer le code d'autodestruction set passcode view + + Enabled + No comment provided by engineer. + Enabled for Activé pour @@ -2554,6 +2617,10 @@ Cette opération ne peut être annulée ! Erreur de changement de paramètre No comment provided by engineer. + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address Erreur lors de la création de l'adresse @@ -2656,6 +2723,7 @@ Cette opération ne peut être annulée ! Error exporting theme: %@ + Erreur d'exportation du thème : %@ No comment provided by engineer. @@ -2685,10 +2753,12 @@ Cette opération ne peut être annulée ! Error reconnecting server + Erreur de reconnexion du serveur No comment provided by engineer. Error reconnecting servers + Erreur de reconnexion des serveurs No comment provided by engineer. @@ -2698,6 +2768,7 @@ Cette opération ne peut être annulée ! Error resetting statistics + Erreur de réinitialisation des statistiques No comment provided by engineer. @@ -2833,6 +2904,7 @@ Cette opération ne peut être annulée ! Errors + Erreurs No comment provided by engineer. @@ -2862,6 +2934,7 @@ Cette opération ne peut être annulée ! Export theme + Exporter le thème No comment provided by engineer. @@ -2901,22 +2974,27 @@ Cette opération ne peut être annulée ! File error + Erreur de fichier No comment provided by engineer. File not found - most likely file was deleted or cancelled. + Fichier introuvable - le fichier a probablement été supprimé ou annulé. file error text File server error: %@ + Erreur de serveur de fichiers : %@ file error text File status + Statut du fichier No comment provided by engineer. File status: %@ + Statut du fichier : %@ copied message info @@ -3049,6 +3127,18 @@ Cette opération ne peut être annulée ! Transféré depuis No comment provided by engineer. + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + Forwarding server: %1$@ Destination server error: %2$@ @@ -3110,10 +3200,12 @@ Erreur : %2$@ Good afternoon! + Bonjour ! message preview Good morning! + Bonjour ! message preview @@ -3398,6 +3490,7 @@ Erreur : %2$@ Import theme + Importer un thème No comment provided by engineer. @@ -3524,6 +3617,7 @@ Erreur : %2$@ Interface colors + Couleurs d'interface No comment provided by engineer. @@ -3864,6 +3958,10 @@ Voici votre lien pour le groupe %@ ! Max 30 secondes, réception immédiate. No comment provided by engineer. + + Medium + blur media + Member Membre @@ -3871,6 +3969,7 @@ Voici votre lien pour le groupe %@ ! Member inactive + Membre inactif item status text @@ -3890,6 +3989,7 @@ Voici votre lien pour le groupe %@ ! Menus + Menus No comment provided by engineer. @@ -3914,10 +4014,12 @@ Voici votre lien pour le groupe %@ ! Message forwarded + Message transféré item status text Message may be delivered later if member becomes active. + Le message peut être transmis plus tard si le membre devient actif. item status description @@ -3942,6 +4044,7 @@ Voici votre lien pour le groupe %@ ! Message reception + Réception de message No comment provided by engineer. @@ -3961,10 +4064,12 @@ Voici votre lien pour le groupe %@ ! Message status + Statut du message No comment provided by engineer. Message status: %@ + Statut du message : %@ copied message info @@ -3994,10 +4099,12 @@ Voici votre lien pour le groupe %@ ! Messages received + Messages reçus No comment provided by engineer. Messages sent + Messages envoyés No comment provided by engineer. @@ -4237,6 +4344,7 @@ Voici votre lien pour le groupe %@ ! No direct connection yet, message is forwarded by admin. + Pas de connexion directe pour l'instant, le message est transmis par l'administrateur. item status description @@ -4256,6 +4364,7 @@ Voici votre lien pour le groupe %@ ! No info, try to reload + Pas d'info, essayez de recharger No comment provided by engineer. @@ -4305,7 +4414,7 @@ Voici votre lien pour le groupe %@ ! Off Off - No comment provided by engineer. + blur media Ok @@ -4444,6 +4553,7 @@ Voici votre lien pour le groupe %@ ! Open server settings + Ouvrir les paramètres du serveur No comment provided by engineer. @@ -4488,6 +4598,7 @@ Voici votre lien pour le groupe %@ ! Other %@ servers + Autres serveurs %@ No comment provided by engineer. @@ -4557,6 +4668,7 @@ Voici votre lien pour le groupe %@ ! Pending + En attente No comment provided by engineer. @@ -4587,6 +4699,8 @@ Voici votre lien pour le groupe %@ ! Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. Please share any other issues with the developers. + Veuillez vérifier que le téléphone portable et l'ordinateur de bureau sont connectés au même réseau local et que le pare-feu de l'ordinateur de bureau autorise la connexion. +Veuillez faire part de tout autre problème aux développeurs. No comment provided by engineer. @@ -4656,10 +4770,6 @@ Erreur : %@ Veuillez conserver votre phrase secrète en lieu sûr, vous NE pourrez PAS la changer si vous la perdez. No comment provided by engineer. - - Please try later. - No comment provided by engineer. - Polish interface Interface en polonais @@ -4692,6 +4802,7 @@ Erreur : %@ Previously connected servers + Serveurs précédemment connectés No comment provided by engineer. @@ -4731,6 +4842,7 @@ Erreur : %@ Private routing error + Erreur de routage privé No comment provided by engineer. @@ -4765,6 +4877,7 @@ Erreur : %@ Profile theme + Thème de profil No comment provided by engineer. @@ -4851,10 +4964,12 @@ Activez-le dans les paramètres *Réseau et serveurs*. Proxied + Routé via un proxy No comment provided by engineer. Proxied servers + Serveurs routés via des proxy No comment provided by engineer. @@ -4924,6 +5039,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. Receive errors + Erreurs reçues No comment provided by engineer. @@ -4948,14 +5064,17 @@ Activez-le dans les paramètres *Réseau et serveurs*. Received messages + Messages reçus No comment provided by engineer. Received reply + Réponse reçue No comment provided by engineer. Received total + Total reçu No comment provided by engineer. @@ -4990,6 +5109,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. Reconnect + Reconnecter No comment provided by engineer. @@ -4999,18 +5119,22 @@ Activez-le dans les paramètres *Réseau et serveurs*. Reconnect all servers + Reconnecter tous les serveurs No comment provided by engineer. Reconnect all servers? + Reconnecter tous les serveurs ? No comment provided by engineer. Reconnect server to force message delivery. It uses additional traffic. + Reconnecter le serveur pour forcer la livraison des messages. Utilise du trafic supplémentaire. No comment provided by engineer. Reconnect server? + Reconnecter le serveur ? No comment provided by engineer. @@ -5065,6 +5189,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. Remove image + Enlever l'image No comment provided by engineer. @@ -5139,10 +5264,12 @@ Activez-le dans les paramètres *Réseau et serveurs*. Reset all statistics + Réinitialiser toutes les statistiques No comment provided by engineer. Reset all statistics? + Réinitialiser toutes les statistiques ? No comment provided by engineer. @@ -5152,6 +5279,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. Reset to app theme + Réinitialisation au thème de l'appli No comment provided by engineer. @@ -5161,6 +5289,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. Reset to user theme + Réinitialisation au thème de l'utilisateur No comment provided by engineer. @@ -5235,6 +5364,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. SMP server + Serveur SMP No comment provided by engineer. @@ -5354,10 +5484,12 @@ Activez-le dans les paramètres *Réseau et serveurs*. Scale + Échelle No comment provided by engineer. Scan / Paste link + Scanner / Coller le lien No comment provided by engineer. @@ -5402,6 +5534,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. Secondary + Secondaire No comment provided by engineer. @@ -5411,6 +5544,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. Secured + Sécurisé No comment provided by engineer. @@ -5430,6 +5564,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. Selected chat preferences prohibit this message. + Les préférences de chat sélectionnées interdisent ce message. No comment provided by engineer. @@ -5484,6 +5619,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. Send errors + Erreurs d'envoi No comment provided by engineer. @@ -5598,6 +5734,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. Sent directly + Envoyé directement No comment provided by engineer. @@ -5612,6 +5749,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. Sent messages + Messages envoyés No comment provided by engineer. @@ -5621,18 +5759,22 @@ Activez-le dans les paramètres *Réseau et serveurs*. Sent reply + Réponse envoyée No comment provided by engineer. Sent total + Total envoyé No comment provided by engineer. Sent via proxy + Envoyé via le proxy No comment provided by engineer. Server address + Adresse du serveur No comment provided by engineer. @@ -5642,6 +5784,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. Server address is incompatible with network settings: %@. + L'adresse du serveur est incompatible avec les paramètres réseau : %@. No comment provided by engineer. @@ -5661,6 +5804,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. Server type + Type de serveur No comment provided by engineer. @@ -5668,12 +5812,9 @@ Activez-le dans les paramètres *Réseau et serveurs*. La version du serveur est incompatible avec les paramètres du réseau. srv error text - - Server version is incompatible with network settings: %@. - No comment provided by engineer. - Server version is incompatible with your app: %@. + La version du serveur est incompatible avec votre appli : %@. No comment provided by engineer. @@ -5683,10 +5824,12 @@ Activez-le dans les paramètres *Réseau et serveurs*. Servers info + Infos serveurs No comment provided by engineer. Servers statistics will be reset - this cannot be undone! + Les statistiques des serveurs seront réinitialisées - il n'est pas possible de revenir en arrière ! No comment provided by engineer. @@ -5706,6 +5849,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. Set default theme + Définir le thème par défaut No comment provided by engineer. @@ -5815,6 +5959,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. Show percentage + Afficher le pourcentage No comment provided by engineer. @@ -5913,6 +6058,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. Size + Taille No comment provided by engineer. @@ -5930,6 +6076,10 @@ Activez-le dans les paramètres *Réseau et serveurs*. Petits groupes (max 20) No comment provided by engineer. + + Soft + blur media + Some non-fatal errors occurred during import - you may see Chat console for more details. Des erreurs non fatales se sont produites lors de l'importation - vous pouvez consulter la console de chat pour plus de détails. @@ -5962,10 +6112,12 @@ Activez-le dans les paramètres *Réseau et serveurs*. Starting from %@. + À partir de %@. No comment provided by engineer. Statistics + Statistiques No comment provided by engineer. @@ -6028,6 +6180,10 @@ Activez-le dans les paramètres *Réseau et serveurs*. Arrêt du chat No comment provided by engineer. + + Strong + blur media + Submit Soumettre @@ -6035,14 +6191,17 @@ Activez-le dans les paramètres *Réseau et serveurs*. Subscribed + Inscrit No comment provided by engineer. Subscription errors + Erreurs d'inscription No comment provided by engineer. Subscriptions ignored + Inscriptions ignorées No comment provided by engineer. @@ -6127,6 +6286,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. Temporary file error + Erreur de fichier temporaire No comment provided by engineer. @@ -6268,6 +6428,7 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise. Themes + Thèmes No comment provided by engineer. @@ -6337,6 +6498,7 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise. This link was used with another mobile device, please create a new link on the desktop. + Ce lien a été utilisé avec un autre appareil mobile, veuillez créer un nouveau lien sur le bureau. No comment provided by engineer. @@ -6346,6 +6508,7 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise. Title + Titre No comment provided by engineer. @@ -6417,6 +6580,7 @@ Vous serez invité à confirmer l'authentification avant que cette fonction ne s Total + Total No comment provided by engineer. @@ -6426,6 +6590,7 @@ Vous serez invité à confirmer l'authentification avant que cette fonction ne s Transport sessions + Sessions de transport No comment provided by engineer. @@ -6622,6 +6787,7 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Upload errors + Erreurs de téléversement No comment provided by engineer. @@ -6636,10 +6802,12 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Uploaded + Téléversé No comment provided by engineer. Uploaded files + Fichiers téléversés No comment provided by engineer. @@ -6719,6 +6887,7 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien User selection + Sélection de l'utilisateur No comment provided by engineer. @@ -6858,10 +7027,12 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Wallpaper accent + Accentuation du papier-peint No comment provided by engineer. Wallpaper background + Fond d'écran No comment provided by engineer. @@ -6971,6 +7142,7 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Wrong key or unknown file chunk address - most likely file is deleted. + Mauvaise clé ou adresse inconnue du bloc de données du fichier - le fichier est probablement supprimé. file error text @@ -6980,6 +7152,7 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien XFTP server + Serveur XFTP No comment provided by engineer. @@ -7066,6 +7239,7 @@ Répéter la demande d'adhésion ? You are not connected to these servers. Private routing is used to deliver messages to them. + Vous n'êtes pas connecté à ces serveurs. Le routage privé est utilisé pour leur délivrer des messages. No comment provided by engineer. @@ -7476,6 +7650,7 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. attempts + tentatives No comment provided by engineer. @@ -7670,6 +7845,7 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. decryption errors + Erreurs de déchiffrement No comment provided by engineer. @@ -7724,6 +7900,7 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. duplicates + doublons No comment provided by engineer. @@ -7808,6 +7985,7 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. expired + expiré No comment provided by engineer. @@ -7842,6 +8020,7 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. inactive + inactif No comment provided by engineer. @@ -8023,10 +8202,12 @@ Les serveurs SimpleX ne peuvent pas voir votre profil. other + autre No comment provided by engineer. other errors + autres erreurs No comment provided by engineer. @@ -8399,4 +8580,154 @@ dernier message reçu : %2$@ + +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Dismiss Sheet + No comment provided by engineer. + + + Encrypted database + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keep Trying + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + No network connection + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Sending File + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Sharing is not supported when passphrase is not stored in KeyChain. + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff b/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff index 2dbd206209..b8a8086d17 100644 --- a/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff +++ b/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff @@ -329,7 +329,7 @@
**Add new contact**: to create your one-time QR Code or link for your contact. - **Új ismerős hozzáadása**: egyszer használatos QR-kód vagy hivatkozás létrehozása a kapcsolattartóhoz. + **Új ismerős hozzáadása**: egyszer használatos QR-kód vagy hivatkozás létrehozása az ismerőse számára. No comment provided by engineer. @@ -349,7 +349,7 @@ **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. - **Megjegyzés**: ha két eszközön is ugyanazt az adatbázist használja, akkor biztonsági védelemként megszakítja a kapcsolataiból érkező üzenetek visszafejtését. + **Megjegyzés**: ha két eszközön is ugyanazt az adatbázist használja, akkor biztonsági védelemként megszakítja az ismerőseitől érkező üzenetek visszafejtését. No comment provided by engineer. @@ -590,6 +590,7 @@ Active connections + Aktív kapcsolatok száma No comment provided by engineer. @@ -994,7 +995,7 @@ Auto-accept images - Fotók automatikus elfogadása + Képek automatikus elfogadása No comment provided by engineer. @@ -1072,6 +1073,10 @@ Letiltva az admin által No comment provided by engineer. + + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. Mindkét fél is hozzáadhat üzenetreakciókat. @@ -1375,6 +1380,7 @@ Configured %@ servers + Beállított %@ kiszolgálók No comment provided by engineer. @@ -1536,6 +1542,10 @@ Ez az egyszer használatos hivatkozása! Kapcsolódási hiba (AUTH) No comment provided by engineer. + + Connection notifications + No comment provided by engineer. + Connection request sent! Kapcsolódási kérés elküldve! @@ -1916,7 +1926,7 @@ Ez az egyszer használatos hivatkozása! Delete and notify contact - Törlés és ismerős értesítése + Törlés, és az ismerős értesítése No comment provided by engineer. @@ -1988,7 +1998,7 @@ Ez a művelet nem vonható vissza! Delete for me - Törlés nálam + Csak nálam No comment provided by engineer. @@ -2098,7 +2108,7 @@ Ez a művelet nem vonható vissza! Delivery receipts are disabled! - Kézbesítési igazolások kikapcsolva! + A kézbesítési jelentések ki vannak kapcsolva! No comment provided by engineer. @@ -2126,11 +2136,19 @@ Ez a művelet nem vonható vissza! Számítógépek No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + Destination server error: %@ Célkiszolgáló hiba: %@ snd error text + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + Detailed statistics Részletes statisztikák @@ -2158,12 +2176,12 @@ Ez a művelet nem vonható vissza! Device authentication is disabled. Turning off SimpleX Lock. - Eszközhitelesítés kikapcsolva. SimpleX zárolás kikapcsolása. + A készüléken nincs beállítva a képernyőzár. A SimpleX zár ki van kapcsolva. No comment provided by engineer. Device authentication is not enabled. You can turn on SimpleX Lock via Settings, once you enable device authentication. - Eszközhitelesítés nem engedélyezett.A SimpleX zárolás bekapcsolható a Beállításokon keresztül, miután az eszköz hitelesítés engedélyezésre került. + A készüléken nincs beállítva a képernyőzár. A SimpleX zár az „Adatvédelem és biztonság” menüben kapcsolható be, miután beállította a képernyőzárat az eszközén. No comment provided by engineer. @@ -2188,7 +2206,7 @@ Ez a művelet nem vonható vissza! Disable SimpleX Lock - SimpleX zárolás kikapcsolása + SimpleX zár kikapcsolása authentication reason @@ -2196,6 +2214,10 @@ Ez a művelet nem vonható vissza! Letiltás mindenki számára No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message Eltűnő üzenet @@ -2363,7 +2385,7 @@ Ez a művelet nem vonható vissza! Enable SimpleX Lock - SimpleX zárolás engedélyezése + SimpleX zár bekapcsolása authentication reason @@ -2421,6 +2443,10 @@ Ez a művelet nem vonható vissza! Önmegsemmisítő jelkód engedélyezése set passcode view + + Enabled + No comment provided by engineer. + Enabled for Engedélyezve @@ -2591,6 +2617,10 @@ Ez a művelet nem vonható vissza! Hiba a beállítás megváltoztatásakor No comment provided by engineer. + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address Hiba a cím létrehozásakor @@ -3097,6 +3127,18 @@ Ez a művelet nem vonható vissza! Továbbítva innen: No comment provided by engineer. + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + Forwarding server: %1$@ Destination server error: %2$@ @@ -3665,7 +3707,7 @@ Hiba: %2$@ It can happen when you or your connection used the old database backup. - Ez akkor fordulhat elő, ha ön vagy a kapcsolata régi adatbázis biztonsági mentést használt. + Ez akkor fordulhat elő, ha ön vagy az ismerőse régi adatbázis biztonsági mentést használt. No comment provided by engineer. @@ -3916,6 +3958,10 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Max. 30 másodperc, azonnal érkezett. No comment provided by engineer. + + Medium + blur media + Member Tag @@ -3998,6 +4044,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Message reception + Üzenetjelentés No comment provided by engineer. @@ -4366,8 +4413,8 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Off - Ki - No comment provided by engineer. + Kikapcsolva + blur media Ok @@ -4551,6 +4598,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Other %@ servers + További %@ kiszolgálók No comment provided by engineer. @@ -4722,10 +4770,6 @@ Hiba: %@ Tárolja el biztonságosan jelmondatát, mert ha elveszíti azt, NEM tudja megváltoztatni. No comment provided by engineer. - - Please try later. - No comment provided by engineer. - Polish interface Lengyel kezelőfelület @@ -4798,6 +4842,7 @@ Hiba: %@ Private routing error + Privát útválasztási hiba No comment provided by engineer. @@ -5739,6 +5784,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.< Server address is incompatible with network settings: %@. + A kiszolgáló címe nem kompatibilis a hálózati beállításokkal: %@. No comment provided by engineer. @@ -5766,12 +5812,9 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.< A kiszolgáló verziója nem kompatibilis a hálózati beállításokkal. srv error text - - Server version is incompatible with network settings: %@. - No comment provided by engineer. - Server version is incompatible with your app: %@. + A kiszolgáló verziója nem kompatibilis az alkalmazással: %@. No comment provided by engineer. @@ -5916,6 +5959,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.< Show percentage + Százalék megjelenítése No comment provided by engineer. @@ -5950,22 +5994,22 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.< SimpleX Lock - SimpleX zárolás + SimpleX zár No comment provided by engineer. SimpleX Lock mode - SimpleX zárolási mód + Zárolási mód No comment provided by engineer. SimpleX Lock not enabled! - SimpleX zárolás nincs engedélyezve! + A SimpleX zár nincs bekapcsolva! No comment provided by engineer. SimpleX Lock turned on - SimpleX zárolás bekapcsolva + SimpleX zár bekapcsolva No comment provided by engineer. @@ -6033,6 +6077,10 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.< Kis csoportok (max. 20 tag) No comment provided by engineer. + + Soft + blur media + Some non-fatal errors occurred during import - you may see Chat console for more details. Néhány nem végzetes hiba történt az importálás során – további részletekért a csevegési konzolban olvashat. @@ -6133,6 +6181,10 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.< Csevegés megállítása folyamatban No comment provided by engineer. + + Strong + blur media + Submit Elküldés @@ -6190,7 +6242,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.< Take picture - Fotó készítése + Kép készítése No comment provided by engineer. @@ -6392,7 +6444,7 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain. - Ez a művelet nem vonható vissza - az összes fogadott és küldött fájl a médiatartalommal együtt törlésre kerülnek. Az alacsony felbontású fotók viszont megmaradnak. + Ez a művelet nem vonható vissza - az összes fogadott és küldött fájl a médiatartalommal együtt törlésre kerülnek. Az alacsony felbontású képek viszont megmaradnak. No comment provided by engineer. @@ -6498,8 +6550,8 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. - Az adatavédelem érdekében kapcsolja be a SimpleX zárolás funkciót. -A funkció engedélyezése előtt a rendszer felszólítja a hitelesítés befejezésére. + A biztonsága érdekében kapcsolja be a SimpleX zár funkciót. +A funkció bekapcsolása előtt a rendszer felszólítja a képernyőzár beállítására az eszközén. No comment provided by engineer. @@ -6509,7 +6561,7 @@ A funkció engedélyezése előtt a rendszer felszólítja a hitelesítés befej To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page. - Rejtett profilja feltárásához írja be a teljes jelszót a keresőmezőbe a **Csevegési profiljai** oldalon. + Rejtett profilja megjelenítéséhez írja be a teljes jelszavát a keresőmezőbe a **Csevegési profilok** menüben. No comment provided by engineer. @@ -7218,7 +7270,7 @@ Csatlakozási kérés megismétlése? You can hide or mute a user profile - swipe it to the right. - Elrejthet vagy némíthat egy felhasználói profilt – csúsztasson jobbra. + Elrejtheti vagy lenémíthatja a felhasználó profiljait - csúsztassa jobbra a profilt. No comment provided by engineer. @@ -7258,7 +7310,7 @@ Csatlakozási kérés megismétlése? You can turn on SimpleX Lock via Settings. - A SimpleX zárolás a Beállításokon keresztül kapcsolható be. + A SimpleX zár az „Adatvédelem és biztonság” menüben kapcsolható be. No comment provided by engineer. @@ -7440,7 +7492,7 @@ Kapcsolódási kérés megismétlése? Your chat profiles - Csevegési profiljai + Csevegési profilok No comment provided by engineer. @@ -8146,7 +8198,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. on - be + bekapcsolva group pref value @@ -8211,7 +8263,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. removed profile picture - törölt profilkép + törölte a profilképét profile update event chat item @@ -8529,4 +8581,154 @@ utoljára fogadott üzenet: %2$@ + +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Dismiss Sheet + No comment provided by engineer. + + + Encrypted database + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keep Trying + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + No network connection + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Sending File + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Sharing is not supported when passphrase is not stored in KeyChain. + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/hu.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff index bb2241c336..dfdbabf84f 100644 --- a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff +++ b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff @@ -590,6 +590,7 @@
Active connections + Connessioni attive No comment provided by engineer. @@ -1072,6 +1073,10 @@ Bloccato dall'amministratore No comment provided by engineer. + + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. Sia tu che il tuo contatto potete aggiungere reazioni ai messaggi. @@ -1375,6 +1380,7 @@ Configured %@ servers + Configurati %@ server No comment provided by engineer. @@ -1536,6 +1542,10 @@ Questo è il tuo link una tantum! Errore di connessione (AUTH) No comment provided by engineer. + + Connection notifications + No comment provided by engineer. + Connection request sent! Richiesta di connessione inviata! @@ -2126,11 +2136,19 @@ Non è reversibile! Dispositivi desktop No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + Destination server error: %@ Errore del server di destinazione: %@ snd error text + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + Detailed statistics Statistiche dettagliate @@ -2196,6 +2214,10 @@ Non è reversibile! Disattiva per tutti No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message Messaggio a tempo @@ -2421,6 +2443,10 @@ Non è reversibile! Attiva il codice di autodistruzione set passcode view + + Enabled + No comment provided by engineer. + Enabled for Attivo per @@ -2591,6 +2617,10 @@ Non è reversibile! Errore nella modifica dell'impostazione No comment provided by engineer. + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address Errore nella creazione dell'indirizzo @@ -3097,6 +3127,18 @@ Non è reversibile! Inoltrato da No comment provided by engineer. + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + Forwarding server: %1$@ Destination server error: %2$@ @@ -3916,6 +3958,10 @@ Questo è il tuo link per il gruppo %@! Max 30 secondi, ricevuto istantaneamente. No comment provided by engineer. + + Medium + blur media + Member Membro @@ -3998,6 +4044,7 @@ Questo è il tuo link per il gruppo %@! Message reception + Ricezione messaggi No comment provided by engineer. @@ -4367,7 +4414,7 @@ Questo è il tuo link per il gruppo %@! Off Off - No comment provided by engineer. + blur media Ok @@ -4551,6 +4598,7 @@ Questo è il tuo link per il gruppo %@! Other %@ servers + Altri %@ server No comment provided by engineer. @@ -4722,10 +4770,6 @@ Errore: %@ Conserva la password in modo sicuro, NON potrai cambiarla se la perdi. No comment provided by engineer. - - Please try later. - No comment provided by engineer. - Polish interface Interfaccia polacca @@ -4798,6 +4842,7 @@ Errore: %@ Private routing error + Errore di instradamento privato No comment provided by engineer. @@ -5739,6 +5784,7 @@ Attivalo nelle impostazioni *Rete e server*. Server address is incompatible with network settings: %@. + L'indirizzo del server è incompatibile con le impostazioni di rete: %@. No comment provided by engineer. @@ -5766,12 +5812,9 @@ Attivalo nelle impostazioni *Rete e server*. La versione del server non è compatibile con le impostazioni di rete. srv error text - - Server version is incompatible with network settings: %@. - No comment provided by engineer. - Server version is incompatible with your app: %@. + La versione del server è incompatibile con la tua app: %@. No comment provided by engineer. @@ -5916,6 +5959,7 @@ Attivalo nelle impostazioni *Rete e server*. Show percentage + Mostra percentuale No comment provided by engineer. @@ -6033,6 +6077,10 @@ Attivalo nelle impostazioni *Rete e server*. Piccoli gruppi (max 20) No comment provided by engineer. + + Soft + blur media + Some non-fatal errors occurred during import - you may see Chat console for more details. Si sono verificati alcuni errori non gravi durante l'importazione: vedi la console della chat per i dettagli. @@ -6133,6 +6181,10 @@ Attivalo nelle impostazioni *Rete e server*. Arresto della chat No comment provided by engineer. + + Strong + blur media + Submit Invia @@ -8529,4 +8581,154 @@ ultimo msg ricevuto: %2$@ + +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Dismiss Sheet + No comment provided by engineer. + + + Encrypted database + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keep Trying + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + No network connection + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Sending File + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Sharing is not supported when passphrase is not stored in KeyChain. + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/it.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff b/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff index 2f94e9c141..f9127d93c6 100644 --- a/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff +++ b/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff @@ -1036,6 +1036,10 @@ Blocked by admin No comment provided by engineer.
+ + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. 自分も相手もメッセージへのリアクションを追加できます。 @@ -1465,6 +1469,10 @@ This is your own one-time link! 接続エラー (AUTH) No comment provided by engineer. + + Connection notifications + No comment provided by engineer. + Connection request sent! 接続リクエストを送信しました! @@ -2028,10 +2036,18 @@ This cannot be undone! Desktop devices No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + Destination server error: %@ snd error text + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + Detailed statistics No comment provided by engineer. @@ -2095,6 +2111,10 @@ This cannot be undone! すべて無効 No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message 消えるメッセージ @@ -2306,6 +2326,10 @@ This cannot be undone! 自己破壊パスコードを有効にする set passcode view + + Enabled + No comment provided by engineer. + Enabled for No comment provided by engineer. @@ -2468,6 +2492,10 @@ This cannot be undone! 設定変更にエラー発生 No comment provided by engineer. + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address アドレス作成にエラー発生 @@ -2943,6 +2971,18 @@ This cannot be undone! Forwarded from No comment provided by engineer. + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + Forwarding server: %1$@ Destination server error: %2$@ @@ -3723,6 +3763,10 @@ This is your link for group %@! 最大 30 秒で即時受信します。 No comment provided by engineer. + + Medium + blur media + Member メンバー @@ -4139,7 +4183,7 @@ This is your link for group %@! Off オフ - No comment provided by engineer. + blur media Ok @@ -4474,10 +4518,6 @@ Error: %@ パスフレーズを失くさないように保管してください。失くすと変更できなくなります。 No comment provided by engineer. - - Please try later. - No comment provided by engineer. - Polish interface ポーランド語UI @@ -5442,10 +5482,6 @@ Enable in *Network & servers* settings. Server version is incompatible with network settings. srv error text - - Server version is incompatible with network settings: %@. - No comment provided by engineer. - Server version is incompatible with your app: %@. No comment provided by engineer. @@ -5695,6 +5731,10 @@ Enable in *Network & servers* settings. 小グループ(最大20名) No comment provided by engineer. + + Soft + blur media + Some non-fatal errors occurred during import - you may see Chat console for more details. インポート中に致命的でないエラーが発生しました - 詳細はチャットコンソールを参照してください。 @@ -5789,6 +5829,10 @@ Enable in *Network & servers* settings. Stopping chat No comment provided by engineer. + + Strong + blur media + Submit 送信 @@ -8053,4 +8097,154 @@ last received msg: %2$@ + +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Dismiss Sheet + No comment provided by engineer. + + + Encrypted database + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keep Trying + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + No network connection + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Sending File + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Sharing is not supported when passphrase is not stored in KeyChain. + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/ja.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff b/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff index 1f7adaeca4..c885e32c11 100644 --- a/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff +++ b/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff @@ -554,6 +554,7 @@
Accent + Accent No comment provided by engineer. @@ -579,14 +580,17 @@ Acknowledged + Erkend No comment provided by engineer. Acknowledgement errors + Bevestigingsfouten No comment provided by engineer. Active connections + Actieve verbindingen No comment provided by engineer. @@ -626,19 +630,22 @@ Add welcome message - Welkomst bericht toevoegen + Welkom bericht toevoegen No comment provided by engineer. Additional accent + Extra accent No comment provided by engineer. Additional accent 2 + Extra accent 2 No comment provided by engineer. Additional secondary + Extra secundair No comment provided by engineer. @@ -668,6 +675,7 @@ Advanced settings + Geavanceerde instellingen No comment provided by engineer. @@ -687,6 +695,7 @@ All data is private to your device. + Alle gegevens zijn privé op uw apparaat. No comment provided by engineer. @@ -711,6 +720,7 @@ All profiles + Alle profielen No comment provided by engineer. @@ -755,12 +765,12 @@ Allow message reactions only if your contact allows them. - Sta berichtreacties alleen toe als uw contact dit toestaat. + Sta bericht reacties alleen toe als uw contact dit toestaat. No comment provided by engineer. Allow message reactions. - Sta berichtreacties toe. + Sta bericht reacties toe. No comment provided by engineer. @@ -805,7 +815,7 @@ Allow your contacts adding message reactions. - Sta uw contactpersonen toe om berichtreacties toe te voegen. + Sta uw contactpersonen toe om bericht reacties toe te voegen. No comment provided by engineer. @@ -915,6 +925,7 @@ Apply to + Toepassen op No comment provided by engineer. @@ -994,6 +1005,7 @@ Background + Achtergrond No comment provided by engineer. @@ -1023,6 +1035,7 @@ Black + Zwart No comment provided by engineer. @@ -1060,9 +1073,13 @@ Geblokkeerd door beheerder No comment provided by engineer. + + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. - Zowel u als uw contact kunnen berichtreacties toevoegen. + Zowel u als uw contact kunnen bericht reacties toevoegen. No comment provided by engineer. @@ -1137,6 +1154,7 @@ Cannot forward message + Kan bericht niet doorsturen No comment provided by engineer. @@ -1212,6 +1230,7 @@ Chat colors + Chat kleuren No comment provided by engineer. @@ -1261,6 +1280,7 @@ Chat theme + Chat thema No comment provided by engineer. @@ -1295,14 +1315,17 @@ Chunks deleted + Stukken verwijderd No comment provided by engineer. Chunks downloaded + Stukken gedownload No comment provided by engineer. Chunks uploaded + Stukken geüpload No comment provided by engineer. @@ -1332,6 +1355,7 @@ Color mode + Kleur mode No comment provided by engineer. @@ -1346,6 +1370,7 @@ Completed + voltooid No comment provided by engineer. @@ -1355,6 +1380,7 @@ Configured %@ servers + %@ servers geconfigureerd No comment provided by engineer. @@ -1463,6 +1489,7 @@ Dit is uw eigen eenmalige link! Connected + Verbonden No comment provided by engineer. @@ -1472,6 +1499,7 @@ Dit is uw eigen eenmalige link! Connected servers + Verbonden servers No comment provided by engineer. @@ -1481,6 +1509,7 @@ Dit is uw eigen eenmalige link! Connecting + Verbinden No comment provided by engineer. @@ -1513,6 +1542,10 @@ Dit is uw eigen eenmalige link! Verbindingsfout (AUTH) No comment provided by engineer. + + Connection notifications + No comment provided by engineer. + Connection request sent! Verbindingsverzoek verzonden! @@ -1530,10 +1563,12 @@ Dit is uw eigen eenmalige link! Connection with desktop stopped + Verbinding met desktop is gestopt No comment provided by engineer. Connections + Verbindingen No comment provided by engineer. @@ -1593,6 +1628,7 @@ Dit is uw eigen eenmalige link! Copy error + Kopieerfout No comment provided by engineer. @@ -1672,6 +1708,7 @@ Dit is uw eigen eenmalige link! Created + Gemaakt No comment provided by engineer. @@ -1711,6 +1748,7 @@ Dit is uw eigen eenmalige link! Current profile + Huidig profiel No comment provided by engineer. @@ -1725,6 +1763,7 @@ Dit is uw eigen eenmalige link! Customize theme + Thema aanpassen No comment provided by engineer. @@ -1734,6 +1773,7 @@ Dit is uw eigen eenmalige link! Dark mode colors + Kleuren in donkere modus No comment provided by engineer. @@ -2043,6 +2083,7 @@ Dit kan niet ongedaan gemaakt worden! Deleted + Verwijderd No comment provided by engineer. @@ -2057,6 +2098,7 @@ Dit kan niet ongedaan gemaakt worden! Deletion errors + Verwijderingsfouten No comment provided by engineer. @@ -2094,17 +2136,27 @@ Dit kan niet ongedaan gemaakt worden! Desktop apparaten No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + Destination server error: %@ Bestemmingsserverfout: %@ snd error text + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + Detailed statistics + Gedetailleerde statistieken No comment provided by engineer. Details + Details No comment provided by engineer. @@ -2162,6 +2214,10 @@ Dit kan niet ongedaan gemaakt worden! Uitschakelen voor iedereen No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message Verdwijnend bericht @@ -2264,6 +2320,7 @@ Dit kan niet ongedaan gemaakt worden! Download errors + Downloadfouten No comment provided by engineer. @@ -2278,10 +2335,12 @@ Dit kan niet ongedaan gemaakt worden! Downloaded + Gedownload No comment provided by engineer. Downloaded files + Gedownloade bestanden No comment provided by engineer. @@ -2384,6 +2443,10 @@ Dit kan niet ongedaan gemaakt worden! Zelfvernietigings wachtwoord inschakelen set passcode view + + Enabled + No comment provided by engineer. + Enabled for Ingeschakeld voor @@ -2501,12 +2564,12 @@ Dit kan niet ongedaan gemaakt worden! Enter welcome message… - Welkomst bericht invoeren… + Welkom bericht invoeren… placeholder Enter welcome message… (optional) - Voer welkomst bericht in... (optioneel) + Voer welkom bericht in... (optioneel) placeholder @@ -2554,6 +2617,10 @@ Dit kan niet ongedaan gemaakt worden! Fout bij wijzigen van instelling No comment provided by engineer. + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address Fout bij aanmaken van adres @@ -2656,6 +2723,7 @@ Dit kan niet ongedaan gemaakt worden! Error exporting theme: %@ + Fout bij exporteren van thema: %@ No comment provided by engineer. @@ -2685,10 +2753,12 @@ Dit kan niet ongedaan gemaakt worden! Error reconnecting server + Fout bij opnieuw verbinding maken met de server No comment provided by engineer. Error reconnecting servers + Fout bij opnieuw verbinden van servers No comment provided by engineer. @@ -2698,6 +2768,7 @@ Dit kan niet ongedaan gemaakt worden! Error resetting statistics + Fout bij het resetten van statistieken No comment provided by engineer. @@ -2833,6 +2904,7 @@ Dit kan niet ongedaan gemaakt worden! Errors + Fouten No comment provided by engineer. @@ -2862,6 +2934,7 @@ Dit kan niet ongedaan gemaakt worden! Export theme + Exporteer thema No comment provided by engineer. @@ -2901,22 +2974,27 @@ Dit kan niet ongedaan gemaakt worden! File error + Bestandsfout No comment provided by engineer. File not found - most likely file was deleted or cancelled. + Bestand niet gevonden - hoogstwaarschijnlijk is het bestand verwijderd of geannuleerd. file error text File server error: %@ + Bestandsserverfout: %@ file error text File status + Bestandsstatus No comment provided by engineer. File status: %@ + Bestandsstatus: %@ copied message info @@ -3049,6 +3127,18 @@ Dit kan niet ongedaan gemaakt worden! Doorgestuurd vanuit No comment provided by engineer. + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + Forwarding server: %1$@ Destination server error: %2$@ @@ -3110,10 +3200,12 @@ Fout: %2$@ Good afternoon! + Goedemiddag! message preview Good morning! + Goedemorgen! message preview @@ -3173,7 +3265,7 @@ Fout: %2$@ Group members can add message reactions. - Groepsleden kunnen berichtreacties toevoegen. + Groepsleden kunnen bericht reacties toevoegen. No comment provided by engineer. @@ -3233,7 +3325,7 @@ Fout: %2$@ Group welcome message - Groep welkomst bericht + Groep welkom bericht No comment provided by engineer. @@ -3398,6 +3490,7 @@ Fout: %2$@ Import theme + Thema importeren No comment provided by engineer. @@ -3524,6 +3617,7 @@ Fout: %2$@ Interface colors + Interface kleuren No comment provided by engineer. @@ -3864,6 +3958,10 @@ Dit is jouw link voor groep %@! Max 30 seconden, direct ontvangen. No comment provided by engineer. + + Medium + blur media + Member Lid @@ -3871,6 +3969,7 @@ Dit is jouw link voor groep %@! Member inactive + Lid inactief item status text @@ -3890,6 +3989,7 @@ Dit is jouw link voor groep %@! Menus + Menu's No comment provided by engineer. @@ -3914,10 +4014,12 @@ Dit is jouw link voor groep %@! Message forwarded + Bericht doorgestuurd item status text Message may be delivered later if member becomes active. + Het bericht kan later worden bezorgd als het lid actief wordt. item status description @@ -3942,6 +4044,7 @@ Dit is jouw link voor groep %@! Message reception + Bericht ontvangst No comment provided by engineer. @@ -3961,10 +4064,12 @@ Dit is jouw link voor groep %@! Message status + Berichtstatus No comment provided by engineer. Message status: %@ + Berichtstatus: %@ copied message info @@ -3994,10 +4099,12 @@ Dit is jouw link voor groep %@! Messages received + Berichten ontvangen No comment provided by engineer. Messages sent + Berichten verzonden No comment provided by engineer. @@ -4237,6 +4344,7 @@ Dit is jouw link voor groep %@! No direct connection yet, message is forwarded by admin. + Nog geen directe verbinding, bericht wordt doorgestuurd door beheerder. item status description @@ -4256,6 +4364,7 @@ Dit is jouw link voor groep %@! No info, try to reload + Geen info, probeer opnieuw te laden No comment provided by engineer. @@ -4305,7 +4414,7 @@ Dit is jouw link voor groep %@! Off Uit - No comment provided by engineer. + blur media Ok @@ -4364,7 +4473,7 @@ Dit is jouw link voor groep %@! Only you can add message reactions. - Alleen jij kunt berichtreacties toevoegen. + Alleen jij kunt bericht reacties toevoegen. No comment provided by engineer. @@ -4389,7 +4498,7 @@ Dit is jouw link voor groep %@! Only your contact can add message reactions. - Alleen uw contact kan berichtreacties toevoegen. + Alleen uw contact kan bericht reacties toevoegen. No comment provided by engineer. @@ -4444,6 +4553,7 @@ Dit is jouw link voor groep %@! Open server settings + Server instellingen openen No comment provided by engineer. @@ -4488,6 +4598,7 @@ Dit is jouw link voor groep %@! Other %@ servers + Andere %@ servers No comment provided by engineer. @@ -4557,6 +4668,7 @@ Dit is jouw link voor groep %@! Pending + in behandeling No comment provided by engineer. @@ -4587,6 +4699,8 @@ Dit is jouw link voor groep %@! Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. Please share any other issues with the developers. + Controleer of mobiel en desktop met hetzelfde lokale netwerk zijn verbonden en of de desktopfirewall de verbinding toestaat. +Deel eventuele andere problemen met de ontwikkelaars. No comment provided by engineer. @@ -4656,10 +4770,6 @@ Fout: %@ Bewaar het wachtwoord veilig, u kunt deze NIET wijzigen als u het kwijtraakt. No comment provided by engineer. - - Please try later. - No comment provided by engineer. - Polish interface Poolse interface @@ -4692,6 +4802,7 @@ Fout: %@ Previously connected servers + Eerder verbonden servers No comment provided by engineer. @@ -4731,6 +4842,7 @@ Fout: %@ Private routing error + Fout in privéroutering No comment provided by engineer. @@ -4765,6 +4877,7 @@ Fout: %@ Profile theme + Profiel thema No comment provided by engineer. @@ -4784,7 +4897,7 @@ Fout: %@ Prohibit message reactions. - Berichtreacties verbieden. + Bericht reacties verbieden. No comment provided by engineer. @@ -4851,10 +4964,12 @@ Schakel dit in in *Netwerk en servers*-instellingen. Proxied + Proxied No comment provided by engineer. Proxied servers + Proxied servers No comment provided by engineer. @@ -4924,6 +5039,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Receive errors + Fouten ontvangen No comment provided by engineer. @@ -4948,14 +5064,17 @@ Schakel dit in in *Netwerk en servers*-instellingen. Received messages + Ontvangen berichten No comment provided by engineer. Received reply + Antwoord ontvangen No comment provided by engineer. Received total + Totaal ontvangen No comment provided by engineer. @@ -4990,6 +5109,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Reconnect + opnieuw verbinden No comment provided by engineer. @@ -4999,18 +5119,22 @@ Schakel dit in in *Netwerk en servers*-instellingen. Reconnect all servers + Maak opnieuw verbinding met alle servers No comment provided by engineer. Reconnect all servers? + Alle servers opnieuw verbinden? No comment provided by engineer. Reconnect server to force message delivery. It uses additional traffic. + Maak opnieuw verbinding met de server om de bezorging van berichten te forceren. Er wordt gebruik gemaakt van extra verkeer.Maak opnieuw verbinding met de server om de bezorging van berichten te forceren. Er wordt gebruik gemaakt van extra data. No comment provided by engineer. Reconnect server? + Server opnieuw verbinden? No comment provided by engineer. @@ -5065,6 +5189,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Remove image + Verwijder afbeelding No comment provided by engineer. @@ -5139,10 +5264,12 @@ Schakel dit in in *Netwerk en servers*-instellingen. Reset all statistics + Reset alle statistieken No comment provided by engineer. Reset all statistics? + Alle statistieken resetten? No comment provided by engineer. @@ -5152,6 +5279,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Reset to app theme + Terugzetten naar app thema No comment provided by engineer. @@ -5161,6 +5289,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Reset to user theme + Terugzetten naar gebruikersthema No comment provided by engineer. @@ -5235,6 +5364,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. SMP server + SMP server No comment provided by engineer. @@ -5329,7 +5459,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Save welcome message? - Welkomst bericht opslaan? + Welkom bericht opslaan? No comment provided by engineer. @@ -5354,10 +5484,12 @@ Schakel dit in in *Netwerk en servers*-instellingen. Scale + Schaal No comment provided by engineer. Scan / Paste link + Link scannen/plakken No comment provided by engineer. @@ -5402,6 +5534,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Secondary + Secundair No comment provided by engineer. @@ -5411,6 +5544,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Secured + Beveiligd No comment provided by engineer. @@ -5430,6 +5564,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Selected chat preferences prohibit this message. + Geselecteerde chat voorkeuren verbieden dit bericht. No comment provided by engineer. @@ -5484,6 +5619,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Send errors + Verzend fouten No comment provided by engineer. @@ -5598,6 +5734,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Sent directly + Direct verzonden No comment provided by engineer. @@ -5612,6 +5749,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Sent messages + Verzonden berichten No comment provided by engineer. @@ -5621,18 +5759,22 @@ Schakel dit in in *Netwerk en servers*-instellingen. Sent reply + Antwoord verzonden No comment provided by engineer. Sent total + Totaal verzonden No comment provided by engineer. Sent via proxy + Verzonden via proxy No comment provided by engineer. Server address + Server adres No comment provided by engineer. @@ -5642,6 +5784,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Server address is incompatible with network settings: %@. + Serveradres is incompatibel met netwerkinstellingen: %@. No comment provided by engineer. @@ -5661,6 +5804,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Server type + Server type No comment provided by engineer. @@ -5668,12 +5812,9 @@ Schakel dit in in *Netwerk en servers*-instellingen. Serverversie is incompatibel met netwerkinstellingen. srv error text - - Server version is incompatible with network settings: %@. - No comment provided by engineer. - Server version is incompatible with your app: %@. + Serverversie is incompatibel met uw app: %@. No comment provided by engineer. @@ -5683,10 +5824,12 @@ Schakel dit in in *Netwerk en servers*-instellingen. Servers info + Server informatie No comment provided by engineer. Servers statistics will be reset - this cannot be undone! + Serverstatistieken worden gereset - dit kan niet ongedaan worden gemaakt! No comment provided by engineer. @@ -5706,6 +5849,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Set default theme + Stel het standaard thema in No comment provided by engineer. @@ -5815,6 +5959,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Show percentage + Percentage weergeven No comment provided by engineer. @@ -5834,6 +5979,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. SimpleX + SimpleX No comment provided by engineer. @@ -5913,6 +6059,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Size + Maat No comment provided by engineer. @@ -5930,6 +6077,10 @@ Schakel dit in in *Netwerk en servers*-instellingen. Kleine groepen (max 20) No comment provided by engineer. + + Soft + blur media + Some non-fatal errors occurred during import - you may see Chat console for more details. Er zijn enkele niet-fatale fouten opgetreden tijdens het importeren - u kunt de Chat console raadplegen voor meer details. @@ -5962,10 +6113,12 @@ Schakel dit in in *Netwerk en servers*-instellingen. Starting from %@. + Beginnend vanaf %@. No comment provided by engineer. Statistics + Statistieken No comment provided by engineer. @@ -6028,6 +6181,10 @@ Schakel dit in in *Netwerk en servers*-instellingen. Chat stoppen No comment provided by engineer. + + Strong + blur media + Submit Indienen @@ -6035,14 +6192,17 @@ Schakel dit in in *Netwerk en servers*-instellingen. Subscribed + Ingeschreven No comment provided by engineer. Subscription errors + Inschrijving fouten No comment provided by engineer. Subscriptions ignored + Inschrijvingen genegeerd No comment provided by engineer. @@ -6127,6 +6287,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Temporary file error + Tijdelijke bestandsfout No comment provided by engineer. @@ -6268,6 +6429,7 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast. Themes + Thema's No comment provided by engineer. @@ -6337,6 +6499,7 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast. This link was used with another mobile device, please create a new link on the desktop. + Deze link is gebruikt met een ander mobiel apparaat. Maak een nieuwe link op de desktop. No comment provided by engineer. @@ -6346,6 +6509,7 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast. Title + Titel No comment provided by engineer. @@ -6417,6 +6581,7 @@ U wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingesc Total + Totaal No comment provided by engineer. @@ -6426,6 +6591,7 @@ U wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingesc Transport sessions + Transportsessies No comment provided by engineer. @@ -6622,6 +6788,7 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Upload errors + Upload fouten No comment provided by engineer. @@ -6636,10 +6803,12 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Uploaded + Geüpload No comment provided by engineer. Uploaded files + Geüploade bestanden No comment provided by engineer. @@ -6719,6 +6888,7 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak User selection + Gebruikersselectie No comment provided by engineer. @@ -6858,10 +7028,12 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Wallpaper accent + Achtergrond accent No comment provided by engineer. Wallpaper background + Wallpaper achtergrond No comment provided by engineer. @@ -6886,12 +7058,12 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Welcome message - Welkomst bericht + Welkom bericht No comment provided by engineer. Welcome message is too long - Welkomstbericht is te lang + Welkom bericht is te lang No comment provided by engineer. @@ -6941,7 +7113,7 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak With optional welcome message. - Met optioneel welkomst bericht. + Met optioneel welkom bericht. No comment provided by engineer. @@ -6971,6 +7143,7 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Wrong key or unknown file chunk address - most likely file is deleted. + Verkeerde sleutel of onbekend bestanddeeladres - hoogstwaarschijnlijk is het bestand verwijderd. file error text @@ -6980,6 +7153,7 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak XFTP server + XFTP server No comment provided by engineer. @@ -7066,6 +7240,7 @@ Deelnameverzoek herhalen? You are not connected to these servers. Private routing is used to deliver messages to them. + U bent niet verbonden met deze servers. Privéroutering wordt gebruikt om berichten bij hen af te leveren. No comment provided by engineer. @@ -7476,6 +7651,7 @@ SimpleX servers kunnen uw profiel niet zien. attempts + pogingen No comment provided by engineer. @@ -7670,6 +7846,7 @@ SimpleX servers kunnen uw profiel niet zien. decryption errors + decoderingsfouten No comment provided by engineer. @@ -7724,6 +7901,7 @@ SimpleX servers kunnen uw profiel niet zien. duplicates + duplicaten No comment provided by engineer. @@ -7808,6 +7986,7 @@ SimpleX servers kunnen uw profiel niet zien. expired + verlopen No comment provided by engineer. @@ -7842,6 +8021,7 @@ SimpleX servers kunnen uw profiel niet zien. inactive + inactief No comment provided by engineer. @@ -8023,10 +8203,12 @@ SimpleX servers kunnen uw profiel niet zien. other + overig No comment provided by engineer. other errors + overige fouten No comment provided by engineer. @@ -8399,4 +8581,154 @@ laatst ontvangen bericht: %2$@ + +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Dismiss Sheet + No comment provided by engineer. + + + Encrypted database + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keep Trying + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + No network connection + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Sending File + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Sharing is not supported when passphrase is not stored in KeyChain. + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/nl.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff index 731095dea8..040eef3d3d 100644 --- a/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff +++ b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff @@ -554,6 +554,7 @@
Accent + Akcent No comment provided by engineer. @@ -579,14 +580,17 @@ Acknowledged + Potwierdzono No comment provided by engineer. Acknowledgement errors + Błędy potwierdzenia No comment provided by engineer. Active connections + Aktywne połączenia No comment provided by engineer. @@ -631,14 +635,17 @@ Additional accent + Dodatkowy akcent No comment provided by engineer. Additional accent 2 + Dodatkowy akcent 2 No comment provided by engineer. Additional secondary + Dodatkowy drugorzędny No comment provided by engineer. @@ -668,6 +675,7 @@ Advanced settings + Zaawansowane ustawienia No comment provided by engineer. @@ -687,6 +695,7 @@ All data is private to your device. + Wszystkie dane są prywatne na Twoim urządzeniu. No comment provided by engineer. @@ -711,6 +720,7 @@ All profiles + Wszystkie profile No comment provided by engineer. @@ -915,6 +925,7 @@ Apply to + Zastosuj dla No comment provided by engineer. @@ -994,6 +1005,7 @@ Background + Tło No comment provided by engineer. @@ -1023,6 +1035,7 @@ Black + Czarny No comment provided by engineer. @@ -1060,6 +1073,10 @@ Zablokowany przez admina No comment provided by engineer. + + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. Zarówno Ty, jak i Twój kontakt możecie dodawać reakcje wiadomości. @@ -1137,6 +1154,7 @@ Cannot forward message + Nie można przekazać wiadomości No comment provided by engineer. @@ -1212,6 +1230,7 @@ Chat colors + Kolory czatu No comment provided by engineer. @@ -1261,6 +1280,7 @@ Chat theme + Motyw czatu No comment provided by engineer. @@ -1295,14 +1315,17 @@ Chunks deleted + Fragmenty usunięte No comment provided by engineer. Chunks downloaded + Fragmenty pobrane No comment provided by engineer. Chunks uploaded + Fragmenty przesłane No comment provided by engineer. @@ -1332,6 +1355,7 @@ Color mode + Tryb koloru No comment provided by engineer. @@ -1346,6 +1370,7 @@ Completed + Zakończono No comment provided by engineer. @@ -1355,6 +1380,7 @@ Configured %@ servers + Skonfigurowano %@ serwerów No comment provided by engineer. @@ -1463,6 +1489,7 @@ To jest twój jednorazowy link! Connected + Połączony No comment provided by engineer. @@ -1472,6 +1499,7 @@ To jest twój jednorazowy link! Connected servers + Połączone serwery No comment provided by engineer. @@ -1481,6 +1509,7 @@ To jest twój jednorazowy link! Connecting + Łączenie No comment provided by engineer. @@ -1513,6 +1542,10 @@ To jest twój jednorazowy link! Błąd połączenia (UWIERZYTELNIANIE) No comment provided by engineer. + + Connection notifications + No comment provided by engineer. + Connection request sent! Prośba o połączenie wysłana! @@ -1530,10 +1563,12 @@ To jest twój jednorazowy link! Connection with desktop stopped + Połączenie z komputerem zakończone No comment provided by engineer. Connections + Połączenia No comment provided by engineer. @@ -1593,6 +1628,7 @@ To jest twój jednorazowy link! Copy error + Kopiuj błąd No comment provided by engineer. @@ -1672,6 +1708,7 @@ To jest twój jednorazowy link! Created + Utworzono No comment provided by engineer. @@ -1711,6 +1748,7 @@ To jest twój jednorazowy link! Current profile + Bieżący profil No comment provided by engineer. @@ -1725,6 +1763,7 @@ To jest twój jednorazowy link! Customize theme + Dostosuj motyw No comment provided by engineer. @@ -1734,6 +1773,7 @@ To jest twój jednorazowy link! Dark mode colors + Kolory ciemnego trybu No comment provided by engineer. @@ -2043,6 +2083,7 @@ To nie może być cofnięte! Deleted + Usunięto No comment provided by engineer. @@ -2057,6 +2098,7 @@ To nie może być cofnięte! Deletion errors + Błędy usuwania No comment provided by engineer. @@ -2094,17 +2136,27 @@ To nie może być cofnięte! Urządzenia komputerowe No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + Destination server error: %@ Błąd docelowego serwera: %@ snd error text + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + Detailed statistics + Szczegółowe statystyki No comment provided by engineer. Details + Szczegóły No comment provided by engineer. @@ -2162,6 +2214,10 @@ To nie może być cofnięte! Wyłącz dla wszystkich No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message Znikająca wiadomość @@ -2264,6 +2320,7 @@ To nie może być cofnięte! Download errors + Błędy pobierania No comment provided by engineer. @@ -2278,10 +2335,12 @@ To nie może być cofnięte! Downloaded + Pobrane No comment provided by engineer. Downloaded files + Pobrane pliki No comment provided by engineer. @@ -2384,6 +2443,10 @@ To nie może być cofnięte! Włącz pin samodestrukcji set passcode view + + Enabled + No comment provided by engineer. + Enabled for Włączony dla @@ -2554,6 +2617,10 @@ To nie może być cofnięte! Błąd zmiany ustawienia No comment provided by engineer. + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address Błąd tworzenia adresu @@ -2656,6 +2723,7 @@ To nie może być cofnięte! Error exporting theme: %@ + Błąd eksportowania motywu: %@ No comment provided by engineer. @@ -2685,10 +2753,12 @@ To nie może być cofnięte! Error reconnecting server + Błąd ponownego łączenia z serwerem No comment provided by engineer. Error reconnecting servers + Błąd ponownego łączenia serwerów No comment provided by engineer. @@ -2698,6 +2768,7 @@ To nie może być cofnięte! Error resetting statistics + Błąd resetowania statystyk No comment provided by engineer. @@ -2833,6 +2904,7 @@ To nie może być cofnięte! Errors + Błędy No comment provided by engineer. @@ -2862,6 +2934,7 @@ To nie może być cofnięte! Export theme + Eksportuj motyw No comment provided by engineer. @@ -2901,22 +2974,27 @@ To nie może być cofnięte! File error + Błąd pliku No comment provided by engineer. File not found - most likely file was deleted or cancelled. + Nie odnaleziono pliku - najprawdopodobniej plik został usunięty lub anulowany. file error text File server error: %@ + Błąd serwera plików: %@ file error text File status + Status pliku No comment provided by engineer. File status: %@ + Status pliku: %@ copied message info @@ -3049,6 +3127,18 @@ To nie może być cofnięte! Przekazane dalej od No comment provided by engineer. + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + Forwarding server: %1$@ Destination server error: %2$@ @@ -3110,10 +3200,12 @@ Błąd: %2$@ Good afternoon! + Dzień dobry! message preview Good morning! + Dzień dobry! message preview @@ -3398,6 +3490,7 @@ Błąd: %2$@ Import theme + Importuj motyw No comment provided by engineer. @@ -3524,6 +3617,7 @@ Błąd: %2$@ Interface colors + Kolory interfejsu No comment provided by engineer. @@ -3864,6 +3958,10 @@ To jest twój link do grupy %@! Maksymalnie 30 sekund, odbierane natychmiast. No comment provided by engineer. + + Medium + blur media + Member Członek @@ -3871,6 +3969,7 @@ To jest twój link do grupy %@! Member inactive + Członek nieaktywny item status text @@ -3890,6 +3989,7 @@ To jest twój link do grupy %@! Menus + Menu No comment provided by engineer. @@ -3914,10 +4014,12 @@ To jest twój link do grupy %@! Message forwarded + Wiadomość przekazana item status text Message may be delivered later if member becomes active. + Wiadomość może zostać dostarczona później jeśli członek stanie się aktywny. item status description @@ -3942,6 +4044,7 @@ To jest twój link do grupy %@! Message reception + Odebranie wiadomości No comment provided by engineer. @@ -3961,10 +4064,12 @@ To jest twój link do grupy %@! Message status + Status wiadomości No comment provided by engineer. Message status: %@ + Status wiadomości: %@ copied message info @@ -3994,10 +4099,12 @@ To jest twój link do grupy %@! Messages received + Otrzymane wiadomości No comment provided by engineer. Messages sent + Wysłane wiadomości No comment provided by engineer. @@ -4237,6 +4344,7 @@ To jest twój link do grupy %@! No direct connection yet, message is forwarded by admin. + Brak bezpośredniego połączenia, wiadomość została przekazana przez administratora. item status description @@ -4256,6 +4364,7 @@ To jest twój link do grupy %@! No info, try to reload + Brak informacji, spróbuj przeładować No comment provided by engineer. @@ -4305,7 +4414,7 @@ To jest twój link do grupy %@! Off Wyłączony - No comment provided by engineer. + blur media Ok @@ -4444,6 +4553,7 @@ To jest twój link do grupy %@! Open server settings + Otwórz ustawienia serwera No comment provided by engineer. @@ -4488,6 +4598,7 @@ To jest twój link do grupy %@! Other %@ servers + Inne %@ serwery No comment provided by engineer. @@ -4557,6 +4668,7 @@ To jest twój link do grupy %@! Pending + Oczekujące No comment provided by engineer. @@ -4587,6 +4699,8 @@ To jest twój link do grupy %@! Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection. Please share any other issues with the developers. + Sprawdź, czy telefon i komputer są podłączone do tej samej sieci lokalnej i czy zapora sieciowa komputera umożliwia połączenie. +Proszę podzielić się innymi problemami z deweloperami. No comment provided by engineer. @@ -4656,10 +4770,6 @@ Błąd: %@ Przechowuj kod dostępu w bezpieczny sposób, w przypadku jego utraty NIE będzie można go zmienić. No comment provided by engineer. - - Please try later. - No comment provided by engineer. - Polish interface Polski interfejs @@ -4692,6 +4802,7 @@ Błąd: %@ Previously connected servers + Wcześniej połączone serwery No comment provided by engineer. @@ -4731,6 +4842,7 @@ Błąd: %@ Private routing error + Błąd prywatnego trasowania No comment provided by engineer. @@ -4765,6 +4877,7 @@ Błąd: %@ Profile theme + Motyw profilu No comment provided by engineer. @@ -4851,10 +4964,12 @@ Włącz w ustawianiach *Sieć i serwery* . Proxied + Trasowane przez proxy No comment provided by engineer. Proxied servers + Serwery trasowane przez proxy No comment provided by engineer. @@ -4924,6 +5039,7 @@ Włącz w ustawianiach *Sieć i serwery* . Receive errors + Błędy otrzymania No comment provided by engineer. @@ -4948,14 +5064,17 @@ Włącz w ustawianiach *Sieć i serwery* . Received messages + Otrzymane wiadomości No comment provided by engineer. Received reply + Otrzymano odpowiedź No comment provided by engineer. Received total + Otrzymano łącznie No comment provided by engineer. @@ -4990,6 +5109,7 @@ Włącz w ustawianiach *Sieć i serwery* . Reconnect + Połącz ponownie No comment provided by engineer. @@ -4999,18 +5119,22 @@ Włącz w ustawianiach *Sieć i serwery* . Reconnect all servers + Połącz ponownie wszystkie serwery No comment provided by engineer. Reconnect all servers? + Połączyć ponownie wszystkie serwery? No comment provided by engineer. Reconnect server to force message delivery. It uses additional traffic. + Ponownie połącz z serwerem w celu wymuszenia dostarczenia wiadomości. Wykorzystuje to dodatkowy ruch. No comment provided by engineer. Reconnect server? + Połączyć ponownie serwer? No comment provided by engineer. @@ -5065,6 +5189,7 @@ Włącz w ustawianiach *Sieć i serwery* . Remove image + Usuń obraz No comment provided by engineer. @@ -5139,10 +5264,12 @@ Włącz w ustawianiach *Sieć i serwery* . Reset all statistics + Resetuj wszystkie statystyki No comment provided by engineer. Reset all statistics? + Zresetować wszystkie statystyki? No comment provided by engineer. @@ -5152,6 +5279,7 @@ Włącz w ustawianiach *Sieć i serwery* . Reset to app theme + Zresetuj do motywu aplikacji No comment provided by engineer. @@ -5161,6 +5289,7 @@ Włącz w ustawianiach *Sieć i serwery* . Reset to user theme + Zresetuj do motywu użytkownika No comment provided by engineer. @@ -5235,6 +5364,7 @@ Włącz w ustawianiach *Sieć i serwery* . SMP server + Serwer SMP No comment provided by engineer. @@ -5354,10 +5484,12 @@ Włącz w ustawianiach *Sieć i serwery* . Scale + Skaluj No comment provided by engineer. Scan / Paste link + Skanuj / Wklej link No comment provided by engineer. @@ -5402,6 +5534,7 @@ Włącz w ustawianiach *Sieć i serwery* . Secondary + Drugorzędny No comment provided by engineer. @@ -5411,6 +5544,7 @@ Włącz w ustawianiach *Sieć i serwery* . Secured + Zabezpieczone No comment provided by engineer. @@ -5430,6 +5564,7 @@ Włącz w ustawianiach *Sieć i serwery* . Selected chat preferences prohibit this message. + Wybrane preferencje czatu zabraniają tej wiadomości. No comment provided by engineer. @@ -5484,6 +5619,7 @@ Włącz w ustawianiach *Sieć i serwery* . Send errors + Wyślij błędy No comment provided by engineer. @@ -5598,6 +5734,7 @@ Włącz w ustawianiach *Sieć i serwery* . Sent directly + Wysłano bezpośrednio No comment provided by engineer. @@ -5612,6 +5749,7 @@ Włącz w ustawianiach *Sieć i serwery* . Sent messages + Wysłane wiadomości No comment provided by engineer. @@ -5621,18 +5759,22 @@ Włącz w ustawianiach *Sieć i serwery* . Sent reply + Wyślij odpowiedź No comment provided by engineer. Sent total + Wysłano łącznie No comment provided by engineer. Sent via proxy + Wysłano przez proxy No comment provided by engineer. Server address + Adres serwera No comment provided by engineer. @@ -5642,6 +5784,7 @@ Włącz w ustawianiach *Sieć i serwery* . Server address is incompatible with network settings: %@. + Adres serwera jest niekompatybilny z ustawieniami sieci: %@. No comment provided by engineer. @@ -5661,6 +5804,7 @@ Włącz w ustawianiach *Sieć i serwery* . Server type + Typ serwera No comment provided by engineer. @@ -5668,12 +5812,9 @@ Włącz w ustawianiach *Sieć i serwery* . Wersja serwera jest niekompatybilna z ustawieniami sieciowymi. srv error text - - Server version is incompatible with network settings: %@. - No comment provided by engineer. - Server version is incompatible with your app: %@. + Wersja serwera jest niekompatybilna z aplikacją: %@. No comment provided by engineer. @@ -5683,10 +5824,12 @@ Włącz w ustawianiach *Sieć i serwery* . Servers info + Informacje o serwerach No comment provided by engineer. Servers statistics will be reset - this cannot be undone! + Statystyki serwerów zostaną zresetowane - nie można tego cofnąć! No comment provided by engineer. @@ -5706,6 +5849,7 @@ Włącz w ustawianiach *Sieć i serwery* . Set default theme + Ustaw domyślny motyw No comment provided by engineer. @@ -5815,6 +5959,7 @@ Włącz w ustawianiach *Sieć i serwery* . Show percentage + Pokaż procent No comment provided by engineer. @@ -5834,6 +5979,7 @@ Włącz w ustawianiach *Sieć i serwery* . SimpleX + SimpleX No comment provided by engineer. @@ -5913,6 +6059,7 @@ Włącz w ustawianiach *Sieć i serwery* . Size + Rozmiar No comment provided by engineer. @@ -5930,6 +6077,10 @@ Włącz w ustawianiach *Sieć i serwery* . Małe grupy (maks. 20) No comment provided by engineer. + + Soft + blur media + Some non-fatal errors occurred during import - you may see Chat console for more details. Podczas importu wystąpiły niekrytyczne błędy - więcej szczegółów można znaleźć w konsoli czatu. @@ -5962,10 +6113,12 @@ Włącz w ustawianiach *Sieć i serwery* . Starting from %@. + Zaczynanie od %@. No comment provided by engineer. Statistics + Statystyki No comment provided by engineer. @@ -6028,6 +6181,10 @@ Włącz w ustawianiach *Sieć i serwery* . Zatrzymywanie czatu No comment provided by engineer. + + Strong + blur media + Submit Zatwierdź @@ -6035,14 +6192,17 @@ Włącz w ustawianiach *Sieć i serwery* . Subscribed + Zasubskrybowano No comment provided by engineer. Subscription errors + Błędy subskrypcji No comment provided by engineer. Subscriptions ignored + Subskrypcje zignorowane No comment provided by engineer. @@ -6127,6 +6287,7 @@ Włącz w ustawianiach *Sieć i serwery* . Temporary file error + Tymczasowy błąd pliku No comment provided by engineer. @@ -6268,6 +6429,7 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom Themes + Motywy No comment provided by engineer. @@ -6337,6 +6499,7 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom This link was used with another mobile device, please create a new link on the desktop. + Ten link dostał użyty z innym urządzeniem mobilnym, proszę stworzyć nowy link na komputerze. No comment provided by engineer. @@ -6346,6 +6509,7 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom Title + Tytuł No comment provided by engineer. @@ -6417,6 +6581,7 @@ Przed włączeniem tej funkcji zostanie wyświetlony monit uwierzytelniania. Total + Łącznie No comment provided by engineer. @@ -6426,6 +6591,7 @@ Przed włączeniem tej funkcji zostanie wyświetlony monit uwierzytelniania. Transport sessions + Sesje transportowe No comment provided by engineer. @@ -6622,6 +6788,7 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Upload errors + Błędy przesłania No comment provided by engineer. @@ -6636,10 +6803,12 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Uploaded + Przesłane No comment provided by engineer. Uploaded files + Przesłane pliki No comment provided by engineer. @@ -6719,6 +6888,7 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc User selection + Wybór użytkownika No comment provided by engineer. @@ -6858,10 +7028,12 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Wallpaper accent + Akcent tapety No comment provided by engineer. Wallpaper background + Tło tapety No comment provided by engineer. @@ -6971,6 +7143,7 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Wrong key or unknown file chunk address - most likely file is deleted. + Zły klucz lub nieznany adres fragmentu pliku - najprawdopodobniej plik został usunięty. file error text @@ -6980,6 +7153,7 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc XFTP server + Serwer XFTP No comment provided by engineer. @@ -7066,6 +7240,7 @@ Powtórzyć prośbę dołączenia? You are not connected to these servers. Private routing is used to deliver messages to them. + Nie jesteś połączony z tymi serwerami. Prywatne trasowanie jest używane do dostarczania do nich wiadomości. No comment provided by engineer. @@ -7476,6 +7651,7 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. attempts + próby No comment provided by engineer. @@ -7670,6 +7846,7 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. decryption errors + błąd odszyfrowywania No comment provided by engineer. @@ -7724,6 +7901,7 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. duplicates + duplikaty No comment provided by engineer. @@ -7808,6 +7986,7 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. expired + wygasły No comment provided by engineer. @@ -7842,6 +8021,7 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. inactive + nieaktywny No comment provided by engineer. @@ -8023,10 +8203,12 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu. other + inne No comment provided by engineer. other errors + inne błędy No comment provided by engineer. @@ -8399,4 +8581,154 @@ ostatnia otrzymana wiadomość: %2$@ + +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Dismiss Sheet + No comment provided by engineer. + + + Encrypted database + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keep Trying + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + No network connection + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Sending File + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Sharing is not supported when passphrase is not stored in KeyChain. + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/pl.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ 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 2fe659c6d8..cae52da21a 100644 --- a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff @@ -1060,6 +1060,10 @@ Заблокирован администратором No comment provided by engineer.
+ + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. И Вы, и Ваш контакт можете добавлять реакции на сообщения. @@ -1513,6 +1517,10 @@ This is your own one-time link! Ошибка соединения (AUTH) No comment provided by engineer. + + Connection notifications + No comment provided by engineer. + Connection request sent! Запрос на соединение отправлен! @@ -2093,11 +2101,19 @@ This cannot be undone! Компьютеры No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + Destination server error: %@ Ошибка сервера получателя: %@ snd error text + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + Detailed statistics No comment provided by engineer. @@ -2161,6 +2177,10 @@ This cannot be undone! Выключить для всех No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message Исчезающее сообщение @@ -2383,6 +2403,10 @@ This cannot be undone! Включить код самоуничтожения set passcode view + + Enabled + No comment provided by engineer. + Enabled for Включено для @@ -2553,6 +2577,10 @@ This cannot be undone! Ошибка при изменении настройки No comment provided by engineer. + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address Ошибка при создании адреса @@ -3048,6 +3076,18 @@ This cannot be undone! Переслано из No comment provided by engineer. + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + Forwarding server: %1$@ Destination server error: %2$@ @@ -3863,6 +3903,10 @@ This is your link for group %@! Макс. 30 секунд, доставляются мгновенно. No comment provided by engineer. + + Medium + blur media + Member Член группы @@ -4303,7 +4347,7 @@ This is your link for group %@! Off Выключено - No comment provided by engineer. + blur media Ok @@ -4654,10 +4698,6 @@ Error: %@ Пожалуйста, надежно сохраните пароль, Вы НЕ сможете его поменять, если потеряете. No comment provided by engineer. - - Please try later. - No comment provided by engineer. - Polish interface Польский интерфейс @@ -5666,10 +5706,6 @@ Enable in *Network & servers* settings. Версия сервера несовместима с настройками сети. srv error text - - Server version is incompatible with network settings: %@. - No comment provided by engineer. - Server version is incompatible with your app: %@. No comment provided by engineer. @@ -5928,6 +5964,10 @@ Enable in *Network & servers* settings. Маленькие группы (до 20) No comment provided by engineer. + + Soft + blur media + Some non-fatal errors occurred during import - you may see Chat console for more details. Во время импорта произошли некоторые ошибки - для получения более подробной информации вы можете обратиться к консоли. @@ -6026,6 +6066,10 @@ Enable in *Network & servers* settings. Остановка чата No comment provided by engineer. + + Strong + blur media + Submit Продолжить @@ -8394,4 +8438,155 @@ last received msg: %2$@ + +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Dismiss Sheet + No comment provided by engineer. + + + Encrypted database + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keep Trying + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + No network connection + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Sending File + No comment provided by engineer. + + + Share + Поделиться + No comment provided by engineer. + + + Sharing is not supported when passphrase is not stored in KeyChain. + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff b/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff index 2fb86f9a76..9a7052c24c 100644 --- a/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff +++ b/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff @@ -1005,6 +1005,10 @@ Blocked by admin No comment provided by engineer.
+ + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. ทั้งคุณและผู้ติดต่อของคุณสามารถเพิ่มปฏิกิริยาของข้อความได้ @@ -1431,6 +1435,10 @@ This is your own one-time link! การเชื่อมต่อผิดพลาด (AUTH) No comment provided by engineer. + + Connection notifications + No comment provided by engineer. + Connection request sent! ส่งคําขอเชื่อมต่อแล้ว! @@ -1992,10 +2000,18 @@ This cannot be undone! Desktop devices No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + Destination server error: %@ snd error text + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + Detailed statistics No comment provided by engineer. @@ -2059,6 +2075,10 @@ This cannot be undone! ปิดการใช้งานสำหรับทุกคน No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message ข้อความที่จะหายไปหลังเวลาที่กําหนด (disappearing message) @@ -2269,6 +2289,10 @@ This cannot be undone! เปิดใช้งานรหัสผ่านแบบทําลายตัวเอง set passcode view + + Enabled + No comment provided by engineer. + Enabled for No comment provided by engineer. @@ -2429,6 +2453,10 @@ This cannot be undone! เกิดข้อผิดพลาดในการเปลี่ยนการตั้งค่า No comment provided by engineer. + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address เกิดข้อผิดพลาดในการสร้างที่อยู่ @@ -2903,6 +2931,18 @@ This cannot be undone! Forwarded from No comment provided by engineer. + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + Forwarding server: %1$@ Destination server error: %2$@ @@ -3681,6 +3721,10 @@ This is your link for group %@! สูงสุด 30 วินาที รับทันที No comment provided by engineer. + + Medium + blur media + Member สมาชิก @@ -4095,7 +4139,7 @@ This is your link for group %@! Off ปิด - No comment provided by engineer. + blur media Ok @@ -4429,10 +4473,6 @@ Error: %@ โปรดจัดเก็บรหัสผ่านอย่างปลอดภัย คุณจะไม่สามารถเปลี่ยนรหัสผ่านได้หากคุณทำรหัสผ่านหาย No comment provided by engineer. - - Please try later. - No comment provided by engineer. - Polish interface อินเตอร์เฟซภาษาโปแลนด์ @@ -5401,10 +5441,6 @@ Enable in *Network & servers* settings. Server version is incompatible with network settings. srv error text - - Server version is incompatible with network settings: %@. - No comment provided by engineer. - Server version is incompatible with your app: %@. No comment provided by engineer. @@ -5651,6 +5687,10 @@ Enable in *Network & servers* settings. Small groups (max 20) No comment provided by engineer. + + Soft + blur media + Some non-fatal errors occurred during import - you may see Chat console for more details. ข้อผิดพลาดที่ไม่ร้ายแรงบางอย่างเกิดขึ้นระหว่างการนำเข้า - คุณอาจดูรายละเอียดเพิ่มเติมได้ที่คอนโซล Chat @@ -5745,6 +5785,10 @@ Enable in *Network & servers* settings. Stopping chat No comment provided by engineer. + + Strong + blur media + Submit ส่ง @@ -8003,4 +8047,154 @@ last received msg: %2$@ + +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Dismiss Sheet + No comment provided by engineer. + + + Encrypted database + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keep Trying + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + No network connection + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Sending File + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Sharing is not supported when passphrase is not stored in KeyChain. + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/th.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff b/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff index 4b4c804fa6..89da2dde22 100644 --- a/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff +++ b/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff @@ -1060,6 +1060,10 @@ Yönetici tarafından engellendi No comment provided by engineer.
+ + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. Sen ve konuştuğun kişi mesaj tepkileri ekleyebilir. @@ -1513,6 +1517,10 @@ Bu senin kendi tek kullanımlık bağlantın! Bağlantı hatası (DOĞRULAMA) No comment provided by engineer. + + Connection notifications + No comment provided by engineer. + Connection request sent! Bağlantı daveti gönderildi! @@ -2094,11 +2102,19 @@ Bu geri alınamaz! Bilgisayar cihazları No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + Destination server error: %@ Hedef sunucu hatası: %@ snd error text + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + Detailed statistics No comment provided by engineer. @@ -2162,6 +2178,10 @@ Bu geri alınamaz! Herkes için devre dışı bırak No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message Kaybolan mesaj @@ -2384,6 +2404,10 @@ Bu geri alınamaz! Kendini imha şifresini etkinleştir set passcode view + + Enabled + No comment provided by engineer. + Enabled for Şunlar için etkinleştirildi @@ -2554,6 +2578,10 @@ Bu geri alınamaz! Ayar değiştirilirken hata oluştu No comment provided by engineer. + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address Adres oluşturulurken hata oluştu @@ -3049,6 +3077,18 @@ Bu geri alınamaz! Şuradan iletildi No comment provided by engineer. + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + Forwarding server: %1$@ Destination server error: %2$@ @@ -3864,6 +3904,10 @@ Bu senin grup için bağlantın %@! Maksimum 30 saniye, anında alındı. No comment provided by engineer. + + Medium + blur media + Member Kişi @@ -4305,7 +4349,7 @@ Bu senin grup için bağlantın %@! Off Kapalı - No comment provided by engineer. + blur media Ok @@ -4656,10 +4700,6 @@ Hata: %@ Lütfen parolayı güvenli bir şekilde saklayın, kaybederseniz parolayı DEĞİŞTİREMEZSİNİZ. No comment provided by engineer. - - Please try later. - No comment provided by engineer. - Polish interface Lehçe arayüz @@ -5668,10 +5708,6 @@ Enable in *Network & servers* settings. Sunucu sürümü ağ ayarlarıyla uyumlu değil. srv error text - - Server version is incompatible with network settings: %@. - No comment provided by engineer. - Server version is incompatible with your app: %@. No comment provided by engineer. @@ -5930,6 +5966,10 @@ Enable in *Network & servers* settings. Küçük gruplar (en fazla 20 kişi) No comment provided by engineer. + + Soft + blur media + Some non-fatal errors occurred during import - you may see Chat console for more details. İçe aktarma sırasında bazı ölümcül olmayan hatalar oluştu - daha fazla ayrıntı için Sohbet konsoluna bakabilirsiniz. @@ -6028,6 +6068,10 @@ Enable in *Network & servers* settings. Sohbeti durdurma No comment provided by engineer. + + Strong + blur media + Submit Gönder @@ -8399,4 +8443,154 @@ son alınan msj: %2$@ + +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Dismiss Sheet + No comment provided by engineer. + + + Encrypted database + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keep Trying + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + No network connection + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Sending File + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Sharing is not supported when passphrase is not stored in KeyChain. + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/tr.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff b/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff index 8056f0753f..5e46e93fc3 100644 --- a/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff +++ b/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff @@ -1060,6 +1060,10 @@ Заблокований адміністратором No comment provided by engineer.
+ + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. Реакції на повідомлення можете додавати як ви, так і ваш контакт. @@ -1513,6 +1517,10 @@ This is your own one-time link! Помилка підключення (AUTH) No comment provided by engineer. + + Connection notifications + No comment provided by engineer. + Connection request sent! Запит на підключення відправлено! @@ -2094,11 +2102,19 @@ This cannot be undone! Настільні пристрої No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + Destination server error: %@ Помилка сервера призначення: %@ snd error text + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + Detailed statistics No comment provided by engineer. @@ -2162,6 +2178,10 @@ This cannot be undone! Вимкнути для всіх No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message Зникаюче повідомлення @@ -2384,6 +2404,10 @@ This cannot be undone! Увімкнути пароль самознищення set passcode view + + Enabled + No comment provided by engineer. + Enabled for Увімкнено для @@ -2554,6 +2578,10 @@ This cannot be undone! Помилка зміни налаштування No comment provided by engineer. + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address Помилка створення адреси @@ -3049,6 +3077,18 @@ This cannot be undone! Переслано з No comment provided by engineer. + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + Forwarding server: %1$@ Destination server error: %2$@ @@ -3864,6 +3904,10 @@ This is your link for group %@! Максимум 30 секунд, отримується миттєво. No comment provided by engineer. + + Medium + blur media + Member Учасник @@ -4305,7 +4349,7 @@ This is your link for group %@! Off Вимкнено - No comment provided by engineer. + blur media Ok @@ -4656,10 +4700,6 @@ Error: %@ Будь ласка, зберігайте пароль надійно, ви НЕ зможете змінити його, якщо втратите. No comment provided by engineer. - - Please try later. - No comment provided by engineer. - Polish interface Польський інтерфейс @@ -5668,10 +5708,6 @@ Enable in *Network & servers* settings. Серверна версія несумісна з мережевими налаштуваннями. srv error text - - Server version is incompatible with network settings: %@. - No comment provided by engineer. - Server version is incompatible with your app: %@. No comment provided by engineer. @@ -5930,6 +5966,10 @@ Enable in *Network & servers* settings. Невеликі групи (максимум 20 осіб) No comment provided by engineer. + + Soft + blur media + Some non-fatal errors occurred during import - you may see Chat console for more details. Під час імпорту виникли деякі нефатальні помилки – ви можете переглянути консоль чату, щоб дізнатися більше. @@ -6028,6 +6068,10 @@ Enable in *Network & servers* settings. Зупинка чату No comment provided by engineer. + + Strong + blur media + Submit Надіслати @@ -8399,4 +8443,154 @@ last received msg: %2$@ + +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Dismiss Sheet + No comment provided by engineer. + + + Encrypted database + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keep Trying + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + No network connection + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Sending File + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Sharing is not supported when passphrase is not stored in KeyChain. + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/uk.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff index 0a55e89f1a..2d0559e1c9 100644 --- a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff +++ b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff @@ -1045,6 +1045,10 @@ 由管理员封禁 No comment provided by engineer.
+ + Blur media + No comment provided by engineer. + Both you and your contact can add message reactions. 您和您的联系人都可以添加消息回应。 @@ -1489,6 +1493,10 @@ This is your own one-time link! 连接错误(AUTH) No comment provided by engineer. + + Connection notifications + No comment provided by engineer. + Connection request sent! 已发送连接请求! @@ -2063,10 +2071,18 @@ This cannot be undone! 桌面设备 No comment provided by engineer. + + Destination server address of %@ is incompatible with forwarding server %@ settings. + No comment provided by engineer. + Destination server error: %@ snd error text + + Destination server version of %@ is incompatible with forwarding server %@. + No comment provided by engineer. + Detailed statistics No comment provided by engineer. @@ -2130,6 +2146,10 @@ This cannot be undone! 全部禁用 No comment provided by engineer. + + Disabled + No comment provided by engineer. + Disappearing message 限时消息 @@ -2350,6 +2370,10 @@ This cannot be undone! 启用自毁密码 set passcode view + + Enabled + No comment provided by engineer. + Enabled for 启用对象 @@ -2517,6 +2541,10 @@ This cannot be undone! 更改设置错误 No comment provided by engineer. + + Error connecting to forwarding server %@. Please try later. + No comment provided by engineer. + Error creating address 创建地址错误 @@ -3009,6 +3037,18 @@ This cannot be undone! 转发自 No comment provided by engineer. + + Forwarding server %@ failed to connect to destination server %@. Please try later. + No comment provided by engineer. + + + Forwarding server address is incompatible with network settings: %@. + No comment provided by engineer. + + + Forwarding server version is incompatible with network settings: %@. + No comment provided by engineer. + Forwarding server: %1$@ Destination server error: %2$@ @@ -3813,6 +3853,10 @@ This is your link for group %@! 最长30秒,立即接收。 No comment provided by engineer. + + Medium + blur media + Member 成员 @@ -4246,7 +4290,7 @@ This is your link for group %@! Off 关闭 - No comment provided by engineer. + blur media Ok @@ -4592,10 +4636,6 @@ Error: %@ 请安全地保存密码,如果您丢失了密码,您将无法更改它。 No comment provided by engineer. - - Please try later. - No comment provided by engineer. - Polish interface 波兰语界面 @@ -5589,10 +5629,6 @@ Enable in *Network & servers* settings. Server version is incompatible with network settings. srv error text - - Server version is incompatible with network settings: %@. - No comment provided by engineer. - Server version is incompatible with your app: %@. No comment provided by engineer. @@ -5849,6 +5885,10 @@ Enable in *Network & servers* settings. 小群组(最多 20 人) No comment provided by engineer. + + Soft + blur media + Some non-fatal errors occurred during import - you may see Chat console for more details. 导入过程中发生了一些非致命错误——您可以查看聊天控制台了解更多详细信息。 @@ -5947,6 +5987,10 @@ Enable in *Network & servers* settings. 正在停止聊天 No comment provided by engineer. + + Strong + blur media + Submit 提交 @@ -8281,4 +8325,154 @@ last received msg: %2$@ + +
+ +
+ + + SimpleX SE + Bundle display name + + + SimpleX SE + Bundle name + + + Copyright © 2024 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+ +
+ +
+ + + %@ + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Comment + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Database downgrade required + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database upgrade required + No comment provided by engineer. + + + Dismiss Sheet + No comment provided by engineer. + + + Encrypted database + No comment provided by engineer. + + + Error preparing file + No comment provided by engineer. + + + Error preparing message + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + File error + No comment provided by engineer. + + + Incompatible database version + No comment provided by engineer. + + + Invalid migration confirmation + No comment provided by engineer. + + + Keep Trying + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + No active profile + No comment provided by engineer. + + + No network connection + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Open the app to downgrade the database. + No comment provided by engineer. + + + Open the app to upgrade the database. + No comment provided by engineer. + + + Please create a profile in the SimpleX app + No comment provided by engineer. + + + Sending File + No comment provided by engineer. + + + Share + No comment provided by engineer. + + + Sharing is not supported when passphrase is not stored in KeyChain. + No comment provided by engineer. + + + Unknown database error: %@ + No comment provided by engineer. + + + Unsupported format + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + +
diff --git a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings index 124ddbcc33..9c675514f4 100644 --- a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX NSE/en.lproj/InfoPlist.strings @@ -1,6 +1,9 @@ /* Bundle display name */ "CFBundleDisplayName" = "SimpleX NSE"; + /* Bundle name */ "CFBundleName" = "SimpleX NSE"; + /* Copyright (human-readable) */ "NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Source Contents/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/NotificationService.swift b/apps/ios/SimpleX NSE/NotificationService.swift index 30c74fa120..1a2a27ba9b 100644 --- a/apps/ios/SimpleX NSE/NotificationService.swift +++ b/apps/ios/SimpleX NSE/NotificationService.swift @@ -390,6 +390,16 @@ func appStateSubscriber(onState: @escaping (AppState) -> Void) -> AppSubscriber } } +let seSubscriber = seMessageSubscriber { + switch $0 { + case let .state(state): + if state == .sendingMessage && NSEChatState.shared.value.canSuspend { + logger.debug("NotificationService: seSubscriber app state \(state.rawValue), suspending") + suspendChat(fastNSESuspendSchedule.timeout) + } + } +} + var receiverStarted = false let startLock = DispatchSemaphore(value: 1) let suspendLock = DispatchSemaphore(value: 1) diff --git a/apps/ios/SimpleX NSE/bg.lproj/Localizable.strings b/apps/ios/SimpleX NSE/bg.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/bg.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/cs.lproj/Localizable.strings b/apps/ios/SimpleX NSE/cs.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/cs.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/de.lproj/Localizable.strings b/apps/ios/SimpleX NSE/de.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/de.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX NSE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9c675514f4 --- /dev/null +++ b/apps/ios/SimpleX NSE/en.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "SimpleX NSE"; + +/* Bundle name */ +"CFBundleName" = "SimpleX NSE"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved."; + diff --git a/apps/ios/SimpleX NSE/en.lproj/Localizable.strings b/apps/ios/SimpleX NSE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/es.lproj/Localizable.strings b/apps/ios/SimpleX NSE/es.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/es.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/fi.lproj/Localizable.strings b/apps/ios/SimpleX NSE/fi.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/fi.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/fr.lproj/Localizable.strings b/apps/ios/SimpleX NSE/fr.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/fr.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/hu.lproj/Localizable.strings b/apps/ios/SimpleX NSE/hu.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/hu.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/it.lproj/Localizable.strings b/apps/ios/SimpleX NSE/it.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/it.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/ja.lproj/Localizable.strings b/apps/ios/SimpleX NSE/ja.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/ja.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/nl.lproj/Localizable.strings b/apps/ios/SimpleX NSE/nl.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/nl.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/pl.lproj/Localizable.strings b/apps/ios/SimpleX NSE/pl.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/pl.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/ru.lproj/Localizable.strings b/apps/ios/SimpleX NSE/ru.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/ru.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/th.lproj/Localizable.strings b/apps/ios/SimpleX NSE/th.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/th.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/tr.lproj/Localizable.strings b/apps/ios/SimpleX NSE/tr.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/tr.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/uk.lproj/Localizable.strings b/apps/ios/SimpleX NSE/uk.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/uk.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX NSE/zh-Hans.lproj/Localizable.strings b/apps/ios/SimpleX NSE/zh-Hans.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX NSE/zh-Hans.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/Info.plist b/apps/ios/SimpleX SE/Info.plist new file mode 100644 index 0000000000..2ce1f45040 --- /dev/null +++ b/apps/ios/SimpleX SE/Info.plist @@ -0,0 +1,35 @@ + + + + + NSExtension + + NSExtensionAttributes + + NSExtensionActivationRule + + NSExtensionActivationSupportsText + + NSExtensionActivationSupportsAttachmentsWithMinCount + 0 + NSExtensionActivationSupportsAttachmentsWithMaxCount + 1 + NSExtensionActivationSupportsWebPageWithMaxCount + 1 + NSExtensionActivationSupportsWebURLWithMaxCount + 1 + NSExtensionActivationSupportsFileWithMaxCount + 1 + NSExtensionActivationSupportsImageWithMaxCount + 1 + NSExtensionActivationSupportsMovieWithMaxCount + 1 + + + NSExtensionPointIdentifier + com.apple.share-services + NSExtensionPrincipalClass + ShareViewController + + + diff --git a/apps/ios/SimpleX SE/SEChatState.swift b/apps/ios/SimpleX SE/SEChatState.swift new file mode 100644 index 0000000000..581bff894a --- /dev/null +++ b/apps/ios/SimpleX SE/SEChatState.swift @@ -0,0 +1,39 @@ +// +// SEChatState.swift +// SimpleX SE +// +// Created by User on 18/07/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +// SEStateGroupDefault must not be used in the share extension directly, only via this singleton +class SEChatState { + static let shared = SEChatState() + private var value_ = seStateGroupDefault.get() + + var value: SEState { + value_ + } + + func set(_ state: SEState) { + seStateGroupDefault.set(state) + sendSEState(state) + value_ = state + } +} + +/// Waits for other processes to set their state to suspended +/// Will wait for maximum of two seconds, since they might not be running +func waitForOtherProcessesToSuspend() async { + let startTime = CFAbsoluteTimeGetCurrent() + while CFAbsoluteTimeGetCurrent() - startTime < 2 { + try? await Task.sleep(nanoseconds: 100 * NSEC_PER_MSEC) + if appStateGroupDefault.get() == .suspended && + nseStateGroupDefault.get() == .suspended { + break + } + } +} diff --git a/apps/ios/SimpleX SE/ShareAPI.swift b/apps/ios/SimpleX SE/ShareAPI.swift new file mode 100644 index 0000000000..47e072ae78 --- /dev/null +++ b/apps/ios/SimpleX SE/ShareAPI.swift @@ -0,0 +1,115 @@ +// +// ShareAPI.swift +// SimpleX SE +// +// Created by User on 15/07/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import OSLog +import Foundation +import SimpleXChat + +let logger = Logger() + +func apiGetActiveUser() throws -> User? { + let r = sendSimpleXCmd(.showActiveUser) + switch r { + case let .activeUser(user): return user + case .chatCmdError(_, .error(.noActiveUser)): return nil + default: throw r + } +} + +func apiStartChat() throws -> Bool { + let r = sendSimpleXCmd(.startChat(mainApp: false, enableSndFiles: true)) + switch r { + case .chatStarted: return true + case .chatRunning: return false + default: throw r + } +} + +func apiSetNetworkConfig(_ cfg: NetCfg) throws { + let r = sendSimpleXCmd(.apiSetNetworkConfig(networkConfig: cfg)) + if case .cmdOk = r { return } + throw r +} + +func apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String) throws { + let r = sendSimpleXCmd(.apiSetAppFilePaths(filesFolder: filesFolder, tempFolder: tempFolder, assetsFolder: assetsFolder)) + if case .cmdOk = r { return } + throw r +} + +func apiSetEncryptLocalFiles(_ enable: Bool) throws { + let r = sendSimpleXCmd(.apiSetEncryptLocalFiles(enable: enable)) + if case .cmdOk = r { return } + throw r +} + +func apiGetChats(userId: User.ID) throws -> Array { + let r = sendSimpleXCmd(.apiGetChats(userId: userId)) + if case let .apiChats(user: _, chats: chats) = r { return chats } + throw r +} + +func apiSendMessage( + chatInfo: ChatInfo, + cryptoFile: CryptoFile?, + msgContent: MsgContent +) throws -> AChatItem { + let r = sendSimpleXCmd( + chatInfo.chatType == .local + ? .apiCreateChatItem( + noteFolderId: chatInfo.apiId, + file: cryptoFile, + msg: msgContent + ) + : .apiSendMessage( + type: chatInfo.chatType, + id: chatInfo.apiId, + file: cryptoFile, + quotedItemId: nil, + msg: msgContent, + live: false, + ttl: nil + ) + ) + if case let .newChatItem(_, chatItem) = r { + return chatItem + } else { + if let filePath = cryptoFile?.filePath { removeFile(filePath) } + throw r + } +} + +func apiActivateChat() throws { + chatReopenStore() + let r = sendSimpleXCmd(.apiActivateChat(restoreChat: false)) + if case .cmdOk = r { return } + throw r +} + +func apiSuspendChat(expired: Bool) { + let r = sendSimpleXCmd(.apiSuspendChat(timeoutMicroseconds: expired ? 0 : 3_000000)) + // Block until `chatSuspended` received or 3 seconds has passed + var suspended = false + if case .cmdOk = r, !expired { + let startTime = CFAbsoluteTimeGetCurrent() + while CFAbsoluteTimeGetCurrent() - startTime < 3 { + switch recvSimpleXMsg(messageTimeout: 3_500000) { + case .chatSuspended: + suspended = false + break + default: continue + } + } + } + if !suspended { + _ = sendSimpleXCmd(.apiSuspendChat(timeoutMicroseconds: 0)) + } + logger.debug("close store") + chatCloseStore() + SEChatState.shared.set(.inactive) +} diff --git a/apps/ios/SimpleX SE/ShareModel.swift b/apps/ios/SimpleX SE/ShareModel.swift new file mode 100644 index 0000000000..35d26dea35 --- /dev/null +++ b/apps/ios/SimpleX SE/ShareModel.swift @@ -0,0 +1,500 @@ +// +// ShareModel.swift +// SimpleX SE +// +// Created by Levitating Pineapple on 09/07/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import UniformTypeIdentifiers +import AVFoundation +import SwiftUI +import SimpleXChat + +/// Maximum size of hex encoded media previews +private let MAX_DATA_SIZE: Int64 = 14000 + +/// Maximum dimension (width or height) of an image, before passed for processing +private let MAX_DOWNSAMPLE_SIZE: Int64 = 2000 + +class ShareModel: ObservableObject { + @Published var sharedContent: SharedContent? + @Published var chats = Array() + @Published var profileImages = Dictionary() + @Published var search = String() + @Published var comment = String() + @Published var selected: ChatData? + @Published var isLoaded = false + @Published var bottomBar: BottomBar = .loadingSpinner + @Published var errorAlert: ErrorAlert? + + enum BottomBar { + case sendButton + case loadingSpinner + case loadingBar(progress: Double) + + var isLoading: Bool { + switch self { + case .sendButton: false + case .loadingSpinner: true + case .loadingBar: true + } + } + } + + var completion: () -> Void = { + fatalError("completion has not been set") + } + + private var itemProvider: NSItemProvider? + + var isSendDisbled: Bool { sharedContent == nil || selected == nil } + + var filteredChats: Array { + search.isEmpty + ? filterChatsToForwardTo(chats: chats) + : filterChatsToForwardTo(chats: chats) + .filter { foundChat($0, search.localizedLowercase) } + } + + func setup(context: NSExtensionContext) { + if let item = context.inputItems.first as? NSExtensionItem, + let itemProvider = item.attachments?.first { + self.itemProvider = itemProvider + self.completion = { + ShareModel.CompletionHandler.isEventLoopEnabled = false + context.completeRequest(returningItems: [item]) { + apiSuspendChat(expired: $0) + } + } + // Init Chat + Task { + if let e = initChat() { + await MainActor.run { errorAlert = e } + } else { + // Load Chats + Task { + switch fetchChats() { + case let .success(chats): + // Decode base64 images on background thread + let profileImages = chats.reduce(into: Dictionary()) { dict, chatData in + if let profileImage = chatData.chatInfo.image, + let uiImage = UIImage(base64Encoded: profileImage) { + dict[chatData.id] = uiImage + } + } + await MainActor.run { + self.chats = chats + self.profileImages = profileImages + withAnimation { isLoaded = true } + } + case let .failure(error): + await MainActor.run { errorAlert = error } + } + } + // Process Attachment + Task { + switch await self.itemProvider!.sharedContent() { + case let .success(chatItemContent): + await MainActor.run { + self.sharedContent = chatItemContent + self.bottomBar = .sendButton + if case let .text(string) = chatItemContent { comment = string } + } + case let .failure(errorAlert): + await MainActor.run { self.errorAlert = errorAlert } + } + } + } + } + } + } + + func send() { + if let sharedContent, let selected { + Task { + await MainActor.run { self.bottomBar = .loadingSpinner } + do { + SEChatState.shared.set(.sendingMessage) + await waitForOtherProcessesToSuspend() + let ci = try apiSendMessage( + chatInfo: selected.chatInfo, + cryptoFile: sharedContent.cryptoFile, + msgContent: sharedContent.msgContent(comment: self.comment) + ) + if selected.chatInfo.chatType == .local { + completion() + } else { + await MainActor.run { self.bottomBar = .loadingBar(progress: .zero) } + if let e = await handleEvents( + isGroupChat: ci.chatInfo.chatType == .group, + isWithoutFile: sharedContent.cryptoFile == nil, + chatItemId: ci.chatItem.id + ) { + await MainActor.run { errorAlert = e } + } else { + completion() + } + } + } catch { + if let e = error as? ErrorAlert { + await MainActor.run { errorAlert = e } + } + } + } + } + } + + private func initChat() -> ErrorAlert? { + do { + if hasChatCtrl() { + try apiActivateChat() + } else { + registerGroupDefaults() + haskell_init_se() + let (_, result) = chatMigrateInit(confirmMigrations: defaultMigrationConfirmation()) + if let e = migrationError(result) { return e } + try apiSetAppFilePaths( + filesFolder: getAppFilesDirectory().path, + tempFolder: getTempFilesDirectory().path, + assetsFolder: getWallpaperDirectory().deletingLastPathComponent().path + ) + let isRunning = try apiStartChat() + logger.log(level: .debug, "chat started, running: \(isRunning)") + } + try apiSetNetworkConfig(getNetCfg()) + try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get()) + } catch { return ErrorAlert(error) } + return nil + } + + private func migrationError(_ r: DBMigrationResult) -> ErrorAlert? { + let useKeychain = storeDBPassphraseGroupDefault.get() + let storedDBKey = kcDatabasePassword.get() + // This switch duplicates DatabaseErrorView. + // TODO allow entering passphrase and make messages the same as in DatabaseErrorView. + return switch r { + case .errorNotADatabase: + if useKeychain && storedDBKey != nil && storedDBKey != "" { + ErrorAlert( + title: "Wrong database passphrase", + message: "Database passphrase is different from saved in the keychain." + ) + } else { + ErrorAlert( + title: "Encrypted database", + message: "Sharing is not supported when passphrase is not stored in KeyChain." + ) + } + case let .errorMigration(_, migrationError): + switch migrationError { + case .upgrade: + ErrorAlert( + title: "Database upgrade required", + message: "Open the app to upgrade the database." + ) + case .downgrade: + ErrorAlert( + title: "Database downgrade required", + message: "Open the app to downgrade the database." + ) + case let .migrationError(mtrError): + ErrorAlert( + title: "Incompatible database version", + message: mtrErrorDescription(mtrError) + ) + } + case let .errorSQL(_, migrationSQLError): + ErrorAlert( + title: "Database error", + message: "Error: \(migrationSQLError)" + ) + case .errorKeychain: + ErrorAlert( + title: "Keychain error", + message: "Cannot access keychain to save database password" + ) + case .invalidConfirmation: + ErrorAlert("Invalid migration confirmation") + case let .unknown(json): + ErrorAlert( + title: "Database error", + message: "Unknown database error: \(json)" + ) + case .ok: nil + } + } + + private func fetchChats() -> Result, ErrorAlert> { + do { + guard let user = try apiGetActiveUser() else { + return .failure( + ErrorAlert( + title: "No active profile", + message: "Please create a profile in the SimpleX app" + ) + ) + } + return .success(try apiGetChats(userId: user.id)) + } catch { + return .failure(ErrorAlert(error)) + } + } + + actor CompletionHandler { + static var isEventLoopEnabled = false + private var fileCompleted = false + private var messageCompleted = false + + func completeFile() { fileCompleted = true } + + func completeMessage() { messageCompleted = true } + + var isRunning: Bool { + Self.isEventLoopEnabled && !(fileCompleted && messageCompleted) + } + } + + /// Polls and processes chat events + /// Returns when message sending has completed optionally returning and error. + private func handleEvents(isGroupChat: Bool, isWithoutFile: Bool, chatItemId: ChatItem.ID) async -> ErrorAlert? { + func isMessage(for item: AChatItem?) -> Bool { + item.map { $0.chatItem.id == chatItemId } ?? false + } + + CompletionHandler.isEventLoopEnabled = true + let ch = CompletionHandler() + if isWithoutFile { await ch.completeFile() } + var networkTimeout = CFAbsoluteTimeGetCurrent() + while await ch.isRunning { + if CFAbsoluteTimeGetCurrent() - networkTimeout > 30 { + networkTimeout = CFAbsoluteTimeGetCurrent() + await MainActor.run { + self.errorAlert = ErrorAlert(title: "No network connection") { + Button("Keep Trying", role: .cancel) { } + Button("Dismiss Sheet", role: .destructive) { self.completion() } + } + } + } + switch recvSimpleXMsg(messageTimeout: 1_000_000) { + case let .sndFileProgressXFTP(_, ci, _, sentSize, totalSize): + guard isMessage(for: ci) else { continue } + networkTimeout = CFAbsoluteTimeGetCurrent() + await MainActor.run { + withAnimation { + let progress = Double(sentSize) / Double(totalSize) + bottomBar = .loadingBar(progress: progress) + } + } + case let .sndFileCompleteXFTP(_, ci, _): + guard isMessage(for: ci) else { continue } + if isGroupChat { + await MainActor.run { bottomBar = .loadingSpinner } + } + await ch.completeFile() + if await !ch.isRunning { break } + case let .chatItemStatusUpdated(_, ci): + guard isMessage(for: ci) else { continue } + if let (title, message) = ci.chatItem.meta.itemStatus.statusInfo { + // `title` and `message` already localized and interpolated + return ErrorAlert( + title: "\(title)", + message: "\(message)" + ) + } else if case let .sndSent(sndProgress) = ci.chatItem.meta.itemStatus { + switch sndProgress { + case .complete: + await ch.completeMessage() + case .partial: + if isGroupChat { + Task { + try? await Task.sleep(nanoseconds: 5 * NSEC_PER_SEC) + await ch.completeMessage() + } + } + } + } + case let .sndFileError(_, ci, _, errorMessage): + guard isMessage(for: ci) else { continue } + if let ci { cleanupFile(ci) } + return ErrorAlert(title: "File error", message: "\(fileErrorInfo(ci) ?? errorMessage)") + case let .sndFileWarning(_, ci, _, errorMessage): + guard isMessage(for: ci) else { continue } + if let ci { cleanupFile(ci) } + return ErrorAlert(title: "File error", message: "\(fileErrorInfo(ci) ?? errorMessage)") + case let .chatError(_, chatError): + return ErrorAlert(chatError) + case let .chatCmdError(_, chatError): + return ErrorAlert(chatError) + default: continue + } + } + return nil + } + + private func fileErrorInfo(_ ci: AChatItem?) -> String? { + switch ci?.chatItem.file?.fileStatus { + case let .sndError(e): e.errorInfo + case let .sndWarning(e): e.errorInfo + default: nil + } + } +} + +/// Chat Item Content extracted from `NSItemProvider` without the comment +enum SharedContent { + case image(preview: String, cryptoFile: CryptoFile) + case movie(preview: String, duration: Int, cryptoFile: CryptoFile) + case url(preview: LinkPreview) + case text(string: String) + case data(cryptoFile: CryptoFile) + + var cryptoFile: CryptoFile? { + switch self { + case let .image(_, cryptoFile): cryptoFile + case let .movie(_, _, cryptoFile): cryptoFile + case .url: nil + case .text: nil + case let .data(cryptoFile): cryptoFile + } + } + + func msgContent(comment: String) -> MsgContent { + switch self { + case let .image(preview, _): .image(text: comment, image: preview) + case let .movie(preview, duration, _): .video(text: comment, image: preview, duration: duration) + case let .url(preview): .link(text: comment, preview: preview) + case .text: .text(comment) + case .data: .file(comment) + } + } +} + +extension NSItemProvider { + fileprivate func sharedContent() async -> Result { + if let type = firstMatching(of: [.image, .movie, .fileURL, .url, .text]) { + switch type { + // Prepare Image message + case .image: + + // Animated + return if hasItemConformingToTypeIdentifier(UTType.gif.identifier) { + if let url = try? await inPlaceUrl(type: type), + let data = try? Data(contentsOf: url), + let image = UIImage(data: data), + let cryptoFile = saveFile(data, generateNewFileName("IMG", "gif"), encrypted: privacyEncryptLocalFilesGroupDefault.get()), + let preview = resizeImageToStrSize(image, maxDataSize: MAX_DATA_SIZE) { + .success(.image(preview: preview, cryptoFile: cryptoFile)) + } else { .failure(ErrorAlert("Error preparing message")) } + + // Static + } else { + if let url = try? await inPlaceUrl(type: type), + let image = downsampleImage(at: url, to: MAX_DOWNSAMPLE_SIZE), + let cryptoFile = saveImage(image), + let preview = resizeImageToStrSize(image, maxDataSize: MAX_DATA_SIZE) { + .success(.image(preview: preview, cryptoFile: cryptoFile)) + } else { .failure(ErrorAlert("Error preparing message")) } + } + + // Prepare Movie message + case .movie: + if let url = try? await inPlaceUrl(type: type), + let trancodedUrl = await transcodeVideo(from: url), + let (image, duration) = AVAsset(url: trancodedUrl).generatePreview(), + let preview = resizeImageToStrSize(image, maxDataSize: MAX_DATA_SIZE), + let cryptoFile = moveTempFileFromURL(trancodedUrl) { + try? FileManager.default.removeItem(at: trancodedUrl) + return .success(.movie(preview: preview, duration: duration, cryptoFile: cryptoFile)) + } else { return .failure(ErrorAlert("Error preparing message")) } + + // Prepare Data message + case .fileURL: + if let url = try? await inPlaceUrl(type: .data) { + if isFileTooLarge(for: url) { + let sizeString = ByteCountFormatter.string( + fromByteCount: Int64(getMaxFileSize(.xftp)), + countStyle: .binary + ) + return .failure( + ErrorAlert( + title: "Large file!", + message: "Currently maximum supported file size is \(sizeString)." + ) + ) + } + if let file = saveFileFromURL(url) { + return .success(.data(cryptoFile: file)) + } + } + return .failure(ErrorAlert("Error preparing file")) + + // Prepare Link message + case .url: + if let url = try? await loadItem(forTypeIdentifier: type.identifier) as? URL { + let content: SharedContent = +// Option to disable previews needs to be taken into account +// if let linkPreview = await getLinkPreview(for: url) { +// .url(preview: linkPreview) +// } else { + .text(string: url.absoluteString) +// } + return .success(content) + } else { return .failure(ErrorAlert("Error preparing message")) } + + // Prepare Text message + case .text: + return if let text = try? await loadItem(forTypeIdentifier: type.identifier) as? String { + .success(.text(string: text)) + } else { .failure(ErrorAlert("Error preparing message")) } + default: return .failure(ErrorAlert("Unsupported format")) + } + } else { + return .failure(ErrorAlert("Unsupported format")) + } + } + + private func inPlaceUrl(type: UTType) async throws -> URL { + try await withCheckedThrowingContinuation { cont in + let _ = loadInPlaceFileRepresentation(forTypeIdentifier: type.identifier) { url, bool, error in + if let url = url { + cont.resume(returning: url) + } else if let error = error { + cont.resume(throwing: error) + } else { + fatalError("Either `url` or `error` must be present") + } + } + } + } + + private func firstMatching(of types: Array) -> UTType? { + for type in types { + if hasItemConformingToTypeIdentifier(type.identifier) { return type } + } + return nil + } +} + + +fileprivate func transcodeVideo(from input: URL) async -> URL? { + let outputUrl = URL( + fileURLWithPath: generateNewFileName( + getTempFilesDirectory().path + "/" + "video", "mp4", + fullPath: true + ) + ) + if await makeVideoQualityLower(input, outputUrl: outputUrl) { + return outputUrl + } else { + try? FileManager.default.removeItem(at: outputUrl) + return nil + } +} + +fileprivate func isFileTooLarge(for url: URL) -> Bool { + fileSize(url) + .map { $0 > getMaxFileSize(.xftp) } + ?? false +} + diff --git a/apps/ios/SimpleX SE/ShareView.swift b/apps/ios/SimpleX SE/ShareView.swift new file mode 100644 index 0000000000..20e6450b99 --- /dev/null +++ b/apps/ios/SimpleX SE/ShareView.swift @@ -0,0 +1,180 @@ +// +// ShareView.swift +// SimpleX SE +// +// Created by Levitating Pineapple on 09/07/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +struct ShareView: View { + @ObservedObject var model: ShareModel + @Environment(\.colorScheme) var colorScheme + + var body: some View { + NavigationView { + ZStack(alignment: .bottom) { + if model.isLoaded { + List(model.filteredChats) { chat in + HStack { + profileImage( + chatInfoId: chat.chatInfo.id, + systemFallback: chatIconName(chat.chatInfo), + size: 30 + ) + Text(chat.chatInfo.displayName) + Spacer() + radioButton(selected: chat == model.selected) + } + .contentShape(Rectangle()) + .onTapGesture { model.selected = model.selected == chat ? nil : chat } + .tag(chat) + } + } else { + ProgressView().frame(maxHeight: .infinity) + } + } + .navigationTitle("Share") + .safeAreaInset(edge: .bottom) { + switch model.bottomBar { + case .sendButton: + compose(isLoading: false) + case .loadingSpinner: + compose(isLoading: true) + case .loadingBar(let progress): + loadingBar(progress: progress) + } + } + } + .searchable( + text: $model.search, + placement: .navigationBarDrawer(displayMode: .always) + ) + .alert($model.errorAlert) { alert in + Button("Ok") { model.completion() } + } + } + + private func compose(isLoading: Bool) -> some View { + VStack(spacing: .zero) { + Divider() + if let content = model.sharedContent { + itemPreview(content) + } + HStack { + Group { + if #available(iOSApplicationExtension 16.0, *) { + TextField("Comment", text: $model.comment, axis: .vertical) + } else { + TextField("Comment", text: $model.comment) + } + } + .contentShape(Rectangle()) + .disabled(isLoading) + .padding(.horizontal, 12) + .padding(.vertical, 4) + Group { + if isLoading { + ProgressView() + } else { + Button(action: model.send) { + Image(systemName: "arrow.up.circle.fill") + .resizable() + } + .disabled(model.isSendDisbled) + } + } + .frame(width: 28, height: 28) + .padding(6) + + } + .background(Color(.systemBackground)) + .clipShape(RoundedRectangle(cornerRadius: 20)) + .overlay( + RoundedRectangle(cornerRadius: 20) + .strokeBorder(.secondary, lineWidth: 0.5).opacity(0.7) + ) + .padding(8) + } + .background(.thinMaterial) + } + + @ViewBuilder private func itemPreview(_ content: SharedContent) -> some View { + switch content { + case let .image(preview, _): imagePreview(preview) + case let .movie(preview, _, _): imagePreview(preview) + case let .url(linkPreview): imagePreview(linkPreview.image) + case let .data(cryptoFile): + previewArea { + Image(systemName: "doc.fill") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 30, height: 30) + .foregroundColor(Color(uiColor: .tertiaryLabel)) + .padding(.leading, 4) + Text(cryptoFile.filePath) + } + case .text: EmptyView() + } + } + + @ViewBuilder private func imagePreview(_ img: String) -> some View { + if let img = UIImage(base64Encoded: img) { + previewArea { + Image(uiImage: img) + .resizable() + .scaledToFit() + .frame(minHeight: 40, maxHeight: 60) + } + } else { + EmptyView() + } + } + + @ViewBuilder private func previewArea(@ViewBuilder content: @escaping () -> V) -> some View { + HStack(alignment: .center, spacing: 8) { + content() + Spacer() + } + .padding(.vertical, 1) + .frame(minHeight: 54) + .background { + switch colorScheme { + case .light: LightColorPaletteApp.sentMessage + case .dark: DarkColorPaletteApp.sentMessage + @unknown default: Color(.tertiarySystemBackground) + } + } + Divider() + } + + private func loadingBar(progress: Double) -> some View { + VStack { + Text("Sending File") + ProgressView(value: progress) + } + .padding() + .background(Material.ultraThin) + } + + private func profileImage(chatInfoId: ChatInfo.ID, systemFallback: String, size: Double) -> some View { + Group { + if let uiImage = model.profileImages[chatInfoId] { + Image(uiImage: uiImage).resizable() + } else { + Image(systemName: systemFallback).resizable() + } + } + .foregroundStyle(Color(.tertiaryLabel)) + .frame(width: size, height: size) + .clipShape(RoundedRectangle(cornerRadius: size * 0.225, style: .continuous)) + } + + private func radioButton(selected: Bool) -> some View { + Image(systemName: selected ? "checkmark.circle.fill" : "circle") + .imageScale(.large) + .foregroundStyle(selected ? Color.accentColor : Color(.tertiaryLabel)) + } +} diff --git a/apps/ios/SimpleX SE/ShareViewController.swift b/apps/ios/SimpleX SE/ShareViewController.swift new file mode 100644 index 0000000000..bf22f44a3b --- /dev/null +++ b/apps/ios/SimpleX SE/ShareViewController.swift @@ -0,0 +1,46 @@ +// +// ShareViewController.swift +// SimpleX SE +// +// Created by Levitating Pineapple on 08/07/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import UIKit +import SwiftUI +import SimpleXChat + +/// Extension Entry point +/// System will create this controller each time share sheet is invoked +/// using `NSExtensionPrincipalClass` in the info.plist +@objc(ShareViewController) +class ShareViewController: UIHostingController { + private let model = ShareModel() + // Assuming iOS continues to only allow single share sheet to be presented at once + static var isVisible: Bool = false + + @objc init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(rootView: ShareView(model: model)) + } + + @available(*, unavailable) + required init?(coder aDecoder: NSCoder) { fatalError() } + + override func viewDidLoad() { + ShareModel.CompletionHandler.isEventLoopEnabled = false + model.setup(context: extensionContext!) + } + + override func viewWillAppear(_ animated: Bool) { + logger.debug("ShareSheet will appear") + super.viewWillAppear(animated) + Self.isVisible = true + } + + override func viewWillDisappear(_ animated: Bool) { + logger.debug("ShareSheet will dissappear") + super.viewWillDisappear(animated) + ShareModel.CompletionHandler.isEventLoopEnabled = false + Self.isVisible = false + } +} diff --git a/apps/ios/SimpleX SE/SimpleX SE.entitlements b/apps/ios/SimpleX SE/SimpleX SE.entitlements new file mode 100644 index 0000000000..51dea2c806 --- /dev/null +++ b/apps/ios/SimpleX SE/SimpleX SE.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.application-groups + + group.chat.simplex.app + + keychain-access-groups + + $(AppIdentifierPrefix)chat.simplex.app + + + diff --git a/apps/ios/SimpleX SE/bg.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/bg.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/bg.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/bg.lproj/Localizable.strings b/apps/ios/SimpleX SE/bg.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/bg.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/cs.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/cs.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/cs.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/cs.lproj/Localizable.strings b/apps/ios/SimpleX SE/cs.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/cs.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/de.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/de.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/de.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/de.lproj/Localizable.strings b/apps/ios/SimpleX SE/de.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/de.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/en.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/en.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/en.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/en.lproj/Localizable.strings b/apps/ios/SimpleX SE/en.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/es.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/es.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/es.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/es.lproj/Localizable.strings b/apps/ios/SimpleX SE/es.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/es.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/fi.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/fi.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/fi.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/fi.lproj/Localizable.strings b/apps/ios/SimpleX SE/fi.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/fi.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/fr.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/fr.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/fr.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/fr.lproj/Localizable.strings b/apps/ios/SimpleX SE/fr.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/fr.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/hu.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/hu.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/hu.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/hu.lproj/Localizable.strings b/apps/ios/SimpleX SE/hu.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/hu.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/it.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/it.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/it.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/it.lproj/Localizable.strings b/apps/ios/SimpleX SE/it.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/it.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/ja.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/ja.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/ja.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/ja.lproj/Localizable.strings b/apps/ios/SimpleX SE/ja.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/ja.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/nl.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/nl.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/nl.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/nl.lproj/Localizable.strings b/apps/ios/SimpleX SE/nl.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/nl.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/pl.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/pl.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/pl.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/pl.lproj/Localizable.strings b/apps/ios/SimpleX SE/pl.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/pl.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/ru.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/ru.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/ru.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/ru.lproj/Localizable.strings b/apps/ios/SimpleX SE/ru.lproj/Localizable.strings new file mode 100644 index 0000000000..30a26c9920 --- /dev/null +++ b/apps/ios/SimpleX SE/ru.lproj/Localizable.strings @@ -0,0 +1,3 @@ +/* No comment provided by engineer. */ +"Share" = "Поделиться"; + diff --git a/apps/ios/SimpleX SE/th.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/th.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/th.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/th.lproj/Localizable.strings b/apps/ios/SimpleX SE/th.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/th.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/tr.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/tr.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/tr.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/tr.lproj/Localizable.strings b/apps/ios/SimpleX SE/tr.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/tr.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/uk.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/uk.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/uk.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/uk.lproj/Localizable.strings b/apps/ios/SimpleX SE/uk.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/uk.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/zh-Hans.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/zh-Hans.lproj/InfoPlist.strings new file mode 100644 index 0000000000..388ac01f7f --- /dev/null +++ b/apps/ios/SimpleX SE/zh-Hans.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ +/* + InfoPlist.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX SE/zh-Hans.lproj/Localizable.strings b/apps/ios/SimpleX SE/zh-Hans.lproj/Localizable.strings new file mode 100644 index 0000000000..5ef592ec70 --- /dev/null +++ b/apps/ios/SimpleX SE/zh-Hans.lproj/Localizable.strings @@ -0,0 +1,7 @@ +/* + Localizable.strings + SimpleX + + Created by EP on 30/07/2024. + Copyright © 2024 SimpleX Chat. All rights reserved. +*/ diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 0ec33d9899..4a3be1e473 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -170,11 +170,6 @@ 649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCDA12805D6EF00C3A862 /* CIImageView.swift */; }; 64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; }; 64AA1C6C27F3537400AC7277 /* DeletedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */; }; - 64BAC45E2C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64BAC4592C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU.a */; }; - 64BAC45F2C495205008D3995 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64BAC45A2C495205008D3995 /* libffi.a */; }; - 64BAC4602C495205008D3995 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64BAC45B2C495205008D3995 /* libgmpxx.a */; }; - 64BAC4612C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64BAC45C2C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU-ghc9.6.3.a */; }; - 64BAC4622C495205008D3995 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64BAC45D2C495205008D3995 /* libgmp.a */; }; 64C06EB52A0A4A7C00792D4D /* ChatItemInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */; }; 64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */; }; 64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */; }; @@ -183,7 +178,6 @@ 64E972072881BB22008DBC02 /* CIGroupInvitationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */; }; 64EEB0F72C353F1C00972D62 /* ServersSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64EEB0F62C353F1C00972D62 /* ServersSummaryView.swift */; }; 64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */; }; - 8C05382E2B39887E006436DC /* VideoUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C05382D2B39887E006436DC /* VideoUtils.swift */; }; 8C69FE7D2B8C7D2700267E38 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */; }; 8C74C3E52C1B900600039E77 /* ThemeTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7E3CE32C0DEAC400BFF63A /* ThemeTypes.swift */; }; 8C74C3E72C1B901900039E77 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C852B072C1086D100BA61E8 /* Color.swift */; }; @@ -199,9 +193,17 @@ 8C9BC2652C240D5200875A27 /* ThemeModeEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9BC2642C240D5100875A27 /* ThemeModeEditor.swift */; }; 8CC4ED902BD7B8530078AEE8 /* CallAudioDeviceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC4ED8F2BD7B8530078AEE8 /* CallAudioDeviceManager.swift */; }; 8CC956EE2BC0041000412A11 /* NetworkObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC956ED2BC0041000412A11 /* NetworkObserver.swift */; }; + CE1EB0E42C459A660099D896 /* ShareAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1EB0E32C459A660099D896 /* ShareAPI.swift */; }; + CE2AD9CE2C452A4D00E844E3 /* ChatUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2AD9CD2C452A4D00E844E3 /* ChatUtils.swift */; }; + CE3097FB2C4C0C9F00180898 /* ErrorAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3097FA2C4C0C9F00180898 /* ErrorAlert.swift */; }; CE38A29A2C3FCA54005ED185 /* ImageUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CBD2859295711D700EC2CF4 /* ImageUtils.swift */; }; CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */ = {isa = PBXBuildFile; productRef = CE38A29B2C3FCD72005ED185 /* SwiftyGif */; }; CE984D4B2C36C5D500E3AEFF /* ChatItemClipShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE984D4A2C36C5D500E3AEFF /* ChatItemClipShape.swift */; }; + CEDE70222C48FD9500233B1F /* SEChatState.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEDE70212C48FD9500233B1F /* SEChatState.swift */; }; + CEE723AA2C3BD3D70009AE93 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE723A92C3BD3D70009AE93 /* ShareViewController.swift */; }; + CEE723B12C3BD3D70009AE93 /* SimpleX SE.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = CEE723A72C3BD3D70009AE93 /* SimpleX SE.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + CEE723F02C3D25C70009AE93 /* ShareView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE723EF2C3D25C70009AE93 /* ShareView.swift */; }; + CEE723F22C3D25ED0009AE93 /* ShareModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE723F12C3D25ED0009AE93 /* ShareModel.swift */; }; CEEA861D2C2ABCB50084E1EA /* ReverseList.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEEA861C2C2ABCB50084E1EA /* ReverseList.swift */; }; D7197A1829AE89660055C05A /* WebRTC in Frameworks */ = {isa = PBXBuildFile; productRef = D7197A1729AE89660055C05A /* WebRTC */; }; D72A9088294BD7A70047C86D /* NativeTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72A9087294BD7A70047C86D /* NativeTextEditor.swift */; }; @@ -210,6 +212,15 @@ D77B92DC2952372200A5A1CC /* SwiftyGif in Frameworks */ = {isa = PBXBuildFile; productRef = D77B92DB2952372200A5A1CC /* SwiftyGif */; }; D7F0E33929964E7E0068AF69 /* LZString in Frameworks */ = {isa = PBXBuildFile; productRef = D7F0E33829964E7E0068AF69 /* LZString */; }; E50581062C3DDD9D009C3F71 /* Yams in Frameworks */ = {isa = PBXBuildFile; productRef = E50581052C3DDD9D009C3F71 /* Yams */; }; + E5DCF8DB2C56FAC1007928CC /* SimpleXChat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; }; + E5DCF8E12C5847EB007928CC /* libHSsimplex-chat-6.0.0.3-5yF58NJ20MV4vZ7jQKFAxB.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5DCF8DC2C5847EB007928CC /* libHSsimplex-chat-6.0.0.3-5yF58NJ20MV4vZ7jQKFAxB.a */; }; + E5DCF8E22C5847EB007928CC /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5DCF8DD2C5847EB007928CC /* libgmp.a */; }; + E5DCF8E32C5847EB007928CC /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5DCF8DE2C5847EB007928CC /* libgmpxx.a */; }; + E5DCF8E42C5847EB007928CC /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5DCF8DF2C5847EB007928CC /* libffi.a */; }; + E5DCF8E52C5847EB007928CC /* libHSsimplex-chat-6.0.0.3-5yF58NJ20MV4vZ7jQKFAxB-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5DCF8E02C5847EB007928CC /* libHSsimplex-chat-6.0.0.3-5yF58NJ20MV4vZ7jQKFAxB-ghc9.6.3.a */; }; + E5DCF9712C590272007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF96F2C590272007928CC /* Localizable.strings */; }; + E5DCF9842C5902CE007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF9822C5902CE007928CC /* Localizable.strings */; }; + E5DCF9982C5906FF007928CC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF9962C5906FF007928CC /* InfoPlist.strings */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -241,6 +252,20 @@ remoteGlobalIDString = 5CE2BA672845308900EC33A6; remoteInfo = SimpleXChat; }; + CEE723AF2C3BD3D70009AE93 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5CA059BE279559F40002BEB4 /* Project object */; + proxyType = 1; + remoteGlobalIDString = CEE723A62C3BD3D70009AE93; + remoteInfo = "SimpleX SE"; + }; + CEE723D12C3C21C90009AE93 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5CA059BE279559F40002BEB4 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5CE2BA672845308900EC33A6; + remoteInfo = SimpleXChat; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -261,6 +286,7 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( + CEE723B12C3BD3D70009AE93 /* SimpleX SE.appex in Embed App Extensions */, 5CE2BA9D284555F500EC33A6 /* SimpleX NSE.appex in Embed App Extensions */, ); name = "Embed App Extensions"; @@ -480,11 +506,6 @@ 649BCDA12805D6EF00C3A862 /* CIImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIImageView.swift; sourceTree = ""; }; 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextItemView.swift; sourceTree = ""; }; 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedItemView.swift; sourceTree = ""; }; - 64BAC4592C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU.a"; sourceTree = ""; }; - 64BAC45A2C495205008D3995 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; - 64BAC45B2C495205008D3995 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; - 64BAC45C2C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU-ghc9.6.3.a"; sourceTree = ""; }; - 64BAC45D2C495205008D3995 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; 64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemInfoView.swift; sourceTree = ""; }; 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTimePicker.swift; sourceTree = ""; }; 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressView.swift; sourceTree = ""; }; @@ -494,7 +515,6 @@ 64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIGroupInvitationView.swift; sourceTree = ""; }; 64EEB0F62C353F1C00972D62 /* ServersSummaryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServersSummaryView.swift; sourceTree = ""; }; 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncognitoHelp.swift; sourceTree = ""; }; - 8C05382D2B39887E006436DC /* VideoUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoUtils.swift; sourceTree = ""; }; 8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = ""; }; 8C74C3EB2C1B92A900039E77 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; 8C74C3ED2C1B942300039E77 /* ChatWallpaper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatWallpaper.swift; sourceTree = ""; }; @@ -509,12 +529,79 @@ 8C9BC2642C240D5100875A27 /* ThemeModeEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeModeEditor.swift; sourceTree = ""; }; 8CC4ED8F2BD7B8530078AEE8 /* CallAudioDeviceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallAudioDeviceManager.swift; sourceTree = ""; }; 8CC956ED2BC0041000412A11 /* NetworkObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkObserver.swift; sourceTree = ""; }; + CE1EB0E32C459A660099D896 /* ShareAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareAPI.swift; sourceTree = ""; }; + CE2AD9CD2C452A4D00E844E3 /* ChatUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatUtils.swift; sourceTree = ""; }; + CE3097FA2C4C0C9F00180898 /* ErrorAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorAlert.swift; sourceTree = ""; }; CE984D4A2C36C5D500E3AEFF /* ChatItemClipShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemClipShape.swift; sourceTree = ""; }; + CEDE70212C48FD9500233B1F /* SEChatState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SEChatState.swift; sourceTree = ""; }; + CEE723A72C3BD3D70009AE93 /* SimpleX SE.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "SimpleX SE.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + CEE723A92C3BD3D70009AE93 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = ""; }; + CEE723AE2C3BD3D70009AE93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CEE723D42C3C21F50009AE93 /* SimpleX SE.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "SimpleX SE.entitlements"; sourceTree = ""; }; + CEE723EF2C3D25C70009AE93 /* ShareView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareView.swift; sourceTree = ""; }; + CEE723F12C3D25ED0009AE93 /* ShareModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareModel.swift; sourceTree = ""; }; CEEA861C2C2ABCB50084E1EA /* ReverseList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReverseList.swift; sourceTree = ""; }; D72A9087294BD7A70047C86D /* NativeTextEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeTextEditor.swift; sourceTree = ""; }; D741547729AF89AF0022400A /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/StoreKit.framework; sourceTree = DEVELOPER_DIR; }; D741547929AF90B00022400A /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/PushKit.framework; sourceTree = DEVELOPER_DIR; }; D7AA2C3429A936B400737B40 /* MediaEncryption.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = MediaEncryption.playground; path = Shared/MediaEncryption.playground; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + E5DCF8DC2C5847EB007928CC /* libHSsimplex-chat-6.0.0.3-5yF58NJ20MV4vZ7jQKFAxB.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.0.0.3-5yF58NJ20MV4vZ7jQKFAxB.a"; sourceTree = ""; }; + E5DCF8DD2C5847EB007928CC /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; + E5DCF8DE2C5847EB007928CC /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; + E5DCF8DF2C5847EB007928CC /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; + E5DCF8E02C5847EB007928CC /* libHSsimplex-chat-6.0.0.3-5yF58NJ20MV4vZ7jQKFAxB-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.0.0.3-5yF58NJ20MV4vZ7jQKFAxB-ghc9.6.3.a"; sourceTree = ""; }; + E5DCF9702C590272007928CC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9722C590274007928CC /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9732C590275007928CC /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; + E5DCF9742C590276007928CC /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9752C590277007928CC /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9762C590278007928CC /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9772C590279007928CC /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9782C590279007928CC /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9792C59027A007928CC /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF97A2C59027A007928CC /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF97B2C59027B007928CC /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF97C2C59027B007928CC /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF97D2C59027C007928CC /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF97E2C59027C007928CC /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF97F2C59027D007928CC /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9802C59027D007928CC /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9812C59027D007928CC /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9832C5902CE007928CC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9852C5902D4007928CC /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9862C5902D5007928CC /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; + E5DCF9872C5902D8007928CC /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9882C5902DC007928CC /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9892C5902DC007928CC /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF98A2C5902DD007928CC /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF98B2C5902DD007928CC /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF98C2C5902DE007928CC /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF98D2C5902DE007928CC /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF98E2C5902E0007928CC /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF98F2C5902E0007928CC /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9902C5902E1007928CC /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9912C5902E1007928CC /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9922C5902E2007928CC /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9932C5902E2007928CC /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9942C5902E3007928CC /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = ""; }; + E5DCF9952C59067B007928CC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9972C5906FF007928CC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9992C59072A007928CC /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF99A2C59072B007928CC /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; + E5DCF99B2C59072B007928CC /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF99C2C59072C007928CC /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF99D2C59072D007928CC /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF99E2C59072E007928CC /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF99F2C59072E007928CC /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9A02C59072F007928CC /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9A12C59072F007928CC /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9A22C590730007928CC /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9A32C590730007928CC /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9A42C590731007928CC /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9A52C590731007928CC /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9A62C590731007928CC /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9A72C590732007928CC /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = ""; }; + E5DCF9A82C590732007928CC /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -554,14 +641,22 @@ buildActionMask = 2147483647; files = ( 5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */, - 64BAC4622C495205008D3995 /* libgmp.a in Frameworks */, - 64BAC45F2C495205008D3995 /* libffi.a in Frameworks */, 5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */, E50581062C3DDD9D009C3F71 /* Yams in Frameworks */, + E5DCF8E52C5847EB007928CC /* libHSsimplex-chat-6.0.0.3-5yF58NJ20MV4vZ7jQKFAxB-ghc9.6.3.a in Frameworks */, + E5DCF8E42C5847EB007928CC /* libffi.a in Frameworks */, + E5DCF8E12C5847EB007928CC /* libHSsimplex-chat-6.0.0.3-5yF58NJ20MV4vZ7jQKFAxB.a in Frameworks */, + E5DCF8E22C5847EB007928CC /* libgmp.a in Frameworks */, CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */, - 64BAC4612C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU-ghc9.6.3.a in Frameworks */, - 64BAC45E2C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU.a in Frameworks */, - 64BAC4602C495205008D3995 /* libgmpxx.a in Frameworks */, + E5DCF8E32C5847EB007928CC /* libgmpxx.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E5DCF8DA2C56FABA007928CC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E5DCF8DB2C56FAC1007928CC /* SimpleXChat.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -628,11 +723,11 @@ 5C764E5C279C70B7000C6508 /* Libraries */ = { isa = PBXGroup; children = ( - 64BAC45A2C495205008D3995 /* libffi.a */, - 64BAC45D2C495205008D3995 /* libgmp.a */, - 64BAC45B2C495205008D3995 /* libgmpxx.a */, - 64BAC45C2C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU-ghc9.6.3.a */, - 64BAC4592C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU.a */, + E5DCF8DF2C5847EB007928CC /* libffi.a */, + E5DCF8DD2C5847EB007928CC /* libgmp.a */, + E5DCF8DE2C5847EB007928CC /* libgmpxx.a */, + E5DCF8E02C5847EB007928CC /* libHSsimplex-chat-6.0.0.3-5yF58NJ20MV4vZ7jQKFAxB-ghc9.6.3.a */, + E5DCF8DC2C5847EB007928CC /* libHSsimplex-chat-6.0.0.3-5yF58NJ20MV4vZ7jQKFAxB.a */, ); path = Libraries; sourceTree = ""; @@ -684,7 +779,6 @@ 64466DCB29FFE3E800E3D48D /* MailView.swift */, 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */, 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */, - 8C05382D2B39887E006436DC /* VideoUtils.swift */, 8C7F8F0D2C19C0C100D16888 /* ViewModifiers.swift */, 8C74C3ED2C1B942300039E77 /* ChatWallpaper.swift */, 8C9BC2642C240D5100875A27 /* ThemeModeEditor.swift */, @@ -703,6 +797,7 @@ 5C764E5C279C70B7000C6508 /* Libraries */, 5CA059C2279559F40002BEB4 /* Shared */, 5CDCAD462818589900503DA2 /* SimpleX NSE */, + CEE723A82C3BD3D70009AE93 /* SimpleX SE */, 5CA059DA279559F40002BEB4 /* Tests iOS */, 5CE2BA692845308900EC33A6 /* SimpleXChat */, 5CA059CB279559F40002BEB4 /* Products */, @@ -733,6 +828,7 @@ 5CA059D7279559F40002BEB4 /* Tests iOS.xctest */, 5CDCAD452818589900503DA2 /* SimpleX NSE.appex */, 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */, + CEE723A72C3BD3D70009AE93 /* SimpleX SE.appex */, ); name = Products; sourceTree = ""; @@ -844,6 +940,7 @@ 5CDCAD472818589900503DA2 /* NotificationService.swift */, 5CDCAD492818589900503DA2 /* Info.plist */, 5CB0BA862826CB3A00B3292C /* InfoPlist.strings */, + E5DCF9822C5902CE007928CC /* Localizable.strings */, ); path = "SimpleX NSE"; sourceTree = ""; @@ -856,10 +953,12 @@ 5CDCAD7228188CFF00503DA2 /* ChatTypes.swift */, 5CDCAD7428188D2900503DA2 /* APITypes.swift */, 5C5E5D3C282447AB00B0488A /* CallTypes.swift */, + CE3097FA2C4C0C9F00180898 /* ErrorAlert.swift */, 5C9FD96A27A56D4D0075386C /* JSON.swift */, 5CDCAD7D2818941F00503DA2 /* API.swift */, 5CDCAD80281A7E2700503DA2 /* Notifications.swift */, 5CBD2859295711D700EC2CF4 /* ImageUtils.swift */, + CE2AD9CD2C452A4D00E844E3 /* ChatUtils.swift */, 64DAE1502809D9F5000DA960 /* FileUtils.swift */, 5C9D81182AA7A4F1001D49FD /* CryptoFile.swift */, 5C00168028C4FE760094D739 /* KeyChain.swift */, @@ -974,6 +1073,22 @@ path = Theme; sourceTree = ""; }; + CEE723A82C3BD3D70009AE93 /* SimpleX SE */ = { + isa = PBXGroup; + children = ( + CEE723D42C3C21F50009AE93 /* SimpleX SE.entitlements */, + CEE723AE2C3BD3D70009AE93 /* Info.plist */, + CEDE70212C48FD9500233B1F /* SEChatState.swift */, + CE1EB0E32C459A660099D896 /* ShareAPI.swift */, + CEE723F12C3D25ED0009AE93 /* ShareModel.swift */, + CEE723EF2C3D25C70009AE93 /* ShareView.swift */, + CEE723A92C3BD3D70009AE93 /* ShareViewController.swift */, + E5DCF96F2C590272007928CC /* Localizable.strings */, + E5DCF9962C5906FF007928CC /* InfoPlist.strings */, + ); + path = "SimpleX SE"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -1005,6 +1120,7 @@ dependencies = ( 5CE2BA6F2845308900EC33A6 /* PBXTargetDependency */, 5CE2BA9F284555F500EC33A6 /* PBXTargetDependency */, + CEE723B02C3BD3D70009AE93 /* PBXTargetDependency */, ); name = "SimpleX (iOS)"; packageProductDependencies = ( @@ -1076,6 +1192,24 @@ productReference = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; productType = "com.apple.product-type.framework"; }; + CEE723A62C3BD3D70009AE93 /* SimpleX SE */ = { + isa = PBXNativeTarget; + buildConfigurationList = CEE723B42C3BD3D70009AE93 /* Build configuration list for PBXNativeTarget "SimpleX SE" */; + buildPhases = ( + CEE723A32C3BD3D70009AE93 /* Sources */, + CEE723A52C3BD3D70009AE93 /* Resources */, + E5DCF8DA2C56FABA007928CC /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + CEE723D22C3C21C90009AE93 /* PBXTargetDependency */, + ); + name = "SimpleX SE"; + productName = "SimpleX SE"; + productReference = CEE723A72C3BD3D70009AE93 /* SimpleX SE.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -1083,7 +1217,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1330; + LastSwiftUpdateCheck = 1540; LastUpgradeCheck = 1340; ORGANIZATIONNAME = "SimpleX Chat"; TargetAttributes = { @@ -1103,6 +1237,9 @@ CreatedOnToolsVersion = 13.3; LastSwiftMigration = 1330; }; + CEE723A62C3BD3D70009AE93 = { + CreatedOnToolsVersion = 15.4; + }; }; }; buildConfigurationList = 5CA059C1279559F40002BEB4 /* Build configuration list for PBXProject "SimpleX" */; @@ -1144,6 +1281,7 @@ 5CA059C9279559F40002BEB4 /* SimpleX (iOS) */, 5CA059D6279559F40002BEB4 /* Tests iOS */, 5CDCAD442818589900503DA2 /* SimpleX NSE */, + CEE723A62C3BD3D70009AE93 /* SimpleX SE */, 5CE2BA672845308900EC33A6 /* SimpleXChat */, ); }; @@ -1173,6 +1311,7 @@ buildActionMask = 2147483647; files = ( 5CB0BA882826CB3A00B3292C /* InfoPlist.strings in Resources */, + E5DCF9842C5902CE007928CC /* Localizable.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1183,6 +1322,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + CEE723A52C3BD3D70009AE93 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E5DCF9712C590272007928CC /* Localizable.strings in Resources */, + E5DCF9982C5906FF007928CC /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -1299,7 +1447,6 @@ 5C55A91F283AD0E400C4E99E /* CallManager.swift in Sources */, 5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */, 649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */, - 8C05382E2B39887E006436DC /* VideoUtils.swift in Sources */, 5CADE79C292131E900072E13 /* ContactPreferencesView.swift in Sources */, 5CB346E52868AA7F001FD2EF /* SuspendChat.swift in Sources */, 5C9C2DA52894777E00CC63B1 /* GroupProfileView.swift in Sources */, @@ -1368,6 +1515,7 @@ buildActionMask = 2147483647; files = ( 5CF937202B24DE8C00E1D781 /* SharedFileSubscriber.swift in Sources */, + CE3097FB2C4C0C9F00180898 /* ErrorAlert.swift in Sources */, 5C00168128C4FE760094D739 /* KeyChain.swift in Sources */, 5CE2BA97284537A800EC33A6 /* dummy.m in Sources */, 5CE2BA922845340900EC33A6 /* FileUtils.swift in Sources */, @@ -1384,10 +1532,23 @@ 8C74C3E52C1B900600039E77 /* ThemeTypes.swift in Sources */, 5CE2BA8D284533A300EC33A6 /* CallTypes.swift in Sources */, 8C74C3E82C1B905B00039E77 /* ChatWallpaperTypes.swift in Sources */, + CE2AD9CE2C452A4D00E844E3 /* ChatUtils.swift in Sources */, 5CE2BA8E284533A300EC33A6 /* API.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; + CEE723A32C3BD3D70009AE93 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CEDE70222C48FD9500233B1F /* SEChatState.swift in Sources */, + CEE723F02C3D25C70009AE93 /* ShareView.swift in Sources */, + CE1EB0E42C459A660099D896 /* ShareAPI.swift in Sources */, + CEE723F22C3D25ED0009AE93 /* ShareModel.swift in Sources */, + CEE723AA2C3BD3D70009AE93 /* ShareViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -1412,6 +1573,16 @@ target = 5CE2BA672845308900EC33A6 /* SimpleXChat */; targetProxy = 5CE2BAA82845617C00EC33A6 /* PBXContainerItemProxy */; }; + CEE723B02C3BD3D70009AE93 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = CEE723A62C3BD3D70009AE93 /* SimpleX SE */; + targetProxy = CEE723AF2C3BD3D70009AE93 /* PBXContainerItemProxy */; + }; + CEE723D22C3C21C90009AE93 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5CE2BA672845308900EC33A6 /* SimpleXChat */; + targetProxy = CEE723D12C3C21C90009AE93 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -1434,6 +1605,7 @@ 5C5B67932ABAF56000DA9412 /* bg */, 5C245F3E2B501F13001CC39F /* tr */, 5C371E502BA9AB6400100AD3 /* hu */, + E5DCF9952C59067B007928CC /* en */, ); name = InfoPlist.strings; sourceTree = ""; @@ -1485,6 +1657,78 @@ name = "SimpleX--iOS--InfoPlist.strings"; sourceTree = ""; }; + E5DCF96F2C590272007928CC /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + E5DCF9702C590272007928CC /* en */, + E5DCF9722C590274007928CC /* bg */, + E5DCF9732C590275007928CC /* zh-Hans */, + E5DCF9742C590276007928CC /* nl */, + E5DCF9752C590277007928CC /* cs */, + E5DCF9762C590278007928CC /* fi */, + E5DCF9772C590279007928CC /* fr */, + E5DCF9782C590279007928CC /* de */, + E5DCF9792C59027A007928CC /* hu */, + E5DCF97A2C59027A007928CC /* it */, + E5DCF97B2C59027B007928CC /* ja */, + E5DCF97C2C59027B007928CC /* pl */, + E5DCF97D2C59027C007928CC /* ru */, + E5DCF97E2C59027C007928CC /* es */, + E5DCF97F2C59027D007928CC /* th */, + E5DCF9802C59027D007928CC /* uk */, + E5DCF9812C59027D007928CC /* tr */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + E5DCF9822C5902CE007928CC /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + E5DCF9832C5902CE007928CC /* en */, + E5DCF9852C5902D4007928CC /* bg */, + E5DCF9862C5902D5007928CC /* zh-Hans */, + E5DCF9872C5902D8007928CC /* cs */, + E5DCF9882C5902DC007928CC /* nl */, + E5DCF9892C5902DC007928CC /* fi */, + E5DCF98A2C5902DD007928CC /* de */, + E5DCF98B2C5902DD007928CC /* fr */, + E5DCF98C2C5902DE007928CC /* it */, + E5DCF98D2C5902DE007928CC /* hu */, + E5DCF98E2C5902E0007928CC /* ja */, + E5DCF98F2C5902E0007928CC /* pl */, + E5DCF9902C5902E1007928CC /* ru */, + E5DCF9912C5902E1007928CC /* es */, + E5DCF9922C5902E2007928CC /* th */, + E5DCF9932C5902E2007928CC /* tr */, + E5DCF9942C5902E3007928CC /* uk */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + E5DCF9962C5906FF007928CC /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + E5DCF9972C5906FF007928CC /* en */, + E5DCF9992C59072A007928CC /* bg */, + E5DCF99A2C59072B007928CC /* zh-Hans */, + E5DCF99B2C59072B007928CC /* cs */, + E5DCF99C2C59072C007928CC /* nl */, + E5DCF99D2C59072D007928CC /* fi */, + E5DCF99E2C59072E007928CC /* fr */, + E5DCF99F2C59072E007928CC /* de */, + E5DCF9A02C59072F007928CC /* hu */, + E5DCF9A12C59072F007928CC /* it */, + E5DCF9A22C590730007928CC /* ja */, + E5DCF9A32C590730007928CC /* pl */, + E5DCF9A42C590731007928CC /* ru */, + E5DCF9A52C590731007928CC /* es */, + E5DCF9A62C590731007928CC /* th */, + E5DCF9A72C590732007928CC /* tr */, + E5DCF9A82C590732007928CC /* uk */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ @@ -1618,7 +1862,7 @@ CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 228; + CURRENT_PROJECT_VERSION = 229; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; @@ -1667,7 +1911,7 @@ CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 228; + CURRENT_PROJECT_VERSION = 229; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; @@ -1753,7 +1997,7 @@ CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 228; + CURRENT_PROJECT_VERSION = 229; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; GCC_OPTIMIZATION_LEVEL = s; @@ -1790,7 +2034,7 @@ CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 228; + CURRENT_PROJECT_VERSION = 229; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; ENABLE_CODE_COVERAGE = NO; @@ -1827,7 +2071,7 @@ CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 228; + CURRENT_PROJECT_VERSION = 229; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1878,7 +2122,7 @@ CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 228; + CURRENT_PROJECT_VERSION = 229; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; DYLIB_COMPATIBILITY_VERSION = 1; @@ -1922,6 +2166,74 @@ }; name = Release; }; + CEE723B22C3BD3D70009AE93 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 5NN7GUYB6T; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "SimpleX SE/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "SimpleX SE"; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 SimpleX Chat. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + CEE723B32C3BD3D70009AE93 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 5NN7GUYB6T; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "SimpleX SE/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "SimpleX SE"; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 SimpleX Chat. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -1970,6 +2282,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + CEE723B42C3BD3D70009AE93 /* Build configuration list for PBXNativeTarget "SimpleX SE" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CEE723B22C3BD3D70009AE93 /* Debug */, + CEE723B32C3BD3D70009AE93 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ diff --git a/apps/ios/SimpleX.xcodeproj/xcshareddata/xcschemes/SimpleX SE.xcscheme b/apps/ios/SimpleX.xcodeproj/xcshareddata/xcschemes/SimpleX SE.xcscheme new file mode 100644 index 0000000000..a2639eb263 --- /dev/null +++ b/apps/ios/SimpleX.xcodeproj/xcshareddata/xcschemes/SimpleX SE.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/ios/SimpleXChat/API.swift b/apps/ios/SimpleXChat/API.swift index bc8a413e8f..987f7f3d41 100644 --- a/apps/ios/SimpleXChat/API.swift +++ b/apps/ios/SimpleXChat/API.swift @@ -117,10 +117,10 @@ public func sendSimpleXCmd(_ cmd: ChatCommand, _ ctrl: chat_ctrl? = nil) -> Chat } // in microseconds -let MESSAGE_TIMEOUT: Int32 = 15_000_000 +public let MESSAGE_TIMEOUT: Int32 = 15_000_000 -public func recvSimpleXMsg(_ ctrl: chat_ctrl? = nil) -> ChatResponse? { - if let cjson = chat_recv_msg_wait(ctrl ?? getChatCtrl(), MESSAGE_TIMEOUT) { +public func recvSimpleXMsg(_ ctrl: chat_ctrl? = nil, messageTimeout: Int32 = MESSAGE_TIMEOUT) -> ChatResponse? { + if let cjson = chat_recv_msg_wait(ctrl ?? getChatCtrl(), messageTimeout) { let s = fromCString(cjson) return s == "" ? nil : chatResponse(s) } diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index c1263f26e2..df95b5ebb6 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -27,6 +27,7 @@ public enum ChatCommand { case apiUnmuteUser(userId: Int64) case apiDeleteUser(userId: Int64, delSMPQueues: Bool, viewPwd: String?) case startChat(mainApp: Bool, enableSndFiles: Bool) + case checkChatRunning case apiStopChat case apiActivateChat(restoreChat: Bool) case apiSuspendChat(timeoutMicroseconds: Int) @@ -45,8 +46,8 @@ public enum ChatCommand { case apiSendMessage(type: ChatType, id: Int64, file: CryptoFile?, quotedItemId: Int64?, msg: MsgContent, live: Bool, ttl: Int?) case apiCreateChatItem(noteFolderId: Int64, file: CryptoFile?, msg: MsgContent) case apiUpdateChatItem(type: ChatType, id: Int64, itemId: Int64, msg: MsgContent, live: Bool) - case apiDeleteChatItem(type: ChatType, id: Int64, itemId: Int64, mode: CIDeleteMode) - case apiDeleteMemberChatItem(groupId: Int64, groupMemberId: Int64, itemId: Int64) + case apiDeleteChatItem(type: ChatType, id: Int64, itemIds: [Int64], mode: CIDeleteMode) + case apiDeleteMemberChatItem(groupId: Int64, itemIds: [Int64]) case apiChatItemReaction(type: ChatType, id: Int64, itemId: Int64, add: Bool, reaction: MsgReaction) case apiForwardChatItem(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemId: Int64, ttl: Int?) case apiGetNtfToken @@ -173,6 +174,7 @@ public enum ChatCommand { case let .apiUnmuteUser(userId): return "/_unmute user \(userId)" case let .apiDeleteUser(userId, delSMPQueues, viewPwd): return "/_delete user \(userId) del_smp=\(onOff(delSMPQueues))\(maybePwd(viewPwd))" case let .startChat(mainApp, enableSndFiles): return "/_start main=\(onOff(mainApp)) snd_files=\(onOff(enableSndFiles))" + case .checkChatRunning: return "/_check running" case .apiStopChat: return "/_stop" case let .apiActivateChat(restore): return "/_app activate restore=\(onOff(restore))" case let .apiSuspendChat(timeoutMicroseconds): return "/_app suspend \(timeoutMicroseconds)" @@ -197,8 +199,8 @@ public enum ChatCommand { let msg = encodeJSON(ComposedMessage(fileSource: file, msgContent: mc)) return "/_create *\(noteFolderId) json \(msg)" case let .apiUpdateChatItem(type, id, itemId, mc, live): return "/_update item \(ref(type, id)) \(itemId) live=\(onOff(live)) \(mc.cmdString)" - case let .apiDeleteChatItem(type, id, itemId, mode): return "/_delete item \(ref(type, id)) \(itemId) \(mode.rawValue)" - case let .apiDeleteMemberChatItem(groupId, groupMemberId, itemId): return "/_delete member item #\(groupId) \(groupMemberId) \(itemId)" + case let .apiDeleteChatItem(type, id, itemIds, mode): return "/_delete item \(ref(type, id)) \(itemIds.map({ "\($0)" }).joined(separator: ",")) \(mode.rawValue)" + case let .apiDeleteMemberChatItem(groupId, itemIds): return "/_delete member item #\(groupId) \(itemIds.map({ "\($0)" }).joined(separator: ","))" case let .apiChatItemReaction(type, id, itemId, add, reaction): return "/_reaction \(ref(type, id)) \(itemId) \(onOff(add)) \(encodeJSON(reaction))" case let .apiForwardChatItem(toChatType, toChatId, fromChatType, fromChatId, itemId, ttl): let ttlStr = ttl != nil ? "\(ttl!)" : "default" @@ -334,6 +336,7 @@ public enum ChatCommand { case .apiUnmuteUser: return "apiUnmuteUser" case .apiDeleteUser: return "apiDeleteUser" case .startChat: return "startChat" + case .checkChatRunning: return "checkChatRunning" case .apiStopChat: return "apiStopChat" case .apiActivateChat: return "apiActivateChat" case .apiSuspendChat: return "apiSuspendChat" @@ -595,7 +598,7 @@ public enum ChatResponse: Decodable, Error { case chatItemUpdated(user: UserRef, chatItem: AChatItem) case chatItemNotChanged(user: UserRef, chatItem: AChatItem) case chatItemReaction(user: UserRef, added: Bool, reaction: ACIReaction) - case chatItemDeleted(user: UserRef, deletedChatItem: AChatItem, toChatItem: AChatItem?, byUser: Bool) + case chatItemsDeleted(user: UserRef, chatItemDeletions: [ChatItemDeletion], byUser: Bool) case contactsList(user: UserRef, contacts: [Contact]) // group events case groupCreated(user: UserRef, groupInfo: GroupInfo) @@ -764,7 +767,7 @@ public enum ChatResponse: Decodable, Error { case .chatItemUpdated: return "chatItemUpdated" case .chatItemNotChanged: return "chatItemNotChanged" case .chatItemReaction: return "chatItemReaction" - case .chatItemDeleted: return "chatItemDeleted" + case .chatItemsDeleted: return "chatItemsDeleted" case .contactsList: return "contactsList" case .groupCreated: return "groupCreated" case .sentGroupInvitation: return "sentGroupInvitation" @@ -931,7 +934,10 @@ public enum ChatResponse: Decodable, Error { case let .chatItemUpdated(u, chatItem): return withUser(u, String(describing: chatItem)) case let .chatItemNotChanged(u, chatItem): return withUser(u, String(describing: chatItem)) case let .chatItemReaction(u, added, reaction): return withUser(u, "added: \(added)\n\(String(describing: reaction))") - case let .chatItemDeleted(u, deletedChatItem, toChatItem, byUser): return withUser(u, "deletedChatItem:\n\(String(describing: deletedChatItem))\ntoChatItem:\n\(String(describing: toChatItem))\nbyUser: \(byUser)") + case let .chatItemsDeleted(u, items, byUser): + let itemsString = items.map { item in + "deletedChatItem:\n\(String(describing: item.deletedChatItem))\ntoChatItem:\n\(String(describing: item.toChatItem))" }.joined(separator: "\n") + return withUser(u, itemsString + "\nbyUser: \(byUser)") case let .contactsList(u, contacts): return withUser(u, String(describing: contacts)) case let .groupCreated(u, groupInfo): return withUser(u, String(describing: groupInfo)) case let .sentGroupInvitation(u, groupInfo, contact, member): return withUser(u, "groupInfo: \(groupInfo)\ncontact: \(contact)\nmember: \(member)") diff --git a/apps/ios/SimpleXChat/AppGroup.swift b/apps/ios/SimpleXChat/AppGroup.swift index 90ac403999..6f9ad3b68e 100644 --- a/apps/ios/SimpleXChat/AppGroup.swift +++ b/apps/ios/SimpleXChat/AppGroup.swift @@ -13,6 +13,7 @@ public let appSuspendTimeout: Int = 15 // seconds let GROUP_DEFAULT_APP_STATE = "appState" let GROUP_DEFAULT_NSE_STATE = "nseState" +let GROUP_DEFAULT_SE_STATE = "seState" let GROUP_DEFAULT_DB_CONTAINER = "dbContainer" public let GROUP_DEFAULT_CHAT_LAST_START = "chatLastStart" public let GROUP_DEFAULT_CHAT_LAST_BACKGROUND_RUN = "chatLastBackgroundRun" @@ -136,6 +137,11 @@ public enum NSEState: String, Codable { } } +public enum SEState: String, Codable { + case inactive + case sendingMessage +} + public enum DBContainer: String { case documents case group @@ -155,6 +161,12 @@ public let nseStateGroupDefault = EnumDefault( withDefault: .suspended // so that NSE that was never launched does not delay the app from resuming ) +public let seStateGroupDefault = EnumDefault( + defaults: groupDefaults, + forKey: GROUP_DEFAULT_SE_STATE, + withDefault: .inactive +) + // inactive app states do not include "stopped" state public func allowBackgroundRefresh() -> Bool { appStateGroupDefault.get().inactive && nseStateGroupDefault.get().inactive diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index 3898d1e4ea..d173f2fad8 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -1463,7 +1463,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable { ) } -public struct ChatData: Decodable, Identifiable, Hashable { +public struct ChatData: Decodable, Identifiable, Hashable, ChatLike { public var chatInfo: ChatInfo public var chatItems: [ChatItem] public var chatStats: ChatStats @@ -2234,6 +2234,11 @@ public struct NtfMsgInfo: Decodable, Hashable { public var msgTs: Date } +public struct ChatItemDeletion: Decodable, Hashable { + public var deletedChatItem: AChatItem + public var toChatItem: AChatItem? = nil +} + public struct AChatItem: Decodable, Hashable { public var chatInfo: ChatInfo public var chatItem: ChatItem diff --git a/apps/ios/SimpleXChat/ChatUtils.swift b/apps/ios/SimpleXChat/ChatUtils.swift new file mode 100644 index 0000000000..a37b6babf7 --- /dev/null +++ b/apps/ios/SimpleXChat/ChatUtils.swift @@ -0,0 +1,62 @@ +// +// ChatUtils.swift +// SimpleXChat +// +// Created by Levitating Pineapple on 15/07/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import Foundation + +public protocol ChatLike { + var chatInfo: ChatInfo { get} + var chatItems: [ChatItem] { get } + var chatStats: ChatStats { get } +} + +public func filterChatsToForwardTo(chats: [C]) -> [C] { + var filteredChats = chats.filter { c in + c.chatInfo.chatType != .local && canForwardToChat(c.chatInfo) + } + if let privateNotes = chats.first(where: { $0.chatInfo.chatType == .local }) { + filteredChats.insert(privateNotes, at: 0) + } + return filteredChats +} + +public func foundChat(_ chat: ChatLike, _ searchStr: String) -> Bool { + let cInfo = chat.chatInfo + return switch cInfo { + case let .direct(contact): + viewNameContains(cInfo, searchStr) || + contact.profile.displayName.localizedLowercase.contains(searchStr) || + contact.fullName.localizedLowercase.contains(searchStr) + default: + viewNameContains(cInfo, searchStr) + } + + func viewNameContains(_ cInfo: ChatInfo, _ s: String) -> Bool { + cInfo.chatViewName.localizedLowercase.contains(s) + } +} + +private func canForwardToChat(_ cInfo: ChatInfo) -> Bool { + switch cInfo { + case let .direct(contact): contact.sendMsgEnabled && !contact.nextSendGrpInv + case let .group(groupInfo): groupInfo.sendMsgEnabled + case let .local(noteFolder): noteFolder.sendMsgEnabled + case .contactRequest: false + case .contactConnection: false + case .invalidJSON: false + } +} + +public func chatIconName(_ cInfo: ChatInfo) -> String { + switch cInfo { + case .direct: "person.crop.circle.fill" + case .group: "person.2.circle.fill" + case .local: "folder.circle.fill" + case .contactRequest: "person.crop.circle.fill" + default: "circle.fill" + } +} diff --git a/apps/ios/SimpleXChat/ErrorAlert.swift b/apps/ios/SimpleXChat/ErrorAlert.swift new file mode 100644 index 0000000000..65ed4c6717 --- /dev/null +++ b/apps/ios/SimpleXChat/ErrorAlert.swift @@ -0,0 +1,159 @@ +// +// ErrorAlert.swift +// SimpleXChat +// +// Created by Levitating Pineapple on 20/07/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI + +public struct ErrorAlert: Error { + public let title: LocalizedStringKey + public let message: LocalizedStringKey? + public let actions: Optional<() -> AnyView> + + public init( + title: LocalizedStringKey, + message: LocalizedStringKey? = nil + ) { + self.title = title + self.message = message + self.actions = nil + } + + public init( + title: LocalizedStringKey, + message: LocalizedStringKey? = nil, + @ViewBuilder actions: @escaping () -> A + ) { + self.title = title + self.message = message + self.actions = { AnyView(actions()) } + } + + public init(_ title: LocalizedStringKey) { + self = ErrorAlert(title: title) + } + + public init(_ error: any Error) { + self = if let chatResponse = error as? ChatResponse { + ErrorAlert(chatResponse) + } else { + ErrorAlert(LocalizedStringKey(error.localizedDescription)) + } + } + + public init(_ chatError: ChatError) { + self = ErrorAlert("\(chatErrorString(chatError))") + } + + public init(_ chatResponse: ChatResponse) { + self = if let networkErrorAlert = getNetworkErrorAlert(chatResponse) { + networkErrorAlert + } else { + ErrorAlert("\(responseError(chatResponse))") + } + } +} + +extension LocalizedStringKey: @unchecked Sendable { } + +extension View { + /// Bridges ``ErrorAlert`` to the generic alert API. + /// - Parameters: + /// - errorAlert: Binding to the Error, which is rendered in the alert + /// - actions: View Builder containing action buttons. + /// System defaults to `Ok` dismiss error action, when no actions are provided. + /// System implicitly adds `Cancel` action, if a destructive action is present + /// + /// - Returns: View, which displays ErrorAlert?, when set. + @ViewBuilder public func alert( + _ errorAlert: Binding, + @ViewBuilder actions: (ErrorAlert) -> A = { _ in EmptyView() } + ) -> some View { + if let alert = errorAlert.wrappedValue { + self.alert( + alert.title, + isPresented: Binding( + get: { errorAlert.wrappedValue != nil }, + set: { if !$0 { errorAlert.wrappedValue = nil } } + ), + actions: { + if let actions_ = alert.actions { + actions_() + } else { + actions(alert) + } + }, + message: { + if let message = alert.message { Text(message) } + } + ) + } else { self } + } +} + +public func getNetworkErrorAlert(_ r: ChatResponse) -> ErrorAlert? { + switch r { + case let .chatCmdError(_, .errorAgent(.BROKER(addr, .TIMEOUT))): + return ErrorAlert(title: "Connection timeout", message: "Please check your network connection with \(serverHostname(addr)) and try again.") + case let .chatCmdError(_, .errorAgent(.BROKER(addr, .NETWORK))): + return ErrorAlert(title: "Connection error", message: "Please check your network connection with \(serverHostname(addr)) and try again.") + case let .chatCmdError(_, .errorAgent(.BROKER(addr, .HOST))): + return ErrorAlert(title: "Connection error", message: "Server address is incompatible with network settings: \(serverHostname(addr)).") + case let .chatCmdError(_, .errorAgent(.BROKER(addr, .TRANSPORT(.version)))): + return ErrorAlert(title: "Connection error", message: "Server version is incompatible with your app: \(serverHostname(addr)).") + case let .chatCmdError(_, .errorAgent(.SMP(serverAddress, .PROXY(proxyErr)))): + return smpProxyErrorAlert(proxyErr, serverAddress) + case let .chatCmdError(_, .errorAgent(.PROXY(proxyServer, relayServer, .protocolError(.PROXY(proxyErr))))): + return proxyDestinationErrorAlert(proxyErr, proxyServer, relayServer) + default: + return nil + } +} + +private func smpProxyErrorAlert(_ proxyErr: ProxyError, _ srvAddr: String) -> ErrorAlert? { + switch proxyErr { + case .BROKER(brokerErr: .TIMEOUT): + return ErrorAlert(title: "Private routing error", message: "Error connecting to forwarding server \(serverHostname(srvAddr)). Please try later.") + case .BROKER(brokerErr: .NETWORK): + return ErrorAlert(title: "Private routing error", message: "Error connecting to forwarding server \(serverHostname(srvAddr)). Please try later.") + case .BROKER(brokerErr: .HOST): + return ErrorAlert(title: "Private routing error", message: "Forwarding server address is incompatible with network settings: \(serverHostname(srvAddr)).") + case .BROKER(brokerErr: .TRANSPORT(.version)): + return ErrorAlert(title: "Private routing error", message: "Forwarding server version is incompatible with network settings: \(serverHostname(srvAddr)).") + default: + return nil + } +} + +private func proxyDestinationErrorAlert(_ proxyErr: ProxyError, _ proxyServer: String, _ relayServer: String) -> ErrorAlert? { + switch proxyErr { + case .BROKER(brokerErr: .TIMEOUT): + return ErrorAlert(title: "Private routing error", message: "Forwarding server \(serverHostname(proxyServer)) failed to connect to destination server \(serverHostname(relayServer)). Please try later.") + case .BROKER(brokerErr: .NETWORK): + return ErrorAlert(title: "Private routing error", message: "Forwarding server \(serverHostname(proxyServer)) failed to connect to destination server \(serverHostname(relayServer)). Please try later.") + case .NO_SESSION: + return ErrorAlert(title: "Private routing error", message: "Forwarding server \(serverHostname(proxyServer)) failed to connect to destination server \(serverHostname(relayServer)). Please try later.") + case .BROKER(brokerErr: .HOST): + return ErrorAlert(title: "Private routing error", message: "Destination server address of \(serverHostname(relayServer)) is incompatible with forwarding server \(serverHostname(proxyServer)) settings.") + case .BROKER(brokerErr: .TRANSPORT(.version)): + return ErrorAlert(title: "Private routing error", message: "Destination server version of \(serverHostname(relayServer)) is incompatible with forwarding server \(serverHostname(proxyServer)).") + default: + return nil + } +} + +public func serverHostname(_ srv: String) -> String { + parseServerAddress(srv)?.hostnames.first ?? srv +} + +public func mtrErrorDescription(_ err: MTRError) -> LocalizedStringKey { + switch err { + case let .noDown(dbMigrations): + "database version is newer than the app, but no down migration for: \(dbMigrations.joined(separator: ", "))" + case let .different(appMigration, dbMigration): + "different migration in the app/database: \(appMigration) / \(dbMigration)" + } +} diff --git a/apps/ios/SimpleXChat/ImageUtils.swift b/apps/ios/SimpleXChat/ImageUtils.swift index fd6d951f48..c387c84aaa 100644 --- a/apps/ios/SimpleXChat/ImageUtils.swift +++ b/apps/ios/SimpleXChat/ImageUtils.swift @@ -10,6 +10,7 @@ import Foundation import SwiftUI import AVKit import SwiftyGif +import LinkPresentation public func getLoadedFileSource(_ file: CIFile?) -> CryptoFile? { if let file = file, file.loaded { @@ -158,6 +159,34 @@ public func imageHasAlpha(_ img: UIImage) -> Bool { return false } +/// Reduces image size, while consuming less RAM +/// +/// Used by ShareExtension to downsize large images +/// before passing them to regular image processing pipeline +/// to avoid exceeding 120MB memory +/// +/// - Parameters: +/// - url: Location of the image data +/// - size: Maximum dimension (width or height) +/// - Returns: Downsampled image or `nil`, if the image can't be located +public func downsampleImage(at url: URL, to size: Int64) -> UIImage? { + autoreleasepool { + if let source = CGImageSourceCreateWithURL(url as CFURL, nil) { + CGImageSourceCreateThumbnailAtIndex( + source, + Int.zero, + [ + kCGImageSourceCreateThumbnailFromImageAlways: true, + kCGImageSourceShouldCacheImmediately: true, + kCGImageSourceCreateThumbnailWithTransform: true, + kCGImageSourceThumbnailMaxPixelSize: String(size) as CFString + ] as CFDictionary + ) + .map { UIImage(cgImage: $0) } + } else { nil } + } +} + public func saveFileFromURL(_ url: URL) -> CryptoFile? { let encrypted = privacyEncryptLocalFilesGroupDefault.get() let savedFile: CryptoFile? @@ -281,6 +310,21 @@ private func dropPrefix(_ s: String, _ prefix: String) -> String { s.hasPrefix(prefix) ? String(s.dropFirst(prefix.count)) : s } +public func makeVideoQualityLower(_ input: URL, outputUrl: URL) async -> Bool { + let asset: AVURLAsset = AVURLAsset(url: input, options: nil) + if let s = AVAssetExportSession(asset: asset, presetName: AVAssetExportPreset640x480) { + s.outputURL = outputUrl + s.outputFileType = .mp4 + s.metadataItemFilter = AVMetadataItemFilter.forSharing() + await s.export() + if let err = s.error { + logger.error("Failed to export video with error: \(err)") + } + return s.status == .completed + } + return false +} + extension AVAsset { public func generatePreview() -> (UIImage, Int)? { let generator = AVAssetImageGenerator(asset: self) @@ -348,3 +392,39 @@ extension UIImage { } } } + +public func getLinkPreview(url: URL, cb: @escaping (LinkPreview?) -> Void) { + logger.debug("getLinkMetadata: fetching URL preview") + LPMetadataProvider().startFetchingMetadata(for: url){ metadata, error in + if let e = error { + logger.error("Error retrieving link metadata: \(e.localizedDescription)") + } + if let metadata = metadata, + let imageProvider = metadata.imageProvider, + imageProvider.canLoadObject(ofClass: UIImage.self) { + imageProvider.loadObject(ofClass: UIImage.self){ object, error in + var linkPreview: LinkPreview? = nil + if let error = error { + logger.error("Couldn't load image preview from link metadata with error: \(error.localizedDescription)") + } else { + if let image = object as? UIImage, + let resized = resizeImageToStrSize(image, maxDataSize: 14000), + let title = metadata.title, + let uri = metadata.originalURL { + linkPreview = LinkPreview(uri: uri, title: title, image: resized) + } + } + cb(linkPreview) + } + } else { + logger.error("Could not load link preview image") + cb(nil) + } + } +} + +public func getLinkPreview(for url: URL) async -> LinkPreview? { + await withCheckedContinuation { cont in + getLinkPreview(url: url) { cont.resume(returning: $0) } + } +} diff --git a/apps/ios/SimpleXChat/SharedFileSubscriber.swift b/apps/ios/SimpleXChat/SharedFileSubscriber.swift index f496e6999e..bf5997f40b 100644 --- a/apps/ios/SimpleXChat/SharedFileSubscriber.swift +++ b/apps/ios/SimpleXChat/SharedFileSubscriber.swift @@ -12,6 +12,8 @@ public typealias AppSubscriber = SharedFileSubscriber> +public typealias SESubscriber = SharedFileSubscriber> + public class SharedFileSubscriber: NSObject, NSFilePresenter { var fileURL: URL public var presentedItemURL: URL? @@ -57,6 +59,8 @@ let appMessagesSharedFile = getGroupContainerDirectory().appendingPathComponent( let nseMessagesSharedFile = getGroupContainerDirectory().appendingPathComponent("chat.simplex.app.SimpleX-NSE.messages", isDirectory: false) +let seMessagesSharedFile = getGroupContainerDirectory().appendingPathComponent("chat.simplex.app.SimpleX-SE.messages", isDirectory: false) + public struct ProcessMessage: Codable { var createdAt: Date = Date.now var message: Message @@ -70,6 +74,10 @@ public enum NSEProcessMessage: Codable { case state(state: NSEState) } +public enum SEProcessMessage: Codable { + case state(state: SEState) +} + public func sendAppProcessMessage(_ message: AppProcessMessage) { SharedFileSubscriber.notify(url: appMessagesSharedFile, message: ProcessMessage(message: message)) } @@ -78,6 +86,10 @@ public func sendNSEProcessMessage(_ message: NSEProcessMessage) { SharedFileSubscriber.notify(url: nseMessagesSharedFile, message: ProcessMessage(message: message)) } +public func sendSEProcessMessage(_ message: SEProcessMessage) { + SharedFileSubscriber.notify(url: seMessagesSharedFile, message: ProcessMessage(message: message)) +} + public func appMessageSubscriber(onMessage: @escaping (AppProcessMessage) -> Void) -> AppSubscriber { SharedFileSubscriber(fileURL: appMessagesSharedFile) { (msg: ProcessMessage) in onMessage(msg.message) @@ -90,6 +102,12 @@ public func nseMessageSubscriber(onMessage: @escaping (NSEProcessMessage) -> Voi } } +public func seMessageSubscriber(onMessage: @escaping (SEProcessMessage) -> Void) -> SESubscriber { + SharedFileSubscriber(fileURL: seMessagesSharedFile) { (msg: ProcessMessage) in + onMessage(msg.message) + } +} + public func sendAppState(_ state: AppState) { sendAppProcessMessage(.state(state: state)) } @@ -97,3 +115,7 @@ public func sendAppState(_ state: AppState) { public func sendNSEState(_ state: NSEState) { sendNSEProcessMessage(.state(state: state)) } + +public func sendSEState(_ state: SEState) { + sendSEProcessMessage(.state(state: state)) +} diff --git a/apps/ios/SimpleXChat/hs_init.c b/apps/ios/SimpleXChat/hs_init.c index adacd57310..4731e7b829 100644 --- a/apps/ios/SimpleXChat/hs_init.c +++ b/apps/ios/SimpleXChat/hs_init.c @@ -39,3 +39,19 @@ void haskell_init_nse(void) { char **pargv = argv; hs_init_with_rtsopts(&argc, &pargv); } + +void haskell_init_se(void) { + int argc = 7; + char *argv[] = { + "simplex", + "+RTS", // requires `hs_init_with_rtsopts` + "-A1m", // chunk size for new allocations + "-H1m", // initial heap size + "-F0.5", // heap growth triggering GC + "-Fd1", // memory return + "-c", // compacting garbage collector + 0 + }; + char **pargv = argv; + hs_init_with_rtsopts(&argc, &pargv); +} diff --git a/apps/ios/SimpleXChat/hs_init.h b/apps/ios/SimpleXChat/hs_init.h index a732fd7113..40be4fc263 100644 --- a/apps/ios/SimpleXChat/hs_init.h +++ b/apps/ios/SimpleXChat/hs_init.h @@ -13,4 +13,6 @@ void haskell_init(void); void haskell_init_nse(void); +void haskell_init_se(void); + #endif /* hs_init_h */ diff --git a/apps/ios/bg.lproj/Localizable.strings b/apps/ios/bg.lproj/Localizable.strings index 142baa5cbe..1e7b7a6314 100644 --- a/apps/ios/bg.lproj/Localizable.strings +++ b/apps/ios/bg.lproj/Localizable.strings @@ -641,7 +641,7 @@ /* rcv group event chat item */ "blocked %@" = "блокиран %@"; -/* marked deleted chat item preview text */ +/* blocked chat item */ "blocked by admin" = "блокиран от админ"; /* No comment provided by engineer. */ @@ -2691,7 +2691,7 @@ time to disappear */ "off" = "изключено"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Изключено"; /* feature offered item */ diff --git a/apps/ios/cs.lproj/Localizable.strings b/apps/ios/cs.lproj/Localizable.strings index 1a83061c8c..c7f916408f 100644 --- a/apps/ios/cs.lproj/Localizable.strings +++ b/apps/ios/cs.lproj/Localizable.strings @@ -2190,7 +2190,7 @@ time to disappear */ "off" = "vypnuto"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Vypnout"; /* feature offered item */ diff --git a/apps/ios/de.lproj/Localizable.strings b/apps/ios/de.lproj/Localizable.strings index d46f3243d4..b410f63f4c 100644 --- a/apps/ios/de.lproj/Localizable.strings +++ b/apps/ios/de.lproj/Localizable.strings @@ -359,6 +359,9 @@ /* No comment provided by engineer. */ "Acknowledgement errors" = "Fehler bei der Bestätigung"; +/* No comment provided by engineer. */ +"Active connections" = "Aktive Verbindungen"; + /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Fügen Sie die Adresse Ihrem Profil hinzu, damit Ihre Kontakte sie mit anderen Personen teilen können. Es wird eine Profilaktualisierung an Ihre Kontakte gesendet."; @@ -426,13 +429,13 @@ "All app data is deleted." = "Werden die App-Daten komplett gelöscht."; /* No comment provided by engineer. */ -"All chats and messages will be deleted - this cannot be undone!" = "Alle Chats und Nachrichten werden gelöscht. Dies kann nicht rückgängig gemacht werden!"; +"All chats and messages will be deleted - this cannot be undone!" = "Es werden alle Chats und Nachrichten gelöscht. Dies kann nicht rückgängig gemacht werden!"; /* No comment provided by engineer. */ "All data is erased when it is entered." = "Alle Daten werden gelöscht, sobald dieser eingegeben wird."; /* No comment provided by engineer. */ -"All data is private to your device." = "Alle Daten sind auf Ihrem Gerät geschützt."; +"All data is private to your device." = "Alle Daten werden nur auf Ihrem Gerät gespeichert."; /* No comment provided by engineer. */ "All group members will remain connected." = "Alle Gruppenmitglieder bleiben verbunden."; @@ -444,7 +447,7 @@ "All messages will be deleted - this cannot be undone!" = "Es werden alle Nachrichten gelöscht. Dies kann nicht rückgängig gemacht werden!"; /* No comment provided by engineer. */ -"All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you." = "Alle Nachrichten werden gelöscht . Dies kann nicht rückgängig gemacht werden! Die Nachrichten werden NUR bei Ihnen gelöscht."; +"All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you." = "Es werden alle Nachrichten gelöscht. Dies kann nicht rückgängig gemacht werden! Die Nachrichten werden NUR bei Ihnen gelöscht."; /* No comment provided by engineer. */ "All new messages from %@ will be hidden!" = "Von %@ werden alle neuen Nachrichten ausgeblendet!"; @@ -686,7 +689,7 @@ /* rcv group event chat item */ "blocked %@" = "%@ wurde blockiert"; -/* marked deleted chat item preview text */ +/* blocked chat item */ "blocked by admin" = "wurde vom Administrator blockiert"; /* No comment provided by engineer. */ @@ -909,6 +912,9 @@ /* No comment provided by engineer. */ "Configure ICE servers" = "ICE-Server konfigurieren"; +/* No comment provided by engineer. */ +"Configured %@ servers" = "Konfigurierte %@ Server"; + /* No comment provided by engineer. */ "Confirm" = "Bestätigen"; @@ -2354,10 +2360,10 @@ "Incoming video call" = "Eingehender Videoanruf"; /* No comment provided by engineer. */ -"Incompatible database version" = "Inkompatible Datenbank-Version"; +"Incompatible database version" = "Datenbank-Version nicht kompatibel"; /* No comment provided by engineer. */ -"Incompatible version" = "Inkompatible Version"; +"Incompatible version" = "Version nicht kompatibel"; /* PIN entry */ "Incorrect passcode" = "Zugangscode ist falsch"; @@ -2689,6 +2695,9 @@ /* notification */ "message received" = "Nachricht empfangen"; +/* No comment provided by engineer. */ +"Message reception" = "Nachrichtenempfang"; + /* No comment provided by engineer. */ "Message routing fallback" = "Fallback für das Nachrichten-Routing"; @@ -2940,7 +2949,7 @@ time to disappear */ "off" = "Aus"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Aus"; /* feature offered item */ @@ -3066,6 +3075,9 @@ /* No comment provided by engineer. */ "Other" = "Andere"; +/* No comment provided by engineer. */ +"Other %@ servers" = "Andere %@ Server"; + /* No comment provided by engineer. */ "other errors" = "Andere Fehler"; @@ -3219,6 +3231,9 @@ /* No comment provided by engineer. */ "Private routing" = "Privates Routing"; +/* No comment provided by engineer. */ +"Private routing error" = "Fehler beim privaten Routing"; + /* No comment provided by engineer. */ "Profile and server connections" = "Profil und Serververbindungen"; @@ -3289,7 +3304,7 @@ "Protocol timeout per KB" = "Protokollzeitüberschreitung pro kB"; /* No comment provided by engineer. */ -"Proxied" = "Proxy"; +"Proxied" = "Proxied"; /* No comment provided by engineer. */ "Proxied servers" = "Proxy-Server"; @@ -3819,8 +3834,11 @@ /* No comment provided by engineer. */ "Server address" = "Server-Adresse"; +/* No comment provided by engineer. */ +"Server address is incompatible with network settings: %@." = "Die Server-Adresse ist nicht mit den Netzwerkeinstellungen kompatibel: %@."; + /* srv error text. */ -"Server address is incompatible with network settings." = "Die Server-Adresse ist nicht mit den Netzwerk-Einstellungen kompatibel."; +"Server address is incompatible with network settings." = "Die Server-Adresse ist nicht mit den Netzwerkeinstellungen kompatibel."; /* queue info */ "server queue info: %@\n\nlast received msg: %@" = "Server-Warteschlangen-Information: %1$@\n\nZuletzt empfangene Nachricht: %2$@"; @@ -3838,7 +3856,10 @@ "Server type" = "Server-Typ"; /* srv error text */ -"Server version is incompatible with network settings." = "Die Server-Version ist nicht mit den Netzwerk-Einstellungen kompatibel."; +"Server version is incompatible with network settings." = "Die Server-Version ist nicht mit den Netzwerkeinstellungen kompatibel."; + +/* No comment provided by engineer. */ +"Server version is incompatible with your app: %@." = "Die Server-Version ist nicht mit Ihrer App kompatibel: %@."; /* No comment provided by engineer. */ "Servers" = "Server"; @@ -3930,6 +3951,9 @@ /* No comment provided by engineer. */ "Show message status" = "Nachrichtenstatus anzeigen"; +/* No comment provided by engineer. */ +"Show percentage" = "Prozentualen Anteil anzeigen"; + /* No comment provided by engineer. */ "Show preview" = "Vorschau anzeigen"; @@ -4228,10 +4252,10 @@ "They can be overridden in contact and group settings." = "Sie können in den Kontakteinstellungen überschrieben werden."; /* No comment provided by engineer. */ -"This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Diese Aktion kann nicht rückgängig gemacht werden! Alle empfangenen und gesendeten Dateien und Medien werden gelöscht. Bilder mit niedriger Auflösung bleiben erhalten."; +"This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Diese Aktion kann nicht rückgängig gemacht werden! Es werden alle empfangenen und gesendeten Dateien und Medien gelöscht. Bilder mit niedriger Auflösung bleiben erhalten."; /* No comment provided by engineer. */ -"This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes." = "Diese Aktion kann nicht rückgängig gemacht werden! Alle empfangenen und gesendeten Nachrichten, die über den ausgewählten Zeitraum hinaus gehen, werden gelöscht. Dieser Vorgang kann mehrere Minuten dauern."; +"This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes." = "Diese Aktion kann nicht rückgängig gemacht werden! Es werden alle empfangenen und gesendeten Nachrichten, die über den ausgewählten Zeitraum hinaus gehen, gelöscht. Dieser Vorgang kann mehrere Minuten dauern."; /* No comment provided by engineer. */ "This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." = "Diese Aktion kann nicht rückgängig gemacht werden! Ihr Profil und Ihre Kontakte, Nachrichten und Dateien gehen unwiderruflich verloren."; diff --git a/apps/ios/es.lproj/Localizable.strings b/apps/ios/es.lproj/Localizable.strings index 1a8835c976..a84f8c0433 100644 --- a/apps/ios/es.lproj/Localizable.strings +++ b/apps/ios/es.lproj/Localizable.strings @@ -334,6 +334,9 @@ /* No comment provided by engineer. */ "above, then choose:" = "y después elige:"; +/* No comment provided by engineer. */ +"Accent" = "Color"; + /* accept contact request via notification accept incoming call via notification */ "Accept" = "Aceptar"; @@ -350,6 +353,15 @@ /* call status */ "accepted call" = "llamada aceptada"; +/* No comment provided by engineer. */ +"Acknowledged" = "Recibido"; + +/* No comment provided by engineer. */ +"Acknowledgement errors" = "Errores de reconocimiento"; + +/* No comment provided by engineer. */ +"Active connections" = "Conexiones activas"; + /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Añade la dirección a tu perfil para que tus contactos puedan compartirla con otros. La actualización del perfil se enviará a tus contactos."; @@ -374,6 +386,15 @@ /* No comment provided by engineer. */ "Add welcome message" = "Añadir mensaje de bienvenida"; +/* No comment provided by engineer. */ +"Additional accent" = "Acento adicional"; + +/* No comment provided by engineer. */ +"Additional accent 2" = "Color adicional 2"; + +/* No comment provided by engineer. */ +"Additional secondary" = "Secundario adicional"; + /* No comment provided by engineer. */ "Address" = "Dirección"; @@ -395,6 +416,9 @@ /* No comment provided by engineer. */ "Advanced network settings" = "Configuración avanzada de red"; +/* No comment provided by engineer. */ +"Advanced settings" = "Configuración avanzada"; + /* chat item text */ "agreeing encryption for %@…" = "acordando cifrado para %@…"; @@ -410,6 +434,9 @@ /* No comment provided by engineer. */ "All data is erased when it is entered." = "Al introducirlo todos los datos son eliminados."; +/* No comment provided by engineer. */ +"All data is private to your device." = "Todos los datos son privados para tu dispositivo."; + /* No comment provided by engineer. */ "All group members will remain connected." = "Todos los miembros del grupo permanecerán conectados."; @@ -425,6 +452,9 @@ /* No comment provided by engineer. */ "All new messages from %@ will be hidden!" = "¡Los mensajes nuevos de %@ estarán ocultos!"; +/* No comment provided by engineer. */ +"All profiles" = "Todos los perfiles"; + /* No comment provided by engineer. */ "All your contacts will remain connected." = "Todos tus contactos permanecerán conectados."; @@ -551,6 +581,9 @@ /* No comment provided by engineer. */ "Apply" = "Aplicar"; +/* No comment provided by engineer. */ +"Apply to" = "Aplicar a"; + /* No comment provided by engineer. */ "Archive and upload" = "Archivar y subir"; @@ -560,6 +593,9 @@ /* No comment provided by engineer. */ "Attach" = "Adjuntar"; +/* No comment provided by engineer. */ +"attempts" = "intentos"; + /* No comment provided by engineer. */ "Audio & video calls" = "Llamadas y videollamadas"; @@ -602,6 +638,9 @@ /* No comment provided by engineer. */ "Back" = "Volver"; +/* No comment provided by engineer. */ +"Background" = "Fondo"; + /* No comment provided by engineer. */ "Bad desktop address" = "Dirección ordenador incorrecta"; @@ -623,6 +662,9 @@ /* No comment provided by engineer. */ "Better messages" = "Mensajes mejorados"; +/* No comment provided by engineer. */ +"Black" = "Negro"; + /* No comment provided by engineer. */ "Block" = "Bloquear"; @@ -647,7 +689,7 @@ /* rcv group event chat item */ "blocked %@" = "ha bloqueado a %@"; -/* marked deleted chat item preview text */ +/* blocked chat item */ "blocked by admin" = "bloqueado por administrador"; /* No comment provided by engineer. */ @@ -713,6 +755,9 @@ /* No comment provided by engineer. */ "Cannot access keychain to save database password" = "Keychain inaccesible para guardar la contraseña de la base de datos"; +/* No comment provided by engineer. */ +"Cannot forward message" = "No se puede reenviar el mensaje"; + /* No comment provided by engineer. */ "Cannot receive file" = "No se puede recibir el archivo"; @@ -771,6 +816,9 @@ /* No comment provided by engineer. */ "Chat archive" = "Archivo del chat"; +/* No comment provided by engineer. */ +"Chat colors" = "Colores del chat"; + /* No comment provided by engineer. */ "Chat console" = "Consola de Chat"; @@ -798,6 +846,9 @@ /* No comment provided by engineer. */ "Chat preferences" = "Preferencias de Chat"; +/* No comment provided by engineer. */ +"Chat theme" = "Tema de chat"; + /* No comment provided by engineer. */ "Chats" = "Chats"; @@ -816,6 +867,15 @@ /* No comment provided by engineer. */ "Choose from library" = "Elige de la biblioteca"; +/* No comment provided by engineer. */ +"Chunks deleted" = "Bloques eliminados"; + +/* No comment provided by engineer. */ +"Chunks downloaded" = "Bloques descargados"; + +/* No comment provided by engineer. */ +"Chunks uploaded" = "Bloques subidos"; + /* No comment provided by engineer. */ "Clear" = "Vaciar"; @@ -831,6 +891,9 @@ /* No comment provided by engineer. */ "Clear verification" = "Eliminar verificación"; +/* No comment provided by engineer. */ +"Color mode" = "Modo de color"; + /* No comment provided by engineer. */ "colored" = "coloreado"; @@ -843,9 +906,15 @@ /* No comment provided by engineer. */ "complete" = "completado"; +/* No comment provided by engineer. */ +"Completed" = "Completado"; + /* No comment provided by engineer. */ "Configure ICE servers" = "Configure servidores ICE"; +/* No comment provided by engineer. */ +"Configured %@ servers" = "%@ servidores configurados"; + /* No comment provided by engineer. */ "Confirm" = "Confirmar"; @@ -912,18 +981,27 @@ /* No comment provided by engineer. */ "connected" = "conectado"; +/* No comment provided by engineer. */ +"Connected" = "Conectado"; + /* No comment provided by engineer. */ "Connected desktop" = "Ordenador conectado"; /* rcv group event chat item */ "connected directly" = "conectado directamente"; +/* No comment provided by engineer. */ +"Connected servers" = "Servidores conectados"; + /* No comment provided by engineer. */ "Connected to desktop" = "Conectado con ordenador"; /* No comment provided by engineer. */ "connecting" = "conectando"; +/* No comment provided by engineer. */ +"Connecting" = "Conectando"; + /* No comment provided by engineer. */ "connecting (accepted)" = "conectando (aceptado)"; @@ -972,9 +1050,15 @@ /* No comment provided by engineer. */ "Connection timeout" = "Tiempo de conexión agotado"; +/* No comment provided by engineer. */ +"Connection with desktop stopped" = "La conexión con el escritorio (desktop) se ha parado"; + /* connection information */ "connection:%@" = "conexión: % @"; +/* No comment provided by engineer. */ +"Connections" = "Conexiones"; + /* profile update event chat item */ "contact %@ changed to %@" = "el contacto %1$@ ha cambiado a %2$@"; @@ -1017,6 +1101,9 @@ /* No comment provided by engineer. */ "Copy" = "Copiar"; +/* No comment provided by engineer. */ +"Copy error" = "Copiar error"; + /* No comment provided by engineer. */ "Core version: v%@" = "Versión Core: v%@"; @@ -1062,6 +1149,9 @@ /* No comment provided by engineer. */ "Create your profile" = "Crea tu perfil"; +/* No comment provided by engineer. */ +"Created" = "Creado"; + /* No comment provided by engineer. */ "Created at" = "Creado"; @@ -1086,6 +1176,9 @@ /* No comment provided by engineer. */ "Current passphrase…" = "Contraseña actual…"; +/* No comment provided by engineer. */ +"Current profile" = "Perfil actual"; + /* No comment provided by engineer. */ "Currently maximum supported file size is %@." = "El tamaño máximo de archivo admitido es %@."; @@ -1095,9 +1188,15 @@ /* No comment provided by engineer. */ "Custom time" = "Tiempo personalizado"; +/* No comment provided by engineer. */ +"Customize theme" = "Personalizar tema"; + /* No comment provided by engineer. */ "Dark" = "Oscuro"; +/* No comment provided by engineer. */ +"Dark mode colors" = "Colores en modo oscuro"; + /* No comment provided by engineer. */ "Database downgrade" = "Degradación de la base de datos"; @@ -1167,6 +1266,9 @@ /* message decrypt error item */ "Decryption error" = "Error descifrado"; +/* No comment provided by engineer. */ +"decryption errors" = "errores de descifrado"; + /* pref value */ "default (%@)" = "predeterminado (%@)"; @@ -1293,6 +1395,9 @@ /* deleted chat item */ "deleted" = "eliminado"; +/* No comment provided by engineer. */ +"Deleted" = "Eliminado"; + /* No comment provided by engineer. */ "Deleted at" = "Eliminado"; @@ -1305,6 +1410,9 @@ /* rcv group event chat item */ "deleted group" = "grupo eliminado"; +/* No comment provided by engineer. */ +"Deletion errors" = "Errores de borrado"; + /* No comment provided by engineer. */ "Delivery" = "Entrega"; @@ -1329,6 +1437,12 @@ /* snd error text */ "Destination server error: %@" = "Error del servidor de destino: %@"; +/* No comment provided by engineer. */ +"Detailed statistics" = "Estadísticas detalladas"; + +/* No comment provided by engineer. */ +"Details" = "Detalles"; + /* No comment provided by engineer. */ "Develop" = "Desarrollo"; @@ -1431,12 +1545,21 @@ /* chat item action */ "Download" = "Descargar"; +/* No comment provided by engineer. */ +"Download errors" = "Errores en la descarga"; + /* No comment provided by engineer. */ "Download failed" = "Descarga fallida"; /* server test step */ "Download file" = "Descargar archivo"; +/* No comment provided by engineer. */ +"Downloaded" = "Descargado"; + +/* No comment provided by engineer. */ +"Downloaded files" = "Archivos descargados"; + /* No comment provided by engineer. */ "Downloading archive" = "Descargando archivo"; @@ -1449,6 +1572,9 @@ /* integrity error chat item */ "duplicate message" = "mensaje duplicado"; +/* No comment provided by engineer. */ +"duplicates" = "duplicados"; + /* No comment provided by engineer. */ "Duration" = "Duración"; @@ -1707,6 +1833,9 @@ /* No comment provided by engineer. */ "Error exporting chat database" = "Error al exportar base de datos"; +/* No comment provided by engineer. */ +"Error exporting theme: %@" = "Error al exportar tema: %@"; + /* No comment provided by engineer. */ "Error importing chat database" = "Error al importar base de datos"; @@ -1722,9 +1851,18 @@ /* No comment provided by engineer. */ "Error receiving file" = "Error al recibir archivo"; +/* No comment provided by engineer. */ +"Error reconnecting server" = "Error al reconectar con el servidor"; + +/* No comment provided by engineer. */ +"Error reconnecting servers" = "Error al reconectar con los servidores"; + /* No comment provided by engineer. */ "Error removing member" = "Error al eliminar miembro"; +/* No comment provided by engineer. */ +"Error resetting statistics" = "Error al restablecer las estadísticas"; + /* No comment provided by engineer. */ "Error saving %@ servers" = "Error al guardar servidores %@"; @@ -1804,6 +1942,9 @@ /* No comment provided by engineer. */ "Error: URL is invalid" = "Error: la URL no es válida"; +/* No comment provided by engineer. */ +"Errors" = "Errores"; + /* No comment provided by engineer. */ "Even when disabled in the conversation." = "Incluso si está desactivado para la conversación."; @@ -1816,12 +1957,18 @@ /* chat item action */ "Expand" = "Expandir"; +/* No comment provided by engineer. */ +"expired" = "expirado"; + /* No comment provided by engineer. */ "Export database" = "Exportar base de datos"; /* No comment provided by engineer. */ "Export error:" = "Error al exportar:"; +/* No comment provided by engineer. */ +"Export theme" = "Exportar tema"; + /* No comment provided by engineer. */ "Exported database archive." = "Archivo de base de datos exportado."; @@ -1843,6 +1990,21 @@ /* No comment provided by engineer. */ "Favorite" = "Favoritos"; +/* No comment provided by engineer. */ +"File error" = "Error de archivo"; + +/* file error text */ +"File not found - most likely file was deleted or cancelled." = "Archivo no encontrado, probablemente haya sido borrado o cancelado."; + +/* file error text */ +"File server error: %@" = "Error del servidor de archivos: %@"; + +/* No comment provided by engineer. */ +"File status" = "Estado del archivo"; + +/* copied message info */ +"File status: %@" = "Estado del archivo: %@"; + /* No comment provided by engineer. */ "File will be deleted from servers." = "El archivo será eliminado de los servidores."; @@ -1957,6 +2119,12 @@ /* No comment provided by engineer. */ "GIFs and stickers" = "GIFs y stickers"; +/* message preview */ +"Good afternoon!" = "¡Buenas tardes!"; + +/* message preview */ +"Good morning!" = "¡Buenos días!"; + /* No comment provided by engineer. */ "Group" = "Grupo"; @@ -2134,6 +2302,9 @@ /* No comment provided by engineer. */ "Import failed" = "Error de importación"; +/* No comment provided by engineer. */ +"Import theme" = "Importar tema"; + /* No comment provided by engineer. */ "Importing archive" = "Importando archivo"; @@ -2155,6 +2326,9 @@ /* No comment provided by engineer. */ "In-call sounds" = "Sonido de llamada"; +/* No comment provided by engineer. */ +"inactive" = "inactivo"; + /* No comment provided by engineer. */ "Incognito" = "Incógnito"; @@ -2218,6 +2392,9 @@ /* No comment provided by engineer. */ "Interface" = "Interfaz"; +/* No comment provided by engineer. */ +"Interface colors" = "Colores del interfaz"; + /* invalid chat data */ "invalid chat" = "chat no válido"; @@ -2470,6 +2647,9 @@ /* rcv group event chat item */ "member connected" = "conectado"; +/* item status text */ +"Member inactive" = "Miembro inactivo"; + /* No comment provided by engineer. */ "Member role will be changed to \"%@\". All group members will be notified." = "El rol del miembro cambiará a \"%@\" y se notificará al grupo."; @@ -2479,6 +2659,9 @@ /* No comment provided by engineer. */ "Member will be removed from group - this cannot be undone!" = "El miembro será expulsado del grupo. ¡No podrá deshacerse!"; +/* No comment provided by engineer. */ +"Menus" = "Menus"; + /* item status text */ "Message delivery error" = "Error en la entrega del mensaje"; @@ -2491,6 +2674,12 @@ /* No comment provided by engineer. */ "Message draft" = "Borrador de mensaje"; +/* item status text */ +"Message forwarded" = "Mensaje reenviado"; + +/* item status description */ +"Message may be delivered later if member becomes active." = "El mensaje puede ser entregado más tarde si el miembro vuelve a estar activo."; + /* No comment provided by engineer. */ "Message queue info" = "Información cola de mensajes"; @@ -2506,6 +2695,9 @@ /* notification */ "message received" = "mensaje recibido"; +/* No comment provided by engineer. */ +"Message reception" = "Recepción de mensaje"; + /* No comment provided by engineer. */ "Message routing fallback" = "Enrutamiento de mensajes alternativo"; @@ -2515,6 +2707,12 @@ /* No comment provided by engineer. */ "Message source remains private." = "El autor del mensaje se mantiene privado."; +/* No comment provided by engineer. */ +"Message status" = "Estado del mensaje"; + +/* copied message info */ +"Message status: %@" = "Estado del mensaje: %@"; + /* No comment provided by engineer. */ "Message text" = "Contacto y texto"; @@ -2530,6 +2728,12 @@ /* No comment provided by engineer. */ "Messages from %@ will be shown!" = "¡Los mensajes de %@ serán mostrados!"; +/* No comment provided by engineer. */ +"Messages received" = "Mensajes recibidos"; + +/* No comment provided by engineer. */ +"Messages sent" = "Mensajes enviados"; + /* No comment provided by engineer. */ "Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Los mensajes, archivos y llamadas están protegidos mediante **cifrado de extremo a extremo** con secreto perfecto hacía adelante, repudio y recuperación tras ataque."; @@ -2695,6 +2899,9 @@ /* No comment provided by engineer. */ "No device token!" = "¡Sin dispositivo token!"; +/* item status description */ +"No direct connection yet, message is forwarded by admin." = "Aún no hay conexión directa, el mensaje es reenviado por el administrador."; + /* No comment provided by engineer. */ "no e2e encryption" = "sin cifrar"; @@ -2707,6 +2914,9 @@ /* No comment provided by engineer. */ "No history" = "Sin historial"; +/* No comment provided by engineer. */ +"No info, try to reload" = "No hay información, intenta recargar"; + /* No comment provided by engineer. */ "No network connection" = "Sin conexión de red"; @@ -2739,7 +2949,7 @@ time to disappear */ "off" = "desactivado"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Desactivado"; /* feature offered item */ @@ -2832,6 +3042,9 @@ /* authentication reason */ "Open migration to another device" = "Abrir menú migración a otro dispositivo"; +/* No comment provided by engineer. */ +"Open server settings" = "Abrir configuración del servidor"; + /* No comment provided by engineer. */ "Open Settings" = "Abrir Configuración"; @@ -2856,9 +3069,18 @@ /* No comment provided by engineer. */ "Or show this code" = "O muestra este código QR"; +/* No comment provided by engineer. */ +"other" = "otro"; + /* No comment provided by engineer. */ "Other" = "Otro"; +/* No comment provided by engineer. */ +"Other %@ servers" = "Otros servidores %@"; + +/* No comment provided by engineer. */ +"other errors" = "otros errores"; + /* member role */ "owner" = "propietario"; @@ -2901,6 +3123,9 @@ /* No comment provided by engineer. */ "peer-to-peer" = "p2p"; +/* No comment provided by engineer. */ +"Pending" = "Pendiente"; + /* No comment provided by engineer. */ "People can connect to you only via the links you share." = "Las personas pueden conectarse contigo solo mediante los enlaces que compartes."; @@ -2922,6 +3147,9 @@ /* No comment provided by engineer. */ "Please ask your contact to enable sending voice messages." = "Solicita que tu contacto habilite el envío de mensajes de voz."; +/* No comment provided by engineer. */ +"Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers." = "Comprueba que el móvil y el ordenador están conectados a la misma red local y que el cortafuegos del ordenador permite la conexión.\nPor favor, comparte cualquier otro problema con los desarrolladores."; + /* No comment provided by engineer. */ "Please check that you used the correct link or ask your contact to send you another one." = "Comprueba que has usado el enlace correcto o pide a tu contacto que te envíe otro."; @@ -2979,6 +3207,9 @@ /* No comment provided by engineer. */ "Preview" = "Vista previa"; +/* No comment provided by engineer. */ +"Previously connected servers" = "Servidores conectados previamente"; + /* No comment provided by engineer. */ "Privacy & security" = "Privacidad y Seguridad"; @@ -3000,6 +3231,9 @@ /* No comment provided by engineer. */ "Private routing" = "Enrutamiento privado"; +/* No comment provided by engineer. */ +"Private routing error" = "Error de enrutamiento privado"; + /* No comment provided by engineer. */ "Profile and server connections" = "Datos del perfil y conexiones"; @@ -3018,6 +3252,9 @@ /* No comment provided by engineer. */ "Profile password" = "Contraseña del perfil"; +/* No comment provided by engineer. */ +"Profile theme" = "Tema del perfil"; + /* No comment provided by engineer. */ "Profile update will be sent to your contacts." = "La actualización del perfil se enviará a tus contactos."; @@ -3066,6 +3303,12 @@ /* No comment provided by engineer. */ "Protocol timeout per KB" = "Timeout protocolo por KB"; +/* No comment provided by engineer. */ +"Proxied" = "Con proxy"; + +/* No comment provided by engineer. */ +"Proxied servers" = "Servidores con proxy"; + /* No comment provided by engineer. */ "Push notifications" = "Notificaciones automáticas"; @@ -3108,6 +3351,9 @@ /* No comment provided by engineer. */ "Receipts are disabled" = "Las confirmaciones están desactivadas"; +/* No comment provided by engineer. */ +"Receive errors" = "Errores de recepción"; + /* No comment provided by engineer. */ "received answer…" = "respuesta recibida…"; @@ -3126,6 +3372,15 @@ /* message info title */ "Received message" = "Mensaje entrante"; +/* No comment provided by engineer. */ +"Received messages" = "Mensajes recibidos"; + +/* No comment provided by engineer. */ +"Received reply" = "Respuesta recibida"; + +/* No comment provided by engineer. */ +"Received total" = "Total recibido"; + /* No comment provided by engineer. */ "Receiving address will be changed to a different server. Address change will complete after sender comes online." = "La dirección de recepción pasará a otro servidor. El cambio se completará cuando el remitente esté en línea."; @@ -3144,9 +3399,24 @@ /* No comment provided by engineer. */ "Recipients see updates as you type them." = "Los destinatarios ven actualizarse mientras escribes."; +/* No comment provided by engineer. */ +"Reconnect" = "Reconectar"; + /* No comment provided by engineer. */ "Reconnect all connected servers to force message delivery. It uses additional traffic." = "Reconectar todos los servidores conectados para forzar la entrega del mensaje. Se usa tráfico adicional."; +/* No comment provided by engineer. */ +"Reconnect all servers" = "Reconectar todos los servidores"; + +/* No comment provided by engineer. */ +"Reconnect all servers?" = "¿Reconectar todos los servidores?"; + +/* No comment provided by engineer. */ +"Reconnect server to force message delivery. It uses additional traffic." = "Reconectar el servidor para forzar la entrega de mensajes. Usa tráfico adicional."; + +/* No comment provided by engineer. */ +"Reconnect server?" = "¿Reconectar servidor?"; + /* No comment provided by engineer. */ "Reconnect servers?" = "¿Reconectar servidores?"; @@ -3180,6 +3450,9 @@ /* No comment provided by engineer. */ "Remove" = "Eliminar"; +/* No comment provided by engineer. */ +"Remove image" = "Eliminar imagen"; + /* No comment provided by engineer. */ "Remove member" = "Expulsar miembro"; @@ -3237,12 +3510,24 @@ /* No comment provided by engineer. */ "Reset" = "Restablecer"; +/* No comment provided by engineer. */ +"Reset all statistics" = "Restablecer estadísticas"; + +/* No comment provided by engineer. */ +"Reset all statistics?" = "¿Restablecer todas las estadísticas?"; + /* No comment provided by engineer. */ "Reset colors" = "Restablecer colores"; +/* No comment provided by engineer. */ +"Reset to app theme" = "Restablecer al tema de la aplicación"; + /* No comment provided by engineer. */ "Reset to defaults" = "Restablecer valores predetarminados"; +/* No comment provided by engineer. */ +"Reset to user theme" = "Restablecer al tema del usuario"; + /* No comment provided by engineer. */ "Restart the app to create a new chat profile" = "Reinicia la aplicación para crear un perfil nuevo"; @@ -3357,6 +3642,12 @@ /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Los servidores WebRTC ICE guardados serán eliminados"; +/* No comment provided by engineer. */ +"Scale" = "Escala"; + +/* No comment provided by engineer. */ +"Scan / Paste link" = "Escanear / Pegar enlace"; + /* No comment provided by engineer. */ "Scan code" = "Escanear código"; @@ -3384,6 +3675,9 @@ /* network option */ "sec" = "seg"; +/* No comment provided by engineer. */ +"Secondary" = "Secundario"; + /* time unit */ "seconds" = "segundos"; @@ -3393,6 +3687,9 @@ /* server test step */ "Secure queue" = "Cola segura"; +/* No comment provided by engineer. */ +"Secured" = "Seguro"; + /* No comment provided by engineer. */ "Security assessment" = "Evaluación de la seguridad"; @@ -3405,6 +3702,9 @@ /* No comment provided by engineer. */ "Select" = "Seleccionar"; +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Las preferencias seleccionadas no permiten este mensaje."; + /* No comment provided by engineer. */ "Self-destruct" = "Autodestrucción"; @@ -3438,6 +3738,9 @@ /* No comment provided by engineer. */ "Send disappearing message" = "Enviar mensaje temporal"; +/* No comment provided by engineer. */ +"Send errors" = "Errores de envío"; + /* No comment provided by engineer. */ "Send link previews" = "Enviar previsualizacion de enlaces"; @@ -3504,15 +3807,36 @@ /* copied message info */ "Sent at: %@" = "Enviado: %@"; +/* No comment provided by engineer. */ +"Sent directly" = "Enviado directamente"; + /* notification */ "Sent file event" = "Evento de archivo enviado"; /* message info title */ "Sent message" = "Mensaje saliente"; +/* No comment provided by engineer. */ +"Sent messages" = "Mensajes enviados"; + /* No comment provided by engineer. */ "Sent messages will be deleted after set time." = "Los mensajes enviados se eliminarán una vez transcurrido el tiempo establecido."; +/* No comment provided by engineer. */ +"Sent reply" = "Respuesta enviada"; + +/* No comment provided by engineer. */ +"Sent total" = "Total enviado"; + +/* No comment provided by engineer. */ +"Sent via proxy" = "Enviado mediante proxy"; + +/* No comment provided by engineer. */ +"Server address" = "Dirección del servidor"; + +/* No comment provided by engineer. */ +"Server address is incompatible with network settings: %@." = "La dirección del servidor es incompatible con la configuración de la red: %@."; + /* srv error text. */ "Server address is incompatible with network settings." = "La dirección del servidor es incompatible con la configuración de la red."; @@ -3528,12 +3852,24 @@ /* No comment provided by engineer. */ "Server test failed!" = "¡Error en prueba del servidor!"; +/* No comment provided by engineer. */ +"Server type" = "Tipo de servidor"; + /* srv error text */ "Server version is incompatible with network settings." = "La versión del servidor es incompatible con la configuración de red."; +/* No comment provided by engineer. */ +"Server version is incompatible with your app: %@." = "La versión del servidor es incompatible con tu aplicación: %@."; + /* No comment provided by engineer. */ "Servers" = "Servidores"; +/* No comment provided by engineer. */ +"Servers info" = "Info servidores"; + +/* No comment provided by engineer. */ +"Servers statistics will be reset - this cannot be undone!" = "Las estadísticas de los servidores serán restablecidas. ¡No podrá deshacerse!"; + /* No comment provided by engineer. */ "Session code" = "Código de sesión"; @@ -3543,6 +3879,9 @@ /* No comment provided by engineer. */ "Set contact name…" = "Escribe el nombre del contacto…"; +/* No comment provided by engineer. */ +"Set default theme" = "Establecer tema predeterminado"; + /* No comment provided by engineer. */ "Set group preferences" = "Establecer preferencias de grupo"; @@ -3612,6 +3951,9 @@ /* No comment provided by engineer. */ "Show message status" = "Estado del mensaje"; +/* No comment provided by engineer. */ +"Show percentage" = "Mostrar porcentaje"; + /* No comment provided by engineer. */ "Show preview" = "Mostrar vista previa"; @@ -3621,6 +3963,9 @@ /* No comment provided by engineer. */ "Show:" = "Mostrar:"; +/* No comment provided by engineer. */ +"SimpleX" = "SimpleX"; + /* No comment provided by engineer. */ "SimpleX address" = "Dirección SimpleX"; @@ -3666,6 +4011,9 @@ /* No comment provided by engineer. */ "Simplified incognito mode" = "Modo incógnito simplificado"; +/* No comment provided by engineer. */ +"Size" = "Tamaño"; + /* No comment provided by engineer. */ "Skip" = "Omitir"; @@ -3675,6 +4023,9 @@ /* No comment provided by engineer. */ "Small groups (max 20)" = "Grupos pequeños (máx. 20)"; +/* No comment provided by engineer. */ +"SMP server" = "Servidor SMP"; + /* No comment provided by engineer. */ "SMP servers" = "Servidores SMP"; @@ -3699,9 +4050,15 @@ /* No comment provided by engineer. */ "Start migration" = "Iniciar migración"; +/* No comment provided by engineer. */ +"Starting from %@." = "Iniciado desde %@."; + /* No comment provided by engineer. */ "starting…" = "inicializando…"; +/* No comment provided by engineer. */ +"Statistics" = "Estadísticas"; + /* No comment provided by engineer. */ "Stop" = "Parar"; @@ -3744,6 +4101,15 @@ /* No comment provided by engineer. */ "Submit" = "Enviar"; +/* No comment provided by engineer. */ +"Subscribed" = "Suscrito"; + +/* No comment provided by engineer. */ +"Subscription errors" = "Errores de suscripción"; + +/* No comment provided by engineer. */ +"Subscriptions ignored" = "Suscripciones ignoradas"; + /* No comment provided by engineer. */ "Support SimpleX Chat" = "Soporte SimpleX Chat"; @@ -3792,6 +4158,9 @@ /* No comment provided by engineer. */ "TCP_KEEPINTVL" = "TCP_KEEPINTVL"; +/* No comment provided by engineer. */ +"Temporary file error" = "Error en archivo temporal"; + /* server test failure */ "Test failed at step %@." = "La prueba ha fallado en el paso %@."; @@ -3873,6 +4242,9 @@ /* No comment provided by engineer. */ "The text you pasted is not a SimpleX link." = "El texto pegado no es un enlace SimpleX."; +/* No comment provided by engineer. */ +"Themes" = "Temas"; + /* No comment provided by engineer. */ "These settings are for your current profile **%@**." = "Esta configuración afecta a tu perfil actual **%@**."; @@ -3915,9 +4287,15 @@ /* No comment provided by engineer. */ "This is your own SimpleX address!" = "¡Esta es tu propia dirección SimpleX!"; +/* No comment provided by engineer. */ +"This link was used with another mobile device, please create a new link on the desktop." = "Este enlace ha sido usado en otro dispositivo móvil, por favor crea un enlace nuevo en el ordenador."; + /* No comment provided by engineer. */ "This setting applies to messages in your current chat profile **%@**." = "Esta configuración se aplica a los mensajes del perfil actual **%@**."; +/* No comment provided by engineer. */ +"Title" = "Título"; + /* No comment provided by engineer. */ "To ask any questions and to receive updates:" = "Para consultar cualquier duda y recibir actualizaciones:"; @@ -3957,9 +4335,15 @@ /* No comment provided by engineer. */ "Toggle incognito when connecting." = "Activa incógnito al conectar."; +/* No comment provided by engineer. */ +"Total" = "Total"; + /* No comment provided by engineer. */ "Transport isolation" = "Aislamiento de transporte"; +/* No comment provided by engineer. */ +"Transport sessions" = "Sesiones de transporte"; + /* No comment provided by engineer. */ "Trying to connect to the server used to receive messages from this contact (error: %@)." = "Intentando conectar con el servidor usado para recibir mensajes de este contacto (error: %@)."; @@ -4095,12 +4479,21 @@ /* No comment provided by engineer. */ "Upgrade and open chat" = "Actualizar y abrir Chat"; +/* No comment provided by engineer. */ +"Upload errors" = "Errores en subida"; + /* No comment provided by engineer. */ "Upload failed" = "Error de subida"; /* server test step */ "Upload file" = "Subir archivo"; +/* No comment provided by engineer. */ +"Uploaded" = "Subido"; + +/* No comment provided by engineer. */ +"Uploaded files" = "Archivos subidos"; + /* No comment provided by engineer. */ "Uploading archive" = "Subiendo archivo"; @@ -4146,6 +4539,9 @@ /* No comment provided by engineer. */ "User profile" = "Perfil de usuario"; +/* No comment provided by engineer. */ +"User selection" = "Selección de usuarios"; + /* No comment provided by engineer. */ "Using .onion hosts requires compatible VPN provider." = "Usar hosts .onion requiere un proveedor VPN compatible."; @@ -4254,6 +4650,12 @@ /* No comment provided by engineer. */ "Waiting for video" = "Esperando el vídeo"; +/* No comment provided by engineer. */ +"Wallpaper accent" = "Color imagen de fondo"; + +/* No comment provided by engineer. */ +"Wallpaper background" = "Color de fondo"; + /* No comment provided by engineer. */ "wants to connect to you!" = "¡quiere contactar contigo!"; @@ -4326,9 +4728,15 @@ /* snd error text */ "Wrong key or unknown connection - most likely this connection is deleted." = "Clave incorrecta o conexión desconocida - probablemente esta conexión fue eliminada."; +/* file error text */ +"Wrong key or unknown file chunk address - most likely file is deleted." = "Clave incorrecta o dirección del bloque del archivo desconocida. Es probable que el archivo se haya eliminado."; + /* No comment provided by engineer. */ "Wrong passphrase!" = "¡Contraseña incorrecta!"; +/* No comment provided by engineer. */ +"XFTP server" = "Servidor XFTP"; + /* No comment provided by engineer. */ "XFTP servers" = "Servidores XFTP"; @@ -4386,6 +4794,9 @@ /* No comment provided by engineer. */ "You are invited to group" = "Has sido invitado a un grupo"; +/* No comment provided by engineer. */ +"You are not connected to these servers. Private routing is used to deliver messages to them." = "No estás conectado a estos servidores. Para enviarles mensajes se usa el enrutamiento privado."; + /* No comment provided by engineer. */ "you are observer" = "Tu rol es observador"; @@ -4414,7 +4825,7 @@ "You can make it visible to your SimpleX contacts via Settings." = "Puedes hacerlo visible para tus contactos de SimpleX en Configuración."; /* notification body */ -"You can now chat with %@" = "Ya puedes enviar mensajes a %@"; +"You can now chat with %@" = "Ya puedes chatear con %@"; /* No comment provided by engineer. */ "You can set lock screen notification preview via settings." = "Puedes configurar las notificaciones de la pantalla de bloqueo desde Configuración."; diff --git a/apps/ios/fi.lproj/Localizable.strings b/apps/ios/fi.lproj/Localizable.strings index 84613e6a54..b9ea0024c5 100644 --- a/apps/ios/fi.lproj/Localizable.strings +++ b/apps/ios/fi.lproj/Localizable.strings @@ -2163,7 +2163,7 @@ time to disappear */ "off" = "pois"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Pois"; /* feature offered item */ diff --git a/apps/ios/fr.lproj/Localizable.strings b/apps/ios/fr.lproj/Localizable.strings index c2777fac75..272c3bb410 100644 --- a/apps/ios/fr.lproj/Localizable.strings +++ b/apps/ios/fr.lproj/Localizable.strings @@ -334,6 +334,9 @@ /* No comment provided by engineer. */ "above, then choose:" = "ci-dessus, puis choisissez :"; +/* No comment provided by engineer. */ +"Accent" = "Principale"; + /* accept contact request via notification accept incoming call via notification */ "Accept" = "Accepter"; @@ -350,6 +353,15 @@ /* call status */ "accepted call" = "appel accepté"; +/* No comment provided by engineer. */ +"Acknowledged" = "Reçu avec accusé de réception"; + +/* No comment provided by engineer. */ +"Acknowledgement errors" = "Erreur d'accusé de réception"; + +/* No comment provided by engineer. */ +"Active connections" = "Connections actives"; + /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Ajoutez une adresse à votre profil, afin que vos contacts puissent la partager avec d'autres personnes. La mise à jour du profil sera envoyée à vos contacts."; @@ -374,6 +386,15 @@ /* No comment provided by engineer. */ "Add welcome message" = "Ajouter un message d'accueil"; +/* No comment provided by engineer. */ +"Additional accent" = "Accent additionnel"; + +/* No comment provided by engineer. */ +"Additional accent 2" = "Accent additionnel 2"; + +/* No comment provided by engineer. */ +"Additional secondary" = "Accent secondaire"; + /* No comment provided by engineer. */ "Address" = "Adresse"; @@ -395,6 +416,9 @@ /* No comment provided by engineer. */ "Advanced network settings" = "Paramètres réseau avancés"; +/* No comment provided by engineer. */ +"Advanced settings" = "Paramètres avancés"; + /* chat item text */ "agreeing encryption for %@…" = "négociation du chiffrement avec %@…"; @@ -410,6 +434,9 @@ /* No comment provided by engineer. */ "All data is erased when it is entered." = "Toutes les données sont effacées lorsqu'il est saisi."; +/* No comment provided by engineer. */ +"All data is private to your device." = "Toutes les données restent confinées dans votre appareil."; + /* No comment provided by engineer. */ "All group members will remain connected." = "Tous les membres du groupe resteront connectés."; @@ -425,6 +452,9 @@ /* No comment provided by engineer. */ "All new messages from %@ will be hidden!" = "Tous les nouveaux messages de %@ seront cachés !"; +/* No comment provided by engineer. */ +"All profiles" = "Tous les profiles"; + /* No comment provided by engineer. */ "All your contacts will remain connected." = "Tous vos contacts resteront connectés."; @@ -551,6 +581,9 @@ /* No comment provided by engineer. */ "Apply" = "Appliquer"; +/* No comment provided by engineer. */ +"Apply to" = "Appliquer à"; + /* No comment provided by engineer. */ "Archive and upload" = "Archiver et transférer"; @@ -560,6 +593,9 @@ /* No comment provided by engineer. */ "Attach" = "Attacher"; +/* No comment provided by engineer. */ +"attempts" = "tentatives"; + /* No comment provided by engineer. */ "Audio & video calls" = "Appels audio et vidéo"; @@ -602,6 +638,9 @@ /* No comment provided by engineer. */ "Back" = "Retour"; +/* No comment provided by engineer. */ +"Background" = "Fond"; + /* No comment provided by engineer. */ "Bad desktop address" = "Mauvaise adresse de bureau"; @@ -623,6 +662,9 @@ /* No comment provided by engineer. */ "Better messages" = "Meilleurs messages"; +/* No comment provided by engineer. */ +"Black" = "Noir"; + /* No comment provided by engineer. */ "Block" = "Bloquer"; @@ -647,7 +689,7 @@ /* rcv group event chat item */ "blocked %@" = "%@ bloqué"; -/* marked deleted chat item preview text */ +/* blocked chat item */ "blocked by admin" = "bloqué par l'administrateur"; /* No comment provided by engineer. */ @@ -713,6 +755,9 @@ /* No comment provided by engineer. */ "Cannot access keychain to save database password" = "Impossible d'accéder à la keychain pour enregistrer le mot de passe de la base de données"; +/* No comment provided by engineer. */ +"Cannot forward message" = "Impossible de transférer le message"; + /* No comment provided by engineer. */ "Cannot receive file" = "Impossible de recevoir le fichier"; @@ -771,6 +816,9 @@ /* No comment provided by engineer. */ "Chat archive" = "Archives du chat"; +/* No comment provided by engineer. */ +"Chat colors" = "Couleurs de chat"; + /* No comment provided by engineer. */ "Chat console" = "Console du chat"; @@ -798,6 +846,9 @@ /* No comment provided by engineer. */ "Chat preferences" = "Préférences de chat"; +/* No comment provided by engineer. */ +"Chat theme" = "Thème de chat"; + /* No comment provided by engineer. */ "Chats" = "Discussions"; @@ -816,6 +867,15 @@ /* No comment provided by engineer. */ "Choose from library" = "Choisir dans la photothèque"; +/* No comment provided by engineer. */ +"Chunks deleted" = "Chunks supprimés"; + +/* No comment provided by engineer. */ +"Chunks downloaded" = "Chunks téléchargés"; + +/* No comment provided by engineer. */ +"Chunks uploaded" = "Chunks téléversés"; + /* No comment provided by engineer. */ "Clear" = "Effacer"; @@ -831,6 +891,9 @@ /* No comment provided by engineer. */ "Clear verification" = "Retirer la vérification"; +/* No comment provided by engineer. */ +"Color mode" = "Mode de couleur"; + /* No comment provided by engineer. */ "colored" = "coloré"; @@ -843,9 +906,15 @@ /* No comment provided by engineer. */ "complete" = "complet"; +/* No comment provided by engineer. */ +"Completed" = "Complété"; + /* No comment provided by engineer. */ "Configure ICE servers" = "Configurer les serveurs ICE"; +/* No comment provided by engineer. */ +"Configured %@ servers" = "%@ serveurs configurés"; + /* No comment provided by engineer. */ "Confirm" = "Confirmer"; @@ -912,18 +981,27 @@ /* No comment provided by engineer. */ "connected" = "connecté"; +/* No comment provided by engineer. */ +"Connected" = "Connecté"; + /* No comment provided by engineer. */ "Connected desktop" = "Bureau connecté"; /* rcv group event chat item */ "connected directly" = "s'est connecté.e de manière directe"; +/* No comment provided by engineer. */ +"Connected servers" = "Serveurs connectés"; + /* No comment provided by engineer. */ "Connected to desktop" = "Connecté au bureau"; /* No comment provided by engineer. */ "connecting" = "connexion"; +/* No comment provided by engineer. */ +"Connecting" = "Connexion"; + /* No comment provided by engineer. */ "connecting (accepted)" = "connexion (acceptée)"; @@ -972,9 +1050,15 @@ /* No comment provided by engineer. */ "Connection timeout" = "Délai de connexion"; +/* No comment provided by engineer. */ +"Connection with desktop stopped" = "La connexion avec le bureau s'est arrêtée"; + /* connection information */ "connection:%@" = "connexion : %@"; +/* No comment provided by engineer. */ +"Connections" = "Connexions"; + /* profile update event chat item */ "contact %@ changed to %@" = "le contact %1$@ est devenu %2$@"; @@ -1017,6 +1101,9 @@ /* No comment provided by engineer. */ "Copy" = "Copier"; +/* No comment provided by engineer. */ +"Copy error" = "Erreur de copie"; + /* No comment provided by engineer. */ "Core version: v%@" = "Version du cœur : v%@"; @@ -1062,6 +1149,9 @@ /* No comment provided by engineer. */ "Create your profile" = "Créez votre profil"; +/* No comment provided by engineer. */ +"Created" = "Créé"; + /* No comment provided by engineer. */ "Created at" = "Créé à"; @@ -1086,6 +1176,9 @@ /* No comment provided by engineer. */ "Current passphrase…" = "Phrase secrète actuelle…"; +/* No comment provided by engineer. */ +"Current profile" = "Profil actuel"; + /* No comment provided by engineer. */ "Currently maximum supported file size is %@." = "Actuellement, la taille maximale des fichiers supportés est de %@."; @@ -1095,9 +1188,15 @@ /* No comment provided by engineer. */ "Custom time" = "Délai personnalisé"; +/* No comment provided by engineer. */ +"Customize theme" = "Personnaliser le thème"; + /* No comment provided by engineer. */ "Dark" = "Sombre"; +/* No comment provided by engineer. */ +"Dark mode colors" = "Couleurs en mode sombre"; + /* No comment provided by engineer. */ "Database downgrade" = "Rétrogradation de la base de données"; @@ -1167,6 +1266,9 @@ /* message decrypt error item */ "Decryption error" = "Erreur de déchiffrement"; +/* No comment provided by engineer. */ +"decryption errors" = "Erreurs de déchiffrement"; + /* pref value */ "default (%@)" = "défaut (%@)"; @@ -1293,6 +1395,9 @@ /* deleted chat item */ "deleted" = "supprimé"; +/* No comment provided by engineer. */ +"Deleted" = "Supprimé"; + /* No comment provided by engineer. */ "Deleted at" = "Supprimé à"; @@ -1305,6 +1410,9 @@ /* rcv group event chat item */ "deleted group" = "groupe supprimé"; +/* No comment provided by engineer. */ +"Deletion errors" = "Erreurs de suppression"; + /* No comment provided by engineer. */ "Delivery" = "Distribution"; @@ -1329,6 +1437,12 @@ /* snd error text */ "Destination server error: %@" = "Erreur du serveur de destination : %@"; +/* No comment provided by engineer. */ +"Detailed statistics" = "Statistiques détaillées"; + +/* No comment provided by engineer. */ +"Details" = "Détails"; + /* No comment provided by engineer. */ "Develop" = "Développer"; @@ -1431,12 +1545,21 @@ /* chat item action */ "Download" = "Télécharger"; +/* No comment provided by engineer. */ +"Download errors" = "Erreurs de téléchargement"; + /* No comment provided by engineer. */ "Download failed" = "Échec du téléchargement"; /* server test step */ "Download file" = "Télécharger le fichier"; +/* No comment provided by engineer. */ +"Downloaded" = "Téléchargé"; + +/* No comment provided by engineer. */ +"Downloaded files" = "Fichiers téléchargés"; + /* No comment provided by engineer. */ "Downloading archive" = "Téléchargement de l'archive"; @@ -1449,6 +1572,9 @@ /* integrity error chat item */ "duplicate message" = "message dupliqué"; +/* No comment provided by engineer. */ +"duplicates" = "doublons"; + /* No comment provided by engineer. */ "Duration" = "Durée"; @@ -1707,6 +1833,9 @@ /* No comment provided by engineer. */ "Error exporting chat database" = "Erreur lors de l'exportation de la base de données du chat"; +/* No comment provided by engineer. */ +"Error exporting theme: %@" = "Erreur d'exportation du thème : %@"; + /* No comment provided by engineer. */ "Error importing chat database" = "Erreur lors de l'importation de la base de données du chat"; @@ -1722,9 +1851,18 @@ /* No comment provided by engineer. */ "Error receiving file" = "Erreur lors de la réception du fichier"; +/* No comment provided by engineer. */ +"Error reconnecting server" = "Erreur de reconnexion du serveur"; + +/* No comment provided by engineer. */ +"Error reconnecting servers" = "Erreur de reconnexion des serveurs"; + /* No comment provided by engineer. */ "Error removing member" = "Erreur lors de la suppression d'un membre"; +/* No comment provided by engineer. */ +"Error resetting statistics" = "Erreur de réinitialisation des statistiques"; + /* No comment provided by engineer. */ "Error saving %@ servers" = "Erreur lors de la sauvegarde des serveurs %@"; @@ -1804,6 +1942,9 @@ /* No comment provided by engineer. */ "Error: URL is invalid" = "Erreur : URL invalide"; +/* No comment provided by engineer. */ +"Errors" = "Erreurs"; + /* No comment provided by engineer. */ "Even when disabled in the conversation." = "Même s'il est désactivé dans la conversation."; @@ -1816,12 +1957,18 @@ /* chat item action */ "Expand" = "Étendre"; +/* No comment provided by engineer. */ +"expired" = "expiré"; + /* No comment provided by engineer. */ "Export database" = "Exporter la base de données"; /* No comment provided by engineer. */ "Export error:" = "Erreur lors de l'exportation :"; +/* No comment provided by engineer. */ +"Export theme" = "Exporter le thème"; + /* No comment provided by engineer. */ "Exported database archive." = "Archive de la base de données exportée."; @@ -1843,6 +1990,21 @@ /* No comment provided by engineer. */ "Favorite" = "Favoris"; +/* No comment provided by engineer. */ +"File error" = "Erreur de fichier"; + +/* file error text */ +"File not found - most likely file was deleted or cancelled." = "Fichier introuvable - le fichier a probablement été supprimé ou annulé."; + +/* file error text */ +"File server error: %@" = "Erreur de serveur de fichiers : %@"; + +/* No comment provided by engineer. */ +"File status" = "Statut du fichier"; + +/* copied message info */ +"File status: %@" = "Statut du fichier : %@"; + /* No comment provided by engineer. */ "File will be deleted from servers." = "Le fichier sera supprimé des serveurs."; @@ -1957,6 +2119,12 @@ /* No comment provided by engineer. */ "GIFs and stickers" = "GIFs et stickers"; +/* message preview */ +"Good afternoon!" = "Bonjour !"; + +/* message preview */ +"Good morning!" = "Bonjour !"; + /* No comment provided by engineer. */ "Group" = "Groupe"; @@ -2134,6 +2302,9 @@ /* No comment provided by engineer. */ "Import failed" = "Échec de l'importation"; +/* No comment provided by engineer. */ +"Import theme" = "Importer un thème"; + /* No comment provided by engineer. */ "Importing archive" = "Importation de l'archive"; @@ -2155,6 +2326,9 @@ /* No comment provided by engineer. */ "In-call sounds" = "Sons d'appel"; +/* No comment provided by engineer. */ +"inactive" = "inactif"; + /* No comment provided by engineer. */ "Incognito" = "Incognito"; @@ -2218,6 +2392,9 @@ /* No comment provided by engineer. */ "Interface" = "Interface"; +/* No comment provided by engineer. */ +"Interface colors" = "Couleurs d'interface"; + /* invalid chat data */ "invalid chat" = "chat invalide"; @@ -2470,6 +2647,9 @@ /* rcv group event chat item */ "member connected" = "est connecté·e"; +/* item status text */ +"Member inactive" = "Membre inactif"; + /* No comment provided by engineer. */ "Member role will be changed to \"%@\". All group members will be notified." = "Le rôle du membre sera changé pour \"%@\". Tous les membres du groupe en seront informés."; @@ -2479,6 +2659,9 @@ /* No comment provided by engineer. */ "Member will be removed from group - this cannot be undone!" = "Ce membre sera retiré du groupe - impossible de revenir en arrière !"; +/* No comment provided by engineer. */ +"Menus" = "Menus"; + /* item status text */ "Message delivery error" = "Erreur de distribution du message"; @@ -2491,6 +2674,12 @@ /* No comment provided by engineer. */ "Message draft" = "Brouillon de message"; +/* item status text */ +"Message forwarded" = "Message transféré"; + +/* item status description */ +"Message may be delivered later if member becomes active." = "Le message peut être transmis plus tard si le membre devient actif."; + /* No comment provided by engineer. */ "Message queue info" = "Informations sur la file d'attente des messages"; @@ -2506,6 +2695,9 @@ /* notification */ "message received" = "message reçu"; +/* No comment provided by engineer. */ +"Message reception" = "Réception de message"; + /* No comment provided by engineer. */ "Message routing fallback" = "Rabattement du routage des messages"; @@ -2515,6 +2707,12 @@ /* No comment provided by engineer. */ "Message source remains private." = "La source du message reste privée."; +/* No comment provided by engineer. */ +"Message status" = "Statut du message"; + +/* copied message info */ +"Message status: %@" = "Statut du message : %@"; + /* No comment provided by engineer. */ "Message text" = "Texte du message"; @@ -2530,6 +2728,12 @@ /* No comment provided by engineer. */ "Messages from %@ will be shown!" = "Les messages de %@ seront affichés !"; +/* No comment provided by engineer. */ +"Messages received" = "Messages reçus"; + +/* No comment provided by engineer. */ +"Messages sent" = "Messages envoyés"; + /* No comment provided by engineer. */ "Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Les messages, fichiers et appels sont protégés par un chiffrement **de bout en bout** avec une confidentialité persistante, une répudiation et une récupération en cas d'effraction."; @@ -2695,6 +2899,9 @@ /* No comment provided by engineer. */ "No device token!" = "Pas de token d'appareil !"; +/* item status description */ +"No direct connection yet, message is forwarded by admin." = "Pas de connexion directe pour l'instant, le message est transmis par l'administrateur."; + /* No comment provided by engineer. */ "no e2e encryption" = "sans chiffrement de bout en bout"; @@ -2707,6 +2914,9 @@ /* No comment provided by engineer. */ "No history" = "Aucun historique"; +/* No comment provided by engineer. */ +"No info, try to reload" = "Pas d'info, essayez de recharger"; + /* No comment provided by engineer. */ "No network connection" = "Pas de connexion au réseau"; @@ -2739,7 +2949,7 @@ time to disappear */ "off" = "off"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Off"; /* feature offered item */ @@ -2832,6 +3042,9 @@ /* authentication reason */ "Open migration to another device" = "Ouvrir le transfert vers un autre appareil"; +/* No comment provided by engineer. */ +"Open server settings" = "Ouvrir les paramètres du serveur"; + /* No comment provided by engineer. */ "Open Settings" = "Ouvrir les Paramètres"; @@ -2856,9 +3069,18 @@ /* No comment provided by engineer. */ "Or show this code" = "Ou présenter ce code"; +/* No comment provided by engineer. */ +"other" = "autre"; + /* No comment provided by engineer. */ "Other" = "Autres"; +/* No comment provided by engineer. */ +"Other %@ servers" = "Autres serveurs %@"; + +/* No comment provided by engineer. */ +"other errors" = "autres erreurs"; + /* member role */ "owner" = "propriétaire"; @@ -2901,6 +3123,9 @@ /* No comment provided by engineer. */ "peer-to-peer" = "pair-à-pair"; +/* No comment provided by engineer. */ +"Pending" = "En attente"; + /* No comment provided by engineer. */ "People can connect to you only via the links you share." = "On ne peut se connecter à vous qu’avec les liens que vous partagez."; @@ -2922,6 +3147,9 @@ /* No comment provided by engineer. */ "Please ask your contact to enable sending voice messages." = "Veuillez demander à votre contact de permettre l'envoi de messages vocaux."; +/* No comment provided by engineer. */ +"Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers." = "Veuillez vérifier que le téléphone portable et l'ordinateur de bureau sont connectés au même réseau local et que le pare-feu de l'ordinateur de bureau autorise la connexion.\nVeuillez faire part de tout autre problème aux développeurs."; + /* No comment provided by engineer. */ "Please check that you used the correct link or ask your contact to send you another one." = "Veuillez vérifier que vous avez utilisé le bon lien ou demandez à votre contact de vous en envoyer un autre."; @@ -2979,6 +3207,9 @@ /* No comment provided by engineer. */ "Preview" = "Aperçu"; +/* No comment provided by engineer. */ +"Previously connected servers" = "Serveurs précédemment connectés"; + /* No comment provided by engineer. */ "Privacy & security" = "Vie privée et sécurité"; @@ -3000,6 +3231,9 @@ /* No comment provided by engineer. */ "Private routing" = "Routage privé"; +/* No comment provided by engineer. */ +"Private routing error" = "Erreur de routage privé"; + /* No comment provided by engineer. */ "Profile and server connections" = "Profil et connexions au serveur"; @@ -3018,6 +3252,9 @@ /* No comment provided by engineer. */ "Profile password" = "Mot de passe de profil"; +/* No comment provided by engineer. */ +"Profile theme" = "Thème de profil"; + /* No comment provided by engineer. */ "Profile update will be sent to your contacts." = "La mise à jour du profil sera envoyée à vos contacts."; @@ -3066,6 +3303,12 @@ /* No comment provided by engineer. */ "Protocol timeout per KB" = "Délai d'attente du protocole par KB"; +/* No comment provided by engineer. */ +"Proxied" = "Routé via un proxy"; + +/* No comment provided by engineer. */ +"Proxied servers" = "Serveurs routés via des proxy"; + /* No comment provided by engineer. */ "Push notifications" = "Notifications push"; @@ -3108,6 +3351,9 @@ /* No comment provided by engineer. */ "Receipts are disabled" = "Les accusés de réception sont désactivés"; +/* No comment provided by engineer. */ +"Receive errors" = "Erreurs reçues"; + /* No comment provided by engineer. */ "received answer…" = "réponse reçu…"; @@ -3126,6 +3372,15 @@ /* message info title */ "Received message" = "Message reçu"; +/* No comment provided by engineer. */ +"Received messages" = "Messages reçus"; + +/* No comment provided by engineer. */ +"Received reply" = "Réponse reçue"; + +/* No comment provided by engineer. */ +"Received total" = "Total reçu"; + /* No comment provided by engineer. */ "Receiving address will be changed to a different server. Address change will complete after sender comes online." = "L'adresse de réception sera changée pour un autre serveur. Le changement d'adresse sera terminé lorsque l'expéditeur sera en ligne."; @@ -3144,9 +3399,24 @@ /* No comment provided by engineer. */ "Recipients see updates as you type them." = "Les destinataires voient les mises à jour au fur et à mesure que vous leur écrivez."; +/* No comment provided by engineer. */ +"Reconnect" = "Reconnecter"; + /* No comment provided by engineer. */ "Reconnect all connected servers to force message delivery. It uses additional traffic." = "Reconnecter tous les serveurs connectés pour forcer la livraison des messages. Cette méthode utilise du trafic supplémentaire."; +/* No comment provided by engineer. */ +"Reconnect all servers" = "Reconnecter tous les serveurs"; + +/* No comment provided by engineer. */ +"Reconnect all servers?" = "Reconnecter tous les serveurs ?"; + +/* No comment provided by engineer. */ +"Reconnect server to force message delivery. It uses additional traffic." = "Reconnecter le serveur pour forcer la livraison des messages. Utilise du trafic supplémentaire."; + +/* No comment provided by engineer. */ +"Reconnect server?" = "Reconnecter le serveur ?"; + /* No comment provided by engineer. */ "Reconnect servers?" = "Reconnecter les serveurs ?"; @@ -3180,6 +3450,9 @@ /* No comment provided by engineer. */ "Remove" = "Supprimer"; +/* No comment provided by engineer. */ +"Remove image" = "Enlever l'image"; + /* No comment provided by engineer. */ "Remove member" = "Retirer le membre"; @@ -3237,12 +3510,24 @@ /* No comment provided by engineer. */ "Reset" = "Réinitialisation"; +/* No comment provided by engineer. */ +"Reset all statistics" = "Réinitialiser toutes les statistiques"; + +/* No comment provided by engineer. */ +"Reset all statistics?" = "Réinitialiser toutes les statistiques ?"; + /* No comment provided by engineer. */ "Reset colors" = "Réinitialisation des couleurs"; +/* No comment provided by engineer. */ +"Reset to app theme" = "Réinitialisation au thème de l'appli"; + /* No comment provided by engineer. */ "Reset to defaults" = "Réinitialisation des valeurs par défaut"; +/* No comment provided by engineer. */ +"Reset to user theme" = "Réinitialisation au thème de l'utilisateur"; + /* No comment provided by engineer. */ "Restart the app to create a new chat profile" = "Redémarrez l'application pour créer un nouveau profil de chat"; @@ -3357,6 +3642,12 @@ /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Les serveurs WebRTC ICE sauvegardés seront supprimés"; +/* No comment provided by engineer. */ +"Scale" = "Échelle"; + +/* No comment provided by engineer. */ +"Scan / Paste link" = "Scanner / Coller le lien"; + /* No comment provided by engineer. */ "Scan code" = "Scanner le code"; @@ -3384,6 +3675,9 @@ /* network option */ "sec" = "sec"; +/* No comment provided by engineer. */ +"Secondary" = "Secondaire"; + /* time unit */ "seconds" = "secondes"; @@ -3393,6 +3687,9 @@ /* server test step */ "Secure queue" = "File d'attente sécurisée"; +/* No comment provided by engineer. */ +"Secured" = "Sécurisé"; + /* No comment provided by engineer. */ "Security assessment" = "Évaluation de sécurité"; @@ -3405,6 +3702,9 @@ /* No comment provided by engineer. */ "Select" = "Choisir"; +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Les préférences de chat sélectionnées interdisent ce message."; + /* No comment provided by engineer. */ "Self-destruct" = "Autodestruction"; @@ -3438,6 +3738,9 @@ /* No comment provided by engineer. */ "Send disappearing message" = "Envoyer un message éphémère"; +/* No comment provided by engineer. */ +"Send errors" = "Erreurs d'envoi"; + /* No comment provided by engineer. */ "Send link previews" = "Envoi d'aperçus de liens"; @@ -3504,15 +3807,36 @@ /* copied message info */ "Sent at: %@" = "Envoyé le : %@"; +/* No comment provided by engineer. */ +"Sent directly" = "Envoyé directement"; + /* notification */ "Sent file event" = "Événement de fichier envoyé"; /* message info title */ "Sent message" = "Message envoyé"; +/* No comment provided by engineer. */ +"Sent messages" = "Messages envoyés"; + /* No comment provided by engineer. */ "Sent messages will be deleted after set time." = "Les messages envoyés seront supprimés après une durée déterminée."; +/* No comment provided by engineer. */ +"Sent reply" = "Réponse envoyée"; + +/* No comment provided by engineer. */ +"Sent total" = "Total envoyé"; + +/* No comment provided by engineer. */ +"Sent via proxy" = "Envoyé via le proxy"; + +/* No comment provided by engineer. */ +"Server address" = "Adresse du serveur"; + +/* No comment provided by engineer. */ +"Server address is incompatible with network settings: %@." = "L'adresse du serveur est incompatible avec les paramètres réseau : %@."; + /* srv error text. */ "Server address is incompatible with network settings." = "L'adresse du serveur est incompatible avec les paramètres du réseau."; @@ -3528,12 +3852,24 @@ /* No comment provided by engineer. */ "Server test failed!" = "Échec du test du serveur !"; +/* No comment provided by engineer. */ +"Server type" = "Type de serveur"; + /* srv error text */ "Server version is incompatible with network settings." = "La version du serveur est incompatible avec les paramètres du réseau."; +/* No comment provided by engineer. */ +"Server version is incompatible with your app: %@." = "La version du serveur est incompatible avec votre appli : %@."; + /* No comment provided by engineer. */ "Servers" = "Serveurs"; +/* No comment provided by engineer. */ +"Servers info" = "Infos serveurs"; + +/* No comment provided by engineer. */ +"Servers statistics will be reset - this cannot be undone!" = "Les statistiques des serveurs seront réinitialisées - il n'est pas possible de revenir en arrière !"; + /* No comment provided by engineer. */ "Session code" = "Code de session"; @@ -3543,6 +3879,9 @@ /* No comment provided by engineer. */ "Set contact name…" = "Définir le nom du contact…"; +/* No comment provided by engineer. */ +"Set default theme" = "Définir le thème par défaut"; + /* No comment provided by engineer. */ "Set group preferences" = "Définir les préférences du groupe"; @@ -3612,6 +3951,9 @@ /* No comment provided by engineer. */ "Show message status" = "Afficher le statut du message"; +/* No comment provided by engineer. */ +"Show percentage" = "Afficher le pourcentage"; + /* No comment provided by engineer. */ "Show preview" = "Afficher l'aperçu"; @@ -3666,6 +4008,9 @@ /* No comment provided by engineer. */ "Simplified incognito mode" = "Mode incognito simplifié"; +/* No comment provided by engineer. */ +"Size" = "Taille"; + /* No comment provided by engineer. */ "Skip" = "Passer"; @@ -3675,6 +4020,9 @@ /* No comment provided by engineer. */ "Small groups (max 20)" = "Petits groupes (max 20)"; +/* No comment provided by engineer. */ +"SMP server" = "Serveur SMP"; + /* No comment provided by engineer. */ "SMP servers" = "Serveurs SMP"; @@ -3699,9 +4047,15 @@ /* No comment provided by engineer. */ "Start migration" = "Démarrer la migration"; +/* No comment provided by engineer. */ +"Starting from %@." = "À partir de %@."; + /* No comment provided by engineer. */ "starting…" = "lancement…"; +/* No comment provided by engineer. */ +"Statistics" = "Statistiques"; + /* No comment provided by engineer. */ "Stop" = "Arrêter"; @@ -3744,6 +4098,15 @@ /* No comment provided by engineer. */ "Submit" = "Soumettre"; +/* No comment provided by engineer. */ +"Subscribed" = "Inscrit"; + +/* No comment provided by engineer. */ +"Subscription errors" = "Erreurs d'inscription"; + +/* No comment provided by engineer. */ +"Subscriptions ignored" = "Inscriptions ignorées"; + /* No comment provided by engineer. */ "Support SimpleX Chat" = "Supporter SimpleX Chat"; @@ -3792,6 +4155,9 @@ /* No comment provided by engineer. */ "TCP_KEEPINTVL" = "TCP_KEEPINTVL"; +/* No comment provided by engineer. */ +"Temporary file error" = "Erreur de fichier temporaire"; + /* server test failure */ "Test failed at step %@." = "Échec du test à l'étape %@."; @@ -3873,6 +4239,9 @@ /* No comment provided by engineer. */ "The text you pasted is not a SimpleX link." = "Le texte collé n'est pas un lien SimpleX."; +/* No comment provided by engineer. */ +"Themes" = "Thèmes"; + /* No comment provided by engineer. */ "These settings are for your current profile **%@**." = "Ces paramètres s'appliquent à votre profil actuel **%@**."; @@ -3915,9 +4284,15 @@ /* No comment provided by engineer. */ "This is your own SimpleX address!" = "Voici votre propre adresse SimpleX !"; +/* No comment provided by engineer. */ +"This link was used with another mobile device, please create a new link on the desktop." = "Ce lien a été utilisé avec un autre appareil mobile, veuillez créer un nouveau lien sur le bureau."; + /* No comment provided by engineer. */ "This setting applies to messages in your current chat profile **%@**." = "Ce paramètre s'applique aux messages de votre profil de chat actuel **%@**."; +/* No comment provided by engineer. */ +"Title" = "Titre"; + /* No comment provided by engineer. */ "To ask any questions and to receive updates:" = "Si vous avez des questions et que vous souhaitez des réponses :"; @@ -3957,9 +4332,15 @@ /* No comment provided by engineer. */ "Toggle incognito when connecting." = "Basculer en mode incognito lors de la connexion."; +/* No comment provided by engineer. */ +"Total" = "Total"; + /* No comment provided by engineer. */ "Transport isolation" = "Transport isolé"; +/* No comment provided by engineer. */ +"Transport sessions" = "Sessions de transport"; + /* No comment provided by engineer. */ "Trying to connect to the server used to receive messages from this contact (error: %@)." = "Tentative de connexion au serveur utilisé pour recevoir les messages de ce contact (erreur : %@)."; @@ -4095,12 +4476,21 @@ /* No comment provided by engineer. */ "Upgrade and open chat" = "Mettre à niveau et ouvrir le chat"; +/* No comment provided by engineer. */ +"Upload errors" = "Erreurs de téléversement"; + /* No comment provided by engineer. */ "Upload failed" = "Échec de l'envoi"; /* server test step */ "Upload file" = "Transférer le fichier"; +/* No comment provided by engineer. */ +"Uploaded" = "Téléversé"; + +/* No comment provided by engineer. */ +"Uploaded files" = "Fichiers téléversés"; + /* No comment provided by engineer. */ "Uploading archive" = "Envoi de l'archive"; @@ -4146,6 +4536,9 @@ /* No comment provided by engineer. */ "User profile" = "Profil d'utilisateur"; +/* No comment provided by engineer. */ +"User selection" = "Sélection de l'utilisateur"; + /* No comment provided by engineer. */ "Using .onion hosts requires compatible VPN provider." = "L'utilisation des hôtes .onion nécessite un fournisseur VPN compatible."; @@ -4254,6 +4647,12 @@ /* No comment provided by engineer. */ "Waiting for video" = "En attente de la vidéo"; +/* No comment provided by engineer. */ +"Wallpaper accent" = "Accentuation du papier-peint"; + +/* No comment provided by engineer. */ +"Wallpaper background" = "Fond d'écran"; + /* No comment provided by engineer. */ "wants to connect to you!" = "veut établir une connexion !"; @@ -4326,9 +4725,15 @@ /* snd error text */ "Wrong key or unknown connection - most likely this connection is deleted." = "Clé erronée ou connexion non identifiée - il est très probable que cette connexion soit supprimée."; +/* file error text */ +"Wrong key or unknown file chunk address - most likely file is deleted." = "Mauvaise clé ou adresse inconnue du bloc de données du fichier - le fichier est probablement supprimé."; + /* No comment provided by engineer. */ "Wrong passphrase!" = "Mauvaise phrase secrète !"; +/* No comment provided by engineer. */ +"XFTP server" = "Serveur XFTP"; + /* No comment provided by engineer. */ "XFTP servers" = "Serveurs XFTP"; @@ -4386,6 +4791,9 @@ /* No comment provided by engineer. */ "You are invited to group" = "Vous êtes invité·e au groupe"; +/* No comment provided by engineer. */ +"You are not connected to these servers. Private routing is used to deliver messages to them." = "Vous n'êtes pas connecté à ces serveurs. Le routage privé est utilisé pour leur délivrer des messages."; + /* No comment provided by engineer. */ "you are observer" = "vous êtes observateur"; diff --git a/apps/ios/hu.lproj/Localizable.strings b/apps/ios/hu.lproj/Localizable.strings index ab732754c9..3027e1df3f 100644 --- a/apps/ios/hu.lproj/Localizable.strings +++ b/apps/ios/hu.lproj/Localizable.strings @@ -68,7 +68,7 @@ "**Add contact**: to create a new invitation link, or connect via a link you received." = "**Ismerős hozzáadása**: új meghívó hivatkozás létrehozásához, vagy egy kapott hivatkozáson keresztül történő kapcsolódáshoz."; /* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Új ismerős hozzáadása**: egyszer használatos QR-kód vagy hivatkozás létrehozása a kapcsolattartóhoz."; +"**Add new contact**: to create your one-time QR Code for your contact." = "**Új ismerős hozzáadása**: egyszer használatos QR-kód vagy hivatkozás létrehozása az ismerőse számára."; /* No comment provided by engineer. */ "**Create group**: to create a new group." = "**Csoport létrehozása**: új csoport létrehozásához."; @@ -86,7 +86,7 @@ "**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." = "**Legprivátabb**: ne használja a SimpleX Chat értesítési kiszolgálót, rendszeresen ellenőrizze az üzeneteket a háttérben (attól függően, hogy milyen gyakran használja az alkalmazást)."; /* No comment provided by engineer. */ -"**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." = "**Megjegyzés**: ha két eszközön is ugyanazt az adatbázist használja, akkor biztonsági védelemként megszakítja a kapcsolataiból érkező üzenetek visszafejtését."; +"**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." = "**Megjegyzés**: ha két eszközön is ugyanazt az adatbázist használja, akkor biztonsági védelemként megszakítja az ismerőseitől érkező üzenetek visszafejtését."; /* No comment provided by engineer. */ "**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Figyelem**: NEM tudja visszaállítani vagy megváltoztatni jelmondatát, ha elveszíti azt."; @@ -359,6 +359,9 @@ /* No comment provided by engineer. */ "Acknowledgement errors" = "Nyugtázott hibák"; +/* No comment provided by engineer. */ +"Active connections" = "Aktív kapcsolatok száma"; + /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Cím hozzáadása a profilhoz, hogy az ismerősei megoszthassák másokkal. A profilfrissítés elküldésre kerül az ismerősei számára."; @@ -630,7 +633,7 @@ "Auto-accept contact requests" = "Kapcsolódási kérelmek automatikus elfogadása"; /* No comment provided by engineer. */ -"Auto-accept images" = "Fotók automatikus elfogadása"; +"Auto-accept images" = "Képek automatikus elfogadása"; /* No comment provided by engineer. */ "Back" = "Vissza"; @@ -686,7 +689,7 @@ /* rcv group event chat item */ "blocked %@" = "letiltotta őt: %@"; -/* marked deleted chat item preview text */ +/* blocked chat item */ "blocked by admin" = "letiltva az admin által"; /* No comment provided by engineer. */ @@ -909,6 +912,9 @@ /* No comment provided by engineer. */ "Configure ICE servers" = "ICE kiszolgálók beállítása"; +/* No comment provided by engineer. */ +"Configured %@ servers" = "Beállított %@ kiszolgálók"; + /* No comment provided by engineer. */ "Confirm" = "Megerősítés"; @@ -1291,7 +1297,7 @@ "Delete all files" = "Minden fájl törlése"; /* No comment provided by engineer. */ -"Delete and notify contact" = "Törlés és ismerős értesítése"; +"Delete and notify contact" = "Törlés, és az ismerős értesítése"; /* No comment provided by engineer. */ "Delete archive" = "Archívum törlése"; @@ -1336,7 +1342,7 @@ "Delete for everyone" = "Törlés mindenkinél"; /* No comment provided by engineer. */ -"Delete for me" = "Törlés nálam"; +"Delete for me" = "Csak nálam"; /* No comment provided by engineer. */ "Delete group" = "Csoport törlése"; @@ -1411,7 +1417,7 @@ "Delivery" = "Kézbesítés"; /* No comment provided by engineer. */ -"Delivery receipts are disabled!" = "Kézbesítési igazolások kikapcsolva!"; +"Delivery receipts are disabled!" = "A kézbesítési jelentések ki vannak kapcsolva!"; /* No comment provided by engineer. */ "Delivery receipts!" = "Kézbesítési igazolások!"; @@ -1447,10 +1453,10 @@ "Device" = "Eszköz"; /* No comment provided by engineer. */ -"Device authentication is disabled. Turning off SimpleX Lock." = "Eszközhitelesítés kikapcsolva. SimpleX zárolás kikapcsolása."; +"Device authentication is disabled. Turning off SimpleX Lock." = "A készüléken nincs beállítva a képernyőzár. A SimpleX zár ki van kapcsolva."; /* No comment provided by engineer. */ -"Device authentication is not enabled. You can turn on SimpleX Lock via Settings, once you enable device authentication." = "Eszközhitelesítés nem engedélyezett.A SimpleX zárolás bekapcsolható a Beállításokon keresztül, miután az eszköz hitelesítés engedélyezésre került."; +"Device authentication is not enabled. You can turn on SimpleX Lock via Settings, once you enable device authentication." = "A készüléken nincs beállítva a képernyőzár. A SimpleX zár az „Adatvédelem és biztonság” menüben kapcsolható be, miután beállította a képernyőzárat az eszközén."; /* No comment provided by engineer. */ "different migration in the app/database: %@ / %@" = "különböző átköltöztetések az alkalmazásban/adatbázisban: %@ / %@"; @@ -1474,7 +1480,7 @@ "Disable for all" = "Letiltás mindenki számára"; /* authentication reason */ -"Disable SimpleX Lock" = "SimpleX zárolás kikapcsolása"; +"Disable SimpleX Lock" = "SimpleX zár kikapcsolása"; /* No comment provided by engineer. */ "disabled" = "letiltva"; @@ -1618,7 +1624,7 @@ "Enable self-destruct passcode" = "Önmegsemmisítő jelkód engedélyezése"; /* authentication reason */ -"Enable SimpleX Lock" = "SimpleX zárolás engedélyezése"; +"Enable SimpleX Lock" = "SimpleX zár bekapcsolása"; /* No comment provided by engineer. */ "Enable TCP keep-alive" = "TCP életben tartása"; @@ -2471,7 +2477,7 @@ "It allows having many anonymous connections without any shared data between them in a single chat profile." = "Lehetővé teszi, hogy egyetlen csevegőprofilon belül több anonim kapcsolat legyen, anélkül, hogy megosztott adatok lennének közöttük."; /* No comment provided by engineer. */ -"It can happen when you or your connection used the old database backup." = "Ez akkor fordulhat elő, ha ön vagy a kapcsolata régi adatbázis biztonsági mentést használt."; +"It can happen when you or your connection used the old database backup." = "Ez akkor fordulhat elő, ha ön vagy az ismerőse régi adatbázis biztonsági mentést használt."; /* No comment provided by engineer. */ "It can happen when:\n1. The messages expired in the sending client after 2 days or on the server after 30 days.\n2. Message decryption failed, because you or your contact used old database backup.\n3. The connection was compromised." = "Ez akkor fordulhat elő, ha:\n1. Az üzenetek 2 nap után, vagy a kiszolgálón 30 nap után lejártak.\n2. Az üzenet visszafejtése sikertelen volt, mert vagy az ismerőse régebbi adatbázis biztonsági mentést használt.\n3. A kapcsolat sérült."; @@ -2689,6 +2695,9 @@ /* notification */ "message received" = "üzenet érkezett"; +/* No comment provided by engineer. */ +"Message reception" = "Üzenetjelentés"; + /* No comment provided by engineer. */ "Message routing fallback" = "Üzenet útválasztási tartalék"; @@ -2940,8 +2949,8 @@ time to disappear */ "off" = "kikapcsolva"; -/* No comment provided by engineer. */ -"Off" = "Ki"; +/* blur media */ +"Off" = "Kikapcsolva"; /* feature offered item */ "offered %@" = "%@ ajánlotta"; @@ -2962,7 +2971,7 @@ "Old database archive" = "Régi adatbázis archívum"; /* group pref value */ -"on" = "be"; +"on" = "bekapcsolva"; /* No comment provided by engineer. */ "One-time invitation link" = "Egyszer használatos meghívó hivatkozás"; @@ -3066,6 +3075,9 @@ /* No comment provided by engineer. */ "Other" = "További"; +/* No comment provided by engineer. */ +"Other %@ servers" = "További %@ kiszolgálók"; + /* No comment provided by engineer. */ "other errors" = "egyéb hibák"; @@ -3219,6 +3231,9 @@ /* No comment provided by engineer. */ "Private routing" = "Privát útválasztás"; +/* No comment provided by engineer. */ +"Private routing error" = "Privát útválasztási hiba"; + /* No comment provided by engineer. */ "Profile and server connections" = "Profil és kiszolgálókapcsolatok"; @@ -3457,7 +3472,7 @@ "removed contact address" = "törölt kapcsolattartási cím"; /* profile update event chat item */ -"removed profile picture" = "törölt profilkép"; +"removed profile picture" = "törölte a profilképét"; /* rcv group event chat item */ "removed you" = "eltávolítottak"; @@ -3819,6 +3834,9 @@ /* No comment provided by engineer. */ "Server address" = "Kiszolgáló címe"; +/* No comment provided by engineer. */ +"Server address is incompatible with network settings: %@." = "A kiszolgáló címe nem kompatibilis a hálózati beállításokkal: %@."; + /* srv error text. */ "Server address is incompatible with network settings." = "A kiszolgáló címe nem kompatibilis a hálózati beállításokkal."; @@ -3840,6 +3858,9 @@ /* srv error text */ "Server version is incompatible with network settings." = "A kiszolgáló verziója nem kompatibilis a hálózati beállításokkal."; +/* No comment provided by engineer. */ +"Server version is incompatible with your app: %@." = "A kiszolgáló verziója nem kompatibilis az alkalmazással: %@."; + /* No comment provided by engineer. */ "Servers" = "Kiszolgálók"; @@ -3930,6 +3951,9 @@ /* No comment provided by engineer. */ "Show message status" = "Üzenet állapot megjelenítése"; +/* No comment provided by engineer. */ +"Show percentage" = "Százalék megjelenítése"; + /* No comment provided by engineer. */ "Show preview" = "Előnézet megjelenítése"; @@ -3970,16 +3994,16 @@ "SimpleX links not allowed" = "A SimpleX hivatkozások küldése le van tiltva"; /* No comment provided by engineer. */ -"SimpleX Lock" = "SimpleX zárolás"; +"SimpleX Lock" = "SimpleX zár"; /* No comment provided by engineer. */ -"SimpleX Lock mode" = "SimpleX zárolási mód"; +"SimpleX Lock mode" = "Zárolási mód"; /* No comment provided by engineer. */ -"SimpleX Lock not enabled!" = "SimpleX zárolás nincs engedélyezve!"; +"SimpleX Lock not enabled!" = "A SimpleX zár nincs bekapcsolva!"; /* No comment provided by engineer. */ -"SimpleX Lock turned on" = "SimpleX zárolás bekapcsolva"; +"SimpleX Lock turned on" = "SimpleX zár bekapcsolva"; /* simplex link type */ "SimpleX one-time invitation" = "SimpleX egyszer használatos meghívó"; @@ -4096,7 +4120,7 @@ "System authentication" = "Rendszerhitelesítés"; /* No comment provided by engineer. */ -"Take picture" = "Fotó készítése"; +"Take picture" = "Kép készítése"; /* No comment provided by engineer. */ "Tap button " = "Koppintson a "; @@ -4228,7 +4252,7 @@ "They can be overridden in contact and group settings." = "Ezek felülbírálhatóak az ismerős- és csoportbeállításokban."; /* No comment provided by engineer. */ -"This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Ez a művelet nem vonható vissza - az összes fogadott és küldött fájl a médiatartalommal együtt törlésre kerülnek. Az alacsony felbontású fotók viszont megmaradnak."; +"This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Ez a művelet nem vonható vissza - az összes fogadott és küldött fájl a médiatartalommal együtt törlésre kerülnek. Az alacsony felbontású képek viszont megmaradnak."; /* No comment provided by engineer. */ "This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes." = "Ez a művelet nem vonható vissza - a kiválasztottnál korábban küldött és fogadott üzenetek törlésre kerülnek. Ez több percet is igénybe vehet."; @@ -4291,7 +4315,7 @@ "To protect timezone, image/voice files use UTC." = "Az időzóna védelme érdekében a kép-/hangfájlok UTC-t használnak."; /* No comment provided by engineer. */ -"To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "Az adatavédelem érdekében kapcsolja be a SimpleX zárolás funkciót.\nA funkció engedélyezése előtt a rendszer felszólítja a hitelesítés befejezésére."; +"To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "A biztonsága érdekében kapcsolja be a SimpleX zár funkciót.\nA funkció bekapcsolása előtt a rendszer felszólítja a képernyőzár beállítására az eszközén."; /* No comment provided by engineer. */ "To protect your IP address, private routing uses your SMP servers to deliver messages." = "Az IP-cím védelmének érdekében a privát útválasztás az SMP kiszolgálókat használja az üzenetek kézbesítéséhez."; @@ -4300,7 +4324,7 @@ "To record voice message please grant permission to use Microphone." = "Hangüzenet rögzítéséhez adjon engedélyt a mikrofon használathoz."; /* No comment provided by engineer. */ -"To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "Rejtett profilja feltárásához írja be a teljes jelszót a keresőmezőbe a **Csevegési profiljai** oldalon."; +"To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "Rejtett profilja megjelenítéséhez írja be a teljes jelszavát a keresőmezőbe a **Csevegési profilok** menüben."; /* No comment provided by engineer. */ "To support instant push notifications the chat database has to be migrated." = "Az azonnali push értesítések támogatásához a csevegési adatbázis migrálása szükséges."; @@ -4795,7 +4819,7 @@ "You can give another try." = "Megpróbálhatja még egyszer."; /* No comment provided by engineer. */ -"You can hide or mute a user profile - swipe it to the right." = "Elrejthet vagy némíthat egy felhasználói profilt – csúsztasson jobbra."; +"You can hide or mute a user profile - swipe it to the right." = "Elrejtheti vagy lenémíthatja a felhasználó profiljait - csúsztassa jobbra a profilt."; /* No comment provided by engineer. */ "You can make it visible to your SimpleX contacts via Settings." = "Láthatóvá teheti SimpleX ismerősök számára a Beállításokban."; @@ -4819,7 +4843,7 @@ "You can start chat via app Settings / Database or by restarting the app" = "A csevegést az alkalmazás Beállítások / Adatbázis menü segítségével vagy az alkalmazás újraindításával indíthatja el"; /* No comment provided by engineer. */ -"You can turn on SimpleX Lock via Settings." = "A SimpleX zárolás a Beállításokon keresztül kapcsolható be."; +"You can turn on SimpleX Lock via Settings." = "A SimpleX zár az „Adatvédelem és biztonság” menüben kapcsolható be."; /* No comment provided by engineer. */ "You can use markdown to format messages:" = "Üzenetek formázása a szövegbe szúrt speciális karakterekkel:"; @@ -4945,7 +4969,7 @@ "Your chat database is not encrypted - set passphrase to encrypt it." = "A csevegési adatbázis nincs titkosítva – adjon meg egy jelmondatot a titkosításhoz."; /* No comment provided by engineer. */ -"Your chat profiles" = "Csevegési profiljai"; +"Your chat profiles" = "Csevegési profilok"; /* No comment provided by engineer. */ "Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)." = "Az ismerősének online kell lennie ahhoz, hogy a kapcsolat létrejöjjön.\nVisszavonhatja ezt a kapcsolatfelvételt és törölheti az ismerőst (ezt később ismét megpróbálhatja egy új hivatkozással)."; diff --git a/apps/ios/it.lproj/Localizable.strings b/apps/ios/it.lproj/Localizable.strings index 24b604ecc0..5854d28162 100644 --- a/apps/ios/it.lproj/Localizable.strings +++ b/apps/ios/it.lproj/Localizable.strings @@ -359,6 +359,9 @@ /* No comment provided by engineer. */ "Acknowledgement errors" = "Errori di riconoscimento"; +/* No comment provided by engineer. */ +"Active connections" = "Connessioni attive"; + /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Aggiungi l'indirizzo al tuo profilo, in modo che i tuoi contatti possano condividerlo con altre persone. L'aggiornamento del profilo verrà inviato ai tuoi contatti."; @@ -686,7 +689,7 @@ /* rcv group event chat item */ "blocked %@" = "ha bloccato %@"; -/* marked deleted chat item preview text */ +/* blocked chat item */ "blocked by admin" = "bloccato dall'amministratore"; /* No comment provided by engineer. */ @@ -909,6 +912,9 @@ /* No comment provided by engineer. */ "Configure ICE servers" = "Configura server ICE"; +/* No comment provided by engineer. */ +"Configured %@ servers" = "Configurati %@ server"; + /* No comment provided by engineer. */ "Confirm" = "Conferma"; @@ -2689,6 +2695,9 @@ /* notification */ "message received" = "messaggio ricevuto"; +/* No comment provided by engineer. */ +"Message reception" = "Ricezione messaggi"; + /* No comment provided by engineer. */ "Message routing fallback" = "Ripiego instradamento messaggio"; @@ -2940,7 +2949,7 @@ time to disappear */ "off" = "off"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Off"; /* feature offered item */ @@ -3066,6 +3075,9 @@ /* No comment provided by engineer. */ "Other" = "Altro"; +/* No comment provided by engineer. */ +"Other %@ servers" = "Altri %@ server"; + /* No comment provided by engineer. */ "other errors" = "altri errori"; @@ -3219,6 +3231,9 @@ /* No comment provided by engineer. */ "Private routing" = "Instradamento privato"; +/* No comment provided by engineer. */ +"Private routing error" = "Errore di instradamento privato"; + /* No comment provided by engineer. */ "Profile and server connections" = "Profilo e connessioni al server"; @@ -3819,6 +3834,9 @@ /* No comment provided by engineer. */ "Server address" = "Indirizzo server"; +/* No comment provided by engineer. */ +"Server address is incompatible with network settings: %@." = "L'indirizzo del server è incompatibile con le impostazioni di rete: %@."; + /* srv error text. */ "Server address is incompatible with network settings." = "L'indirizzo del server non è compatibile con le impostazioni di rete."; @@ -3840,6 +3858,9 @@ /* srv error text */ "Server version is incompatible with network settings." = "La versione del server non è compatibile con le impostazioni di rete."; +/* No comment provided by engineer. */ +"Server version is incompatible with your app: %@." = "La versione del server è incompatibile con la tua app: %@."; + /* No comment provided by engineer. */ "Servers" = "Server"; @@ -3930,6 +3951,9 @@ /* No comment provided by engineer. */ "Show message status" = "Mostra stato del messaggio"; +/* No comment provided by engineer. */ +"Show percentage" = "Mostra percentuale"; + /* No comment provided by engineer. */ "Show preview" = "Mostra anteprima"; diff --git a/apps/ios/ja.lproj/Localizable.strings b/apps/ios/ja.lproj/Localizable.strings index 0f5ccc2b8c..7c8897e6cb 100644 --- a/apps/ios/ja.lproj/Localizable.strings +++ b/apps/ios/ja.lproj/Localizable.strings @@ -2238,7 +2238,7 @@ time to disappear */ "off" = "オフ"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "オフ"; /* feature offered item */ diff --git a/apps/ios/nl.lproj/Localizable.strings b/apps/ios/nl.lproj/Localizable.strings index 9cd3c078b3..23eccdcf3b 100644 --- a/apps/ios/nl.lproj/Localizable.strings +++ b/apps/ios/nl.lproj/Localizable.strings @@ -334,6 +334,9 @@ /* No comment provided by engineer. */ "above, then choose:" = "hier boven, kies dan:"; +/* No comment provided by engineer. */ +"Accent" = "Accent"; + /* accept contact request via notification accept incoming call via notification */ "Accept" = "Accepteer"; @@ -350,6 +353,15 @@ /* call status */ "accepted call" = "geaccepteerde oproep"; +/* No comment provided by engineer. */ +"Acknowledged" = "Erkend"; + +/* No comment provided by engineer. */ +"Acknowledgement errors" = "Bevestigingsfouten"; + +/* No comment provided by engineer. */ +"Active connections" = "Actieve verbindingen"; + /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Voeg een adres toe aan uw profiel, zodat uw contacten het met andere mensen kunnen delen. Profiel update wordt naar uw contacten verzonden."; @@ -372,7 +384,16 @@ "Add to another device" = "Toevoegen aan een ander apparaat"; /* No comment provided by engineer. */ -"Add welcome message" = "Welkomst bericht toevoegen"; +"Add welcome message" = "Welkom bericht toevoegen"; + +/* No comment provided by engineer. */ +"Additional accent" = "Extra accent"; + +/* No comment provided by engineer. */ +"Additional accent 2" = "Extra accent 2"; + +/* No comment provided by engineer. */ +"Additional secondary" = "Extra secundair"; /* No comment provided by engineer. */ "Address" = "Adres"; @@ -395,6 +416,9 @@ /* No comment provided by engineer. */ "Advanced network settings" = "Geavanceerde netwerk instellingen"; +/* No comment provided by engineer. */ +"Advanced settings" = "Geavanceerde instellingen"; + /* chat item text */ "agreeing encryption for %@…" = "versleuteling overeenkomen voor %@…"; @@ -410,6 +434,9 @@ /* No comment provided by engineer. */ "All data is erased when it is entered." = "Alle gegevens worden bij het invoeren gewist."; +/* No comment provided by engineer. */ +"All data is private to your device." = "Alle gegevens zijn privé op uw apparaat."; + /* No comment provided by engineer. */ "All group members will remain connected." = "Alle groepsleden blijven verbonden."; @@ -425,6 +452,9 @@ /* No comment provided by engineer. */ "All new messages from %@ will be hidden!" = "Alle nieuwe berichten van %@ worden verborgen!"; +/* No comment provided by engineer. */ +"All profiles" = "Alle profielen"; + /* No comment provided by engineer. */ "All your contacts will remain connected." = "Al uw contacten blijven verbonden."; @@ -450,10 +480,10 @@ "Allow irreversible message deletion only if your contact allows it to you. (24 hours)" = "Sta het onomkeerbaar verwijderen van berichten alleen toe als uw contact dit toestaat. (24 uur)"; /* No comment provided by engineer. */ -"Allow message reactions only if your contact allows them." = "Sta berichtreacties alleen toe als uw contact dit toestaat."; +"Allow message reactions only if your contact allows them." = "Sta bericht reacties alleen toe als uw contact dit toestaat."; /* No comment provided by engineer. */ -"Allow message reactions." = "Sta berichtreacties toe."; +"Allow message reactions." = "Sta bericht reacties toe."; /* No comment provided by engineer. */ "Allow sending direct messages to members." = "Sta het verzenden van directe berichten naar leden toe."; @@ -480,7 +510,7 @@ "Allow voice messages?" = "Spraak berichten toestaan?"; /* No comment provided by engineer. */ -"Allow your contacts adding message reactions." = "Sta uw contactpersonen toe om berichtreacties toe te voegen."; +"Allow your contacts adding message reactions." = "Sta uw contactpersonen toe om bericht reacties toe te voegen."; /* No comment provided by engineer. */ "Allow your contacts to call you." = "Sta toe dat uw contacten u bellen."; @@ -551,6 +581,9 @@ /* No comment provided by engineer. */ "Apply" = "Toepassen"; +/* No comment provided by engineer. */ +"Apply to" = "Toepassen op"; + /* No comment provided by engineer. */ "Archive and upload" = "Archiveren en uploaden"; @@ -560,6 +593,9 @@ /* No comment provided by engineer. */ "Attach" = "Bijvoegen"; +/* No comment provided by engineer. */ +"attempts" = "pogingen"; + /* No comment provided by engineer. */ "Audio & video calls" = "Audio en video gesprekken"; @@ -602,6 +638,9 @@ /* No comment provided by engineer. */ "Back" = "Terug"; +/* No comment provided by engineer. */ +"Background" = "Achtergrond"; + /* No comment provided by engineer. */ "Bad desktop address" = "Onjuist desktopadres"; @@ -623,6 +662,9 @@ /* No comment provided by engineer. */ "Better messages" = "Betere berichten"; +/* No comment provided by engineer. */ +"Black" = "Zwart"; + /* No comment provided by engineer. */ "Block" = "Blokkeren"; @@ -647,7 +689,7 @@ /* rcv group event chat item */ "blocked %@" = "geblokkeerd %@"; -/* marked deleted chat item preview text */ +/* blocked chat item */ "blocked by admin" = "geblokkeerd door beheerder"; /* No comment provided by engineer. */ @@ -657,7 +699,7 @@ "bold" = "vetgedrukt"; /* No comment provided by engineer. */ -"Both you and your contact can add message reactions." = "Zowel u als uw contact kunnen berichtreacties toevoegen."; +"Both you and your contact can add message reactions." = "Zowel u als uw contact kunnen bericht reacties toevoegen."; /* No comment provided by engineer. */ "Both you and your contact can irreversibly delete sent messages. (24 hours)" = "Zowel jij als je contact kunnen verzonden berichten onherroepelijk verwijderen. (24 uur)"; @@ -713,6 +755,9 @@ /* No comment provided by engineer. */ "Cannot access keychain to save database password" = "Geen toegang tot de keychain om database wachtwoord op te slaan"; +/* No comment provided by engineer. */ +"Cannot forward message" = "Kan bericht niet doorsturen"; + /* No comment provided by engineer. */ "Cannot receive file" = "Kan bestand niet ontvangen"; @@ -771,6 +816,9 @@ /* No comment provided by engineer. */ "Chat archive" = "Gesprek archief"; +/* No comment provided by engineer. */ +"Chat colors" = "Chat kleuren"; + /* No comment provided by engineer. */ "Chat console" = "Chat console"; @@ -798,6 +846,9 @@ /* No comment provided by engineer. */ "Chat preferences" = "Gesprek voorkeuren"; +/* No comment provided by engineer. */ +"Chat theme" = "Chat thema"; + /* No comment provided by engineer. */ "Chats" = "Gesprekken"; @@ -816,6 +867,15 @@ /* No comment provided by engineer. */ "Choose from library" = "Kies uit bibliotheek"; +/* No comment provided by engineer. */ +"Chunks deleted" = "Stukken verwijderd"; + +/* No comment provided by engineer. */ +"Chunks downloaded" = "Stukken gedownload"; + +/* No comment provided by engineer. */ +"Chunks uploaded" = "Stukken geüpload"; + /* No comment provided by engineer. */ "Clear" = "Wissen"; @@ -831,6 +891,9 @@ /* No comment provided by engineer. */ "Clear verification" = "Verwijderd verificatie"; +/* No comment provided by engineer. */ +"Color mode" = "Kleur mode"; + /* No comment provided by engineer. */ "colored" = "gekleurd"; @@ -843,9 +906,15 @@ /* No comment provided by engineer. */ "complete" = "compleet"; +/* No comment provided by engineer. */ +"Completed" = "voltooid"; + /* No comment provided by engineer. */ "Configure ICE servers" = "ICE servers configureren"; +/* No comment provided by engineer. */ +"Configured %@ servers" = "%@ servers geconfigureerd"; + /* No comment provided by engineer. */ "Confirm" = "Bevestigen"; @@ -912,18 +981,27 @@ /* No comment provided by engineer. */ "connected" = "verbonden"; +/* No comment provided by engineer. */ +"Connected" = "Verbonden"; + /* No comment provided by engineer. */ "Connected desktop" = "Verbonden desktop"; /* rcv group event chat item */ "connected directly" = "direct verbonden"; +/* No comment provided by engineer. */ +"Connected servers" = "Verbonden servers"; + /* No comment provided by engineer. */ "Connected to desktop" = "Verbonden met desktop"; /* No comment provided by engineer. */ "connecting" = "Verbinden"; +/* No comment provided by engineer. */ +"Connecting" = "Verbinden"; + /* No comment provided by engineer. */ "connecting (accepted)" = "verbinden (geaccepteerd)"; @@ -972,9 +1050,15 @@ /* No comment provided by engineer. */ "Connection timeout" = "Timeout verbinding"; +/* No comment provided by engineer. */ +"Connection with desktop stopped" = "Verbinding met desktop is gestopt"; + /* connection information */ "connection:%@" = "verbinding:%@"; +/* No comment provided by engineer. */ +"Connections" = "Verbindingen"; + /* profile update event chat item */ "contact %@ changed to %@" = "contactpersoon %1$@ gewijzigd in %2$@"; @@ -1017,6 +1101,9 @@ /* No comment provided by engineer. */ "Copy" = "Kopiëren"; +/* No comment provided by engineer. */ +"Copy error" = "Kopieerfout"; + /* No comment provided by engineer. */ "Core version: v%@" = "Core versie: v% @"; @@ -1062,6 +1149,9 @@ /* No comment provided by engineer. */ "Create your profile" = "Maak je profiel aan"; +/* No comment provided by engineer. */ +"Created" = "Gemaakt"; + /* No comment provided by engineer. */ "Created at" = "Gemaakt op"; @@ -1086,6 +1176,9 @@ /* No comment provided by engineer. */ "Current passphrase…" = "Huidige wachtwoord…"; +/* No comment provided by engineer. */ +"Current profile" = "Huidig profiel"; + /* No comment provided by engineer. */ "Currently maximum supported file size is %@." = "De momenteel maximaal ondersteunde bestandsgrootte is %@."; @@ -1095,9 +1188,15 @@ /* No comment provided by engineer. */ "Custom time" = "Aangepaste tijd"; +/* No comment provided by engineer. */ +"Customize theme" = "Thema aanpassen"; + /* No comment provided by engineer. */ "Dark" = "Donker"; +/* No comment provided by engineer. */ +"Dark mode colors" = "Kleuren in donkere modus"; + /* No comment provided by engineer. */ "Database downgrade" = "Database downgraden"; @@ -1167,6 +1266,9 @@ /* message decrypt error item */ "Decryption error" = "Decodering fout"; +/* No comment provided by engineer. */ +"decryption errors" = "decoderingsfouten"; + /* pref value */ "default (%@)" = "standaard (%@)"; @@ -1293,6 +1395,9 @@ /* deleted chat item */ "deleted" = "verwijderd"; +/* No comment provided by engineer. */ +"Deleted" = "Verwijderd"; + /* No comment provided by engineer. */ "Deleted at" = "Verwijderd om"; @@ -1305,6 +1410,9 @@ /* rcv group event chat item */ "deleted group" = "verwijderde groep"; +/* No comment provided by engineer. */ +"Deletion errors" = "Verwijderingsfouten"; + /* No comment provided by engineer. */ "Delivery" = "Bezorging"; @@ -1329,6 +1437,12 @@ /* snd error text */ "Destination server error: %@" = "Bestemmingsserverfout: %@"; +/* No comment provided by engineer. */ +"Detailed statistics" = "Gedetailleerde statistieken"; + +/* No comment provided by engineer. */ +"Details" = "Details"; + /* No comment provided by engineer. */ "Develop" = "Ontwikkelen"; @@ -1431,12 +1545,21 @@ /* chat item action */ "Download" = "Downloaden"; +/* No comment provided by engineer. */ +"Download errors" = "Downloadfouten"; + /* No comment provided by engineer. */ "Download failed" = "Download mislukt"; /* server test step */ "Download file" = "Download bestand"; +/* No comment provided by engineer. */ +"Downloaded" = "Gedownload"; + +/* No comment provided by engineer. */ +"Downloaded files" = "Gedownloade bestanden"; + /* No comment provided by engineer. */ "Downloading archive" = "Archief downloaden"; @@ -1449,6 +1572,9 @@ /* integrity error chat item */ "duplicate message" = "dubbel bericht"; +/* No comment provided by engineer. */ +"duplicates" = "duplicaten"; + /* No comment provided by engineer. */ "Duration" = "Duur"; @@ -1612,10 +1738,10 @@ "Enter this device name…" = "Voer deze apparaatnaam in…"; /* placeholder */ -"Enter welcome message…" = "Welkomst bericht invoeren…"; +"Enter welcome message…" = "Welkom bericht invoeren…"; /* placeholder */ -"Enter welcome message… (optional)" = "Voer welkomst bericht in... (optioneel)"; +"Enter welcome message… (optional)" = "Voer welkom bericht in... (optioneel)"; /* No comment provided by engineer. */ "Enter your name…" = "Vul uw naam in…"; @@ -1707,6 +1833,9 @@ /* No comment provided by engineer. */ "Error exporting chat database" = "Fout bij het exporteren van de chat database"; +/* No comment provided by engineer. */ +"Error exporting theme: %@" = "Fout bij exporteren van thema: %@"; + /* No comment provided by engineer. */ "Error importing chat database" = "Fout bij het importeren van de chat database"; @@ -1722,9 +1851,18 @@ /* No comment provided by engineer. */ "Error receiving file" = "Fout bij ontvangen van bestand"; +/* No comment provided by engineer. */ +"Error reconnecting server" = "Fout bij opnieuw verbinding maken met de server"; + +/* No comment provided by engineer. */ +"Error reconnecting servers" = "Fout bij opnieuw verbinden van servers"; + /* No comment provided by engineer. */ "Error removing member" = "Fout bij verwijderen van lid"; +/* No comment provided by engineer. */ +"Error resetting statistics" = "Fout bij het resetten van statistieken"; + /* No comment provided by engineer. */ "Error saving %@ servers" = "Fout bij opslaan van %@ servers"; @@ -1804,6 +1942,9 @@ /* No comment provided by engineer. */ "Error: URL is invalid" = "Fout: URL is ongeldig"; +/* No comment provided by engineer. */ +"Errors" = "Fouten"; + /* No comment provided by engineer. */ "Even when disabled in the conversation." = "Zelfs wanneer uitgeschakeld in het gesprek."; @@ -1816,12 +1957,18 @@ /* chat item action */ "Expand" = "Uitklappen"; +/* No comment provided by engineer. */ +"expired" = "verlopen"; + /* No comment provided by engineer. */ "Export database" = "Database exporteren"; /* No comment provided by engineer. */ "Export error:" = "Exportfout:"; +/* No comment provided by engineer. */ +"Export theme" = "Exporteer thema"; + /* No comment provided by engineer. */ "Exported database archive." = "Geëxporteerd database archief."; @@ -1843,6 +1990,21 @@ /* No comment provided by engineer. */ "Favorite" = "Favoriet"; +/* No comment provided by engineer. */ +"File error" = "Bestandsfout"; + +/* file error text */ +"File not found - most likely file was deleted or cancelled." = "Bestand niet gevonden - hoogstwaarschijnlijk is het bestand verwijderd of geannuleerd."; + +/* file error text */ +"File server error: %@" = "Bestandsserverfout: %@"; + +/* No comment provided by engineer. */ +"File status" = "Bestandsstatus"; + +/* copied message info */ +"File status: %@" = "Bestandsstatus: %@"; + /* No comment provided by engineer. */ "File will be deleted from servers." = "Het bestand wordt van de servers verwijderd."; @@ -1957,6 +2119,12 @@ /* No comment provided by engineer. */ "GIFs and stickers" = "GIF's en stickers"; +/* message preview */ +"Good afternoon!" = "Goedemiddag!"; + +/* message preview */ +"Good morning!" = "Goedemorgen!"; + /* No comment provided by engineer. */ "Group" = "Groep"; @@ -1994,7 +2162,7 @@ "Group links" = "Groep links"; /* No comment provided by engineer. */ -"Group members can add message reactions." = "Groepsleden kunnen berichtreacties toevoegen."; +"Group members can add message reactions." = "Groepsleden kunnen bericht reacties toevoegen."; /* No comment provided by engineer. */ "Group members can irreversibly delete sent messages. (24 hours)" = "Groepsleden kunnen verzonden berichten onherroepelijk verwijderen. (24 uur)"; @@ -2033,7 +2201,7 @@ "group profile updated" = "groep profiel bijgewerkt"; /* No comment provided by engineer. */ -"Group welcome message" = "Groep welkomst bericht"; +"Group welcome message" = "Groep welkom bericht"; /* No comment provided by engineer. */ "Group will be deleted for all members - this cannot be undone!" = "Groep wordt verwijderd voor alle leden, dit kan niet ongedaan worden gemaakt!"; @@ -2134,6 +2302,9 @@ /* No comment provided by engineer. */ "Import failed" = "Importeren is mislukt"; +/* No comment provided by engineer. */ +"Import theme" = "Thema importeren"; + /* No comment provided by engineer. */ "Importing archive" = "Archief importeren"; @@ -2155,6 +2326,9 @@ /* No comment provided by engineer. */ "In-call sounds" = "Geluiden tijdens het bellen"; +/* No comment provided by engineer. */ +"inactive" = "inactief"; + /* No comment provided by engineer. */ "Incognito" = "Incognito"; @@ -2218,6 +2392,9 @@ /* No comment provided by engineer. */ "Interface" = "Interface"; +/* No comment provided by engineer. */ +"Interface colors" = "Interface kleuren"; + /* invalid chat data */ "invalid chat" = "ongeldige gesprek"; @@ -2470,6 +2647,9 @@ /* rcv group event chat item */ "member connected" = "is toegetreden"; +/* item status text */ +"Member inactive" = "Lid inactief"; + /* No comment provided by engineer. */ "Member role will be changed to \"%@\". All group members will be notified." = "De rol van lid wordt gewijzigd in \"%@\". Alle groepsleden worden op de hoogte gebracht."; @@ -2479,6 +2659,9 @@ /* No comment provided by engineer. */ "Member will be removed from group - this cannot be undone!" = "Lid wordt uit de groep verwijderd, dit kan niet ongedaan worden gemaakt!"; +/* No comment provided by engineer. */ +"Menus" = "Menu's"; + /* item status text */ "Message delivery error" = "Fout bij bezorging van bericht"; @@ -2491,6 +2674,12 @@ /* No comment provided by engineer. */ "Message draft" = "Concept bericht"; +/* item status text */ +"Message forwarded" = "Bericht doorgestuurd"; + +/* item status description */ +"Message may be delivered later if member becomes active." = "Het bericht kan later worden bezorgd als het lid actief wordt."; + /* No comment provided by engineer. */ "Message queue info" = "Informatie over berichtenwachtrij"; @@ -2506,6 +2695,9 @@ /* notification */ "message received" = "bericht ontvangen"; +/* No comment provided by engineer. */ +"Message reception" = "Bericht ontvangst"; + /* No comment provided by engineer. */ "Message routing fallback" = "Terugval op berichtroutering"; @@ -2515,6 +2707,12 @@ /* No comment provided by engineer. */ "Message source remains private." = "Berichtbron blijft privé."; +/* No comment provided by engineer. */ +"Message status" = "Berichtstatus"; + +/* copied message info */ +"Message status: %@" = "Berichtstatus: %@"; + /* No comment provided by engineer. */ "Message text" = "Bericht tekst"; @@ -2530,6 +2728,12 @@ /* No comment provided by engineer. */ "Messages from %@ will be shown!" = "Berichten van %@ worden getoond!"; +/* No comment provided by engineer. */ +"Messages received" = "Berichten ontvangen"; + +/* No comment provided by engineer. */ +"Messages sent" = "Berichten verzonden"; + /* No comment provided by engineer. */ "Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Berichten, bestanden en oproepen worden beschermd door **end-to-end codering** met perfecte voorwaartse geheimhouding, afwijzing en inbraakherstel."; @@ -2695,6 +2899,9 @@ /* No comment provided by engineer. */ "No device token!" = "Geen apparaattoken!"; +/* item status description */ +"No direct connection yet, message is forwarded by admin." = "Nog geen directe verbinding, bericht wordt doorgestuurd door beheerder."; + /* No comment provided by engineer. */ "no e2e encryption" = "geen e2e versleuteling"; @@ -2707,6 +2914,9 @@ /* No comment provided by engineer. */ "No history" = "Geen geschiedenis"; +/* No comment provided by engineer. */ +"No info, try to reload" = "Geen info, probeer opnieuw te laden"; + /* No comment provided by engineer. */ "No network connection" = "Geen netwerkverbinding"; @@ -2739,7 +2949,7 @@ time to disappear */ "off" = "uit"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Uit"; /* feature offered item */ @@ -2788,7 +2998,7 @@ "Only group owners can enable voice messages." = "Alleen groep eigenaren kunnen spraak berichten inschakelen."; /* No comment provided by engineer. */ -"Only you can add message reactions." = "Alleen jij kunt berichtreacties toevoegen."; +"Only you can add message reactions." = "Alleen jij kunt bericht reacties toevoegen."; /* No comment provided by engineer. */ "Only you can irreversibly delete messages (your contact can mark them for deletion). (24 hours)" = "Alleen jij kunt berichten onomkeerbaar verwijderen (je contact kan ze markeren voor verwijdering). (24 uur)"; @@ -2803,7 +3013,7 @@ "Only you can send voice messages." = "Alleen jij kunt spraak berichten verzenden."; /* No comment provided by engineer. */ -"Only your contact can add message reactions." = "Alleen uw contact kan berichtreacties toevoegen."; +"Only your contact can add message reactions." = "Alleen uw contact kan bericht reacties toevoegen."; /* No comment provided by engineer. */ "Only your contact can irreversibly delete messages (you can mark them for deletion). (24 hours)" = "Alleen uw contact kan berichten onherroepelijk verwijderen (u kunt ze markeren voor verwijdering). (24 uur)"; @@ -2832,6 +3042,9 @@ /* authentication reason */ "Open migration to another device" = "Open de migratie naar een ander apparaat"; +/* No comment provided by engineer. */ +"Open server settings" = "Server instellingen openen"; + /* No comment provided by engineer. */ "Open Settings" = "Open instellingen"; @@ -2856,9 +3069,18 @@ /* No comment provided by engineer. */ "Or show this code" = "Of laat deze code zien"; +/* No comment provided by engineer. */ +"other" = "overig"; + /* No comment provided by engineer. */ "Other" = "Ander"; +/* No comment provided by engineer. */ +"Other %@ servers" = "Andere %@ servers"; + +/* No comment provided by engineer. */ +"other errors" = "overige fouten"; + /* member role */ "owner" = "Eigenaar"; @@ -2901,6 +3123,9 @@ /* No comment provided by engineer. */ "peer-to-peer" = "peer-to-peer"; +/* No comment provided by engineer. */ +"Pending" = "in behandeling"; + /* No comment provided by engineer. */ "People can connect to you only via the links you share." = "Mensen kunnen alleen verbinding met u maken via de links die u deelt."; @@ -2922,6 +3147,9 @@ /* No comment provided by engineer. */ "Please ask your contact to enable sending voice messages." = "Vraag uw contact om het verzenden van spraak berichten in te schakelen."; +/* No comment provided by engineer. */ +"Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers." = "Controleer of mobiel en desktop met hetzelfde lokale netwerk zijn verbonden en of de desktopfirewall de verbinding toestaat.\nDeel eventuele andere problemen met de ontwikkelaars."; + /* No comment provided by engineer. */ "Please check that you used the correct link or ask your contact to send you another one." = "Controleer of u de juiste link heeft gebruikt of vraag uw contact om u een andere te sturen."; @@ -2979,6 +3207,9 @@ /* No comment provided by engineer. */ "Preview" = "Voorbeeld"; +/* No comment provided by engineer. */ +"Previously connected servers" = "Eerder verbonden servers"; + /* No comment provided by engineer. */ "Privacy & security" = "Privacy en beveiliging"; @@ -3000,6 +3231,9 @@ /* No comment provided by engineer. */ "Private routing" = "Privéroutering"; +/* No comment provided by engineer. */ +"Private routing error" = "Fout in privéroutering"; + /* No comment provided by engineer. */ "Profile and server connections" = "Profiel- en serververbindingen"; @@ -3018,6 +3252,9 @@ /* No comment provided by engineer. */ "Profile password" = "Profiel wachtwoord"; +/* No comment provided by engineer. */ +"Profile theme" = "Profiel thema"; + /* No comment provided by engineer. */ "Profile update will be sent to your contacts." = "Profiel update wordt naar uw contacten verzonden."; @@ -3028,7 +3265,7 @@ "Prohibit irreversible message deletion." = "Verbied het onomkeerbaar verwijderen van berichten."; /* No comment provided by engineer. */ -"Prohibit message reactions." = "Berichtreacties verbieden."; +"Prohibit message reactions." = "Bericht reacties verbieden."; /* No comment provided by engineer. */ "Prohibit messages reactions." = "Berichten reacties verbieden."; @@ -3066,6 +3303,12 @@ /* No comment provided by engineer. */ "Protocol timeout per KB" = "Protocol timeout per KB"; +/* No comment provided by engineer. */ +"Proxied" = "Proxied"; + +/* No comment provided by engineer. */ +"Proxied servers" = "Proxied servers"; + /* No comment provided by engineer. */ "Push notifications" = "Push meldingen"; @@ -3108,6 +3351,9 @@ /* No comment provided by engineer. */ "Receipts are disabled" = "Bevestigingen zijn uitgeschakeld"; +/* No comment provided by engineer. */ +"Receive errors" = "Fouten ontvangen"; + /* No comment provided by engineer. */ "received answer…" = "antwoord gekregen…"; @@ -3126,6 +3372,15 @@ /* message info title */ "Received message" = "Ontvangen bericht"; +/* No comment provided by engineer. */ +"Received messages" = "Ontvangen berichten"; + +/* No comment provided by engineer. */ +"Received reply" = "Antwoord ontvangen"; + +/* No comment provided by engineer. */ +"Received total" = "Totaal ontvangen"; + /* No comment provided by engineer. */ "Receiving address will be changed to a different server. Address change will complete after sender comes online." = "Het ontvangstadres wordt gewijzigd naar een andere server. Adres wijziging wordt voltooid nadat de afzender online is."; @@ -3144,9 +3399,24 @@ /* No comment provided by engineer. */ "Recipients see updates as you type them." = "Ontvangers zien updates terwijl u ze typt."; +/* No comment provided by engineer. */ +"Reconnect" = "opnieuw verbinden"; + /* No comment provided by engineer. */ "Reconnect all connected servers to force message delivery. It uses additional traffic." = "Verbind alle verbonden servers opnieuw om de bezorging van berichten af te dwingen. Het maakt gebruik van extra data."; +/* No comment provided by engineer. */ +"Reconnect all servers" = "Maak opnieuw verbinding met alle servers"; + +/* No comment provided by engineer. */ +"Reconnect all servers?" = "Alle servers opnieuw verbinden?"; + +/* No comment provided by engineer. */ +"Reconnect server to force message delivery. It uses additional traffic." = "Maak opnieuw verbinding met de server om de bezorging van berichten te forceren. Er wordt gebruik gemaakt van extra verkeer.Maak opnieuw verbinding met de server om de bezorging van berichten te forceren. Er wordt gebruik gemaakt van extra data."; + +/* No comment provided by engineer. */ +"Reconnect server?" = "Server opnieuw verbinden?"; + /* No comment provided by engineer. */ "Reconnect servers?" = "Servers opnieuw verbinden?"; @@ -3180,6 +3450,9 @@ /* No comment provided by engineer. */ "Remove" = "Verwijderen"; +/* No comment provided by engineer. */ +"Remove image" = "Verwijder afbeelding"; + /* No comment provided by engineer. */ "Remove member" = "Lid verwijderen"; @@ -3237,12 +3510,24 @@ /* No comment provided by engineer. */ "Reset" = "Resetten"; +/* No comment provided by engineer. */ +"Reset all statistics" = "Reset alle statistieken"; + +/* No comment provided by engineer. */ +"Reset all statistics?" = "Alle statistieken resetten?"; + /* No comment provided by engineer. */ "Reset colors" = "Kleuren resetten"; +/* No comment provided by engineer. */ +"Reset to app theme" = "Terugzetten naar app thema"; + /* No comment provided by engineer. */ "Reset to defaults" = "Resetten naar standaardwaarden"; +/* No comment provided by engineer. */ +"Reset to user theme" = "Terugzetten naar gebruikersthema"; + /* No comment provided by engineer. */ "Restart the app to create a new chat profile" = "Start de app opnieuw om een nieuw chat profiel aan te maken"; @@ -3337,7 +3622,7 @@ "Save settings?" = "Instellingen opslaan?"; /* No comment provided by engineer. */ -"Save welcome message?" = "Welkomst bericht opslaan?"; +"Save welcome message?" = "Welkom bericht opslaan?"; /* No comment provided by engineer. */ "saved" = "opgeslagen"; @@ -3357,6 +3642,12 @@ /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Opgeslagen WebRTC ICE servers worden verwijderd"; +/* No comment provided by engineer. */ +"Scale" = "Schaal"; + +/* No comment provided by engineer. */ +"Scan / Paste link" = "Link scannen/plakken"; + /* No comment provided by engineer. */ "Scan code" = "Code scannen"; @@ -3384,6 +3675,9 @@ /* network option */ "sec" = "sec"; +/* No comment provided by engineer. */ +"Secondary" = "Secundair"; + /* time unit */ "seconds" = "seconden"; @@ -3393,6 +3687,9 @@ /* server test step */ "Secure queue" = "Veilige wachtrij"; +/* No comment provided by engineer. */ +"Secured" = "Beveiligd"; + /* No comment provided by engineer. */ "Security assessment" = "Beveiligingsbeoordeling"; @@ -3405,6 +3702,9 @@ /* No comment provided by engineer. */ "Select" = "Selecteer"; +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Geselecteerde chat voorkeuren verbieden dit bericht."; + /* No comment provided by engineer. */ "Self-destruct" = "Zelfvernietiging"; @@ -3438,6 +3738,9 @@ /* No comment provided by engineer. */ "Send disappearing message" = "Stuur een verdwijnend bericht"; +/* No comment provided by engineer. */ +"Send errors" = "Verzend fouten"; + /* No comment provided by engineer. */ "Send link previews" = "Link voorbeelden verzenden"; @@ -3504,15 +3807,36 @@ /* copied message info */ "Sent at: %@" = "Verzonden op: %@"; +/* No comment provided by engineer. */ +"Sent directly" = "Direct verzonden"; + /* notification */ "Sent file event" = "Verzonden bestandsgebeurtenis"; /* message info title */ "Sent message" = "Verzonden bericht"; +/* No comment provided by engineer. */ +"Sent messages" = "Verzonden berichten"; + /* No comment provided by engineer. */ "Sent messages will be deleted after set time." = "Verzonden berichten worden na ingestelde tijd verwijderd."; +/* No comment provided by engineer. */ +"Sent reply" = "Antwoord verzonden"; + +/* No comment provided by engineer. */ +"Sent total" = "Totaal verzonden"; + +/* No comment provided by engineer. */ +"Sent via proxy" = "Verzonden via proxy"; + +/* No comment provided by engineer. */ +"Server address" = "Server adres"; + +/* No comment provided by engineer. */ +"Server address is incompatible with network settings: %@." = "Serveradres is incompatibel met netwerkinstellingen: %@."; + /* srv error text. */ "Server address is incompatible with network settings." = "Serveradres is niet compatibel met netwerkinstellingen."; @@ -3528,12 +3852,24 @@ /* No comment provided by engineer. */ "Server test failed!" = "Servertest mislukt!"; +/* No comment provided by engineer. */ +"Server type" = "Server type"; + /* srv error text */ "Server version is incompatible with network settings." = "Serverversie is incompatibel met netwerkinstellingen."; +/* No comment provided by engineer. */ +"Server version is incompatible with your app: %@." = "Serverversie is incompatibel met uw app: %@."; + /* No comment provided by engineer. */ "Servers" = "Servers"; +/* No comment provided by engineer. */ +"Servers info" = "Server informatie"; + +/* No comment provided by engineer. */ +"Servers statistics will be reset - this cannot be undone!" = "Serverstatistieken worden gereset - dit kan niet ongedaan worden gemaakt!"; + /* No comment provided by engineer. */ "Session code" = "Sessie code"; @@ -3543,6 +3879,9 @@ /* No comment provided by engineer. */ "Set contact name…" = "Contactnaam instellen…"; +/* No comment provided by engineer. */ +"Set default theme" = "Stel het standaard thema in"; + /* No comment provided by engineer. */ "Set group preferences" = "Groep voorkeuren instellen"; @@ -3612,6 +3951,9 @@ /* No comment provided by engineer. */ "Show message status" = "Toon berichtstatus"; +/* No comment provided by engineer. */ +"Show percentage" = "Percentage weergeven"; + /* No comment provided by engineer. */ "Show preview" = "Toon voorbeeld"; @@ -3621,6 +3963,9 @@ /* No comment provided by engineer. */ "Show:" = "Toon:"; +/* No comment provided by engineer. */ +"SimpleX" = "SimpleX"; + /* No comment provided by engineer. */ "SimpleX address" = "SimpleX adres"; @@ -3666,6 +4011,9 @@ /* No comment provided by engineer. */ "Simplified incognito mode" = "Vereenvoudigde incognitomodus"; +/* No comment provided by engineer. */ +"Size" = "Maat"; + /* No comment provided by engineer. */ "Skip" = "Overslaan"; @@ -3675,6 +4023,9 @@ /* No comment provided by engineer. */ "Small groups (max 20)" = "Kleine groepen (max 20)"; +/* No comment provided by engineer. */ +"SMP server" = "SMP server"; + /* No comment provided by engineer. */ "SMP servers" = "SMP servers"; @@ -3699,9 +4050,15 @@ /* No comment provided by engineer. */ "Start migration" = "Start migratie"; +/* No comment provided by engineer. */ +"Starting from %@." = "Beginnend vanaf %@."; + /* No comment provided by engineer. */ "starting…" = "beginnen…"; +/* No comment provided by engineer. */ +"Statistics" = "Statistieken"; + /* No comment provided by engineer. */ "Stop" = "Stop"; @@ -3744,6 +4101,15 @@ /* No comment provided by engineer. */ "Submit" = "Indienen"; +/* No comment provided by engineer. */ +"Subscribed" = "Ingeschreven"; + +/* No comment provided by engineer. */ +"Subscription errors" = "Inschrijving fouten"; + +/* No comment provided by engineer. */ +"Subscriptions ignored" = "Inschrijvingen genegeerd"; + /* No comment provided by engineer. */ "Support SimpleX Chat" = "Ondersteuning van SimpleX Chat"; @@ -3792,6 +4158,9 @@ /* No comment provided by engineer. */ "TCP_KEEPINTVL" = "TCP_KEEPINTVL"; +/* No comment provided by engineer. */ +"Temporary file error" = "Tijdelijke bestandsfout"; + /* server test failure */ "Test failed at step %@." = "Test mislukt bij stap %@."; @@ -3873,6 +4242,9 @@ /* No comment provided by engineer. */ "The text you pasted is not a SimpleX link." = "De tekst die u hebt geplakt is geen SimpleX link."; +/* No comment provided by engineer. */ +"Themes" = "Thema's"; + /* No comment provided by engineer. */ "These settings are for your current profile **%@**." = "Deze instellingen zijn voor uw huidige profiel **%@**."; @@ -3915,9 +4287,15 @@ /* No comment provided by engineer. */ "This is your own SimpleX address!" = "Dit is uw eigen SimpleX adres!"; +/* No comment provided by engineer. */ +"This link was used with another mobile device, please create a new link on the desktop." = "Deze link is gebruikt met een ander mobiel apparaat. Maak een nieuwe link op de desktop."; + /* No comment provided by engineer. */ "This setting applies to messages in your current chat profile **%@**." = "Deze instelling is van toepassing op berichten in je huidige chat profiel **%@**."; +/* No comment provided by engineer. */ +"Title" = "Titel"; + /* No comment provided by engineer. */ "To ask any questions and to receive updates:" = "Om vragen te stellen en updates te ontvangen:"; @@ -3957,9 +4335,15 @@ /* No comment provided by engineer. */ "Toggle incognito when connecting." = "Schakel incognito in tijdens het verbinden."; +/* No comment provided by engineer. */ +"Total" = "Totaal"; + /* No comment provided by engineer. */ "Transport isolation" = "Transport isolation"; +/* No comment provided by engineer. */ +"Transport sessions" = "Transportsessies"; + /* No comment provided by engineer. */ "Trying to connect to the server used to receive messages from this contact (error: %@)." = "Proberen verbinding te maken met de server die wordt gebruikt om berichten van dit contact te ontvangen (fout: %@)."; @@ -4095,12 +4479,21 @@ /* No comment provided by engineer. */ "Upgrade and open chat" = "Upgrade en open chat"; +/* No comment provided by engineer. */ +"Upload errors" = "Upload fouten"; + /* No comment provided by engineer. */ "Upload failed" = "Upload mislukt"; /* server test step */ "Upload file" = "Upload bestand"; +/* No comment provided by engineer. */ +"Uploaded" = "Geüpload"; + +/* No comment provided by engineer. */ +"Uploaded files" = "Geüploade bestanden"; + /* No comment provided by engineer. */ "Uploading archive" = "Archief uploaden"; @@ -4146,6 +4539,9 @@ /* No comment provided by engineer. */ "User profile" = "Gebruikers profiel"; +/* No comment provided by engineer. */ +"User selection" = "Gebruikersselectie"; + /* No comment provided by engineer. */ "Using .onion hosts requires compatible VPN provider." = "Het gebruik van .onion-hosts vereist een compatibele VPN-provider."; @@ -4254,6 +4650,12 @@ /* No comment provided by engineer. */ "Waiting for video" = "Wachten op video"; +/* No comment provided by engineer. */ +"Wallpaper accent" = "Achtergrond accent"; + +/* No comment provided by engineer. */ +"Wallpaper background" = "Wallpaper achtergrond"; + /* No comment provided by engineer. */ "wants to connect to you!" = "wil met je in contact komen!"; @@ -4273,10 +4675,10 @@ "Welcome %@!" = "Welkom %@!"; /* No comment provided by engineer. */ -"Welcome message" = "Welkomst bericht"; +"Welcome message" = "Welkom bericht"; /* No comment provided by engineer. */ -"Welcome message is too long" = "Welkomstbericht is te lang"; +"Welcome message is too long" = "Welkom bericht is te lang"; /* No comment provided by engineer. */ "What's new" = "Wat is er nieuw"; @@ -4309,7 +4711,7 @@ "With encrypted files and media." = "‐Met versleutelde bestanden en media."; /* No comment provided by engineer. */ -"With optional welcome message." = "Met optioneel welkomst bericht."; +"With optional welcome message." = "Met optioneel welkom bericht."; /* No comment provided by engineer. */ "With reduced battery usage." = "Met verminderd batterijgebruik."; @@ -4326,9 +4728,15 @@ /* snd error text */ "Wrong key or unknown connection - most likely this connection is deleted." = "Verkeerde sleutel of onbekende verbinding - hoogstwaarschijnlijk is deze verbinding verwijderd."; +/* file error text */ +"Wrong key or unknown file chunk address - most likely file is deleted." = "Verkeerde sleutel of onbekend bestanddeeladres - hoogstwaarschijnlijk is het bestand verwijderd."; + /* No comment provided by engineer. */ "Wrong passphrase!" = "Verkeerd wachtwoord!"; +/* No comment provided by engineer. */ +"XFTP server" = "XFTP server"; + /* No comment provided by engineer. */ "XFTP servers" = "XFTP servers"; @@ -4386,6 +4794,9 @@ /* No comment provided by engineer. */ "You are invited to group" = "Je bent uitgenodigd voor de groep"; +/* No comment provided by engineer. */ +"You are not connected to these servers. Private routing is used to deliver messages to them." = "U bent niet verbonden met deze servers. Privéroutering wordt gebruikt om berichten bij hen af te leveren."; + /* No comment provided by engineer. */ "you are observer" = "jij bent waarnemer"; diff --git a/apps/ios/pl.lproj/Localizable.strings b/apps/ios/pl.lproj/Localizable.strings index 6807e4f240..fd284d9537 100644 --- a/apps/ios/pl.lproj/Localizable.strings +++ b/apps/ios/pl.lproj/Localizable.strings @@ -334,6 +334,9 @@ /* No comment provided by engineer. */ "above, then choose:" = "powyżej, a następnie wybierz:"; +/* No comment provided by engineer. */ +"Accent" = "Akcent"; + /* accept contact request via notification accept incoming call via notification */ "Accept" = "Akceptuj"; @@ -350,6 +353,15 @@ /* call status */ "accepted call" = "zaakceptowane połączenie"; +/* No comment provided by engineer. */ +"Acknowledged" = "Potwierdzono"; + +/* No comment provided by engineer. */ +"Acknowledgement errors" = "Błędy potwierdzenia"; + +/* No comment provided by engineer. */ +"Active connections" = "Aktywne połączenia"; + /* No comment provided by engineer. */ "Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Dodaj adres do swojego profilu, aby Twoje kontakty mogły go udostępnić innym osobom. Aktualizacja profilu zostanie wysłana do Twoich kontaktów."; @@ -374,6 +386,15 @@ /* No comment provided by engineer. */ "Add welcome message" = "Dodaj wiadomość powitalną"; +/* No comment provided by engineer. */ +"Additional accent" = "Dodatkowy akcent"; + +/* No comment provided by engineer. */ +"Additional accent 2" = "Dodatkowy akcent 2"; + +/* No comment provided by engineer. */ +"Additional secondary" = "Dodatkowy drugorzędny"; + /* No comment provided by engineer. */ "Address" = "Adres"; @@ -395,6 +416,9 @@ /* No comment provided by engineer. */ "Advanced network settings" = "Zaawansowane ustawienia sieci"; +/* No comment provided by engineer. */ +"Advanced settings" = "Zaawansowane ustawienia"; + /* chat item text */ "agreeing encryption for %@…" = "uzgadnianie szyfrowania dla %@…"; @@ -410,6 +434,9 @@ /* No comment provided by engineer. */ "All data is erased when it is entered." = "Wszystkie dane są usuwane po jego wprowadzeniu."; +/* No comment provided by engineer. */ +"All data is private to your device." = "Wszystkie dane są prywatne na Twoim urządzeniu."; + /* No comment provided by engineer. */ "All group members will remain connected." = "Wszyscy członkowie grupy pozostaną połączeni."; @@ -425,6 +452,9 @@ /* No comment provided by engineer. */ "All new messages from %@ will be hidden!" = "Wszystkie nowe wiadomości z %@ zostaną ukryte!"; +/* No comment provided by engineer. */ +"All profiles" = "Wszystkie profile"; + /* No comment provided by engineer. */ "All your contacts will remain connected." = "Wszystkie Twoje kontakty pozostaną połączone."; @@ -551,6 +581,9 @@ /* No comment provided by engineer. */ "Apply" = "Zastosuj"; +/* No comment provided by engineer. */ +"Apply to" = "Zastosuj dla"; + /* No comment provided by engineer. */ "Archive and upload" = "Archiwizuj i prześlij"; @@ -560,6 +593,9 @@ /* No comment provided by engineer. */ "Attach" = "Dołącz"; +/* No comment provided by engineer. */ +"attempts" = "próby"; + /* No comment provided by engineer. */ "Audio & video calls" = "Połączenia audio i wideo"; @@ -602,6 +638,9 @@ /* No comment provided by engineer. */ "Back" = "Wstecz"; +/* No comment provided by engineer. */ +"Background" = "Tło"; + /* No comment provided by engineer. */ "Bad desktop address" = "Zły adres komputera"; @@ -623,6 +662,9 @@ /* No comment provided by engineer. */ "Better messages" = "Lepsze wiadomości"; +/* No comment provided by engineer. */ +"Black" = "Czarny"; + /* No comment provided by engineer. */ "Block" = "Zablokuj"; @@ -647,7 +689,7 @@ /* rcv group event chat item */ "blocked %@" = "zablokowany %@"; -/* marked deleted chat item preview text */ +/* blocked chat item */ "blocked by admin" = "zablokowany przez admina"; /* No comment provided by engineer. */ @@ -713,6 +755,9 @@ /* No comment provided by engineer. */ "Cannot access keychain to save database password" = "Nie można uzyskać dostępu do pęku kluczy, aby zapisać hasło do bazy danych"; +/* No comment provided by engineer. */ +"Cannot forward message" = "Nie można przekazać wiadomości"; + /* No comment provided by engineer. */ "Cannot receive file" = "Nie można odebrać pliku"; @@ -771,6 +816,9 @@ /* No comment provided by engineer. */ "Chat archive" = "Archiwum czatu"; +/* No comment provided by engineer. */ +"Chat colors" = "Kolory czatu"; + /* No comment provided by engineer. */ "Chat console" = "Konsola czatu"; @@ -798,6 +846,9 @@ /* No comment provided by engineer. */ "Chat preferences" = "Preferencje czatu"; +/* No comment provided by engineer. */ +"Chat theme" = "Motyw czatu"; + /* No comment provided by engineer. */ "Chats" = "Czaty"; @@ -816,6 +867,15 @@ /* No comment provided by engineer. */ "Choose from library" = "Wybierz z biblioteki"; +/* No comment provided by engineer. */ +"Chunks deleted" = "Fragmenty usunięte"; + +/* No comment provided by engineer. */ +"Chunks downloaded" = "Fragmenty pobrane"; + +/* No comment provided by engineer. */ +"Chunks uploaded" = "Fragmenty przesłane"; + /* No comment provided by engineer. */ "Clear" = "Wyczyść"; @@ -831,6 +891,9 @@ /* No comment provided by engineer. */ "Clear verification" = "Wyczyść weryfikację"; +/* No comment provided by engineer. */ +"Color mode" = "Tryb koloru"; + /* No comment provided by engineer. */ "colored" = "kolorowy"; @@ -843,9 +906,15 @@ /* No comment provided by engineer. */ "complete" = "kompletny"; +/* No comment provided by engineer. */ +"Completed" = "Zakończono"; + /* No comment provided by engineer. */ "Configure ICE servers" = "Skonfiguruj serwery ICE"; +/* No comment provided by engineer. */ +"Configured %@ servers" = "Skonfigurowano %@ serwerów"; + /* No comment provided by engineer. */ "Confirm" = "Potwierdź"; @@ -912,18 +981,27 @@ /* No comment provided by engineer. */ "connected" = "połączony"; +/* No comment provided by engineer. */ +"Connected" = "Połączony"; + /* No comment provided by engineer. */ "Connected desktop" = "Połączony komputer"; /* rcv group event chat item */ "connected directly" = "połącz bezpośrednio"; +/* No comment provided by engineer. */ +"Connected servers" = "Połączone serwery"; + /* No comment provided by engineer. */ "Connected to desktop" = "Połączony do komputera"; /* No comment provided by engineer. */ "connecting" = "łączenie"; +/* No comment provided by engineer. */ +"Connecting" = "Łączenie"; + /* No comment provided by engineer. */ "connecting (accepted)" = "łączenie (zaakceptowane)"; @@ -972,9 +1050,15 @@ /* No comment provided by engineer. */ "Connection timeout" = "Czas połączenia minął"; +/* No comment provided by engineer. */ +"Connection with desktop stopped" = "Połączenie z komputerem zakończone"; + /* connection information */ "connection:%@" = "połączenie: %@"; +/* No comment provided by engineer. */ +"Connections" = "Połączenia"; + /* profile update event chat item */ "contact %@ changed to %@" = "kontakt %1$@ zmieniony na %2$@"; @@ -1017,6 +1101,9 @@ /* No comment provided by engineer. */ "Copy" = "Kopiuj"; +/* No comment provided by engineer. */ +"Copy error" = "Kopiuj błąd"; + /* No comment provided by engineer. */ "Core version: v%@" = "Wersja rdzenia: v%@"; @@ -1062,6 +1149,9 @@ /* No comment provided by engineer. */ "Create your profile" = "Utwórz swój profil"; +/* No comment provided by engineer. */ +"Created" = "Utworzono"; + /* No comment provided by engineer. */ "Created at" = "Utworzony o"; @@ -1086,6 +1176,9 @@ /* No comment provided by engineer. */ "Current passphrase…" = "Obecne hasło…"; +/* No comment provided by engineer. */ +"Current profile" = "Bieżący profil"; + /* No comment provided by engineer. */ "Currently maximum supported file size is %@." = "Obecnie maksymalna obsługiwana wielkość pliku wynosi %@."; @@ -1095,9 +1188,15 @@ /* No comment provided by engineer. */ "Custom time" = "Niestandardowy czas"; +/* No comment provided by engineer. */ +"Customize theme" = "Dostosuj motyw"; + /* No comment provided by engineer. */ "Dark" = "Ciemny"; +/* No comment provided by engineer. */ +"Dark mode colors" = "Kolory ciemnego trybu"; + /* No comment provided by engineer. */ "Database downgrade" = "Obniż wersję bazy danych"; @@ -1167,6 +1266,9 @@ /* message decrypt error item */ "Decryption error" = "Błąd odszyfrowania"; +/* No comment provided by engineer. */ +"decryption errors" = "błąd odszyfrowywania"; + /* pref value */ "default (%@)" = "domyślne (%@)"; @@ -1293,6 +1395,9 @@ /* deleted chat item */ "deleted" = "usunięty"; +/* No comment provided by engineer. */ +"Deleted" = "Usunięto"; + /* No comment provided by engineer. */ "Deleted at" = "Usunięto o"; @@ -1305,6 +1410,9 @@ /* rcv group event chat item */ "deleted group" = "usunięta grupa"; +/* No comment provided by engineer. */ +"Deletion errors" = "Błędy usuwania"; + /* No comment provided by engineer. */ "Delivery" = "Dostarczenie"; @@ -1329,6 +1437,12 @@ /* snd error text */ "Destination server error: %@" = "Błąd docelowego serwera: %@"; +/* No comment provided by engineer. */ +"Detailed statistics" = "Szczegółowe statystyki"; + +/* No comment provided by engineer. */ +"Details" = "Szczegóły"; + /* No comment provided by engineer. */ "Develop" = "Deweloperskie"; @@ -1431,12 +1545,21 @@ /* chat item action */ "Download" = "Pobierz"; +/* No comment provided by engineer. */ +"Download errors" = "Błędy pobierania"; + /* No comment provided by engineer. */ "Download failed" = "Pobieranie nie udane"; /* server test step */ "Download file" = "Pobierz plik"; +/* No comment provided by engineer. */ +"Downloaded" = "Pobrane"; + +/* No comment provided by engineer. */ +"Downloaded files" = "Pobrane pliki"; + /* No comment provided by engineer. */ "Downloading archive" = "Pobieranie archiwum"; @@ -1449,6 +1572,9 @@ /* integrity error chat item */ "duplicate message" = "zduplikowana wiadomość"; +/* No comment provided by engineer. */ +"duplicates" = "duplikaty"; + /* No comment provided by engineer. */ "Duration" = "Czas trwania"; @@ -1707,6 +1833,9 @@ /* No comment provided by engineer. */ "Error exporting chat database" = "Błąd eksportu bazy danych czatu"; +/* No comment provided by engineer. */ +"Error exporting theme: %@" = "Błąd eksportowania motywu: %@"; + /* No comment provided by engineer. */ "Error importing chat database" = "Błąd importu bazy danych czatu"; @@ -1722,9 +1851,18 @@ /* No comment provided by engineer. */ "Error receiving file" = "Błąd odbioru pliku"; +/* No comment provided by engineer. */ +"Error reconnecting server" = "Błąd ponownego łączenia z serwerem"; + +/* No comment provided by engineer. */ +"Error reconnecting servers" = "Błąd ponownego łączenia serwerów"; + /* No comment provided by engineer. */ "Error removing member" = "Błąd usuwania członka"; +/* No comment provided by engineer. */ +"Error resetting statistics" = "Błąd resetowania statystyk"; + /* No comment provided by engineer. */ "Error saving %@ servers" = "Błąd zapisu %@ serwerów"; @@ -1804,6 +1942,9 @@ /* No comment provided by engineer. */ "Error: URL is invalid" = "Błąd: URL jest nieprawidłowy"; +/* No comment provided by engineer. */ +"Errors" = "Błędy"; + /* No comment provided by engineer. */ "Even when disabled in the conversation." = "Nawet po wyłączeniu w rozmowie."; @@ -1816,12 +1957,18 @@ /* chat item action */ "Expand" = "Rozszerz"; +/* No comment provided by engineer. */ +"expired" = "wygasły"; + /* No comment provided by engineer. */ "Export database" = "Eksportuj bazę danych"; /* No comment provided by engineer. */ "Export error:" = "Błąd eksportu:"; +/* No comment provided by engineer. */ +"Export theme" = "Eksportuj motyw"; + /* No comment provided by engineer. */ "Exported database archive." = "Wyeksportowane archiwum bazy danych."; @@ -1843,6 +1990,21 @@ /* No comment provided by engineer. */ "Favorite" = "Ulubione"; +/* No comment provided by engineer. */ +"File error" = "Błąd pliku"; + +/* file error text */ +"File not found - most likely file was deleted or cancelled." = "Nie odnaleziono pliku - najprawdopodobniej plik został usunięty lub anulowany."; + +/* file error text */ +"File server error: %@" = "Błąd serwera plików: %@"; + +/* No comment provided by engineer. */ +"File status" = "Status pliku"; + +/* copied message info */ +"File status: %@" = "Status pliku: %@"; + /* No comment provided by engineer. */ "File will be deleted from servers." = "Plik zostanie usunięty z serwerów."; @@ -1957,6 +2119,12 @@ /* No comment provided by engineer. */ "GIFs and stickers" = "GIF-y i naklejki"; +/* message preview */ +"Good afternoon!" = "Dzień dobry!"; + +/* message preview */ +"Good morning!" = "Dzień dobry!"; + /* No comment provided by engineer. */ "Group" = "Grupa"; @@ -2134,6 +2302,9 @@ /* No comment provided by engineer. */ "Import failed" = "Import nie udał się"; +/* No comment provided by engineer. */ +"Import theme" = "Importuj motyw"; + /* No comment provided by engineer. */ "Importing archive" = "Importowanie archiwum"; @@ -2155,6 +2326,9 @@ /* No comment provided by engineer. */ "In-call sounds" = "Dźwięki w rozmowie"; +/* No comment provided by engineer. */ +"inactive" = "nieaktywny"; + /* No comment provided by engineer. */ "Incognito" = "Incognito"; @@ -2218,6 +2392,9 @@ /* No comment provided by engineer. */ "Interface" = "Interfejs"; +/* No comment provided by engineer. */ +"Interface colors" = "Kolory interfejsu"; + /* invalid chat data */ "invalid chat" = "nieprawidłowy czat"; @@ -2470,6 +2647,9 @@ /* rcv group event chat item */ "member connected" = "połączony"; +/* item status text */ +"Member inactive" = "Członek nieaktywny"; + /* No comment provided by engineer. */ "Member role will be changed to \"%@\". All group members will be notified." = "Rola członka grupy zostanie zmieniona na \"%@\". Wszyscy członkowie grupy zostaną powiadomieni."; @@ -2479,6 +2659,9 @@ /* No comment provided by engineer. */ "Member will be removed from group - this cannot be undone!" = "Członek zostanie usunięty z grupy - nie można tego cofnąć!"; +/* No comment provided by engineer. */ +"Menus" = "Menu"; + /* item status text */ "Message delivery error" = "Błąd dostarczenia wiadomości"; @@ -2491,6 +2674,12 @@ /* No comment provided by engineer. */ "Message draft" = "Wersja robocza wiadomości"; +/* item status text */ +"Message forwarded" = "Wiadomość przekazana"; + +/* item status description */ +"Message may be delivered later if member becomes active." = "Wiadomość może zostać dostarczona później jeśli członek stanie się aktywny."; + /* No comment provided by engineer. */ "Message queue info" = "Informacje kolejki wiadomości"; @@ -2506,6 +2695,9 @@ /* notification */ "message received" = "wiadomość otrzymana"; +/* No comment provided by engineer. */ +"Message reception" = "Odebranie wiadomości"; + /* No comment provided by engineer. */ "Message routing fallback" = "Rezerwowe trasowania wiadomości"; @@ -2515,6 +2707,12 @@ /* No comment provided by engineer. */ "Message source remains private." = "Źródło wiadomości pozostaje prywatne."; +/* No comment provided by engineer. */ +"Message status" = "Status wiadomości"; + +/* copied message info */ +"Message status: %@" = "Status wiadomości: %@"; + /* No comment provided by engineer. */ "Message text" = "Tekst wiadomości"; @@ -2530,6 +2728,12 @@ /* No comment provided by engineer. */ "Messages from %@ will be shown!" = "Wiadomości od %@ zostaną pokazane!"; +/* No comment provided by engineer. */ +"Messages received" = "Otrzymane wiadomości"; + +/* No comment provided by engineer. */ +"Messages sent" = "Wysłane wiadomości"; + /* No comment provided by engineer. */ "Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Wiadomości, pliki i połączenia są chronione przez **szyfrowanie end-to-end** z doskonałym utajnianiem z wyprzedzeniem i odzyskiem po złamaniu."; @@ -2695,6 +2899,9 @@ /* No comment provided by engineer. */ "No device token!" = "Brak tokenu urządzenia!"; +/* item status description */ +"No direct connection yet, message is forwarded by admin." = "Brak bezpośredniego połączenia, wiadomość została przekazana przez administratora."; + /* No comment provided by engineer. */ "no e2e encryption" = "brak szyfrowania e2e"; @@ -2707,6 +2914,9 @@ /* No comment provided by engineer. */ "No history" = "Brak historii"; +/* No comment provided by engineer. */ +"No info, try to reload" = "Brak informacji, spróbuj przeładować"; + /* No comment provided by engineer. */ "No network connection" = "Brak połączenia z siecią"; @@ -2739,7 +2949,7 @@ time to disappear */ "off" = "wyłączony"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Wyłączony"; /* feature offered item */ @@ -2832,6 +3042,9 @@ /* authentication reason */ "Open migration to another device" = "Otwórz migrację na innym urządzeniu"; +/* No comment provided by engineer. */ +"Open server settings" = "Otwórz ustawienia serwera"; + /* No comment provided by engineer. */ "Open Settings" = "Otwórz Ustawienia"; @@ -2856,9 +3069,18 @@ /* No comment provided by engineer. */ "Or show this code" = "Lub pokaż ten kod"; +/* No comment provided by engineer. */ +"other" = "inne"; + /* No comment provided by engineer. */ "Other" = "Inne"; +/* No comment provided by engineer. */ +"Other %@ servers" = "Inne %@ serwery"; + +/* No comment provided by engineer. */ +"other errors" = "inne błędy"; + /* member role */ "owner" = "właściciel"; @@ -2901,6 +3123,9 @@ /* No comment provided by engineer. */ "peer-to-peer" = "peer-to-peer"; +/* No comment provided by engineer. */ +"Pending" = "Oczekujące"; + /* No comment provided by engineer. */ "People can connect to you only via the links you share." = "Ludzie mogą się z Tobą połączyć tylko poprzez linki, które udostępniasz."; @@ -2922,6 +3147,9 @@ /* No comment provided by engineer. */ "Please ask your contact to enable sending voice messages." = "Poproś Twój kontakt o włączenie wysyłania wiadomości głosowych."; +/* No comment provided by engineer. */ +"Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers." = "Sprawdź, czy telefon i komputer są podłączone do tej samej sieci lokalnej i czy zapora sieciowa komputera umożliwia połączenie.\nProszę podzielić się innymi problemami z deweloperami."; + /* No comment provided by engineer. */ "Please check that you used the correct link or ask your contact to send you another one." = "Sprawdź, czy użyłeś prawidłowego linku lub poproś Twój kontakt o przesłanie innego."; @@ -2979,6 +3207,9 @@ /* No comment provided by engineer. */ "Preview" = "Podgląd"; +/* No comment provided by engineer. */ +"Previously connected servers" = "Wcześniej połączone serwery"; + /* No comment provided by engineer. */ "Privacy & security" = "Prywatność i bezpieczeństwo"; @@ -3000,6 +3231,9 @@ /* No comment provided by engineer. */ "Private routing" = "Prywatne trasowanie"; +/* No comment provided by engineer. */ +"Private routing error" = "Błąd prywatnego trasowania"; + /* No comment provided by engineer. */ "Profile and server connections" = "Profil i połączenia z serwerem"; @@ -3018,6 +3252,9 @@ /* No comment provided by engineer. */ "Profile password" = "Hasło profilu"; +/* No comment provided by engineer. */ +"Profile theme" = "Motyw profilu"; + /* No comment provided by engineer. */ "Profile update will be sent to your contacts." = "Aktualizacja profilu zostanie wysłana do Twoich kontaktów."; @@ -3066,6 +3303,12 @@ /* No comment provided by engineer. */ "Protocol timeout per KB" = "Limit czasu protokołu na KB"; +/* No comment provided by engineer. */ +"Proxied" = "Trasowane przez proxy"; + +/* No comment provided by engineer. */ +"Proxied servers" = "Serwery trasowane przez proxy"; + /* No comment provided by engineer. */ "Push notifications" = "Powiadomienia push"; @@ -3108,6 +3351,9 @@ /* No comment provided by engineer. */ "Receipts are disabled" = "Potwierdzenia są wyłączone"; +/* No comment provided by engineer. */ +"Receive errors" = "Błędy otrzymania"; + /* No comment provided by engineer. */ "received answer…" = "otrzymano odpowiedź…"; @@ -3126,6 +3372,15 @@ /* message info title */ "Received message" = "Otrzymano wiadomość"; +/* No comment provided by engineer. */ +"Received messages" = "Otrzymane wiadomości"; + +/* No comment provided by engineer. */ +"Received reply" = "Otrzymano odpowiedź"; + +/* No comment provided by engineer. */ +"Received total" = "Otrzymano łącznie"; + /* No comment provided by engineer. */ "Receiving address will be changed to a different server. Address change will complete after sender comes online." = "Adres odbiorczy zostanie zmieniony na inny serwer. Zmiana adresu zostanie zakończona gdy nadawca będzie online."; @@ -3144,9 +3399,24 @@ /* No comment provided by engineer. */ "Recipients see updates as you type them." = "Odbiorcy widzą aktualizacje podczas ich wpisywania."; +/* No comment provided by engineer. */ +"Reconnect" = "Połącz ponownie"; + /* No comment provided by engineer. */ "Reconnect all connected servers to force message delivery. It uses additional traffic." = "Połącz ponownie wszystkie połączone serwery, aby wymusić dostarczanie wiadomości. Wykorzystuje dodatkowy ruch."; +/* No comment provided by engineer. */ +"Reconnect all servers" = "Połącz ponownie wszystkie serwery"; + +/* No comment provided by engineer. */ +"Reconnect all servers?" = "Połączyć ponownie wszystkie serwery?"; + +/* No comment provided by engineer. */ +"Reconnect server to force message delivery. It uses additional traffic." = "Ponownie połącz z serwerem w celu wymuszenia dostarczenia wiadomości. Wykorzystuje to dodatkowy ruch."; + +/* No comment provided by engineer. */ +"Reconnect server?" = "Połączyć ponownie serwer?"; + /* No comment provided by engineer. */ "Reconnect servers?" = "Ponownie połączyć serwery?"; @@ -3180,6 +3450,9 @@ /* No comment provided by engineer. */ "Remove" = "Usuń"; +/* No comment provided by engineer. */ +"Remove image" = "Usuń obraz"; + /* No comment provided by engineer. */ "Remove member" = "Usuń członka"; @@ -3237,12 +3510,24 @@ /* No comment provided by engineer. */ "Reset" = "Resetuj"; +/* No comment provided by engineer. */ +"Reset all statistics" = "Resetuj wszystkie statystyki"; + +/* No comment provided by engineer. */ +"Reset all statistics?" = "Zresetować wszystkie statystyki?"; + /* No comment provided by engineer. */ "Reset colors" = "Resetuj kolory"; +/* No comment provided by engineer. */ +"Reset to app theme" = "Zresetuj do motywu aplikacji"; + /* No comment provided by engineer. */ "Reset to defaults" = "Przywróć wartości domyślne"; +/* No comment provided by engineer. */ +"Reset to user theme" = "Zresetuj do motywu użytkownika"; + /* No comment provided by engineer. */ "Restart the app to create a new chat profile" = "Uruchom ponownie aplikację, aby utworzyć nowy profil czatu"; @@ -3357,6 +3642,12 @@ /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Zapisane serwery WebRTC ICE zostaną usunięte"; +/* No comment provided by engineer. */ +"Scale" = "Skaluj"; + +/* No comment provided by engineer. */ +"Scan / Paste link" = "Skanuj / Wklej link"; + /* No comment provided by engineer. */ "Scan code" = "Zeskanuj kod"; @@ -3384,6 +3675,9 @@ /* network option */ "sec" = "sek"; +/* No comment provided by engineer. */ +"Secondary" = "Drugorzędny"; + /* time unit */ "seconds" = "sekundy"; @@ -3393,6 +3687,9 @@ /* server test step */ "Secure queue" = "Bezpieczna kolejka"; +/* No comment provided by engineer. */ +"Secured" = "Zabezpieczone"; + /* No comment provided by engineer. */ "Security assessment" = "Ocena bezpieczeństwa"; @@ -3405,6 +3702,9 @@ /* No comment provided by engineer. */ "Select" = "Wybierz"; +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Wybrane preferencje czatu zabraniają tej wiadomości."; + /* No comment provided by engineer. */ "Self-destruct" = "Samozniszczenie"; @@ -3438,6 +3738,9 @@ /* No comment provided by engineer. */ "Send disappearing message" = "Wyślij znikającą wiadomość"; +/* No comment provided by engineer. */ +"Send errors" = "Wyślij błędy"; + /* No comment provided by engineer. */ "Send link previews" = "Wyślij podgląd linku"; @@ -3504,15 +3807,36 @@ /* copied message info */ "Sent at: %@" = "Wysłano o: %@"; +/* No comment provided by engineer. */ +"Sent directly" = "Wysłano bezpośrednio"; + /* notification */ "Sent file event" = "Wyślij zdarzenie pliku"; /* message info title */ "Sent message" = "Wyślij wiadomość"; +/* No comment provided by engineer. */ +"Sent messages" = "Wysłane wiadomości"; + /* No comment provided by engineer. */ "Sent messages will be deleted after set time." = "Wysłane wiadomości zostaną usunięte po ustawionym czasie."; +/* No comment provided by engineer. */ +"Sent reply" = "Wyślij odpowiedź"; + +/* No comment provided by engineer. */ +"Sent total" = "Wysłano łącznie"; + +/* No comment provided by engineer. */ +"Sent via proxy" = "Wysłano przez proxy"; + +/* No comment provided by engineer. */ +"Server address" = "Adres serwera"; + +/* No comment provided by engineer. */ +"Server address is incompatible with network settings: %@." = "Adres serwera jest niekompatybilny z ustawieniami sieci: %@."; + /* srv error text. */ "Server address is incompatible with network settings." = "Adres serwera jest niekompatybilny z ustawieniami sieciowymi."; @@ -3528,12 +3852,24 @@ /* No comment provided by engineer. */ "Server test failed!" = "Test serwera nie powiódł się!"; +/* No comment provided by engineer. */ +"Server type" = "Typ serwera"; + /* srv error text */ "Server version is incompatible with network settings." = "Wersja serwera jest niekompatybilna z ustawieniami sieciowymi."; +/* No comment provided by engineer. */ +"Server version is incompatible with your app: %@." = "Wersja serwera jest niekompatybilna z aplikacją: %@."; + /* No comment provided by engineer. */ "Servers" = "Serwery"; +/* No comment provided by engineer. */ +"Servers info" = "Informacje o serwerach"; + +/* No comment provided by engineer. */ +"Servers statistics will be reset - this cannot be undone!" = "Statystyki serwerów zostaną zresetowane - nie można tego cofnąć!"; + /* No comment provided by engineer. */ "Session code" = "Kod sesji"; @@ -3543,6 +3879,9 @@ /* No comment provided by engineer. */ "Set contact name…" = "Ustaw nazwę kontaktu…"; +/* No comment provided by engineer. */ +"Set default theme" = "Ustaw domyślny motyw"; + /* No comment provided by engineer. */ "Set group preferences" = "Ustaw preferencje grupy"; @@ -3612,6 +3951,9 @@ /* No comment provided by engineer. */ "Show message status" = "Pokaż status wiadomości"; +/* No comment provided by engineer. */ +"Show percentage" = "Pokaż procent"; + /* No comment provided by engineer. */ "Show preview" = "Pokaż podgląd"; @@ -3621,6 +3963,9 @@ /* No comment provided by engineer. */ "Show:" = "Pokaż:"; +/* No comment provided by engineer. */ +"SimpleX" = "SimpleX"; + /* No comment provided by engineer. */ "SimpleX address" = "Adres SimpleX"; @@ -3666,6 +4011,9 @@ /* No comment provided by engineer. */ "Simplified incognito mode" = "Uproszczony tryb incognito"; +/* No comment provided by engineer. */ +"Size" = "Rozmiar"; + /* No comment provided by engineer. */ "Skip" = "Pomiń"; @@ -3675,6 +4023,9 @@ /* No comment provided by engineer. */ "Small groups (max 20)" = "Małe grupy (maks. 20)"; +/* No comment provided by engineer. */ +"SMP server" = "Serwer SMP"; + /* No comment provided by engineer. */ "SMP servers" = "Serwery SMP"; @@ -3699,9 +4050,15 @@ /* No comment provided by engineer. */ "Start migration" = "Rozpocznij migrację"; +/* No comment provided by engineer. */ +"Starting from %@." = "Zaczynanie od %@."; + /* No comment provided by engineer. */ "starting…" = "uruchamianie…"; +/* No comment provided by engineer. */ +"Statistics" = "Statystyki"; + /* No comment provided by engineer. */ "Stop" = "Zatrzymaj"; @@ -3744,6 +4101,15 @@ /* No comment provided by engineer. */ "Submit" = "Zatwierdź"; +/* No comment provided by engineer. */ +"Subscribed" = "Zasubskrybowano"; + +/* No comment provided by engineer. */ +"Subscription errors" = "Błędy subskrypcji"; + +/* No comment provided by engineer. */ +"Subscriptions ignored" = "Subskrypcje zignorowane"; + /* No comment provided by engineer. */ "Support SimpleX Chat" = "Wspieraj SimpleX Chat"; @@ -3792,6 +4158,9 @@ /* No comment provided by engineer. */ "TCP_KEEPINTVL" = "TCP_KEEPINTVL"; +/* No comment provided by engineer. */ +"Temporary file error" = "Tymczasowy błąd pliku"; + /* server test failure */ "Test failed at step %@." = "Test nie powiódł się na etapie %@."; @@ -3873,6 +4242,9 @@ /* No comment provided by engineer. */ "The text you pasted is not a SimpleX link." = "Tekst, który wkleiłeś nie jest linkiem SimpleX."; +/* No comment provided by engineer. */ +"Themes" = "Motywy"; + /* No comment provided by engineer. */ "These settings are for your current profile **%@**." = "Te ustawienia dotyczą Twojego bieżącego profilu **%@**."; @@ -3915,9 +4287,15 @@ /* No comment provided by engineer. */ "This is your own SimpleX address!" = "To jest twój własny adres SimpleX!"; +/* No comment provided by engineer. */ +"This link was used with another mobile device, please create a new link on the desktop." = "Ten link dostał użyty z innym urządzeniem mobilnym, proszę stworzyć nowy link na komputerze."; + /* No comment provided by engineer. */ "This setting applies to messages in your current chat profile **%@**." = "To ustawienie dotyczy wiadomości Twojego bieżącego profilu czatu **%@**."; +/* No comment provided by engineer. */ +"Title" = "Tytuł"; + /* No comment provided by engineer. */ "To ask any questions and to receive updates:" = "Aby zadać wszelkie pytania i otrzymywać aktualizacje:"; @@ -3957,9 +4335,15 @@ /* No comment provided by engineer. */ "Toggle incognito when connecting." = "Przełącz incognito przy połączeniu."; +/* No comment provided by engineer. */ +"Total" = "Łącznie"; + /* No comment provided by engineer. */ "Transport isolation" = "Izolacja transportu"; +/* No comment provided by engineer. */ +"Transport sessions" = "Sesje transportowe"; + /* No comment provided by engineer. */ "Trying to connect to the server used to receive messages from this contact (error: %@)." = "Próbowanie połączenia z serwerem używanym do odbierania wiadomości od tego kontaktu (błąd: %@)."; @@ -4095,12 +4479,21 @@ /* No comment provided by engineer. */ "Upgrade and open chat" = "Zaktualizuj i otwórz czat"; +/* No comment provided by engineer. */ +"Upload errors" = "Błędy przesłania"; + /* No comment provided by engineer. */ "Upload failed" = "Wgrywanie nie udane"; /* server test step */ "Upload file" = "Prześlij plik"; +/* No comment provided by engineer. */ +"Uploaded" = "Przesłane"; + +/* No comment provided by engineer. */ +"Uploaded files" = "Przesłane pliki"; + /* No comment provided by engineer. */ "Uploading archive" = "Wgrywanie archiwum"; @@ -4146,6 +4539,9 @@ /* No comment provided by engineer. */ "User profile" = "Profil użytkownika"; +/* No comment provided by engineer. */ +"User selection" = "Wybór użytkownika"; + /* No comment provided by engineer. */ "Using .onion hosts requires compatible VPN provider." = "Używanie hostów .onion wymaga kompatybilnego dostawcy VPN."; @@ -4254,6 +4650,12 @@ /* No comment provided by engineer. */ "Waiting for video" = "Oczekiwanie na film"; +/* No comment provided by engineer. */ +"Wallpaper accent" = "Akcent tapety"; + +/* No comment provided by engineer. */ +"Wallpaper background" = "Tło tapety"; + /* No comment provided by engineer. */ "wants to connect to you!" = "chce się z Tobą połączyć!"; @@ -4326,9 +4728,15 @@ /* snd error text */ "Wrong key or unknown connection - most likely this connection is deleted." = "Zły klucz lub nieznane połączenie - najprawdopodobniej to połączenie jest usunięte."; +/* file error text */ +"Wrong key or unknown file chunk address - most likely file is deleted." = "Zły klucz lub nieznany adres fragmentu pliku - najprawdopodobniej plik został usunięty."; + /* No comment provided by engineer. */ "Wrong passphrase!" = "Nieprawidłowe hasło!"; +/* No comment provided by engineer. */ +"XFTP server" = "Serwer XFTP"; + /* No comment provided by engineer. */ "XFTP servers" = "Serwery XFTP"; @@ -4386,6 +4794,9 @@ /* No comment provided by engineer. */ "You are invited to group" = "Jesteś zaproszony do grupy"; +/* No comment provided by engineer. */ +"You are not connected to these servers. Private routing is used to deliver messages to them." = "Nie jesteś połączony z tymi serwerami. Prywatne trasowanie jest używane do dostarczania do nich wiadomości."; + /* No comment provided by engineer. */ "you are observer" = "jesteś obserwatorem"; diff --git a/apps/ios/ru.lproj/Localizable.strings b/apps/ios/ru.lproj/Localizable.strings index 255ce8a6b5..d842364f91 100644 --- a/apps/ios/ru.lproj/Localizable.strings +++ b/apps/ios/ru.lproj/Localizable.strings @@ -2733,7 +2733,7 @@ time to disappear */ "off" = "нет"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Выключено"; /* feature offered item */ diff --git a/apps/ios/th.lproj/Localizable.strings b/apps/ios/th.lproj/Localizable.strings index 22b707f886..b1d328ce6e 100644 --- a/apps/ios/th.lproj/Localizable.strings +++ b/apps/ios/th.lproj/Localizable.strings @@ -2100,7 +2100,7 @@ time to disappear */ "off" = "ปิด"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "ปิด"; /* feature offered item */ diff --git a/apps/ios/tr.lproj/Localizable.strings b/apps/ios/tr.lproj/Localizable.strings index 644fa5fdbe..98645f8b42 100644 --- a/apps/ios/tr.lproj/Localizable.strings +++ b/apps/ios/tr.lproj/Localizable.strings @@ -647,7 +647,7 @@ /* rcv group event chat item */ "blocked %@" = "engellendi %@"; -/* marked deleted chat item preview text */ +/* blocked chat item */ "blocked by admin" = "yönetici tarafından engellendi"; /* No comment provided by engineer. */ @@ -2739,7 +2739,7 @@ time to disappear */ "off" = "kapalı"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Kapalı"; /* feature offered item */ diff --git a/apps/ios/uk.lproj/Localizable.strings b/apps/ios/uk.lproj/Localizable.strings index d41e32efc9..1d2630dd1f 100644 --- a/apps/ios/uk.lproj/Localizable.strings +++ b/apps/ios/uk.lproj/Localizable.strings @@ -647,7 +647,7 @@ /* rcv group event chat item */ "blocked %@" = "заблоковано %@"; -/* marked deleted chat item preview text */ +/* blocked chat item */ "blocked by admin" = "заблоковано адміністратором"; /* No comment provided by engineer. */ @@ -2739,7 +2739,7 @@ time to disappear */ "off" = "вимкнено"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "Вимкнено"; /* feature offered item */ diff --git a/apps/ios/zh-Hans.lproj/Localizable.strings b/apps/ios/zh-Hans.lproj/Localizable.strings index 9d990ab609..07eecee246 100644 --- a/apps/ios/zh-Hans.lproj/Localizable.strings +++ b/apps/ios/zh-Hans.lproj/Localizable.strings @@ -599,7 +599,7 @@ /* rcv group event chat item */ "blocked %@" = "已封禁 %@"; -/* marked deleted chat item preview text */ +/* blocked chat item */ "blocked by admin" = "由管理员封禁"; /* No comment provided by engineer. */ @@ -2571,7 +2571,7 @@ time to disappear */ "off" = "关闭"; -/* No comment provided by engineer. */ +/* blur media */ "Off" = "关闭"; /* feature offered item */ diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 025f722734..16a61da6b8 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -1760,6 +1760,12 @@ enum class ConnStatus { } } +@Serializable +data class ChatItemDeletion ( + val deletedChatItem: AChatItem, + val toChatItem: AChatItem? = null +) + @Serializable class AChatItem ( val chatInfo: ChatInfo, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index cac6a7082d..87bf26aea5 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -461,12 +461,11 @@ object ChatController { Log.d(TAG, "user: $user") try { apiSetNetworkConfig(getNetCfg()) - val justStarted = apiStartChat() - appPrefs.chatStopped.set(false) + val chatRunning = apiCheckChatRunning() val users = listUsers(null) chatModel.users.clear() chatModel.users.addAll(users) - if (justStarted) { + if (!chatRunning) { chatModel.currentUser.value = user chatModel.localUserCreated.value = true getUserChatData(null) @@ -485,6 +484,8 @@ object ChatController { } Log.d(TAG, "startChat: running") } + apiStartChat() + appPrefs.chatStopped.set(false) } catch (e: Throwable) { Log.e(TAG, "failed starting chat $e") throw e @@ -738,6 +739,15 @@ object ChatController { } } + private suspend fun apiCheckChatRunning(): Boolean { + val r = sendCmd(null, CC.CheckChatRunning()) + when (r) { + is CR.ChatRunning -> return true + is CR.ChatStopped -> return false + else -> throw Exception("failed check chat running: ${r.responseType} ${r.details}") + } + } + suspend fun apiStopChat(): Boolean { val r = sendCmd(null, CC.ApiStopChat()) when (r) { @@ -875,16 +885,16 @@ object ChatController { return null } - suspend fun apiDeleteChatItem(rh: Long?, type: ChatType, id: Long, itemId: Long, mode: CIDeleteMode): CR.ChatItemDeleted? { - val r = sendCmd(rh, CC.ApiDeleteChatItem(type, id, itemId, mode)) - if (r is CR.ChatItemDeleted) return r + suspend fun apiDeleteChatItems(rh: Long?, type: ChatType, id: Long, itemIds: List, mode: CIDeleteMode): List? { + val r = sendCmd(rh, CC.ApiDeleteChatItem(type, id, itemIds, mode)) + if (r is CR.ChatItemsDeleted) return r.chatItemDeletions Log.e(TAG, "apiDeleteChatItem bad response: ${r.responseType} ${r.details}") return null } - suspend fun apiDeleteMemberChatItem(rh: Long?, groupId: Long, groupMemberId: Long, itemId: Long): Pair? { - val r = sendCmd(rh, CC.ApiDeleteMemberChatItem(groupId, groupMemberId, itemId)) - if (r is CR.ChatItemDeleted) return r.deletedChatItem.chatItem to r.toChatItem?.chatItem + suspend fun apiDeleteMemberChatItems(rh: Long?, groupId: Long, itemIds: List): List? { + val r = sendCmd(rh, CC.ApiDeleteMemberChatItem(groupId, itemIds)) + if (r is CR.ChatItemsDeleted) return r.chatItemDeletions Log.e(TAG, "apiDeleteMemberChatItem bad response: ${r.responseType} ${r.details}") return null } @@ -2094,31 +2104,34 @@ object ChatController { chatModel.updateChatItem(r.reaction.chatInfo, r.reaction.chatReaction.chatItem) } } - is CR.ChatItemDeleted -> { + is CR.ChatItemsDeleted -> { if (!active(r.user)) { - if (r.toChatItem == null && r.deletedChatItem.chatItem.isRcvNew && r.deletedChatItem.chatInfo.ntfsEnabled) { - chatModel.decreaseUnreadCounter(rhId, r.user) + r.chatItemDeletions.forEach { (deletedChatItem, toChatItem) -> + if (toChatItem == null && deletedChatItem.chatItem.isRcvNew && deletedChatItem.chatInfo.ntfsEnabled) { + chatModel.decreaseUnreadCounter(rhId, r.user) + } } return } - - val cInfo = r.deletedChatItem.chatInfo - val cItem = r.deletedChatItem.chatItem - AudioPlayer.stop(cItem) - val isLastChatItem = chatModel.getChat(cInfo.id)?.chatItems?.lastOrNull()?.id == cItem.id - if (isLastChatItem && ntfManager.hasNotificationsForChat(cInfo.id)) { - ntfManager.cancelNotificationsForChat(cInfo.id) - ntfManager.displayNotification( - r.user, - cInfo.id, - cInfo.displayName, - generalGetString(if (r.toChatItem != null) MR.strings.marked_deleted_description else MR.strings.deleted_description) - ) - } - if (r.toChatItem == null) { - chatModel.removeChatItem(rhId, cInfo, cItem) - } else { - chatModel.upsertChatItem(rhId, cInfo, r.toChatItem.chatItem) + r.chatItemDeletions.forEach { (deletedChatItem, toChatItem) -> + val cInfo = deletedChatItem.chatInfo + val cItem = deletedChatItem.chatItem + AudioPlayer.stop(cItem) + val isLastChatItem = chatModel.getChat(cInfo.id)?.chatItems?.lastOrNull()?.id == cItem.id + if (isLastChatItem && ntfManager.hasNotificationsForChat(cInfo.id)) { + ntfManager.cancelNotificationsForChat(cInfo.id) + ntfManager.displayNotification( + r.user, + cInfo.id, + cInfo.displayName, + generalGetString(if (toChatItem != null) MR.strings.marked_deleted_description else MR.strings.deleted_description) + ) + } + if (toChatItem == null) { + chatModel.removeChatItem(rhId, cInfo, cItem) + } else { + chatModel.upsertChatItem(rhId, cInfo, toChatItem.chatItem) + } } } is CR.ReceivedGroupInvitation -> { @@ -2709,6 +2722,7 @@ sealed class CC { class ApiUnmuteUser(val userId: Long): CC() class ApiDeleteUser(val userId: Long, val delSMPQueues: Boolean, val viewPwd: String?): CC() class StartChat(val mainApp: Boolean): CC() + class CheckChatRunning: CC() class ApiStopChat: CC() @Serializable class ApiSetAppFilePaths(val appFilesFolder: String, val appTempFolder: String, val appAssetsFolder: String, val appRemoteHostsFolder: String): CC() @@ -2726,8 +2740,8 @@ sealed class CC { class ApiSendMessage(val type: ChatType, val id: Long, val file: CryptoFile?, val quotedItemId: Long?, val mc: MsgContent, val live: Boolean, val ttl: Int?): CC() class ApiCreateChatItem(val noteFolderId: Long, val file: CryptoFile?, val mc: MsgContent): CC() class ApiUpdateChatItem(val type: ChatType, val id: Long, val itemId: Long, val mc: MsgContent, val live: Boolean): CC() - class ApiDeleteChatItem(val type: ChatType, val id: Long, val itemId: Long, val mode: CIDeleteMode): CC() - class ApiDeleteMemberChatItem(val groupId: Long, val groupMemberId: Long, val itemId: Long): CC() + class ApiDeleteChatItem(val type: ChatType, val id: Long, val itemIds: List, val mode: CIDeleteMode): CC() + class ApiDeleteMemberChatItem(val groupId: Long, val itemIds: List): CC() class ApiChatItemReaction(val type: ChatType, val id: Long, val itemId: Long, val add: Boolean, val reaction: MsgReaction): CC() class ApiForwardChatItem(val toChatType: ChatType, val toChatId: Long, val fromChatType: ChatType, val fromChatId: Long, val itemId: Long, val ttl: Int?): CC() class ApiNewGroup(val userId: Long, val incognito: Boolean, val groupProfile: GroupProfile): CC() @@ -2854,6 +2868,7 @@ sealed class CC { is ApiUnmuteUser -> "/_unmute user $userId" is ApiDeleteUser -> "/_delete user $userId del_smp=${onOff(delSMPQueues)}${maybePwd(viewPwd)}" is StartChat -> "/_start main=${onOff(mainApp)}" + is CheckChatRunning -> "/_check running" is ApiStopChat -> "/_stop" is ApiSetAppFilePaths -> "/set file paths ${json.encodeToString(this)}" is ApiSetEncryptLocalFiles -> "/_files_encrypt ${onOff(enable)}" @@ -2875,8 +2890,8 @@ sealed class CC { "/_create *$noteFolderId json ${json.encodeToString(ComposedMessage(file, null, mc))}" } is ApiUpdateChatItem -> "/_update item ${chatRef(type, id)} $itemId live=${onOff(live)} ${mc.cmdString}" - is ApiDeleteChatItem -> "/_delete item ${chatRef(type, id)} $itemId ${mode.deleteMode}" - is ApiDeleteMemberChatItem -> "/_delete member item #$groupId $groupMemberId $itemId" + is ApiDeleteChatItem -> "/_delete item ${chatRef(type, id)} ${itemIds.joinToString(",")} ${mode.deleteMode}" + is ApiDeleteMemberChatItem -> "/_delete member item #$groupId ${itemIds.joinToString(",")}" is ApiChatItemReaction -> "/_reaction ${chatRef(type, id)} $itemId ${onOff(add)} ${json.encodeToString(reaction)}" is ApiForwardChatItem -> { val ttlStr = if (ttl != null) "$ttl" else "default" @@ -3007,6 +3022,7 @@ sealed class CC { is ApiUnmuteUser -> "apiUnmuteUser" is ApiDeleteUser -> "apiDeleteUser" is StartChat -> "startChat" + is CheckChatRunning -> "checkChatRunning" is ApiStopChat -> "apiStopChat" is ApiSetAppFilePaths -> "apiSetAppFilePaths" is ApiSetEncryptLocalFiles -> "apiSetEncryptLocalFiles" @@ -4666,7 +4682,7 @@ sealed class CR { @Serializable @SerialName("chatItemUpdated") class ChatItemUpdated(val user: UserRef, val chatItem: AChatItem): CR() @Serializable @SerialName("chatItemNotChanged") class ChatItemNotChanged(val user: UserRef, val chatItem: AChatItem): CR() @Serializable @SerialName("chatItemReaction") class ChatItemReaction(val user: UserRef, val added: Boolean, val reaction: ACIReaction): CR() - @Serializable @SerialName("chatItemDeleted") class ChatItemDeleted(val user: UserRef, val deletedChatItem: AChatItem, val toChatItem: AChatItem? = null, val byUser: Boolean): CR() + @Serializable @SerialName("chatItemsDeleted") class ChatItemsDeleted(val user: UserRef, val chatItemDeletions: List, val byUser: Boolean): CR() // group events @Serializable @SerialName("groupCreated") class GroupCreated(val user: UserRef, val groupInfo: GroupInfo): CR() @Serializable @SerialName("sentGroupInvitation") class SentGroupInvitation(val user: UserRef, val groupInfo: GroupInfo, val contact: Contact, val member: GroupMember): CR() @@ -4841,7 +4857,7 @@ sealed class CR { is ChatItemUpdated -> "chatItemUpdated" is ChatItemNotChanged -> "chatItemNotChanged" is ChatItemReaction -> "chatItemReaction" - is ChatItemDeleted -> "chatItemDeleted" + is ChatItemsDeleted -> "chatItemsDeleted" is GroupCreated -> "groupCreated" is SentGroupInvitation -> "sentGroupInvitation" is UserAcceptedGroupSent -> "userAcceptedGroupSent" @@ -5008,7 +5024,7 @@ sealed class CR { is ChatItemUpdated -> withUser(user, json.encodeToString(chatItem)) is ChatItemNotChanged -> withUser(user, json.encodeToString(chatItem)) is ChatItemReaction -> withUser(user, "added: $added\n${json.encodeToString(reaction)}") - is ChatItemDeleted -> withUser(user, "deletedChatItem:\n${json.encodeToString(deletedChatItem)}\ntoChatItem:\n${json.encodeToString(toChatItem)}\nbyUser: $byUser") + is ChatItemsDeleted -> withUser(user, "${chatItemDeletions.map { (deletedChatItem, toChatItem) -> "deletedChatItem: ${json.encodeToString(deletedChatItem)}\ntoChatItem: ${json.encodeToString(toChatItem)}" }} \nbyUser: $byUser") is GroupCreated -> withUser(user, json.encodeToString(groupInfo)) is SentGroupInvitation -> withUser(user, "groupInfo: $groupInfo\ncontact: $contact\nmember: $member") is UserAcceptedGroupSent -> json.encodeToString(groupInfo) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt index 610d8d95e9..d293bfe1fb 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt @@ -249,30 +249,30 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: suspend (chatId: val groupMember = toModerate?.second val deletedChatItem: ChatItem? val toChatItem: ChatItem? - if (mode == CIDeleteMode.cidmBroadcast && groupInfo != null && groupMember != null) { - val r = chatModel.controller.apiDeleteMemberChatItem( + val r = if (mode == CIDeleteMode.cidmBroadcast && groupInfo != null && groupMember != null) { + chatModel.controller.apiDeleteMemberChatItems( chatRh, groupId = groupInfo.groupId, - groupMemberId = groupMember.groupMemberId, - itemId = itemId + itemIds = listOf(itemId) ) - deletedChatItem = r?.first - toChatItem = r?.second } else { - val r = chatModel.controller.apiDeleteChatItem( + chatModel.controller.apiDeleteChatItems( chatRh, type = cInfo.chatType, id = cInfo.apiId, - itemId = itemId, + itemIds = listOf(itemId), mode = mode ) - deletedChatItem = r?.deletedChatItem?.chatItem - toChatItem = r?.toChatItem?.chatItem } - if (toChatItem == null && deletedChatItem != null) { - chatModel.removeChatItem(chatRh, cInfo, deletedChatItem) - } else if (toChatItem != null) { - chatModel.upsertChatItem(chatRh, cInfo, toChatItem) + val deleted = r?.firstOrNull() + if (deleted != null) { + deletedChatItem = deleted.deletedChatItem.chatItem + toChatItem = deleted.toChatItem?.chatItem + if (toChatItem != null) { + chatModel.upsertChatItem(chatRh, cInfo, toChatItem) + } else { + chatModel.removeChatItem(chatRh, cInfo, deletedChatItem) + } } } }, @@ -280,18 +280,14 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: suspend (chatId: if (itemIds.isNotEmpty()) { val chatInfo = chat.chatInfo withBGApi { - val deletedItems: ArrayList = arrayListOf() - for (itemId in itemIds) { - val di = chatModel.controller.apiDeleteChatItem( - chatRh, chatInfo.chatType, chatInfo.apiId, itemId, CIDeleteMode.cidmInternal - )?.deletedChatItem?.chatItem - if (di != null) { - deletedItems.add(di) + val deleted = chatModel.controller.apiDeleteChatItems( + chatRh, chatInfo.chatType, chatInfo.apiId, itemIds, CIDeleteMode.cidmInternal + ) + if (deleted != null) { + for (di in deleted) { + chatModel.removeChatItem(chatRh, chatInfo, di.deletedChatItem.chatItem) } } - for (di in deletedItems) { - chatModel.removeChatItem(chatRh, chatInfo, di) - } } } }, @@ -341,7 +337,7 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: suspend (chatId: } }, openDirectChat = { contactId -> - withBGApi { + scope.launch { openDirectChat(chatRh, contactId, chatModel) } }, @@ -1156,6 +1152,8 @@ private fun ScrollToBottom(chatId: ChatId, listState: LazyListState, chatItems: * this coroutine will be canceled with the message "Current mutation had a higher priority" because of animatedScroll. * Which breaks auto-scrolling to bottom. So just ignoring the exception * */ + } catch (e: IllegalArgumentException) { + Log.e(TAG, "Failed to scroll: ${e.stackTraceToString()}") } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt index dc32bb1318..f6d23c020f 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt @@ -28,7 +28,7 @@ import chat.simplex.common.views.chat.item.ItemAction import chat.simplex.common.views.helpers.* import chat.simplex.common.views.newchat.* import chat.simplex.res.MR -import kotlinx.coroutines.delay +import kotlinx.coroutines.* import kotlinx.datetime.Clock @Composable @@ -56,6 +56,8 @@ fun ChatListNavLinkView(chat: Chat, nextChatSelected: State) { } } + val scope = rememberCoroutineScope() + when (chat.chatInfo) { is ChatInfo.Direct -> { val contactNetworkStatus = chatModel.contactNetworkStatus(chat.chatInfo.contact) @@ -65,7 +67,7 @@ fun ChatListNavLinkView(chat: Chat, nextChatSelected: State) { ChatPreviewView(chat, showChatPreviews, chatModel.draft.value, chatModel.draftChatId.value, chatModel.currentUser.value?.profile?.displayName, contactNetworkStatus, disabled, linkMode, inProgress = false, progressByTimeout = false) } }, - click = { directChatAction(chat.remoteHostId, chat.chatInfo.contact, chatModel) }, + click = { scope.launch { directChatAction(chat.remoteHostId, chat.chatInfo.contact, chatModel) } }, dropdownMenuItems = { tryOrShowError("${chat.id}ChatListNavLinkDropdown", error = {}) { ContactMenuItems(chat, chat.chatInfo.contact, chatModel, showMenu, showMarkRead) @@ -84,7 +86,7 @@ fun ChatListNavLinkView(chat: Chat, nextChatSelected: State) { ChatPreviewView(chat, showChatPreviews, chatModel.draft.value, chatModel.draftChatId.value, chatModel.currentUser.value?.profile?.displayName, null, disabled, linkMode, inProgress.value, progressByTimeout) } }, - click = { if (!inProgress.value) groupChatAction(chat.remoteHostId, chat.chatInfo.groupInfo, chatModel, inProgress) }, + click = { if (!inProgress.value) scope.launch { groupChatAction(chat.remoteHostId, chat.chatInfo.groupInfo, chatModel, inProgress) } }, dropdownMenuItems = { tryOrShowError("${chat.id}ChatListNavLinkDropdown", error = {}) { GroupMenuItems(chat, chat.chatInfo.groupInfo, chatModel, showMenu, inProgress, showMarkRead) @@ -102,7 +104,7 @@ fun ChatListNavLinkView(chat: Chat, nextChatSelected: State) { ChatPreviewView(chat, showChatPreviews, chatModel.draft.value, chatModel.draftChatId.value, chatModel.currentUser.value?.profile?.displayName, null, disabled, linkMode, inProgress = false, progressByTimeout = false) } }, - click = { noteFolderChatAction(chat.remoteHostId, chat.chatInfo.noteFolder) }, + click = { scope.launch { noteFolderChatAction(chat.remoteHostId, chat.chatInfo.noteFolder) } }, dropdownMenuItems = { tryOrShowError("${chat.id}ChatListNavLinkDropdown", error = {}) { NoteFolderMenuItems(chat, showMenu, showMarkRead) @@ -178,42 +180,42 @@ private fun ErrorChatListItem() { } } -fun directChatAction(rhId: Long?, contact: Contact, chatModel: ChatModel) { +suspend fun directChatAction(rhId: Long?, contact: Contact, chatModel: ChatModel) { when { contact.activeConn == null && contact.profile.contactLink != null -> askCurrentOrIncognitoProfileConnectContactViaAddress(chatModel, rhId, contact, close = null, openChat = true) - else -> withBGApi { openChat(rhId, ChatInfo.Direct(contact), chatModel) } + else -> openChat(rhId, ChatInfo.Direct(contact), chatModel) } } -fun groupChatAction(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel, inProgress: MutableState? = null) { +suspend fun groupChatAction(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel, inProgress: MutableState? = null) { when (groupInfo.membership.memberStatus) { GroupMemberStatus.MemInvited -> acceptGroupInvitationAlertDialog(rhId, groupInfo, chatModel, inProgress) GroupMemberStatus.MemAccepted -> groupInvitationAcceptedAlert(rhId) - else -> withBGApi { openChat(rhId, ChatInfo.Group(groupInfo), chatModel) } + else -> openChat(rhId, ChatInfo.Group(groupInfo), chatModel) } } -fun noteFolderChatAction(rhId: Long?, noteFolder: NoteFolder) { - withBGApi { openChat(rhId, ChatInfo.Local(noteFolder), chatModel) } +suspend fun noteFolderChatAction(rhId: Long?, noteFolder: NoteFolder) { + openChat(rhId, ChatInfo.Local(noteFolder), chatModel) } -suspend fun openDirectChat(rhId: Long?, contactId: Long, chatModel: ChatModel) { +suspend fun openDirectChat(rhId: Long?, contactId: Long, chatModel: ChatModel) = coroutineScope { val chat = chatModel.controller.apiGetChat(rhId, ChatType.Direct, contactId) - if (chat != null) { + if (chat != null && isActive) { openLoadedChat(chat, chatModel) } } -suspend fun openGroupChat(rhId: Long?, groupId: Long, chatModel: ChatModel) { +suspend fun openGroupChat(rhId: Long?, groupId: Long, chatModel: ChatModel) = coroutineScope { val chat = chatModel.controller.apiGetChat(rhId, ChatType.Group, groupId) - if (chat != null) { + if (chat != null && isActive) { openLoadedChat(chat, chatModel) } } -suspend fun openChat(rhId: Long?, chatInfo: ChatInfo, chatModel: ChatModel) { +suspend fun openChat(rhId: Long?, chatInfo: ChatInfo, chatModel: ChatModel) = coroutineScope { val chat = chatModel.controller.apiGetChat(rhId, chatInfo.chatType, chatInfo.apiId) - if (chat != null) { + if (chat != null && isActive) { openLoadedChat(chat, chatModel) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ShareListNavLinkView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ShareListNavLinkView.kt index 91dffeb7c8..c755ab50a0 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ShareListNavLinkView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ShareListNavLinkView.kt @@ -13,6 +13,7 @@ import chat.simplex.common.model.* import chat.simplex.common.ui.theme.* import chat.simplex.common.views.helpers.* import chat.simplex.res.MR +import kotlinx.coroutines.launch @Composable fun ShareListNavLinkView( @@ -23,6 +24,7 @@ fun ShareListNavLinkView( hasSimplexLink: Boolean ) { val stopped = chatModel.chatRunning.value == false + val scope = rememberCoroutineScope() when (chat.chatInfo) { is ChatInfo.Direct -> { val voiceProhibited = isVoice && !chat.chatInfo.featureEnabled(ChatFeature.Voice) @@ -32,7 +34,7 @@ fun ShareListNavLinkView( if (voiceProhibited) { showForwardProhibitedByPrefAlert() } else { - directChatAction(chat.remoteHostId, chat.chatInfo.contact, chatModel) + scope.launch { directChatAction(chat.remoteHostId, chat.chatInfo.contact, chatModel) } } }, stopped @@ -49,7 +51,7 @@ fun ShareListNavLinkView( if (prohibitedByPref) { showForwardProhibitedByPrefAlert() } else { - groupChatAction(chat.remoteHostId, chat.chatInfo.groupInfo, chatModel) + scope.launch { groupChatAction(chat.remoteHostId, chat.chatInfo.groupInfo, chatModel) } } }, stopped @@ -58,7 +60,7 @@ fun ShareListNavLinkView( is ChatInfo.Local -> ShareListNavLinkLayout( chatLinkPreview = { SharePreviewView(chat, disabled = false) }, - click = { noteFolderChatAction(chat.remoteHostId, chat.chatInfo.noteFolder) }, + click = { scope.launch { noteFolderChatAction(chat.remoteHostId, chat.chatInfo.noteFolder) } }, stopped ) is ChatInfo.ContactRequest, is ChatInfo.ContactConnection, is ChatInfo.InvalidJSON -> {} diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml index 2b87fdd91c..cd50902ef7 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml @@ -318,7 +318,7 @@ ملون لدى جهة الاتصال التعمية بين الطريفين إنشاء - إنشاء حسابك الشخصي + إنشاء ملف تعريف مكالمة جارية... تفعيل التدمير الذاتي الموافقة على التعمية… @@ -354,7 +354,7 @@ حذف المجموعة؟ حذف الرابط حُذِفت في: %s - المجموعة المحذوفة + المجموعة حُذِفت حذف الصورة تخصيص السمات حذف قاعدة البيانات @@ -1403,7 +1403,7 @@ سيتم إخفاء كافة الرسائل الجديدة من %s! محظور حظر أعضاء المجموعة - جهة الاتصال المحذوفة + جهة الاتصال حُذِفت أنشِئ مجموعة باستخدام ملف تعريف عشوائي. أنشِئ مجموعة أنشِئ ملف تعريف @@ -1889,7 +1889,7 @@ خوادم SMP أخرى خوادم XFTP المهيأة خوادم XFTP أخرى - نسبة الاشتراك + أظهِر النسبة المئوية مُعطّل مستقرّ يتوفر تحديث: %s @@ -1947,15 +1947,15 @@ التمس التحديثات أخطاء معترف بها نُزّل تحديث التطبيق - جميع المستخدمين + جميع ملفات التعريف المحاولات تجريبي رُفع القطع متصل الخوادم المتصلة جارِ الاتصال - الاتصالات المشتركة - المستخدم الحالي + الاتصالات النسطة + ملف التعريف الحالي أخطاء الحذف إحصائيات مفصلة عطّل @@ -1969,7 +1969,7 @@ ثبّت التحديث قد يتم تسليم الرسالة لاحقًا إذا أصبح العضو نشطًا. الرسائل المُستلمة - اشتراكات الرسائل + استقبال الرسائل لا توجد معلومات، حاول إعادة التحميل أخطاء أخرى قيد الانتظار @@ -1987,4 +1987,15 @@ لكي يتم إعلامك بالإصدارات الجديدة، شغّل الفحص الدوري للإصدارات المستقرة أو التجريبية. أنت غير متصل بهذه الخوادم. يتم استخدام التوجيه الخاص لتسليم الرسائل إليهم. قرّب + حدث خطأ أثناء الاتصال بخادم التحويل %1$s. يُرجى المحاولة لاحقا. + عنوان خادم التحويل غير متوافق مع إعدادات الشبكة: %1$s. + عنوان خادم الوجهة %1$s غير متوافق مع إعدادات خادم التحويل %2$s. + إصدار الخادم الوجهة %1$s غير متوافق مع خادم التحويل %2$s. + فشل خادم التحويل %1$s في الاتصال بالخادم الوجهة %2$s. يُرجى المحاولة لاحقا. + إصدار خادم التحويل غير متوافق مع إعدادات الشبكة: %1$s. + مطفي + قوي + تمويه الوسائط + متوسط + ناعم \ No newline at end of file diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml index 2029650eb8..b4fcc3867a 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml @@ -218,7 +218,7 @@ Benachrichtigungen Kontakt löschen? - Der Kontakt und alle Nachrichten werden gelöscht. Dies kann nicht rückgängig gemacht werden! + Es wird der Kontakt und alle Nachrichten gelöscht. Dies kann nicht rückgängig gemacht werden! Kontakt löschen Kontaktname festlegen… Verbunden @@ -278,7 +278,7 @@ Ablehnen Chatinhalte löschen? - Alle Nachrichten werden gelöscht. Dies kann nicht rückgängig gemacht werden! Die Nachrichten werden NUR bei Ihnen gelöscht. + Es werden alle Nachrichten gelöscht. Dies kann nicht rückgängig gemacht werden! Die Nachrichten werden NUR bei Ihnen gelöscht. Löschen Chatinhalte löschen Chatinhalte löschen @@ -593,20 +593,20 @@ Fehler beim Exportieren der Chat-Datenbank Chat-Datenbank importieren? Ihre aktuelle Chat-Datenbank wird GELÖSCHT und durch die importierte ERSETZT. -\nDiese Aktion kann nicht rückgängig gemacht werden – Ihr Profil, Ihre Kontakte, Nachrichten und Dateien gehen unwiderruflich verloren. +\nDiese Aktion kann nicht rückgängig gemacht werden! Ihr Profil, Ihre Kontakte, Nachrichten und Dateien gehen unwiderruflich verloren. Importieren Fehler beim Löschen der Chat-Datenbank Fehler beim Importieren der Chat-Datenbank Chat-Datenbank importiert Starten Sie die App neu, um die importierte Chat-Datenbank zu verwenden. Chat-Profil löschen? - Diese Aktion kann nicht rückgängig gemacht werden – Ihr Profil, Ihre Kontakte, Nachrichten und Dateien gehen unwiderruflich verloren. + Diese Aktion kann nicht rückgängig gemacht werden! Ihr Profil, Ihre Kontakte, Nachrichten und Dateien gehen unwiderruflich verloren. Chat-Datenbank gelöscht Starten Sie die App neu, um ein neues Chat-Profil zu erstellen. Sie dürfen die neueste Version Ihrer Chat-Datenbank NUR auf einem Gerät verwenden, andernfalls erhalten Sie möglicherweise keine Nachrichten mehr von einigen Ihrer Kontakte. Chat beenden, um Datenbankaktionen zu erlauben. Dateien und Medien löschen? - Diese Aktion kann nicht rückgängig gemacht werden – alle empfangenen und gesendeten Dateien und Medien werden gelöscht. Bilder mit niedriger Auflösung bleiben erhalten. + Diese Aktion kann nicht rückgängig gemacht werden! Es werden alle empfangenen und gesendeten Dateien und Medien gelöscht. Bilder mit niedriger Auflösung bleiben erhalten. Keine empfangenen oder gesendeten Dateien %d Datei(en) mit einem Gesamtspeicherverbrauch von %s nie @@ -616,7 +616,7 @@ %s Sekunde(n) Löschen der Nachrichten Automatisches Löschen von Nachrichten aktivieren? - Diese Aktion kann nicht rückgängig gemacht werden – alle empfangenen und gesendeten Nachrichten, die über den ausgewählten Zeitraum hinaus gehen, werden gelöscht. Dieser Vorgang kann mehrere Minuten dauern. + Diese Aktion kann nicht rückgängig gemacht werden! Es werden alle empfangenen und gesendeten Nachrichten, die über den ausgewählten Zeitraum hinaus gehen, gelöscht. Dieser Vorgang kann mehrere Minuten dauern. Nachrichten löschen Fehler beim Ändern der Einstellung @@ -667,7 +667,7 @@ Der Versuch, das Passwort der Datenbank zu ändern, konnte nicht abgeschlossen werden. Datenbanksicherung wiederherstellen Datenbanksicherung wiederherstellen? - Bitte geben Sie das vorherige Passwort ein, nachdem Sie die Datenbanksicherung wiederhergestellt haben. Diese Aktion kann nicht rückgängig gemacht werden. + Bitte geben Sie das vorherige Passwort ein, nachdem Sie die Datenbanksicherung wiederhergestellt haben. Diese Aktion kann nicht rückgängig gemacht werden! Wiederherstellen Fehler bei der Wiederherstellung der Datenbank Das Passwort wurde nicht im Schlüsselbund gefunden. Bitte geben Sie es manuell ein. Das kann passieren, wenn Sie die App-Daten mit einem Backup-Programm wieder hergestellt haben. Bitte nehmen Sie Kontakt mit den Entwicklern auf, wenn das nicht der Fall ist. @@ -989,7 +989,7 @@ App-Version: v%s Core Version: v%s Profil hinzufügen - Alle Chats und Nachrichten werden gelöscht. Dies kann nicht rückgängig gemacht werden! + Es werden alle Chats und Nachrichten gelöscht. Dies kann nicht rückgängig gemacht werden! Chat-Profil löschen für PING-Zähler Transport-Isolations-Modus aktualisieren\? @@ -1088,7 +1088,7 @@ Datenbank-Aktualisierung Unterschiedlicher Migrationsstand in der App/Datenbank: %s / %s Datenbank herabstufen und den Chat öffnen - Inkompatible Datenbank-Version + Datenbank-Version nicht kompatibel Warnung: Sie könnten einige Daten verlieren! Datenbank auf alte Version herabstufen Datenbank-IDs und Transport-Isolationsoption. @@ -1555,14 +1555,14 @@ Falsche Desktop-Adresse Geräte Desktop-Verbindung trennen? - Desktop-App-Version %s ist mit dieser App nicht kompatibel. + Die Desktop-App-Version %s ist nicht mit dieser App kompatibel. Neues Mobiltelefon-Gerät Nur ein Gerät kann gleichzeitig genutzt werden Verknüpfe Mobiltelefon- und Desktop-Apps! 🔗 Über ein sicheres quantenbeständiges Protokoll Vom Desktop aus nutzen und scannen Sie den QR-Code.]]> Um unerwünschte Nachrichten zu verbergen. - Inkompatible Version + Version nicht kompatibel (Neu)]]> Desktop entkoppeln? Verknüpfte Desktop-Optionen @@ -1854,14 +1854,14 @@ Fehler: %1$s Warnung bei der Nachrichtenzustellung Falscher Schlüssel oder unbekannte Verbindung - höchstwahrscheinlich ist diese Verbindung gelöscht. - Die Server-Version ist nicht mit den Netzwerk-Einstellungen kompatibel. + Die Server-Version ist nicht mit den Netzwerkeinstellungen kompatibel. Kapazität überschritten - der Empfänger hat die zuvor gesendeten Nachrichten nicht empfangen. Weiterleitungsserver: %1$s \nFehler auf dem Zielserver: %2$s Weiterleitungsserver: %1$s \nFehler: %2$s Netzwerk-Fehler - die Nachricht ist nach vielen Sende-Versuchen abgelaufen. - Die Server-Adresse ist nicht mit den Netzwerk-Einstellungen kompatibel. + Die Server-Adresse ist nicht mit den Netzwerkeinstellungen kompatibel. Immer Privates Routing Nie @@ -1967,8 +1967,8 @@ Inaktiv Verbunden Verbinden - Abonnierte Verbindungen - Aktueller Nutzer + Aktive Verbindungen + Aktuelles Profil Detaillierte Statistiken Details Heruntergeladen @@ -1977,7 +1977,7 @@ Fehler beim Zurücksetzen der Statistiken Fehler Empfangene Nachrichten - Nachrichten-Abonnements + Nachrichtenempfang Ausstehend Bisher verbundene Server Proxy-Server @@ -2010,7 +2010,7 @@ Aktualisierung installieren Bitte starten Sie die App neu. Bestätigt - Alle Nutzer + Alle Profile App-Aktualisierung wurde heruntergeladen Nach Aktualisierungen suchen Daten-Pakete heruntergeladen @@ -2026,7 +2026,7 @@ Keine Information - es wird versucht neu zu laden Dateispeicherort öffnen andere - Die Server-Adresse ist nicht mit den Netzwerk-Einstellungen kompatibel: %1$s. + Die Server-Adresse ist nicht mit den Netzwerkeinstellungen kompatibel: %1$s. Link scannen / einfügen Alle Server neu verbinden Server neu verbinden? @@ -2045,13 +2045,13 @@ Server-Adresse Später erinnern Server-Informationen - Ihre App ist nicht kompatibel mit der Server-Version: %1$s. - Prozentualer Anteil der Abonnements + Ihre App ist nicht mit der Server-Version kompatibel: %1$s. + Prozentualen Anteil anzeigen Zoom SMP-Server Informationen zeigen für Beginnend mit %s. -\nAlle Daten sind auf Ihrem Gerät geschützt. +\nAlle Daten werden nur auf Ihrem Gerät gespeichert. Statistiken Transport-Sitzungen Hochgeladen @@ -2071,4 +2071,15 @@ Aktualisierung verfügbar: %s Aktivieren Sie die periodische Überprüfung auf stabile oder Beta-Versionen der App, um über neue Versionen benachrichtigt zu werden. Herunterladen der Aktualisierung abgebrochen + Die Weiterleitungsserver-Adresse ist nicht kompatibel mit den Netzwerkeinstellungen: %1$s. + Die Verbindung des Weiterleitungsservers %1$s zum Zielserver %2$s schlug fehl. Bitte versuchen Sie es später erneut. + Die Zielserver-Version von %1$s ist nicht mit dem Weiterleitungsserver %2$s kompatibel. + Die Weiterleitungsserver-Version ist nicht kompatibel mit den Netzwerkeinstellungen: %1$s. + Die Zielserver-Adresse von %1$s ist nicht mit den Einstellungen des Weiterleitungsservers %2$s kompatibel. + Fehler beim Verbinden zum Weiterleitungsserver %1$s. Bitte versuchen Sie es später erneut. + Medium unscharf machen + Weich + Hart + Medium + Aus \ No newline at end of file diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml index dcf6b25181..836ab0865d 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml @@ -1777,7 +1777,7 @@ Servidor de reenvío: %1$s \nError: %2$s Problema en la red - el mensaje ha expirado tras muchos intentos de envío. - La versión del servidor es incompatible con la configuración de red. + La versión del servidor es incompatible con la configuración de la red. Enrutamiento privado Con servidores desconocidos NO usar enrutamiento privado. @@ -1841,7 +1841,7 @@ información cola del servidor: %1$s \n \núltimo mensaje recibido: %2$s - Restablecer al tema de la app + Restablecer al tema de la aplicación Enrutamiento privado de mensajes 🚀 Recibe archivos de forma segura Mejora del envío de mensajes @@ -1859,19 +1859,135 @@ Error al inicializar WebView. Actualiza tu sistema a la última versión. Por favor, ponte en contacto con los desarrolladores. \nError: %s Interfaz en persa - Clave incorrecta o dirección de bloque de datos del archivo desconocida - lo más probable es que el archivo se haya borrado. - Archivo no encontrado - el archivo probablemente ha sido borrado o cancelado. + Clave incorrecta o dirección del bloque del archivo desconocida. Es probable que el archivo se haya eliminado. + Archivo no encontrado, probablemente haya sido borrado o cancelado. Error del servidor de archivos: %1$s Error de archivo - Error de archivo temporal + Error en archivo temporal Estado del mensaje: %s Estado del mensaje Estado del archivo: %s Estado del archivo - Comprueba que el móvil y el ordenador están conectados a la misma red local y que el firewall del ordenador permite la conexión. + Comprueba que el móvil y el ordenador están conectados a la misma red local y que el cortafuegos del ordenador permite la conexión. \nPor favor, comparte cualquier otro problema con los desarrolladores. - Error al copiar + Copiar error Este enlace ha sido usado en otro dispositivo móvil, por favor crea un enlace nuevo en el ordenador. No se puede enviar el mensaje Las preferencias seleccionadas no permiten este mensaje. + Info servidores + Archivos + Mostrando info de + Suscrito + Errores de suscripción + Suscripciones ignoradas + Para ser notificado sobre versiones nuevas, activa el chequeo periódico para las versiones Estable o Beta. + Beta + Servidores SMP configurados + Servidores XFTP configurados + Servidores conectados + Conectando + Perfil actual + Zoom + Subido + Actualización disponible: %s + Descarga de actualización cancelada + Actualización descargada + Buscar actualizaciones + Buscar actualizaciones + Estadísticas + Total + Sesiones de transporte + Servidor XFTP + No estás conectado a estos servidores. Para enviarles mensajes se usa el enrutamiento privado. + Todos los perfiles + Conectado + Estadísticas detalladas + Detalles + Archivos subidos + Errores en subida + intentos + Completado + Conexiones + Creado + errores de descifrado + Eliminado + Errores de borrado + desactivado + Mensaje reenviado + El mensaje puede ser entregado más tarde si el miembro vuelve a estar activo. + Miembro inactivo + Por favor, inténtalo más tarde. + Error de enrutamiento privado + La dirección del servidor es incompatible con la configuración de red: %1$s. + La versión del servidor es incompatible con tu aplicación: %1$s. + Tamaño fuente + Error al restablecer las estadísticas + Restablecer + Las estadísticas de los servidores serán restablecidas. ¡No podrá deshacerse! + Descargado + Servidor SMP + Aún no hay conexión directa, el mensaje es reenviado por el administrador. + Otros servidores SMP + Otros servidores XFTP + Escanear / Pegar enlace + Mostrar porcentaje + Desactivar + Desactivado + Descargando actualización, por favor no cierres la aplicación + Descarga %s (%s) + Instalación completada + Instalar actualización + Abrir ubicación del archivo + Por favor, reinicia la aplicación. + Recordar más tarde + Saltar esta versión + Estable + inactivo + Error + Error al reconectar con el servidor + Error al reconectar con los servidores + Errores + Recepción de mensajes + Mensajes recibidos + Mensajes enviados + Sin información, intenta recargar + Pendiente + Servidores conectados previamente + Servidores con proxy + Mensajes recibidos + Total recibido + Errores de recepción + Reconectar todos los servidores + ¿Reconectar servidor? + ¿Reconectar servidores? + Reconectar el servidor para forzar la entrega de mensajes. Usa tráfico adicional. + Reconectar todos los servidores para forzar la entrega de mensajes. Usa tráfico adicional. + Restablecer estadísticas + ¿Restablecer todas las estadísticas? + Mensajes enviados + Total enviado + Archivos descargados + Errores en la descarga + duplicados + expirado + Abrir configuración del servidor + otro + otros errores + Con proxy + Reconectar + Seguro + Errores de envío + Enviado directamente + Enviado mediante proxy + Dirección del servidor + Tamaño + Conexiones activas + Iniciado desde %s. + Iniciado desde %s +\nTodos los datos son privados a tu dispositivo + Bloques eliminados + Bloques descargados + Bloques subidos + Reconocido + Errores de reconocimiento \ No newline at end of file diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/fr/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/fr/strings.xml index 92091ef126..a077223b45 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/fr/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/fr/strings.xml @@ -1135,7 +1135,7 @@ Partager l\'adresse Vous pouvez partager cette adresse avec vos contacts pour leur permettre de se connecter avec %s. Aperçu - Fond d\'écran + Fond Thème sombre Exporter le thème Importer un thème @@ -1873,4 +1873,120 @@ Ce lien a été utilisé avec un autre appareil mobile, veuillez créer un nouveau lien sur le desktop. Impossible d\'envoyer le message Les paramètres de chat sélectionnés ne permettent pas l\'envoi de ce message. + Connections actives + Tous les profiles + Reçu avec accusé de réception + La mise à jour de l\'app est téléchargée + Terminé + Profil actuel + Erreurs de déchiffrement + désactivé + Supprimé + Désactiver + Téléchargement de la mise à jour de l\'appli, ne pas fermer l\'appli + Téléchargement %s (%s) + Erreur de reconnexion au serveur + inactif + Scanner / Coller le lien + Le message peut être transmis plus tard si le membre devient actif. + Reconnecter tous les serveurs connectés pour forcer la livraison des messages. Cette méthode utilise du trafic supplémentaire. + Sessions de transport + L\'adresse du serveur est incompatible avec les paramètres réseau : %1$s. + La version du serveur est incompatible avec votre application : %1$s. + Erreur de routage privé + Veuillez essayer plus tard. + Membre inactif + Message transféré + Pas de connexion directe pour l\'instant, le message est transmis par l\'administrateur. + Serveurs connectés + Serveurs précédemment connectés + Serveurs routés via des proxy + Vous n\'êtes pas connecté à ces serveurs. Le routage privé est utilisé pour leur délivrer des messages. + Reconnecter le serveur ? + Reconnecter les serveurs ? + Reconnecter le serveur pour forcer la livraison des messages. Utilise du trafic supplémentaire. + Erreur de réinitialisation des statistiques + Réinitialiser + Les statistiques des serveurs seront réinitialisées - il n\'est pas possible de revenir en arrière ! + Téléversé + Statistiques détaillées + Messages envoyés + Serveur SMP + Chunks supprimés + Chunks téléchargés + Fichiers téléchargés + Erreurs de téléchargement + Adresse du serveur + Erreurs de téléversement + Bêta + Vérifier les mises à jour + Vérifier les mises à jour + Serveurs SMP configurés + Serveurs XFTP configurés + Désactivé + Installé avec succès + Installer la mise à jour + Ouvrir l\'emplacement du fichier + Autres serveurs SMP + Autres serveurs XFTP + Veuillez redémarrer l\'application. + Rappeler plus tard + Afficher le pourcentage + Sauter cette version + Stable + Pour être informé des nouvelles versions, activez la vérification périodique des versions Stable ou Bêta. + Mise à jour disponible : %s + Téléchargement de la mise à jour annulé + Taille de police + Zoom + tentatives + Connecté + Connexion + Détails + Téléchargé + Erreur + Erreur de reconnexion des serveurs + Erreurs + Fichiers + Réception de message + Messages reçus + Messages envoyés + Pas d\'information, essayez de recharger + En attente + Routé via un proxy + Messages reçus + Total reçu + Erreurs de réception + Reconnecter + Reconnecter tous les serveurs + Réinitialiser toutes les statistiques + Réinitialiser toutes les statistiques ? + Envoyé directement + Total envoyé + Envoyé via un proxy + Infos serveurs + Afficher les informations pour + À partir de %s. + À partir de %s. +\nToutes les données restent confinées dans votre appareil. + Statistiques + Total + Serveur XFTP + Erreurs d\'accusé de réception + Chunks téléversés + Connexions + Créé + Erreurs de suppression + doublons + expiré + Ouvrir les paramètres du serveur + autre + autres erreurs + Sécurisé + Erreurs d\'envoi + Taille + Inscrit + Erreurs d\'inscription + Inscriptions ignorées + Fichiers téléversés \ No newline at end of file diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml index 7195024ab5..e1e3e00d71 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml @@ -149,7 +149,7 @@ Kamera A Keystore-hoz nem sikerül hozzáférni az adatbázis jelszó mentése végett hívás folyamatban - Fotók automatikus elfogadása + Képek automatikus elfogadása A hívások kezdeményezése engedélyezve van az ismerősei számára. ALKALMAZÁS IKON Kiszolgáló hozzáadása QR-kód beolvasásával. @@ -293,7 +293,7 @@ Hitelesítés törlése készítő Megerősítés - Törlés nálam + Csak nálam %d üzenet törlése? Egyedi témák kapcsolódás (elfogadva) @@ -361,7 +361,7 @@ Az adatbázis titkosítás jelmondata megváltoztatásra és mentésre kerül a Keystore-ban. Az adatbázis titkosításra kerül és a jelmondat eltárolásra a beállításokban. Kiszolgáló törlése - Eszközhitelesítés kikapcsolva. SimpleX zárolás kikapcsolása. + A készüléken nincs beállítva a képernyőzár. A SimpleX zár ki van kapcsolva. Letiltás Letiltás minden csoport számára Engedélyezés minden csoport számára @@ -372,7 +372,7 @@ Számítógép címe %dmp Kézbesítési jelentések! - Eszközhitelesítés nem engedélyezett.A SimpleX zárolás bekapcsolható a Beállításokon keresztül, miután az eszköz hitelesítés engedélyezésre került. + A készüléken nincs beállítva a képernyőzár. A SimpleX zár az „Adatvédelem és biztonság” menüben kapcsolható be, miután beállította a képernyőzárat az eszközén. Titkosítás visszafejtési hiba Eltűnik ekkor: %s szerkesztve @@ -398,7 +398,7 @@ Eltűnő üzenet Ne hozzon létre címet Ne mutasd újra - SimpleX zárolás kikapcsolása + SimpleX zár kikapcsolása e2e titkosított ESZKÖZ e2e titkosított videóhívás @@ -424,7 +424,7 @@ engedélyezve az ön számára Eltűnő üzenetek Törlés - Törlés és ismerős értesítése + Törlés, és az ismerős értesítése letiltva %d másodperc Minden fájl törlése @@ -463,7 +463,7 @@ Adatbázis jelmondat szükséges a csevegés megnyitásához. %dnap Engedélyezés mindenki számára - Kézbesítési jelentések kikapcsolva! + A kézbesítési jelentések le vannak tiltva! Kibontás Hiba az üzenet küldésekor Jelkód megadása @@ -613,7 +613,7 @@ Csoport beállítások Hiba: %s Eltűnő üzenetek - SimpleX zárolás engedélyezése + SimpleX zár bekapcsolása Hiba a kapcsolat szinkronizálása során Hiba a cím létrehozásakor engedélyezve @@ -663,7 +663,7 @@ Azonnali értesítések Inkognitó mód Csevegési adatbázis importálása? - Azonnali értesítések kikapcsolva! + Az azonnali értesítések le vannak tiltva! Azonnali értesítések! Kép A fájlok- és a médiatartalom küldése le van tiltva ebben a csoportban. @@ -678,7 +678,7 @@ Megerősítés esetén az üzenetküldő kiszolgálók látni fogják az IP-címét és a szolgáltatóját – azt, hogy mely kiszolgálókhoz kapcsolódik. A kép akkor érkezik meg, amikor a küldője befejezte annak feltöltését. QR kód beolvasásával.]]> - Kapott SimpleX Chat meghívó hivatkozását megnyithatja böngészőjében: + A kapott SimpleX Chat meghívó hivatkozását megnyithatja böngészőjében: Ha az alkalmazás megnyitásakor megadja az önmegsemmisítő jelkódot: Megtalált számítógép Számítógépek @@ -726,7 +726,7 @@ Hamarosan további fejlesztések érkeznek! Az üzenetreakciók küldése le van tiltva ebben a csevegésben. Helytelen biztonsági kód! - Ez akkor fordulhat elő, ha ön, vagy az ismerőse régi adatbázis biztonsági mentést használt. + Ez akkor fordulhat elő, ha ön vagy az ismerőse régi adatbázis biztonsági mentést használt. Új asztali alkalmazás! Most már az adminok is: \n- törölhetik a tagok üzeneteit. @@ -853,7 +853,7 @@ Csoport elhagyása? nem Hamarosan további fejlesztések érkeznek! - ki + kikapcsolva SimpleX Chat telepítése a terminálhoz Új megjelenített név: Új jelmondat… @@ -1098,7 +1098,7 @@ Hívások nem sikerült elküldeni KEZELŐFELÜLET SZÍNEI - Visszaállít + Visszaállítás Előző jelszó megadása az adatbázis biztonsági mentésének visszaállítása után. Ez a művelet nem vonható vissza. Másodlagos SOCKS PROXY @@ -1192,7 +1192,7 @@ Színek alaphelyzetbe állítása Mentés Váltás - Kapott hivatkozás beillesztése az ismerősökhöz történő kapcsolódáshoz… + A kapott hivatkozás beillesztése az ismerősökhöz történő kapcsolódáshoz… Beolvasás Port megnyitása a tűzfalon indítás… @@ -1217,7 +1217,7 @@ Eltávolítás Tor .onion kiszolgálók használata Felfedés - SimpleX zárolási mód + Zárolási mód Fájl visszavonása XFTP kiszolgálók A fájlok- és a médiatartalom küldése le van tiltva. @@ -1256,21 +1256,21 @@ Lengyel kezelőfelület Kiszolgáló használata Fogadva ekkor: %s - SimpleX zárolás + SimpleX zár Mentés és csoporttagok értesítése Alaphelyzetbe állítás Csak az ismerőse tud üzenetreakciókat küldeni. Hangüzenetek elhagyta a csoportot Hangüzenet rögzítése - SimpleX zárolás bekapcsolva + SimpleX zár bekapcsolva közvetlen üzenet küldése Beolvasás mobilról Kapcsolatok ellenőrzése Üzenet megosztása… másodperc - SimpleX zárolás nincs engedélyezve! - SimpleX zárolás + A SimpleX zár nincs bekapcsolva! + SimpleX zár Beállítások Csevegési adatbázis %1$s eltávolítva @@ -1338,11 +1338,11 @@ A kiszolgálónak engedélyre van szüksége a várólisták létrehozásához, ellenőrizze jelszavát Kapcsolódni fog a csoport összes tagjához. Lehetséges, hogy a kiszolgáló címében szereplő tanúsítvány-ujjlenyomat helytelen - Az adatavédelem érdekében kapcsolja be a SimpleX zárolás funkciót. -\nA funkció engedélyezése előtt a rendszer felszólítja a hitelesítés befejezésére. + A biztonsága érdekében kapcsolja be a SimpleX zár funkciót. +\nA funkció bekapcsolása előtt a rendszer felszólítja a képernyőzár beállítására az eszközén. A videó akkor érkezik meg, amikor a küldője elérhető lesz, várjon, vagy ellenőrizze később! Hálózati kapcsolat ellenőrzése a következővel: %1$s, és próbálja újra. - A SimpleX zárolás a Beállításokon keresztül kapcsolható be. + A SimpleX zár az „Adatvédelem és biztonság” menüben kapcsolható be. Az alkalmazás összeomlott Ellenőrizze, hogy a megfelelő hivatkozást használta-e, vagy kérje meg ismerősét, hogy küldjön egy másikat. A kép nem dekódolható. Próbálja meg egy másik képpel, vagy lépjen kapcsolatba a fejlesztőkkel. @@ -1404,9 +1404,9 @@ A csevegési adatbázis nem titkosított - állítson be egy jelmondatot annak védelméhez. Közvetlen internet kapcsolat használata? Továbbra is kap hívásokat és értesítéseket a némított profiloktól, ha azok aktívak. - A fő csevegési profilja megküldésre kerül a csoporttagok számára + A fő csevegési profilja elküldésre kerül a csoporttagok számára Később engedélyezheti őket az alkalmazás Adatvédelem és biztonság menüpontban. - Rejtett profiljának felfedéséhez írja be a teljes jelszót a Csevegési profilok oldal keresőmezőjébe. + Rejtett profilja megjelenítéséhez írja be a teljes jelszavát a keresőmezőbe a Csevegési profilok menüben. A csevegés frissítése és megnyitása Hangüzeneteket küldéséhez engedélyeznie kell azok küldését az ismerősei számára. fogadja az üzeneteket, ismerősöket – a kiszolgálók, amelyeket az üzenetküldéshez használ.]]> @@ -1430,7 +1430,7 @@ a SimpleX Chat fejlesztőivel, ahol bármiről kérdezhet és értesülhet az újdonságokról.]]> Opcionális üdvözlő üzenettel. Ismeretlen adatbázis hiba: %s - Elrejthet vagy némíthat egy felhasználói profilt - tartsa lenyomva a menühöz. + Elrejtheti vagy lenémíthatja a felhasználó profiljait - koppintson (vagy asztali alkalmazásban kattintson) hosszan a profilra a felugró menühöz. Inkognító mód kapcsolódáskor. Tor .onion kiszolgálók beállításainak frissítése? Megoszthat egy hivatkozást vagy QR-kódot - így bárki csatlakozhat a csoporthoz. Ha a csoport később törlésre kerül, akkor nem fogja elveszíteni annak tagjait. @@ -1454,7 +1454,7 @@ Profilja csak az ismerőseivel kerül megosztásra. Néhány kiszolgáló megbukott a teszten: Koppintson a csatlakozáshoz - Ez a művelet nem vonható vissza - az összes fogadott és küldött fájl a médiatartalommal együtt törlésre kerülnek. Az alacsony felbontású fotók viszont megmaradnak. + Ez a művelet nem vonható vissza - az összes fogadott és küldött fájl a médiatartalommal együtt törlésre kerülnek. Az alacsony felbontású képek viszont megmaradnak. Kézbesítési jelentések engedélyezve vannak %d ismerősnél Küldés ezen keresztül: Köszönet a felhasználóknak - hozzájárulás a Weblaten! @@ -1589,7 +1589,7 @@ ismeretlen státusz %1$s megváltoztatta a nevét erre: %2$s törölt kapcsolattartási cím - törölt profilkép + törölte a profilképét új kapcsolattartási cím beállítása új profilképet állított be frissített profil @@ -1700,7 +1700,7 @@ Átköltöztetés befejezve Átköltöztetés egy másik eszközre QR-kód használatával. Átköltöztetés - Megjegyzés: ha két eszközön is ugyanazt az adatbázist használja, akkor biztonsági védelemként megszakítja a kapcsolataiból érkező üzenetek visszafejtését.]]> + Megjegyzés: ha két eszközön is ugyanazt az adatbázist használja, akkor biztonsági védelemként megszakítja az ismerőseitől érkező üzenetek visszafejtését.]]> Megpróbálhatja még egyszer. Hibás hivatkozás végpontok közötti kvantumrezisztens titkosítás @@ -1884,7 +1884,7 @@ Kapcsolódás Hibák Függőben - Kezdve ettől: %s. + Ekkortól kezdve: %s. \nMinden adat biztonságban van a készülékén. Elküldött üzenetek Proxyzott kiszolgálók @@ -1909,7 +1909,7 @@ Összes elküldött Proxyn keresztül küldve SMP-kiszolgáló - Kezdve ettől: %s. + Ekkortól kezdve: %s. Feltöltve XFTP-kiszolgáló Proxyzott @@ -1930,13 +1930,13 @@ Nyugtázott hibák próbálkozások Törölt fájltöredékek - Minden felhasználó + Összes profil Feltöltött fájltöredékek Elkészült Kapcsolódott kiszolgálók Beállított XFTP-kiszolgálók Kapcsolódva - Jelenlegi felhasználó + Jelenlegi profil Részletek visszafejtési hibák Törölve @@ -1959,12 +1959,12 @@ Információk megjelenítése ehhez: A kiszolgáló verziója nem kompatibilis az alkalmazással: %1$s. Ön nem kapcsolódik ezekhez a kiszolgálókhoz. A privát útválasztás az üzenetek kézbesítésére szolgál. - Feliratkozott kapcsolatok - Üzenet feliratkozások + Aktív kapcsolatok száma + Üzenetjelentés Feliratkozva Feliratkozási hibák Elutasított feliratkozások - Feliratkozási százalék + Százalék megjelenítése Alkalmazásfrissítés letöltve Frissítések keresése Frissítések keresése diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/in/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/in/strings.xml index d41069a1b2..cd62881dad 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/in/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/in/strings.xml @@ -49,4 +49,53 @@ Tentang SimpleX Chat Terima Terima + Batalkan + Kamera + tebal + menelepon… + Bluetooth + Panggilan ditutup + Ubah + Peringatan: arsip akan dihapus.]]> + Buat grup: untuk membuat grup baru.]]> + Blokir anggota + Blokir + Kamera + Hitam + Blokir anggota? + Ubah alamat penerima + Beta + Blokir untuk semua + Blokir anggota untuk semua? + Panggilan telah ditutup! + Seluler + Hubungkan melalui tautan satu kali? + Gabung Grup? + Gunakan profil saat ini + Gunakan profil penyamaran baru + Profil Anda akan dikirim ke kontak yang menerima tautan ini. + Anda akan terhubung ke semua anggota grup. + Hubungkan + Hubungkan penyamaran + Membuka basis data… + k + Hubungkan melalui alamat kontak? + Bersihkan + Bersihkan + Bersihkan + Cek koneksi internetmu dan coba lagi + Hubungkan + terhubung + Hubungkan + Terhubung + Terhubung + terhubung + Hubungkan langsung? + tersambung + selesai + terhubung + Komputer yang terhubung + Terhubung + Selesai + Ponsel yang terhubung \ No newline at end of file diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml index f2f62c1fb9..e5bc6ed20a 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml @@ -1874,7 +1874,7 @@ Impossibile inviare il messaggio Le preferenze della chat selezionata vietano questo messaggio. Connessioni - Connessioni sottoscritte + Connessioni attive Creato errori di decifrazione Statistiche dettagliate @@ -1883,7 +1883,7 @@ Errore di riconnessione al server Errore di riconnessione ai server scaduto - Iscrizioni ai messaggi + Ricezione messaggi altro altri errori In attesa @@ -1933,12 +1933,12 @@ Il messaggio può essere consegnato più tardi se il membro diventa attivo. Server SMP configurati Altri server SMP - Percentuale di iscrizione + Mostra percentuale inattivo Zoom Connesso In connessione - Utente attuale + Profilo attuale Dettagli Errori Messaggi ricevuti @@ -1948,7 +1948,7 @@ Informazioni di Statistiche Sessioni di trasporto - Tutti gli utenti + Tutti i profili tentativi Server XFTP configurati Completato @@ -1989,4 +1989,15 @@ Aggiornamento dell\'app scaricato Cerca aggiornamenti Installa aggiornamento + Il server di inoltro %1$s non è riuscito a connettersi al server di destinazione %2$s. Riprova più tardi. + L\'indirizzo del server di inoltro è incompatibile con le impostazioni di rete: %1$s. + L\'indirizzo del server di destinazione di %1$s è incompatibile con le impostazioni del server di inoltro %2$s. + La versione del server di destinazione di %1$s è incompatibile con il server di inoltro %2$s. + Errore di connessione al server di inoltro %1$s. Riprova più tardi. + La versione server di inoltro è incompatibile con le impostazioni di rete: %1$s. + Off + Sfocatura file multimediali + Leggera + Media + Forte \ No newline at end of file diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/nl/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/nl/strings.xml index 34255ce99b..58f09108ce 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/nl/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/nl/strings.xml @@ -863,7 +863,7 @@ Verbied het sturen van directe berichten naar leden. Verbieden het verzenden van spraak berichten. De beveiliging van SimpleX Chat is gecontroleerd door Trail of Bits. - Met optioneel welkomst bericht. + Met optioneel welkom bericht. Spraak berichten Uw contacten kunnen volledige verwijdering van berichten toestaan. U moet elke keer dat de app start het wachtwoord invoeren, deze wordt niet op het apparaat opgeslagen. @@ -961,12 +961,12 @@ Chinese en Spaanse interface Voer wachtwoord in bij zoeken Fout bij opslaan gebruikers wachtwoord - Welkomst bericht toevoegen + Welkom bericht toevoegen Niet meer weergeven Groep moderatie Fout bij updaten van gebruikers privacy Verder verminderd batterij verbruik - Groep welkomst bericht + Groep welkom bericht Verborgen chat profielen Profiel verbergen Verbergen @@ -984,16 +984,16 @@ Servers opslaan\? Bewaar profiel wachtwoord Stel het getoonde bericht in voor nieuwe leden! - Welkomst bericht opslaan\? + Welkom bericht opslaan? Ondersteuning voor bluetooth en andere verbeteringen. Tik om profiel te activeren. Dank aan de gebruikers – draag bij via Weblate! U kunt een gebruikers profiel verbergen of dempen - houd het vast voor het menu. zichtbaar maken Dempen opheffen - Welkomst bericht + Welkom bericht Om uw verborgen profiel te onthullen, voert u een volledig wachtwoord in een zoekveld in op de pagina Uw chat profielen. - Welkomst bericht + Welkom bericht U ontvangt nog steeds oproepen en meldingen van gedempte profielen wanneer deze actief zijn. Database downgraden Ongeldige migratie bevestiging @@ -1135,13 +1135,13 @@ Laten we praten in SimpleX Chat Sla instellingen voor automatisch accepteren op Instellingen opslaan\? - Voer welkomst bericht in... (optioneel) + Voer welkom bericht in... (optioneel) Maak geen adres aan Hoi! \nMaak verbinding met mij via SimpleX Chat: %s U kan het later maken Adres delen - Welkomst bericht invoeren… + Welkom bericht invoeren… Thema importeren SimpleX Extra accent @@ -1181,18 +1181,18 @@ Als u uw zelfvernietigings wachtwoord invoert tijdens het openen van de app: Als u deze toegangscode invoert bij het openen van de app, worden alle app-gegevens onomkeerbaar verwijderd! Toegangscode instellen - Berichtreacties verbieden. - Alleen jij kunt berichtreacties toevoegen. + Bericht reacties verbieden. + Alleen jij kunt bericht reacties toevoegen. Reacties op berichten zijn verboden in deze groep. Berichten reacties verbieden. - Sta berichtreacties alleen toe als uw contact dit toestaat. - Sta uw contactpersonen toe om berichtreacties toe te voegen. - Sta berichtreacties toe. - Groepsleden kunnen berichtreacties toevoegen. - Zowel u als uw contact kunnen berichtreacties toevoegen. + Sta bericht reacties alleen toe als uw contact dit toestaat. + Sta uw contactpersonen toe om bericht reacties toe te voegen. + Sta bericht reacties toe. + Groepsleden kunnen bericht reacties toevoegen. + Zowel u als uw contact kunnen bericht reacties toevoegen. Reacties op berichten Reacties op berichten zijn verboden in deze chat. - Alleen uw contact kan berichtreacties toevoegen. + Alleen uw contact kan bericht reacties toevoegen. dagen uren minuten @@ -1628,7 +1628,7 @@ je hebt %s geblokkeerd je hebt %s gedeblokkeerd Bericht te groot - Welkomstbericht is te lang + Welkom bericht is te lang De databasemigratie wordt uitgevoerd. \nDit kan enkele minuten duren. Audio oproep @@ -1834,7 +1834,7 @@ Bescherm uw IP-adres tegen de berichtenrelais die door uw contacten zijn gekozen. \nSchakel dit in in *Netwerk en servers*-instellingen. Herhalen - Chatkleuren + Chat kleuren Profiel thema Chat thema Extra accent 2 @@ -1870,7 +1870,7 @@ Bestandsstatus: %s Berichtstatus Kan bericht niet verzenden - Geselecteerde chatvoorkeuren verbieden dit bericht. + Geselecteerde chat voorkeuren verbieden dit bericht. Fout in privéroutering Serverversie is niet compatibel met uw app: %1$s. Bericht doorgestuurd @@ -1878,17 +1878,17 @@ Overige XFTP servers Link scannen/plakken Zoom - Huidige gebruiker + Huidig profiel Bestanden Server informatie Informatie weergeven voor Fouten Statistieken Transportsessies - Verbindingen geabonneerd + Actieve verbindingen Details Berichten ontvangen - Berichten abonnementen + Bericht ontvangst Beginnend vanaf %s. \nAlle gegevens zijn privé op uw apparaat. Verbonden servers @@ -1933,13 +1933,13 @@ Gedownloade bestanden Beveiligd Maat - Geabonneerd + Ingeschreven Geüploade bestanden Upload fouten Downloadfouten Server instellingen openen Server adres - Alle gebruikers + Alle profielen pogingen Stukken verwijderd voltooid @@ -1963,4 +1963,28 @@ Het serveradres is niet compatibel met de netwerkinstellingen: %1$s. Serverstatistieken worden gereset - dit kan niet ongedaan worden gemaakt! Beginnend vanaf %s. + Geconfigureerde SMP-servers + Geconfigureerde XFTP-servers + Percentage weergeven + Uitgeschakeld + App update downloaden. Sluit de app niet + %s downloaden (%s) + Succesvol geïnstalleerd + Installeer update + Open de bestandslocatie + Herstart de app. + Herinner later + Sla deze versie over + uitgeschakeld + App update is gedownload + Beta + Controleer op updates + Controleer op updates + Uitschakelen + Inschrijving fouten + Inschrijvingen genegeerd + Stabiel + Update beschikbaar: %s + Downloaden van update geannuleerd + Als u op de hoogte wilt worden gehouden van de nieuwe releases, schakelt u periodieke controle op stabiele of bètaversies in. \ No newline at end of file diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/pl/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/pl/strings.xml index ce2e3751a5..2422148b45 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/pl/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/pl/strings.xml @@ -1873,4 +1873,126 @@ Zły klucz lub nieznany adres fragmentu pliku - najprawdopodobniej plik został usunięty. Nie można wysłać wiadomości Wybrane preferencje czatu zabraniają tej wiadomości. + Błąd połączenia z serwerem przekierowania %1$s. Spróbuj ponownie później. + Adres serwera docelowego %1$s jest niekompatybilny z ustawieniami serwera przekazującego %2$s. + Serwer przekazujący %1$s nie mógł połączyć się z serwerem docelowym %2$s. Spróbuj ponownie później. + Wersja serwera przekierowującego jest niekompatybilna z ustawieniami sieciowymi: %1$s. + Wersja serwera docelowego %1$s jest niekompatybilna z serwerem przekierowującym %2$s. + Członek nieaktywny + Wiadomość przekazana + Wiadomość może zostać dostarczona później jeśli członek stanie się aktywny. + Beta + Sprawdź aktualizacje + Wyłączony + Pobierz %s (%s) + Aktualizacja aplikacji jest pobrana + Sprawdź aktualizacje + Pobieranie aktualizacji aplikacji, nie zamykaj aplikacji + Zainstalowano pomyślnie + Zainstaluj aktualizacje + Wyłącz + wyłączony + nieaktywny + Rozmiar czcionki + Połączony + Bieżący profil + Otrzymane wiadomości + Odebranie wiadomości + Błąd resetowania statystyk + duplikaty + Zakończono + Połączenia + Utworzono + Błędy usuwania + Fragmenty pobrane + Fragmenty przesłane + Pobrane pliki + Skonfigurowane serwery SMP + Potwierdzono + Błędy potwierdzenia + Usunięto + Aktywne połączenia + Wszystkie profile + Skonfigurowane serwery XFTP + błąd odszyfrowywania + Szczegółowe statystyki + Szczegóły + Błędy pobierania + Adres serwera przekierowującego jest niekompatybilny z ustawieniami sieciowymi: %1$s. + Pliki + Łączenie + Błędy + Połączone serwery + Błąd + Błąd ponownego łączenia z serwerem + Błąd ponownego łączenia serwerów + Pobrane + próby + wygasły + Fragmenty usunięte + Brak bezpośredniego połączenia, wiadomość została przekazana przez administratora. + Inne serwery SMP + Inne serwery XFTP + Pokaż procent + Stabilny + Aktualizacja dostępna: %s + Otwórz lokalizację pliku + Proszę zrestartować aplikację. + Przypomnij później + Pomiń tę wersję + Pobieranie aktualizacji anulowane + Aby otrzymywać powiadomienia o nowych wersjach, włącz okresowe sprawdzanie wersji Stabilnych lub Beta. + Przybliż + Brak informacji, spróbuj przeładować + Informacje o serwerach + Wyświetlanie informacji dla + Statystyki + Sesje transportowe + Zaczynanie od %s. +\nWszystkie dane są prywatne na Twoim urządzeniu. + Połącz ponownie wszystkie serwery + Połączyć ponownie serwer? + Nie jesteś połączony z tymi serwerami. Prywatne trasowanie jest używane do dostarczania do nich wiadomości. + Otrzymane wiadomości + Resetuj + Resetuj wszystkie statystyki + Wysłane wiadomości + Statystyki serwerów zostaną zresetowane - nie można tego cofnąć! + Przesłane + inne + Trasowane przez proxy + Otrzymano łącznie + inne błędy + Zabezpieczone + Zasubskrybowano + Przesłane pliki + Otwórz ustawienia serwera + Adres serwera + Wysłane wiadomości + Ponownie połącz ze wszystkimi połączonymi serwerami w celu wymuszenia dostarczenia wiadomości. Wykorzystuje to dodatkowy ruch. + Zresetować wszystkie statystyki? + Błędy subskrypcji + Subskrypcje zignorowane + Wysłano łącznie + Adres serwera jest niekompatybilny z ustawieniami sieci: %1$s. + Proszę spróbować później. + Błąd prywatnego trasowania + Wersja serwera jest niekompatybilna z aplikacją: %1$s. + Skanuj / Wklej link + Łącznie + Oczekujące + Wcześniej połączone serwery + Serwery trasowane przez proxy + Połączyć ponownie serwery? + Ponownie połącz z serwerem w celu wymuszenia dostarczenia wiadomości. Wykorzystuje to dodatkowy ruch. + Wysłano przez proxy + Serwer XFTP + Serwer SMP + Błędy otrzymania + Połącz ponownie + Wysłano bezpośrednio + Zaczynanie od %s. + Wyślij błędy + Rozmiar + Błędy przesłania \ No newline at end of file diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/pt-rBR/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/pt-rBR/strings.xml index bf14ac248d..f0a0c53496 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/pt-rBR/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/pt-rBR/strings.xml @@ -1658,4 +1658,10 @@ Permitir o envio de links do SimpleX. Todos os membros Configurações avançadas + Verificar atualizações + Completado + Servidores SMP configurados + Servidores XFTP configurados + Verifique sua conexão de internet e tente novamente + Verificar atualizações \ No newline at end of file diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/vi/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/vi/strings.xml index 85a7037cb6..c0b372413d 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/vi/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/vi/strings.xml @@ -482,4 +482,64 @@ Máy tính Địa chỉ máy tính Máy tính có một phiên bản không được hỗ trợ. Vui lòng đảm bảo rằng bạn sử dụng cùng một phiên bản ở cả hai thiết bị. + Đã xác nhận + Các kết nối đang hoạt động + Tất cả hồ sơ + Beta + Kiểm tra cập nhật + Đang kết nối + Các máy chủ SMP đã được cấu hình + Các máy chủ XFTP đã được cấu hình + Bản cập nhật ứng dụng đã được tải xuống + Kiểm tra cập nhật + Đã kết nối + thử + Các máy chủ đã kết nối + Lỗi xác nhận + Đã hoàn thành + Các khúc đã bị xóa + Các khúc đã được tải xuống + Các khúc đã được tải lên + Hồ sơ hiện tại + lỗi giải mã + Thống kê chi tiết + Chi tiết + Các kết nối + Đã tạo + Đã xóa + Lỗi xóa + %d sự kiện nhóm + Tin nhắn trực tiếp giữa các thành viên bị cấm trong nhóm này. + %d tệp với tổng kích thước là %s + phần di dời khác nhau trong ứng dụng/cơ sở dữ liệu: %s / %s + Tin nhắn trực tiếp + %dh + %d giờ + %d giờ + Tên, hình đại diện và cách ly truyền tải khác nhau. + Thiết bị + trực tiếp + Xác thực thiết bị không được bật. Bạn có thể bật SimpleX Lock thông qua phần Cài đặt, sau khi bạn bật xác thực thiết bị. + Tắt chỉ báo đã nhận? + Tin nhắn tự xóa + Tin nhắn tự xóa + Ngắt kết nối + Tắt (giữ thông tin ghi đè) + Biến mất vào lúc: %s + Tin nhắn tự xóa + Tắt cho tất cả + Tắt cho tất cả các nhóm + Biến mất vào lúc + Ngắt kết nối + Tắt thông báo + Tắt SimpleX Lock + Tắt (giữ thông tin ghi đè về nhóm) + Tin nhắn tự xóa bị cấm trong cuộc hội thoại này. + Tắt + Tắt chỉ báo đã nhận cho nhóm? + đã bị tắt + Đã bị tắt + Tắt + đã bị tắt + Tin nhắn tự xóa bị cấm trong nhóm này. \ No newline at end of file diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml index dd675791c6..92b5f792b1 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml @@ -1880,10 +1880,10 @@ 其他 SMP 服务器 其他 XFTP 服务器 扫描/粘贴链接 - 订阅百分比 + 显示百分比 不活跃 缩放 - 所有用户 + 所有配置文件 文件 没有信息,试试重新加载 服务器信息 @@ -1891,7 +1891,7 @@ 已连接 已连接的服务器 连接中 - 订阅的连接 + 活跃连接 详细统计数据 详情 已下载 @@ -1901,7 +1901,7 @@ 重设统计数据出错 错误 收到的消息 - 消息订阅 + 消息接收 待连接 先前连接的服务器 已代理的服务器 @@ -1962,7 +1962,7 @@ 订阅被忽略 已配置的 SMP 服务器 已配置的 XFTP 服务器 - 当前用户 + 当前配置文件 传输会话 已上传 已停用 @@ -1989,4 +1989,15 @@ 测试版 安装成功 安装更新 + %1$s 的目的地服务器版本不兼容转发服务器 %2$s. + 转发服务器 %1$s 连接目的地服务器 %2$s 失败。请稍后尝试。 + 转发服务器地址不兼容网络设置:%1$s。 + 转发服务器版本不兼容网络设置:%1$s。 + %1$s 的目的地服务器地址不兼容转发服务器 %2$s 的设置 + 连接转发服务器 %1$s 出错。请稍后尝试。 + 模糊媒体文件 + 中度 + 关闭 + 轻柔 + 强烈 \ No newline at end of file diff --git a/apps/multiplatform/gradle.properties b/apps/multiplatform/gradle.properties index 35bc671ccf..10af65c5af 100644 --- a/apps/multiplatform/gradle.properties +++ b/apps/multiplatform/gradle.properties @@ -26,11 +26,11 @@ android.enableJetifier=true kotlin.mpp.androidSourceSetLayoutVersion=2 kotlin.jvm.target=11 -android.version_name=6.0-beta.1 -android.version_code=226 +android.version_name=6.0-beta.2 +android.version_code=227 -desktop.version_name=6.0-beta.1 -desktop.version_code=57 +desktop.version_name=6.0-beta.2 +desktop.version_code=58 kotlin.version=1.9.23 gradle.plugin.version=8.2.0 diff --git a/apps/simplex-directory-service/src/Directory/Events.hs b/apps/simplex-directory-service/src/Directory/Events.hs index 31a7e94aad..33b43a239b 100644 --- a/apps/simplex-directory-service/src/Directory/Events.hs +++ b/apps/simplex-directory-service/src/Directory/Events.hs @@ -72,7 +72,7 @@ crDirectoryEvent = \case CRDeletedMemberUser {groupInfo} -> Just $ DEServiceRemovedFromGroup groupInfo CRGroupDeleted {groupInfo} -> Just $ DEGroupDeleted groupInfo CRChatItemUpdated {chatItem = AChatItem _ SMDRcv (DirectChat ct) _} -> Just $ DEItemEditIgnored ct - CRChatItemDeleted {deletedChatItem = AChatItem _ SMDRcv (DirectChat ct) _, byUser = False} -> Just $ DEItemDeleteIgnored ct + CRChatItemsDeleted {chatItemDeletions = ((ChatItemDeletion (AChatItem _ SMDRcv (DirectChat ct) _) _) : _), byUser = False} -> Just $ DEItemDeleteIgnored ct CRNewChatItem {chatItem = AChatItem _ SMDRcv (DirectChat ct) ci@ChatItem {content = CIRcvMsgContent mc, meta = CIMeta {itemLive}}} -> Just $ case (mc, itemLive) of (MCText t, Nothing) -> DEContactCommand ct ciId $ fromRight err $ A.parseOnly (directoryCmdP <* A.endOfInput) $ T.dropWhileEnd isSpace t diff --git a/cabal.project b/cabal.project index 99ef5ddaeb..5f2c823561 100644 --- a/cabal.project +++ b/cabal.project @@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: c605156302b78a8411a1a083c52e7d5506f11f9c + tag: 03ea151be5dce9a4b8683f9f28805bdc8ba66758 source-repository-package type: git diff --git a/package.yaml b/package.yaml index 1c9b372b20..be6d5a808e 100644 --- a/package.yaml +++ b/package.yaml @@ -1,5 +1,5 @@ name: simplex-chat -version: 6.0.0.1 +version: 6.0.0.3 #synopsis: #description: homepage: https://github.com/simplex-chat/simplex-chat#readme diff --git a/scripts/desktop/prepare-openssl-windows.sh b/scripts/desktop/prepare-openssl-windows.sh index 942646853f..d65d4b8e31 100644 --- a/scripts/desktop/prepare-openssl-windows.sh +++ b/scripts/desktop/prepare-openssl-windows.sh @@ -12,7 +12,7 @@ cd $root_dir if [ ! -f dist-newstyle/openssl-1.1.1w/libcrypto-1_1-x64.dll ]; then mkdir dist-newstyle 2>/dev/null || true cd dist-newstyle - curl --tlsv1.2 https://www.openssl.org/source/openssl-1.1.1w.tar.gz -o openssl.tar.gz + curl --tlsv1.2 https://www.openssl.org/source/openssl-1.1.1w.tar.gz -L -o openssl.tar.gz $WINDIR\\System32\\tar.exe -xvzf openssl.tar.gz cd openssl-1.1.1w ./Configure mingw64 diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index 89984b4b74..30390eeb9e 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."c605156302b78a8411a1a083c52e7d5506f11f9c" = "064idjqf5cz2l2079gphj5gpc7pb7kc1kgg0fv5jx1ni5c4a3yzb"; + "https://github.com/simplex-chat/simplexmq.git"."03ea151be5dce9a4b8683f9f28805bdc8ba66758" = "1gfkqzda6vw3fsd34c08jygwg0m5v211sm7v7jdvj0sx1p8428d4"; "https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d"; "https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl"; diff --git a/simplex-chat.cabal b/simplex-chat.cabal index a05608c5ad..ff34640c6e 100644 --- a/simplex-chat.cabal +++ b/simplex-chat.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: simplex-chat -version: 6.0.0.1 +version: 6.0.0.3 category: Web, System, Services, Cryptography homepage: https://github.com/simplex-chat/simplex-chat#readme author: simplex.chat diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index bcf6856c4f..4298b60a67 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -41,7 +41,7 @@ import Data.Fixed (div') import Data.Functor (($>)) import Data.Functor.Identity import Data.Int (Int64) -import Data.List (find, foldl', isSuffixOf, partition, sortOn) +import Data.List (find, foldl', isSuffixOf, mapAccumL, partition, sortOn) import Data.List.NonEmpty (NonEmpty (..), nonEmpty, toList, (<|)) import qualified Data.List.NonEmpty as L import Data.Map.Strict (Map) @@ -389,7 +389,7 @@ startChatController :: Bool -> Bool -> CM' (Async ()) startChatController mainApp enableSndFiles = do asks smpAgent >>= liftIO . resumeAgentClient unless mainApp $ chatWriteVar' subscriptionMode SMOnlyCreate - users <- fromRight [] <$> runExceptT (withStore' getUsers) + users <- fromRight [] <$> runExceptT (withFastStore' getUsers) restoreCalls s <- asks agentAsync readTVarIO s >>= maybe (start s users) (pure . fst) @@ -406,9 +406,8 @@ startChatController mainApp enableSndFiles = do startXFTP xftpStartWorkers void $ forkIO $ startFilesToReceive users startCleanupManager - startExpireCIs users - else - when enableSndFiles $ startXFTP xftpStartSndWorkers + void $ forkIO $ startExpireCIs users + else when enableSndFiles $ startXFTP xftpStartSndWorkers pure a1 startXFTP startWorkers = do tmp <- readTVarIO =<< asks tempDirectory @@ -457,7 +456,7 @@ startReceiveUserFiles user = do restoreCalls :: CM' () restoreCalls = do - savedCalls <- fromRight [] <$> runExceptT (withStore' getCalls) + savedCalls <- fromRight [] <$> runExceptT (withFastStore' getCalls) let callsMap = M.fromList $ map (\call@Call {contactId} -> (contactId, call)) savedCalls calls <- asks currentCalls atomically $ writeTVar calls callsMap @@ -529,61 +528,66 @@ processChatCommand' vr = \case u <- asks currentUser (smp, smpServers) <- chooseServers SPSMP (xftp, xftpServers) <- chooseServers SPXFTP - users <- withStore' getUsers + users <- withFastStore' getUsers forM_ users $ \User {localDisplayName = n, activeUser, viewPwdHash} -> when (n == displayName) . throwChatError $ if activeUser || isNothing viewPwdHash then CEUserExists displayName else CEInvalidDisplayName {displayName, validName = ""} auId <- withAgent (\a -> createUser a smp xftp) ts <- liftIO $ getCurrentTime >>= if pastTimestamp then coupleDaysAgo else pure - user <- withStore $ \db -> createUserRecordAt db (AgentUserId auId) p True ts - when (null users) $ withStore (\db -> createContact db user simplexContactProfile) `catchChatError` \_ -> pure () - withStore $ \db -> createNoteFolder db user + user <- withFastStore $ \db -> createUserRecordAt db (AgentUserId auId) p True ts + createPresetContactCards user `catchChatError` \_ -> pure () + withFastStore $ \db -> createNoteFolder db user storeServers user smpServers storeServers user xftpServers atomically . writeTVar u $ Just user pure $ CRActiveUser user where + createPresetContactCards :: User -> CM () + createPresetContactCards user = + withFastStore $ \db -> do + createContact db user simplexTeamContactProfile + createContact db user simplexStatusContactProfile chooseServers :: (ProtocolTypeI p, UserProtocol p) => SProtocolType p -> CM (NonEmpty (ServerCfg p), [ServerCfg p]) chooseServers protocol | sameServers = asks currentUser >>= readTVarIO >>= \case Nothing -> throwChatError CENoActiveUser - Just user -> chosenServers =<< withStore' (`getProtocolServers` user) + Just user -> chosenServers =<< withFastStore' (`getProtocolServers` user) | otherwise = chosenServers [] where chosenServers servers = do cfg <- asks config pure (useServers cfg protocol servers, servers) storeServers user servers = - unless (null servers) . withStore $ + unless (null servers) . withFastStore $ \db -> overwriteProtocolServers db user servers coupleDaysAgo t = (`addUTCTime` t) . fromInteger . negate . (+ (2 * day)) <$> randomRIO (0, day) day = 86400 - ListUsers -> CRUsersList <$> withStore' getUsersInfo + ListUsers -> CRUsersList <$> withFastStore' getUsersInfo APISetActiveUser userId' viewPwd_ -> do unlessM (lift chatStarted) $ throwChatError CEChatNotStarted user_ <- chatReadVar currentUser user' <- privateGetUser userId' validateUserPassword_ user_ user' viewPwd_ - withStore' (`setActiveUser` userId') + withFastStore' (`setActiveUser` userId') let user'' = user' {activeUser = True} chatWriteVar currentUser $ Just user'' pure $ CRActiveUser user'' SetActiveUser uName viewPwd_ -> do - tryChatError (withStore (`getUserIdByName` uName)) >>= \case + tryChatError (withFastStore (`getUserIdByName` uName)) >>= \case Left _ -> throwChatError CEUserUnknown Right userId -> processChatCommand $ APISetActiveUser userId viewPwd_ - SetAllContactReceipts onOff -> withUser $ \_ -> withStore' (`updateAllContactReceipts` onOff) >> ok_ + SetAllContactReceipts onOff -> withUser $ \_ -> withFastStore' (`updateAllContactReceipts` onOff) >> ok_ APISetUserContactReceipts userId' settings -> withUser $ \user -> do user' <- privateGetUser userId' validateUserPassword user user' Nothing - withStore' $ \db -> updateUserContactReceipts db user' settings + withFastStore' $ \db -> updateUserContactReceipts db user' settings ok user SetUserContactReceipts settings -> withUser $ \User {userId} -> processChatCommand $ APISetUserContactReceipts userId settings APISetUserGroupReceipts userId' settings -> withUser $ \user -> do user' <- privateGetUser userId' validateUserPassword user user' Nothing - withStore' $ \db -> updateUserGroupReceipts db user' settings + withFastStore' $ \db -> updateUserGroupReceipts db user' settings ok user SetUserGroupReceipts settings -> withUser $ \User {userId} -> processChatCommand $ APISetUserGroupReceipts userId settings APIHideUser userId' (UserPwd viewPwd) -> withUser $ \user -> do @@ -592,7 +596,7 @@ processChatCommand' vr = \case Just _ -> throwChatError $ CEUserAlreadyHidden userId' _ -> do when (T.null viewPwd) $ throwChatError $ CEEmptyUserPassword userId' - users <- withStore' getUsers + users <- withFastStore' getUsers unless (length (filter (isNothing . viewPwdHash) users) > 1) $ throwChatError $ CECantHideLastUser userId' viewPwdHash' <- hashPassword setUserPrivacy user user' {viewPwdHash = viewPwdHash', showNtfs = False} @@ -625,6 +629,7 @@ processChatCommand' vr = \case asks agentAsync >>= readTVarIO >>= \case Just _ -> pure CRChatRunning _ -> checkStoreNotChanged . lift $ startChatController mainApp enableSndFiles $> CRChatStarted + CheckChatRunning -> maybe CRChatStopped (const CRChatRunning) <$> chatReadVar agentAsync APIStopChat -> do ask >>= liftIO . stopChatController pure CRChatStopped @@ -633,7 +638,7 @@ processChatCommand' vr = \case lift $ withAgent' foregroundAgent chatWriteVar chatActivated True when restoreChat $ do - users <- withStore' getUsers + users <- withFastStore' getUsers lift $ do void . forkIO $ subscribeUsers True users void . forkIO $ startFilesToReceive users @@ -681,8 +686,8 @@ processChatCommand' vr = \case fileErrs <- lift $ importArchive cfg setStoreChanged pure $ CRArchiveImported fileErrs - APISaveAppSettings as -> withStore' (`saveAppSettings` as) >> ok_ - APIGetAppSettings platformDefaults -> CRAppSettings <$> withStore' (`getAppSettings` platformDefaults) + APISaveAppSettings as -> withFastStore' (`saveAppSettings` as) >> ok_ + APIGetAppSettings platformDefaults -> CRAppSettings <$> withFastStore' (`getAppSettings` platformDefaults) APIDeleteStorage -> withStoreChanged deleteStorage APIStorageEncryption cfg -> withStoreChanged $ sqlCipherExport cfg TestStorageEncryption key -> sqlCipherTestKey key >> ok_ @@ -701,31 +706,31 @@ processChatCommand' vr = \case . M.assocs <$> withConnection st (readTVarIO . DB.slow) APIGetChats {userId, pendingConnections, pagination, query} -> withUserId' userId $ \user -> do - (errs, previews) <- partitionEithers <$> withStore' (\db -> getChatPreviews db vr user pendingConnections pagination query) + (errs, previews) <- partitionEithers <$> withFastStore' (\db -> getChatPreviews db vr user pendingConnections pagination query) unless (null errs) $ toView $ CRChatErrors (Just user) (map ChatErrorStore errs) pure $ CRApiChats user previews APIGetChat (ChatRef cType cId) pagination search -> withUser $ \user -> case cType of -- TODO optimize queries calculating ChatStats, currently they're disabled CTDirect -> do - directChat <- withStore (\db -> getDirectChat db vr user cId pagination search) + directChat <- withFastStore (\db -> getDirectChat db vr user cId pagination search) pure $ CRApiChat user (AChat SCTDirect directChat) CTGroup -> do - groupChat <- withStore (\db -> getGroupChat db vr user cId pagination search) + groupChat <- withFastStore (\db -> getGroupChat db vr user cId pagination search) pure $ CRApiChat user (AChat SCTGroup groupChat) CTLocal -> do - localChat <- withStore (\db -> getLocalChat db user cId pagination search) + localChat <- withFastStore (\db -> getLocalChat db user cId pagination search) pure $ CRApiChat user (AChat SCTLocal localChat) CTContactRequest -> pure $ chatCmdError (Just user) "not implemented" CTContactConnection -> pure $ chatCmdError (Just user) "not supported" APIGetChatItems pagination search -> withUser $ \user -> do - chatItems <- withStore $ \db -> getAllChatItems db vr user pagination search + chatItems <- withFastStore $ \db -> getAllChatItems db vr user pagination search pure $ CRChatItems user Nothing chatItems APIGetChatItemInfo chatRef itemId -> withUser $ \user -> do - (aci@(AChatItem cType dir _ ci), versions) <- withStore $ \db -> + (aci@(AChatItem cType dir _ ci), versions) <- withFastStore $ \db -> (,) <$> getAChatItem db vr user chatRef itemId <*> liftIO (getChatItemVersions db itemId) let itemVersions = if null versions then maybeToList $ mkItemVersion ci else versions memberDeliveryStatuses <- case (cType, dir) of - (SCTGroup, SMDSnd) -> L.nonEmpty <$> withStore' (`getGroupSndStatuses` itemId) + (SCTGroup, SMDSnd) -> L.nonEmpty <$> withFastStore' (`getGroupSndStatuses` itemId) _ -> pure Nothing forwardedFromChatItem <- getForwardedFromItem user ci pure $ CRChatItemInfo user aci ChatItemInfo {itemVersions, memberDeliveryStatuses, forwardedFromChatItem} @@ -733,9 +738,9 @@ processChatCommand' vr = \case getForwardedFromItem :: User -> ChatItem c d -> CM (Maybe AChatItem) getForwardedFromItem user ChatItem {meta = CIMeta {itemForwarded}} = case itemForwarded of Just (CIFFContact _ _ (Just ctId) (Just fwdItemId)) -> - Just <$> withStore (\db -> getAChatItem db vr user (ChatRef CTDirect ctId) fwdItemId) + Just <$> withFastStore (\db -> getAChatItem db vr user (ChatRef CTDirect ctId) fwdItemId) Just (CIFFGroup _ _ (Just gId) (Just fwdItemId)) -> - Just <$> withStore (\db -> getAChatItem db vr user (ChatRef CTGroup gId) fwdItemId) + Just <$> withFastStore (\db -> getAChatItem db vr user (ChatRef CTGroup gId) fwdItemId) _ -> pure Nothing APISendMessage (ChatRef cType chatId) live itemTTL cm -> withUser $ \user -> case cType of CTDirect -> @@ -751,9 +756,9 @@ processChatCommand' vr = \case createNoteFolderContentItem user folderId cm Nothing APIUpdateChatItem (ChatRef cType chatId) itemId live mc -> withUser $ \user -> case cType of CTDirect -> withContactLock "updateChatItem" chatId $ do - ct@Contact {contactId} <- withStore $ \db -> getContact db vr user chatId + ct@Contact {contactId} <- withFastStore $ \db -> getContact db vr user chatId assertDirectAllowed user MDSnd ct XMsgUpdate_ - cci <- withStore $ \db -> getDirectCIWithReactions db user ct itemId + cci <- withFastStore $ \db -> getDirectCIWithReactions db user ct itemId case cci of CChatItem SMDSnd ci@ChatItem {meta = CIMeta {itemSharedMsgId, itemTimed, itemLive, editable}, content = ciContent} -> do case (ciContent, itemSharedMsgId, editable) of @@ -762,7 +767,7 @@ processChatCommand' vr = \case if changed || fromMaybe False itemLive then do (SndMessage {msgId}, _) <- sendDirectContactMessage user ct (XMsgUpdate itemSharedMId mc (ttl' <$> itemTimed) (justTrue . (live &&) =<< itemLive)) - ci' <- withStore' $ \db -> do + ci' <- withFastStore' $ \db -> do currentTs <- liftIO getCurrentTime when changed $ addInitialAndNewCIVersions db itemId (chatItemTs' ci, oldMC) (currentTs, mc) @@ -774,12 +779,12 @@ processChatCommand' vr = \case _ -> throwChatError CEInvalidChatItemUpdate CChatItem SMDRcv _ -> throwChatError CEInvalidChatItemUpdate CTGroup -> withGroupLock "updateChatItem" chatId $ do - Group gInfo@GroupInfo {groupId, membership} ms <- withStore $ \db -> getGroup db vr user chatId + Group gInfo@GroupInfo {groupId, membership} ms <- withFastStore $ \db -> getGroup db vr user chatId assertUserGroupRole gInfo GRAuthor if prohibitedSimplexLinks gInfo membership mc then pure $ chatCmdError (Just user) ("feature not allowed " <> T.unpack (groupFeatureNameText GFSimplexLinks)) else do - cci <- withStore $ \db -> getGroupCIWithReactions db user gInfo itemId + cci <- withFastStore $ \db -> getGroupCIWithReactions db user gInfo itemId case cci of CChatItem SMDSnd ci@ChatItem {meta = CIMeta {itemSharedMsgId, itemTimed, itemLive, editable}, content = ciContent} -> do case (ciContent, itemSharedMsgId, editable) of @@ -788,7 +793,7 @@ processChatCommand' vr = \case if changed || fromMaybe False itemLive then do (SndMessage {msgId}, _) <- sendGroupMessage user gInfo ms (XMsgUpdate itemSharedMId mc (ttl' <$> itemTimed) (justTrue . (live &&) =<< itemLive)) - ci' <- withStore' $ \db -> do + ci' <- withFastStore' $ \db -> do currentTs <- liftIO getCurrentTime when changed $ addInitialAndNewCIVersions db itemId (chatItemTs' ci, oldMC) (currentTs, mc) @@ -800,11 +805,11 @@ processChatCommand' vr = \case _ -> throwChatError CEInvalidChatItemUpdate CChatItem SMDRcv _ -> throwChatError CEInvalidChatItemUpdate CTLocal -> do - (nf@NoteFolder {noteFolderId}, cci) <- withStore $ \db -> (,) <$> getNoteFolder db user chatId <*> getLocalChatItem db user chatId itemId + (nf@NoteFolder {noteFolderId}, cci) <- withFastStore $ \db -> (,) <$> getNoteFolder db user chatId <*> getLocalChatItem db user chatId itemId case cci of CChatItem SMDSnd ci@ChatItem {content = CISndMsgContent oldMC} | mc == oldMC -> pure $ CRChatItemNotChanged user (AChatItem SCTLocal SMDSnd (LocalChat nf) ci) - | otherwise -> withStore' $ \db -> do + | otherwise -> withFastStore' $ \db -> do currentTs <- getCurrentTime addInitialAndNewCIVersions db itemId (chatItemTs' ci, oldMC) (currentTs, mc) ci' <- updateLocalChatItem' db user noteFolderId ci (CISndMsgContent mc) True @@ -812,57 +817,110 @@ processChatCommand' vr = \case _ -> throwChatError CEInvalidChatItemUpdate CTContactRequest -> pure $ chatCmdError (Just user) "not supported" CTContactConnection -> pure $ chatCmdError (Just user) "not supported" - APIDeleteChatItem (ChatRef cType chatId) itemId mode -> withUser $ \user -> case cType of + APIDeleteChatItem (ChatRef cType chatId) itemIds mode -> withUser $ \user -> case cType of CTDirect -> withContactLock "deleteChatItem" chatId $ do - (ct, CChatItem msgDir ci@ChatItem {meta = CIMeta {itemSharedMsgId, deletable}}) <- withStore $ \db -> (,) <$> getContact db vr user chatId <*> getDirectChatItem db user chatId itemId - case (mode, msgDir, itemSharedMsgId, deletable) of - (CIDMInternal, _, _, _) -> deleteDirectCI user ct ci True False - (CIDMBroadcast, SMDSnd, Just itemSharedMId, True) -> do + ct <- withStore $ \db -> getContact db vr user chatId + (errs, items) <- lift $ partitionEithers <$> withStoreBatch (\db -> map (getDirectCI db) (L.toList itemIds)) + unless (null errs) $ toView $ CRChatErrors (Just user) errs + case mode of + CIDMInternal -> deleteDirectCIs user ct items True False + CIDMBroadcast -> do + assertDeletable items assertDirectAllowed user MDSnd ct XMsgDel_ - (SndMessage {msgId}, _) <- sendDirectContactMessage user ct (XMsgDel itemSharedMId Nothing) + let msgIds = itemsMsgIds items + events = map (\msgId -> XMsgDel msgId Nothing) msgIds + forM_ (L.nonEmpty events) $ \events' -> + sendDirectContactMessages user ct events' if featureAllowed SCFFullDelete forUser ct - then deleteDirectCI user ct ci True False - else markDirectCIDeleted user ct ci msgId True =<< liftIO getCurrentTime - (CIDMBroadcast, _, _, _) -> throwChatError CEInvalidChatItemDelete + then deleteDirectCIs user ct items True False + else markDirectCIsDeleted user ct items True =<< liftIO getCurrentTime + where + getDirectCI :: DB.Connection -> ChatItemId -> IO (Either ChatError (CChatItem 'CTDirect)) + getDirectCI db itemId = runExceptT . withExceptT ChatErrorStore $ getDirectChatItem db user chatId itemId CTGroup -> withGroupLock "deleteChatItem" chatId $ do Group gInfo ms <- withStore $ \db -> getGroup db vr user chatId - CChatItem msgDir ci@ChatItem {meta = CIMeta {itemSharedMsgId, deletable}} <- withStore $ \db -> getGroupChatItem db user chatId itemId - case (mode, msgDir, itemSharedMsgId, deletable) of - (CIDMInternal, _, _, _) -> deleteGroupCI user gInfo ci True False Nothing =<< liftIO getCurrentTime - (CIDMBroadcast, SMDSnd, Just itemSharedMId, True) -> do + (errs, items) <- lift $ partitionEithers <$> withStoreBatch (\db -> map (getGroupCI db) (L.toList itemIds)) + unless (null errs) $ toView $ CRChatErrors (Just user) errs + case mode of + CIDMInternal -> deleteGroupCIs user gInfo items True False Nothing =<< liftIO getCurrentTime + CIDMBroadcast -> do + assertDeletable items assertUserGroupRole gInfo GRObserver -- can still delete messages sent earlier - (SndMessage {msgId}, _) <- sendGroupMessage user gInfo ms $ XMsgDel itemSharedMId Nothing - delGroupChatItem user gInfo ci msgId Nothing - (CIDMBroadcast, _, _, _) -> throwChatError CEInvalidChatItemDelete + let msgIds = itemsMsgIds items + events = L.nonEmpty $ map (`XMsgDel` Nothing) msgIds + mapM_ (sendGroupMessages user gInfo ms) events + delGroupChatItems user gInfo items Nothing + where + getGroupCI :: DB.Connection -> ChatItemId -> IO (Either ChatError (CChatItem 'CTGroup)) + getGroupCI db itemId = runExceptT . withExceptT ChatErrorStore $ getGroupChatItem db user chatId itemId CTLocal -> do - (nf, CChatItem _ ci) <- withStore $ \db -> (,) <$> getNoteFolder db user chatId <*> getLocalChatItem db user chatId itemId - deleteLocalCI user nf ci True False + nf <- withStore $ \db -> getNoteFolder db user chatId + (errs, items) <- lift $ partitionEithers <$> withStoreBatch (\db -> map (getLocalCI db) (L.toList itemIds)) + unless (null errs) $ toView $ CRChatErrors (Just user) errs + deleteLocalCIs user nf items True False + where + getLocalCI :: DB.Connection -> ChatItemId -> IO (Either ChatError (CChatItem 'CTLocal)) + getLocalCI db itemId = runExceptT . withExceptT ChatErrorStore $ getLocalChatItem db user chatId itemId CTContactRequest -> pure $ chatCmdError (Just user) "not supported" CTContactConnection -> pure $ chatCmdError (Just user) "not supported" - APIDeleteMemberChatItem gId mId itemId -> withUser $ \user -> withGroupLock "deleteChatItem" gId $ do + where + assertDeletable :: forall c. ChatTypeI c => [CChatItem c] -> CM () + assertDeletable items = do + currentTs <- liftIO getCurrentTime + unless (all (itemDeletable currentTs) items) $ throwChatError CEInvalidChatItemDelete + where + itemDeletable :: UTCTime -> CChatItem c -> Bool + itemDeletable currentTs (CChatItem msgDir ChatItem {meta = CIMeta {itemSharedMsgId, itemTs, itemDeleted}, content}) = + case msgDir of + -- We check with a 6 hour margin compared to CIMeta deletable to account for deletion on the border + SMDSnd -> isJust itemSharedMsgId && deletable' content itemDeleted itemTs (nominalDay + 6 * 3600) currentTs + SMDRcv -> False + itemsMsgIds :: [CChatItem c] -> [SharedMsgId] + itemsMsgIds = mapMaybe (\(CChatItem _ ChatItem {meta = CIMeta {itemSharedMsgId}}) -> itemSharedMsgId) + APIDeleteMemberChatItem gId itemIds -> withUser $ \user -> withGroupLock "deleteChatItem" gId $ do Group gInfo@GroupInfo {membership} ms <- withStore $ \db -> getGroup db vr user gId - CChatItem _ ci@ChatItem {chatDir, meta = CIMeta {itemSharedMsgId}} <- withStore $ \db -> getGroupChatItem db user gId itemId - case (chatDir, itemSharedMsgId) of - (CIGroupRcv GroupMember {groupMemberId, memberRole, memberId}, Just itemSharedMId) -> do - when (groupMemberId /= mId) $ throwChatError CEInvalidChatItemDelete - assertUserGroupRole gInfo $ max GRAdmin memberRole - (SndMessage {msgId}, _) <- sendGroupMessage user gInfo ms $ XMsgDel itemSharedMId $ Just memberId - delGroupChatItem user gInfo ci msgId (Just membership) - (_, _) -> throwChatError CEInvalidChatItemDelete + (errs, items) <- lift $ partitionEithers <$> withStoreBatch (\db -> map (getGroupCI db user) (L.toList itemIds)) + unless (null errs) $ toView $ CRChatErrors (Just user) errs + assertDeletable gInfo items + assertUserGroupRole gInfo GRAdmin + let msgMemIds = itemsMsgMemIds gInfo items + events = L.nonEmpty $ map (\(msgId, memId) -> XMsgDel msgId (Just memId)) msgMemIds + mapM_ (sendGroupMessages user gInfo ms) events + delGroupChatItems user gInfo items (Just membership) + where + getGroupCI :: DB.Connection -> User -> ChatItemId -> IO (Either ChatError (CChatItem 'CTGroup)) + getGroupCI db user itemId = runExceptT . withExceptT ChatErrorStore $ getGroupChatItem db user gId itemId + assertDeletable :: GroupInfo -> [CChatItem 'CTGroup] -> CM () + assertDeletable GroupInfo {membership = GroupMember {memberRole = membershipMemRole}} items = + unless (all itemDeletable items) $ throwChatError CEInvalidChatItemDelete + where + itemDeletable :: CChatItem 'CTGroup -> Bool + itemDeletable (CChatItem _ ChatItem {chatDir, meta = CIMeta {itemSharedMsgId}}) = + case chatDir of + CIGroupRcv GroupMember {memberRole} -> membershipMemRole >= memberRole && isJust itemSharedMsgId + CIGroupSnd -> isJust itemSharedMsgId + itemsMsgMemIds :: GroupInfo -> [CChatItem 'CTGroup] -> [(SharedMsgId, MemberId)] + itemsMsgMemIds GroupInfo {membership = GroupMember {memberId = membershipMemId}} = mapMaybe itemMsgMemIds + where + itemMsgMemIds :: CChatItem 'CTGroup -> Maybe (SharedMsgId, MemberId) + itemMsgMemIds (CChatItem _ ChatItem {chatDir, meta = CIMeta {itemSharedMsgId}}) = + join <$> forM itemSharedMsgId $ \msgId -> Just $ case chatDir of + CIGroupRcv GroupMember {memberId} -> (msgId, memberId) + CIGroupSnd -> (msgId, membershipMemId) APIChatItemReaction (ChatRef cType chatId) itemId add reaction -> withUser $ \user -> case cType of CTDirect -> withContactLock "chatItemReaction" chatId $ - withStore (\db -> (,) <$> getContact db vr user chatId <*> getDirectChatItem db user chatId itemId) >>= \case + withFastStore (\db -> (,) <$> getContact db vr user chatId <*> getDirectChatItem db user chatId itemId) >>= \case (ct, CChatItem md ci@ChatItem {meta = CIMeta {itemSharedMsgId = Just itemSharedMId}}) -> do unless (featureAllowed SCFReactions forUser ct) $ throwChatError (CECommandError $ "feature not allowed " <> T.unpack (chatFeatureNameText CFReactions)) unless (ciReactionAllowed ci) $ throwChatError (CECommandError "reaction not allowed - chat item has no content") - rs <- withStore' $ \db -> getDirectReactions db ct itemSharedMId True + rs <- withFastStore' $ \db -> getDirectReactions db ct itemSharedMId True checkReactionAllowed rs (SndMessage {msgId}, _) <- sendDirectContactMessage user ct $ XMsgReact itemSharedMId Nothing reaction add createdAt <- liftIO getCurrentTime - reactions <- withStore' $ \db -> do + reactions <- withFastStore' $ \db -> do setDirectReaction db ct itemSharedMId True reaction add msgId createdAt liftIO $ getDirectCIReactions db ct itemSharedMId let ci' = CChatItem md ci {reactions} @@ -871,18 +929,18 @@ processChatCommand' vr = \case _ -> throwChatError $ CECommandError "reaction not possible - no shared item ID" CTGroup -> withGroupLock "chatItemReaction" chatId $ - withStore (\db -> (,) <$> getGroup db vr user chatId <*> getGroupChatItem db user chatId itemId) >>= \case + withFastStore (\db -> (,) <$> getGroup db vr user chatId <*> getGroupChatItem db user chatId itemId) >>= \case (Group g@GroupInfo {membership} ms, CChatItem md ci@ChatItem {meta = CIMeta {itemSharedMsgId = Just itemSharedMId}}) -> do unless (groupFeatureAllowed SGFReactions g) $ throwChatError (CECommandError $ "feature not allowed " <> T.unpack (chatFeatureNameText CFReactions)) unless (ciReactionAllowed ci) $ throwChatError (CECommandError "reaction not allowed - chat item has no content") let GroupMember {memberId = itemMemberId} = chatItemMember g ci - rs <- withStore' $ \db -> getGroupReactions db g membership itemMemberId itemSharedMId True + rs <- withFastStore' $ \db -> getGroupReactions db g membership itemMemberId itemSharedMId True checkReactionAllowed rs (SndMessage {msgId}, _) <- sendGroupMessage user g ms (XMsgReact itemSharedMId (Just itemMemberId) reaction add) createdAt <- liftIO getCurrentTime - reactions <- withStore' $ \db -> do + reactions <- withFastStore' $ \db -> do setGroupReaction db g membership itemMemberId itemSharedMId True reaction add msgId createdAt liftIO $ getGroupCIReactions db g itemMemberId itemSharedMId let ci' = CChatItem md ci {reactions} @@ -916,7 +974,7 @@ processChatCommand' vr = \case prepareForward :: User -> CM (ComposedMessage, Maybe CIForwardedFrom) prepareForward user = case fromCType of CTDirect -> withContactLock "forwardChatItem, from contact" fromChatId $ do - (ct, CChatItem _ ci) <- withStore $ \db -> do + (ct, CChatItem _ ci) <- withFastStore $ \db -> do ct <- getContact db vr user fromChatId cci <- getDirectChatItem db user fromChatId itemId pure (ct, cci) @@ -930,7 +988,7 @@ processChatCommand' vr = \case | localAlias /= "" = localAlias | otherwise = displayName CTGroup -> withGroupLock "forwardChatItem, from group" fromChatId $ do - (gInfo, CChatItem _ ci) <- withStore $ \db -> do + (gInfo, CChatItem _ ci) <- withFastStore $ \db -> do gInfo <- getGroupInfo db vr user fromChatId cci <- getGroupChatItem db user fromChatId itemId pure (gInfo, cci) @@ -942,7 +1000,7 @@ processChatCommand' vr = \case forwardName :: GroupInfo -> ContactName forwardName GroupInfo {groupProfile = GroupProfile {displayName}} = displayName CTLocal -> do - (CChatItem _ ci) <- withStore $ \db -> getLocalChatItem db user fromChatId itemId + (CChatItem _ ci) <- withFastStore $ \db -> getLocalChatItem db user fromChatId itemId (mc, _) <- forwardMC ci file <- forwardCryptoFile ci let ciff = forwardCIFF ci Nothing @@ -1003,56 +1061,56 @@ processChatCommand' vr = \case when (B.length ch /= chSize') $ throwError $ CF.FTCEFileIOError "encrypting file: unexpected EOF" liftIO . CF.hPut w $ LB.fromStrict ch when (size' > 0) $ copyChunks r w size' - APIUserRead userId -> withUserId userId $ \user -> withStore' (`setUserChatsRead` user) >> ok user + APIUserRead userId -> withUserId userId $ \user -> withFastStore' (`setUserChatsRead` user) >> ok user UserRead -> withUser $ \User {userId} -> processChatCommand $ APIUserRead userId APIChatRead (ChatRef cType chatId) fromToIds -> withUser $ \_ -> case cType of CTDirect -> do - user <- withStore $ \db -> getUserByContactId db chatId - timedItems <- withStore' $ \db -> getDirectUnreadTimedItems db user chatId fromToIds + user <- withFastStore $ \db -> getUserByContactId db chatId + timedItems <- withFastStore' $ \db -> getDirectUnreadTimedItems db user chatId fromToIds ts <- liftIO getCurrentTime forM_ timedItems $ \(itemId, ttl) -> do let deleteAt = addUTCTime (realToFrac ttl) ts - withStore' $ \db -> setDirectChatItemDeleteAt db user chatId itemId deleteAt + withFastStore' $ \db -> setDirectChatItemDeleteAt db user chatId itemId deleteAt startProximateTimedItemThread user (ChatRef CTDirect chatId, itemId) deleteAt - withStore' $ \db -> updateDirectChatItemsRead db user chatId fromToIds + withFastStore' $ \db -> updateDirectChatItemsRead db user chatId fromToIds ok user CTGroup -> do - user@User {userId} <- withStore $ \db -> getUserByGroupId db chatId - timedItems <- withStore' $ \db -> getGroupUnreadTimedItems db user chatId fromToIds + user@User {userId} <- withFastStore $ \db -> getUserByGroupId db chatId + timedItems <- withFastStore' $ \db -> getGroupUnreadTimedItems db user chatId fromToIds ts <- liftIO getCurrentTime forM_ timedItems $ \(itemId, ttl) -> do let deleteAt = addUTCTime (realToFrac ttl) ts - withStore' $ \db -> setGroupChatItemDeleteAt db user chatId itemId deleteAt + withFastStore' $ \db -> setGroupChatItemDeleteAt db user chatId itemId deleteAt startProximateTimedItemThread user (ChatRef CTGroup chatId, itemId) deleteAt - withStore' $ \db -> updateGroupChatItemsRead db userId chatId fromToIds + withFastStore' $ \db -> updateGroupChatItemsRead db userId chatId fromToIds ok user CTLocal -> do - user <- withStore $ \db -> getUserByNoteFolderId db chatId - withStore' $ \db -> updateLocalChatItemsRead db user chatId fromToIds + user <- withFastStore $ \db -> getUserByNoteFolderId db chatId + withFastStore' $ \db -> updateLocalChatItemsRead db user chatId fromToIds ok user CTContactRequest -> pure $ chatCmdError Nothing "not supported" CTContactConnection -> pure $ chatCmdError Nothing "not supported" APIChatUnread (ChatRef cType chatId) unreadChat -> withUser $ \user -> case cType of CTDirect -> do - withStore $ \db -> do + withFastStore $ \db -> do ct <- getContact db vr user chatId liftIO $ updateContactUnreadChat db user ct unreadChat ok user CTGroup -> do - withStore $ \db -> do + withFastStore $ \db -> do Group {groupInfo} <- getGroup db vr user chatId liftIO $ updateGroupUnreadChat db user groupInfo unreadChat ok user CTLocal -> do - withStore $ \db -> do + withFastStore $ \db -> do nf <- getNoteFolder db user chatId liftIO $ updateNoteFolderUnreadChat db user nf unreadChat ok user _ -> pure $ chatCmdError (Just user) "not supported" APIDeleteChat cRef@(ChatRef cType chatId) cdm -> withUser $ \user@User {userId} -> case cType of CTDirect -> do - ct <- withStore $ \db -> getContact db vr user chatId - filesInfo <- withStore' $ \db -> getContactFileInfo db user ct + ct <- withFastStore $ \db -> getContact db vr user chatId + filesInfo <- withFastStore' $ \db -> getContactFileInfo db user ct withContactLock "deleteChat direct" chatId . procCmd $ case cdm of CDMFull notify -> do @@ -1061,33 +1119,33 @@ processChatCommand' vr = \case sendDelDeleteConns ct notify -- functions below are called in separate transactions to prevent crashes on android -- (possibly, race condition on integrity check?) - withStore' $ \db -> do + withFastStore' $ \db -> do deleteContactConnections db user ct deleteContactFiles db user ct - withStore $ \db -> deleteContact db user ct + withFastStore $ \db -> deleteContact db user ct pure $ CRContactDeleted user ct CDMEntity notify -> do cancelFilesInProgress user filesInfo sendDelDeleteConns ct notify - ct' <- withStore $ \db -> do + ct' <- withFastStore $ \db -> do liftIO $ deleteContactConnections db user ct liftIO $ void $ updateContactStatus db user ct CSDeletedByUser getContact db vr user chatId pure $ CRContactDeleted user ct' CDMMessages -> do void $ processChatCommand $ APIClearChat cRef - withStore' $ \db -> setContactChatDeleted db user ct True + withFastStore' $ \db -> setContactChatDeleted db user ct True pure $ CRContactDeleted user ct {chatDeleted = True} where sendDelDeleteConns ct notify = do let doSendDel = contactReady ct && contactActive ct && notify when doSendDel $ void (sendDirectContactMessage user ct XDirectDel) `catchChatError` const (pure ()) - contactConnIds <- map aConnId <$> withStore' (\db -> getContactConnections db vr userId ct) + contactConnIds <- map aConnId <$> withFastStore' (\db -> getContactConnections db vr userId ct) deleteAgentConnectionsAsync' user contactConnIds doSendDel CTContactConnection -> withConnectionLock "deleteChat contactConnection" chatId . procCmd $ do - conn@PendingContactConnection {pccAgentConnId = AgentConnId acId} <- withStore $ \db -> getPendingContactConnection db userId chatId + conn@PendingContactConnection {pccAgentConnId = AgentConnId acId} <- withFastStore $ \db -> getPendingContactConnection db userId chatId deleteAgentConnectionAsync user acId - withStore' $ \db -> deletePendingContactConnection db userId chatId + withFastStore' $ \db -> deletePendingContactConnection db userId chatId pure $ CRContactConnectionDeleted user conn CTGroup -> do Group gInfo@GroupInfo {membership} members <- withStore $ \db -> getGroup db vr user chatId @@ -1133,34 +1191,34 @@ processChatCommand' vr = \case CTContactRequest -> pure $ chatCmdError (Just user) "not supported" APIClearChat (ChatRef cType chatId) -> withUser $ \user@User {userId} -> case cType of CTDirect -> do - ct <- withStore $ \db -> getContact db vr user chatId - filesInfo <- withStore' $ \db -> getContactFileInfo db user ct + ct <- withFastStore $ \db -> getContact db vr user chatId + filesInfo <- withFastStore' $ \db -> getContactFileInfo db user ct cancelFilesInProgress user filesInfo deleteFilesLocally filesInfo - withStore' $ \db -> deleteContactCIs db user ct + withFastStore' $ \db -> deleteContactCIs db user ct pure $ CRChatCleared user (AChatInfo SCTDirect $ DirectChat ct) CTGroup -> do - gInfo <- withStore $ \db -> getGroupInfo db vr user chatId - filesInfo <- withStore' $ \db -> getGroupFileInfo db user gInfo + gInfo <- withFastStore $ \db -> getGroupInfo db vr user chatId + filesInfo <- withFastStore' $ \db -> getGroupFileInfo db user gInfo cancelFilesInProgress user filesInfo deleteFilesLocally filesInfo - withStore' $ \db -> deleteGroupCIs db user gInfo - membersToDelete <- withStore' $ \db -> getGroupMembersForExpiration db vr user gInfo - forM_ membersToDelete $ \m -> withStore' $ \db -> deleteGroupMember db user m + withFastStore' $ \db -> deleteGroupChatItemsMessages db user gInfo + membersToDelete <- withFastStore' $ \db -> getGroupMembersForExpiration db vr user gInfo + forM_ membersToDelete $ \m -> withFastStore' $ \db -> deleteGroupMember db user m pure $ CRChatCleared user (AChatInfo SCTGroup $ GroupChat gInfo) CTLocal -> do - nf <- withStore $ \db -> getNoteFolder db user chatId - filesInfo <- withStore' $ \db -> getNoteFolderFileInfo db user nf + nf <- withFastStore $ \db -> getNoteFolder db user chatId + filesInfo <- withFastStore' $ \db -> getNoteFolderFileInfo db user nf deleteFilesLocally filesInfo - withStore' $ \db -> deleteNoteFolderFiles db userId nf - withStore' $ \db -> deleteNoteFolderCIs db user nf + withFastStore' $ \db -> deleteNoteFolderFiles db userId nf + withFastStore' $ \db -> deleteNoteFolderCIs db user nf pure $ CRChatCleared user (AChatInfo SCTLocal $ LocalChat nf) CTContactConnection -> pure $ chatCmdError (Just user) "not supported" CTContactRequest -> pure $ chatCmdError (Just user) "not supported" APIAcceptContact incognito connReqId -> withUser $ \_ -> do - (user@User {userId}, cReq@UserContactRequest {userContactLinkId}) <- withStore $ \db -> getContactRequest' db connReqId + (user@User {userId}, cReq@UserContactRequest {userContactLinkId}) <- withFastStore $ \db -> getContactRequest' db connReqId withUserContactLock "acceptContact" userContactLinkId $ do - ucl <- withStore $ \db -> getUserContactLinkById db userId userContactLinkId + ucl <- withFastStore $ \db -> getUserContactLinkById db userId userContactLinkId let contactUsed = (\(_, groupId_, _) -> isNothing groupId_) ucl -- [incognito] generate profile to send, create connection with incognito profile incognitoProfile <- if incognito then Just . NewIncognito <$> liftIO generateRandomProfile else pure Nothing @@ -1168,7 +1226,7 @@ processChatCommand' vr = \case pure $ CRAcceptingContactRequest user ct APIRejectContact connReqId -> withUser $ \user -> do cReq@UserContactRequest {userContactLinkId, agentContactConnId = AgentConnId connId, agentInvitationId = AgentInvId invId} <- - withStore $ \db -> + withFastStore $ \db -> getContactRequest db user connReqId `storeFinally` liftIO (deleteContactRequest db user connReqId) withUserContactLock "rejectContact" userContactLinkId $ do @@ -1176,7 +1234,7 @@ processChatCommand' vr = \case pure $ CRContactRequestRejected user cReq APISendCallInvitation contactId callType -> withUser $ \user -> do -- party initiating call - ct <- withStore $ \db -> getContact db vr user contactId + ct <- withFastStore $ \db -> getContact db vr user contactId assertDirectAllowed user MDSnd ct XCallInv_ if featureAllowed SCFCalls forUser ct then do @@ -1196,14 +1254,14 @@ processChatCommand' vr = \case ok user else pure $ chatCmdError (Just user) ("feature not allowed " <> T.unpack (chatFeatureNameText CFCalls)) SendCallInvitation cName callType -> withUser $ \user -> do - contactId <- withStore $ \db -> getContactIdByName db user cName + contactId <- withFastStore $ \db -> getContactIdByName db user cName processChatCommand $ APISendCallInvitation contactId callType APIRejectCall contactId -> -- party accepting call withCurrentCall contactId $ \user ct Call {chatItemId, callState} -> case callState of CallInvitationReceived {} -> do let aciContent = ACIContent SMDRcv $ CIRcvCall CISCallRejected 0 - withStore' $ \db -> updateDirectChatItemsRead db user contactId $ Just (chatItemId, chatItemId) + withFastStore' $ \db -> updateDirectChatItemsRead db user contactId $ Just (chatItemId, chatItemId) timed_ <- contactCITimed ct updateDirectChatItemView user ct chatItemId aciContent False False timed_ Nothing forM_ (timed_ >>= timedDeleteAt') $ @@ -1219,7 +1277,7 @@ processChatCommand' vr = \case callState' = CallOfferSent {localCallType = callType, peerCallType, localCallSession = rtcSession, sharedKey} aciContent = ACIContent SMDRcv $ CIRcvCall CISCallAccepted 0 (SndMessage {msgId}, _) <- sendDirectContactMessage user ct (XCallOffer callId offer) - withStore' $ \db -> updateDirectChatItemsRead db user contactId $ Just (chatItemId, chatItemId) + withFastStore' $ \db -> updateDirectChatItemsRead db user contactId $ Just (chatItemId, chatItemId) updateDirectChatItemView user ct chatItemId aciContent False False Nothing $ Just msgId pure $ Just call {callState = callState'} _ -> throwChatError . CECallState $ callStateTag callState @@ -1253,7 +1311,7 @@ processChatCommand' vr = \case (SndMessage {msgId}, _) <- sendDirectContactMessage user ct (XCallEnd callId) updateCallItemStatus user ct call WCSDisconnected $ Just msgId pure Nothing - APIGetCallInvitations -> withUser $ \_ -> lift $ do + APIGetCallInvitations -> withUser' $ \_ -> lift $ do calls <- asks currentCalls >>= readTVarIO let invs = mapMaybe callInvitation $ M.elems calls rcvCallInvitations <- rights <$> mapM rcvCallInvitation invs @@ -1262,7 +1320,7 @@ processChatCommand' vr = \case callInvitation Call {contactId, callState, callTs} = case callState of CallInvitationReceived {peerCallType, sharedKey} -> Just (contactId, callTs, peerCallType, sharedKey) _ -> Nothing - rcvCallInvitation (contactId, callTs, peerCallType, sharedKey) = runExceptT . withStore $ \db -> do + rcvCallInvitation (contactId, callTs, peerCallType, sharedKey) = runExceptT . withFastStore $ \db -> do user <- getUserByContactId db contactId contact <- getContact db vr user contactId pure RcvCallInvitation {user, contact, callType = peerCallType, sharedKey, callTs} @@ -1273,20 +1331,20 @@ processChatCommand' vr = \case updateCallItemStatus user ct call receivedStatus Nothing $> Just call APIUpdateProfile userId profile -> withUserId userId (`updateProfile` profile) APISetContactPrefs contactId prefs' -> withUser $ \user -> do - ct <- withStore $ \db -> getContact db vr user contactId + ct <- withFastStore $ \db -> getContact db vr user contactId updateContactPrefs user ct prefs' APISetContactAlias contactId localAlias -> withUser $ \user@User {userId} -> do - ct' <- withStore $ \db -> do + ct' <- withFastStore $ \db -> do ct <- getContact db vr user contactId liftIO $ updateContactAlias db userId ct localAlias pure $ CRContactAliasUpdated user ct' APISetConnectionAlias connId localAlias -> withUser $ \user@User {userId} -> do - conn' <- withStore $ \db -> do + conn' <- withFastStore $ \db -> do conn <- getPendingContactConnection db userId connId liftIO $ updateContactConnectionAlias db userId conn localAlias pure $ CRConnectionAliasUpdated user conn' APISetUserUIThemes uId uiThemes -> withUser $ \user@User {userId} -> do - user'@User {userId = uId'} <- withStore $ \db -> do + user'@User {userId = uId'} <- withFastStore $ \db -> do user' <- getUser db uId liftIO $ setUserUIThemes db user uiThemes pure user' @@ -1294,18 +1352,18 @@ processChatCommand' vr = \case ok user' APISetChatUIThemes (ChatRef cType chatId) uiThemes -> withUser $ \user -> case cType of CTDirect -> do - withStore $ \db -> do + withFastStore $ \db -> do ct <- getContact db vr user chatId liftIO $ setContactUIThemes db user ct uiThemes ok user CTGroup -> do - withStore $ \db -> do + withFastStore $ \db -> do g <- getGroupInfo db vr user chatId liftIO $ setGroupUIThemes db user g uiThemes ok user _ -> pure $ chatCmdError (Just user) "not supported" APIParseMarkdown text -> pure . CRApiParsedMarkdown $ parseMaybeMarkdownList text - APIGetNtfToken -> withUser $ \_ -> crNtfToken <$> withAgent getNtfToken + APIGetNtfToken -> withUser' $ \_ -> crNtfToken <$> withAgent getNtfToken APIRegisterToken token mode -> withUser $ \_ -> CRNtfTokenStatus <$> withAgent (\a -> registerNtfToken a token mode) APIVerifyToken token nonce code -> withUser $ \_ -> withAgent (\a -> verifyNtfToken a token nonce code) >> ok_ @@ -1321,13 +1379,13 @@ processChatCommand' vr = \case pure CRNtfMessages {user_, connEntity_, msgTs = msgTs', ntfMessage_ = ntfMsgInfo <$> msg} APIGetUserProtoServers userId (AProtocolType p) -> withUserId userId $ \user -> withServerProtocol p $ do cfg@ChatConfig {defaultServers} <- asks config - servers <- withStore' (`getProtocolServers` user) + servers <- withFastStore' (`getProtocolServers` user) pure $ CRUserProtoServers user $ AUPS $ UserProtoServers p (useServers cfg p servers) (cfgServers p defaultServers) GetUserProtoServers aProtocol -> withUser $ \User {userId} -> processChatCommand $ APIGetUserProtoServers userId aProtocol APISetUserProtoServers userId (APSC p (ProtoServersConfig servers)) | null servers || any (\ServerCfg {enabled} -> enabled) servers -> withUserId userId $ \user -> withServerProtocol p $ do - withStore $ \db -> overwriteProtocolServers db user servers + withFastStore $ \db -> overwriteProtocolServers db user servers cfg <- asks config lift $ withAgent' $ \a -> setProtocolServers a (aUserId user) $ useServers cfg p servers ok user @@ -1343,21 +1401,21 @@ processChatCommand' vr = \case withChatLock "setChatItemTTL" $ do case newTTL_ of Nothing -> do - withStore' $ \db -> setChatItemTTL db user newTTL_ + withFastStore' $ \db -> setChatItemTTL db user newTTL_ lift $ setExpireCIFlag user False Just newTTL -> do - oldTTL <- withStore' (`getChatItemTTL` user) + oldTTL <- withFastStore' (`getChatItemTTL` user) when (maybe True (newTTL <) oldTTL) $ do lift $ setExpireCIFlag user False expireChatItems user newTTL True - withStore' $ \db -> setChatItemTTL db user newTTL_ + withFastStore' $ \db -> setChatItemTTL db user newTTL_ lift $ startExpireCIThread user lift . whenM chatStarted $ setExpireCIFlag user True ok user SetChatItemTTL newTTL_ -> withUser' $ \User {userId} -> do processChatCommand $ APISetChatItemTTL userId newTTL_ APIGetChatItemTTL userId -> withUserId' userId $ \user -> do - ttl <- withStore' (`getChatItemTTL` user) + ttl <- withFastStore' (`getChatItemTTL` user) pure $ CRChatItemTTL user ttl GetChatItemTTL -> withUser' $ \User {userId} -> do processChatCommand $ APIGetChatItemTTL userId @@ -1375,7 +1433,7 @@ processChatCommand' vr = \case ok_ APISetChatSettings (ChatRef cType chatId) chatSettings -> withUser $ \user -> case cType of CTDirect -> do - ct <- withStore $ \db -> do + ct <- withFastStore $ \db -> do ct <- getContact db vr user chatId liftIO $ updateContactSettings db user chatId chatSettings pure ct @@ -1383,7 +1441,7 @@ processChatCommand' vr = \case withAgent $ \a -> toggleConnectionNtfs a connId (chatHasNtfs chatSettings) ok user CTGroup -> do - ms <- withStore $ \db -> do + ms <- withFastStore $ \db -> do Group _ ms <- getGroup db vr user chatId liftIO $ updateGroupSettings db user chatId chatSettings pure ms @@ -1392,7 +1450,7 @@ processChatCommand' vr = \case ok user _ -> pure $ chatCmdError (Just user) "not supported" APISetMemberSettings gId gMemberId settings -> withUser $ \user -> do - m <- withStore $ \db -> do + m <- withFastStore $ \db -> do liftIO $ updateGroupMemberSettings db user gId gMemberId settings getGroupMember db vr user gId gMemberId let ntfOn = showMessages $ memberSettings m @@ -1400,60 +1458,60 @@ processChatCommand' vr = \case ok user APIContactInfo contactId -> withUser $ \user@User {userId} -> do -- [incognito] print user's incognito profile for this contact - ct@Contact {activeConn} <- withStore $ \db -> getContact db vr user contactId + ct@Contact {activeConn} <- withFastStore $ \db -> getContact db vr user contactId incognitoProfile <- case activeConn of Nothing -> pure Nothing Just Connection {customUserProfileId} -> - forM customUserProfileId $ \profileId -> withStore (\db -> getProfileById db userId profileId) + forM customUserProfileId $ \profileId -> withFastStore (\db -> getProfileById db userId profileId) connectionStats <- mapM (withAgent . flip getConnectionServers) (contactConnId ct) pure $ CRContactInfo user ct connectionStats (fmap fromLocalProfile incognitoProfile) APIContactQueueInfo contactId -> withUser $ \user -> do - ct@Contact {activeConn} <- withStore $ \db -> getContact db vr user contactId + ct@Contact {activeConn} <- withFastStore $ \db -> getContact db vr user contactId case activeConn of Just conn -> getConnQueueInfo user conn Nothing -> throwChatError $ CEContactNotActive ct APIGroupInfo gId -> withUser $ \user -> do - (g, s) <- withStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> liftIO (getGroupSummary db user gId) + (g, s) <- withFastStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> liftIO (getGroupSummary db user gId) pure $ CRGroupInfo user g s APIGroupMemberInfo gId gMemberId -> withUser $ \user -> do - (g, m) <- withStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> getGroupMember db vr user gId gMemberId + (g, m) <- withFastStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> getGroupMember db vr user gId gMemberId connectionStats <- mapM (withAgent . flip getConnectionServers) (memberConnId m) pure $ CRGroupMemberInfo user g m connectionStats APIGroupMemberQueueInfo gId gMemberId -> withUser $ \user -> do - GroupMember {activeConn} <- withStore $ \db -> getGroupMember db vr user gId gMemberId + GroupMember {activeConn} <- withFastStore $ \db -> getGroupMember db vr user gId gMemberId case activeConn of Just conn -> getConnQueueInfo user conn Nothing -> throwChatError CEGroupMemberNotActive APISwitchContact contactId -> withUser $ \user -> do - ct <- withStore $ \db -> getContact db vr user contactId + ct <- withFastStore $ \db -> getContact db vr user contactId case contactConnId ct of Just connId -> do connectionStats <- withAgent $ \a -> switchConnectionAsync a "" connId pure $ CRContactSwitchStarted user ct connectionStats Nothing -> throwChatError $ CEContactNotActive ct APISwitchGroupMember gId gMemberId -> withUser $ \user -> do - (g, m) <- withStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> getGroupMember db vr user gId gMemberId + (g, m) <- withFastStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> getGroupMember db vr user gId gMemberId case memberConnId m of Just connId -> do connectionStats <- withAgent (\a -> switchConnectionAsync a "" connId) pure $ CRGroupMemberSwitchStarted user g m connectionStats _ -> throwChatError CEGroupMemberNotActive APIAbortSwitchContact contactId -> withUser $ \user -> do - ct <- withStore $ \db -> getContact db vr user contactId + ct <- withFastStore $ \db -> getContact db vr user contactId case contactConnId ct of Just connId -> do connectionStats <- withAgent $ \a -> abortConnectionSwitch a connId pure $ CRContactSwitchAborted user ct connectionStats Nothing -> throwChatError $ CEContactNotActive ct APIAbortSwitchGroupMember gId gMemberId -> withUser $ \user -> do - (g, m) <- withStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> getGroupMember db vr user gId gMemberId + (g, m) <- withFastStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> getGroupMember db vr user gId gMemberId case memberConnId m of Just connId -> do connectionStats <- withAgent $ \a -> abortConnectionSwitch a connId pure $ CRGroupMemberSwitchAborted user g m connectionStats _ -> throwChatError CEGroupMemberNotActive APISyncContactRatchet contactId force -> withUser $ \user -> withContactLock "syncContactRatchet" contactId $ do - ct <- withStore $ \db -> getContact db vr user contactId + ct <- withFastStore $ \db -> getContact db vr user contactId case contactConn ct of Just conn@Connection {pqSupport} -> do cStats@ConnectionStats {ratchetSyncState = rss} <- withAgent $ \a -> synchronizeRatchet a (aConnId conn) pqSupport force @@ -1461,7 +1519,7 @@ processChatCommand' vr = \case pure $ CRContactRatchetSyncStarted user ct cStats Nothing -> throwChatError $ CEContactNotActive ct APISyncGroupMemberRatchet gId gMemberId force -> withUser $ \user -> withGroupLock "syncGroupMemberRatchet" gId $ do - (g, m) <- withStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> getGroupMember db vr user gId gMemberId + (g, m) <- withFastStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> getGroupMember db vr user gId gMemberId case memberConnId m of Just connId -> do cStats@ConnectionStats {ratchetSyncState = rss} <- withAgent $ \a -> synchronizeRatchet a connId PQSupportOff force @@ -1469,7 +1527,7 @@ processChatCommand' vr = \case pure $ CRGroupMemberRatchetSyncStarted user g m cStats _ -> throwChatError CEGroupMemberNotActive APIGetContactCode contactId -> withUser $ \user -> do - ct@Contact {activeConn} <- withStore $ \db -> getContact db vr user contactId + ct@Contact {activeConn} <- withFastStore $ \db -> getContact db vr user contactId case activeConn of Just conn@Connection {connId} -> do code <- getConnectionCode $ aConnId conn @@ -1477,13 +1535,13 @@ processChatCommand' vr = \case Just SecurityCode {securityCode} | sameVerificationCode code securityCode -> pure ct | otherwise -> do - withStore' $ \db -> setConnectionVerified db user connId Nothing + withFastStore' $ \db -> setConnectionVerified db user connId Nothing pure (ct :: Contact) {activeConn = Just $ (conn :: Connection) {connectionCode = Nothing}} _ -> pure ct pure $ CRContactCode user ct' code Nothing -> throwChatError $ CEContactNotActive ct APIGetGroupMemberCode gId gMemberId -> withUser $ \user -> do - (g, m@GroupMember {activeConn}) <- withStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> getGroupMember db vr user gId gMemberId + (g, m@GroupMember {activeConn}) <- withFastStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> getGroupMember db vr user gId gMemberId case activeConn of Just conn@Connection {connId} -> do code <- getConnectionCode $ aConnId conn @@ -1491,48 +1549,48 @@ processChatCommand' vr = \case Just SecurityCode {securityCode} | sameVerificationCode code securityCode -> pure m | otherwise -> do - withStore' $ \db -> setConnectionVerified db user connId Nothing + withFastStore' $ \db -> setConnectionVerified db user connId Nothing pure (m :: GroupMember) {activeConn = Just $ (conn :: Connection) {connectionCode = Nothing}} _ -> pure m pure $ CRGroupMemberCode user g m' code _ -> throwChatError CEGroupMemberNotActive APIVerifyContact contactId code -> withUser $ \user -> do - ct@Contact {activeConn} <- withStore $ \db -> getContact db vr user contactId + ct@Contact {activeConn} <- withFastStore $ \db -> getContact db vr user contactId case activeConn of Just conn -> verifyConnectionCode user conn code Nothing -> throwChatError $ CEContactNotActive ct APIVerifyGroupMember gId gMemberId code -> withUser $ \user -> do - GroupMember {activeConn} <- withStore $ \db -> getGroupMember db vr user gId gMemberId + GroupMember {activeConn} <- withFastStore $ \db -> getGroupMember db vr user gId gMemberId case activeConn of Just conn -> verifyConnectionCode user conn code _ -> throwChatError CEGroupMemberNotActive APIEnableContact contactId -> withUser $ \user -> do - ct@Contact {activeConn} <- withStore $ \db -> getContact db vr user contactId + ct@Contact {activeConn} <- withFastStore $ \db -> getContact db vr user contactId case activeConn of Just conn -> do - withStore' $ \db -> setAuthErrCounter db user conn 0 + withFastStore' $ \db -> setAuthErrCounter db user conn 0 ok user Nothing -> throwChatError $ CEContactNotActive ct APIEnableGroupMember gId gMemberId -> withUser $ \user -> do - GroupMember {activeConn} <- withStore $ \db -> getGroupMember db vr user gId gMemberId + GroupMember {activeConn} <- withFastStore $ \db -> getGroupMember db vr user gId gMemberId case activeConn of Just conn -> do - withStore' $ \db -> setAuthErrCounter db user conn 0 + withFastStore' $ \db -> setAuthErrCounter db user conn 0 ok user _ -> throwChatError CEGroupMemberNotActive SetShowMessages cName ntfOn -> updateChatSettings cName (\cs -> cs {enableNtfs = ntfOn}) SetSendReceipts cName rcptsOn_ -> updateChatSettings cName (\cs -> cs {sendRcpts = rcptsOn_}) SetShowMemberMessages gName mName showMessages -> withUser $ \user -> do (gId, mId) <- getGroupAndMemberId user gName mName - gInfo <- withStore $ \db -> getGroupInfo db vr user gId - m <- withStore $ \db -> getGroupMember db vr user gId mId + gInfo <- withFastStore $ \db -> getGroupInfo db vr user gId + m <- withFastStore $ \db -> getGroupMember db vr user gId mId let GroupInfo {membership = GroupMember {memberRole = membershipRole}} = gInfo when (membershipRole >= GRAdmin) $ throwChatError $ CECantBlockMemberForSelf gInfo m showMessages let settings = (memberSettings m) {showMessages} processChatCommand $ APISetMemberSettings gId mId settings ContactInfo cName -> withContactName cName APIContactInfo ShowGroupInfo gName -> withUser $ \user -> do - groupId <- withStore $ \db -> getGroupIdByName db user gName + groupId <- withFastStore $ \db -> getGroupIdByName db user gName processChatCommand $ APIGroupInfo groupId GroupMemberInfo gName mName -> withMemberName gName mName APIGroupMemberInfo ContactQueueInfo cName -> withContactName cName APIContactQueueInfo @@ -1557,12 +1615,12 @@ processChatCommand' vr = \case subMode <- chatReadVar subscriptionMode (connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing IKPQOn subMode -- TODO PQ pass minVersion from the current range - conn <- withStore' $ \db -> createDirectConnection db user connId cReq ConnNew incognitoProfile subMode initialChatVersion PQSupportOn + conn <- withFastStore' $ \db -> createDirectConnection db user connId cReq ConnNew incognitoProfile subMode initialChatVersion PQSupportOn pure $ CRInvitation user cReq conn AddContact incognito -> withUser $ \User {userId} -> processChatCommand $ APIAddContact userId incognito APISetConnectionIncognito connId incognito -> withUser $ \user@User {userId} -> do - conn'_ <- withStore $ \db -> do + conn'_ <- withFastStore $ \db -> do conn@PendingContactConnection {pccConnStatus, customUserProfileId} <- getPendingContactConnection db userId connId case (pccConnStatus, customUserProfileId, incognito) of (ConnNew, Nothing, True) -> liftIO $ do @@ -1590,7 +1648,7 @@ processChatCommand' vr = \case let chatV = agentToChatVersion agentV dm <- encodeConnInfoPQ pqSup' chatV $ XInfo profileToSend connId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq pqSup' - conn@PendingContactConnection {pccConnId} <- withStore' $ \db -> createDirectConnection db user connId cReq ConnJoined (incognitoProfile $> profileToSend) subMode chatV pqSup' + conn@PendingContactConnection {pccConnId} <- withFastStore' $ \db -> createDirectConnection db user connId cReq ConnJoined (incognitoProfile $> profileToSend) subMode chatV pqSup' joinPreparedAgentConnection user pccConnId connId cReq dm pqSup' subMode pure $ CRSentConfirmation user conn APIConnect userId incognito (Just (ACR SCMContact cReq)) -> withUserId userId $ \user -> connectViaContact user incognito cReq @@ -1604,7 +1662,7 @@ processChatCommand' vr = \case _ -> processChatCommand $ APIConnect userId incognito aCReqUri Connect _ Nothing -> throwChatError CEInvalidConnReq APIConnectContactViaAddress userId incognito contactId -> withUserId userId $ \user -> do - ct@Contact {activeConn, profile = LocalProfile {contactLink}} <- withStore $ \db -> getContact db vr user contactId + ct@Contact {activeConn, profile = LocalProfile {contactLink}} <- withFastStore $ \db -> getContact db vr user contactId when (isJust activeConn) $ throwChatError (CECommandError "contact already has connection") case contactLink of Just cReq -> connectContactViaAddress user incognito ct cReq @@ -1620,23 +1678,23 @@ processChatCommand' vr = \case DeleteContact cName cdm -> withContactName cName $ \ctId -> APIDeleteChat (ChatRef CTDirect ctId) cdm ClearContact cName -> withContactName cName $ APIClearChat . ChatRef CTDirect APIListContacts userId -> withUserId userId $ \user -> - CRContactsList user <$> withStore' (\db -> getUserContacts db vr user) + CRContactsList user <$> withFastStore' (\db -> getUserContacts db vr user) ListContacts -> withUser $ \User {userId} -> processChatCommand $ APIListContacts userId APICreateMyAddress userId -> withUserId userId $ \user -> procCmd $ do subMode <- chatReadVar subscriptionMode (connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact Nothing IKPQOn subMode - withStore $ \db -> createUserContactLink db user connId cReq subMode + withFastStore $ \db -> createUserContactLink db user connId cReq subMode pure $ CRUserContactLinkCreated user cReq CreateMyAddress -> withUser $ \User {userId} -> processChatCommand $ APICreateMyAddress userId APIDeleteMyAddress userId -> withUserId userId $ \user@User {profile = p} -> do - conns <- withStore $ \db -> getUserAddressConnections db vr user + conns <- withFastStore $ \db -> getUserAddressConnections db vr user withChatLock "deleteMyAddress" $ do deleteAgentConnectionsAsync user $ map aConnId conns - withStore' (`deleteUserAddress` user) + withFastStore' (`deleteUserAddress` user) let p' = (fromLocalProfile p :: Profile) {contactLink = Nothing} - r <- updateProfile_ user p' $ withStore' $ \db -> setUserProfileContactLink db user Nothing + r <- updateProfile_ user p' $ withFastStore' $ \db -> setUserProfileContactLink db user Nothing let user' = case r of CRUserProfileUpdated u' _ _ _ -> u' _ -> user @@ -1644,54 +1702,54 @@ processChatCommand' vr = \case DeleteMyAddress -> withUser $ \User {userId} -> processChatCommand $ APIDeleteMyAddress userId APIShowMyAddress userId -> withUserId' userId $ \user -> - CRUserContactLink user <$> withStore (`getUserAddress` user) + CRUserContactLink user <$> withFastStore (`getUserAddress` user) ShowMyAddress -> withUser' $ \User {userId} -> processChatCommand $ APIShowMyAddress userId APISetProfileAddress userId False -> withUserId userId $ \user@User {profile = p} -> do let p' = (fromLocalProfile p :: Profile) {contactLink = Nothing} - updateProfile_ user p' $ withStore' $ \db -> setUserProfileContactLink db user Nothing + updateProfile_ user p' $ withFastStore' $ \db -> setUserProfileContactLink db user Nothing APISetProfileAddress userId True -> withUserId userId $ \user@User {profile = p} -> do - ucl@UserContactLink {connReqContact} <- withStore (`getUserAddress` user) + ucl@UserContactLink {connReqContact} <- withFastStore (`getUserAddress` user) let p' = (fromLocalProfile p :: Profile) {contactLink = Just connReqContact} - updateProfile_ user p' $ withStore' $ \db -> setUserProfileContactLink db user $ Just ucl + updateProfile_ user p' $ withFastStore' $ \db -> setUserProfileContactLink db user $ Just ucl SetProfileAddress onOff -> withUser $ \User {userId} -> processChatCommand $ APISetProfileAddress userId onOff APIAddressAutoAccept userId autoAccept_ -> withUserId userId $ \user -> do - contactLink <- withStore (\db -> updateUserAddressAutoAccept db user autoAccept_) + contactLink <- withFastStore (\db -> updateUserAddressAutoAccept db user autoAccept_) pure $ CRUserContactLinkUpdated user contactLink AddressAutoAccept autoAccept_ -> withUser $ \User {userId} -> processChatCommand $ APIAddressAutoAccept userId autoAccept_ AcceptContact incognito cName -> withUser $ \User {userId} -> do - connReqId <- withStore $ \db -> getContactRequestIdByName db userId cName + connReqId <- withFastStore $ \db -> getContactRequestIdByName db userId cName processChatCommand $ APIAcceptContact incognito connReqId RejectContact cName -> withUser $ \User {userId} -> do - connReqId <- withStore $ \db -> getContactRequestIdByName db userId cName + connReqId <- withFastStore $ \db -> getContactRequestIdByName db userId cName processChatCommand $ APIRejectContact connReqId ForwardMessage toChatName fromContactName forwardedMsg -> withUser $ \user -> do - contactId <- withStore $ \db -> getContactIdByName db user fromContactName - forwardedItemId <- withStore $ \db -> getDirectChatItemIdByText' db user contactId forwardedMsg + contactId <- withFastStore $ \db -> getContactIdByName db user fromContactName + forwardedItemId <- withFastStore $ \db -> getDirectChatItemIdByText' db user contactId forwardedMsg toChatRef <- getChatRef user toChatName processChatCommand $ APIForwardChatItem toChatRef (ChatRef CTDirect contactId) forwardedItemId Nothing ForwardGroupMessage toChatName fromGroupName fromMemberName_ forwardedMsg -> withUser $ \user -> do - groupId <- withStore $ \db -> getGroupIdByName db user fromGroupName - forwardedItemId <- withStore $ \db -> getGroupChatItemIdByText db user groupId fromMemberName_ forwardedMsg + groupId <- withFastStore $ \db -> getGroupIdByName db user fromGroupName + forwardedItemId <- withFastStore $ \db -> getGroupChatItemIdByText db user groupId fromMemberName_ forwardedMsg toChatRef <- getChatRef user toChatName processChatCommand $ APIForwardChatItem toChatRef (ChatRef CTGroup groupId) forwardedItemId Nothing ForwardLocalMessage toChatName forwardedMsg -> withUser $ \user -> do - folderId <- withStore (`getUserNoteFolderId` user) - forwardedItemId <- withStore $ \db -> getLocalChatItemIdByText' db user folderId forwardedMsg + folderId <- withFastStore (`getUserNoteFolderId` user) + forwardedItemId <- withFastStore $ \db -> getLocalChatItemIdByText' db user folderId forwardedMsg toChatRef <- getChatRef user toChatName processChatCommand $ APIForwardChatItem toChatRef (ChatRef CTLocal folderId) forwardedItemId Nothing SendMessage (ChatName cType name) msg -> withUser $ \user -> do let mc = MCText msg case cType of CTDirect -> - withStore' (\db -> runExceptT $ getContactIdByName db user name) >>= \case + withFastStore' (\db -> runExceptT $ getContactIdByName db user name) >>= \case Right ctId -> do let chatRef = ChatRef CTDirect ctId processChatCommand . APISendMessage chatRef False Nothing $ ComposedMessage Nothing Nothing mc Left _ -> - withStore' (\db -> runExceptT $ getActiveMembersByName db vr user name) >>= \case + withFastStore' (\db -> runExceptT $ getActiveMembersByName db vr user name) >>= \case Right [(gInfo, member)] -> do let GroupInfo {localDisplayName = gName} = gInfo GroupMember {localDisplayName = mName} = member @@ -1701,22 +1759,22 @@ processChatCommand' vr = \case _ -> throwChatError $ CEContactNotFound name Nothing CTGroup -> do - gId <- withStore $ \db -> getGroupIdByName db user name + gId <- withFastStore $ \db -> getGroupIdByName db user name let chatRef = ChatRef CTGroup gId processChatCommand . APISendMessage chatRef False Nothing $ ComposedMessage Nothing Nothing mc CTLocal | name == "" -> do - folderId <- withStore (`getUserNoteFolderId` user) + folderId <- withFastStore (`getUserNoteFolderId` user) processChatCommand . APICreateChatItem folderId $ ComposedMessage Nothing Nothing mc | otherwise -> throwChatError $ CECommandError "not supported" _ -> throwChatError $ CECommandError "not supported" SendMemberContactMessage gName mName msg -> withUser $ \user -> do (gId, mId) <- getGroupAndMemberId user gName mName - m <- withStore $ \db -> getGroupMember db vr user gId mId + m <- withFastStore $ \db -> getGroupMember db vr user gId mId let mc = MCText msg case memberContactId m of Nothing -> do - g <- withStore $ \db -> getGroupInfo db vr user gId + g <- withFastStore $ \db -> getGroupInfo db vr user gId unless (groupFeatureMemberAllowed SGFDirectMessages (membership g) g) $ throwChatError $ CECommandError "direct messages not allowed" toView $ CRNoMemberContactCreating user g m processChatCommand (APICreateMemberContact gId mId) >>= \case @@ -1732,7 +1790,7 @@ processChatCommand' vr = \case let mc = MCText msg processChatCommand . APISendMessage chatRef True Nothing $ ComposedMessage Nothing Nothing mc SendMessageBroadcast msg -> withUser $ \user -> do - contacts <- withStore' $ \db -> getUserContacts db vr user + contacts <- withFastStore' $ \db -> getUserContacts db vr user withChatLock "sendMessageBroadcast" . procCmd $ do let ctConns_ = L.nonEmpty $ foldr addContactConn [] contacts case ctConns_ of @@ -1742,7 +1800,7 @@ processChatCommand' vr = \case Just (ctConns :: NonEmpty (Contact, Connection)) -> do let idsEvts = L.map ctSndEvent ctConns sndMsgs <- lift $ createSndMessages idsEvts - let msgReqs_ :: NonEmpty (Either ChatError MsgReq) = L.zipWith (fmap . ctMsgReq) ctConns sndMsgs + let msgReqs_ :: NonEmpty (Either ChatError ChatMsgReq) = L.zipWith (fmap . ctMsgReq) ctConns sndMsgs (errs, ctSndMsgs :: [(Contact, SndMessage)]) <- partitionEithers . L.toList . zipWith3' combineResults ctConns sndMsgs <$> deliverMessagesB msgReqs_ timestamp <- liftIO getCurrentTime @@ -1756,11 +1814,11 @@ processChatCommand' vr = \case _ -> ctConns ctSndEvent :: (Contact, Connection) -> (ConnOrGroupId, ChatMsgEvent 'Json) ctSndEvent (_, Connection {connId}) = (ConnectionId connId, XMsgNew $ MCSimple (extMsgContent mc Nothing)) - ctMsgReq :: (Contact, Connection) -> SndMessage -> MsgReq - ctMsgReq (_, conn) SndMessage {msgId, msgBody} = (conn, MsgFlags {notification = hasNotification XMsgNew_}, msgBody, msgId) + ctMsgReq :: (Contact, Connection) -> SndMessage -> ChatMsgReq + ctMsgReq (_, conn) SndMessage {msgId, msgBody} = (conn, MsgFlags {notification = hasNotification XMsgNew_}, msgBody, [msgId]) zipWith3' :: (a -> b -> c -> d) -> NonEmpty a -> NonEmpty b -> NonEmpty c -> NonEmpty d zipWith3' f ~(x :| xs) ~(y :| ys) ~(z :| zs) = f x y z :| zipWith3 f xs ys zs - combineResults :: (Contact, Connection) -> Either ChatError SndMessage -> Either ChatError (Int64, PQEncryption) -> Either ChatError (Contact, SndMessage) + combineResults :: (Contact, Connection) -> Either ChatError SndMessage -> Either ChatError ([Int64], PQEncryption) -> Either ChatError (Contact, SndMessage) combineResults (ct, _) (Right msg') (Right _) = Right (ct, msg') combineResults _ (Left e) _ = Left e combineResults _ _ (Left e) = Left e @@ -1768,18 +1826,18 @@ processChatCommand' vr = \case createCI db user createdAt (ct, sndMsg) = void $ createNewSndChatItem db user (CDDirectSnd ct) sndMsg (CISndMsgContent mc) Nothing Nothing Nothing False createdAt SendMessageQuote cName (AMsgDirection msgDir) quotedMsg msg -> withUser $ \user@User {userId} -> do - contactId <- withStore $ \db -> getContactIdByName db user cName - quotedItemId <- withStore $ \db -> getDirectChatItemIdByText db userId contactId msgDir quotedMsg + contactId <- withFastStore $ \db -> getContactIdByName db user cName + quotedItemId <- withFastStore $ \db -> getDirectChatItemIdByText db userId contactId msgDir quotedMsg let mc = MCText msg processChatCommand . APISendMessage (ChatRef CTDirect contactId) False Nothing $ ComposedMessage Nothing (Just quotedItemId) mc DeleteMessage chatName deletedMsg -> withUser $ \user -> do chatRef <- getChatRef user chatName deletedItemId <- getSentChatItemIdByText user chatRef deletedMsg - processChatCommand $ APIDeleteChatItem chatRef deletedItemId CIDMBroadcast + processChatCommand $ APIDeleteChatItem chatRef (deletedItemId :| []) CIDMBroadcast DeleteMemberMessage gName mName deletedMsg -> withUser $ \user -> do - (gId, mId) <- getGroupAndMemberId user gName mName - deletedItemId <- withStore $ \db -> getGroupChatItemIdByText db user gId (Just mName) deletedMsg - processChatCommand $ APIDeleteMemberChatItem gId mId deletedItemId + gId <- withFastStore $ \db -> getGroupIdByName db user gName + deletedItemId <- withFastStore $ \db -> getGroupChatItemIdByText db user gId (Just mName) deletedMsg + processChatCommand $ APIDeleteMemberChatItem gId (deletedItemId :| []) EditMessage chatName editedMsg msg -> withUser $ \user -> do chatRef <- getChatRef user chatName editedItemId <- getSentChatItemIdByText user chatRef editedMsg @@ -1798,14 +1856,14 @@ processChatCommand' vr = \case gVar <- asks random -- [incognito] generate incognito profile for group membership incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing - groupInfo <- withStore $ \db -> createNewGroup db vr gVar user gProfile incognitoProfile + groupInfo <- withFastStore $ \db -> createNewGroup db vr gVar user gProfile incognitoProfile createInternalChatItem user (CDGroupSnd groupInfo) (CISndGroupE2EEInfo $ E2EInfo {pqEnabled = PQEncOff}) Nothing pure $ CRGroupCreated user groupInfo NewGroup incognito gProfile -> withUser $ \User {userId} -> processChatCommand $ APINewGroup userId incognito gProfile APIAddMember groupId contactId memRole -> withUser $ \user -> withGroupLock "addMember" groupId $ do -- TODO for large groups: no need to load all members to determine if contact is a member - (group, contact) <- withStore $ \db -> (,) <$> getGroup db vr user groupId <*> getContact db vr user contactId + (group, contact) <- withFastStore $ \db -> (,) <$> getGroup db vr user groupId <*> getContact db vr user contactId assertDirectAllowed user MDSnd contact XGrpInv_ let Group gInfo members = group Contact {localDisplayName = cName} = contact @@ -1820,13 +1878,13 @@ processChatCommand' vr = \case gVar <- asks random subMode <- chatReadVar subscriptionMode (agentConnId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing IKPQOff subMode - member <- withStore $ \db -> createNewContactMember db gVar user gInfo contact memRole agentConnId cReq subMode + member <- withFastStore $ \db -> createNewContactMember db gVar user gInfo contact memRole agentConnId cReq subMode sendInvitation member cReq pure $ CRSentGroupInvitation user gInfo contact member Just member@GroupMember {groupMemberId, memberStatus, memberRole = mRole} | memberStatus == GSMemInvited -> do - unless (mRole == memRole) $ withStore' $ \db -> updateGroupMemberRole db user member memRole - withStore' (\db -> getMemberInvitation db user groupMemberId) >>= \case + unless (mRole == memRole) $ withFastStore' $ \db -> updateGroupMemberRole db user member memRole + withFastStore' (\db -> getMemberInvitation db user groupMemberId) >>= \case Just cReq -> do sendInvitation member {memberRole = memRole} cReq pure $ CRSentGroupInvitation user gInfo contact member {memberRole = memRole} @@ -1834,7 +1892,7 @@ processChatCommand' vr = \case | otherwise -> throwChatError $ CEGroupDuplicateMember cName APIJoinGroup groupId -> withUser $ \user@User {userId} -> do withGroupLock "joinGroup" groupId . procCmd $ do - (invitation, ct) <- withStore $ \db -> do + (invitation, ct) <- withFastStore $ \db -> do inv@ReceivedGroupInvitation {fromMember} <- getGroupInvitation db vr user groupId (inv,) <$> getContactViaMember db vr user fromMember let ReceivedGroupInvitation {fromMember, connRequest, groupInfo = g@GroupInfo {membership}} = invitation @@ -1846,14 +1904,14 @@ processChatCommand' vr = \case dm <- encodeConnInfo $ XGrpAcpt membershipMemId agentConnId <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True connRequest PQSupportOff let chatV = vr `peerConnChatVersion` peerChatVRange - cId <- withStore' $ \db -> do + cId <- withFastStore' $ \db -> do Connection {connId = cId} <- createMemberConnection db userId fromMember agentConnId chatV peerChatVRange subMode updateGroupMemberStatus db userId fromMember GSMemAccepted updateGroupMemberStatus db userId membership GSMemAccepted pure cId void (withAgent $ \a -> joinConnection a (aUserId user) (Just agentConnId) True connRequest dm PQSupportOff subMode) `catchChatError` \e -> do - withStore' $ \db -> do + withFastStore' $ \db -> do deleteConnectionRecord db user cId updateGroupMemberStatus db userId fromMember GSMemInvited updateGroupMemberStatus db userId membership GSMemInvited @@ -1863,7 +1921,7 @@ processChatCommand' vr = \case pure $ CRUserAcceptedGroupSent user g {membership = membership {memberStatus = GSMemAccepted}} Nothing Nothing -> throwChatError $ CEContactNotActive ct APIMemberRole groupId memberId memRole -> withUser $ \user -> do - Group gInfo@GroupInfo {membership} members <- withStore $ \db -> getGroup db vr user groupId + Group gInfo@GroupInfo {membership} members <- withFastStore $ \db -> getGroup db vr user groupId if memberId == groupMemberId' membership then changeMemberRole user gInfo members membership $ SGEUserRole memRole else case find ((== memberId) . groupMemberId') members of @@ -1875,10 +1933,10 @@ processChatCommand' vr = \case assertUserGroupRole gInfo $ maximum [GRAdmin, mRole, memRole] withGroupLock "memberRole" groupId . procCmd $ do unless (mRole == memRole) $ do - withStore' $ \db -> updateGroupMemberRole db user m memRole + withFastStore' $ \db -> updateGroupMemberRole db user m memRole case mStatus of GSMemInvited -> do - withStore (\db -> (,) <$> mapM (getContact db vr user) memberContactId <*> liftIO (getMemberInvitation db user $ groupMemberId' m)) >>= \case + withFastStore (\db -> (,) <$> mapM (getContact db vr user) memberContactId <*> liftIO (getMemberInvitation db user $ groupMemberId' m)) >>= \case (Just ct, Just cReq) -> sendGrpInvitation user ct gInfo (m :: GroupMember) {memberRole = memRole} cReq _ -> throwChatError $ CEGroupCantResendInvitation gInfo cName _ -> do @@ -1887,7 +1945,7 @@ processChatCommand' vr = \case toView $ CRNewChatItem user (AChatItem SCTGroup SMDSnd (GroupChat gInfo) ci) pure CRMemberRoleUser {user, groupInfo = gInfo, member = m {memberRole = memRole}, fromRole = mRole, toRole = memRole} APIBlockMemberForAll groupId memberId blocked -> withUser $ \user -> do - Group gInfo@GroupInfo {membership} members <- withStore $ \db -> getGroup db vr user groupId + Group gInfo@GroupInfo {membership} members <- withFastStore $ \db -> getGroup db vr user groupId when (memberId == groupMemberId' membership) $ throwChatError $ CECommandError "can't block/unblock self" case splitMember memberId members of Nothing -> throwChatError $ CEException "expected to find a single blocked member" @@ -1898,11 +1956,11 @@ processChatCommand' vr = \case withGroupLock "blockForAll" groupId . procCmd $ do let mrs = if blocked then MRSBlocked else MRSUnrestricted event = XGrpMemRestrict bmMemberId MemberRestrictions {restriction = mrs} - (msg, _) <- sendGroupMessage' user gInfo remainingMembers event + msg <- sendGroupMessage' user gInfo remainingMembers event let ciContent = CISndGroupEvent $ SGEMemberBlocked memberId (fromLocalProfile bmp) blocked ci <- saveSndChatItem user (CDGroupSnd gInfo) msg ciContent toView $ CRNewChatItem user (AChatItem SCTGroup SMDSnd (GroupChat gInfo) ci) - bm' <- withStore $ \db -> do + bm' <- withFastStore $ \db -> do liftIO $ updateGroupMemberBlocked db user groupId memberId mrs getGroupMember db vr user groupId memberId toggleNtf user bm' (not blocked) @@ -1912,7 +1970,7 @@ processChatCommand' vr = \case (_, []) -> Nothing (ms1, bm : ms2) -> Just (bm, ms1 <> ms2) APIRemoveMember groupId memberId -> withUser $ \user -> do - Group gInfo members <- withStore $ \db -> getGroup db vr user groupId + Group gInfo members <- withFastStore $ \db -> getGroup db vr user groupId case find ((== memberId) . groupMemberId') members of Nothing -> throwChatError CEGroupMemberNotFound Just m@GroupMember {memberId = mId, memberRole = mRole, memberStatus = mStatus, memberProfile} -> do @@ -1921,7 +1979,7 @@ processChatCommand' vr = \case case mStatus of GSMemInvited -> do deleteMemberConnection user m - withStore' $ \db -> deleteGroupMember db user m + withFastStore' $ \db -> deleteGroupMember db user m _ -> do (msg, _) <- sendGroupMessage user gInfo members $ XGrpMemDel mId ci <- saveSndChatItem user (CDGroupSnd gInfo) msg (CISndGroupEvent $ SGEMemberDeleted memberId (fromLocalProfile memberProfile)) @@ -1931,85 +1989,85 @@ processChatCommand' vr = \case deleteOrUpdateMemberRecord user m pure $ CRUserDeletedMember user gInfo m {memberStatus = GSMemRemoved} APILeaveGroup groupId -> withUser $ \user@User {userId} -> do - Group gInfo@GroupInfo {membership} members <- withStore $ \db -> getGroup db vr user groupId - filesInfo <- withStore' $ \db -> getGroupFileInfo db user gInfo + Group gInfo@GroupInfo {membership} members <- withFastStore $ \db -> getGroup db vr user groupId + filesInfo <- withFastStore' $ \db -> getGroupFileInfo db user gInfo withGroupLock "leaveGroup" groupId . procCmd $ do cancelFilesInProgress user filesInfo - (msg, _) <- sendGroupMessage' user gInfo members XGrpLeave + msg <- sendGroupMessage' user gInfo members XGrpLeave ci <- saveSndChatItem user (CDGroupSnd gInfo) msg (CISndGroupEvent SGEUserLeft) toView $ CRNewChatItem user (AChatItem SCTGroup SMDSnd (GroupChat gInfo) ci) -- TODO delete direct connections that were unused deleteGroupLinkIfExists user gInfo -- member records are not deleted to keep history deleteMembersConnections' user members True - withStore' $ \db -> updateGroupMemberStatus db userId membership GSMemLeft + withFastStore' $ \db -> updateGroupMemberStatus db userId membership GSMemLeft pure $ CRLeftMemberUser user gInfo {membership = membership {memberStatus = GSMemLeft}} APIListMembers groupId -> withUser $ \user -> - CRGroupMembers user <$> withStore (\db -> getGroup db vr user groupId) + CRGroupMembers user <$> withFastStore (\db -> getGroup db vr user groupId) AddMember gName cName memRole -> withUser $ \user -> do - (groupId, contactId) <- withStore $ \db -> (,) <$> getGroupIdByName db user gName <*> getContactIdByName db user cName + (groupId, contactId) <- withFastStore $ \db -> (,) <$> getGroupIdByName db user gName <*> getContactIdByName db user cName processChatCommand $ APIAddMember groupId contactId memRole JoinGroup gName -> withUser $ \user -> do - groupId <- withStore $ \db -> getGroupIdByName db user gName + groupId <- withFastStore $ \db -> getGroupIdByName db user gName processChatCommand $ APIJoinGroup groupId MemberRole gName gMemberName memRole -> withMemberName gName gMemberName $ \gId gMemberId -> APIMemberRole gId gMemberId memRole BlockForAll gName gMemberName blocked -> withMemberName gName gMemberName $ \gId gMemberId -> APIBlockMemberForAll gId gMemberId blocked RemoveMember gName gMemberName -> withMemberName gName gMemberName APIRemoveMember LeaveGroup gName -> withUser $ \user -> do - groupId <- withStore $ \db -> getGroupIdByName db user gName + groupId <- withFastStore $ \db -> getGroupIdByName db user gName processChatCommand $ APILeaveGroup groupId DeleteGroup gName -> withUser $ \user -> do - groupId <- withStore $ \db -> getGroupIdByName db user gName + groupId <- withFastStore $ \db -> getGroupIdByName db user gName processChatCommand $ APIDeleteChat (ChatRef CTGroup groupId) (CDMFull True) ClearGroup gName -> withUser $ \user -> do - groupId <- withStore $ \db -> getGroupIdByName db user gName + groupId <- withFastStore $ \db -> getGroupIdByName db user gName processChatCommand $ APIClearChat (ChatRef CTGroup groupId) ListMembers gName -> withUser $ \user -> do - groupId <- withStore $ \db -> getGroupIdByName db user gName + groupId <- withFastStore $ \db -> getGroupIdByName db user gName processChatCommand $ APIListMembers groupId APIListGroups userId contactId_ search_ -> withUserId userId $ \user -> - CRGroupsList user <$> withStore' (\db -> getUserGroupsWithSummary db vr user contactId_ search_) + CRGroupsList user <$> withFastStore' (\db -> getUserGroupsWithSummary db vr user contactId_ search_) ListGroups cName_ search_ -> withUser $ \user@User {userId} -> do - ct_ <- forM cName_ $ \cName -> withStore $ \db -> getContactByName db vr user cName + ct_ <- forM cName_ $ \cName -> withFastStore $ \db -> getContactByName db vr user cName processChatCommand $ APIListGroups userId (contactId' <$> ct_) search_ APIUpdateGroupProfile groupId p' -> withUser $ \user -> do - g <- withStore $ \db -> getGroup db vr user groupId + g <- withFastStore $ \db -> getGroup db vr user groupId runUpdateGroupProfile user g p' UpdateGroupNames gName GroupProfile {displayName, fullName} -> updateGroupProfileByName gName $ \p -> p {displayName, fullName} ShowGroupProfile gName -> withUser $ \user -> - CRGroupProfile user <$> withStore (\db -> getGroupInfoByName db vr user gName) + CRGroupProfile user <$> withFastStore (\db -> getGroupInfoByName db vr user gName) UpdateGroupDescription gName description -> updateGroupProfileByName gName $ \p -> p {description} ShowGroupDescription gName -> withUser $ \user -> - CRGroupDescription user <$> withStore (\db -> getGroupInfoByName db vr user gName) + CRGroupDescription user <$> withFastStore (\db -> getGroupInfoByName db vr user gName) APICreateGroupLink groupId mRole -> withUser $ \user -> withGroupLock "createGroupLink" groupId $ do - gInfo <- withStore $ \db -> getGroupInfo db vr user groupId + gInfo <- withFastStore $ \db -> getGroupInfo db vr user groupId assertUserGroupRole gInfo GRAdmin when (mRole > GRMember) $ throwChatError $ CEGroupMemberInitialRole gInfo mRole groupLinkId <- GroupLinkId <$> drgRandomBytes 16 subMode <- chatReadVar subscriptionMode let crClientData = encodeJSON $ CRDataGroup groupLinkId (connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMContact (Just crClientData) IKPQOff subMode - withStore $ \db -> createGroupLink db user gInfo connId cReq groupLinkId mRole subMode + withFastStore $ \db -> createGroupLink db user gInfo connId cReq groupLinkId mRole subMode pure $ CRGroupLinkCreated user gInfo cReq mRole APIGroupLinkMemberRole groupId mRole' -> withUser $ \user -> withGroupLock "groupLinkMemberRole" groupId $ do - gInfo <- withStore $ \db -> getGroupInfo db vr user groupId - (groupLinkId, groupLink, mRole) <- withStore $ \db -> getGroupLink db user gInfo + gInfo <- withFastStore $ \db -> getGroupInfo db vr user groupId + (groupLinkId, groupLink, mRole) <- withFastStore $ \db -> getGroupLink db user gInfo assertUserGroupRole gInfo GRAdmin when (mRole' > GRMember) $ throwChatError $ CEGroupMemberInitialRole gInfo mRole' - when (mRole' /= mRole) $ withStore' $ \db -> setGroupLinkMemberRole db user groupLinkId mRole' + when (mRole' /= mRole) $ withFastStore' $ \db -> setGroupLinkMemberRole db user groupLinkId mRole' pure $ CRGroupLink user gInfo groupLink mRole' APIDeleteGroupLink groupId -> withUser $ \user -> withGroupLock "deleteGroupLink" groupId $ do - gInfo <- withStore $ \db -> getGroupInfo db vr user groupId + gInfo <- withFastStore $ \db -> getGroupInfo db vr user groupId deleteGroupLink' user gInfo pure $ CRGroupLinkDeleted user gInfo APIGetGroupLink groupId -> withUser $ \user -> do - gInfo <- withStore $ \db -> getGroupInfo db vr user groupId - (_, groupLink, mRole) <- withStore $ \db -> getGroupLink db user gInfo + gInfo <- withFastStore $ \db -> getGroupInfo db vr user groupId + (_, groupLink, mRole) <- withFastStore $ \db -> getGroupLink db user gInfo pure $ CRGroupLink user gInfo groupLink mRole APICreateMemberContact gId gMemberId -> withUser $ \user -> do - (g, m) <- withStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> getGroupMember db vr user gId gMemberId + (g, m) <- withFastStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> getGroupMember db vr user gId gMemberId assertUserGroupRole g GRAuthor unless (groupFeatureMemberAllowed SGFDirectMessages (membership g) g) $ throwChatError $ CECommandError "direct messages not allowed" case memberConn m of @@ -2020,19 +2078,19 @@ processChatCommand' vr = \case -- TODO PQ should negotitate contact connection with PQSupportOn? (connId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing IKPQOff subMode -- [incognito] reuse membership incognito profile - ct <- withStore' $ \db -> createMemberContact db user connId cReq g m mConn subMode + ct <- withFastStore' $ \db -> createMemberContact db user connId cReq g m mConn subMode -- TODO not sure it is correct to set connections status here? lift $ setContactNetworkStatus ct NSConnected pure $ CRNewMemberContact user ct g m _ -> throwChatError CEGroupMemberNotActive APISendMemberContactInvitation contactId msgContent_ -> withUser $ \user -> do - (g@GroupInfo {groupId}, m, ct, cReq) <- withStore $ \db -> getMemberContact db vr user contactId + (g@GroupInfo {groupId}, m, ct, cReq) <- withFastStore $ \db -> getMemberContact db vr user contactId when (contactGrpInvSent ct) $ throwChatError $ CECommandError "x.grp.direct.inv already sent" case memberConn m of Just mConn -> do let msg = XGrpDirectInv cReq msgContent_ (sndMsg, _, _) <- sendDirectMemberMessage mConn msg groupId - withStore' $ \db -> setContactGrpInvSent db ct True + withFastStore' $ \db -> setContactGrpInvSent db ct True let ct' = ct {contactGrpInvSent = True} forM_ msgContent_ $ \mc -> do ci <- saveSndChatItem user (CDDirectSnd ct') sndMsg (CISndMsgContent mc) @@ -2040,28 +2098,28 @@ processChatCommand' vr = \case pure $ CRNewMemberContactSentInv user ct' g m _ -> throwChatError CEGroupMemberNotActive CreateGroupLink gName mRole -> withUser $ \user -> do - groupId <- withStore $ \db -> getGroupIdByName db user gName + groupId <- withFastStore $ \db -> getGroupIdByName db user gName processChatCommand $ APICreateGroupLink groupId mRole GroupLinkMemberRole gName mRole -> withUser $ \user -> do - groupId <- withStore $ \db -> getGroupIdByName db user gName + groupId <- withFastStore $ \db -> getGroupIdByName db user gName processChatCommand $ APIGroupLinkMemberRole groupId mRole DeleteGroupLink gName -> withUser $ \user -> do - groupId <- withStore $ \db -> getGroupIdByName db user gName + groupId <- withFastStore $ \db -> getGroupIdByName db user gName processChatCommand $ APIDeleteGroupLink groupId ShowGroupLink gName -> withUser $ \user -> do - groupId <- withStore $ \db -> getGroupIdByName db user gName + groupId <- withFastStore $ \db -> getGroupIdByName db user gName processChatCommand $ APIGetGroupLink groupId SendGroupMessageQuote gName cName quotedMsg msg -> withUser $ \user -> do - groupId <- withStore $ \db -> getGroupIdByName db user gName - quotedItemId <- withStore $ \db -> getGroupChatItemIdByText db user groupId cName quotedMsg + groupId <- withFastStore $ \db -> getGroupIdByName db user gName + quotedItemId <- withFastStore $ \db -> getGroupChatItemIdByText db user groupId cName quotedMsg let mc = MCText msg processChatCommand . APISendMessage (ChatRef CTGroup groupId) False Nothing $ ComposedMessage Nothing (Just quotedItemId) mc ClearNoteFolder -> withUser $ \user -> do - folderId <- withStore (`getUserNoteFolderId` user) + folderId <- withFastStore (`getUserNoteFolderId` user) processChatCommand $ APIClearChat (ChatRef CTLocal folderId) LastChats count_ -> withUser' $ \user -> do let count = fromMaybe 5000 count_ - (errs, previews) <- partitionEithers <$> withStore' (\db -> getChatPreviews db vr user False (PTLast count) clqNoFilters) + (errs, previews) <- partitionEithers <$> withFastStore' (\db -> getChatPreviews db vr user False (PTLast count) clqNoFilters) unless (null errs) $ toView $ CRChatErrors (Just user) (map ChatErrorStore errs) pure $ CRChats previews LastMessages (Just chatName) count search -> withUser $ \user -> do @@ -2069,22 +2127,22 @@ processChatCommand' vr = \case chatResp <- processChatCommand $ APIGetChat chatRef (CPLast count) search pure $ CRChatItems user (Just chatName) (aChatItems . chat $ chatResp) LastMessages Nothing count search -> withUser $ \user -> do - chatItems <- withStore $ \db -> getAllChatItems db vr user (CPLast count) search + chatItems <- withFastStore $ \db -> getAllChatItems db vr user (CPLast count) search pure $ CRChatItems user Nothing chatItems LastChatItemId (Just chatName) index -> withUser $ \user -> do chatRef <- getChatRef user chatName chatResp <- processChatCommand (APIGetChat chatRef (CPLast $ index + 1) Nothing) pure $ CRChatItemId user (fmap aChatItemId . listToMaybe . aChatItems . chat $ chatResp) LastChatItemId Nothing index -> withUser $ \user -> do - chatItems <- withStore $ \db -> getAllChatItems db vr user (CPLast $ index + 1) Nothing + chatItems <- withFastStore $ \db -> getAllChatItems db vr user (CPLast $ index + 1) Nothing pure $ CRChatItemId user (fmap aChatItemId . listToMaybe $ chatItems) ShowChatItem (Just itemId) -> withUser $ \user -> do - chatItem <- withStore $ \db -> do + chatItem <- withFastStore $ \db -> do chatRef <- getChatRefViaItemId db user itemId getAChatItem db vr user chatRef itemId pure $ CRChatItems user Nothing ((: []) chatItem) ShowChatItem Nothing -> withUser $ \user -> do - chatItems <- withStore $ \db -> getAllChatItems db vr user (CPLast 1) Nothing + chatItems <- withFastStore $ \db -> getAllChatItems db vr user (CPLast 1) Nothing pure $ CRChatItems user Nothing chatItems ShowChatItemInfo chatName msg -> withUser $ \user -> do chatRef <- getChatRef user chatName @@ -2108,6 +2166,7 @@ processChatCommand' vr = \case ForwardFile chatName fileId -> forwardFile chatName fileId SendFile ForwardImage chatName fileId -> forwardFile chatName fileId SendImage SendFileDescription _chatName _f -> pure $ chatCmdError Nothing "TODO" + -- TODO to use priority transactions we need a parameter that differentiates manual and automatic acceptance ReceiveFile fileId userApprovedRelays encrypted_ rcvInline_ filePath_ -> withUser $ \_ -> withFileLock "receiveFile" fileId . procCmd $ do (user, ft@RcvFileTransfer {fileStatus}) <- withStore (`getRcvFileTransferById` fileId) @@ -2122,7 +2181,7 @@ processChatCommand' vr = \case ok_ CancelFile fileId -> withUser $ \user@User {userId} -> withFileLock "cancelFile" fileId . procCmd $ - withStore (\db -> getFileTransfer db user fileId) >>= \case + withFastStore (\db -> getFileTransfer db user fileId) >>= \case FTSnd ftm@FileTransferMeta {xftpSndFile, cancelled} fts | cancelled -> throwChatError $ CEFileCancel fileId "file already cancelled" | not (null fts) && all fileCancelledOrCompleteSMP fts -> @@ -2130,16 +2189,16 @@ processChatCommand' vr = \case | otherwise -> do fileAgentConnIds <- cancelSndFile user ftm fts True deleteAgentConnectionsAsync user fileAgentConnIds - withStore (\db -> liftIO $ lookupChatRefByFileId db user fileId) >>= \case + withFastStore (\db -> liftIO $ lookupChatRefByFileId db user fileId) >>= \case Nothing -> pure () Just (ChatRef CTDirect contactId) -> do - (contact, sharedMsgId) <- withStore $ \db -> (,) <$> getContact db vr user contactId <*> getSharedMsgIdByFileId db userId fileId + (contact, sharedMsgId) <- withFastStore $ \db -> (,) <$> getContact db vr user contactId <*> getSharedMsgIdByFileId db userId fileId void . sendDirectContactMessage user contact $ XFileCancel sharedMsgId Just (ChatRef CTGroup groupId) -> do - (Group gInfo ms, sharedMsgId) <- withStore $ \db -> (,) <$> getGroup db vr user groupId <*> getSharedMsgIdByFileId db userId fileId + (Group gInfo ms, sharedMsgId) <- withFastStore $ \db -> (,) <$> getGroup db vr user groupId <*> getSharedMsgIdByFileId db userId fileId void . sendGroupMessage user gInfo ms $ XFileCancel sharedMsgId Just _ -> throwChatError $ CEFileInternal "invalid chat ref for file transfer" - ci <- withStore $ \db -> lookupChatItemByFileId db vr user fileId + ci <- withFastStore $ \db -> lookupChatItemByFileId db vr user fileId pure $ CRSndFileCancelled user ci ftm fts where fileCancelledOrCompleteSMP SndFileTransfer {fileStatus = s} = @@ -2150,7 +2209,7 @@ processChatCommand' vr = \case | otherwise -> case xftpRcvFile of Nothing -> do cancelRcvFileTransfer user ftr >>= mapM_ (deleteAgentConnectionAsync user) - ci <- withStore $ \db -> lookupChatItemByFileId db vr user fileId + ci <- withFastStore $ \db -> lookupChatItemByFileId db vr user fileId pure $ CRRcvFileCancelled user ci ftr Just XFTPRcvFile {agentRcvFileId} -> do forM_ (liveRcvFileTransferPath ftr) $ \filePath -> do @@ -2161,9 +2220,9 @@ processChatCommand' vr = \case aci_ <- resetRcvCIFileStatus user fileId CIFSRcvInvitation pure $ CRRcvFileCancelled user aci_ ftr FileStatus fileId -> withUser $ \user -> do - withStore (\db -> lookupChatItemByFileId db vr user fileId) >>= \case + withFastStore (\db -> lookupChatItemByFileId db vr user fileId) >>= \case Nothing -> do - fileStatus <- withStore $ \db -> getFileTransferProgress db user fileId + fileStatus <- withFastStore $ \db -> getFileTransferProgress db user fileId pure $ CRFileTransferStatus user fileStatus Just ci@(AChatItem _ _ _ ChatItem {file}) -> case file of Just CIFile {fileProtocol = FPLocal} -> @@ -2171,7 +2230,7 @@ processChatCommand' vr = \case Just CIFile {fileProtocol = FPXFTP} -> pure $ CRFileTransferStatusXFTP user ci _ -> do - fileStatus <- withStore $ \db -> getFileTransferProgress db user fileId + fileStatus <- withFastStore $ \db -> getFileTransferProgress db user fileId pure $ CRFileTransferStatus user fileStatus ShowProfile -> withUser $ \user@User {profile} -> pure $ CRUserProfile user (fromLocalProfile profile) UpdateProfile displayName fullName -> withUser $ \user@User {profile} -> do @@ -2185,7 +2244,7 @@ processChatCommand' vr = \case let p = (fromLocalProfile profile :: Profile) {preferences = Just . setPreference f (Just allowed) $ preferences' user} updateProfile user p SetContactFeature (ACF f) cName allowed_ -> withUser $ \user -> do - ct@Contact {userPreferences} <- withStore $ \db -> getContactByName db vr user cName + ct@Contact {userPreferences} <- withFastStore $ \db -> getContactByName db vr user cName let prefs' = setPreference f allowed_ $ Just userPreferences updateContactPrefs user ct prefs' SetGroupFeature (AGFNR f) gName enabled -> @@ -2200,7 +2259,7 @@ processChatCommand' vr = \case p = (fromLocalProfile profile :: Profile) {preferences = Just . setPreference' SCFTimedMessages (Just pref) $ preferences' user} updateProfile user p SetContactTimedMessages cName timedMessagesEnabled_ -> withUser $ \user -> do - ct@Contact {userPreferences = userPreferences@Preferences {timedMessages}} <- withStore $ \db -> getContactByName db vr user cName + ct@Contact {userPreferences = userPreferences@Preferences {timedMessages}} <- withFastStore $ \db -> getContactByName db vr user cName let currentTTL = timedMessages >>= \TimedMessagesPreference {ttl} -> ttl pref_ = tmeToPref currentTTL <$> timedMessagesEnabled_ prefs' = setPreference' SCFTimedMessages pref_ $ Just userPreferences @@ -2244,7 +2303,7 @@ processChatCommand' vr = \case ShowVersion -> do -- simplexmqCommitQ makes iOS builds crash m( let versionInfo = coreVersionInfo "" - chatMigrations <- map upMigration <$> withStore' (Migrations.getCurrent . DB.conn) + chatMigrations <- map upMigration <$> withFastStore' (Migrations.getCurrent . DB.conn) agentMigrations <- withAgent getAgentMigrations pure $ CRVersionInfo {versionInfo, chatMigrations, agentMigrations} DebugLocks -> lift $ do @@ -2317,10 +2376,10 @@ processChatCommand' vr = \case getChatRef :: User -> ChatName -> CM ChatRef getChatRef user (ChatName cType name) = ChatRef cType <$> case cType of - CTDirect -> withStore $ \db -> getContactIdByName db user name - CTGroup -> withStore $ \db -> getGroupIdByName db user name + CTDirect -> withFastStore $ \db -> getContactIdByName db user name + CTGroup -> withFastStore $ \db -> getGroupIdByName db user name CTLocal - | name == "" -> withStore (`getUserNoteFolderId` user) + | name == "" -> withFastStore (`getUserNoteFolderId` user) | otherwise -> throwChatError $ CECommandError "not supported" _ -> throwChatError $ CECommandError "not supported" checkChatStopped :: CM ChatResponse -> CM ChatResponse @@ -2332,10 +2391,10 @@ processChatCommand' vr = \case checkStoreNotChanged :: CM ChatResponse -> CM ChatResponse checkStoreNotChanged = ifM (asks chatStoreChanged >>= readTVarIO) (throwChatError CEChatStoreChanged) withUserName :: UserName -> (UserId -> ChatCommand) -> CM ChatResponse - withUserName uName cmd = withStore (`getUserIdByName` uName) >>= processChatCommand . cmd + withUserName uName cmd = withFastStore (`getUserIdByName` uName) >>= processChatCommand . cmd withContactName :: ContactName -> (ContactId -> ChatCommand) -> CM ChatResponse withContactName cName cmd = withUser $ \user -> - withStore (\db -> getContactIdByName db user cName) >>= processChatCommand . cmd + withFastStore (\db -> getContactIdByName db user cName) >>= processChatCommand . cmd withMemberName :: GroupName -> ContactName -> (GroupId -> GroupMemberId -> ChatCommand) -> CM ChatResponse withMemberName gName mName cmd = withUser $ \user -> getGroupAndMemberId user gName mName >>= processChatCommand . uncurry cmd @@ -2345,23 +2404,23 @@ processChatCommand' vr = \case verifyConnectionCode user conn@Connection {connId} (Just code) = do code' <- getConnectionCode $ aConnId conn let verified = sameVerificationCode code code' - when verified . withStore' $ \db -> setConnectionVerified db user connId $ Just code' + when verified . withFastStore' $ \db -> setConnectionVerified db user connId $ Just code' pure $ CRConnectionVerified user verified code' verifyConnectionCode user conn@Connection {connId} _ = do code' <- getConnectionCode $ aConnId conn - withStore' $ \db -> setConnectionVerified db user connId Nothing + withFastStore' $ \db -> setConnectionVerified db user connId Nothing pure $ CRConnectionVerified user False code' getSentChatItemIdByText :: User -> ChatRef -> Text -> CM Int64 getSentChatItemIdByText user@User {userId, localDisplayName} (ChatRef cType cId) msg = case cType of - CTDirect -> withStore $ \db -> getDirectChatItemIdByText db userId cId SMDSnd msg - CTGroup -> withStore $ \db -> getGroupChatItemIdByText db user cId (Just localDisplayName) msg - CTLocal -> withStore $ \db -> getLocalChatItemIdByText db user cId SMDSnd msg + CTDirect -> withFastStore $ \db -> getDirectChatItemIdByText db userId cId SMDSnd msg + CTGroup -> withFastStore $ \db -> getGroupChatItemIdByText db user cId (Just localDisplayName) msg + CTLocal -> withFastStore $ \db -> getLocalChatItemIdByText db user cId SMDSnd msg _ -> throwChatError $ CECommandError "not supported" getChatItemIdByText :: User -> ChatRef -> Text -> CM Int64 getChatItemIdByText user (ChatRef cType cId) msg = case cType of - CTDirect -> withStore $ \db -> getDirectChatItemIdByText' db user cId msg - CTGroup -> withStore $ \db -> getGroupChatItemIdByText' db user cId msg - CTLocal -> withStore $ \db -> getLocalChatItemIdByText' db user cId msg + CTDirect -> withFastStore $ \db -> getDirectChatItemIdByText' db user cId msg + CTGroup -> withFastStore $ \db -> getGroupChatItemIdByText' db user cId msg + CTLocal -> withFastStore $ \db -> getLocalChatItemIdByText' db user cId msg _ -> throwChatError $ CECommandError "not supported" connectViaContact :: User -> IncognitoEnabled -> ConnectionRequestUri 'CMContact -> CM ChatResponse connectViaContact user@User {userId} incognito cReq@(CRContactUri ConnReqUriData {crClientData}) = withInvitationLock "connectViaContact" (strEncode cReq) $ do @@ -2370,7 +2429,7 @@ processChatCommand' vr = \case case groupLinkId of -- contact address Nothing -> - withStore' (\db -> getConnReqContactXContactId db vr user cReqHash) >>= \case + withFastStore' (\db -> getConnReqContactXContactId db vr user cReqHash) >>= \case (Just contact, _) -> pure $ CRContactAlreadyExists user contact (_, xContactId_) -> procCmd $ do let randomXContactId = XContactId <$> drgRandomBytes 16 @@ -2378,7 +2437,7 @@ processChatCommand' vr = \case connect' Nothing cReqHash xContactId False -- group link Just gLinkId -> - withStore' (\db -> getConnReqContactXContactId db vr user cReqHash) >>= \case + withFastStore' (\db -> getConnReqContactXContactId db vr user cReqHash) >>= \case (Just _contact, _) -> procCmd $ do -- allow repeat contact request newXContactId <- XContactId <$> drgRandomBytes 16 @@ -2394,7 +2453,7 @@ processChatCommand' vr = \case -- [incognito] generate profile to send incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing subMode <- chatReadVar subscriptionMode - conn@PendingContactConnection {pccConnId} <- withStore' $ \db -> createConnReqConnection db userId connId cReqHash xContactId incognitoProfile groupLinkId subMode chatV pqSup + conn@PendingContactConnection {pccConnId} <- withFastStore' $ \db -> createConnReqConnection db userId connId cReqHash xContactId incognitoProfile groupLinkId subMode chatV pqSup joinContact user pccConnId connId cReq incognitoProfile xContactId inGroup pqSup chatV pure $ CRSentInvitation user conn incognitoProfile connectContactViaAddress :: User -> IncognitoEnabled -> Contact -> ConnectionRequestUri 'CMContact -> CM ChatResponse @@ -2407,7 +2466,7 @@ processChatCommand' vr = \case -- [incognito] generate profile to send incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing subMode <- chatReadVar subscriptionMode - (pccConnId, ct') <- withStore $ \db -> createAddressContactConnection db vr user ct connId cReqHash newXContactId incognitoProfile subMode chatV pqSup + (pccConnId, ct') <- withFastStore $ \db -> createAddressContactConnection db vr user ct connId cReqHash newXContactId incognitoProfile subMode chatV pqSup joinContact user pccConnId connId cReq incognitoProfile newXContactId False pqSup chatV pure $ CRSentInvitationToContact user ct' incognitoProfile prepareContact :: User -> ConnectionRequestUri 'CMContact -> PQSupport -> CM (ConnId, VersionChat) @@ -2431,7 +2490,7 @@ processChatCommand' vr = \case joinPreparedAgentConnection user pccConnId connId cReq connInfo pqSup subMode = do void (withAgent $ \a -> joinConnection a (aUserId user) (Just connId) True cReq connInfo pqSup subMode) `catchChatError` \e -> do - withStore' $ \db -> deleteConnectionRecord db user pccConnId + withFastStore' $ \db -> deleteConnectionRecord db user pccConnId withAgent $ \a -> deleteConnectionAsync a False connId throwError e contactMember :: Contact -> [GroupMember] -> Maybe GroupMember @@ -2446,14 +2505,14 @@ processChatCommand' vr = \case when (fromInteger fileSize > maxFileSize) $ throwChatError $ CEFileSize f pure fileSize updateProfile :: User -> Profile -> CM ChatResponse - updateProfile user p' = updateProfile_ user p' $ withStore $ \db -> updateUserProfile db user p' + updateProfile user p' = updateProfile_ user p' $ withFastStore $ \db -> updateUserProfile db user p' updateProfile_ :: User -> Profile -> CM User -> CM ChatResponse updateProfile_ user@User {profile = p@LocalProfile {displayName = n}} p'@Profile {displayName = n'} updateUser | p' == fromLocalProfile p = pure $ CRUserProfileNoChange user | otherwise = do when (n /= n') $ checkValidName n' -- read contacts before user update to correctly merge preferences - contacts <- withStore' $ \db -> getUserContacts db vr user + contacts <- withFastStore' $ \db -> getUserContacts db vr user user' <- updateUser asks currentUser >>= atomically . (`writeTVar` Just user') withChatLock "updateProfile" . procCmd $ do @@ -2488,10 +2547,10 @@ processChatCommand' vr = \case mergedProfile' = userProfileToSend user' Nothing (Just ct') False ctSndEvent :: ChangedProfileContact -> (ConnOrGroupId, ChatMsgEvent 'Json) ctSndEvent ChangedProfileContact {mergedProfile', conn = Connection {connId}} = (ConnectionId connId, XInfo mergedProfile') - ctMsgReq :: ChangedProfileContact -> Either ChatError SndMessage -> Either ChatError MsgReq + ctMsgReq :: ChangedProfileContact -> Either ChatError SndMessage -> Either ChatError ChatMsgReq ctMsgReq ChangedProfileContact {conn} = fmap $ \SndMessage {msgId, msgBody} -> - (conn, MsgFlags {notification = hasNotification XInfo_}, msgBody, msgId) + (conn, MsgFlags {notification = hasNotification XInfo_}, msgBody, [msgId]) updateContactPrefs :: User -> Contact -> Preferences -> CM ChatResponse updateContactPrefs _ ct@Contact {activeConn = Nothing} _ = throwChatError $ CEContactNotActive ct updateContactPrefs user@User {userId} ct@Contact {activeConn = Just Connection {customUserProfileId}, userPreferences = contactUserPrefs} contactUserPrefs' @@ -2531,12 +2590,12 @@ processChatCommand' vr = \case when (memberStatus membership == GSMemInvited) $ throwChatError (CEGroupNotJoined g) when (memberRemoved membership) $ throwChatError CEGroupMemberUserRemoved unless (memberActive membership) $ throwChatError CEGroupMemberNotActive - delGroupChatItem :: MsgDirectionI d => User -> GroupInfo -> ChatItem 'CTGroup d -> MessageId -> Maybe GroupMember -> CM ChatResponse - delGroupChatItem user gInfo ci msgId byGroupMember = do + delGroupChatItems :: User -> GroupInfo -> [CChatItem 'CTGroup] -> Maybe GroupMember -> CM ChatResponse + delGroupChatItems user gInfo items byGroupMember = do deletedTs <- liftIO getCurrentTime if groupFeatureAllowed SGFFullDelete gInfo - then deleteGroupCI user gInfo ci True False byGroupMember deletedTs - else markGroupCIDeleted user gInfo ci msgId True byGroupMember deletedTs + then deleteGroupCIs user gInfo items True False byGroupMember deletedTs + else markGroupCIsDeleted user gInfo items True byGroupMember deletedTs updateGroupProfileByName :: GroupName -> (GroupProfile -> GroupProfile) -> CM ChatResponse updateGroupProfileByName gName update = withUser $ \user -> do g@(Group GroupInfo {groupProfile = p} _) <- withStore $ \db -> @@ -2631,34 +2690,34 @@ processChatCommand' vr = \case setUserPrivacy user@User {userId} user'@User {userId = userId'} | userId == userId' = do asks currentUser >>= atomically . (`writeTVar` Just user') - withStore' (`updateUserPrivacy` user') + withFastStore' (`updateUserPrivacy` user') pure $ CRUserPrivacy {user = user', updatedUser = user'} | otherwise = do - withStore' (`updateUserPrivacy` user') + withFastStore' (`updateUserPrivacy` user') pure $ CRUserPrivacy {user, updatedUser = user'} checkDeleteChatUser :: User -> CM () checkDeleteChatUser user@User {userId} = do - users <- withStore' getUsers + users <- withFastStore' getUsers let otherVisible = filter (\User {userId = userId', viewPwdHash} -> userId /= userId' && isNothing viewPwdHash) users when (activeUser user && length otherVisible > 0) $ throwChatError (CECantDeleteActiveUser userId) deleteChatUser :: User -> Bool -> CM ChatResponse deleteChatUser user delSMPQueues = do - filesInfo <- withStore' (`getUserFileInfo` user) + filesInfo <- withFastStore' (`getUserFileInfo` user) cancelFilesInProgress user filesInfo deleteFilesLocally filesInfo withAgent $ \a -> deleteUser a (aUserId user) delSMPQueues - withStore' (`deleteUserRecord` user) + withFastStore' (`deleteUserRecord` user) when (activeUser user) $ chatWriteVar currentUser Nothing ok_ updateChatSettings :: ChatName -> (ChatSettings -> ChatSettings) -> CM ChatResponse updateChatSettings (ChatName cType name) updateSettings = withUser $ \user -> do (chatId, chatSettings) <- case cType of - CTDirect -> withStore $ \db -> do + CTDirect -> withFastStore $ \db -> do ctId <- getContactIdByName db user name Contact {chatSettings} <- getContact db vr user ctId pure (ctId, chatSettings) CTGroup -> - withStore $ \db -> do + withFastStore $ \db -> do gId <- getGroupIdByName db user name GroupInfo {chatSettings} <- getGroupInfo db vr user gId pure (gId, chatSettings) @@ -2666,7 +2725,7 @@ processChatCommand' vr = \case processChatCommand $ APISetChatSettings (ChatRef cType chatId) $ updateSettings chatSettings connectPlan :: User -> AConnectionRequestUri -> CM ConnectionPlan connectPlan user (ACR SCMInvitation (CRInvitationUri crData e2e)) = do - withStore' (\db -> getConnectionEntityByConnReq db vr user cReqSchemas) >>= \case + withFastStore' (\db -> getConnectionEntityByConnReq db vr user cReqSchemas) >>= \case Nothing -> pure $ CPInvitationLink ILPOk Just (RcvDirectMsgConnection conn ct_) -> do let Connection {connStatus, contactConnInitiated} = conn @@ -2691,12 +2750,12 @@ processChatCommand' vr = \case case groupLinkId of -- contact address Nothing -> - withStore' (\db -> getUserContactLinkByConnReq db user cReqSchemas) >>= \case + withFastStore' (\db -> getUserContactLinkByConnReq db user cReqSchemas) >>= \case Just _ -> pure $ CPContactAddress CAPOwnLink Nothing -> - withStore' (\db -> getContactConnEntityByConnReqHash db vr user cReqHashes) >>= \case + withFastStore' (\db -> getContactConnEntityByConnReqHash db vr user cReqHashes) >>= \case Nothing -> - withStore' (\db -> getContactWithoutConnViaAddress db vr user cReqSchemas) >>= \case + withFastStore' (\db -> getContactWithoutConnViaAddress db vr user cReqSchemas) >>= \case Nothing -> pure $ CPContactAddress CAPOk Just ct -> pure $ CPContactAddress (CAPContactViaAddress ct) Just (RcvDirectMsgConnection _conn Nothing) -> pure $ CPContactAddress CAPConnectingConfirmReconnect @@ -2707,11 +2766,11 @@ processChatCommand' vr = \case Just _ -> throwChatError $ CECommandError "found connection entity is not RcvDirectMsgConnection" -- group link Just _ -> - withStore' (\db -> getGroupInfoByUserContactLinkConnReq db vr user cReqSchemas) >>= \case + withFastStore' (\db -> getGroupInfoByUserContactLinkConnReq db vr user cReqSchemas) >>= \case Just g -> pure $ CPGroupLink (GLPOwnLink g) Nothing -> do - connEnt_ <- withStore' $ \db -> getContactConnEntityByConnReqHash db vr user cReqHashes - gInfo_ <- withStore' $ \db -> getGroupInfoByGroupLinkHash db vr user cReqHashes + connEnt_ <- withFastStore' $ \db -> getContactConnEntityByConnReqHash db vr user cReqHashes + gInfo_ <- withFastStore' $ \db -> getGroupInfoByGroupLinkHash db vr user cReqHashes case (gInfo_, connEnt_) of (Nothing, Nothing) -> pure $ CPGroupLink GLPOk (Nothing, Just (RcvDirectMsgConnection _conn Nothing)) -> pure $ CPGroupLink GLPConnectingConfirmReconnect @@ -2734,7 +2793,7 @@ processChatCommand' vr = \case cReqHashes = bimap hash hash cReqSchemas hash = ConnReqUriHash . C.sha256Hash . strEncode updateCIGroupInvitationStatus user GroupInfo {groupId} newStatus = do - AChatItem _ _ cInfo ChatItem {content, meta = CIMeta {itemId}} <- withStore $ \db -> getChatItemByGroupId db vr user groupId + AChatItem _ _ cInfo ChatItem {content, meta = CIMeta {itemId}} <- withFastStore $ \db -> getChatItemByGroupId db vr user groupId case (cInfo, content) of (DirectChat ct@Contact {contactId}, CIRcvGroupInvitation ciGroupInv@CIGroupInvitation {status} memRole) | status == CIGISPending -> do @@ -2746,9 +2805,9 @@ processChatCommand' vr = \case _ -> pure () -- prohibited sendContactContentMessage :: User -> ContactId -> Bool -> Maybe Int -> ComposedMessage -> Maybe CIForwardedFrom -> CM ChatResponse sendContactContentMessage user contactId live itemTTL (ComposedMessage file_ quotedItemId_ mc) itemForwarded = do - ct@Contact {contactUsed} <- withStore $ \db -> getContact db vr user contactId + ct@Contact {contactUsed} <- withFastStore $ \db -> getContact db vr user contactId assertDirectAllowed user MDSnd ct XMsgNew_ - unless contactUsed $ withStore' $ \db -> updateContactUsed db user ct + unless contactUsed $ withFastStore' $ \db -> updateContactUsed db user ct if isVoice mc && not (featureAllowed SCFVoice forUser ct) then pure $ chatCmdError (Just user) ("feature not allowed " <> T.unpack (chatFeatureNameText CFVoice)) else do @@ -2771,7 +2830,7 @@ processChatCommand' vr = \case (Nothing, Just _) -> pure (MCForward (ExtMsgContent mc fInv_ (ttl' <$> timed_) (justTrue live)), Nothing) (Just quotedItemId, Nothing) -> do CChatItem _ qci@ChatItem {meta = CIMeta {itemTs, itemSharedMsgId}, formattedText, file} <- - withStore $ \db -> getDirectChatItem db user contactId quotedItemId + withFastStore $ \db -> getDirectChatItem db user contactId quotedItemId (origQmc, qd, sent) <- quoteData qci let msgRef = MsgRef {msgId = itemSharedMsgId, sentAt = itemTs, sent, memberId = Nothing} qmc = quoteContent mc origQmc file @@ -2786,7 +2845,7 @@ processChatCommand' vr = \case quoteData _ = throwChatError CEInvalidQuote sendGroupContentMessage :: User -> GroupId -> Bool -> Maybe Int -> ComposedMessage -> Maybe CIForwardedFrom -> CM ChatResponse sendGroupContentMessage user groupId live itemTTL (ComposedMessage file_ quotedItemId_ mc) itemForwarded = do - g@(Group gInfo _) <- withStore $ \db -> getGroup db vr user groupId + g@(Group gInfo _) <- withFastStore $ \db -> getGroup db vr user groupId assertUserGroupRole gInfo GRAuthor send g where @@ -2797,10 +2856,10 @@ processChatCommand' vr = \case (fInv_, ciFile_) <- L.unzip <$> setupSndFileTransfer g (length $ filter memberCurrent ms) timed_ <- sndGroupCITimed live gInfo itemTTL (msgContainer, quotedItem_) <- prepareGroupMsg user gInfo mc quotedItemId_ itemForwarded fInv_ timed_ live - (msg, groupSndResult) <- sendGroupMessage user gInfo ms (XMsgNew msgContainer) + (msg, r) <- sendGroupMessage user gInfo ms (XMsgNew msgContainer) ci <- saveSndChatItem' user (CDGroupSnd gInfo) msg (CISndMsgContent mc) ciFile_ quotedItem_ itemForwarded timed_ live - withStore' $ \db -> do - let GroupSndResult {sentTo, pending, forwarded} = groupSndResult + withFastStore' $ \db -> do + let GroupSndResult {sentTo, pending, forwarded} = mkGroupSndResult r createMemberSndStatuses db ci sentTo GSSNew createMemberSndStatuses db ci forwarded GSSForwarded createMemberSndStatuses db ci pending GSSInactive @@ -2820,20 +2879,20 @@ processChatCommand' vr = \case (fInv, ciFile, ft) <- xftpSndFileTransfer_ user file fileSize n $ Just contactOrGroup case contactOrGroup of CGContact Contact {activeConn} -> forM_ activeConn $ \conn -> - withStore' $ \db -> createSndFTDescrXFTP db user Nothing conn ft dummyFileDescr + withFastStore' $ \db -> createSndFTDescrXFTP db user Nothing conn ft dummyFileDescr CGGroup (Group _ ms) -> forM_ ms $ \m -> saveMemberFD m `catchChatError` (toView . CRChatError (Just user)) where -- we are not sending files to pending members, same as with inline files saveMemberFD m@GroupMember {activeConn = Just conn@Connection {connStatus}} = when ((connStatus == ConnReady || connStatus == ConnSndReady) && not (connDisabled conn)) $ - withStore' $ + withFastStore' $ \db -> createSndFTDescrXFTP db user (Just m) conn ft dummyFileDescr saveMemberFD _ = pure () pure (fInv, ciFile) createNoteFolderContentItem :: User -> NoteFolderId -> ComposedMessage -> Maybe CIForwardedFrom -> CM ChatResponse createNoteFolderContentItem user folderId (ComposedMessage file_ quotedItemId_ mc) itemForwarded = do forM_ quotedItemId_ $ \_ -> throwError $ ChatError $ CECommandError "not supported" - nf <- withStore $ \db -> getNoteFolder db user folderId + nf <- withFastStore $ \db -> getNoteFolder db user folderId createdAt <- liftIO getCurrentTime let content = CISndMsgContent mc let cd = CDLocalSnd nf @@ -2842,13 +2901,13 @@ processChatCommand' vr = \case fsFilePath <- lift $ toFSFilePath filePath fileSize <- liftIO $ CF.getFileContentsSize $ CryptoFile fsFilePath cryptoArgs chunkSize <- asks $ fileChunkSize . config - withStore' $ \db -> do + withFastStore' $ \db -> do fileId <- createLocalFile CIFSSndStored db user nf ciId createdAt cf fileSize chunkSize pure CIFile {fileId, fileName = takeFileName filePath, fileSize, fileSource = Just cf, fileStatus = CIFSSndStored, fileProtocol = FPLocal} let ci = mkChatItem cd ciId content ciFile_ Nothing Nothing itemForwarded Nothing False createdAt Nothing createdAt pure . CRNewChatItem user $ AChatItem SCTLocal SMDSnd (LocalChat nf) ci getConnQueueInfo user Connection {connId, agentConnId = AgentConnId acId} = do - msgInfo <- withStore' (`getLastRcvMsgInfo` connId) + msgInfo <- withFastStore' (`getLastRcvMsgInfo` connId) CRQueueInfo user msgInfo <$> withAgent (`getConnectionQueueInfo` acId) contactCITimed :: Contact -> CM (Maybe CITimed) @@ -3661,12 +3720,12 @@ deleteTimedItem user (ChatRef cType chatId, itemId) deleteAt = do vr <- chatVersionRange case cType of CTDirect -> do - (ct, CChatItem _ ci) <- withStore $ \db -> (,) <$> getContact db vr user chatId <*> getDirectChatItem db user chatId itemId - deleteDirectCI user ct ci True True >>= toView + (ct, ci) <- withStore $ \db -> (,) <$> getContact db vr user chatId <*> getDirectChatItem db user chatId itemId + deleteDirectCIs user ct [ci] True True >>= toView CTGroup -> do - (gInfo, CChatItem _ ci) <- withStore $ \db -> (,) <$> getGroupInfo db vr user chatId <*> getGroupChatItem db user chatId itemId + (gInfo, ci) <- withStore $ \db -> (,) <$> getGroupInfo db vr user chatId <*> getGroupChatItem db user chatId itemId deletedTs <- liftIO getCurrentTime - deleteGroupCI user gInfo ci True True Nothing deletedTs >>= toView + deleteGroupCIs user gInfo [ci] True True Nothing deletedTs >>= toView _ -> toView . CRChatError (Just user) . ChatError $ CEInternalError "bad deleteTimedItem cType" startUpdatedTimedItemThread :: User -> ChatRef -> ChatItem c d -> ChatItem c d -> CM () @@ -4039,13 +4098,10 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = processINFOpqSupport conn pqSupport _conn' <- saveConnInfo conn connInfo pure () - MSG meta _msgFlags msgBody -> - -- TODO only acknowledge without saving message? - -- probably this branch is never executed, so there should be no reason - -- to save message if contact hasn't been created yet - chat item isn't created anyway - withAckMessage' "new contact msg" agentConnId meta $ - void $ - saveDirectRcvMSG conn meta msgBody + MSG meta _msgFlags _msgBody -> + -- We are not saving message (saveDirectRcvMSG) as contact hasn't been created yet, + -- chat item is also not created here + withAckMessage' "new contact msg" agentConnId meta $ pure () SENT msgId _proxy -> do void $ continueSending connEntity conn sentMsgDeliveryEvent conn msgId @@ -4088,37 +4144,53 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = let MsgMeta {pqEncryption} = msgMeta (ct', conn') <- updateContactPQRcv user ct conn pqEncryption checkIntegrityCreateItem (CDDirectRcv ct') msgMeta `catchChatError` \_ -> pure () - (conn'', msg@RcvMessage {chatMsgEvent = ACME _ event}) <- saveDirectRcvMSG conn' msgMeta msgBody - let tag = toCMEventTag event - atomically $ writeTVar tags [tshow tag] - logInfo $ "contact msg=" <> tshow tag <> " " <> eInfo - let ct'' = ct' {activeConn = Just conn''} :: Contact - assertDirectAllowed user MDRcv ct'' tag - case event of - XMsgNew mc -> newContentMessage ct'' mc msg msgMeta - XMsgFileDescr sharedMsgId fileDescr -> messageFileDescription ct'' sharedMsgId fileDescr - XMsgUpdate sharedMsgId mContent ttl live -> messageUpdate ct'' sharedMsgId mContent msg msgMeta ttl live - XMsgDel sharedMsgId _ -> messageDelete ct'' sharedMsgId msg msgMeta - XMsgReact sharedMsgId _ reaction add -> directMsgReaction ct'' sharedMsgId reaction add msg msgMeta - -- TODO discontinue XFile - XFile fInv -> processFileInvitation' ct'' fInv msg msgMeta - XFileCancel sharedMsgId -> xFileCancel ct'' sharedMsgId - XFileAcptInv sharedMsgId fileConnReq_ fName -> xFileAcptInv ct'' sharedMsgId fileConnReq_ fName - XInfo p -> xInfo ct'' p - XDirectDel -> xDirectDel ct'' msg msgMeta - XGrpInv gInv -> processGroupInvitation ct'' gInv msg msgMeta - XInfoProbe probe -> xInfoProbe (COMContact ct'') probe - XInfoProbeCheck probeHash -> xInfoProbeCheck (COMContact ct'') probeHash - XInfoProbeOk probe -> xInfoProbeOk (COMContact ct'') probe - XCallInv callId invitation -> xCallInv ct'' callId invitation msg msgMeta - XCallOffer callId offer -> xCallOffer ct'' callId offer msg - XCallAnswer callId answer -> xCallAnswer ct'' callId answer msg - XCallExtra callId extraInfo -> xCallExtra ct'' callId extraInfo msg - XCallEnd callId -> xCallEnd ct'' callId msg - BFileChunk sharedMsgId chunk -> bFileChunk ct'' sharedMsgId chunk msgMeta - _ -> messageError $ "unsupported message: " <> T.pack (show event) - let Contact {chatSettings = ChatSettings {sendRcpts}} = ct'' - pure $ fromMaybe (sendRcptsContacts user) sendRcpts && hasDeliveryReceipt tag + forM_ aChatMsgs $ \case + Right (ACMsg _ chatMsg) -> + processEvent ct' conn' tags eInfo chatMsg `catchChatError` \e -> toView $ CRChatError (Just user) e + Left e -> do + atomically $ modifyTVar' tags ("error" :) + logInfo $ "contact msg=error " <> eInfo <> " " <> tshow e + toView $ CRChatError (Just user) (ChatError . CEException $ "error parsing chat message: " <> e) + checkSendRcpt ct' $ rights aChatMsgs -- not crucial to use ct'' from processEvent + where + aChatMsgs = parseChatMessages msgBody + processEvent :: Contact -> Connection -> TVar [Text] -> Text -> MsgEncodingI e => ChatMessage e -> CM () + processEvent ct' conn' tags eInfo chatMsg@ChatMessage {chatMsgEvent} = do + let tag = toCMEventTag chatMsgEvent + atomically $ modifyTVar' tags (tshow tag :) + logInfo $ "contact msg=" <> tshow tag <> " " <> eInfo + (conn'', msg@RcvMessage {chatMsgEvent = ACME _ event}) <- saveDirectRcvMSG conn' msgMeta msgBody chatMsg + let ct'' = ct' {activeConn = Just conn''} :: Contact + case event of + XMsgNew mc -> newContentMessage ct'' mc msg msgMeta + XMsgFileDescr sharedMsgId fileDescr -> messageFileDescription ct'' sharedMsgId fileDescr + XMsgUpdate sharedMsgId mContent ttl live -> messageUpdate ct'' sharedMsgId mContent msg msgMeta ttl live + XMsgDel sharedMsgId _ -> messageDelete ct'' sharedMsgId msg msgMeta + XMsgReact sharedMsgId _ reaction add -> directMsgReaction ct'' sharedMsgId reaction add msg msgMeta + -- TODO discontinue XFile + XFile fInv -> processFileInvitation' ct'' fInv msg msgMeta + XFileCancel sharedMsgId -> xFileCancel ct'' sharedMsgId + XFileAcptInv sharedMsgId fileConnReq_ fName -> xFileAcptInv ct'' sharedMsgId fileConnReq_ fName + XInfo p -> xInfo ct'' p + XDirectDel -> xDirectDel ct'' msg msgMeta + XGrpInv gInv -> processGroupInvitation ct'' gInv msg msgMeta + XInfoProbe probe -> xInfoProbe (COMContact ct'') probe + XInfoProbeCheck probeHash -> xInfoProbeCheck (COMContact ct'') probeHash + XInfoProbeOk probe -> xInfoProbeOk (COMContact ct'') probe + XCallInv callId invitation -> xCallInv ct'' callId invitation msg msgMeta + XCallOffer callId offer -> xCallOffer ct'' callId offer msg + XCallAnswer callId answer -> xCallAnswer ct'' callId answer msg + XCallExtra callId extraInfo -> xCallExtra ct'' callId extraInfo msg + XCallEnd callId -> xCallEnd ct'' callId msg + BFileChunk sharedMsgId chunk -> bFileChunk ct'' sharedMsgId chunk msgMeta + _ -> messageError $ "unsupported message: " <> T.pack (show event) + checkSendRcpt :: Contact -> [AChatMessage] -> CM Bool + checkSendRcpt ct' aMsgs = do + let Contact {chatSettings = ChatSettings {sendRcpts}} = ct' + pure $ fromMaybe (sendRcptsContacts user) sendRcpts && any aChatMsgHasReceipt aMsgs + where + aChatMsgHasReceipt (ACMsg _ ChatMessage {chatMsgEvent}) = + hasDeliveryReceipt (toCMEventTag chatMsgEvent) RCVD msgMeta msgRcpt -> withAckMessage' "contact rcvd" agentConnId msgMeta $ directMsgReceived ct conn msgMeta msgRcpt @@ -4522,7 +4594,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = atomically $ modifyTVar' tags ("error" :) logInfo $ "group msg=error " <> eInfo <> " " <> tshow e toView $ CRChatError (Just user) (ChatError . CEException $ "error parsing chat message: " <> e) - forwardMsg_ `catchChatError` (toView . CRChatError (Just user)) + forwardMsgs (rights aChatMsgs) `catchChatError` (toView . CRChatError (Just user)) checkSendRcpt $ rights aChatMsgs where aChatMsgs = parseChatMessages msgBody @@ -4574,27 +4646,24 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = where aChatMsgHasReceipt (ACMsg _ ChatMessage {chatMsgEvent}) = hasDeliveryReceipt (toCMEventTag chatMsgEvent) - forwardMsg_ :: CM () - forwardMsg_ = do + forwardMsgs :: [AChatMessage] -> CM () + forwardMsgs aMsgs = do let GroupMember {memberRole = membershipMemRole} = membership - when (membershipMemRole >= GRAdmin && not (blockedByAdmin m)) $ case aChatMsgs of - -- currently only a single message is forwarded - [Right (ACMsg _ chatMsg)] -> - forM_ (forwardedGroupMsg chatMsg) $ \chatMsg' -> do - ChatConfig {highlyAvailable} <- asks config - -- members introduced to this invited member - introducedMembers <- - if memberCategory m == GCInviteeMember - then withStore' $ \db -> getForwardIntroducedMembers db vr user m highlyAvailable - else pure [] - -- invited members to which this member was introduced - invitedMembers <- withStore' $ \db -> getForwardInvitedMembers db vr user m highlyAvailable - let GroupMember {memberId} = m - ms = forwardedToGroupMembers (introducedMembers <> invitedMembers) chatMsg' - msg = XGrpMsgForward memberId chatMsg' brokerTs - unless (null ms) . void $ - sendGroupMessage' user gInfo ms msg - _ -> pure () + when (membershipMemRole >= GRAdmin && not (blockedByAdmin m)) $ do + let forwardedMsgs = mapMaybe (\(ACMsg _ chatMsg) -> forwardedGroupMsg chatMsg) aMsgs + forM_ (L.nonEmpty forwardedMsgs) $ \forwardedMsgs' -> do + ChatConfig {highlyAvailable} <- asks config + -- members introduced to this invited member + introducedMembers <- + if memberCategory m == GCInviteeMember + then withStore' $ \db -> getForwardIntroducedMembers db vr user m highlyAvailable + else pure [] + -- invited members to which this member was introduced + invitedMembers <- withStore' $ \db -> getForwardInvitedMembers db vr user m highlyAvailable + let GroupMember {memberId} = m + ms = forwardedToGroupMembers (introducedMembers <> invitedMembers) forwardedMsgs' + events = L.map (\cm -> XGrpMsgForward memberId cm brokerTs) forwardedMsgs' + unless (null ms) $ sendGroupMessages user gInfo ms events RCVD msgMeta msgRcpt -> withAckMessage' "group rcvd" agentConnId msgMeta $ groupMsgReceived gInfo m conn msgMeta msgRcpt @@ -4640,8 +4709,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = -- [async agent commands] continuation on receiving OK when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData -> pure () JOINED _ -> - -- [async agent commands] continuation on receiving JOINED - when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData -> pure () + -- [async agent commands] continuation on receiving JOINED + when (corrId /= "") $ withCompletedCommand conn agentMsg $ \_cmdData -> pure () QCONT -> do continued <- continueSending connEntity conn when continued $ sendPendingGroupMessages user m conn @@ -5191,19 +5260,26 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = _ -> messageError "x.msg.update: contact attempted invalid message update" messageDelete :: Contact -> SharedMsgId -> RcvMessage -> MsgMeta -> CM () - messageDelete ct@Contact {contactId} sharedMsgId RcvMessage {msgId} msgMeta = do + messageDelete ct@Contact {contactId} sharedMsgId _rcvMessage msgMeta = do deleteRcvChatItem `catchCINotFound` (toView . CRChatItemDeletedNotFound user ct) where brokerTs = metaBrokerTs msgMeta deleteRcvChatItem = do - CChatItem msgDir ci <- withStore $ \db -> getDirectChatItemBySharedMsgId db user contactId sharedMsgId + cci@(CChatItem msgDir ci) <- withStore $ \db -> getDirectChatItemBySharedMsgId db user contactId sharedMsgId case msgDir of - SMDRcv -> - if featureAllowed SCFFullDelete forContact ct - then deleteDirectCI user ct ci False False >>= toView - else markDirectCIDeleted user ct ci msgId False brokerTs >>= toView + SMDRcv + | rcvItemDeletable ci brokerTs -> + if featureAllowed SCFFullDelete forContact ct + then deleteDirectCIs user ct [cci] False False >>= toView + else markDirectCIsDeleted user ct [cci] False brokerTs >>= toView + | otherwise -> messageError "x.msg.del: contact attempted invalid message delete" SMDSnd -> messageError "x.msg.del: contact attempted invalid message delete" + rcvItemDeletable :: ChatItem c d -> UTCTime -> Bool + rcvItemDeletable ChatItem {meta = CIMeta {itemTs, itemDeleted}} brokerTs = + -- 78 hours margin to account for possible sending delay + diffUTCTime brokerTs itemTs < (78 * 3600) && isNothing itemDeleted + directMsgReaction :: Contact -> SharedMsgId -> MsgReaction -> Bool -> RcvMessage -> MsgMeta -> CM () directMsgReaction ct sharedMsgId reaction add RcvMessage {msgId} MsgMeta {broker = (_, brokerTs)} = do when (featureAllowed SCFReactions forContact ct) $ do @@ -5281,7 +5357,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = ci <- createNonLive file_ ci' <- withStore' $ \db -> markGroupCIBlockedByAdmin db user gInfo ci groupMsgToView gInfo ci' - applyModeration CIModeration {moderatorMember = moderator@GroupMember {memberRole = moderatorRole}, createdByMsgId, moderatedAt} + applyModeration CIModeration {moderatorMember = moderator@GroupMember {memberRole = moderatorRole}, moderatedAt} | moderatorRole < GRAdmin || moderatorRole < memberRole = createContentItem | groupFeatureAllowed SGFFullDelete gInfo = do @@ -5291,7 +5367,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = | otherwise = do file_ <- processFileInv ci <- createNonLive file_ - toView =<< markGroupCIDeleted user gInfo ci createdByMsgId False (Just moderator) moderatedAt + toView =<< markGroupCIsDeleted user gInfo [CChatItem SMDRcv ci] False (Just moderator) moderatedAt createNonLive file_ = saveRcvChatItem' user (CDGroupRcv gInfo m) msg sharedMsgId_ brokerTs (CIRcvMsgContent content) (snd <$> file_) timed' False createContentItem = do @@ -5350,30 +5426,40 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = groupMessageDelete gInfo@GroupInfo {groupId, membership} m@GroupMember {memberId, memberRole = senderRole} sharedMsgId sndMemberId_ RcvMessage {msgId} brokerTs = do let msgMemberId = fromMaybe memberId sndMemberId_ withStore' (\db -> runExceptT $ getGroupMemberCIBySharedMsgId db user groupId msgMemberId sharedMsgId) >>= \case - Right (CChatItem _ ci@ChatItem {chatDir}) -> case chatDir of - CIGroupRcv mem - | sameMemberId memberId mem && msgMemberId == memberId -> delete ci Nothing >>= toView - | otherwise -> deleteMsg mem ci - CIGroupSnd -> deleteMsg membership ci + Right cci@(CChatItem _ ci@ChatItem {chatDir}) -> case chatDir of + CIGroupRcv mem -> case sndMemberId_ of + -- regular deletion + Nothing + | sameMemberId memberId mem && msgMemberId == memberId && rcvItemDeletable ci brokerTs -> + delete cci Nothing >>= toView + | otherwise -> + messageError "x.msg.del: member attempted invalid message delete" + -- moderation (not limited by time) + Just _ + | sameMemberId memberId mem && msgMemberId == memberId -> + delete cci (Just m) >>= toView + | otherwise -> + moderate mem cci + CIGroupSnd -> moderate membership cci Left e | msgMemberId == memberId -> messageError $ "x.msg.del: message not found, " <> tshow e | senderRole < GRAdmin -> messageError $ "x.msg.del: message not found, message of another member with insufficient member permissions, " <> tshow e | otherwise -> withStore' $ \db -> createCIModeration db gInfo m msgMemberId sharedMsgId msgId brokerTs where - deleteMsg :: MsgDirectionI d => GroupMember -> ChatItem 'CTGroup d -> CM () - deleteMsg mem ci = case sndMemberId_ of + moderate :: GroupMember -> CChatItem 'CTGroup -> CM () + moderate mem cci = case sndMemberId_ of Just sndMemberId - | sameMemberId sndMemberId mem -> checkRole mem $ delete ci (Just m) >>= toView + | sameMemberId sndMemberId mem -> checkRole mem $ delete cci (Just m) >>= toView | otherwise -> messageError "x.msg.del: message of another member with incorrect memberId" _ -> messageError "x.msg.del: message of another member without memberId" checkRole GroupMember {memberRole} a | senderRole < GRAdmin || senderRole < memberRole = messageError "x.msg.del: message of another member with insufficient member permissions" | otherwise = a - delete :: MsgDirectionI d => ChatItem 'CTGroup d -> Maybe GroupMember -> CM ChatResponse - delete ci byGroupMember - | groupFeatureAllowed SGFFullDelete gInfo = deleteGroupCI user gInfo ci False False byGroupMember brokerTs - | otherwise = markGroupCIDeleted user gInfo ci msgId False byGroupMember brokerTs + delete :: CChatItem 'CTGroup -> Maybe GroupMember -> CM ChatResponse + delete cci byGroupMember + | groupFeatureAllowed SGFFullDelete gInfo = deleteGroupCIs user gInfo [cci] False False byGroupMember brokerTs + | otherwise = markGroupCIsDeleted user gInfo [cci] False byGroupMember brokerTs -- TODO remove once XFile is discontinued processFileInvitation' :: Contact -> FileInvitation -> RcvMessage -> MsgMeta -> CM () @@ -5549,9 +5635,9 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = _ -> messageError "x.file.acpt.inv: member connection is not active" else messageError "x.file.acpt.inv: fileName is different from expected" - groupMsgToView :: GroupInfo -> ChatItem 'CTGroup 'MDRcv -> CM () + groupMsgToView :: forall d. MsgDirectionI d => GroupInfo -> ChatItem 'CTGroup d -> CM () groupMsgToView gInfo ci = - toView $ CRNewChatItem user (AChatItem SCTGroup SMDRcv (GroupChat gInfo) ci) + toView $ CRNewChatItem user (AChatItem SCTGroup (msgDirection @d) (GroupChat gInfo) ci) processGroupInvitation :: Contact -> GroupInvitation -> RcvMessage -> MsgMeta -> CM () processGroupInvitation ct inv msg msgMeta = do @@ -6653,6 +6739,22 @@ deleteOrUpdateMemberRecord user@User {userId} member = Just _ -> updateGroupMemberStatus db userId member GSMemRemoved Nothing -> deleteGroupMember db user member +sendDirectContactMessages :: MsgEncodingI e => User -> Contact -> NonEmpty (ChatMsgEvent e) -> CM () +sendDirectContactMessages user ct events = do + Connection {connChatVersion = v} <- liftEither $ contactSendConn_ ct + if v >= batchSend2Version + then sendDirectContactMessages' user ct events + else mapM_ (void . sendDirectContactMessage user ct) events + +sendDirectContactMessages' :: MsgEncodingI e => User -> Contact -> NonEmpty (ChatMsgEvent e) -> CM () +sendDirectContactMessages' user ct events = do + conn@Connection {connId} <- liftEither $ contactSendConn_ ct + let idsEvts = L.map (ConnectionId connId,) events + msgFlags = MsgFlags {notification = any (hasNotification . toCMEventTag) events} + (errs, msgs) <- lift $ partitionEithers . L.toList <$> createSndMessages idsEvts + unless (null errs) $ toView $ CRChatErrors (Just user) errs + mapM_ (batchSendConnMessages user conn msgFlags) (L.nonEmpty msgs) + sendDirectContactMessage :: MsgEncodingI e => User -> Contact -> ChatMsgEvent e -> CM (SndMessage, Int64) sendDirectContactMessage user ct chatMsgEvent = do conn@Connection {connId} <- liftEither $ contactSendConn_ ct @@ -6709,38 +6811,23 @@ sendGroupMemberMessages user conn events groupId = do (errs, msgs) <- lift $ partitionEithers . L.toList <$> createSndMessages idsEvts unless (null errs) $ toView $ CRChatErrors (Just user) errs forM_ (L.nonEmpty msgs) $ \msgs' -> - batchSendGroupMemberMessages user conn msgs' + batchSendConnMessages user conn MsgFlags {notification = True} msgs' -batchSendGroupMemberMessages :: User -> Connection -> NonEmpty SndMessage -> CM () -batchSendGroupMemberMessages user conn msgs = do - -- TODO v5.7 based on version (?) - -- let shouldCompress = False - -- let batched = if shouldCompress then batchSndMessagesBinary msgs' else batchSndMessagesJSON msgs' +batchSendConnMessages :: User -> Connection -> MsgFlags -> NonEmpty SndMessage -> CM () +batchSendConnMessages user conn msgFlags msgs = do let batched = batchSndMessagesJSON msgs let (errs', msgBatches) = partitionEithers batched -- shouldn't happen, as large messages would have caused createNewSndMessage to throw SELargeMsg unless (null errs') $ toView $ CRChatErrors (Just user) errs' - forM_ msgBatches $ \batch -> - processSndMessageBatch conn batch `catchChatError` (toView . CRChatError (Just user)) + forM_ (L.nonEmpty msgBatches) $ \msgBatches' -> do + let msgReq = L.map (msgBatchReq conn msgFlags) msgBatches' + void $ deliverMessages msgReq -processSndMessageBatch :: Connection -> MsgBatch -> CM () -processSndMessageBatch conn@Connection {connId} (MsgBatch batchBody sndMsgs) = do - (agentMsgId, _pqEnc) <- withAgent $ \a -> sendMessage a (aConnId conn) PQEncOff MsgFlags {notification = True} batchBody - let sndMsgDelivery = SndMsgDelivery {connId, agentMsgId} - lift . void . withStoreBatch' $ \db -> map (\SndMessage {msgId} -> createSndMsgDelivery db sndMsgDelivery msgId) sndMsgs - --- TODO v5.7 update batching for groups batchSndMessagesJSON :: NonEmpty SndMessage -> [Either ChatError MsgBatch] batchSndMessagesJSON = batchMessages maxEncodedMsgLength . L.toList --- batchSndMessagesBinary :: NonEmpty SndMessage -> [Either ChatError MsgBatch] --- batchSndMessagesBinary msgs = map toMsgBatch . SMP.batchTransmissions_ (maxEncodedMsgLength) $ L.zip (map compress1 msgs) msgs --- where --- toMsgBatch :: SMP.TransportBatch SndMessage -> Either ChatError MsgBatch --- toMsgBatch = \case --- SMP.TBTransmissions combined _n sms -> Right $ MsgBatch (markCompressedBatch combined) sms --- SMP.TBError tbe SndMessage {msgId} -> Left . ChatError $ CEInternalError (show tbe <> " " <> show msgId) --- SMP.TBTransmission {} -> Left . ChatError $ CEInternalError "batchTransmissions_ didn't produce a batch" +msgBatchReq :: Connection -> MsgFlags -> MsgBatch -> ChatMsgReq +msgBatchReq conn msgFlags (MsgBatch batchBody sndMsgs) = (conn, msgFlags, batchBody, map (\SndMessage {msgId} -> msgId) sndMsgs) encodeConnInfo :: MsgEncodingI e => ChatMsgEvent e -> CM ByteString encodeConnInfo chatMsgEvent = do @@ -6767,19 +6854,23 @@ deliverMessage conn cmEventTag msgBody msgId = do deliverMessage' :: Connection -> MsgFlags -> MsgBody -> MessageId -> CM (Int64, PQEncryption) deliverMessage' conn msgFlags msgBody msgId = - deliverMessages ((conn, msgFlags, msgBody, msgId) :| []) >>= \case - r :| [] -> liftEither r + deliverMessages ((conn, msgFlags, msgBody, [msgId]) :| []) >>= \case + r :| [] -> case r of + Right ([deliveryId], pqEnc) -> pure (deliveryId, pqEnc) + Right (deliveryIds, _) -> throwChatError $ CEInternalError $ "deliverMessage: expected 1 delivery id, got " <> show (length deliveryIds) + Left e -> throwError e rs -> throwChatError $ CEInternalError $ "deliverMessage: expected 1 result, got " <> show (length rs) -type MsgReq = (Connection, MsgFlags, MsgBody, MessageId) +-- [MessageId] - SndMessage ids inside MsgBatch, or single message id +type ChatMsgReq = (Connection, MsgFlags, MsgBody, [MessageId]) -deliverMessages :: NonEmpty MsgReq -> CM (NonEmpty (Either ChatError (Int64, PQEncryption))) +deliverMessages :: NonEmpty ChatMsgReq -> CM (NonEmpty (Either ChatError ([Int64], PQEncryption))) deliverMessages msgs = deliverMessagesB $ L.map Right msgs -deliverMessagesB :: NonEmpty (Either ChatError MsgReq) -> CM (NonEmpty (Either ChatError (Int64, PQEncryption))) +deliverMessagesB :: NonEmpty (Either ChatError ChatMsgReq) -> CM (NonEmpty (Either ChatError ([Int64], PQEncryption))) deliverMessagesB msgReqs = do msgReqs' <- liftIO compressBodies - sent <- L.zipWith prepareBatch msgReqs' <$> withAgent (`sendMessagesB` L.map toAgent msgReqs') + sent <- L.zipWith prepareBatch msgReqs' <$> withAgent (`sendMessagesB` snd (mapAccumL toAgent Nothing msgReqs')) lift . void $ withStoreBatch' $ \db -> map (updatePQSndEnabled db) (rights . L.toList $ sent) lift . withStoreBatch $ \db -> L.map (bindRight $ createDelivery db) sent where @@ -6795,16 +6886,20 @@ deliverMessagesB msgReqs = do when (B.length msgBody' > maxCompressedMsgLength) $ throwError $ ChatError $ CEException "large compressed message" pure (conn, msgFlags, msgBody', msgId) _ -> pure mr - toAgent = \case - Right (conn@Connection {pqEncryption}, msgFlags, msgBody, _msgId) -> Right (aConnId conn, pqEncryption, msgFlags, msgBody) - Left _ce -> Left (AP.INTERNAL "ChatError, skip") -- as long as it is Left, the agent batchers should just step over it + toAgent prev = \case + Right (conn@Connection {connId, pqEncryption}, msgFlags, msgBody, _msgIds) -> + let cId = case prev of + Just prevId | prevId == connId -> "" + _ -> aConnId conn + in (Just connId, Right (cId, pqEncryption, msgFlags, msgBody)) + Left _ce -> (prev, Left (AP.INTERNAL "ChatError, skip")) -- as long as it is Left, the agent batchers should just step over it prepareBatch (Right req) (Right ar) = Right (req, ar) prepareBatch (Left ce) _ = Left ce -- restore original ChatError prepareBatch _ (Left ae) = Left $ ChatErrorAgent ae Nothing - createDelivery :: DB.Connection -> (MsgReq, (AgentMsgId, PQEncryption)) -> IO (Either ChatError (Int64, PQEncryption)) - createDelivery db ((Connection {connId}, _, _, msgId), (agentMsgId, pqEnc')) = - Right . (,pqEnc') <$> createSndMsgDelivery db (SndMsgDelivery {connId, agentMsgId}) msgId - updatePQSndEnabled :: DB.Connection -> (MsgReq, (AgentMsgId, PQEncryption)) -> IO () + createDelivery :: DB.Connection -> (ChatMsgReq, (AgentMsgId, PQEncryption)) -> IO (Either ChatError ([Int64], PQEncryption)) + createDelivery db ((Connection {connId}, _, _, msgIds), (agentMsgId, pqEnc')) = do + Right . (,pqEnc') <$> mapM (createSndMsgDelivery db (SndMsgDelivery {connId, agentMsgId})) msgIds + updatePQSndEnabled :: DB.Connection -> (ChatMsgReq, (AgentMsgId, PQEncryption)) -> IO () updatePQSndEnabled db ((Connection {connId, pqSndEnabled}, _, _, _), (_, pqSndEnabled')) = case (pqSndEnabled, pqSndEnabled') of (Just b, b') | b' /= b -> updatePQ @@ -6815,11 +6910,11 @@ deliverMessagesB msgReqs = do -- TODO combine profile update and message into one batch -- Take into account that it may not fit, and that we currently don't support sending multiple messages to the same connection in one call. -sendGroupMessage :: MsgEncodingI e => User -> GroupInfo -> [GroupMember] -> ChatMsgEvent e -> CM (SndMessage, GroupSndResult) +sendGroupMessage :: MsgEncodingI e => User -> GroupInfo -> [GroupMember] -> ChatMsgEvent e -> CM (SndMessage, GroupSndResultData) sendGroupMessage user gInfo members chatMsgEvent = do when shouldSendProfileUpdate $ sendProfileUpdate `catchChatError` (toView . CRChatError (Just user)) - sendGroupMessage' user gInfo members chatMsgEvent + sendGroupMessage_ user gInfo members chatMsgEvent where User {profile = p, userMemberProfileUpdatedAt} = user GroupInfo {userMemberProfileSentAt} = gInfo @@ -6837,32 +6932,59 @@ sendGroupMessage user gInfo members chatMsgEvent = do currentTs <- liftIO getCurrentTime withStore' $ \db -> updateUserMemberProfileSentAt db user gInfo currentTs +type GroupSndResultData = (([Either ChatError ([Int64], PQEncryption)], [(GroupMember, Connection)]), ([Either ChatError ()], [GroupMember]), [GroupMember]) + data GroupSndResult = GroupSndResult { sentTo :: [GroupMember], pending :: [GroupMember], forwarded :: [GroupMember] } -sendGroupMessage' :: MsgEncodingI e => User -> GroupInfo -> [GroupMember] -> ChatMsgEvent e -> CM (SndMessage, GroupSndResult) -sendGroupMessage' user gInfo@GroupInfo {groupId} members chatMsgEvent = do - msg@SndMessage {msgId, msgBody} <- createSndMessage chatMsgEvent (GroupId groupId) - recipientMembers <- liftIO $ shuffleMembers (filter memberCurrent members) - let msgFlags = MsgFlags {notification = hasNotification $ toCMEventTag chatMsgEvent} - (toSend, pending, forwarded, _, dups) = foldr addMember ([], [], [], S.empty, 0 :: Int) recipientMembers +sendGroupMessage' :: MsgEncodingI e => User -> GroupInfo -> [GroupMember] -> ChatMsgEvent e -> CM SndMessage +sendGroupMessage' user gInfo members chatMsgEvent = fst <$> sendGroupMessage_ user gInfo members chatMsgEvent + +sendGroupMessage_ :: MsgEncodingI e => User -> GroupInfo -> [GroupMember] -> ChatMsgEvent e -> CM (SndMessage, GroupSndResultData) +sendGroupMessage_ user gInfo members chatMsgEvent = + sendGroupMessages_ user gInfo members (chatMsgEvent :| []) >>= \case + (msg :| [], r) -> pure (msg, r) + _ -> throwChatError $ CEInternalError "sendGroupMessage': expected 1 message" + +sendGroupMessages :: MsgEncodingI e => User -> GroupInfo -> [GroupMember] -> NonEmpty (ChatMsgEvent e) -> CM () +sendGroupMessages user gInfo members events = void $ sendGroupMessages_ user gInfo members events + +mkGroupSndResult :: GroupSndResultData -> GroupSndResult +mkGroupSndResult ((delivered, sentTo), (stored, pending), forwarded) = + GroupSndResult + { sentTo = filterSent' delivered sentTo fst, + pending = filterSent' stored pending id, + forwarded + } + where + -- TODO in theory this could deduplicate members and keep results only when ... some sent? or all sent? + -- This is not important, as it is not used in batch calls + filterSent' :: [Either ChatError a] -> [mem] -> (mem -> GroupMember) -> [GroupMember] + filterSent' rs ms mem = [mem m | (Right _, m) <- zip rs ms] + +sendGroupMessages_ :: MsgEncodingI e => User -> GroupInfo -> [GroupMember] -> NonEmpty (ChatMsgEvent e) -> CM (NonEmpty SndMessage, GroupSndResultData) +sendGroupMessages_ user gInfo@GroupInfo {groupId} members events = do + let idsEvts = L.map (GroupId groupId,) events + (errs, msgs) <- lift $ partitionEithers . L.toList <$> createSndMessages idsEvts + unless (null errs) $ toView $ CRChatErrors (Just user) errs + case L.nonEmpty msgs of + Nothing -> throwChatError $ CEInternalError "sendGroupMessages: no messages created" + Just msgs' -> do + recipientMembers <- liftIO $ shuffleMembers (filter memberCurrent members) + let msgFlags = MsgFlags {notification = any (hasNotification . toCMEventTag) events} + (toSendSeparate, toSendBatched, pending, forwarded, _, dups) = + foldr addMember ([], [], [], [], S.empty, 0 :: Int) recipientMembers + when (dups /= 0) $ logError $ "sendGroupMessage: " <> tshow dups <> " duplicate members" -- TODO PQ either somehow ensure that group members connections cannot have pqSupport/pqEncryption or pass Off's here - msgReqs = map (\(_, conn) -> (conn, msgFlags, msgBody, msgId)) toSend - when (dups /= 0) $ logError $ "sendGroupMessage: " <> tshow dups <> " duplicate members" - delivered <- maybe (pure []) (fmap L.toList . deliverMessages) $ L.nonEmpty msgReqs - let errors = lefts delivered - unless (null errors) $ toView $ CRChatErrors (Just user) errors - stored <- lift . withStoreBatch' $ \db -> map (\m -> createPendingGroupMessage db (groupMemberId' m) msgId Nothing) pending - let gsr = - GroupSndResult - { sentTo = filterSent delivered toSend fst, - pending = filterSent stored pending id, - forwarded - } - pure (msg, gsr) + let msgReqs = prepareMsgReqs msgFlags msgs' toSendSeparate toSendBatched + delivered <- maybe (pure []) (fmap L.toList . deliverMessages) $ L.nonEmpty msgReqs + let errors = lefts delivered + unless (null errors) $ toView $ CRChatErrors (Just user) errors + stored <- lift . withStoreBatch' $ \db -> map (\m -> createPendingMsgs db m msgs') pending + pure (msgs', ((delivered, toSendSeparate <> toSendBatched), (stored, pending), forwarded)) where shuffleMembers :: [GroupMember] -> IO [GroupMember] shuffleMembers ms = do @@ -6870,31 +6992,52 @@ sendGroupMessage' user gInfo@GroupInfo {groupId} members chatMsgEvent = do liftM2 (<>) (shuffle adminMs) (shuffle otherMs) where isAdmin GroupMember {memberRole} = memberRole >= GRAdmin - addMember m acc@(toSend, pending, forwarded, !mIds, !dups) = case memberSendAction gInfo chatMsgEvent members m of - Just a - | mId `S.member` mIds -> (toSend, pending, forwarded, mIds, dups + 1) - | otherwise -> case a of - MSASend conn -> ((m, conn) : toSend, pending, forwarded, mIds', dups) - MSAPending -> (toSend, m : pending, forwarded, mIds', dups) - MSAForwarded -> (toSend, pending, m : forwarded, mIds', dups) - Nothing -> acc + addMember m acc@(toSendSeparate, toSendBatched, pending, forwarded, !mIds, !dups) = + case memberSendAction gInfo events members m of + Just a + | mId `S.member` mIds -> (toSendSeparate, toSendBatched, pending, forwarded, mIds, dups + 1) + | otherwise -> case a of + MSASend conn -> ((m, conn) : toSendSeparate, toSendBatched, pending, forwarded, mIds', dups) + MSASendBatched conn -> (toSendSeparate, (m, conn) : toSendBatched, pending, forwarded, mIds', dups) + MSAPending -> (toSendSeparate, toSendBatched, m : pending, forwarded, mIds', dups) + MSAForwarded -> (toSendSeparate, toSendBatched, pending, m : forwarded, mIds', dups) + Nothing -> acc where mId = groupMemberId' m mIds' = S.insert mId mIds - filterSent :: [Either ChatError a] -> [mem] -> (mem -> GroupMember) -> [GroupMember] - filterSent rs ms mem = [mem m | (Right _, m) <- zip rs ms] + prepareMsgReqs :: MsgFlags -> NonEmpty SndMessage -> [(GroupMember, Connection)] -> [(GroupMember, Connection)] -> [ChatMsgReq] + prepareMsgReqs msgFlags msgs toSendSeparate toSendBatched = do + let msgReqsSeparate = foldr (\(_, conn) reqs -> foldr (\msg -> (sndMessageReq conn msg :)) reqs msgs) [] toSendSeparate + batched = batchSndMessagesJSON msgs + -- _errs shouldn't happen, as large messages would have caused createNewSndMessage to throw SELargeMsg + (_errs, msgBatches) = partitionEithers batched + case L.nonEmpty msgBatches of + Just msgBatches' -> do + let msgReqsBatched = foldr (\(_, conn) reqs -> foldr (\batch -> (msgBatchReq conn msgFlags batch :)) reqs msgBatches') [] toSendBatched + msgReqsSeparate <> msgReqsBatched + Nothing -> msgReqsSeparate + where + sndMessageReq :: Connection -> SndMessage -> ChatMsgReq + sndMessageReq conn SndMessage {msgId, msgBody} = (conn, msgFlags, msgBody, [msgId]) + createPendingMsgs :: DB.Connection -> GroupMember -> NonEmpty SndMessage -> IO () + createPendingMsgs db m = mapM_ (\SndMessage {msgId} -> createPendingGroupMessage db (groupMemberId' m) msgId Nothing) -data MemberSendAction = MSASend Connection | MSAPending | MSAForwarded +data MemberSendAction = MSASend Connection | MSASendBatched Connection | MSAPending | MSAForwarded -memberSendAction :: GroupInfo -> ChatMsgEvent e -> [GroupMember] -> GroupMember -> Maybe MemberSendAction -memberSendAction gInfo chatMsgEvent members m = case memberConn m of +memberSendAction :: GroupInfo -> NonEmpty (ChatMsgEvent e) -> [GroupMember] -> GroupMember -> Maybe MemberSendAction +memberSendAction gInfo events members m@GroupMember {memberRole} = case memberConn m of Nothing -> pendingOrForwarded Just conn@Connection {connStatus} | connDisabled conn || connStatus == ConnDeleted -> Nothing | connInactive conn -> Just MSAPending - | connStatus == ConnSndReady || connStatus == ConnReady -> Just (MSASend conn) + | connStatus == ConnSndReady || connStatus == ConnReady -> sendBatchedOrSeparate conn | otherwise -> pendingOrForwarded where + sendBatchedOrSeparate conn + -- admin doesn't support batch forwarding - send messages separately so that admin can forward one by one + | memberRole >= GRAdmin && not (m `supportsVersion` batchSend2Version) = Just (MSASend conn) + -- either member is not admin, or admin supports batched forwarding + | otherwise = Just (MSASendBatched conn) pendingOrForwarded = case memberCategory m of GCUserMember -> Nothing -- shouldn't happen GCInviteeMember -> Just MSAPending @@ -6903,8 +7046,8 @@ memberSendAction gInfo chatMsgEvent members m = case memberConn m of GCPostMember -> forwardSupportedOrPending (invitedByGroupMemberId m) where forwardSupportedOrPending invitingMemberId_ - | membersSupport && isForwardedGroupMsg chatMsgEvent = Just MSAForwarded - | isXGrpMsgForward = Nothing + | membersSupport && all isForwardedGroupMsg events = Just MSAForwarded + | any isXGrpMsgForward events = Nothing | otherwise = Just MSAPending where membersSupport = @@ -6916,7 +7059,7 @@ memberSendAction gInfo chatMsgEvent members m = case memberConn m of Just invitingMember -> invitingMember `supportsVersion` groupForwardVersion Nothing -> False Nothing -> False - isXGrpMsgForward = case chatMsgEvent of + isXGrpMsgForward event = case event of XGrpMsgForward {} -> True _ -> False @@ -6926,8 +7069,9 @@ sendGroupMemberMessage user gInfo@GroupInfo {groupId} m@GroupMember {groupMember messageMember msg `catchChatError` (toView . CRChatError (Just user)) where messageMember :: SndMessage -> CM () - messageMember SndMessage {msgId, msgBody} = forM_ (memberSendAction gInfo chatMsgEvent [m] m) $ \case + messageMember SndMessage {msgId, msgBody} = forM_ (memberSendAction gInfo (chatMsgEvent :| []) [m] m) $ \case MSASend conn -> deliverMessage conn (toCMEventTag chatMsgEvent) msgBody msgId >> postDeliver + MSASendBatched conn -> deliverMessage conn (toCMEventTag chatMsgEvent) msgBody msgId >> postDeliver MSAPending -> withStore' $ \db -> createPendingGroupMessage db groupMemberId msgId introId_ MSAForwarded -> pure () @@ -6937,7 +7081,7 @@ sendPendingGroupMessages user GroupMember {groupMemberId} conn = do pgms <- withStore' $ \db -> getPendingGroupMessages db groupMemberId forM_ (L.nonEmpty pgms) $ \pgms' -> do let msgs = L.map (\(sndMsg, _, _) -> sndMsg) pgms' - batchSendGroupMemberMessages user conn msgs + batchSendConnMessages user conn MsgFlags {notification = True} msgs lift . void . withStoreBatch' $ \db -> L.map (\SndMessage {msgId} -> deletePendingGroupMessage db groupMemberId msgId) msgs lift . void . withStoreBatch' $ \db -> L.map (\(_, tag, introId_) -> updateIntro_ db tag introId_) pgms' where @@ -6946,19 +7090,14 @@ sendPendingGroupMessages user GroupMember {groupMemberId} conn = do (ACMEventTag _ XGrpMemFwd_, Just introId) -> updateIntroStatus db introId GMIntroInvForwarded _ -> pure () --- TODO [batch send] refactor direct message processing same as groups (e.g. checkIntegrity before processing) -saveDirectRcvMSG :: Connection -> MsgMeta -> MsgBody -> CM (Connection, RcvMessage) -saveDirectRcvMSG conn@Connection {connId} agentMsgMeta msgBody = - case parseChatMessages msgBody of - [Right (ACMsg _ ChatMessage {chatVRange, msgId = sharedMsgId_, chatMsgEvent})] -> do - conn' <- updatePeerChatVRange conn chatVRange - let agentMsgId = fst $ recipient agentMsgMeta - newMsg = NewRcvMessage {chatMsgEvent, msgBody} - rcvMsgDelivery = RcvMsgDelivery {connId, agentMsgId, agentMsgMeta} - msg <- withStore $ \db -> createNewMessageAndRcvMsgDelivery db (ConnectionId connId) newMsg sharedMsgId_ rcvMsgDelivery Nothing - pure (conn', msg) - [Left e] -> error $ "saveDirectRcvMSG: error parsing chat message: " <> e - _ -> error "saveDirectRcvMSG: batching not supported" +saveDirectRcvMSG :: MsgEncodingI e => Connection -> MsgMeta -> MsgBody -> ChatMessage e -> CM (Connection, RcvMessage) +saveDirectRcvMSG conn@Connection {connId} agentMsgMeta msgBody ChatMessage {chatVRange, msgId = sharedMsgId_, chatMsgEvent} = do + conn' <- updatePeerChatVRange conn chatVRange + let agentMsgId = fst $ recipient agentMsgMeta + newMsg = NewRcvMessage {chatMsgEvent, msgBody} + rcvMsgDelivery = RcvMsgDelivery {connId, agentMsgId, agentMsgMeta} + msg <- withStore $ \db -> createNewMessageAndRcvMsgDelivery db (ConnectionId connId) newMsg sharedMsgId_ rcvMsgDelivery Nothing + pure (conn', msg) saveGroupRcvMsg :: MsgEncodingI e => User -> GroupId -> GroupMember -> Connection -> MsgMeta -> MsgBody -> ChatMessage e -> CM (GroupMember, Connection, RcvMessage) saveGroupRcvMsg user groupId authorMember conn@Connection {connId} agentMsgMeta msgBody ChatMessage {chatVRange, msgId = sharedMsgId_, chatMsgEvent} = do @@ -7029,59 +7168,85 @@ mkChatItem cd ciId content file quotedItem sharedMsgId itemForwarded itemTimed l meta = mkCIMeta ciId content itemText itemStatus Nothing sharedMsgId itemForwarded Nothing False itemTimed (justTrue live) currentTs itemTs forwardedByMember currentTs currentTs in ChatItem {chatDir = toCIDirection cd, meta, content, formattedText = parseMaybeMarkdownList itemText, quotedItem, reactions = [], file} -deleteDirectCI :: MsgDirectionI d => User -> Contact -> ChatItem 'CTDirect d -> Bool -> Bool -> CM ChatResponse -deleteDirectCI user ct ci@ChatItem {file} byUser timed = do - deleteCIFile user file - withStore' $ \db -> deleteDirectChatItem db user ct ci - pure $ CRChatItemDeleted user (AChatItem SCTDirect msgDirection (DirectChat ct) ci) Nothing byUser timed - -deleteGroupCI :: MsgDirectionI d => User -> GroupInfo -> ChatItem 'CTGroup d -> Bool -> Bool -> Maybe GroupMember -> UTCTime -> CM ChatResponse -deleteGroupCI user gInfo ci@ChatItem {file} byUser timed byGroupMember_ deletedTs = do - deleteCIFile user file - toCi <- withStore' $ \db -> - case byGroupMember_ of - Nothing -> deleteGroupChatItem db user gInfo ci $> Nothing - Just m -> Just <$> updateGroupChatItemModerated db user gInfo ci m deletedTs - pure $ CRChatItemDeleted user (gItem ci) (gItem <$> toCi) byUser timed +deleteDirectCIs :: User -> Contact -> [CChatItem 'CTDirect] -> Bool -> Bool -> CM ChatResponse +deleteDirectCIs user ct items byUser timed = do + let ciFilesInfo = mapMaybe (\(CChatItem _ ChatItem {file}) -> mkCIFileInfo <$> file) items + deleteCIFiles user ciFilesInfo + (errs, deletions) <- lift $ partitionEithers <$> withStoreBatch' (\db -> map (deleteItem db) items) + unless (null errs) $ toView $ CRChatErrors (Just user) errs + pure $ CRChatItemsDeleted user deletions byUser timed where - gItem = AChatItem SCTGroup msgDirection (GroupChat gInfo) + deleteItem db (CChatItem md ci) = do + deleteDirectChatItem db user ct ci + pure $ contactDeletion md ct ci Nothing -deleteLocalCI :: MsgDirectionI d => User -> NoteFolder -> ChatItem 'CTLocal d -> Bool -> Bool -> CM ChatResponse -deleteLocalCI user nf ci@ChatItem {file = file_} byUser timed = do - forM_ file_ $ \file -> do - let filesInfo = [mkCIFileInfo file] - deleteFilesLocally filesInfo - withStore' $ \db -> deleteLocalChatItem db user nf ci - pure $ CRChatItemDeleted user (AChatItem SCTLocal msgDirection (LocalChat nf) ci) Nothing byUser timed - -deleteCIFile :: MsgDirectionI d => User -> Maybe (CIFile d) -> CM () -deleteCIFile user file_ = - forM_ file_ $ \file -> do - let filesInfo = [mkCIFileInfo file] - cancelFilesInProgress user filesInfo - deleteFilesLocally filesInfo - -markDirectCIDeleted :: MsgDirectionI d => User -> Contact -> ChatItem 'CTDirect d -> MessageId -> Bool -> UTCTime -> CM ChatResponse -markDirectCIDeleted user ct ci@ChatItem {file} msgId byUser deletedTs = do - cancelCIFile user file - ci' <- withStore' $ \db -> markDirectChatItemDeleted db user ct ci msgId deletedTs - pure $ CRChatItemDeleted user (ctItem ci) (Just $ ctItem ci') byUser False +deleteGroupCIs :: User -> GroupInfo -> [CChatItem 'CTGroup] -> Bool -> Bool -> Maybe GroupMember -> UTCTime -> CM ChatResponse +deleteGroupCIs user gInfo items byUser timed byGroupMember_ deletedTs = do + let ciFilesInfo = mapMaybe (\(CChatItem _ ChatItem {file}) -> mkCIFileInfo <$> file) items + deleteCIFiles user ciFilesInfo + (errs, deletions) <- lift $ partitionEithers <$> withStoreBatch' (\db -> map (deleteItem db) items) + unless (null errs) $ toView $ CRChatErrors (Just user) errs + pure $ CRChatItemsDeleted user deletions byUser timed where - ctItem = AChatItem SCTDirect msgDirection (DirectChat ct) + deleteItem :: DB.Connection -> CChatItem 'CTGroup -> IO ChatItemDeletion + deleteItem db (CChatItem md ci) = do + ci' <- case byGroupMember_ of + Just m -> Just <$> updateGroupChatItemModerated db user gInfo ci m deletedTs + Nothing -> Nothing <$ deleteGroupChatItem db user gInfo ci + pure $ groupDeletion md gInfo ci ci' -markGroupCIDeleted :: MsgDirectionI d => User -> GroupInfo -> ChatItem 'CTGroup d -> MessageId -> Bool -> Maybe GroupMember -> UTCTime -> CM ChatResponse -markGroupCIDeleted user gInfo ci@ChatItem {file} msgId byUser byGroupMember_ deletedTs = do - cancelCIFile user file - ci' <- withStore' $ \db -> markGroupChatItemDeleted db user gInfo ci msgId byGroupMember_ deletedTs - pure $ CRChatItemDeleted user (gItem ci) (Just $ gItem ci') byUser False +deleteLocalCIs :: User -> NoteFolder -> [CChatItem 'CTLocal] -> Bool -> Bool -> CM ChatResponse +deleteLocalCIs user nf items byUser timed = do + let ciFilesInfo = mapMaybe (\(CChatItem _ ChatItem {file}) -> mkCIFileInfo <$> file) items + deleteFilesLocally ciFilesInfo + (errs, deletions) <- lift $ partitionEithers <$> withStoreBatch' (\db -> map (deleteItem db) items) + unless (null errs) $ toView $ CRChatErrors (Just user) errs + pure $ CRChatItemsDeleted user deletions byUser timed where - gItem = AChatItem SCTGroup msgDirection (GroupChat gInfo) + deleteItem db (CChatItem md ci) = do + deleteLocalChatItem db user nf ci + pure $ ChatItemDeletion (nfItem md ci) Nothing + nfItem :: MsgDirectionI d => SMsgDirection d -> ChatItem 'CTLocal d -> AChatItem + nfItem md = AChatItem SCTLocal md (LocalChat nf) -cancelCIFile :: MsgDirectionI d => User -> Maybe (CIFile d) -> CM () -cancelCIFile user file_ = - forM_ file_ $ \file -> do - let filesInfo = [mkCIFileInfo file] - cancelFilesInProgress user filesInfo +deleteCIFiles :: User -> [CIFileInfo] -> CM () +deleteCIFiles user filesInfo = do + cancelFilesInProgress user filesInfo + deleteFilesLocally filesInfo + +markDirectCIsDeleted :: User -> Contact -> [CChatItem 'CTDirect] -> Bool -> UTCTime -> CM ChatResponse +markDirectCIsDeleted user ct items byUser deletedTs = do + let ciFilesInfo = mapMaybe (\(CChatItem _ ChatItem {file}) -> mkCIFileInfo <$> file) items + cancelFilesInProgress user ciFilesInfo + (errs, deletions) <- lift $ partitionEithers <$> withStoreBatch' (\db -> map (markDeleted db) items) + unless (null errs) $ toView $ CRChatErrors (Just user) errs + pure $ CRChatItemsDeleted user deletions byUser False + where + markDeleted db (CChatItem md ci) = do + ci' <- markDirectChatItemDeleted db user ct ci deletedTs + pure $ contactDeletion md ct ci (Just ci') + +markGroupCIsDeleted :: User -> GroupInfo -> [CChatItem 'CTGroup] -> Bool -> Maybe GroupMember -> UTCTime -> CM ChatResponse +markGroupCIsDeleted user gInfo items byUser byGroupMember_ deletedTs = do + let ciFilesInfo = mapMaybe (\(CChatItem _ ChatItem {file}) -> mkCIFileInfo <$> file) items + cancelFilesInProgress user ciFilesInfo + (errs, deletions) <- lift $ partitionEithers <$> withStoreBatch' (\db -> map (markDeleted db) items) + unless (null errs) $ toView $ CRChatErrors (Just user) errs + pure $ CRChatItemsDeleted user deletions byUser False + where + markDeleted db (CChatItem md ci) = do + ci' <- markGroupChatItemDeleted db user gInfo ci byGroupMember_ deletedTs + pure $ groupDeletion md gInfo ci (Just ci') + +groupDeletion :: MsgDirectionI d => SMsgDirection d -> GroupInfo -> ChatItem 'CTGroup d -> Maybe (ChatItem 'CTGroup d) -> ChatItemDeletion +groupDeletion md g ci ci' = ChatItemDeletion (gItem ci) (gItem <$> ci') + where + gItem = AChatItem SCTGroup md (GroupChat g) + +contactDeletion :: MsgDirectionI d => SMsgDirection d -> Contact -> ChatItem 'CTDirect d -> Maybe (ChatItem 'CTDirect d) -> ChatItemDeletion +contactDeletion md ct ci ci' = ChatItemDeletion (ctItem ci) (ctItem <$> ci') + where + ctItem = AChatItem SCTDirect md (DirectChat ct) createAgentConnectionAsync :: ConnectionModeI c => User -> CommandFunction -> Bool -> SConnectionMode c -> SubscriptionMode -> CM (CommandId, ConnId) createAgentConnectionAsync user cmdFunction enableNtfs cMode subMode = do @@ -7392,6 +7557,7 @@ chatCommandP = enableSndFiles <- " snd_files=" *> onOffP <|> pure mainApp pure StartChat {mainApp, enableSndFiles}, "/_start" $> StartChat True True, + "/_check running" $> CheckChatRunning, "/_stop" $> APIStopChat, "/_app activate restore=" *> (APIActivateChat <$> onOffP), "/_app activate" $> APIActivateChat True, @@ -7433,8 +7599,8 @@ chatCommandP = "/_send " *> (APISendMessage <$> chatRefP <*> liveMessageP <*> sendMessageTTLP <*> (" json " *> jsonP <|> " text " *> (ComposedMessage Nothing Nothing <$> mcTextP))), "/_create *" *> (APICreateChatItem <$> A.decimal <*> (" json " *> jsonP <|> " text " *> (ComposedMessage Nothing Nothing <$> mcTextP))), "/_update item " *> (APIUpdateChatItem <$> chatRefP <* A.space <*> A.decimal <*> liveMessageP <* A.space <*> msgContentP), - "/_delete item " *> (APIDeleteChatItem <$> chatRefP <* A.space <*> A.decimal <* A.space <*> ciDeleteMode), - "/_delete member item #" *> (APIDeleteMemberChatItem <$> A.decimal <* A.space <*> A.decimal <* A.space <*> A.decimal), + "/_delete item " *> (APIDeleteChatItem <$> chatRefP <*> _strP <* A.space <*> ciDeleteMode), + "/_delete member item #" *> (APIDeleteMemberChatItem <$> A.decimal <*> _strP), "/_reaction " *> (APIChatItemReaction <$> chatRefP <* A.space <*> A.decimal <* A.space <*> onOffP <* A.space <*> jsonP), "/_forward " *> (APIForwardChatItem <$> chatRefP <* A.space <*> chatRefP <* A.space <*> A.decimal <*> sendMessageTTLP), "/_read user " *> (APIUserRead <$> A.decimal), @@ -7830,8 +7996,8 @@ adminContactReq :: ConnReqContact adminContactReq = either error id $ strDecode "simplex:/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D" -simplexContactProfile :: Profile -simplexContactProfile = +simplexTeamContactProfile :: Profile +simplexTeamContactProfile = Profile { displayName = "SimpleX Chat team", fullName = "", @@ -7840,6 +8006,16 @@ simplexContactProfile = preferences = Nothing } +simplexStatusContactProfile :: Profile +simplexStatusContactProfile = + Profile + { displayName = "SimpleX-Status", + fullName = "", + image = Just (ImageData "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAr6ADAAQAAAABAAAArwAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgArwCvAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAQEBAQEBAgEBAgMCAgIDBAMDAwMEBgQEBAQEBgcGBgYGBgYHBwcHBwcHBwgICAgICAkJCQkJCwsLCwsLCwsLC//bAEMBAgICAwMDBQMDBQsIBggLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLC//dAAQAC//aAAwDAQACEQMRAD8A/v4ooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/Q/v4ooooAKKKKACiiigAoorE8R+ItF8J6Jc+IvEVwlrZ2iGSWWQ4CgVUISlJRirtmdatTo05VaslGMU223ZJLVtvokbdFfl3of/BRbS734rtpup2Ig8LSsIYrjnzkOcea3bafTqBX6cafqFjq1jFqemSrPbzqHjkQ5VlPIINetm2Q43LXD65T5eZXX+XquqPiuC/Efh/itYh5HiVUdGTjJWaflJJ6uEvsy2fqXKKKK8c+5Ciq17e2mnWkl/fyLDDCpd3c4VVHJJJr8c/2kf8Ago34q8M3mpTfByG3fT7CGSJZrlC3nStwJF5GFU8gd69LA5VicXTrVaMfdpxcpPokk397toj4LjvxKyLhGjRqZxValVkowhFc05O9m0tPdjfV7dN2kfq346+J3w9+GWlPrXxA1m00i1QZL3Uqxj8Mnn8K/Mj4tf8ABYD4DeEJ5dM+Gmn3niq4TIE0YEFtn/ffBI+imv51vHfxA8b/ABR1+bxT8RNUuNXvp3LtJcOWCk84VeigdgBXI18LXzupLSkrL72fzrxH9IXNsTKVPKKMaMOkpe/P8fdXpaXqfqvrf/BYH9p6+1w3+iafo1jZA8WrRPKSPeTcpz9BX1l8J/8Ags34PvxDp/xn8M3OmSnAe709hcQfUoSHA/A1/PtSE4/GuKGZ4mLvz39T4TL/ABe4swlZ1ljpTvvGaUo/dbT/ALdsf2rfCX9pT4HfHGzF18M/EdnqTYBaFXCzJn+9G2GH5V7nX8IOm6hqGkX8eraLcy2d3EcpPbuY5FPsykGv6gf+CWf7QPxB+OPwX1Ky+JF22pX3h69+yJdyf62WJlDrvPdlzjPevdwGae3l7OcbP8D+i/DTxm/1ixkcqx2H5K7TalF3jLlV2rPWLtqtWvM/T2iiivYP3c//0f7+KKKKACiiigAooooAK/Fv/goX8Qvi2fFcXgfWrRtP8NDEls0bZS7YfxORxlT0Xt1r9pK8u+L/AMI/Cfxp8F3HgvxbFujlGYpgB5kMg6Op9R+tfR8K5vQy3MYYnE01KK0843+0vNf8NZn5f4wcFZhxTwziMpy3FOjVeqSdo1Lf8u5u11GXk97Xuro/mBFyDX3t+yL+2Be/CW+h8B+OHafw7cyALIxJa0Ldx6p6jt1FfMvx/wDgR4w/Z+8YN4d8RoZrSbLWd4owk6D+TDuK8KF0K/pLFYHA51geWVp0pq6a/Brs1/wH2P8ALvJsz4h4D4h9tR5qGLoS5ZRls11jJbSjJferSi9mf1uafqFlqtlFqWmyrPBOoeORDlWU8gg069vrPTbSS/v5FhghUu7ucKqjqSa/CH9j79sm++EuoQ/D/wAeSNceHbmRVjlZstZk9x6p6jt2q3+15+2fffFS8n8AfD2V7bw9CxWWZThrwj+Se3evxB+G2Zf2n9TX8Lf2nTl/+S/u/PbU/v2P0nuGv9Vf7cf+9/D9Xv73tLd/+ffXn7afF7pqftbfth3nxUu5vAXgGR7fw/A5WWUHDXZX19E9B361+Z/xKm3eCL9R3UfzFbQul6Cn+I/A3ivxR8LPEXivSbVn07RoVkurg8Iu5gAue7HPSv1HOsrwmVcN4uhRSjBUp6vq3Fq7fVt/5I/gTNeI884x4kjmeYOVWtKSdop2hCPvWjFbQjFNv5ybbuz4Toqa0ge9uoLOIhWnkSNSxwAXIUEnsBnmv0+/aK/4Jg+O/gj8Hoviz4b1n/hJFt40l1G2ig2NDG4yZEIJ3KvfgHHNfxVTw9SpGUoK6W5+xZVw1mWZYfEYrA0XOFBKU2raJ31te72b0T0R+XRIAyegr+gr/glx+yZoHhjwBc/tKfFywiafUY2OmpeIGS3sVGWmIbgF+TkjhR71+YP7DX7Lt9+1H8ZLfR75WTw5pBS61ScDKsoIKwg+snf0Ffqd/wAFSv2o4Phf4Ltv2WvhmVtrjUbRBfvA2Ps1kOFhAHQyAc9ML9a9HL6UacHi6q0W3mz9Q8M8owuV4KvxpnEL0aN40Yv/AJeVXpp5LZPo7v7J+M/7U/jX4e/EL4/+JfFXwrsI9P0Ke5K26RKESTZw0oUcAOeQBX7J/wDBFU5+HPjYf9RWH/0SK/nqACgKOgr+hT/giouPh143b11SH/0SKWVzc8YpPrf8jHwexk8XxzSxVRJSn7WTSVknKMnoui7H7a0UUV9cf3Mf/9L+/iiiigAoorzX4wfGD4afAP4bav8AF74v6xbaD4d0K3e6vb26cJHHGgyevUnoAOSeBTjFyajFXYHpVFf55Xxt/wCDu34nj9vzS/G3wX0Qz/ArQ2ksLnSp1CXurQyMA15uPMTqBmJD2+914/uU/Y//AGxfgH+3P8ENL+P37OutxazoWpoNwHyzW02PmhmjPKSKeCD9RxXqY/JcXg4QqV4WUvw8n2ZnCrGTaTPqGiiivKNDy/4u/CLwd8afBtx4N8ZW4kilBMUoH7yGTs6HsR+tfzjftA/AXxl+z54yfw34jQzWkuXs7xF/dzR/0YdxX9OPiDxBofhPQ7vxN4mu4rDT7CF57m4ncJHFFGMszMcAAAZJNf53n/Bav/g5W1H4ufGjTvg5+xB5F14E8JX4l1HVriIE6xNE2GjhLDKQdRuGC55HHX9L8Os+x2ExP1eKcsO/iX8vmvPy6/ifg3jZ4NYDjDBPFUEqeYU17k/50vsT8n0lvF+V0fq0LhTUgnA4r4y/ZG/bJ+FX7YXw9HjDwBP5N/ahV1LTZeJrSUjoR3U/wsOK+sRdL/n/APXX9G0nCrBTpu6Z/mVmuSYvLcXUwOPpOnWg7SjJWaf9ap7NarQ+pf2dP2evGH7Q3i4aLogNvp1uQ15esMpEnoPVj2Ffrd+1V8GvDnw5/YU8X+APh/Z7IrewEjYGXlZGUs7nqSQM18C/sO/ti6b8F7o/Dnx6qpoN9LvS6RRvglbjL45ZT69vpX7wX1poHjjwxNYzbL3TdUt2jbaQySRSrg4PoQa/nnxXxGaTxLwmIjy4e3uW2lpu33Xbp87v+7Po58I8L4nhfFVMuqKeY1oTp1nJe9S5k0oxWtoPfmXxve1uVfwqKA0YHYiv6Ev+CZ37bVv490eP9mb4zXAn1GKJo9Murg5F3bgYMLk9XUcD+8tflR+1/wDsn+Nv2XfiNdadqFs8vh28md9Mv1GY3iJyEY9nXoQa+UrC/v8ASr+DVdJnktbq2dZYZomKvG6nIZSOhFfztQrVMJW1Xqu5+Z8PZ5mvBWeSc4NSg+WrTeinHqv1jL56ptP+s7xHZ/A//gnR8EfE/jTwra+RHqF5JdxWpbLTXcwwkSnrsGPwXNfyrfEDx54l+J/jXU/iB4wna51LVZ3nmdj3Y8KPQKOAPQV2vxX/AGhvjT8corC3+K2vz6vFpq7beNgERT3YqvBY92NeNVeOxirNRpq0Fsju8RePKWfTo4TLqPscFRXuU9F7z+KTSuvJK7srvqwr+ir/AIIuaVd2/wAH/FesSIRDd6uFjb+8Y41Dfka/BX4YfCzx78ZfGVr4C+G+nyajqV22Aqj5I17u7dFUdya/r+/ZV+Aenfs2fBLSPhbZyC4ntVaW7nAx5tzKd0jfTJwPYV1ZLQk63tbaI+w8AOHcXiM8ebcjVClGS5ujlJWUV3sm27baX3R9FUUUV9Uf2gf/0/7+KKKKACv4If8Ag8QT9vN9W8IsVk/4Z+WJedOL7f7Xyd32/HGNu3yc/LnPev73q84+Lnwj+G/x3+HGr/CT4uaRba74d123e1vbK6QPHJG4weD0I6gjkHkV6WUY9YLFQxDgpJdP8vMipDmi0f4W1frt/wAEhP8Agrt8af8AglD8b38V+Fo21zwPr7xp4i0B3KpcRoeJoTyEnjBO04+boeK+m/8AguZ/wQz+I3/BMD4kyfEn4Ww3fiD4Oa5KzWWolC76XKx4tbphwOuI3PDAc81/PdX7LCeFzHC3VpU5f18mjympU5eZ/t9fsk/tb/Av9tv4G6N+0F+z3rUWs6BrEQYFCPNt5cfPDMnVJEPDKf5V794h8Q6F4T0O78TeJ7uGw06wiae4uZ3EcUUaDLMzHAAA6k1/j9f8EiP+Cunxv/4JTfHAeKPCZfWfAuuyRx+IvD8jkRTxg486Lsk8YJ2n+Loa/V7/AILy/wDBxZd/t2eHl/Zc/Y6mu9I+Gl1DDNrWoSBoLvUpGAY2+OqQoeH/AL5GOlfneI4OxCxio0taT+12Xn59u53xxMeW73ND/g4M/wCDgzVP2yNV1H9jz9j3UZrD4ZWE7waxrEDlH110ONiEYItgQe/7z6V/I6AAMDgCgAKNo6Cv0j/4Jkf8Ex/j/wD8FOvj/Y/Cj4UWE9voFvNGdf18xk2um2pPzEt0MhGdiZyTX6FhsNhctwvLH3YR1bfXzfn/AEjhlKVSR77/AMEMf2Rf2v8A9qr9tPRrb9mNpdL0fSp438UaxKjNYW+nk/PHKOA7uoIjTrnniv7Lfj98CvG37PPjiXwj4uiLxNl7S7UYjuIuzD39R1Ffvt+wn+wd+z5/wTy+A+n/AAF/Z70pbKyt1V728cA3V/c4w0079WYnoOijgV7V8cPgb4G+Pngqfwb41twwYEwXCgebBJ2ZT/MdDXi5N4mTwmYWqRvhXpb7S/vL9V28z8c8YfBXC8XYL61hbQx9Ne7LpNfyT8v5ZfZfkfyXi5r9Lf2Jv24bn4S3UHwz+JkzT+HZ5AsNy5LNZlu3vHn8q+KPj38CPHf7PPjabwn4yt2ELMxtLsD91cRg8Mp6Z9R2rxAXAPANfuePyzL89y/2c7TpTV1JdOzT6Nf8Bn8C5FnGfcEZ79Yw96OJpPlnCS0a6xkusX/k4u9mf2IeK/B/w++Mngt9C8U2ltrWi6lEGCuA6OrDhlPY+hHNfztftw/8E4tN+AGlTfE34ba3HJo0koVdMvGC3CFv4Ym/5aAenBArvf2PP2+9R+CGmv4B+JSy6joEUbtaOp3TQOBkRj1Rjx7V8uftEftH+Nf2i/G7+KPEzmG0hyllZqT5cEef1Y9zX4LT8GMTisynhsY7UI6qot5J7Jefe+i87o/prxI8YuEM/wCF6WM+rc2ZSXKo6qVJrdykvih/Ktebsmnb4DkilicxyqVYdQRzXUaN4R1HVMSzjyIf7zDk/QV6dIlpJIJ5Y1Z16MRk1+qf7DX7Ed58ULmH4p/Fe2kt/D8Dq9paSDabwjncf+mf/oX0rKXg3lOR+0zDPMW6lCL92EVyufZN3vfyjbvdI/AeFsJnHFOPp5TktD97L4pP4YLrJu2iXnq3ok20es/8Erv2f/G/gf8AtD4ozj7Bo2pwiFIpY/3t2VOQ4J5VFzx659q/aKq9paWthax2VlGsUMShERBtVVHAAA6AVYr4LNcdTxWIdSjRjSpqyjGKslFber7t6tn+k3APB1LhjJaOUUqsqjjdylJ/FKTvJpfZV9orbzd2yiiivNPsj//U/v4ooooAKKKKAPO/iz8Jvh18c/h1q/wm+LGk2+ueHtdt3tb2yukDxyxuMEEHoR1B6g81/lm/8Fy/+CFfxG/4Jh/ENvid8J4bzxF8Htdmke1vliaRtHctxbXTAEBecRyHAbGDzX+q54j8R6B4Q0C88U+KbyHT9N0+F7i5ubhxHFFFGMszMcAADqa/zM/+Dhb/AIL06p+3f4rvP2Tf2Xr6S0+Eui3DR397GcHXriM8N7W6EfIP4jz6V9fwfPGLFctD+H9q+3/D9jmxKjy+9ufyq0UAY4or9ZPMP0v/AOCX3/BLf9oT/gqP8d4Phf8ACa0lsvDtjLG3iDxDJGTa6bbse56NKwB8uPOSfav9ZX9hD9hT4Df8E8v2fdK/Z7+AenLbWNkoe8vHUfab+6I+eeZhyWY9B0UcCv8AKC/4JUf8FV/j1/wSu+PCfEf4aSHUvC+rPHH4i0CViIL63U43D+7MgJKN+B4r/Wd/Yy/bM+BH7eHwH0j9oL9n7Vo9S0fU4182LI8+0nx88MydVdTxz16ivzbjZ43nipfwOlu/n59uh6GE5Labn1ZRRRXwB2Hi3x3+BPgj9oHwJceCPGcIIYFre4UfvYJezKf5jvX8vH7QvwB8d/s4eOZfB/jKEtDIS9neKP3VxFngqfX1Hav6gvj58e/An7PHgK48ceN7gLtBW2twf3txL2RR/M9hX8rX7Qn7Rnjz9o3x5L418ZyhUXKWlqh/dW8WeFUevqe5r988G4Zu3Ut/ueu/839z/wBu6fM/jj6UdPhlwo8y/wCFTS3Lb+H/ANPf/bPtf9unlQuAec077SPWueFznrTxc1+/eyP4udE/XX9g79h24+K8tv8AF74qQvD4fgkDWdo64N4V53H/AKZg/wDfX0r+ge0tLWwtY7KyjWKGJQiIgwqqOAAOwFfzc/sIft2XnwO1KH4ZfEeVp/Ct5L8k7Es9k7YHH/TMnkjt1r+kDTNT07WtOg1fSJ0ubW5QSRSxncjowyCCOoNfyr4q0s3jmreYfwtfZW+Hl/8Akv5r6/Kx/or9HSXDX+rqhkqtidPb81vac/d/3P5Lab/auXqKKK/Lz+gwooooA//V/v4ooooAKxfEniTQPB2gXnirxVew6dpunQvcXV1cOI4oYoxlndjgAADJJrar/PV/4Ozf+CiX7Xlr8Yrf9hCx0u98GfDaS0iv5L1GZT4iZs5HmKceTERgx9d3LcYr08py2eOxMaEXbu/L9SKk1CN2fIX/AAcD/wDBfrXv27vFF1+yx+ylqFzpnwl0id476+icxSa/MhwGOMEWykHYv8fU9hX8qoAAwOAKUAAYFfqj/wAEnf8AglH8cv8Agqp8ek+Hvw/R9M8I6NJFJ4k19lzHZW7k/ImeGmcAhF/E8V+xUKGFyzC2Xuwju/1fds8tuVSXmM/4JQ/8Epfjr/wVU+Pcfw5+HiPpXhPSXjl8ReIZEJhsoGP3E7PO4B2J+J4r7o/4Li/8EC/H3/BL/UYPjH8Hp7vxV8JNQMcL3sy7rnTLkgDbcFRjZI3KPwATg9q/0rP2MP2MPgL+wZ8BdI/Z5/Z60hNM0bS4x5kpANxeTn7887gAvI55JPToOK9y+J/ww8AfGfwBqvwu+KOlW+t6Brdu9re2V0gkilicYIIP6HqDXwVbjSu8YqlNfulpy9139e3Y7VhY8tnuf4VdfqD/AMErP+Cpvx1/4Jb/ALQNn8S/h7cS6j4VvpUj8QeH2kIt723zgsB0WVRyjetffn/BeH/ghJ4x/wCCZvjlvjP8EYbvXPg5rk7GKcqZJdGmc5FvOwH+rOcRyH0wea/nCr9ApVcNmOGuvehL+vk0cLUqcvM/24v2Mf20PgH+3l8CdK/aA/Z61iPVNI1FF86LI+0Wc+PnhnTqjqeOevUcV3nx/wD2gfh/+zp4CuPHHjq5CBQVtrZT+9uJeyIP5noBX+Ud/wAEL/25f2t/2NP2u7A/s7xPrPhzW5Yk8T6LOzCyls1PzTE9I5UXJRupPHIr+p39o79pXx/+0v8AEGbxv42l2RrlLO0QnyreLPCqPX1PUmvM4b8KauYZg5VJWwkdW/tP+6vPu+i8z8r8VvF3D8L4P6vhbTx017sekF/PL/21fafkjV/aF/aN8e/tHePZ/GvjOc+XuK2lopPlW8WeFUevqe9eFfasDmsL7UB1r9kv+Cen/BPuX4mPa/Gv41Wrw6HE4k0/T5FwbsjkO4PPl56D+L6V/QWbZjlnDmW+1q2hSgrRit2+kYrq/wDh2fw9kXDmdcZ526NK9SvUfNOctorrKT6JdF6JIh/Yq/4JyXXxq8MSfEn4wtPpukXkLLp1vH8s0hYcTHPRR1Ud6+KP2nP2bvHX7MXj+Twl4pUz2U+Xsb5QRHcRZ/Rh/Etf2D2trbWNtHZ2caxRRKEREGFVRwAAOgFeSfHL4G+Af2gvAVz4A8f2wmt5huimUDzYJB0dD2I/Wv5/yrxgx0c3niMcr4abtyL7C6OPdrr/ADeWlv604g+jdlFTh6ngsrfLjaauqj/5eS6xn2i/s2+Hz1v/ABi+d3r9O/2DP28r/wCBGpRfDT4lSvdeFL2UBJmYs9izcZX1j7kduor48/ah/Zr8bfsu/EWTwZ4pHn2c4MtheqMJcQ5IB9mHRhXzd9oAFf0Djsuy3iHLeSdqlGorpr8Gn0a/4DW6P5DyrMc74Mzz2tG9LE0XaUXs11jJdYv/ACaezP7pdK1bTNd02DWdGnS6tLlBJFLEwZHRuQQR1FaFfix/wSG1n47X3hPVLHXUL+BoT/oEtxneLjPzLD6pjr2B6d6/aev424nyP+yMyrZf7RT5Huvv17NdV0Z/pTwPxP8A6w5Lh82dGVJ1FrGXdaNp9YveL6oKKKK8A+sP/9b+/iiiigAr4E/4KI/8E4f2b/8AgpZ8DLr4M/H7SklljV5NJ1aJQLzTblhxLC/Uc43L0YcGvvuitKNadKaqU3aS2Ymk1Zn+Vt8Nf+DZH9vDxJ/wUEn/AGQfGti+m+DdMkF5eeNlTNjLpRb5Xgz964cfL5XVWyTx1/0lv2L/ANif9nv9gn4H6b8Bv2dNDh0jSrFF8+YKDcXs4GGmuJOskjHPJ6dBxX1lgZz3pa9bNc+xWPjGFV2iui6vu/60M6dKMNgooorxTU4T4m/DHwB8ZfAeqfDH4paRba7oGtQPbXtjeRiWGaJxghlII/wr/M//AOCw/wDwbq/En9kb9o7Ttc/ZhQ6h8KvGl4VgknkUyaJIxy0UmTueMDmNgCexr/SN/aA/aA+Hf7N3w6u/iL8RbtYYIFIggBHm3Ev8Mca9yfyA5NfyB/tTftZfEX9qv4gSeL/GEv2exgLJYWEZPlW8WeOO7H+Ju9fsXhRwnmOZYl4hNwwi+Jv7T/lj5930Xnofj3iv4nYThrCPD0bTxs17kekV/PPy7L7T8rn58fs1fs1/Df8AZg8Dp4U8CwB7qYK19fuAZrmQDkseyjsvQV9GfaWrAWcjvUnnt6mv62w+Cp0KapUo2itkfwFmOLxWPxNTGYyo51Zu8pN6t/1stktEftx/wTa/YHsfi6sHx2+L8aT6BFJnT7DcGFy6dWlAzhQf4T171/SBaWltY20dlZRrFDEoREQYVVHAAA6AV/Hv+xJ+3N4y/ZO8Wi0ui+oeE9QkX7dYk5KdjLFzw49Ohr+tj4c/Efwb8WPB1l498A30eoaZqEYkiljOevVWHZh0IPIr+TPGXLs6p5p9Zxz5sO9KbXwxX8rXSXd/a3Wmi/t76P8AmHD08l+qZZDkxUdaydueT/mT0vDsl8Oz1d33FFFFfjR/QB4x8dPgN8O/2hvA1x4F+Idms8MgJhmAxLbydnjbqCP1r8RPg3/wSV8Z/wDC9r7T/izMreDNIlEkM8TYfUVPKpgcoAPv+/Ar+iKivrsh43zbKMLWweCq2hUXXXlf80eza0/HdJnwPFHhpkHEGOw+YZlQ5qlJ7rTnXSM/5op6/hs2jD8NeGdA8HaHbeGvC9nFYWFmgjhghUIiKOwArcoor5Oc5Tk5Sd292fd06cacVCCtFaJLZLsgoooqSz//1/7+KKKKACiiigAooooAK8J/aK/aG+H37M/wzvPiX8QrgRwwDbb26kebczH7saDuSep7DmvdW3bTt69s1/Hj/wAFS9c/acu/2hbiw+Psf2fTYWf+w47bd9ha2zw0ZPWQj7+eQfav0Dw44PpcRZssLXqqFOK5pK9pSS6RXfu+i1PzvxN4zrcN5PLF4ei51JPli7XjFv7U327Lq9Dwr9qv9rn4lftZ+Pv+Ev8AG8i29na7ksNPiJ8m2iJ7Ak5Y/wATHrXy/wDacDJNYfn45PFftR/wTX/4Ju6j8aryz+OXxttpLXwtbSrJY2Mi7W1Bl53MD0hB/wC+vpX9jZpmGU8LZT7WolTo01aMVu30jFdW/wDNvqz+HcryTOeLs4dODdSvUd5Tlsl1lJ9Eui9Elsix/wAE8/8Agmpc/Hq3HxZ+OcFxY+F8f6Daj93Jen++eMiMdum76V88ft4fsM+LP2RvGH9p6MJtS8G6gxNnfMMmFj/yxmIAAYfwnuPev7DbGxs9Ms4tP0+JYIIFCRxoNqqq8AADoBXL+P8AwB4R+KHhG+8C+OrGPUNM1CMxTQyjIIPcehHUEdDX8x4PxqzWOdvH11fDS0dJbKPRp/zrdvrtta39V47wCyWeQRy7D6YqOqrPeUuqkv5Hsl9ndXd7/wACwuGHevvT9iL9u7x1+yP4n+wMDqXhPUJVN/YMTlOxlh/uuB+BqH9vD9hXxl+yD4v/ALS03zNT8HajIfsV8VyYSf8AljNjgMOx/iHvX59C6bHav6fjDKeJsqurVcPVX9ecZRfzTP5LdLOeE850vRxNJ/15SjJfJo/v3+GnxJ8HfF3wRp/xC8BXiX2l6lEJYZEPr1Vh2YdCDyDXd1/PD/wRa8KftJW8moeKfPNp8N7kMBBdKT9ouR/Hbgn5QP4m6Gv6Hq/iHjXh6lkmb1svoVlUjF6Nbq/2ZdOZdbfhsf6AcC8SVs9yahmWIoOlOS1T2dvtR68r3V/x3ZRRRXyh9eFFFFABRRRQB//Q/v4ooooAKKKKACiiigAr5u/aj/Zg+HX7VvwyuPh14+i2N/rLO8jA861mHR0Pp2YdCOK+kaK6sDjq+DxEMVhZuFSDumt00cmOwOHxuHnhcVBTpzVpJ7NM/nF/ZW/4I2eINL+MV9rH7Rk0Vz4d0G5H2GCA8anjlXfuiDjcvJJ46V/RfY2FlpdlFpumxJBbwII444wFVEUYAAHAAFW6K9/injHM+Ia8a+Y1L8qsorSK7tLu3q3+iSPn+E+C8q4dw86GW07czvJvWT7Jvstkv1bYUUUV8sfVnEfEb4c+Dvix4Mv/AAB49sY9Q0vUYjFNDIMjB7j0YdQRyDX4HeH/APgiNJB+0LKNe1vzvhzARcxBeLyUEn/R27ADu46jtmv6KKK+r4d42zjI6Vajl1ZxjUVmt7P+aN9pW0uv0R8lxJwNk2e1aFfMqCnKk7p7XX8srbxvrZ/qzn/CnhXw/wCCPDll4R8K2sdlp2nQrBbwRDCoiDAAFdBRRXy05ynJzm7t6tvqfVwhGEVCCsloktkgoooqSgooooAKKKKAP//R/v4ooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/Z"), + contactLink = Just (either error id $ strDecode "simplex:/contact/#/?v=1-2&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FShQuD-rPokbDvkyotKx5NwM8P3oUXHxA%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEA6fSx1k9zrOmF0BJpCaTarZvnZpMTAVQhd3RkDQ35KT0%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion"), + preferences = Nothing + } + timeItToView :: String -> CM' a -> CM' a timeItToView s action = do t1 <- liftIO getCurrentTime diff --git a/src/Simplex/Chat/Bot.hs b/src/Simplex/Chat/Bot.hs index c5c5ff7eed..f3de92e1f2 100644 --- a/src/Simplex/Chat/Bot.hs +++ b/src/Simplex/Chat/Bot.hs @@ -3,6 +3,7 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedLists #-} module Simplex.Chat.Bot where @@ -73,9 +74,9 @@ sendComposedMessage' cc ctId quotedItemId msgContent = do deleteMessage :: ChatController -> Contact -> ChatItemId -> IO () deleteMessage cc ct chatItemId = do - let cmd = APIDeleteChatItem (contactRef ct) chatItemId CIDMInternal + let cmd = APIDeleteChatItem (contactRef ct) [chatItemId] CIDMInternal sendChatCmd cc cmd >>= \case - CRChatItemDeleted {} -> printLog cc CLLInfo $ "deleted message from " <> contactInfo ct + CRChatItemsDeleted {} -> printLog cc CLLInfo $ "deleted message(s) from " <> contactInfo ct r -> putStrLn $ "unexpected delete message response: " <> show r contactRef :: Contact -> ChatRef diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 66a4aca95d..59b91dc1c1 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -69,11 +69,11 @@ import Simplex.Chat.Types.UITheme import Simplex.Chat.Util (liftIOEither) import Simplex.FileTransfer.Description (FileDescriptionURI) import Simplex.Messaging.Agent (AgentClient, SubscriptionsInfo) -import Simplex.Messaging.Agent.Client (AgentLocks, AgentQueuesInfo (..), AgentWorkersDetails (..), AgentWorkersSummary (..), ProtocolTestFailure, ServerQueueInfo, SMPServerSubs, UserNetworkInfo) +import Simplex.Messaging.Agent.Client (AgentLocks, AgentQueuesInfo (..), AgentWorkersDetails (..), AgentWorkersSummary (..), ProtocolTestFailure, SMPServerSubs, ServerQueueInfo, UserNetworkInfo) import Simplex.Messaging.Agent.Env.SQLite (AgentConfig, NetworkConfig, ServerCfg) import Simplex.Messaging.Agent.Lock import Simplex.Messaging.Agent.Protocol -import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation, SQLiteStore, UpMigration, withTransaction) +import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation, SQLiteStore, UpMigration, withTransaction, withTransactionPriority) import Simplex.Messaging.Agent.Store.SQLite.DB (SlowQueryStats (..)) import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB import Simplex.Messaging.Client (SMPProxyFallback (..), SMPProxyMode (..), SocksMode (..)) @@ -264,6 +264,7 @@ data ChatCommand | APIDeleteUser UserId Bool (Maybe UserPwd) | DeleteUser UserName Bool (Maybe UserPwd) | StartChat {mainApp :: Bool, enableSndFiles :: Bool} -- enableSndFiles has no effect when mainApp is True + | CheckChatRunning | APIStopChat | APIActivateChat {restoreChat :: Bool} | APISuspendChat {suspendTimeout :: Int} @@ -292,8 +293,8 @@ data ChatCommand | APISendMessage {chatRef :: ChatRef, liveMessage :: Bool, ttl :: Maybe Int, composedMessage :: ComposedMessage} | APICreateChatItem {noteFolderId :: NoteFolderId, composedMessage :: ComposedMessage} | APIUpdateChatItem {chatRef :: ChatRef, chatItemId :: ChatItemId, liveMessage :: Bool, msgContent :: MsgContent} - | APIDeleteChatItem ChatRef ChatItemId CIDeleteMode - | APIDeleteMemberChatItem GroupId GroupMemberId ChatItemId + | APIDeleteChatItem ChatRef (NonEmpty ChatItemId) CIDeleteMode + | APIDeleteMemberChatItem GroupId (NonEmpty ChatItemId) | APIChatItemReaction {chatRef :: ChatRef, chatItemId :: ChatItemId, add :: Bool, reaction :: MsgReaction} | APIForwardChatItem {toChatRef :: ChatRef, fromChatRef :: ChatRef, chatItemId :: ChatItemId, ttl :: Maybe Int} | APIUserRead UserId @@ -598,7 +599,7 @@ data ChatResponse | CRChatItemUpdated {user :: User, chatItem :: AChatItem} | CRChatItemNotChanged {user :: User, chatItem :: AChatItem} | CRChatItemReaction {user :: User, added :: Bool, reaction :: ACIReaction} - | CRChatItemDeleted {user :: User, deletedChatItem :: AChatItem, toChatItem :: Maybe AChatItem, byUser :: Bool, timed :: Bool} + | CRChatItemsDeleted {user :: User, chatItemDeletions :: [ChatItemDeletion], byUser :: Bool, timed :: Bool} | CRChatItemDeletedNotFound {user :: User, contact :: Contact, sharedMsgId :: SharedMsgId} | CRBroadcastSent {user :: User, msgContent :: MsgContent, successes :: Int, failures :: Int, timestamp :: UTCTime} | CRMsgIntegrityError {user :: User, msgError :: MsgErrorType} @@ -1080,6 +1081,12 @@ tmeToPref currentTTL tme = uncurry TimedMessagesPreference $ case tme of TMEEnableKeepTTL -> (FAYes, currentTTL) TMEDisableKeepTTL -> (FANo, currentTTL) +data ChatItemDeletion = ChatItemDeletion + { deletedChatItem :: AChatItem, + toChatItem :: Maybe AChatItem + } + deriving (Show) + data ChatLogLevel = CLLDebug | CLLInfo | CLLWarning | CLLError | CLLImportant deriving (Eq, Ord, Show) @@ -1393,11 +1400,24 @@ toView' ev = do withStore' :: (DB.Connection -> IO a) -> CM a withStore' action = withStore $ liftIO . action +{-# INLINE withStore' #-} + +withFastStore' :: (DB.Connection -> IO a) -> CM a +withFastStore' action = withFastStore $ liftIO . action +{-# INLINE withFastStore' #-} withStore :: (DB.Connection -> ExceptT StoreError IO a) -> CM a -withStore action = do +withStore = withStorePriority False +{-# INLINE withStore #-} + +withFastStore :: (DB.Connection -> ExceptT StoreError IO a) -> CM a +withFastStore = withStorePriority True +{-# INLINE withFastStore #-} + +withStorePriority :: Bool -> (DB.Connection -> ExceptT StoreError IO a) -> CM a +withStorePriority priority action = do ChatController {chatStore} <- ask - liftIOEither $ withTransaction chatStore (runExceptT . withExceptT ChatErrorStore . action) `E.catches` handleDBErrors + liftIOEither $ withTransactionPriority chatStore priority (runExceptT . withExceptT ChatErrorStore . action) `E.catches` handleDBErrors withStoreBatch :: Traversable t => (DB.Connection -> t (IO (Either ChatError a))) -> CM' (t (Either ChatError a)) withStoreBatch actions = do @@ -1473,6 +1493,8 @@ $(JQ.deriveJSON defaultJSON ''ServerAddress) $(JQ.deriveJSON defaultJSON ''ParsedServerAddress) +$(JQ.deriveJSON defaultJSON ''ChatItemDeletion) + $(JQ.deriveJSON defaultJSON ''CoreVersionInfo) $(JQ.deriveJSON defaultJSON ''SlowSQLQuery) diff --git a/src/Simplex/Chat/Messages.hs b/src/Simplex/Chat/Messages.hs index e59429b7ea..78e3b4c640 100644 --- a/src/Simplex/Chat/Messages.hs +++ b/src/Simplex/Chat/Messages.hs @@ -35,7 +35,7 @@ import Data.Maybe (fromMaybe, isJust, isNothing) import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (decodeLatin1, encodeUtf8) -import Data.Time.Clock (UTCTime, diffUTCTime, nominalDay) +import Data.Time.Clock (UTCTime, diffUTCTime, nominalDay, NominalDiffTime) import Data.Type.Equality import Data.Typeable (Typeable) import Database.SQLite.Simple.FromField (FromField (..)) @@ -364,15 +364,19 @@ data CIMeta (c :: ChatType) (d :: MsgDirection) = CIMeta mkCIMeta :: forall c d. ChatTypeI c => ChatItemId -> CIContent d -> Text -> CIStatus d -> Maybe Bool -> Maybe SharedMsgId -> Maybe CIForwardedFrom -> Maybe (CIDeleted c) -> Bool -> Maybe CITimed -> Maybe Bool -> UTCTime -> ChatItemTs -> Maybe GroupMemberId -> UTCTime -> UTCTime -> CIMeta c d mkCIMeta itemId itemContent itemText itemStatus sentViaProxy itemSharedMsgId itemForwarded itemDeleted itemEdited itemTimed itemLive currentTs itemTs forwardedByMember createdAt updatedAt = - let deletable = case itemContent of - CISndMsgContent _ -> - case chatTypeI @c of - SCTLocal -> isNothing itemDeleted - _ -> diffUTCTime currentTs itemTs < nominalDay && isNothing itemDeleted - _ -> False + let deletable = deletable' itemContent itemDeleted itemTs nominalDay currentTs editable = deletable && isNothing itemForwarded in CIMeta {itemId, itemTs, itemText, itemStatus, sentViaProxy, itemSharedMsgId, itemForwarded, itemDeleted, itemEdited, itemTimed, itemLive, deletable, editable, forwardedByMember, createdAt, updatedAt} +deletable' :: forall c d. ChatTypeI c => CIContent d -> Maybe (CIDeleted c) -> UTCTime -> NominalDiffTime -> UTCTime -> Bool +deletable' itemContent itemDeleted itemTs allowedInterval currentTs = + case itemContent of + CISndMsgContent _ -> + case chatTypeI @c of + SCTLocal -> isNothing itemDeleted + _ -> diffUTCTime currentTs itemTs < allowedInterval && isNothing itemDeleted + _ -> False + dummyMeta :: ChatItemId -> UTCTime -> Text -> CIMeta c 'MDSnd dummyMeta itemId ts itemText = CIMeta diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index d611760fbf..6d4b2eda68 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -31,8 +31,9 @@ import Data.ByteString.Char8 (ByteString) import qualified Data.ByteString.Char8 as B import Data.ByteString.Internal (c2w, w2c) import qualified Data.ByteString.Lazy.Char8 as LB +import Data.List.NonEmpty (NonEmpty (..)) import qualified Data.List.NonEmpty as L -import Data.Maybe (fromMaybe) +import Data.Maybe (fromMaybe, mapMaybe) import Data.String import Data.Text (Text) import qualified Data.Text as T @@ -63,12 +64,14 @@ import Simplex.Messaging.Version hiding (version) -- 5 - batch sending messages (12/23/2023) -- 6 - send group welcome message after history (12/29/2023) -- 7 - update member profiles (1/15/2024) +-- 8 - compress messages and PQ e2e encryption (2024-03-08) +-- 9 - batch sending in direct connections (2024-07-24) -- This should not be used directly in code, instead use `maxVersion chatVRange` from ChatConfig. -- This indirection is needed for backward/forward compatibility testing. -- Testing with real app versions is still needed, as tests use the current code with different version ranges, not the old code. currentChatVersion :: VersionChat -currentChatVersion = VersionChat 8 +currentChatVersion = VersionChat 9 -- This should not be used directly in code, instead use `chatVRange` from ChatConfig (see comment above) supportedChatVRange :: VersionRangeChat @@ -103,6 +106,10 @@ memberProfileUpdateVersion = VersionChat 7 pqEncryptionCompressionVersion :: VersionChat pqEncryptionCompressionVersion = VersionChat 8 +-- version range that supports batch sending in direct connections, and forwarding batched messages in groups +batchSend2Version :: VersionChat +batchSend2Version = VersionChat 9 + agentToChatVersion :: VersionSMPA -> VersionChat agentToChatVersion v | v < pqdrSMPAgentVersion = initialChatVersion @@ -323,13 +330,20 @@ forwardedGroupMsg msg@ChatMessage {chatMsgEvent} = case encoding @e of SJson | isForwardedGroupMsg chatMsgEvent -> Just msg _ -> Nothing --- applied after checking forwardedGroupMsg and building list of group members to forward to, see Chat -forwardedToGroupMembers :: forall e. MsgEncodingI e => [GroupMember] -> ChatMessage e -> [GroupMember] -forwardedToGroupMembers ms ChatMessage {chatMsgEvent} = case encoding @e of - SJson -> case chatMsgEvent of - XGrpMemRestrict mId _ -> filter (\GroupMember {memberId} -> memberId /= mId) ms - _ -> ms - _ -> [] +-- applied after checking forwardedGroupMsg and building list of group members to forward to, see Chat; +-- this filters out members if any of forwarded events in batch is an XGrpMemRestrict event referring to them, +-- but practically XGrpMemRestrict is not batched with other events so it wouldn't prevent forwarding of other events +-- to these members +forwardedToGroupMembers :: forall e. MsgEncodingI e => [GroupMember] -> NonEmpty (ChatMessage e) -> [GroupMember] +forwardedToGroupMembers ms forwardedMsgs = + filter (\GroupMember {memberId} -> memberId `notElem` restrictMemberIds) ms + where + restrictMemberIds = mapMaybe restrictMemberId $ L.toList forwardedMsgs + restrictMemberId ChatMessage {chatMsgEvent} = case encoding @e of + SJson -> case chatMsgEvent of + XGrpMemRestrict mId _ -> Just mId + _ -> Nothing + _ -> Nothing data MsgReaction = MREmoji {emoji :: MREmojiChar} | MRUnknown {tag :: Text, json :: J.Object} deriving (Eq, Show) diff --git a/src/Simplex/Chat/Store/Messages.hs b/src/Simplex/Chat/Store/Messages.hs index 5d9f5a7619..b1bd1b8dd2 100644 --- a/src/Simplex/Chat/Store/Messages.hs +++ b/src/Simplex/Chat/Store/Messages.hs @@ -19,7 +19,7 @@ module Simplex.Chat.Store.Messages -- * Message and chat item functions deleteContactCIs, getGroupFileInfo, - deleteGroupCIs, + deleteGroupChatItemsMessages, createNewSndMessage, createSndMsgDelivery, createNewMessageAndRcvMsgDelivery, @@ -170,8 +170,8 @@ getGroupFileInfo db User {userId} GroupInfo {groupId} = map toFileInfo <$> DB.query db (fileInfoQuery <> " WHERE i.user_id = ? AND i.group_id = ?") (userId, groupId) -deleteGroupCIs :: DB.Connection -> User -> GroupInfo -> IO () -deleteGroupCIs db User {userId} GroupInfo {groupId} = do +deleteGroupChatItemsMessages :: DB.Connection -> User -> GroupInfo -> IO () +deleteGroupChatItemsMessages db User {userId} GroupInfo {groupId} = do DB.execute db "DELETE FROM messages WHERE group_id = ?" (Only groupId) DB.execute db "DELETE FROM chat_item_reactions WHERE group_id = ?" (Only groupId) DB.execute db "DELETE FROM chat_items WHERE user_id = ? AND group_id = ?" (userId, groupId) @@ -1733,11 +1733,10 @@ deleteChatItemVersions_ :: DB.Connection -> ChatItemId -> IO () deleteChatItemVersions_ db itemId = DB.execute db "DELETE FROM chat_item_versions WHERE chat_item_id = ?" (Only itemId) -markDirectChatItemDeleted :: DB.Connection -> User -> Contact -> ChatItem 'CTDirect d -> MessageId -> UTCTime -> IO (ChatItem 'CTDirect d) -markDirectChatItemDeleted db User {userId} Contact {contactId} ci@ChatItem {meta} msgId deletedTs = do +markDirectChatItemDeleted :: DB.Connection -> User -> Contact -> ChatItem 'CTDirect d -> UTCTime -> IO (ChatItem 'CTDirect d) +markDirectChatItemDeleted db User {userId} Contact {contactId} ci@ChatItem {meta} deletedTs = do currentTs <- liftIO getCurrentTime let itemId = chatItemId' ci - insertChatItemMessage_ db itemId msgId currentTs DB.execute db [sql| @@ -1900,7 +1899,7 @@ updateGroupChatItemModerated db User {userId} GroupInfo {groupId} ci m@GroupMemb WHERE user_id = ? AND group_id = ? AND chat_item_id = ? |] (deletedTs, groupMemberId, toContent, toText, currentTs, userId, groupId, itemId) - pure $ ci {content = toContent, meta = (meta ci) {itemText = toText, itemDeleted = Just (CIModerated (Just deletedTs) m), editable = False, deletable = False}, formattedText = Nothing} + pure ci {content = toContent, meta = (meta ci) {itemText = toText, itemDeleted = Just (CIModerated (Just deletedTs) m), editable = False, deletable = False}, formattedText = Nothing} updateGroupCIBlockedByAdmin :: DB.Connection -> User -> GroupInfo -> ChatItem 'CTGroup d -> UTCTime -> IO (ChatItem 'CTGroup d) updateGroupCIBlockedByAdmin db User {userId} GroupInfo {groupId} ci deletedTs = do @@ -1931,14 +1930,13 @@ pattern DBCIBlocked = 2 pattern DBCIBlockedByAdmin :: Int pattern DBCIBlockedByAdmin = 3 -markGroupChatItemDeleted :: DB.Connection -> User -> GroupInfo -> ChatItem 'CTGroup d -> MessageId -> Maybe GroupMember -> UTCTime -> IO (ChatItem 'CTGroup d) -markGroupChatItemDeleted db User {userId} GroupInfo {groupId} ci@ChatItem {meta} msgId byGroupMember_ deletedTs = do +markGroupChatItemDeleted :: DB.Connection -> User -> GroupInfo -> ChatItem 'CTGroup d -> Maybe GroupMember -> UTCTime -> IO (ChatItem 'CTGroup d) +markGroupChatItemDeleted db User {userId} GroupInfo {groupId} ci@ChatItem {meta} byGroupMember_ deletedTs = do currentTs <- liftIO getCurrentTime let itemId = chatItemId' ci (deletedByGroupMemberId, itemDeleted) = case byGroupMember_ of Just m@GroupMember {groupMemberId} -> (Just groupMemberId, Just $ CIModerated (Just deletedTs) m) _ -> (Nothing, Just $ CIDeleted @'CTGroup (Just deletedTs)) - insertChatItemMessage_ db itemId msgId currentTs DB.execute db [sql| diff --git a/src/Simplex/Chat/Terminal/Input.hs b/src/Simplex/Chat/Terminal/Input.hs index 5c36994190..2d1039e585 100644 --- a/src/Simplex/Chat/Terminal/Input.hs +++ b/src/Simplex/Chat/Terminal/Input.hs @@ -71,7 +71,7 @@ runInputLoop ct@ChatTerminal {termState, liveMessageState} cc = forever $ do CRChatItems u chatName_ _ -> whenCurrUser cc u $ mapM_ (setActive ct . chatActiveTo) chatName_ CRNewChatItem u (AChatItem _ SMDSnd cInfo _) -> whenCurrUser cc u $ setActiveChat ct cInfo CRChatItemUpdated u (AChatItem _ SMDSnd cInfo _) -> whenCurrUser cc u $ setActiveChat ct cInfo - CRChatItemDeleted u (AChatItem _ _ cInfo _) _ _ _ -> whenCurrUser cc u $ setActiveChat ct cInfo + CRChatItemsDeleted u ((ChatItemDeletion (AChatItem _ _ cInfo _) _) : _) _ _ -> whenCurrUser cc u $ setActiveChat ct cInfo CRContactDeleted u c -> whenCurrUser cc u $ unsetActiveContact ct c CRGroupDeletedUser u g -> whenCurrUser cc u $ unsetActiveGroup ct g CRSentGroupInvitation u g _ _ -> whenCurrUser cc u $ setActiveGroup ct g diff --git a/src/Simplex/Chat/Terminal/Main.hs b/src/Simplex/Chat/Terminal/Main.hs index 5d684d7283..a946ba3483 100644 --- a/src/Simplex/Chat/Terminal/Main.hs +++ b/src/Simplex/Chat/Terminal/Main.hs @@ -23,20 +23,24 @@ import System.Terminal (withTerminal) simplexChatCLI :: ChatConfig -> Maybe (ServiceName -> ChatConfig -> ChatOpts -> IO ()) -> IO () simplexChatCLI cfg server_ = do appDir <- getAppUserDataDirectory "simplex" - opts@ChatOpts {chatCmd, chatServerPort} <- getChatOpts appDir "simplex_v1" + opts <- getChatOpts appDir "simplex_v1" + simplexChatCLI' cfg opts server_ + +simplexChatCLI' :: ChatConfig -> ChatOpts -> Maybe (ServiceName -> ChatConfig -> ChatOpts -> IO ()) -> IO () +simplexChatCLI' cfg opts@ChatOpts {chatCmd, chatCmdLog, chatCmdDelay, chatServerPort} server_ = do if null chatCmd then case chatServerPort of Just chatPort -> case server_ of Just server -> server chatPort cfg opts Nothing -> putStrLn "Not allowed to run as a WebSockets server" >> exitFailure - _ -> runCLI opts - else simplexChatCore cfg opts $ runCommand opts + _ -> runCLI + else simplexChatCore cfg opts runCommand where - runCLI opts = do + runCLI = do welcome cfg opts t <- withTerminal pure simplexChatTerminal cfg opts t - runCommand ChatOpts {chatCmd, chatCmdLog, chatCmdDelay} user cc = do + runCommand user cc = do when (chatCmdLog /= CCLNone) . void . forkIO . forever $ do (_, _, r') <- atomically . readTBQueue $ outputQ cc case r' of diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 8d5c782866..545f8c08e4 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -127,7 +127,10 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe CRChatItemStatusUpdated u ci -> ttyUser u $ viewChatItemStatusUpdated ci ts tz testView showReceipts CRChatItemUpdated u (AChatItem _ _ chat item) -> ttyUser u $ unmuted u chat item $ viewItemUpdate chat item liveItems ts tz CRChatItemNotChanged u ci -> ttyUser u $ viewItemNotChanged ci - CRChatItemDeleted u (AChatItem _ _ chat deletedItem) toItem byUser timed -> ttyUser u $ unmuted u chat deletedItem $ viewItemDelete chat deletedItem toItem byUser timed ts tz testView + CRChatItemsDeleted u deletions byUser timed -> case deletions of + [ChatItemDeletion (AChatItem _ _ chat deletedItem) toItem] -> + ttyUser u $ unmuted u chat deletedItem $ viewItemDelete chat deletedItem toItem byUser timed ts tz testView + deletions' -> ttyUser u [sShow (length deletions') <> " messages deleted"] CRChatItemReaction u added (ACIReaction _ _ chat reaction) -> ttyUser u $ unmutedReaction u chat reaction $ viewItemReaction showReactions chat reaction added ts tz CRChatItemDeletedNotFound u Contact {localDisplayName = c} _ -> ttyUser u [ttyFrom $ c <> "> [deleted - original message not found]"] CRBroadcastSent u mc s f t -> ttyUser u $ viewSentBroadcast mc s f ts tz t @@ -408,7 +411,7 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe CRArchiveImported archiveErrs -> if null archiveErrs then ["ok"] else ["archive import errors: " <> plain (show archiveErrs)] CRAppSettings as -> ["app settings: " <> viewJSON as] CRTimedAction _ _ -> [] - CRCustomChatResponse u r -> ttyUser' u $ [plain r] + CRCustomChatResponse u r -> ttyUser' u $ map plain $ T.lines r where ttyUser :: User -> [StyledString] -> [StyledString] ttyUser user@User {showNtfs, activeUser} ss diff --git a/tests/ChatClient.hs b/tests/ChatClient.hs index ffea6a3529..e3d557166b 100644 --- a/tests/ChatClient.hs +++ b/tests/ChatClient.hs @@ -224,10 +224,11 @@ testCfgCreateGroupDirect = mkCfgCreateGroupDirect testCfg mkCfgCreateGroupDirect :: ChatConfig -> ChatConfig -mkCfgCreateGroupDirect cfg = cfg { - chatVRange = groupCreateDirectVRange, - agentConfig = testAgentCfgSlow -} +mkCfgCreateGroupDirect cfg = + cfg + { chatVRange = groupCreateDirectVRange, + agentConfig = testAgentCfgSlow + } groupCreateDirectVRange :: VersionRangeChat groupCreateDirectVRange = mkVersionRange (VersionChat 1) (VersionChat 1) diff --git a/tests/ChatTests/Direct.hs b/tests/ChatTests/Direct.hs index 2bbcf87d5b..473d62a8cf 100644 --- a/tests/ChatTests/Direct.hs +++ b/tests/ChatTests/Direct.hs @@ -15,6 +15,7 @@ import Data.Aeson (ToJSON) import qualified Data.Aeson as J import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy.Char8 as LB +import Data.List (intercalate) import qualified Data.Text as T import Simplex.Chat.AppSettings (defaultAppSettings) import qualified Simplex.Chat.AppSettings as AS @@ -44,6 +45,8 @@ chatDirectTests = do it "direct message update" testDirectMessageUpdate it "direct message edit history" testDirectMessageEditHistory it "direct message delete" testDirectMessageDelete + it "direct message delete multiple" testDirectMessageDeleteMultiple + it "direct message delete multiple (many chat batches)" testDirectMessageDeleteMultipleManyBatches it "direct live message" testDirectLiveMessage it "direct timed message" testDirectTimedMessage it "repeat AUTH errors disable contact" testRepeatAuthErrorsDisableContact @@ -685,6 +688,52 @@ testDirectMessageDelete = bob #$> ("/_delete item @2 " <> itemId 4 <> " internal", id, "message deleted") bob #$> ("/_get chat @2 count=100", chat', chatFeatures' <> [((0, "hello 🙂"), Nothing), ((1, "do you receive my messages?"), Just (0, "hello 🙂"))]) +testDirectMessageDeleteMultiple :: HasCallStack => FilePath -> IO () +testDirectMessageDeleteMultiple = + testChat2 aliceProfile bobProfile $ + \alice bob -> do + connectUsers alice bob + + alice #> "@bob hello" + bob <# "alice> hello" + msgId1 <- lastItemId alice + + alice #> "@bob hey" + bob <# "alice> hey" + msgId2 <- lastItemId alice + + alice ##> ("/_delete item @2 " <> msgId1 <> "," <> msgId2 <> " broadcast") + alice <## "2 messages deleted" + bob <# "alice> [marked deleted] hello" + bob <# "alice> [marked deleted] hey" + + alice #$> ("/_get chat @2 count=2", chat, [(1, "hello [marked deleted]"), (1, "hey [marked deleted]")]) + bob #$> ("/_get chat @2 count=2", chat, [(0, "hello [marked deleted]"), (0, "hey [marked deleted]")]) + +testDirectMessageDeleteMultipleManyBatches :: HasCallStack => FilePath -> IO () +testDirectMessageDeleteMultipleManyBatches = + testChat2 aliceProfile bobProfile $ + \alice bob -> do + connectUsers alice bob + + alice #> "@bob message 0" + bob <# "alice> message 0" + msgIdFirst <- lastItemId alice + + forM_ [(1 :: Int) .. 300] $ \i -> do + alice #> ("@bob message " <> show i) + bob <# ("alice> message " <> show i) + msgIdLast <- lastItemId alice + + let mIdFirst = read msgIdFirst :: Int + mIdLast = read msgIdLast :: Int + deleteIds = intercalate "," (map show [mIdFirst .. mIdLast]) + alice `send` ("/_delete item @2 " <> deleteIds <> " broadcast") + _ <- getTermLine alice + alice <## "301 messages deleted" + forM_ [(0 :: Int) .. 300] $ \i -> do + bob <# ("alice> [marked deleted] message " <> show i) + testDirectLiveMessage :: HasCallStack => FilePath -> IO () testDirectLiveMessage = testChat2 aliceProfile bobProfile $ \alice bob -> do @@ -1138,6 +1187,7 @@ testSubscribeAppNSE tmp = alice <## "chat suspended" nseAlice ##> "/_start main=off" nseAlice <## "chat started" + threadDelay 100000 nseAlice ##> "/ad" cLink <- getContactLink nseAlice True bob ##> ("/c " <> cLink) @@ -1392,14 +1442,14 @@ testMultipleUserAddresses = cLinkAlisa <- getContactLink alice True bob ##> ("/c " <> cLinkAlisa) alice <#? bob - alice #$> ("/_get chats 2 pcc=on", chats, [("<@bob", ""), ("*", "")]) + alice #$> ("/_get chats 2 pcc=on", chats, [("<@bob", ""), ("@SimpleX-Status", ""), ("@SimpleX Chat team", ""), ("*", "")]) alice ##> "/ac bob" alice <## "bob (Bob): accepting contact request, you can send messages to contact" concurrently_ (bob <## "alisa: contact is connected") (alice <## "bob (Bob): contact is connected") threadDelay 100000 - alice #$> ("/_get chats 2 pcc=on", chats, [("@bob", lastChatFeature), ("*", "")]) + alice #$> ("/_get chats 2 pcc=on", chats, [("@bob", lastChatFeature), ("@SimpleX-Status", ""), ("@SimpleX Chat team", ""), ("*", "")]) alice <##> bob bob #> "@alice hey alice" @@ -1430,7 +1480,7 @@ testMultipleUserAddresses = (cath <## "alisa: contact is connected") (alice <## "cath (Catherine): contact is connected") threadDelay 100000 - alice #$> ("/_get chats 2 pcc=on", chats, [("@cath", lastChatFeature), ("@bob", "hey"), ("*", "")]) + alice #$> ("/_get chats 2 pcc=on", chats, [("@cath", lastChatFeature), ("@bob", "hey"), ("@SimpleX-Status", ""), ("@SimpleX Chat team", ""), ("*", "")]) alice <##> cath -- first user doesn't have cath as contact @@ -1633,7 +1683,7 @@ testUsersDifferentCIExpirationTTL tmp = do bob #> "@alisa alisa 4" alice <# "bob> alisa 4" - alice #$> ("/_get chat @4 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2"), (1, "alisa 3"), (0, "alisa 4")]) + alice #$> ("/_get chat @6 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2"), (1, "alisa 3"), (0, "alisa 4")]) threadDelay 3000000 @@ -1646,11 +1696,11 @@ testUsersDifferentCIExpirationTTL tmp = do -- second user messages alice ##> "/user alisa" showActiveUser alice "alisa" - alice #$> ("/_get chat @4 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2"), (1, "alisa 3"), (0, "alisa 4")]) + alice #$> ("/_get chat @6 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2"), (1, "alisa 3"), (0, "alisa 4")]) threadDelay 2000000 - alice #$> ("/_get chat @4 count=100", chat, []) + alice #$> ("/_get chat @6 count=100", chat, []) where cfg = testCfg {initialCleanupManagerDelay = 0, cleanupManagerStepDelay = 0, ciExpirationInterval = 500000} @@ -1716,7 +1766,7 @@ testUsersRestartCIExpiration tmp = do bob #> "@alisa alisa 4" alice <# "bob> alisa 4" - alice #$> ("/_get chat @4 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2"), (1, "alisa 3"), (0, "alisa 4")]) + alice #$> ("/_get chat @6 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2"), (1, "alisa 3"), (0, "alisa 4")]) threadDelay 3000000 @@ -1729,11 +1779,11 @@ testUsersRestartCIExpiration tmp = do -- second user messages alice ##> "/user alisa" showActiveUser alice "alisa" - alice #$> ("/_get chat @4 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2"), (1, "alisa 3"), (0, "alisa 4")]) + alice #$> ("/_get chat @6 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2"), (1, "alisa 3"), (0, "alisa 4")]) threadDelay 3000000 - alice #$> ("/_get chat @4 count=100", chat, []) + alice #$> ("/_get chat @6 count=100", chat, []) where cfg = testCfg {initialCleanupManagerDelay = 0, cleanupManagerStepDelay = 0, ciExpirationInterval = 500000} @@ -1775,7 +1825,7 @@ testEnableCIExpirationOnlyForOneUser tmp = do bob #> "@alisa alisa 4" alice <# "bob> alisa 4" - alice #$> ("/_get chat @4 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2"), (1, "alisa 3"), (0, "alisa 4")]) + alice #$> ("/_get chat @6 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2"), (1, "alisa 3"), (0, "alisa 4")]) threadDelay 2000000 @@ -1787,14 +1837,14 @@ testEnableCIExpirationOnlyForOneUser tmp = do -- messages are not deleted for second user alice ##> "/user alisa" showActiveUser alice "alisa" - alice #$> ("/_get chat @4 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2"), (1, "alisa 3"), (0, "alisa 4")]) + alice #$> ("/_get chat @6 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2"), (1, "alisa 3"), (0, "alisa 4")]) withTestChatCfg tmp cfg "alice" $ \alice -> do alice <## "1 contacts connected (use /cs for the list)" alice <## "[user: alice] 1 contacts connected (use /cs for the list)" -- messages are not deleted for second user after restart - alice #$> ("/_get chat @4 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2"), (1, "alisa 3"), (0, "alisa 4")]) + alice #$> ("/_get chat @6 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2"), (1, "alisa 3"), (0, "alisa 4")]) alice #> "@bob alisa 5" bob <# "alisa> alisa 5" @@ -1804,7 +1854,7 @@ testEnableCIExpirationOnlyForOneUser tmp = do threadDelay 2000000 -- new messages are not deleted for second user - alice #$> ("/_get chat @4 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2"), (1, "alisa 3"), (0, "alisa 4"), (1, "alisa 5"), (0, "alisa 6")]) + alice #$> ("/_get chat @6 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2"), (1, "alisa 3"), (0, "alisa 4"), (1, "alisa 5"), (0, "alisa 6")]) where cfg = testCfg {initialCleanupManagerDelay = 0, cleanupManagerStepDelay = 0, ciExpirationInterval = 500000} @@ -1838,12 +1888,12 @@ testDisableCIExpirationOnlyForOneUser tmp = do bob #> "@alisa alisa 2" alice <# "bob> alisa 2" - alice #$> ("/_get chat @4 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2")]) + alice #$> ("/_get chat @6 count=100", chat, chatFeatures <> [(1, "alisa 1"), (0, "alisa 2")]) threadDelay 2000000 -- second user messages are deleted - alice #$> ("/_get chat @4 count=100", chat, []) + alice #$> ("/_get chat @6 count=100", chat, []) withTestChatCfg tmp cfg "alice" $ \alice -> do alice <## "1 contacts connected (use /cs for the list)" @@ -1857,12 +1907,12 @@ testDisableCIExpirationOnlyForOneUser tmp = do bob #> "@alisa alisa 4" alice <# "bob> alisa 4" - alice #$> ("/_get chat @4 count=100", chat, [(1, "alisa 3"), (0, "alisa 4")]) + alice #$> ("/_get chat @6 count=100", chat, [(1, "alisa 3"), (0, "alisa 4")]) threadDelay 2000000 -- second user messages are deleted - alice #$> ("/_get chat @4 count=100", chat, []) + alice #$> ("/_get chat @6 count=100", chat, []) where cfg = testCfg {initialCleanupManagerDelay = 0, cleanupManagerStepDelay = 0, ciExpirationInterval = 500000} @@ -1877,7 +1927,7 @@ testUsersTimedMessages tmp = do alice ##> "/create user alisa" showActiveUser alice "alisa" connectUsers alice bob - configureTimedMessages alice bob "4" "3" + configureTimedMessages alice bob "6" "3" -- first user messages alice ##> "/user alice" @@ -1906,7 +1956,7 @@ testUsersTimedMessages tmp = do alice ##> "/user alisa" showActiveUser alice "alisa" - alice #$> ("/_get chat @4 count=100", chat, [(1, "alisa 1"), (0, "alisa 2")]) + alice #$> ("/_get chat @6 count=100", chat, [(1, "alisa 1"), (0, "alisa 2")]) threadDelay 1000000 @@ -1921,7 +1971,7 @@ testUsersTimedMessages tmp = do alice ##> "/user alisa" showActiveUser alice "alisa" - alice #$> ("/_get chat @4 count=100", chat, [(1, "alisa 1"), (0, "alisa 2")]) + alice #$> ("/_get chat @6 count=100", chat, [(1, "alisa 1"), (0, "alisa 2")]) threadDelay 1000000 @@ -1932,7 +1982,7 @@ testUsersTimedMessages tmp = do alice ##> "/user" showActiveUser alice "alisa" - alice #$> ("/_get chat @4 count=100", chat, []) + alice #$> ("/_get chat @6 count=100", chat, []) -- first user messages alice ##> "/user alice" @@ -1962,7 +2012,7 @@ testUsersTimedMessages tmp = do alice ##> "/user alisa" showActiveUser alice "alisa" - alice #$> ("/_get chat @4 count=100", chat, [(1, "alisa 3"), (0, "alisa 4")]) + alice #$> ("/_get chat @6 count=100", chat, [(1, "alisa 3"), (0, "alisa 4")]) -- messages are deleted after restart threadDelay 1000000 @@ -1978,7 +2028,7 @@ testUsersTimedMessages tmp = do alice ##> "/user alisa" showActiveUser alice "alisa" - alice #$> ("/_get chat @4 count=100", chat, [(1, "alisa 3"), (0, "alisa 4")]) + alice #$> ("/_get chat @6 count=100", chat, [(1, "alisa 3"), (0, "alisa 4")]) threadDelay 1000000 @@ -1989,7 +2039,7 @@ testUsersTimedMessages tmp = do alice ##> "/user" showActiveUser alice "alisa" - alice #$> ("/_get chat @4 count=100", chat, []) + alice #$> ("/_get chat @6 count=100", chat, []) where configureTimedMessages :: HasCallStack => TestCC -> TestCC -> String -> String -> IO () configureTimedMessages alice bob bobId ttl = do diff --git a/tests/ChatTests/Groups.hs b/tests/ChatTests/Groups.hs index 67d1bfeae5..07da140d78 100644 --- a/tests/ChatTests/Groups.hs +++ b/tests/ChatTests/Groups.hs @@ -11,7 +11,7 @@ import ChatClient import ChatTests.Utils import Control.Concurrent (threadDelay) import Control.Concurrent.Async (concurrently_) -import Control.Monad (void, when) +import Control.Monad (forM_, void, when) import qualified Data.ByteString.Char8 as B import Data.List (isInfixOf) import qualified Data.Text as T @@ -19,6 +19,7 @@ import Simplex.Chat.Controller (ChatConfig (..)) import Simplex.Chat.Options import Simplex.Chat.Protocol (supportedChatVRange) import Simplex.Chat.Store (agentStoreFile, chatStoreFile) +import Data.List (intercalate) import Simplex.Chat.Types (VersionRangeChat) import Simplex.Chat.Types.Shared (GroupMemberRole (..)) import Simplex.Messaging.Agent.Env.SQLite @@ -52,12 +53,16 @@ chatGroupTests = do it "group message update" testGroupMessageUpdate it "group message edit history" testGroupMessageEditHistory it "group message delete" testGroupMessageDelete + it "group message delete multiple" testGroupMessageDeleteMultiple + it "group message delete multiple (many chat batches)" testGroupMessageDeleteMultipleManyBatches it "group live message" testGroupLiveMessage it "update group profile" testUpdateGroupProfile it "update member role" testUpdateMemberRole it "unused contacts are deleted after all their groups are deleted" testGroupDeleteUnusedContacts it "group description is shown as the first message to new members" testGroupDescription it "moderate message of another group member" testGroupModerate + it "moderate own message (should process as deletion)" testGroupModerateOwn + it "moderate multiple messages" testGroupModerateMultiple it "moderate message of another group member (full delete)" testGroupModerateFullDelete it "moderate message that arrives after the event of moderation" testGroupDelayedModeration it "moderate message that arrives after the event of moderation (full delete)" testGroupDelayedModerationFullDelete @@ -648,6 +653,7 @@ testGroupSameName :: HasCallStack => FilePath -> IO () testGroupSameName = testChat2 aliceProfile bobProfile $ \alice _ -> do + threadDelay 100000 alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" @@ -1253,6 +1259,77 @@ testGroupMessageDelete = bob #$> ("/_get chat #1 count=3", chat', [((0, "hello!"), Nothing), ((1, "hi alice"), Just (0, "hello!")), ((0, "how are you? [marked deleted]"), Nothing)]) cath #$> ("/_get chat #1 count=3", chat', [((0, "hello!"), Nothing), ((0, "hi alice"), Just (0, "hello!")), ((1, "how are you? [marked deleted]"), Nothing)]) +testGroupMessageDeleteMultiple :: HasCallStack => FilePath -> IO () +testGroupMessageDeleteMultiple = + testChat3 aliceProfile bobProfile cathProfile $ + \alice bob cath -> do + createGroup3 "team" alice bob cath + + threadDelay 1000000 + alice #> "#team hello" + concurrently_ + (bob <# "#team alice> hello") + (cath <# "#team alice> hello") + msgId1 <- lastItemId alice + + threadDelay 1000000 + alice #> "#team hey" + concurrently_ + (bob <# "#team alice> hey") + (cath <# "#team alice> hey") + msgId2 <- lastItemId alice + + threadDelay 1000000 + alice ##> ("/_delete item #1 " <> msgId1 <> "," <> msgId2 <> " broadcast") + alice <## "2 messages deleted" + concurrentlyN_ + [ do + bob <# "#team alice> [marked deleted] hello" + bob <# "#team alice> [marked deleted] hey", + do + cath <# "#team alice> [marked deleted] hello" + cath <# "#team alice> [marked deleted] hey" + ] + + alice #$> ("/_get chat #1 count=2", chat, [(1, "hello [marked deleted]"), (1, "hey [marked deleted]")]) + bob #$> ("/_get chat #1 count=2", chat, [(0, "hello [marked deleted]"), (0, "hey [marked deleted]")]) + cath #$> ("/_get chat #1 count=2", chat, [(0, "hello [marked deleted]"), (0, "hey [marked deleted]")]) + +testGroupMessageDeleteMultipleManyBatches :: HasCallStack => FilePath -> IO () +testGroupMessageDeleteMultipleManyBatches = + testChat3 aliceProfile bobProfile cathProfile $ + \alice bob cath -> do + createGroup3 "team" alice bob cath + + bob ##> "/set receipts all off" + bob <## "ok" + cath ##> "/set receipts all off" + cath <## "ok" + + alice #> "#team message 0" + concurrently_ + (bob <# "#team alice> message 0") + (cath <# "#team alice> message 0") + msgIdFirst <- lastItemId alice + + forM_ [(1 :: Int) .. 300] $ \i -> do + alice #> ("#team message " <> show i) + concurrently_ + (bob <# ("#team alice> message " <> show i)) + (cath <# ("#team alice> message " <> show i)) + msgIdLast <- lastItemId alice + + let mIdFirst = read msgIdFirst :: Int + mIdLast = read msgIdLast :: Int + deleteIds = intercalate "," (map show [mIdFirst .. mIdLast]) + alice `send` ("/_delete item #1 " <> deleteIds <> " broadcast") + _ <- getTermLine alice + alice <## "301 messages deleted" + forM_ [(0 :: Int) .. 300] $ \i -> + concurrently_ + (bob <# ("#team alice> [marked deleted] message " <> show i)) + (cath <# ("#team alice> [marked deleted] message " <> show i)) + testGroupLiveMessage :: HasCallStack => FilePath -> IO () testGroupLiveMessage = testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do @@ -1538,7 +1615,7 @@ testGroupModerate = (bob <# "#team alice> hello") (cath <# "#team alice> hello") bob ##> "\\\\ #team @alice hello" - bob <## "#team: you have insufficient permissions for this action, the required role is owner" + bob <## "cannot delete this item" threadDelay 1000000 cath #> "#team hi" concurrently_ @@ -1553,6 +1630,55 @@ testGroupModerate = bob #$> ("/_get chat #1 count=1", chat, [(0, "hi [marked deleted by you]")]) cath #$> ("/_get chat #1 count=1", chat, [(1, "hi [marked deleted by bob]")]) +testGroupModerateOwn :: HasCallStack => FilePath -> IO () +testGroupModerateOwn = + testChat2 aliceProfile bobProfile $ + \alice bob -> do + createGroup2 "team" alice bob + threadDelay 1000000 + alice #> "#team hello" + bob <# "#team alice> hello" + alice ##> "\\\\ #team @alice hello" + alice <## "message marked deleted by you" + bob <# "#team alice> [marked deleted by alice] hello" + alice #$> ("/_get chat #1 count=1", chat, [(1, "hello [marked deleted by you]")]) + bob #$> ("/_get chat #1 count=1", chat, [(0, "hello [marked deleted by alice]")]) + +testGroupModerateMultiple :: HasCallStack => FilePath -> IO () +testGroupModerateMultiple = + testChat3 aliceProfile bobProfile cathProfile $ + \alice bob cath -> do + createGroup3 "team" alice bob cath + + threadDelay 1000000 + alice #> "#team hello" + concurrently_ + (bob <# "#team alice> hello") + (cath <# "#team alice> hello") + msgId1 <- lastItemId alice + + threadDelay 1000000 + bob #> "#team hey" + concurrently_ + (alice <# "#team bob> hey") + (cath <# "#team bob> hey") + msgId2 <- lastItemId alice + + alice ##> ("/_delete member item #1 " <> msgId1 <> "," <> msgId2) + alice <## "2 messages deleted" + concurrentlyN_ + [ do + bob <# "#team alice> [marked deleted by alice] hello" + bob <# "#team bob> [marked deleted by alice] hey", + do + cath <# "#team alice> [marked deleted by alice] hello" + cath <# "#team bob> [marked deleted by alice] hey" + ] + + alice #$> ("/_get chat #1 count=2", chat, [(1, "hello [marked deleted by you]"), (0, "hey [marked deleted by you]")]) + bob #$> ("/_get chat #1 count=2", chat, [(0, "hello [marked deleted by alice]"), (1, "hey [marked deleted by alice]")]) + cath #$> ("/_get chat #1 count=2", chat, [(0, "hello [marked deleted by alice]"), (0, "hey [marked deleted by alice]")]) + testGroupModerateFullDelete :: HasCallStack => FilePath -> IO () testGroupModerateFullDelete = testChatCfg3 testCfgCreateGroupDirect aliceProfile bobProfile cathProfile $ @@ -1844,6 +1970,7 @@ testGroupLink :: HasCallStack => FilePath -> IO () testGroupLink = testChatCfg3 testCfgGroupLinkViaContact aliceProfile bobProfile cathProfile $ \alice bob cath -> do + threadDelay 100000 alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" @@ -1947,6 +2074,7 @@ testGroupLinkDeleteGroupRejoin :: HasCallStack => FilePath -> IO () testGroupLinkDeleteGroupRejoin = testChatCfg2 testCfgGroupLinkViaContact aliceProfile bobProfile $ \alice bob -> do + threadDelay 100000 alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" @@ -2003,6 +2131,7 @@ testGroupLinkContactUsed :: HasCallStack => FilePath -> IO () testGroupLinkContactUsed = testChatCfg2 testCfgGroupLinkViaContact aliceProfile bobProfile $ \alice bob -> do + threadDelay 100000 alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" @@ -2151,6 +2280,7 @@ testGroupLinkUnusedHostContactDeleted = testChatCfg2 cfg aliceProfile bobProfile $ \alice bob -> do -- create group 1 + threadDelay 100000 alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" @@ -2285,6 +2415,7 @@ testGroupLinkMemberRole :: HasCallStack => FilePath -> IO () testGroupLinkMemberRole = testChatCfg3 testCfgGroupLinkViaContact aliceProfile bobProfile cathProfile $ \alice bob cath -> do + threadDelay 100000 alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" @@ -2420,6 +2551,7 @@ testPlanGroupLinkOkKnown :: HasCallStack => FilePath -> IO () testPlanGroupLinkOkKnown = testChatCfg2 testCfgGroupLinkViaContact aliceProfile bobProfile $ \alice bob -> do + threadDelay 100000 alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" @@ -2463,6 +2595,7 @@ testPlanHostContactDeletedGroupLinkKnown :: HasCallStack => FilePath -> IO () testPlanHostContactDeletedGroupLinkKnown = testChatCfg2 testCfgGroupLinkViaContact aliceProfile bobProfile $ \alice bob -> do + threadDelay 100000 alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" @@ -2569,14 +2702,15 @@ testPlanGroupLinkOwn tmp = testPlanGroupLinkConnecting :: HasCallStack => FilePath -> IO () testPlanGroupLinkConnecting tmp = do -- gLink <- withNewTestChatCfg tmp cfg "alice" aliceProfile $ \alice -> do - gLink <- withNewTestChatCfg tmp cfg "alice" aliceProfile $ \a -> withTestOutput a $ \alice -> do + gLink <- withNewTestChatCfg tmp cfg "alice" aliceProfile $ \alice -> do + threadDelay 100000 alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" alice ##> "/create link #team" getGroupLink alice "team" GRMember True -- withNewTestChatCfg tmp cfg "bob" bobProfile $ \bob -> do - withNewTestChatCfg tmp cfg "bob" bobProfile $ \b -> withTestOutput b $ \bob -> do + withNewTestChatCfg tmp cfg "bob" bobProfile $ \bob -> do threadDelay 100000 bob ##> ("/c " <> gLink) @@ -2591,14 +2725,14 @@ testPlanGroupLinkConnecting tmp = do threadDelay 100000 -- withTestChatCfg tmp cfg "alice" $ \alice -> do - withTestChatCfg tmp cfg "alice" $ \a -> withTestOutput a $ \alice -> do + withTestChatCfg tmp cfg "alice" $ \alice -> do alice <### [ "1 group links active", "#team: group is empty", "bob (Bob): accepting request to join group #team..." ] -- withTestChatCfg tmp cfg "bob" $ \bob -> do - withTestChatCfg tmp cfg "bob" $ \b -> withTestOutput b $ \bob -> do + withTestChatCfg tmp cfg "bob" $ \bob -> do threadDelay 500000 bob ##> ("/_connect plan 1 " <> gLink) bob <## "group link: connecting" @@ -2615,8 +2749,8 @@ testPlanGroupLinkConnecting tmp = do testPlanGroupLinkLeaveRejoin :: HasCallStack => FilePath -> IO () testPlanGroupLinkLeaveRejoin = testChatCfg2 testCfgGroupLinkViaContact aliceProfile bobProfile $ - -- \alice bob -> do - \a b -> withTestOutput a $ \alice -> withTestOutput b $ \bob -> do + \alice bob -> do + threadDelay 100000 alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" @@ -2705,6 +2839,7 @@ testGroupLinkNoContact :: HasCallStack => FilePath -> IO () testGroupLinkNoContact = testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do + threadDelay 100000 alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" @@ -2928,6 +3063,7 @@ testGroupLinkNoContactMemberRole :: HasCallStack => FilePath -> IO () testGroupLinkNoContactMemberRole = testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do + threadDelay 100000 alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" @@ -3041,6 +3177,7 @@ testGroupLinkNoContactInviteeIncognito :: HasCallStack => FilePath -> IO () testGroupLinkNoContactInviteeIncognito = testChat2 aliceProfile bobProfile $ \alice bob -> do + threadDelay 100000 alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" @@ -3145,6 +3282,7 @@ testPlanGroupLinkNoContactKnown :: HasCallStack => FilePath -> IO () testPlanGroupLinkNoContactKnown = testChat2 aliceProfile bobProfile $ \alice bob -> do + threadDelay 100000 alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" @@ -3180,6 +3318,7 @@ testPlanGroupLinkNoContactKnown = testPlanGroupLinkNoContactConnecting :: HasCallStack => FilePath -> IO () testPlanGroupLinkNoContactConnecting tmp = do gLink <- withNewTestChat tmp "alice" aliceProfile $ \alice -> do + threadDelay 100000 alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" @@ -3226,6 +3365,7 @@ testPlanGroupLinkNoContactConnecting tmp = do testPlanGroupLinkNoContactConnectingSlow :: HasCallStack => FilePath -> IO () testPlanGroupLinkNoContactConnectingSlow tmp = do gLink <- withNewTestChatCfg tmp testCfgSlow "alice" aliceProfile $ \alice -> do + threadDelay 100000 alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" @@ -4123,6 +4263,7 @@ testMemberContactIncognito = testChatCfg3 testCfgGroupLinkViaContact aliceProfile bobProfile cathProfile $ \alice bob cath -> do -- create group, bob joins incognito + threadDelay 100000 alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" @@ -5371,6 +5512,7 @@ testMembershipProfileUpdateNextGroupMessage = testChat3 aliceProfile bobProfile cathProfile $ \alice bob cath -> do -- create group 1 + threadDelay 100000 alice ##> "/g team" alice <## "group #team is created" alice <## "to add members use /a team or /create link #team" diff --git a/tests/ChatTests/Profiles.hs b/tests/ChatTests/Profiles.hs index 6971898ddf..23004677c9 100644 --- a/tests/ChatTests/Profiles.hs +++ b/tests/ChatTests/Profiles.hs @@ -2169,7 +2169,7 @@ testSetUITheme = a <## "you've shared main profile with this contact" a <## "connection not verified, use /code command to see security code" a <## "quantum resistant end-to-end encryption" - a <## "peer chat protocol version range: (Version 1, Version 8)" + a <## "peer chat protocol version range: (Version 1, Version 9)" groupInfo a = do a <## "group ID: 1" a <## "current members: 1" diff --git a/tests/ProtocolTests.hs b/tests/ProtocolTests.hs index 0bc801e824..d9552452cd 100644 --- a/tests/ProtocolTests.hs +++ b/tests/ProtocolTests.hs @@ -133,7 +133,7 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do "{\"v\":\"1\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"}}}" ##==## ChatMessage chatInitialVRange (Just $ SharedMsgId "\1\2\3\4") (XMsgNew (MCSimple (extMsgContent (MCText "hello") Nothing))) it "x.msg.new chat message with chat version range" $ - "{\"v\":\"1-8\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"}}}" + "{\"v\":\"1-9\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"}}}" ##==## ChatMessage supportedChatVRange (Just $ SharedMsgId "\1\2\3\4") (XMsgNew (MCSimple (extMsgContent (MCText "hello") Nothing))) it "x.msg.new quote" $ "{\"v\":\"1\",\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello to you too\",\"type\":\"text\"},\"quote\":{\"content\":{\"text\":\"hello there!\",\"type\":\"text\"},\"msgRef\":{\"msgId\":\"BQYHCA==\",\"sent\":true,\"sentAt\":\"1970-01-01T00:00:01.000000001Z\"}}}}" @@ -243,13 +243,13 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do "{\"v\":\"1\",\"event\":\"x.grp.mem.new\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" #==# XGrpMemNew MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Nothing, profile = testProfile} it "x.grp.mem.new with member chat version range" $ - "{\"v\":\"1\",\"event\":\"x.grp.mem.new\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-8\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" + "{\"v\":\"1\",\"event\":\"x.grp.mem.new\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-9\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" #==# XGrpMemNew MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Just $ ChatVersionRange supportedChatVRange, profile = testProfile} it "x.grp.mem.intro" $ "{\"v\":\"1\",\"event\":\"x.grp.mem.intro\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" #==# XGrpMemIntro MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Nothing, profile = testProfile} Nothing it "x.grp.mem.intro with member chat version range" $ - "{\"v\":\"1\",\"event\":\"x.grp.mem.intro\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-8\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" + "{\"v\":\"1\",\"event\":\"x.grp.mem.intro\",\"params\":{\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-9\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" #==# XGrpMemIntro MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Just $ ChatVersionRange supportedChatVRange, profile = testProfile} Nothing it "x.grp.mem.intro with member restrictions" $ "{\"v\":\"1\",\"event\":\"x.grp.mem.intro\",\"params\":{\"memberRestrictions\":{\"restriction\":\"blocked\"},\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" @@ -264,7 +264,7 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do "{\"v\":\"1\",\"event\":\"x.grp.mem.fwd\",\"params\":{\"memberIntro\":{\"directConnReq\":\"simplex:/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-3%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D2-3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\",\"groupConnReq\":\"simplex:/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-3%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D2-3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"},\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" #==# XGrpMemFwd MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Nothing, profile = testProfile} IntroInvitation {groupConnReq = testConnReq, directConnReq = Just testConnReq} it "x.grp.mem.fwd with member chat version range and w/t directConnReq" $ - "{\"v\":\"1\",\"event\":\"x.grp.mem.fwd\",\"params\":{\"memberIntro\":{\"groupConnReq\":\"simplex:/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-3%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D2-3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"},\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-8\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" + "{\"v\":\"1\",\"event\":\"x.grp.mem.fwd\",\"params\":{\"memberIntro\":{\"groupConnReq\":\"simplex:/invitation#/?v=1&smp=smp%3A%2F%2F1234-w%3D%3D%40smp.simplex.im%3A5223%2F3456-w%3D%3D%23%2F%3Fv%3D1-3%26dh%3DMCowBQYDK2VuAyEAjiswwI3O_NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o%253D&e2e=v%3D2-3%26x3dh%3DMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D%2CMEIwBQYDK2VvAzkAmKuSYeQ_m0SixPDS8Wq8VBaTS1cW-Lp0n0h4Diu-kUpR-qXx4SDJ32YGEFoGFGSbGPry5Ychr6U%3D\"},\"memberInfo\":{\"memberRole\":\"admin\",\"memberId\":\"AQIDBA==\",\"v\":\"1-9\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}}" #==# XGrpMemFwd MemberInfo {memberId = MemberId "\1\2\3\4", memberRole = GRAdmin, v = Just $ ChatVersionRange supportedChatVRange, profile = testProfile} IntroInvitation {groupConnReq = testConnReq, directConnReq = Nothing} it "x.grp.mem.info" $ "{\"v\":\"1\",\"event\":\"x.grp.mem.info\",\"params\":{\"memberId\":\"AQIDBA==\",\"profile\":{\"fullName\":\"Alice\",\"displayName\":\"alice\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\",\"preferences\":{\"reactions\":{\"allow\":\"yes\"},\"voice\":{\"allow\":\"yes\"}}}}}"