diff --git a/.gitignore b/.gitignore index e3ea5d267b..645b55ec9d 100644 --- a/.gitignore +++ b/.gitignore @@ -61,6 +61,7 @@ website/package/generated* # Ignore build tool output, e.g. code coverage website/.nyc_output/ website/coverage/ +result # Ignore API documentation website/api-docs/ diff --git a/README.md b/README.md index e62fb8c8b3..c2df084477 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ You must: Messages not following these rules will be deleted, the right to send messages may be revoked, and the access to the new members to the group may be temporarily restricted, to prevent re-joining under a different name - our imperfect group moderation does not have a better solution at the moment. -You can join an English-speaking users group if you want to ask any questions: [#SimpleX users group](https://simplex.chat/contact#/?v=1-4&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2Fos8FftfoV8zjb2T89fUEjJtF7y64p5av%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAQqMgh0fw2lPhjn3PDIEfAKA_E0-gf8Hr8zzhYnDivRs%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22lBPiveK2mjfUH43SN77R0w%3D%3D%22%7D) +You can join an English-speaking users group if you want to ask any questions: [#SimpleX users group](https://simplex.chat/contact#/?v=2-4&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2Fos8FftfoV8zjb2T89fUEjJtF7y64p5av%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAQqMgh0fw2lPhjn3PDIEfAKA_E0-gf8Hr8zzhYnDivRs%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22lBPiveK2mjfUH43SN77R0w%3D%3D%22%7D) There is also a group [#simplex-devs](https://simplex.chat/contact#/?v=1-4&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FvYCRjIflKNMGYlfTkuHe4B40qSlQ0439%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAHNdcqNbzXZhyMoSBjT2R0-Eb1EPaLyUg3KZjn-kmM1w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22PD20tcXjw7IpkkMCfR6HLA%3D%3D%22%7D) for developers who build on SimpleX platform: @@ -83,7 +83,7 @@ There is also a group [#simplex-devs](https://simplex.chat/contact#/?v=1-4&smp=s There are groups in other languages, that we have the apps interface translated into. These groups are for testing, and asking questions to other SimpleX Chat users: -[\#SimpleX-DE](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FkIEl7OQzcp-J6aDmjdlQbRJwqkcZE7XR%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAR16PCu02MobRmKAsjzhDWMZcWP9hS8l5AUZi-Gs8z18%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22puYPMCQt11yPUvgmI5jCiw%3D%3D%22%7D) (German-speaking), [\#SimpleX-ES](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FaJ8O1O8A8GbeoaHTo_V8dcefaCl7ouPb%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEA034qWTA3sWcTsi6aWhNf9BA34vKVCFaEBdP2R66z6Ao%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22wiZ1v_wNjLPlT-nCSB-bRA%3D%3D%22%7D) (Spanish-speaking), [\#SimpleX-FR](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2Fhpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg%3D%40smp5.simplex.im%2FvIHQDxTor53nwnWWTy5cHNwQQAdWN5Hw%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAPdgK1eBnETmgiqEQufbUkydKBJafoRx4iRrtrC2NAGc%253D%26srv%3Djjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%221FyUryBPza-1ZFFE80Ekbg%3D%3D%22%7D) (French-speaking), [\#SimpleX-RU](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FXZyt3hJmWsycpN7Dqve_wbrAqb6myk1R%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAMFVIoytozTEa_QXOgoZFq_oe0IwZBYKvW50trSFXzXo%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22xz05ngjA3pNIxLZ32a8Vxg%3D%3D%22%7D) (Russian-speaking), [\#SimpleX-IT](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2F0weR-ZgDUl7ruOtI_8TZwEsnJP6UiImA%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAq4PSThO9Fvb5ydF48wB0yNbpzCbuQJCW3vZ9BGUfcxk%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22e-iceLA0SctC62eARgYDWg%3D%3D%22%7D) (Italian-speaking). +[\#SimpleX-DE](https://simplex.chat/contact#/?v=1-4&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FmfiivxDKWFuowXrQOp11jsY8TuP__rBL%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAiz3pKNwvKudckFYMUfgoT0s96B0jfZ7ALHAu7rtE9HQ%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22jZeJpXGrRXQJU_-MSJ_v2A%3D%3D%22%7D) (German-speaking), [\#SimpleX-ES](https://simplex.chat/contact#/?v=2-4&smp=smp%3A%2F%2Fhpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg%3D%40smp5.simplex.im%2FJ5ES83pJimY2BRklS8fvy_iQwIU37xra%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEA0F0STP6UqN_12_k2cjjTrIjFgBGeWhOAmbY1qlk3pnM%253D%26srv%3Djjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22VmUU0fqmYdCRmVCyvStvHA%3D%3D%22%7D) (Spanish-speaking), [\#SimpleX-FR](https://simplex.chat/contact#/?v=2-7&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FxCHBE_6PBRMqNEpm4UQDHXb9cz-mN7dd%23%2F%3Fv%3D1-3%26dh%3DMCowBQYDK2VuAyEAetqlcM7zTCRw-iatnwCrvpJSto7lq5Yv6AsBMWv7GSM%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22foO5Xw4hhjOa_x7zET7otw%3D%3D%22%7D) (French-speaking), [\#SimpleX-RU](https://simplex.chat/contact#/?v=2-4&smp=smp%3A%2F%2Fhpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg%3D%40smp5.simplex.im%2FVXQTB0J2lLjYkgjWByhl6-1qmb5fgZHh%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAI6JaEWezfSwvcoTEkk6au-gkjrXR2ew2OqZYMYBvayk%253D%26srv%3Djjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22ORH9OEe8Duissh-hslfeVg%3D%3D%22%7D) (Russian-speaking), [\#SimpleX-IT](https://simplex.chat/contact#/?v=2-7&smp=smp%3A%2F%2Fhpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg%3D%40smp5.simplex.im%2FqpHu0psOUdYfc11yQCzSyq5JhijrBzZT%23%2F%3Fv%3D1-3%26dh%3DMCowBQYDK2VuAyEACZ_7fbwlM45wl6cGif8cY47oPQ_AMdP0ATqOYLA6zHY%253D%26srv%3Djjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%229uRQRTir3ealdcSfB0zsrw%3D%3D%22%7D) (Italian-speaking). You can join either by opening these links in the app or by opening them in a desktop browser and scanning the QR code. diff --git a/apps/ios/Shared/Assets.xcassets/checkmark.2.symbolset/Contents.json b/apps/ios/Shared/Assets.xcassets/checkmark.2.symbolset/Contents.json new file mode 100644 index 0000000000..8e38b499dd --- /dev/null +++ b/apps/ios/Shared/Assets.xcassets/checkmark.2.symbolset/Contents.json @@ -0,0 +1,12 @@ +{ + "info": { + "author": "xcode", + "version": 1 + }, + "symbols": [ + { + "filename": "checkmark.2.svg", + "idiom": "universal" + } + ] +} \ No newline at end of file diff --git a/apps/ios/Shared/Assets.xcassets/checkmark.2.symbolset/checkmark.2.svg b/apps/ios/Shared/Assets.xcassets/checkmark.2.symbolset/checkmark.2.svg new file mode 100644 index 0000000000..577fa1db76 --- /dev/null +++ b/apps/ios/Shared/Assets.xcassets/checkmark.2.symbolset/checkmark.2.svg @@ -0,0 +1,227 @@ + + + checkmark.2 + + + + + + + Weight/Scale Variations + + + Ultralight + + + Thin + + + Light + + + Regular + + + Medium + + + Semibold + + + Bold + + + Heavy + + + Black + + + + + + + + + + + + + Design Variations + + + Symbols are supported in up to nine weights and three scales. + + + For optimal layout with text and other symbols, vertically align + + + symbols with the adjacent text. + + + + + + + + + Margins + + + Leading and trailing margins on the left and right side of each symbol + + + + can be adjusted by modifying the x-location of the margin guidelines. + + + + Modifications are automatically applied proportionally to all + + + scales and weights. + + + + + + Exporting + + + Symbols should be outlined when exporting to ensure the + + + design is preserved when submitting to Xcode. + + + Template v.5.0 + + + Requires Xcode 15 or greater + + + Generated from double.checkmark + + + Typeset at 100.0 points + + + Small + + + Medium + + + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/ios/Shared/Assets.xcassets/checkmark.wide.symbolset/Contents.json b/apps/ios/Shared/Assets.xcassets/checkmark.wide.symbolset/Contents.json new file mode 100644 index 0000000000..11a91cb811 --- /dev/null +++ b/apps/ios/Shared/Assets.xcassets/checkmark.wide.symbolset/Contents.json @@ -0,0 +1,12 @@ +{ + "info": { + "author": "xcode", + "version": 1 + }, + "symbols": [ + { + "filename": "checkmark.wide.svg", + "idiom": "universal" + } + ] +} \ No newline at end of file diff --git a/apps/ios/Shared/Assets.xcassets/checkmark.wide.symbolset/checkmark.wide.svg b/apps/ios/Shared/Assets.xcassets/checkmark.wide.symbolset/checkmark.wide.svg new file mode 100644 index 0000000000..b5dfc6b3de --- /dev/null +++ b/apps/ios/Shared/Assets.xcassets/checkmark.wide.symbolset/checkmark.wide.svg @@ -0,0 +1,218 @@ + + + checkmark.wide + + + + + + + Weight/Scale Variations + + + Ultralight + + + Thin + + + Light + + + Regular + + + Medium + + + Semibold + + + Bold + + + Heavy + + + Black + + + + + + + + + + + + + Design Variations + + + Symbols are supported in up to nine weights and three scales. + + + For optimal layout with text and other symbols, vertically align + + + symbols with the adjacent text. + + + + + + + + + Margins + + + Leading and trailing margins on the left and right side of each symbol + + + + can be adjusted by modifying the x-location of the margin guidelines. + + + + Modifications are automatically applied proportionally to all + + + scales and weights. + + + + + + Exporting + + + Symbols should be outlined when exporting to ensure the + + + design is preserved when submitting to Xcode. + + + Template v.5.0 + + + Requires Xcode 15 or greater + + + Generated from double.checkmark + + + Typeset at 100.0 points + + + Small + + + Medium + + + Large + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/ios/Shared/ContentView.swift b/apps/ios/Shared/ContentView.swift index 1f41e7c31d..6d600f33ff 100644 --- a/apps/ios/Shared/ContentView.swift +++ b/apps/ios/Shared/ContentView.swift @@ -29,12 +29,12 @@ struct ContentView: View { @AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false @AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = false @AppStorage(DEFAULT_NOTIFICATION_ALERT_SHOWN) private var notificationAlertShown = false - @State private var showSettings = false @State private var showWhatsNew = false @State private var showChooseLAMode = false @State private var showSetPasscode = false @State private var waitingForOrPassedAuth = true @State private var chatListActionSheet: ChatListActionSheet? = nil + @State private var chatListUserPickerSheet: UserPickerSheet? = nil private let callTopPadding: CGFloat = 40 @@ -86,7 +86,7 @@ struct ContentView: View { callView(call) } - if !showSettings, let la = chatModel.laRequest { + if chatListUserPickerSheet == nil, let la = chatModel.laRequest { LocalAuthView(authRequest: la) .onDisappear { // this flag is separate from accessAuthenticated to show initializationView while we wait for authentication @@ -109,9 +109,6 @@ struct ContentView: View { } } .alert(isPresented: $alertManager.presentAlert) { alertManager.alertView! } - .sheet(isPresented: $showSettings) { - SettingsView(showSettings: $showSettings) - } .confirmationDialog("SimpleX Lock mode", isPresented: $showChooseLAMode, titleVisibility: .visible) { Button("System authentication") { initialEnableLA() } Button("Passcode entry") { showSetPasscode = true } @@ -253,7 +250,7 @@ struct ContentView: View { private func mainView() -> some View { ZStack(alignment: .top) { - ChatListView(showSettings: $showSettings).privacySensitive(protectScreen) + ChatListView(activeUserPickerSheet: $chatListUserPickerSheet).privacySensitive(protectScreen) .onAppear { requestNtfAuthorization() // Local Authentication notice is to be shown on next start after onboarding is complete @@ -300,9 +297,18 @@ struct ContentView: View { if let contactId = contacts?.first?.personHandle?.value, let chat = chatModel.getChat(contactId), case let .direct(contact) = chat.chatInfo { - logger.debug("callToRecentContact: schedule call") - DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - CallController.shared.startCall(contact, mediaType) + let activeCall = chatModel.activeCall + // This line works when a user clicks on a video button in CallKit UI while in call. + // The app tries to make another call to the same contact and overwite activeCall instance making its state broken + if let activeCall, contactId == activeCall.contact.id, mediaType == .video, !activeCall.hasVideo { + Task { + await chatModel.callCommand.processCommand(.media(source: .camera, enable: true)) + } + } else if activeCall == nil { + logger.debug("callToRecentContact: schedule call") + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + CallController.shared.startCall(contact, mediaType) + } } } } diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index caa54887f7..8525a141e3 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -61,7 +61,7 @@ class ItemsModel: ObservableObject { init() { publisher - .throttle(for: 0.25, scheduler: DispatchQueue.main, latest: true) + .throttle(for: 0.2, scheduler: DispatchQueue.main, latest: true) .sink { self.objectWillChange.send() } .store(in: &bag) } @@ -123,6 +123,14 @@ class NetworkModel: ObservableObject { } } +/// ChatItemWithMenu can depend on previous or next item for it's appearance +/// This dummy model is used to force an update of all chat items, +/// when they might have changed appearance. +class ChatItemDummyModel: ObservableObject { + static let shared = ChatItemDummyModel() + func sendUpdate() { objectWillChange.send() } +} + final class ChatModel: ObservableObject { @Published var onboardingStage: OnboardingStage? @Published var setDeliveryReceipts = false @@ -427,19 +435,17 @@ final class ChatModel: ObservableObject { private func _upsertChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) -> Bool { if let i = getChatItemIndex(cItem) { - withConditionalAnimation { - _updateChatItem(at: i, with: cItem) - } + _updateChatItem(at: i, with: cItem) + ChatItemDummyModel.shared.sendUpdate() return false } else { - withConditionalAnimation(itemAnimation()) { - var ci = cItem - if let status = chatItemStatuses.removeValue(forKey: ci.id), case .sndNew = ci.meta.itemStatus { - ci.meta.itemStatus = status - } - im.reversedChatItems.insert(ci, at: hasLiveDummy ? 1 : 0) - im.itemAdded = true + var ci = cItem + if let status = chatItemStatuses.removeValue(forKey: ci.id), case .sndNew = ci.meta.itemStatus { + ci.meta.itemStatus = status } + im.reversedChatItems.insert(ci, at: hasLiveDummy ? 1 : 0) + im.itemAdded = true + ChatItemDummyModel.shared.sendUpdate() return true } @@ -520,7 +526,7 @@ final class ChatModel: ObservableObject { } func updateCurrentUserUiThemes(uiThemes: ThemeModeOverrides?) { - guard var current = currentUser else { return } + guard var current = currentUser, current.uiThemes != uiThemes else { return } current.uiThemes = uiThemes let i = users.firstIndex(where: { $0.user.userId == current.userId }) if let i { @@ -556,6 +562,7 @@ final class ChatModel: ObservableObject { // update preview _updateChat(cInfo.id) { chat in self.decreaseUnreadCounter(user: self.currentUser!, by: chat.chatStats.unreadCount) + self.updateFloatingButtons(unreadCount: 0) chat.chatStats = ChatStats() } // update current chat @@ -572,6 +579,12 @@ final class ChatModel: ObservableObject { } } + private func updateFloatingButtons(unreadCount: Int) { + let fbm = ChatView.FloatingButtonModel.shared + fbm.totalUnread = unreadCount + fbm.objectWillChange.send() + } + func markChatItemsRead(_ cInfo: ChatInfo, aboveItem: ChatItem? = nil) { if let cItem = aboveItem { if chatId == cInfo.id, let i = getChatItemIndex(cItem) { @@ -590,6 +603,7 @@ final class ChatModel: ObservableObject { if markedCount > 0 { chat.chatStats.unreadCount -= markedCount self.decreaseUnreadCounter(user: self.currentUser!, by: markedCount) + self.updateFloatingButtons(unreadCount: chat.chatStats.unreadCount) } } } @@ -619,19 +633,15 @@ final class ChatModel: ObservableObject { } } - func markChatItemRead(_ cInfo: ChatInfo, _ cItem: ChatItem) async { - if chatId == cInfo.id, - let itemIndex = getChatItemIndex(cItem), - im.reversedChatItems[itemIndex].isRcvNew { - await MainActor.run { - withTransaction(Transaction()) { - // update current chat - markChatItemRead_(itemIndex) - // update preview - unreadCollector.changeUnreadCounter(cInfo.id, by: -1) + func markChatItemsRead(_ cInfo: ChatInfo, _ itemIds: [ChatItem.ID]) { + if self.chatId == cInfo.id { + for itemId in itemIds { + if let i = im.reversedChatItems.firstIndex(where: { $0.id == itemId }) { + markChatItemRead_(i) } } } + self.unreadCollector.changeUnreadCounter(cInfo.id, by: -itemIds.count) } private let unreadCollector = UnreadCollector() @@ -657,9 +667,10 @@ final class ChatModel: ObservableObject { } func changeUnreadCounter(_ chatId: ChatId, by count: Int) { - DispatchQueue.main.async { - self.unreadCounts[chatId] = (self.unreadCounts[chatId] ?? 0) + count + if chatId == ChatModel.shared.chatId { + ChatView.FloatingButtonModel.shared.totalUnread += count } + self.unreadCounts[chatId] = (self.unreadCounts[chatId] ?? 0) + count subject.send() } } @@ -881,35 +892,6 @@ final class ChatModel: ObservableObject { _ = upsertGroupMember(groupInfo, updatedMember) } } - - func unreadChatItemCounts(itemsInView: Set) -> UnreadChatItemCounts { - var i = 0 - var totalBelow = 0 - var unreadBelow = 0 - while i < im.reversedChatItems.count - 1 && !itemsInView.contains(im.reversedChatItems[i].viewId) { - totalBelow += 1 - if im.reversedChatItems[i].isRcvNew { - unreadBelow += 1 - } - i += 1 - } - return UnreadChatItemCounts( - // TODO these thresholds account for the fact that items are still "visible" while - // covered by compose area, they should be replaced with the actual height in pixels below the screen. - isNearBottom: totalBelow < 15, - isReallyNearBottom: totalBelow < 2, - unreadBelow: unreadBelow - ) - } - - func topItemInView(itemsInView: Set) -> ChatItem? { - let maxIx = im.reversedChatItems.count - 1 - var i = 0 - let inView = { itemsInView.contains(self.im.reversedChatItems[$0].viewId) } - while i < maxIx && !inView(i) { i += 1 } - while i < maxIx && inView(i) { i += 1 } - return im.reversedChatItems[min(i - 1, maxIx)] - } } struct ShowingInvitation { @@ -922,12 +904,6 @@ struct NTFContactRequest { var chatId: String } -struct UnreadChatItemCounts: Equatable { - var isNearBottom: Bool - var isReallyNearBottom: Bool - var unreadBelow: Int -} - final class Chat: ObservableObject, Identifiable, ChatLike { @Published var chatInfo: ChatInfo @Published var chatItems: [ChatItem] diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 6c5d5504e6..a0a32156c4 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -357,17 +357,23 @@ func apiGetChatItemInfo(type: ChatType, id: Int64, itemId: Int64) async throws - throw r } -func apiForwardChatItem(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemId: Int64, ttl: Int?) async -> ChatItem? { - let cmd: ChatCommand = .apiForwardChatItem(toChatType: toChatType, toChatId: toChatId, fromChatType: fromChatType, fromChatId: fromChatId, itemId: itemId, ttl: ttl) +func apiPlanForwardChatItems(type: ChatType, id: Int64, itemIds: [Int64]) async throws -> ([Int64], ForwardConfirmation?) { + let r = await chatSendCmd(.apiPlanForwardChatItems(toChatType: type, toChatId: id, itemIds: itemIds)) + if case let .forwardPlan(_, chatItemIds, forwardConfimation) = r { return (chatItemIds, forwardConfimation) } + throw r +} + +func apiForwardChatItems(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemIds: [Int64], ttl: Int?) async -> [ChatItem]? { + let cmd: ChatCommand = .apiForwardChatItems(toChatType: toChatType, toChatId: toChatId, fromChatType: fromChatType, fromChatId: fromChatId, itemIds: itemIds, ttl: ttl) return await processSendMessageCmd(toChatType: toChatType, cmd: cmd) } -func apiSendMessage(type: ChatType, id: Int64, file: CryptoFile?, quotedItemId: Int64?, msg: MsgContent, live: Bool = false, ttl: Int? = nil) async -> ChatItem? { - let cmd: ChatCommand = .apiSendMessage(type: type, id: id, file: file, quotedItemId: quotedItemId, msg: msg, live: live, ttl: ttl) +func apiSendMessages(type: ChatType, id: Int64, live: Bool = false, ttl: Int? = nil, composedMessages: [ComposedMessage]) async -> [ChatItem]? { + let cmd: ChatCommand = .apiSendMessages(type: type, id: id, live: live, ttl: ttl, composedMessages: composedMessages) return await processSendMessageCmd(toChatType: type, cmd: cmd) } -private func processSendMessageCmd(toChatType: ChatType, cmd: ChatCommand) async -> ChatItem? { +private func processSendMessageCmd(toChatType: ChatType, cmd: ChatCommand) async -> [ChatItem]? { let chatModel = ChatModel.shared let r: ChatResponse if toChatType == .direct { @@ -380,10 +386,13 @@ private func processSendMessageCmd(toChatType: ChatType, cmd: ChatCommand) async } }) r = await chatSendCmd(cmd, bgTask: false) - if case let .newChatItem(_, aChatItem) = r { - cItem = aChatItem.chatItem - chatModel.messageDelivery[aChatItem.chatItem.id] = endTask - return cItem + if case let .newChatItems(_, aChatItems) = r { + let cItems = aChatItems.map { $0.chatItem } + if let cItemLast = cItems.last { + cItem = cItemLast + chatModel.messageDelivery[cItemLast.id] = endTask + } + return cItems } if let networkErrorAlert = networkErrorAlert(r) { AlertManager.shared.showAlert(networkErrorAlert) @@ -394,18 +403,18 @@ private func processSendMessageCmd(toChatType: ChatType, cmd: ChatCommand) async return nil } else { r = await chatSendCmd(cmd, bgDelay: msgDelay) - if case let .newChatItem(_, aChatItem) = r { - return aChatItem.chatItem + if case let .newChatItems(_, aChatItems) = r { + return aChatItems.map { $0.chatItem } } sendMessageErrorAlert(r) return nil } } -func apiCreateChatItem(noteFolderId: Int64, file: CryptoFile?, msg: MsgContent) async -> ChatItem? { - let r = await chatSendCmd(.apiCreateChatItem(noteFolderId: noteFolderId, file: file, msg: msg)) - if case let .newChatItem(_, aChatItem) = r { return aChatItem.chatItem } - createChatItemErrorAlert(r) +func apiCreateChatItems(noteFolderId: Int64, composedMessages: [ComposedMessage]) async -> [ChatItem]? { + let r = await chatSendCmd(.apiCreateChatItems(noteFolderId: noteFolderId, composedMessages: composedMessages)) + if case let .newChatItems(_, aChatItems) = r { return aChatItems.map { $0.chatItem } } + createChatItemsErrorAlert(r) return nil } @@ -417,8 +426,8 @@ private func sendMessageErrorAlert(_ r: ChatResponse) { ) } -private func createChatItemErrorAlert(_ r: ChatResponse) { - logger.error("apiCreateChatItem error: \(String(describing: r))") +private func createChatItemsErrorAlert(_ r: ChatResponse) { + logger.error("apiCreateChatItems error: \(String(describing: r))") AlertManager.shared.showAlertMsg( title: "Error creating message", message: "Error: \(responseError(r))" @@ -453,7 +462,7 @@ func apiGetNtfToken() -> (DeviceToken?, NtfTknStatus?, NotificationsMode, String let r = chatSendCmdSync(.apiGetNtfToken) switch r { case let .ntfToken(token, status, ntfMode, ntfServer): return (token, status, ntfMode, ntfServer) - case .chatCmdError(_, .errorAgent(.CMD(.PROHIBITED))): return (nil, nil, .off, nil) + case .chatCmdError(_, .errorAgent(.CMD(.PROHIBITED, _))): return (nil, nil, .off, nil) default: logger.debug("apiGetNtfToken response: \(String(describing: r))") return (nil, nil, .off, nil) @@ -576,12 +585,18 @@ func apiContactInfo(_ contactId: Int64) async throws -> (ConnectionStats?, Profi throw r } -func apiGroupMemberInfo(_ groupId: Int64, _ groupMemberId: Int64) throws -> (GroupMember, ConnectionStats?) { +func apiGroupMemberInfoSync(_ groupId: Int64, _ groupMemberId: Int64) throws -> (GroupMember, ConnectionStats?) { let r = chatSendCmdSync(.apiGroupMemberInfo(groupId: groupId, groupMemberId: groupMemberId)) if case let .groupMemberInfo(_, _, member, connStats_) = r { return (member, connStats_) } throw r } +func apiGroupMemberInfo(_ groupId: Int64, _ groupMemberId: Int64) async throws -> (GroupMember, ConnectionStats?) { + let r = await chatSendCmd(.apiGroupMemberInfo(groupId: groupId, groupMemberId: groupMemberId)) + if case let .groupMemberInfo(_, _, member, connStats_) = r { return (member, connStats_) } + throw r +} + func apiContactQueueInfo(_ contactId: Int64) async throws -> (RcvMsgInfo?, ServerQueueInfo) { let r = await chatSendCmd(.apiContactQueueInfo(contactId: contactId)) if case let .queueInfo(_, rcvMsgInfo, queueInfo) = r { return (rcvMsgInfo, queueInfo) } @@ -636,8 +651,8 @@ func apiGetContactCode(_ contactId: Int64) async throws -> (Contact, String) { throw r } -func apiGetGroupMemberCode(_ groupId: Int64, _ groupMemberId: Int64) throws -> (GroupMember, String) { - let r = chatSendCmdSync(.apiGetGroupMemberCode(groupId: groupId, groupMemberId: groupMemberId)) +func apiGetGroupMemberCode(_ groupId: Int64, _ groupMemberId: Int64) async throws -> (GroupMember, String) { + let r = await chatSendCmd(.apiGetGroupMemberCode(groupId: groupId, groupMemberId: groupMemberId)) if case let .groupMemberCode(_, _, member, connectionCode) = r { return (member, connectionCode) } throw r } @@ -673,6 +688,13 @@ func apiSetConnectionIncognito(connId: Int64, incognito: Bool) async throws -> P throw r } +func apiChangeConnectionUser(connId: Int64, userId: Int64) async throws -> PendingContactConnection? { + let r = await chatSendCmd(.apiChangeConnectionUser(connId: connId, userId: userId)) + + if case let .connectionUserChanged(_, _, toConnection, _) = r {return toConnection} + throw r +} + func apiConnectPlan(connReq: String) async throws -> ConnectionPlan { let userId = try currentUserId("apiConnectPlan") let r = await chatSendCmd(.apiConnectPlan(userId: userId, connReq: connReq)) @@ -990,6 +1012,10 @@ func apiChatRead(type: ChatType, id: Int64, itemRange: (Int64, Int64)) async thr try await sendCommandOkResp(.apiChatRead(type: type, id: id, itemRange: itemRange)) } +func apiChatItemsRead(type: ChatType, id: Int64, itemIds: [Int64]) async throws { + try await sendCommandOkResp(.apiChatItemsRead(type: type, id: id, itemIds: itemIds)) +} + func apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool) async throws { try await sendCommandOkResp(.apiChatUnread(type: type, id: id, unreadChat: unreadChat)) } @@ -1025,77 +1051,122 @@ func standaloneFileInfo(url: String, ctrl: chat_ctrl? = nil) async -> MigrationF } func receiveFile(user: any UserLike, fileId: Int64, userApprovedRelays: Bool = false, auto: Bool = false) async { - if let chatItem = await apiReceiveFile( - fileId: fileId, - userApprovedRelays: userApprovedRelays || !privacyAskToApproveRelaysGroupDefault.get(), - encrypted: privacyEncryptLocalFilesGroupDefault.get(), + await receiveFiles( + user: user, + fileIds: [fileId], + userApprovedRelays: userApprovedRelays, auto: auto - ) { - await chatItemSimpleUpdate(user, chatItem) - } + ) } -func apiReceiveFile(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool, inline: Bool? = nil, auto: Bool = false) async -> AChatItem? { - let r = await chatSendCmd(.receiveFile(fileId: fileId, userApprovedRelays: userApprovedRelays, encrypted: encrypted, inline: inline)) - let am = AlertManager.shared - if case let .rcvFileAccepted(_, chatItem) = r { return chatItem } - if case .rcvFileAcceptedSndCancelled = r { - logger.debug("apiReceiveFile error: sender cancelled file transfer") - if !auto { - am.showAlertMsg( - title: "Cannot receive file", - message: "Sender cancelled file transfer." +func receiveFiles(user: any UserLike, fileIds: [Int64], userApprovedRelays: Bool = false, auto: Bool = false) async { + var fileIdsToApprove = [Int64]() + var srvsToApprove = Set() + var otherFileErrs = [ChatResponse]() + + for fileId in fileIds { + let r = await chatSendCmd( + .receiveFile( + fileId: fileId, + userApprovedRelays: userApprovedRelays || !privacyAskToApproveRelaysGroupDefault.get(), + encrypted: privacyEncryptLocalFilesGroupDefault.get(), + inline: nil ) + ) + switch r { + case let .rcvFileAccepted(_, chatItem): + await chatItemSimpleUpdate(user, chatItem) + default: + if let chatError = chatError(r) { + switch chatError { + case let .fileNotApproved(fileId, unknownServers): + fileIdsToApprove.append(fileId) + srvsToApprove.formUnion(unknownServers) + default: + otherFileErrs.append(r) + } + } } - } else if let networkErrorAlert = networkErrorAlert(r) { - logger.error("apiReceiveFile network error: \(String(describing: r))") - if !auto { - am.showAlert(networkErrorAlert) + } + + if !auto { + let otherErrsStr = if otherFileErrs.isEmpty { + "" + } else if otherFileErrs.count == 1 { + "\(otherFileErrs[0])" + } else if otherFileErrs.count == 2 { + "\(otherFileErrs[0])\n\(otherFileErrs[1])" + } else { + "\(otherFileErrs[0])\n\(otherFileErrs[1])\nand \(otherFileErrs.count - 2) other error(s)" } - } else { - switch chatError(r) { - case .fileCancelled: - logger.debug("apiReceiveFile ignoring fileCancelled error") - case .fileAlreadyReceiving: - logger.debug("apiReceiveFile ignoring fileAlreadyReceiving error") - case let .fileNotApproved(fileId, unknownServers): - logger.debug("apiReceiveFile fileNotApproved error") - if !auto { - let srvs = unknownServers.map { s in + + // If there are not approved files, alert is shown the same way both in case of singular and plural files reception + if !fileIdsToApprove.isEmpty { + let srvs = srvsToApprove + .map { s in if let srv = parseServerAddress(s), !srv.hostnames.isEmpty { srv.hostnames[0] } else { serverHost(s) } } - am.showAlert(Alert( - title: Text("Unknown servers!"), - message: Text("Without Tor or VPN, your IP address will be visible to these XFTP relays: \(srvs.sorted().joined(separator: ", "))."), - primaryButton: .default( - Text("Download"), - action: { - Task { - logger.debug("apiReceiveFile fileNotApproved alert - in Task") - if let user = ChatModel.shared.currentUser { - await receiveFile(user: user, fileId: fileId, userApprovedRelays: true) - } + .sorted() + .joined(separator: ", ") + let fIds = fileIdsToApprove + await MainActor.run { + showAlert( + title: NSLocalizedString("Unknown servers!", comment: "alert title"), + message: ( + String.localizedStringWithFormat(NSLocalizedString("Without Tor or VPN, your IP address will be visible to these XFTP relays: %@.", comment: "alert message"), srvs) + + (otherErrsStr != "" ? "\n\n" + String.localizedStringWithFormat(NSLocalizedString("Other file errors:\n%@", comment: "alert message"), otherErrsStr) : "") + ), + buttonTitle: NSLocalizedString("Download", comment: "alert button"), + buttonAction: { + Task { + logger.debug("apiReceiveFile fileNotApproved alert - in Task") + if let user = ChatModel.shared.currentUser { + await receiveFiles(user: user, fileIds: fIds, userApprovedRelays: true) } } - ), - secondaryButton: .cancel() - )) + }, + cancelButton: true + ) } - default: - logger.error("apiReceiveFile error: \(String(describing: r))") - if !auto { - am.showAlertMsg( - title: "Error receiving file", - message: "Error: \(responseError(r))" + } else if otherFileErrs.count == 1 { // If there is a single other error, we differentiate on it + let errorResponse = otherFileErrs.first! + switch errorResponse { + case let .rcvFileAcceptedSndCancelled(_, rcvFileTransfer): + logger.debug("receiveFiles error: sender cancelled file transfer \(rcvFileTransfer.fileId)") + await MainActor.run { + showAlert( + NSLocalizedString("Cannot receive file", comment: "alert title"), + message: NSLocalizedString("Sender cancelled file transfer.", comment: "alert message") + ) + } + default: + if let chatError = chatError(errorResponse) { + switch chatError { + case .fileCancelled, .fileAlreadyReceiving: + logger.debug("receiveFiles ignoring FileCancelled or FileAlreadyReceiving error") + default: + await MainActor.run { + showAlert( + NSLocalizedString("Error receiving file", comment: "alert title"), + message: responseError(errorResponse) + ) + } + } + } + } + } else if otherFileErrs.count > 1 { // If there are multiple other errors, we show general alert + await MainActor.run { + showAlert( + NSLocalizedString("Error receiving file", comment: "alert title"), + message: String.localizedStringWithFormat(NSLocalizedString("File errors:\n%@", comment: "alert message"), otherErrsStr) ) } } } - return nil } func cancelFile(user: User, fileId: Int64) async { @@ -1277,11 +1348,23 @@ func markChatUnread(_ chat: Chat, unreadChat: Bool = true) async { func apiMarkChatItemRead(_ cInfo: ChatInfo, _ cItem: ChatItem) async { do { - logger.debug("apiMarkChatItemRead: \(cItem.id)") try await apiChatRead(type: cInfo.chatType, id: cInfo.apiId, itemRange: (cItem.id, cItem.id)) - await ChatModel.shared.markChatItemRead(cInfo, cItem) + DispatchQueue.main.async { + ChatModel.shared.markChatItemsRead(cInfo, [cItem.id]) + } } catch { - logger.error("apiMarkChatItemRead apiChatRead error: \(responseError(error))") + logger.error("apiChatRead error: \(responseError(error))") + } +} + +func apiMarkChatItemsRead(_ cInfo: ChatInfo, _ itemIds: [ChatItem.ID]) async { + do { + try await apiChatItemsRead(type: cInfo.chatType, id: cInfo.apiId, itemIds: itemIds) + DispatchQueue.main.async { + ChatModel.shared.markChatItemsRead(cInfo, itemIds) + } + } catch { + logger.error("apiChatItemsRead error: \(responseError(error))") } } @@ -1775,36 +1858,45 @@ func processReceivedMsg(_ res: ChatResponse) async { n.networkStatuses = ns } } - case let .newChatItem(user, aChatItem): - let cInfo = aChatItem.chatInfo - let cItem = aChatItem.chatItem - await MainActor.run { - if active(user) { - m.addChatItem(cInfo, cItem) - } else if cItem.isRcvNew && cInfo.ntfsEnabled { - m.increaseUnreadCounter(user: user) + case let .newChatItems(user, chatItems): + for chatItem in chatItems { + let cInfo = chatItem.chatInfo + let cItem = chatItem.chatItem + await MainActor.run { + if active(user) { + m.addChatItem(cInfo, cItem) + } else if cItem.isRcvNew && cInfo.ntfsEnabled { + m.increaseUnreadCounter(user: user) + } + } + if let file = cItem.autoReceiveFile() { + Task { + await receiveFile(user: user, fileId: file.fileId, auto: true) + } + } + if cItem.showNotification { + NtfManager.shared.notifyMessageReceived(user, cInfo, cItem) } } - if let file = cItem.autoReceiveFile() { - Task { - await receiveFile(user: user, fileId: file.fileId, auto: true) + case let .chatItemsStatusesUpdated(user, chatItems): + for chatItem in chatItems { + let cInfo = chatItem.chatInfo + let cItem = chatItem.chatItem + if !cItem.isDeletedContent && active(user) { + await MainActor.run { m.updateChatItem(cInfo, cItem, status: cItem.meta.itemStatus) } } - } - if cItem.showNotification { - NtfManager.shared.notifyMessageReceived(user, cInfo, cItem) - } - case let .chatItemStatusUpdated(user, aChatItem): - let cInfo = aChatItem.chatInfo - let cItem = aChatItem.chatItem - if !cItem.isDeletedContent && active(user) { - await MainActor.run { m.updateChatItem(cInfo, cItem, status: cItem.meta.itemStatus) } - } - if let endTask = m.messageDelivery[cItem.id] { - switch cItem.meta.itemStatus { - case .sndSent: endTask() - case .sndErrorAuth: endTask() - case .sndError: endTask() - default: () + if let endTask = m.messageDelivery[cItem.id] { + switch cItem.meta.itemStatus { + case .sndNew: () + case .sndSent: endTask() + case .sndRcvd: endTask() + case .sndErrorAuth: endTask() + case .sndError: endTask() + case .sndWarning: endTask() + case .rcvNew: () + case .rcvRead: () + case .invalid: () + } } } case let .chatItemUpdated(user, aChatItem): @@ -1998,7 +2090,6 @@ func processReceivedMsg(_ res: ChatResponse) async { await withCall(contact) { call in await MainActor.run { call.callState = .offerReceived - call.peerMedia = callType.media call.sharedKey = sharedKey } let useRelay = UserDefaults.standard.bool(forKey: DEFAULT_WEBRTC_POLICY_RELAY) diff --git a/apps/ios/Shared/Theme/Theme.swift b/apps/ios/Shared/Theme/Theme.swift index e2641eb8dd..de67390026 100644 --- a/apps/ios/Shared/Theme/Theme.swift +++ b/apps/ios/Shared/Theme/Theme.swift @@ -102,7 +102,7 @@ extension ThemeWallpaper { public func importFromString() -> ThemeWallpaper { if preset == nil, let image { // Need to save image from string and to save its path - if let parsed = UIImage(base64Encoded: image), + if let parsed = imageFromBase64(image), let filename = saveWallpaperFile(image: parsed) { var copy = self copy.image = nil @@ -122,7 +122,7 @@ extension ThemeWallpaper { let preset: String? = if case let WallpaperType.preset(filename, _) = type { filename } else { nil } let scale: Float? = if case let WallpaperType.preset(_, scale) = type { scale } else { if case let WallpaperType.image(_, scale, _) = type { scale } else { 1.0 } } let scaleType: WallpaperScaleType? = if case let WallpaperType.image(_, _, scaleType) = type { scaleType } else { nil } - let image: String? = if case WallpaperType.image = type, let image = type.uiImage { resizeImageToStrSize(image, maxDataSize: 5_000_000) } else { nil } + let image: String? = if case WallpaperType.image = type, let image = type.uiImage { resizeImageToStrSizeSync(image, maxDataSize: 5_000_000) } else { nil } return ThemeWallpaper ( preset: preset, scale: scale, diff --git a/apps/ios/Shared/Theme/ThemeManager.swift b/apps/ios/Shared/Theme/ThemeManager.swift index 9d648750d1..4166619d04 100644 --- a/apps/ios/Shared/Theme/ThemeManager.swift +++ b/apps/ios/Shared/Theme/ThemeManager.swift @@ -197,7 +197,7 @@ class ThemeManager { var themeIds = currentThemeIdsDefault.get() themeIds[nonSystemThemeName] = prevValue.themeId currentThemeIdsDefault.set(themeIds) - applyTheme(nonSystemThemeName) + applyTheme(currentThemeDefault.get()) } static func copyFromSameThemeOverrides(_ type: WallpaperType?, _ lowerLevelOverride: ThemeModeOverride?, _ pref: Binding) -> Bool { diff --git a/apps/ios/Shared/Views/Call/ActiveCallView.swift b/apps/ios/Shared/Views/Call/ActiveCallView.swift index d238c2dbae..2f76f1f046 100644 --- a/apps/ios/Shared/Views/Call/ActiveCallView.swift +++ b/apps/ios/Shared/Views/Call/ActiveCallView.swift @@ -17,8 +17,8 @@ struct ActiveCallView: View { @ObservedObject var call: Call @Environment(\.scenePhase) var scenePhase @State private var client: WebRTCClient? = nil - @State private var activeCall: WebRTCClient.Call? = nil @State private var localRendererAspectRatio: CGFloat? = nil + @State var remoteContentMode: UIView.ContentMode = .scaleAspectFill @Binding var canConnectCall: Bool @State var prevColorScheme: ColorScheme = .dark @State var pipShown = false @@ -27,24 +27,39 @@ struct ActiveCallView: View { var body: some View { ZStack(alignment: .topLeading) { ZStack(alignment: .bottom) { - if let client = client, [call.peerMedia, call.localMedia].contains(.video), activeCall != nil { + if let client = client, call.hasVideo { GeometryReader { g in let width = g.size.width * 0.3 ZStack(alignment: .topTrailing) { - CallViewRemote(client: client, activeCall: $activeCall, activeCallViewIsCollapsed: $m.activeCallViewIsCollapsed, pipShown: $pipShown) - CallViewLocal(client: client, activeCall: $activeCall, localRendererAspectRatio: $localRendererAspectRatio, pipShown: $pipShown) - .cornerRadius(10) - .frame(width: width, height: width / (localRendererAspectRatio ?? 1)) - .padding([.top, .trailing], 17) ZStack(alignment: .center) { // For some reason, when the view in GeometryReader and ZStack is visible, it steals clicks on a back button, so showing something on top like this with background color helps (.clear color doesn't work) } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.primary.opacity(0.000001)) + + CallViewRemote(client: client, call: call, activeCallViewIsCollapsed: $m.activeCallViewIsCollapsed, contentMode: $remoteContentMode, pipShown: $pipShown) + .onTapGesture { + remoteContentMode = remoteContentMode == .scaleAspectFill ? .scaleAspectFit : .scaleAspectFill + } + + Group { + let localVideoTrack = client.activeCall?.localVideoTrack ?? client.notConnectedCall?.localCameraAndTrack?.1 + if localVideoTrack != nil { + CallViewLocal(client: client, localRendererAspectRatio: $localRendererAspectRatio, pipShown: $pipShown) + .onDisappear { + localRendererAspectRatio = nil + } + } else { + Rectangle().fill(.black) + } + } + .cornerRadius(10) + .frame(width: width, height: localRendererAspectRatio == nil ? (g.size.width < g.size.height ? width * 1.33 : width / 1.33) : width / (localRendererAspectRatio ?? 1)) + .padding([.top, .trailing], 17) } } } - if let call = m.activeCall, let client = client, (!pipShown || !call.supportsVideo) { + if let call = m.activeCall, let client = client, (!pipShown || !call.hasVideo) { ActiveCallOverlay(call: call, client: client) } } @@ -54,6 +69,9 @@ struct ActiveCallView: View { .onAppear { logger.debug("ActiveCallView: appear client is nil \(client == nil), scenePhase \(String(describing: scenePhase)), canConnectCall \(canConnectCall)") AppDelegate.keepScreenOn(true) + Task { + await askRequiredPermissions() + } createWebRTCClient() dismissAllSheets() hideKeyboard() @@ -84,7 +102,7 @@ struct ActiveCallView: View { private func createWebRTCClient() { if client == nil && canConnectCall { - client = WebRTCClient($activeCall, { msg in await MainActor.run { processRtcMessage(msg: msg) } }, $localRendererAspectRatio) + client = WebRTCClient({ msg in await MainActor.run { processRtcMessage(msg: msg) } }, $localRendererAspectRatio) Task { await m.callCommand.setClient(client) } @@ -99,7 +117,7 @@ struct ActiveCallView: View { logger.debug("ActiveCallView: response \(msg.resp.respType)") switch msg.resp { case let .capabilities(capabilities): - let callType = CallType(media: call.localMedia, capabilities: capabilities) + let callType = CallType(media: call.initialCallType, capabilities: capabilities) Task { do { try await apiSendCallInvitation(call.contact, callType) @@ -110,7 +128,7 @@ struct ActiveCallView: View { call.callState = .invitationSent call.localCapabilities = capabilities } - if call.supportsVideo && !AVAudioSession.sharedInstance().hasExternalAudioDevice() { + if call.hasVideo && !AVAudioSession.sharedInstance().hasExternalAudioDevice() { try? AVAudioSession.sharedInstance().setCategory(.playback, options: [.allowBluetooth, .allowAirPlay, .allowBluetoothA2DP]) } CallSoundsPlayer.shared.startConnectingCallSound() @@ -120,7 +138,7 @@ struct ActiveCallView: View { Task { do { try await apiSendCallOffer(call.contact, offer, iceCandidates, - media: call.localMedia, capabilities: capabilities) + media: call.initialCallType, capabilities: capabilities) } catch { logger.error("apiSendCallOffer \(responseError(error))") } @@ -164,6 +182,9 @@ struct ActiveCallView: View { } if state.connectionState == "closed" { closeCallView(client) + if let callUUID = m.activeCall?.callUUID { + CallController.shared.endCall(callUUID: callUUID) + } m.activeCall = nil m.activeCallViewIsCollapsed = false } @@ -182,6 +203,14 @@ struct ActiveCallView: View { CallSoundsPlayer.shared.vibrate(long: false) wasConnected = true } + case let .peerMedia(source, enabled): + switch source { + case .mic: call.peerMediaSources.mic = enabled + case .camera: call.peerMediaSources.camera = enabled + case .screenAudio: call.peerMediaSources.screenAudio = enabled + case .screenVideo: call.peerMediaSources.screenVideo = enabled + case .unknown: () + } case .ended: closeCallView(client) call.callState = .ended @@ -214,16 +243,38 @@ struct ActiveCallView: View { ChatReceiver.shared.messagesChannel = nil return } - if case let .chatItemStatusUpdated(_, msg) = msg, - msg.chatInfo.id == call.contact.id, - case .sndCall = msg.chatItem.content, - case .sndRcvd = msg.chatItem.meta.itemStatus { + if case let .chatItemsStatusesUpdated(_, chatItems) = msg, + chatItems.contains(where: { ci in + ci.chatInfo.id == call.contact.id && + ci.chatItem.content.isSndCall && + ci.chatItem.meta.itemStatus.isSndRcvd + }) { CallSoundsPlayer.shared.startInCallSound() ChatReceiver.shared.messagesChannel = nil } } } + private func askRequiredPermissions() async { + let mic = await WebRTCClient.isAuthorized(for: .audio) + await MainActor.run { + call.localMediaSources.mic = mic + } + let cameraAuthorized = AVCaptureDevice.authorizationStatus(for: .video) == .authorized + var camera = call.initialCallType == .audio || cameraAuthorized + if call.initialCallType == .video && !cameraAuthorized { + camera = await WebRTCClient.isAuthorized(for: .video) + await MainActor.run { + if camera, let client { + client.setCameraEnabled(true) + } + } + } + if !mic || !camera { + WebRTCClient.showUnauthorizedAlert(for: !mic ? .audio : .video) + } + } + private func closeCallView(_ client: WebRTCClient) { if m.activeCall != nil { m.showCallView = false @@ -239,44 +290,16 @@ struct ActiveCallOverlay: View { var body: some View { VStack { - switch call.localMedia { - case .video: + switch call.hasVideo { + case true: videoCallInfoView(call) .foregroundColor(.white) .opacity(0.8) - .padding() - - Spacer() - - HStack { - toggleAudioButton() - Spacer() - if deviceManager.availableInputs.allSatisfy({ $0.portType == .builtInMic }) { - toggleSpeakerButton() - .frame(width: 40, height: 40) - } else if call.hasMedia { - AudioDevicePicker() - .scaleEffect(2) - .frame(maxWidth: 40, maxHeight: 40) - } else { - Color.clear.frame(width: 40, height: 40) - } - Spacer() - endCallButton() - Spacer() - if call.videoEnabled { - flipCameraButton() - } else { - Color.clear.frame(width: 40, height: 40) - } - Spacer() - toggleVideoButton() - } - .padding(.horizontal, 20) - .padding(.bottom, 16) - .frame(maxWidth: .infinity, alignment: .center) - - case .audio: + .padding(.horizontal) + // Fixed vertical padding required for preserving position of buttons row when changing audio-to-video and back in landscape orientation. + // Otherwise, bigger padding is added by SwiftUI when switching call types + .padding(.vertical, 10) + case false: ZStack(alignment: .topLeading) { Button { chatModel.activeCallViewIsCollapsed = true @@ -291,35 +314,32 @@ struct ActiveCallOverlay: View { } .foregroundColor(.white) .opacity(0.8) - .padding() + .padding(.horizontal) + .padding(.vertical, 10) .frame(maxHeight: .infinity) } - - Spacer() - - ZStack(alignment: .bottom) { - toggleAudioButton() - .frame(maxWidth: .infinity, alignment: .leading) - endCallButton() - // Check if the only input is microphone. And in this case show toggle button, - // If there are more inputs, it probably means something like bluetooth headphones are available - // and in this case show iOS button for choosing different output. - // There is no way to get available outputs, only inputs - if deviceManager.availableInputs.allSatisfy({ $0.portType == .builtInMic }) { - toggleSpeakerButton() - .frame(maxWidth: .infinity, alignment: .trailing) - } else if call.hasMedia { - AudioDevicePicker() - .scaleEffect(2) - .frame(maxWidth: 50, maxHeight: 40) - .frame(maxWidth: .infinity, alignment: .trailing) - } else { - Color.clear.frame(width: 50, height: 40) - } - } - .padding(.bottom, 60) - .padding(.horizontal, 48) } + + Spacer() + + HStack { + toggleMicButton() + Spacer() + audioDeviceButton() + Spacer() + endCallButton() + Spacer() + if call.localMediaSources.camera { + flipCameraButton() + } else { + Color.clear.frame(width: 60, height: 60) + } + Spacer() + toggleCameraButton() + } + .padding(.horizontal, 20) + .padding(.bottom, 16) + .frame(maxWidth: 440, alignment: .center) } .frame(maxWidth: .infinity) .onAppear { @@ -381,36 +401,54 @@ struct ActiveCallOverlay: View { private func endCallButton() -> some View { let cc = CallController.shared - return callButton("phone.down.fill", width: 60, height: 60) { + return callButton("phone.down.fill", .red, padding: 10) { if let uuid = call.callUUID { cc.endCall(callUUID: uuid) } else { cc.endCall(call: call) {} } } - .foregroundColor(.red) } - private func toggleAudioButton() -> some View { - controlButton(call, call.audioEnabled ? "mic.fill" : "mic.slash") { + private func toggleMicButton() -> some View { + controlButton(call, call.localMediaSources.mic ? "mic.fill" : "mic.slash", padding: 14) { Task { - client.setAudioEnabled(!call.audioEnabled) - DispatchQueue.main.async { - call.audioEnabled = !call.audioEnabled - } + if await WebRTCClient.isAuthorized(for: .audio) { + client.setAudioEnabled(!call.localMediaSources.mic) + } else { WebRTCClient.showUnauthorizedAlert(for: .audio) } + } + } + } + + func audioDeviceButton() -> some View { + // Check if the only input is microphone. And in this case show toggle button, + // If there are more inputs, it probably means something like bluetooth headphones are available + // and in this case show iOS button for choosing different output. + // There is no way to get available outputs, only inputs + Group { + if deviceManager.availableInputs.allSatisfy({ $0.portType == .builtInMic }) { + toggleSpeakerButton() + } else { + audioDevicePickerButton() + } + } + .onChange(of: call.localMediaSources.hasVideo) { hasVideo in + let current = AVAudioSession.sharedInstance().currentRoute.outputs.first?.portType + let speakerEnabled = current == .builtInSpeaker + let receiverEnabled = current == .builtInReceiver + // react automatically only when receiver were selected, otherwise keep an external device selected + if !speakerEnabled && hasVideo && receiverEnabled { + client.setSpeakerEnabledAndConfigureSession(!speakerEnabled, skipExternalDevice: true) + call.speakerEnabled = !speakerEnabled } } } private func toggleSpeakerButton() -> some View { - controlButton(call, call.speakerEnabled ? "speaker.wave.2.fill" : "speaker.wave.1.fill") { - Task { - let speakerEnabled = AVAudioSession.sharedInstance().currentRoute.outputs.first?.portType == .builtInSpeaker - client.setSpeakerEnabledAndConfigureSession(!speakerEnabled) - DispatchQueue.main.async { - call.speakerEnabled = !speakerEnabled - } - } + controlButton(call, !call.peerMediaSources.mic ? "speaker.slash" : call.speakerEnabled ? "speaker.wave.2.fill" : "speaker.wave.1.fill", padding: !call.peerMediaSources.mic ? 16 : call.speakerEnabled ? 15 : 17) { + let speakerEnabled = AVAudioSession.sharedInstance().currentRoute.outputs.first?.portType == .builtInSpeaker + client.setSpeakerEnabledAndConfigureSession(!speakerEnabled) + call.speakerEnabled = !speakerEnabled } .onAppear { deviceManager.call = call @@ -418,53 +456,67 @@ struct ActiveCallOverlay: View { } } - private func toggleVideoButton() -> some View { - controlButton(call, call.videoEnabled ? "video.fill" : "video.slash") { + private func toggleCameraButton() -> some View { + controlButton(call, call.localMediaSources.camera ? "video.fill" : "video.slash", padding: call.localMediaSources.camera ? 16 : 14) { Task { - client.setVideoEnabled(!call.videoEnabled) - DispatchQueue.main.async { - call.videoEnabled = !call.videoEnabled - } + if await WebRTCClient.isAuthorized(for: .video) { + client.setCameraEnabled(!call.localMediaSources.camera) + } else { WebRTCClient.showUnauthorizedAlert(for: .video) } } } + .disabled(call.initialCallType == .audio && client.activeCall?.peerHasOldVersion == true) } @ViewBuilder private func flipCameraButton() -> some View { - controlButton(call, "arrow.triangle.2.circlepath") { + controlButton(call, "arrow.triangle.2.circlepath", padding: 12) { Task { - client.flipCamera() + if await WebRTCClient.isAuthorized(for: .video) { + client.flipCamera() + } } } } - @ViewBuilder private func controlButton(_ call: Call, _ imageName: String, _ perform: @escaping () -> Void) -> some View { - if call.hasMedia { - callButton(imageName, width: 50, height: 38, perform) - .foregroundColor(.white) - .opacity(0.85) - } else { - Color.clear.frame(width: 50, height: 38) - } + @ViewBuilder private func controlButton(_ call: Call, _ imageName: String, padding: CGFloat, _ perform: @escaping () -> Void) -> some View { + callButton(imageName, call.peerMediaSources.hasVideo ? Color.black.opacity(0.2) : Color.white.opacity(0.2), padding: padding, perform) } - private func callButton(_ imageName: String, width: CGFloat, height: CGFloat, _ perform: @escaping () -> Void) -> some View { + @ViewBuilder private func audioDevicePickerButton() -> some View { + AudioDevicePicker() + .opacity(0.8) + .scaleEffect(2) + .padding(10) + .frame(width: 60, height: 60) + .background(call.peerMediaSources.hasVideo ? Color.black.opacity(0.2) : Color.white.opacity(0.2)) + .clipShape(.circle) + } + + private func callButton(_ imageName: String, _ background: Color, padding: CGFloat, _ perform: @escaping () -> Void) -> some View { Button { perform() } label: { Image(systemName: imageName) .resizable() .scaledToFit() - .frame(maxWidth: width, maxHeight: height) + .padding(padding) + .frame(width: 60, height: 60) + .background(background) } + .foregroundColor(whiteColorWithAlpha) + .clipShape(.circle) + } + + private var whiteColorWithAlpha: Color { + get { Color(red: 204 / 255, green: 204 / 255, blue: 204 / 255) } } } struct ActiveCallOverlay_Previews: PreviewProvider { static var previews: some View { Group{ - ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callUUID: UUID().uuidString.lowercased(), callState: .offerSent, localMedia: .video), client: WebRTCClient(Binding.constant(nil), { _ in }, Binding.constant(nil))) + ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callUUID: UUID().uuidString.lowercased(), callState: .offerSent, initialCallType: .video), client: WebRTCClient({ _ in }, Binding.constant(nil))) .background(.black) - ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callUUID: UUID().uuidString.lowercased(), callState: .offerSent, localMedia: .audio), client: WebRTCClient(Binding.constant(nil), { _ in }, Binding.constant(nil))) + ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callUUID: UUID().uuidString.lowercased(), callState: .offerSent, initialCallType: .audio), client: WebRTCClient({ _ in }, Binding.constant(nil))) .background(.black) } } diff --git a/apps/ios/Shared/Views/Call/CallController.swift b/apps/ios/Shared/Views/Call/CallController.swift index 36887d6184..1f28180e87 100644 --- a/apps/ios/Shared/Views/Call/CallController.swift +++ b/apps/ios/Shared/Views/Call/CallController.swift @@ -104,7 +104,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse } func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) { - if callManager.enableMedia(media: .audio, enable: !action.isMuted, callUUID: action.callUUID.uuidString.lowercased()) { + if callManager.enableMedia(source: .mic, enable: !action.isMuted, callUUID: action.callUUID.uuidString.lowercased()) { action.fulfill() } else { action.fail() @@ -121,8 +121,8 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse RTCAudioSession.sharedInstance().audioSessionDidActivate(audioSession) RTCAudioSession.sharedInstance().isAudioEnabled = true do { - let supportsVideo = ChatModel.shared.activeCall?.supportsVideo == true - if supportsVideo { + let hasVideo = ChatModel.shared.activeCall?.hasVideo == true + if hasVideo { try audioSession.setCategory(.playAndRecord, mode: .videoChat, options: [.defaultToSpeaker, .mixWithOthers, .allowBluetooth, .allowAirPlay, .allowBluetoothA2DP]) } else { try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: [.mixWithOthers, .allowBluetooth, .allowAirPlay, .allowBluetoothA2DP]) @@ -133,7 +133,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse try? await Task.sleep(nanoseconds: UInt64(i) * 300_000000) if let preferred = audioSession.preferredInputDevice() { await MainActor.run { try? audioSession.setPreferredInput(preferred) } - } else if supportsVideo { + } else if hasVideo { await MainActor.run { try? audioSession.overrideOutputAudioPort(.speaker) } } } @@ -357,11 +357,9 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse self.provider.reportCall(with: uuid, updated: update) } } else if callManager.startOutgoingCall(callUUID: callUUID) { - if callManager.startOutgoingCall(callUUID: callUUID) { - logger.debug("CallController.startCall: call started") - } else { - logger.error("CallController.startCall: no active call") - } + logger.debug("CallController.startCall: call started") + } else { + logger.error("CallController.startCall: no active call") } } diff --git a/apps/ios/Shared/Views/Call/CallManager.swift b/apps/ios/Shared/Views/Call/CallManager.swift index f3021815af..a3e1df2301 100644 --- a/apps/ios/Shared/Views/Call/CallManager.swift +++ b/apps/ios/Shared/Views/Call/CallManager.swift @@ -12,7 +12,7 @@ import SimpleXChat class CallManager { func newOutgoingCall(_ contact: Contact, _ media: CallMediaType) -> String { let uuid = UUID().uuidString.lowercased() - let call = Call(direction: .outgoing, contact: contact, callUUID: uuid, callState: .waitCapabilities, localMedia: media) + let call = Call(direction: .outgoing, contact: contact, callUUID: uuid, callState: .waitCapabilities, initialCallType: media) call.speakerEnabled = media == .video ChatModel.shared.activeCall = call return uuid @@ -22,7 +22,7 @@ class CallManager { let m = ChatModel.shared if let call = m.activeCall, call.callUUID == callUUID { m.showCallView = true - Task { await m.callCommand.processCommand(.capabilities(media: call.localMedia)) } + Task { await m.callCommand.processCommand(.capabilities(media: call.initialCallType)) } return true } return false @@ -44,7 +44,7 @@ class CallManager { contact: invitation.contact, callUUID: invitation.callUUID, callState: .invitationAccepted, - localMedia: invitation.callType.media, + initialCallType: invitation.callType.media, sharedKey: invitation.sharedKey ) call.speakerEnabled = invitation.callType.media == .video @@ -68,10 +68,10 @@ class CallManager { } } - func enableMedia(media: CallMediaType, enable: Bool, callUUID: String) -> Bool { + func enableMedia(source: CallMediaSource, enable: Bool, callUUID: String) -> Bool { if let call = ChatModel.shared.activeCall, call.callUUID == callUUID { let m = ChatModel.shared - Task { await m.callCommand.processCommand(.media(media: media, enable: enable)) } + Task { await m.callCommand.processCommand(.media(source: source, enable: enable)) } return true } return false diff --git a/apps/ios/Shared/Views/Call/CallViewRenderers.swift b/apps/ios/Shared/Views/Call/CallViewRenderers.swift index a3201d9351..e779093a24 100644 --- a/apps/ios/Shared/Views/Call/CallViewRenderers.swift +++ b/apps/ios/Shared/Views/Call/CallViewRenderers.swift @@ -10,40 +10,49 @@ import AVKit struct CallViewRemote: UIViewRepresentable { var client: WebRTCClient - var activeCall: Binding + @ObservedObject var call: Call @State var enablePip: (Bool) -> Void = {_ in } @Binding var activeCallViewIsCollapsed: Bool + @Binding var contentMode: UIView.ContentMode @Binding var pipShown: Bool - init(client: WebRTCClient, activeCall: Binding, activeCallViewIsCollapsed: Binding, pipShown: Binding) { - self.client = client - self.activeCall = activeCall - self._activeCallViewIsCollapsed = activeCallViewIsCollapsed - self._pipShown = pipShown - } - func makeUIView(context: Context) -> UIView { let view = UIView() - if let call = activeCall.wrappedValue { - let remoteRenderer = RTCMTLVideoView(frame: view.frame) - remoteRenderer.videoContentMode = .scaleAspectFill - client.addRemoteRenderer(call, remoteRenderer) - addSubviewAndResize(remoteRenderer, into: view) + let remoteCameraRenderer = RTCMTLVideoView(frame: view.frame) + remoteCameraRenderer.videoContentMode = contentMode + remoteCameraRenderer.tag = 0 - if AVPictureInPictureController.isPictureInPictureSupported() { - makeViewWithRTCRenderer(call, remoteRenderer, view, context) - } + let screenVideo = call.peerMediaSources.screenVideo + let remoteScreenRenderer = RTCMTLVideoView(frame: view.frame) + remoteScreenRenderer.videoContentMode = contentMode + remoteScreenRenderer.tag = 1 + remoteScreenRenderer.alpha = screenVideo ? 1 : 0 + + context.coordinator.cameraRenderer = remoteCameraRenderer + context.coordinator.screenRenderer = remoteScreenRenderer + client.addRemoteCameraRenderer(remoteCameraRenderer) + client.addRemoteScreenRenderer(remoteScreenRenderer) + if screenVideo { + addSubviewAndResize(remoteScreenRenderer, remoteCameraRenderer, into: view) + } else { + addSubviewAndResize(remoteCameraRenderer, remoteScreenRenderer, into: view) + } + + if AVPictureInPictureController.isPictureInPictureSupported() { + makeViewWithRTCRenderer(remoteCameraRenderer, remoteScreenRenderer, view, context) } return view } - func makeViewWithRTCRenderer(_ call: WebRTCClient.Call, _ remoteRenderer: RTCMTLVideoView, _ view: UIView, _ context: Context) { - let pipRemoteRenderer = RTCMTLVideoView(frame: view.frame) - pipRemoteRenderer.videoContentMode = .scaleAspectFill - + func makeViewWithRTCRenderer(_ remoteCameraRenderer: RTCMTLVideoView, _ remoteScreenRenderer: RTCMTLVideoView, _ view: UIView, _ context: Context) { + let pipRemoteCameraRenderer = RTCMTLVideoView(frame: view.frame) + pipRemoteCameraRenderer.videoContentMode = .scaleAspectFill + + let pipRemoteScreenRenderer = RTCMTLVideoView(frame: view.frame) + pipRemoteScreenRenderer.videoContentMode = .scaleAspectFill + let pipVideoCallViewController = AVPictureInPictureVideoCallViewController() pipVideoCallViewController.preferredContentSize = CGSize(width: 1080, height: 1920) - addSubviewAndResize(pipRemoteRenderer, into: pipVideoCallViewController.view) let pipContentSource = AVPictureInPictureController.ContentSource( activeVideoCallSourceView: view, contentViewController: pipVideoCallViewController @@ -55,7 +64,9 @@ struct CallViewRemote: UIViewRepresentable { context.coordinator.pipController = pipController context.coordinator.willShowHide = { show in if show { - client.addRemoteRenderer(call, pipRemoteRenderer) + client.addRemoteCameraRenderer(pipRemoteCameraRenderer) + client.addRemoteScreenRenderer(pipRemoteScreenRenderer) + context.coordinator.relayout() DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { activeCallViewIsCollapsed = true } @@ -67,13 +78,29 @@ struct CallViewRemote: UIViewRepresentable { } context.coordinator.didShowHide = { show in if show { - remoteRenderer.isHidden = true + remoteCameraRenderer.isHidden = true + remoteScreenRenderer.isHidden = true } else { - client.removeRemoteRenderer(call, pipRemoteRenderer) - remoteRenderer.isHidden = false + client.removeRemoteCameraRenderer(pipRemoteCameraRenderer) + client.removeRemoteScreenRenderer(pipRemoteScreenRenderer) + remoteCameraRenderer.isHidden = false + remoteScreenRenderer.isHidden = false } pipShown = show } + context.coordinator.relayout = { + let camera = call.peerMediaSources.camera + let screenVideo = call.peerMediaSources.screenVideo + pipRemoteCameraRenderer.alpha = camera ? 1 : 0 + pipRemoteScreenRenderer.alpha = screenVideo ? 1 : 0 + if screenVideo { + addSubviewAndResize(pipRemoteScreenRenderer, pipRemoteCameraRenderer, pip: true, into: pipVideoCallViewController.view) + } else { + addSubviewAndResize(pipRemoteCameraRenderer, pipRemoteScreenRenderer, pip: true, into: pipVideoCallViewController.view) + } + (pipVideoCallViewController.view.subviews[0] as! RTCMTLVideoView).videoContentMode = contentMode + (pipVideoCallViewController.view.subviews[1] as! RTCMTLVideoView).videoContentMode = .scaleAspectFill + } DispatchQueue.main.async { enablePip = { enable in if enable != pipShown /* pipController.isPictureInPictureActive */ { @@ -88,24 +115,50 @@ struct CallViewRemote: UIViewRepresentable { } func makeCoordinator() -> Coordinator { - Coordinator() + Coordinator(client) } func updateUIView(_ view: UIView, context: Context) { logger.debug("CallView.updateUIView remote") + let camera = view.subviews.first(where: { $0.tag == 0 })! + let screen = view.subviews.first(where: { $0.tag == 1 })! + let screenVideo = call.peerMediaSources.screenVideo + if screenVideo && screen.alpha == 0 { + screen.alpha = 1 + addSubviewAndResize(screen, camera, into: view) + } else if !screenVideo && screen.alpha == 1 { + screen.alpha = 0 + addSubviewAndResize(camera, screen, into: view) + } + (view.subviews[0] as! RTCMTLVideoView).videoContentMode = contentMode + (view.subviews[1] as! RTCMTLVideoView).videoContentMode = .scaleAspectFill + + camera.alpha = call.peerMediaSources.camera ? 1 : 0 + screen.alpha = call.peerMediaSources.screenVideo ? 1 : 0 + DispatchQueue.main.async { if activeCallViewIsCollapsed != pipShown { enablePip(activeCallViewIsCollapsed) + } else if pipShown { + context.coordinator.relayout() } } } // MARK: - Coordinator class Coordinator: NSObject, AVPictureInPictureControllerDelegate { + var cameraRenderer: RTCMTLVideoView? + var screenRenderer: RTCMTLVideoView? + var client: WebRTCClient var pipController: AVPictureInPictureController? = nil var willShowHide: (Bool) -> Void = { _ in } var didShowHide: (Bool) -> Void = { _ in } - + var relayout: () -> Void = {} + + required init(_ client: WebRTCClient) { + self.client = client + } + func pictureInPictureControllerWillStartPictureInPicture(_ pictureInPictureController: AVPictureInPictureController) { willShowHide(true) } @@ -127,11 +180,20 @@ struct CallViewRemote: UIViewRepresentable { } deinit { + // TODO: deinit is not called when changing call type from audio to video and back, + // which causes many renderers can be created and added to stream (if enabling/disabling + // video while not yet connected in outgoing call) pipController?.stopPictureInPicture() pipController?.canStartPictureInPictureAutomaticallyFromInline = false pipController?.contentSource = nil pipController?.delegate = nil pipController = nil + if let cameraRenderer { + client.removeRemoteCameraRenderer(cameraRenderer) + } + if let screenRenderer { + client.removeRemoteScreenRenderer(screenRenderer) + } } } @@ -148,51 +210,109 @@ struct CallViewRemote: UIViewRepresentable { struct CallViewLocal: UIViewRepresentable { var client: WebRTCClient - var activeCall: Binding var localRendererAspectRatio: Binding @State var pipStateChanged: (Bool) -> Void = {_ in } @Binding var pipShown: Bool - init(client: WebRTCClient, activeCall: Binding, localRendererAspectRatio: Binding, pipShown: Binding) { + init(client: WebRTCClient, localRendererAspectRatio: Binding, pipShown: Binding) { self.client = client - self.activeCall = activeCall self.localRendererAspectRatio = localRendererAspectRatio self._pipShown = pipShown } func makeUIView(context: Context) -> UIView { let view = UIView() - if let call = activeCall.wrappedValue { - let localRenderer = RTCEAGLVideoView(frame: .zero) - client.addLocalRenderer(call, localRenderer) - client.startCaptureLocalVideo(call) - addSubviewAndResize(localRenderer, into: view) - DispatchQueue.main.async { - pipStateChanged = { shown in - localRenderer.isHidden = shown - } + let localRenderer = RTCEAGLVideoView(frame: .zero) + context.coordinator.renderer = localRenderer + client.addLocalRenderer(localRenderer) + addSubviewAndResize(localRenderer, nil, into: view) + DispatchQueue.main.async { + pipStateChanged = { shown in + localRenderer.isHidden = shown } } return view } + func makeCoordinator() -> Coordinator { + Coordinator(client) + } + func updateUIView(_ view: UIView, context: Context) { logger.debug("CallView.updateUIView local") pipStateChanged(pipShown) } + + // MARK: - Coordinator + class Coordinator: NSObject, AVPictureInPictureControllerDelegate { + var renderer: RTCEAGLVideoView? + var client: WebRTCClient + + required init(_ client: WebRTCClient) { + self.client = client + } + + deinit { + if let renderer { + client.removeLocalRenderer(renderer) + } + } + } } -private func addSubviewAndResize(_ view: UIView, into containerView: UIView) { - containerView.addSubview(view) - view.translatesAutoresizingMaskIntoConstraints = false - containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[view]|", - options: [], - metrics: nil, - views: ["view": view])) +private func addSubviewAndResize(_ fullscreen: UIView, _ end: UIView?, pip: Bool = false, into containerView: UIView) { + if containerView.subviews.firstIndex(of: fullscreen) == 0 && ((end == nil && containerView.subviews.count == 1) || (end != nil && containerView.subviews.firstIndex(of: end!) == 1)) { + // Nothing to do, elements on their places + return + } + containerView.removeConstraints(containerView.constraints) + containerView.subviews.forEach { sub in sub.removeFromSuperview()} - containerView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[view]|", + containerView.addSubview(fullscreen) + fullscreen.translatesAutoresizingMaskIntoConstraints = false + fullscreen.layer.cornerRadius = 0 + fullscreen.layer.masksToBounds = false + + if let end { + containerView.addSubview(end) + end.translatesAutoresizingMaskIntoConstraints = false + end.layer.cornerRadius = pip ? 8 : 10 + end.layer.masksToBounds = true + } + + let constraintFullscreenV = NSLayoutConstraint.constraints( + withVisualFormat: "V:|[fullscreen]|", options: [], metrics: nil, - views: ["view": view])) + views: ["fullscreen": fullscreen] + ) + let constraintFullscreenH = NSLayoutConstraint.constraints( + withVisualFormat: "H:|[fullscreen]|", + options: [], + metrics: nil, + views: ["fullscreen": fullscreen] + ) + + containerView.addConstraints(constraintFullscreenV) + containerView.addConstraints(constraintFullscreenH) + + if let end { + let constraintEndWidth = NSLayoutConstraint( + item: end, attribute: .width, relatedBy: .equal, toItem: containerView, attribute: .width, multiplier: pip ? 0.5 : 0.3, constant: 0 + ) + let constraintEndHeight = NSLayoutConstraint( + item: end, attribute: .height, relatedBy: .equal, toItem: containerView, attribute: .width, multiplier: pip ? 0.5 * 1.33 : 0.3 * 1.33, constant: 0 + ) + let constraintEndX = NSLayoutConstraint( + item: end, attribute: .leading, relatedBy: .equal, toItem: containerView, attribute: .trailing, multiplier: pip ? 0.5 : 0.7, constant: pip ? -8 : -17 + ) + let constraintEndY = NSLayoutConstraint( + item: end, attribute: .bottom, relatedBy: .equal, toItem: containerView, attribute: .bottom, multiplier: 1, constant: pip ? -8 : -92 + ) + containerView.addConstraint(constraintEndWidth) + containerView.addConstraint(constraintEndHeight) + containerView.addConstraint(constraintEndX) + containerView.addConstraint(constraintEndY) + } containerView.layoutIfNeeded() } diff --git a/apps/ios/Shared/Views/Call/IncomingCallView.swift b/apps/ios/Shared/Views/Call/IncomingCallView.swift index 4960281d72..5479a9fada 100644 --- a/apps/ios/Shared/Views/Call/IncomingCallView.swift +++ b/apps/ios/Shared/Views/Call/IncomingCallView.swift @@ -38,6 +38,7 @@ struct IncomingCallView: View { } HStack { ProfilePreview(profileOf: invitation.contact, color: .white) + .padding(.vertical, 6) Spacer() callButton("Reject", "phone.down.fill", .red) { diff --git a/apps/ios/Shared/Views/Call/WebRTC.swift b/apps/ios/Shared/Views/Call/WebRTC.swift index ba990981a1..ef9135761c 100644 --- a/apps/ios/Shared/Views/Call/WebRTC.swift +++ b/apps/ios/Shared/Views/Call/WebRTC.swift @@ -19,14 +19,13 @@ class Call: ObservableObject, Equatable { var direction: CallDirection var contact: Contact var callUUID: String? - var localMedia: CallMediaType + var initialCallType: CallMediaType + @Published var localMediaSources: CallMediaSources @Published var callState: CallState @Published var localCapabilities: CallCapabilities? - @Published var peerMedia: CallMediaType? + @Published var peerMediaSources: CallMediaSources = CallMediaSources() @Published var sharedKey: String? - @Published var audioEnabled = true @Published var speakerEnabled = false - @Published var videoEnabled: Bool @Published var connectionInfo: ConnectionInfo? @Published var connectedAt: Date? = nil @@ -35,32 +34,33 @@ class Call: ObservableObject, Equatable { contact: Contact, callUUID: String?, callState: CallState, - localMedia: CallMediaType, + initialCallType: CallMediaType, sharedKey: String? = nil ) { self.direction = direction self.contact = contact self.callUUID = callUUID self.callState = callState - self.localMedia = localMedia + self.initialCallType = initialCallType self.sharedKey = sharedKey - self.videoEnabled = localMedia == .video + self.localMediaSources = CallMediaSources( + mic: AVCaptureDevice.authorizationStatus(for: .audio) == .authorized, + camera: initialCallType == .video && AVCaptureDevice.authorizationStatus(for: .video) == .authorized) } var encrypted: Bool { get { localEncrypted && sharedKey != nil } } - var localEncrypted: Bool { get { localCapabilities?.encryption ?? false } } + private var localEncrypted: Bool { get { localCapabilities?.encryption ?? false } } var encryptionStatus: LocalizedStringKey { get { switch callState { case .waitCapabilities: return "" case .invitationSent: return localEncrypted ? "e2e encrypted" : "no e2e encryption" case .invitationAccepted: return sharedKey == nil ? "contact has no e2e encryption" : "contact has e2e encryption" - default: return !localEncrypted ? "no e2e encryption" : sharedKey == nil ? "contact has no e2e encryption" : "e2e encrypted" + default: return !localEncrypted ? "no e2e encryption" : sharedKey == nil ? "contact has no e2e encryption" : "e2e encrypted" } } } - var hasMedia: Bool { get { callState == .offerSent || callState == .negotiated || callState == .connected } } - var supportsVideo: Bool { get { peerMedia == .video || localMedia == .video } } + var hasVideo: Bool { get { localMediaSources.hasVideo || peerMediaSources.hasVideo } } } enum CallDirection { @@ -105,18 +105,28 @@ struct WVAPIMessage: Equatable, Decodable, Encodable { var command: WCallCommand? } +struct CallMediaSources: Equatable, Codable { + var mic: Bool = false + var camera: Bool = false + var screenAudio: Bool = false + var screenVideo: Bool = false + + var hasVideo: Bool { get { camera || screenVideo } } +} + enum WCallCommand: Equatable, Encodable, Decodable { case capabilities(media: CallMediaType) case start(media: CallMediaType, aesKey: String? = nil, iceServers: [RTCIceServer]? = nil, relay: Bool? = nil) case offer(offer: String, iceCandidates: String, media: CallMediaType, aesKey: String? = nil, iceServers: [RTCIceServer]? = nil, relay: Bool? = nil) case answer(answer: String, iceCandidates: String) case ice(iceCandidates: String) - case media(media: CallMediaType, enable: Bool) + case media(source: CallMediaSource, enable: Bool) case end enum CodingKeys: String, CodingKey { case type case media + case source case aesKey case offer case answer @@ -167,9 +177,9 @@ enum WCallCommand: Equatable, Encodable, Decodable { case let .ice(iceCandidates): try container.encode("ice", forKey: .type) try container.encode(iceCandidates, forKey: .iceCandidates) - case let .media(media, enable): + case let .media(source, enable): try container.encode("media", forKey: .type) - try container.encode(media, forKey: .media) + try container.encode(source, forKey: .media) try container.encode(enable, forKey: .enable) case .end: try container.encode("end", forKey: .type) @@ -205,9 +215,9 @@ enum WCallCommand: Equatable, Encodable, Decodable { let iceCandidates = try container.decode(String.self, forKey: CodingKeys.iceCandidates) self = .ice(iceCandidates: iceCandidates) case "media": - let media = try container.decode(CallMediaType.self, forKey: CodingKeys.media) + let source = try container.decode(CallMediaSource.self, forKey: CodingKeys.source) let enable = try container.decode(Bool.self, forKey: CodingKeys.enable) - self = .media(media: media, enable: enable) + self = .media(source: source, enable: enable) case "end": self = .end default: @@ -224,6 +234,7 @@ enum WCallResponse: Equatable, Decodable { case ice(iceCandidates: String) case connection(state: ConnectionState) case connected(connectionInfo: ConnectionInfo) + case peerMedia(source: CallMediaSource, enabled: Bool) case ended case ok case error(message: String) @@ -238,6 +249,8 @@ enum WCallResponse: Equatable, Decodable { case state case connectionInfo case message + case source + case enabled } var respType: String { @@ -249,6 +262,7 @@ enum WCallResponse: Equatable, Decodable { case .ice: return "ice" case .connection: return "connection" case .connected: return "connected" + case .peerMedia: return "peerMedia" case .ended: return "ended" case .ok: return "ok" case .error: return "error" @@ -283,6 +297,10 @@ enum WCallResponse: Equatable, Decodable { case "connected": let connectionInfo = try container.decode(ConnectionInfo.self, forKey: CodingKeys.connectionInfo) self = .connected(connectionInfo: connectionInfo) + case "peerMedia": + let source = try container.decode(CallMediaSource.self, forKey: CodingKeys.source) + let enabled = try container.decode(Bool.self, forKey: CodingKeys.enabled) + self = .peerMedia(source: source, enabled: enabled) case "ended": self = .ended case "ok": @@ -324,6 +342,10 @@ extension WCallResponse: Encodable { case let .connected(connectionInfo): try container.encode("connected", forKey: .type) try container.encode(connectionInfo, forKey: .connectionInfo) + case let .peerMedia(source, enabled): + try container.encode("peerMedia", forKey: .type) + try container.encode(source, forKey: .source) + try container.encode(enabled, forKey: .enabled) case .ended: try container.encode("ended", forKey: .type) case .ok: @@ -376,7 +398,7 @@ actor WebRTCCommandProcessor { func shouldRunCommand(_ client: WebRTCClient, _ c: WCallCommand) -> Bool { switch c { case .capabilities, .start, .offer, .end: true - default: client.activeCall.wrappedValue != nil + default: client.activeCall != nil } } } diff --git a/apps/ios/Shared/Views/Call/WebRTCClient.swift b/apps/ios/Shared/Views/Call/WebRTCClient.swift index 79e0dea16c..db7910836e 100644 --- a/apps/ios/Shared/Views/Call/WebRTCClient.swift +++ b/apps/ios/Shared/Views/Call/WebRTCClient.swift @@ -23,15 +23,24 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg struct Call { var connection: RTCPeerConnection var iceCandidates: IceCandidates - var localMedia: CallMediaType var localCamera: RTCVideoCapturer? - var localVideoSource: RTCVideoSource? - var localStream: RTCVideoTrack? - var remoteStream: RTCVideoTrack? - var device: AVCaptureDevice.Position = .front + var localAudioTrack: RTCAudioTrack? + var localVideoTrack: RTCVideoTrack? + var remoteAudioTrack: RTCAudioTrack? + var remoteVideoTrack: RTCVideoTrack? + var remoteScreenAudioTrack: RTCAudioTrack? + var remoteScreenVideoTrack: RTCVideoTrack? + var device: AVCaptureDevice.Position var aesKey: String? var frameEncryptor: RTCFrameEncryptor? var frameDecryptor: RTCFrameDecryptor? + var peerHasOldVersion: Bool + } + + struct NotConnectedCall { + var audioTrack: RTCAudioTrack? + var localCameraAndTrack: (RTCVideoCapturer, RTCVideoTrack)? + var device: AVCaptureDevice.Position = .front } actor IceCandidates { @@ -51,17 +60,20 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg private let rtcAudioSession = RTCAudioSession.sharedInstance() private let audioQueue = DispatchQueue(label: "chat.simplex.app.audio") private var sendCallResponse: (WVAPIMessage) async -> Void - var activeCall: Binding + var activeCall: Call? + var notConnectedCall: NotConnectedCall? private var localRendererAspectRatio: Binding + var cameraRenderers: [RTCVideoRenderer] = [] + var screenRenderers: [RTCVideoRenderer] = [] + @available(*, unavailable) override init() { fatalError("Unimplemented") } - required init(_ activeCall: Binding, _ sendCallResponse: @escaping (WVAPIMessage) async -> Void, _ localRendererAspectRatio: Binding) { + required init(_ sendCallResponse: @escaping (WVAPIMessage) async -> Void, _ localRendererAspectRatio: Binding) { self.sendCallResponse = sendCallResponse - self.activeCall = activeCall self.localRendererAspectRatio = localRendererAspectRatio rtcAudioSession.useManualAudio = CallController.useCallKit() rtcAudioSession.isAudioEnabled = !CallController.useCallKit() @@ -78,39 +90,45 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg func initializeCall(_ iceServers: [WebRTC.RTCIceServer]?, _ mediaType: CallMediaType, _ aesKey: String?, _ relay: Bool?) -> Call { let connection = createPeerConnection(iceServers ?? getWebRTCIceServers() ?? defaultIceServers, relay) connection.delegate = self - createAudioSender(connection) - var localStream: RTCVideoTrack? = nil - var remoteStream: RTCVideoTrack? = nil + let device = notConnectedCall?.device ?? .front var localCamera: RTCVideoCapturer? = nil - var localVideoSource: RTCVideoSource? = nil - if mediaType == .video { - (localStream, remoteStream, localCamera, localVideoSource) = createVideoSender(connection) + var localAudioTrack: RTCAudioTrack? = nil + var localVideoTrack: RTCVideoTrack? = nil + if let localCameraAndTrack = notConnectedCall?.localCameraAndTrack { + (localCamera, localVideoTrack) = localCameraAndTrack + } else if notConnectedCall == nil && mediaType == .video { + (localCamera, localVideoTrack) = createVideoTrackAndStartCapture(device) } + if let audioTrack = notConnectedCall?.audioTrack { + localAudioTrack = audioTrack + } else if notConnectedCall == nil { + localAudioTrack = createAudioTrack() + } + notConnectedCall?.localCameraAndTrack = nil + notConnectedCall?.audioTrack = nil + var frameEncryptor: RTCFrameEncryptor? = nil var frameDecryptor: RTCFrameDecryptor? = nil if aesKey != nil { let encryptor = RTCFrameEncryptor.init(sizeChange: Int32(WebRTCClient.ivTagBytes)) encryptor.delegate = self frameEncryptor = encryptor - connection.senders.forEach { $0.setRtcFrameEncryptor(encryptor) } let decryptor = RTCFrameDecryptor.init(sizeChange: -Int32(WebRTCClient.ivTagBytes)) decryptor.delegate = self frameDecryptor = decryptor - // Has no video receiver in outgoing call if applied here, see [peerConnection(_ connection: RTCPeerConnection, didChange newState] - // connection.receivers.forEach { $0.setRtcFrameDecryptor(decryptor) } } return Call( connection: connection, iceCandidates: IceCandidates(), - localMedia: mediaType, localCamera: localCamera, - localVideoSource: localVideoSource, - localStream: localStream, - remoteStream: remoteStream, + localAudioTrack: localAudioTrack, + localVideoTrack: localVideoTrack, + device: device, aesKey: aesKey, frameEncryptor: frameEncryptor, - frameDecryptor: frameDecryptor + frameDecryptor: frameDecryptor, + peerHasOldVersion: false ) } @@ -151,18 +169,24 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg func sendCallCommand(command: WCallCommand) async { var resp: WCallResponse? = nil - let pc = activeCall.wrappedValue?.connection + let pc = activeCall?.connection switch command { - case .capabilities: + case let .capabilities(media): // outgoing + let localCameraAndTrack: (RTCVideoCapturer, RTCVideoTrack)? = media == .video + ? createVideoTrackAndStartCapture(.front) + : nil + notConnectedCall = NotConnectedCall(audioTrack: createAudioTrack(), localCameraAndTrack: localCameraAndTrack, device: .front) resp = .capabilities(capabilities: CallCapabilities(encryption: WebRTCClient.enableEncryption)) - case let .start(media: media, aesKey, iceServers, relay): + case let .start(media: media, aesKey, iceServers, relay): // incoming logger.debug("starting incoming call - create webrtc session") - if activeCall.wrappedValue != nil { endCall() } + if activeCall != nil { endCall() } let encryption = WebRTCClient.enableEncryption let call = initializeCall(iceServers?.toWebRTCIceServers(), media, encryption ? aesKey : nil, relay) - activeCall.wrappedValue = call + activeCall = call + setupLocalTracks(true, call) let (offer, error) = await call.connection.offer() if let offer = offer { + setupEncryptionForLocalTracks(call) resp = .offer( offer: compressToBase64(input: encodeJSON(CustomRTCSessionDescription(type: offer.type.toSdpType(), sdp: offer.sdp))), iceCandidates: compressToBase64(input: encodeJSON(await self.getInitialIceCandidates())), @@ -172,18 +196,24 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg } else { resp = .error(message: "offer error: \(error?.localizedDescription ?? "unknown error")") } - case let .offer(offer, iceCandidates, media, aesKey, iceServers, relay): - if activeCall.wrappedValue != nil { + case let .offer(offer, iceCandidates, media, aesKey, iceServers, relay): // outgoing + if activeCall != nil { resp = .error(message: "accept: call already started") } else if !WebRTCClient.enableEncryption && aesKey != nil { resp = .error(message: "accept: encryption is not supported") } else if let offer: CustomRTCSessionDescription = decodeJSON(decompressFromBase64(input: offer)), let remoteIceCandidates: [RTCIceCandidate] = decodeJSON(decompressFromBase64(input: iceCandidates)) { let call = initializeCall(iceServers?.toWebRTCIceServers(), media, WebRTCClient.enableEncryption ? aesKey : nil, relay) - activeCall.wrappedValue = call + activeCall = call let pc = call.connection if let type = offer.type, let sdp = offer.sdp { if (try? await pc.setRemoteDescription(RTCSessionDescription(type: type.toWebRTCSdpType(), sdp: sdp))) != nil { + setupLocalTracks(false, call) + setupEncryptionForLocalTracks(call) + pc.transceivers.forEach { transceiver in + transceiver.setDirection(.sendRecv, error: nil) + } + await adaptToOldVersion(pc.transceivers.count <= 2) let (answer, error) = await pc.answer() if let answer = answer { self.addIceCandidates(pc, remoteIceCandidates) @@ -200,7 +230,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg } } } - case let .answer(answer, iceCandidates): + case let .answer(answer, iceCandidates): // incoming if pc == nil { resp = .error(message: "answer: call not started") } else if pc?.localDescription == nil { @@ -212,6 +242,9 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg let type = answer.type, let sdp = answer.sdp, let pc = pc { if (try? await pc.setRemoteDescription(RTCSessionDescription(type: type.toWebRTCSdpType(), sdp: sdp))) != nil { + var currentDirection: RTCRtpTransceiverDirection = .sendOnly + pc.transceivers[2].currentDirection(¤tDirection) + await adaptToOldVersion(currentDirection == .sendOnly) addIceCandidates(pc, remoteIceCandidates) resp = .ok } else { @@ -226,13 +259,11 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg } else { resp = .error(message: "ice: call not started") } - case let .media(media, enable): - if activeCall.wrappedValue == nil { + case let .media(source, enable): + if activeCall == nil { resp = .error(message: "media: call not started") - } else if activeCall.wrappedValue?.localMedia == .audio && media == .video { - resp = .error(message: "media: no video") } else { - enableMedia(media, enable) + await enableMedia(source, enable) resp = .ok } case .end: @@ -247,7 +278,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg func getInitialIceCandidates() async -> [RTCIceCandidate] { await untilIceComplete(timeoutMs: 750, stepMs: 150) {} - let candidates = await activeCall.wrappedValue?.iceCandidates.getAndClear() ?? [] + let candidates = await activeCall?.iceCandidates.getAndClear() ?? [] logger.debug("WebRTCClient: sending initial ice candidates: \(candidates.count)") return candidates } @@ -255,7 +286,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg func waitForMoreIceCandidates() { Task { await untilIceComplete(timeoutMs: 12000, stepMs: 1500) { - let candidates = await self.activeCall.wrappedValue?.iceCandidates.getAndClear() ?? [] + let candidates = await self.activeCall?.iceCandidates.getAndClear() ?? [] if candidates.count > 0 { logger.debug("WebRTCClient: sending more ice candidates: \(candidates.count)") await self.sendIceCandidates(candidates) @@ -272,25 +303,202 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg ) } - func enableMedia(_ media: CallMediaType, _ enable: Bool) { - logger.debug("WebRTCClient: enabling media \(media.rawValue) \(enable)") - media == .video ? setVideoEnabled(enable) : setAudioEnabled(enable) + func setupMuteUnmuteListener(_ transceiver: RTCRtpTransceiver, _ track: RTCMediaStreamTrack) { + // logger.log("Setting up mute/unmute listener in the call without encryption for mid = \(transceiver.mid)") + Task { + var lastBytesReceived: Int64 = 0 + // muted initially + var mutedSeconds = 4 + while let call = self.activeCall, transceiver.receiver.track?.readyState == .live { + let stats: RTCStatisticsReport = await call.connection.statistics(for: transceiver.receiver) + let stat = stats.statistics.values.first(where: { stat in stat.type == "inbound-rtp"}) + if let stat { + //logger.debug("Stat \(stat.debugDescription)") + let bytes = stat.values["bytesReceived"] as! Int64 + if bytes <= lastBytesReceived { + mutedSeconds += 1 + if mutedSeconds == 3 { + await MainActor.run { + self.onMediaMuteUnmute(transceiver.mid, true) + } + } + } else { + if mutedSeconds >= 3 { + await MainActor.run { + self.onMediaMuteUnmute(transceiver.mid, false) + } + } + lastBytesReceived = bytes + mutedSeconds = 0 + } + } + try? await Task.sleep(nanoseconds: 1000_000000) + } + } } - func addLocalRenderer(_ activeCall: Call, _ renderer: RTCEAGLVideoView) { - activeCall.localStream?.add(renderer) + @MainActor + func onMediaMuteUnmute(_ transceiverMid: String?, _ mute: Bool) { + guard let activeCall = ChatModel.shared.activeCall else { return } + let source = mediaSourceFromTransceiverMid(transceiverMid) + logger.log("Mute/unmute \(source.rawValue) track = \(mute) with mid = \(transceiverMid ?? "nil")") + if source == .mic && activeCall.peerMediaSources.mic == mute { + activeCall.peerMediaSources.mic = !mute + } else if (source == .camera && activeCall.peerMediaSources.camera == mute) { + activeCall.peerMediaSources.camera = !mute + } else if (source == .screenAudio && activeCall.peerMediaSources.screenAudio == mute) { + activeCall.peerMediaSources.screenAudio = !mute + } else if (source == .screenVideo && activeCall.peerMediaSources.screenVideo == mute) { + activeCall.peerMediaSources.screenVideo = !mute + } + } + + @MainActor + func enableMedia(_ source: CallMediaSource, _ enable: Bool) { + logger.debug("WebRTCClient: enabling media \(source.rawValue) \(enable)") + source == .camera ? setCameraEnabled(enable) : setAudioEnabled(enable) + } + + @MainActor + func adaptToOldVersion(_ peerHasOldVersion: Bool) { + activeCall?.peerHasOldVersion = peerHasOldVersion + if peerHasOldVersion { + logger.debug("The peer has an old version. Remote audio track is nil = \(self.activeCall?.remoteAudioTrack == nil), video = \(self.activeCall?.remoteVideoTrack == nil)") + onMediaMuteUnmute("0", false) + if activeCall?.remoteVideoTrack != nil { + onMediaMuteUnmute("1", false) + } + if ChatModel.shared.activeCall?.localMediaSources.camera == true && ChatModel.shared.activeCall?.peerMediaSources.camera == false { + logger.debug("Stopping video track for the old version") + activeCall?.connection.senders[1].track = nil + ChatModel.shared.activeCall?.localMediaSources.camera = false + (activeCall?.localCamera as? RTCCameraVideoCapturer)?.stopCapture() + activeCall?.localCamera = nil + activeCall?.localVideoTrack = nil + } + } + } + + func addLocalRenderer(_ renderer: RTCEAGLVideoView) { + if let activeCall { + if let track = activeCall.localVideoTrack { + track.add(renderer) + } + } else if let notConnectedCall { + if let track = notConnectedCall.localCameraAndTrack?.1 { + track.add(renderer) + } + } // To get width and height of a frame, see videoView(videoView:, didChangeVideoSize) renderer.delegate = self } + func removeLocalRenderer(_ renderer: RTCEAGLVideoView) { + if let activeCall { + if let track = activeCall.localVideoTrack { + track.remove(renderer) + } + } else if let notConnectedCall { + if let track = notConnectedCall.localCameraAndTrack?.1 { + track.remove(renderer) + } + } + renderer.delegate = nil + } + func videoView(_ videoView: RTCVideoRenderer, didChangeVideoSize size: CGSize) { guard size.height > 0 else { return } localRendererAspectRatio.wrappedValue = size.width / size.height } + func setupLocalTracks(_ incomingCall: Bool, _ call: Call) { + let pc = call.connection + let transceivers = call.connection.transceivers + let audioTrack = call.localAudioTrack + let videoTrack = call.localVideoTrack + + if incomingCall { + let micCameraInit = RTCRtpTransceiverInit() + // streamIds required for old versions which adds tracks from stream, not from track property + micCameraInit.streamIds = ["micCamera"] + + let screenAudioVideoInit = RTCRtpTransceiverInit() + screenAudioVideoInit.streamIds = ["screenAudioVideo"] + + // incoming call, no transceivers yet. But they should be added in order: mic, camera, screen audio, screen video + // mid = 0, mic + if let audioTrack { + pc.addTransceiver(with: audioTrack, init: micCameraInit) + } else { + pc.addTransceiver(of: .audio, init: micCameraInit) + } + // mid = 1, camera + if let videoTrack { + pc.addTransceiver(with: videoTrack, init: micCameraInit) + } else { + pc.addTransceiver(of: .video, init: micCameraInit) + } + // mid = 2, screenAudio + pc.addTransceiver(of: .audio, init: screenAudioVideoInit) + // mid = 3, screenVideo + pc.addTransceiver(of: .video, init: screenAudioVideoInit) + } else { + // new version + if transceivers.count > 2 { + // Outgoing call. All transceivers are ready. Don't addTrack() because it will create new transceivers, replace existing (nil) tracks + transceivers + .first(where: { elem in mediaSourceFromTransceiverMid(elem.mid) == .mic })? + .sender.track = audioTrack + transceivers + .first(where: { elem in mediaSourceFromTransceiverMid(elem.mid) == .camera })? + .sender.track = videoTrack + } else { + // old version, only two transceivers + if let audioTrack { + pc.add(audioTrack, streamIds: ["micCamera"]) + } else { + // it's important to have any track in order to be able to turn it on again (currently it's off) + let sender = pc.add(createAudioTrack(), streamIds: ["micCamera"]) + sender?.track = nil + } + if let videoTrack { + pc.add(videoTrack, streamIds: ["micCamera"]) + } else { + // it's important to have any track in order to be able to turn it on again (currently it's off) + let localVideoSource = WebRTCClient.factory.videoSource() + let localVideoTrack = WebRTCClient.factory.videoTrack(with: localVideoSource, trackId: "video0") + let sender = pc.add(localVideoTrack, streamIds: ["micCamera"]) + sender?.track = nil + } + } + } + } + + func mediaSourceFromTransceiverMid(_ mid: String?) -> CallMediaSource { + switch mid { + case "0": + return .mic + case "1": + return .camera + case "2": + return .screenAudio + case "3": + return .screenVideo + default: + return .unknown + } + } + + // Should be called after local description set + func setupEncryptionForLocalTracks(_ call: Call) { + if let encryptor = call.frameEncryptor { + call.connection.senders.forEach { $0.setRtcFrameEncryptor(encryptor) } + } + } + func frameDecryptor(_ decryptor: RTCFrameDecryptor, mediaType: RTCRtpMediaType, withFrame encrypted: Data) -> Data? { guard encrypted.count > 0 else { return nil } - if var key: [CChar] = activeCall.wrappedValue?.aesKey?.cString(using: .utf8), + if var key: [CChar] = activeCall?.aesKey?.cString(using: .utf8), let pointer: UnsafeMutableRawPointer = malloc(encrypted.count) { memcpy(pointer, (encrypted as NSData).bytes, encrypted.count) let isKeyFrame = encrypted[0] & 1 == 0 @@ -304,7 +512,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg func frameEncryptor(_ encryptor: RTCFrameEncryptor, mediaType: RTCRtpMediaType, withFrame unencrypted: Data) -> Data? { guard unencrypted.count > 0 else { return nil } - if var key: [CChar] = activeCall.wrappedValue?.aesKey?.cString(using: .utf8), + if var key: [CChar] = activeCall?.aesKey?.cString(using: .utf8), let pointer: UnsafeMutableRawPointer = malloc(unencrypted.count + WebRTCClient.ivTagBytes) { memcpy(pointer, (unencrypted as NSData).bytes, unencrypted.count) let isKeyFrame = unencrypted[0] & 1 == 0 @@ -327,18 +535,42 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg } } - func addRemoteRenderer(_ activeCall: Call, _ renderer: RTCVideoRenderer) { - activeCall.remoteStream?.add(renderer) + func addRemoteCameraRenderer(_ renderer: RTCVideoRenderer) { + if activeCall?.remoteVideoTrack != nil { + activeCall?.remoteVideoTrack?.add(renderer) + } else { + cameraRenderers.append(renderer) + } } - func removeRemoteRenderer(_ activeCall: Call, _ renderer: RTCVideoRenderer) { - activeCall.remoteStream?.remove(renderer) + func removeRemoteCameraRenderer(_ renderer: RTCVideoRenderer) { + if activeCall?.remoteVideoTrack != nil { + activeCall?.remoteVideoTrack?.remove(renderer) + } else { + cameraRenderers.removeAll(where: { $0.isEqual(renderer) }) + } } - func startCaptureLocalVideo(_ activeCall: Call) { + func addRemoteScreenRenderer(_ renderer: RTCVideoRenderer) { + if activeCall?.remoteScreenVideoTrack != nil { + activeCall?.remoteScreenVideoTrack?.add(renderer) + } else { + screenRenderers.append(renderer) + } + } + + func removeRemoteScreenRenderer(_ renderer: RTCVideoRenderer) { + if activeCall?.remoteScreenVideoTrack != nil { + activeCall?.remoteScreenVideoTrack?.remove(renderer) + } else { + screenRenderers.removeAll(where: { $0.isEqual(renderer) }) + } + } + + func startCaptureLocalVideo(_ device: AVCaptureDevice.Position?, _ capturer: RTCVideoCapturer?) { #if targetEnvironment(simulator) guard - let capturer = activeCall.localCamera as? RTCFileVideoCapturer + let capturer = (activeCall?.localCamera ?? notConnectedCall?.localCameraAndTrack?.0) as? RTCFileVideoCapturer else { logger.error("Unable to work with a file capturer") return @@ -348,10 +580,10 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg capturer.startCapturing(fromFileNamed: "sounds/video.mp4") #else guard - let capturer = activeCall.localCamera as? RTCCameraVideoCapturer, - let camera = (RTCCameraVideoCapturer.captureDevices().first { $0.position == activeCall.device }) + let capturer = capturer as? RTCCameraVideoCapturer, + let camera = (RTCCameraVideoCapturer.captureDevices().first { $0.position == device }) else { - logger.error("Unable to find a camera") + logger.error("Unable to find a camera or local track") return } @@ -377,19 +609,6 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg #endif } - private func createAudioSender(_ connection: RTCPeerConnection) { - let streamId = "stream" - let audioTrack = createAudioTrack() - connection.add(audioTrack, streamIds: [streamId]) - } - - private func createVideoSender(_ connection: RTCPeerConnection) -> (RTCVideoTrack?, RTCVideoTrack?, RTCVideoCapturer?, RTCVideoSource?) { - let streamId = "stream" - let (localVideoTrack, localCamera, localVideoSource) = createVideoTrack() - connection.add(localVideoTrack, streamIds: [streamId]) - return (localVideoTrack, connection.transceivers.first { $0.mediaType == .video }?.receiver.track as? RTCVideoTrack, localCamera, localVideoSource) - } - private func createAudioTrack() -> RTCAudioTrack { let audioConstrains = RTCMediaConstraints(mandatoryConstraints: nil, optionalConstraints: nil) let audioSource = WebRTCClient.factory.audioSource(with: audioConstrains) @@ -397,7 +616,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg return audioTrack } - private func createVideoTrack() -> (RTCVideoTrack, RTCVideoCapturer, RTCVideoSource) { + private func createVideoTrackAndStartCapture(_ device: AVCaptureDevice.Position) -> (RTCVideoCapturer, RTCVideoTrack) { let localVideoSource = WebRTCClient.factory.videoSource() #if targetEnvironment(simulator) @@ -407,7 +626,8 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg #endif let localVideoTrack = WebRTCClient.factory.videoTrack(with: localVideoSource, trackId: "video0") - return (localVideoTrack, localCamera, localVideoSource) + startCaptureLocalVideo(device, localCamera) + return (localCamera, localVideoTrack) } func endCall() { @@ -420,15 +640,16 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg } private func _endCall() { - guard let call = activeCall.wrappedValue else { return } + (notConnectedCall?.localCameraAndTrack?.0 as? RTCCameraVideoCapturer)?.stopCapture() + guard let call = activeCall else { return } logger.debug("WebRTCClient: ending the call") - activeCall.wrappedValue = nil - (call.localCamera as? RTCCameraVideoCapturer)?.stopCapture() call.connection.close() call.connection.delegate = nil call.frameEncryptor?.delegate = nil call.frameDecryptor?.delegate = nil + (call.localCamera as? RTCCameraVideoCapturer)?.stopCapture() audioSessionToDefaults() + activeCall = nil } func untilIceComplete(timeoutMs: UInt64, stepMs: UInt64, action: @escaping () async -> Void) async { @@ -437,7 +658,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg _ = try? await Task.sleep(nanoseconds: stepMs * 1000000) t += stepMs await action() - } while t < timeoutMs && activeCall.wrappedValue?.connection.iceGatheringState != .complete + } while t < timeoutMs && activeCall?.connection.iceGatheringState != .complete } } @@ -498,11 +719,40 @@ extension WebRTCClient: RTCPeerConnectionDelegate { logger.debug("Connection should negotiate") } + func peerConnection(_ peerConnection: RTCPeerConnection, didStartReceivingOn transceiver: RTCRtpTransceiver) { + if let track = transceiver.receiver.track { + DispatchQueue.main.async { + // Doesn't work for outgoing video call (audio in video call works ok still, same as incoming call) +// if let decryptor = self.activeCall?.frameDecryptor { +// transceiver.receiver.setRtcFrameDecryptor(decryptor) +// } + let source = self.mediaSourceFromTransceiverMid(transceiver.mid) + switch source { + case .mic: self.activeCall?.remoteAudioTrack = track as? RTCAudioTrack + case .camera: + self.activeCall?.remoteVideoTrack = track as? RTCVideoTrack + self.cameraRenderers.forEach({ renderer in + self.activeCall?.remoteVideoTrack?.add(renderer) + }) + self.cameraRenderers.removeAll() + case .screenAudio: self.activeCall?.remoteScreenAudioTrack = track as? RTCAudioTrack + case .screenVideo: + self.activeCall?.remoteScreenVideoTrack = track as? RTCVideoTrack + self.screenRenderers.forEach({ renderer in + self.activeCall?.remoteScreenVideoTrack?.add(renderer) + }) + self.screenRenderers.removeAll() + case .unknown: () + } + } + self.setupMuteUnmuteListener(transceiver, track) + } + } + func peerConnection(_ connection: RTCPeerConnection, didChange newState: RTCIceConnectionState) { debugPrint("Connection new connection state: \(newState.toString() ?? "" + newState.rawValue.description) \(connection.receivers)") - guard let call = activeCall.wrappedValue, - let connectionStateString = newState.toString(), + guard let connectionStateString = newState.toString(), let iceConnectionStateString = connection.iceConnectionState.toString(), let iceGatheringStateString = connection.iceGatheringState.toString(), let signalingStateString = connection.signalingState.toString() @@ -523,18 +773,14 @@ extension WebRTCClient: RTCPeerConnectionDelegate { switch newState { case .checking: - if let frameDecryptor = activeCall.wrappedValue?.frameDecryptor { + if let frameDecryptor = activeCall?.frameDecryptor { connection.receivers.forEach { $0.setRtcFrameDecryptor(frameDecryptor) } } - let enableSpeaker: Bool - switch call.localMedia { - case .video: enableSpeaker = true - default: enableSpeaker = false - } + let enableSpeaker: Bool = ChatModel.shared.activeCall?.localMediaSources.hasVideo == true setSpeakerEnabledAndConfigureSession(enableSpeaker) case .connected: sendConnectedEvent(connection) case .disconnected, .failed: endCall() - default: do {} + default: () } } } @@ -546,7 +792,7 @@ extension WebRTCClient: RTCPeerConnectionDelegate { func peerConnection(_ connection: RTCPeerConnection, didGenerate candidate: WebRTC.RTCIceCandidate) { // logger.debug("Connection generated candidate \(candidate.debugDescription)") Task { - await self.activeCall.wrappedValue?.iceCandidates.append(candidate.toCandidate(nil, nil)) + await self.activeCall?.iceCandidates.append(candidate.toCandidate(nil, nil)) } } @@ -601,11 +847,42 @@ extension WebRTCClient: RTCPeerConnectionDelegate { } extension WebRTCClient { - func setAudioEnabled(_ enabled: Bool) { - setTrackEnabled(RTCAudioTrack.self, enabled) + static func isAuthorized(for type: AVMediaType) async -> Bool { + let status = AVCaptureDevice.authorizationStatus(for: type) + var isAuthorized = status == .authorized + if status == .notDetermined { + isAuthorized = await AVCaptureDevice.requestAccess(for: type) + } + return isAuthorized } - func setSpeakerEnabledAndConfigureSession( _ enabled: Bool) { + static func showUnauthorizedAlert(for type: AVMediaType) { + if type == .audio { + AlertManager.shared.showAlert(Alert( + title: Text("No permission to record speech"), + message: Text("To record speech please grant permission to use Microphone."), + primaryButton: .default(Text("Open Settings")) { + DispatchQueue.main.async { + UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil) + } + }, + secondaryButton: .cancel() + )) + } else if type == .video { + AlertManager.shared.showAlert(Alert( + title: Text("No permission to record video"), + message: Text("To record video please grant permission to use Camera."), + primaryButton: .default(Text("Open Settings")) { + DispatchQueue.main.async { + UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!, options: [:], completionHandler: nil) + } + }, + secondaryButton: .cancel() + )) + } + } + + func setSpeakerEnabledAndConfigureSession( _ enabled: Bool, skipExternalDevice: Bool = false) { logger.debug("WebRTCClient: configuring session with speaker enabled \(enabled)") audioQueue.async { [weak self] in guard let self = self else { return } @@ -618,7 +895,7 @@ extension WebRTCClient { if enabled { try self.rtcAudioSession.setCategory(AVAudioSession.Category.playAndRecord.rawValue, with: [.defaultToSpeaker, .allowBluetooth, .allowAirPlay, .allowBluetoothA2DP]) try self.rtcAudioSession.setMode(AVAudioSession.Mode.videoChat.rawValue) - if hasExternalAudioDevice, let preferred = self.rtcAudioSession.session.preferredInputDevice() { + if hasExternalAudioDevice && !skipExternalDevice, let preferred = self.rtcAudioSession.session.preferredInputDevice() { try self.rtcAudioSession.setPreferredInput(preferred) } else { try self.rtcAudioSession.overrideOutputAudioPort(.speaker) @@ -628,7 +905,7 @@ extension WebRTCClient { try self.rtcAudioSession.setMode(AVAudioSession.Mode.voiceChat.rawValue) try self.rtcAudioSession.overrideOutputAudioPort(.none) } - if hasExternalAudioDevice { + if hasExternalAudioDevice && !skipExternalDevice { logger.debug("WebRTCClient: configuring session with external device available, skip configuring speaker") } try self.rtcAudioSession.setActive(true) @@ -659,25 +936,59 @@ extension WebRTCClient { } } - func setVideoEnabled(_ enabled: Bool) { - setTrackEnabled(RTCVideoTrack.self, enabled) + @MainActor + func setAudioEnabled(_ enabled: Bool) { + if activeCall != nil { + activeCall?.localAudioTrack = enabled ? createAudioTrack() : nil + activeCall?.connection.transceivers.first(where: { t in mediaSourceFromTransceiverMid(t.mid) == .mic })?.sender.track = activeCall?.localAudioTrack + } else if notConnectedCall != nil { + notConnectedCall?.audioTrack = enabled ? createAudioTrack() : nil + } + ChatModel.shared.activeCall?.localMediaSources.mic = enabled + } + + @MainActor + func setCameraEnabled(_ enabled: Bool) { + if let call = activeCall { + if enabled { + if call.localVideoTrack == nil { + let device = activeCall?.device ?? notConnectedCall?.device ?? .front + let (camera, track) = createVideoTrackAndStartCapture(device) + activeCall?.localCamera = camera + activeCall?.localVideoTrack = track + } + } else { + (call.localCamera as? RTCCameraVideoCapturer)?.stopCapture() + activeCall?.localCamera = nil + activeCall?.localVideoTrack = nil + } + call.connection.transceivers + .first(where: { t in mediaSourceFromTransceiverMid(t.mid) == .camera })? + .sender.track = activeCall?.localVideoTrack + ChatModel.shared.activeCall?.localMediaSources.camera = activeCall?.localVideoTrack != nil + } else if let call = notConnectedCall { + if enabled { + let device = activeCall?.device ?? notConnectedCall?.device ?? .front + notConnectedCall?.localCameraAndTrack = createVideoTrackAndStartCapture(device) + } else { + (call.localCameraAndTrack?.0 as? RTCCameraVideoCapturer)?.stopCapture() + notConnectedCall?.localCameraAndTrack = nil + } + ChatModel.shared.activeCall?.localMediaSources.camera = notConnectedCall?.localCameraAndTrack != nil + } } func flipCamera() { - switch activeCall.wrappedValue?.device { - case .front: activeCall.wrappedValue?.device = .back - case .back: activeCall.wrappedValue?.device = .front - default: () + let device = activeCall?.device ?? notConnectedCall?.device + if activeCall != nil { + activeCall?.device = device == .front ? .back : .front + } else { + notConnectedCall?.device = device == .front ? .back : .front } - if let call = activeCall.wrappedValue { - startCaptureLocalVideo(call) - } - } - - private func setTrackEnabled(_ type: T.Type, _ enabled: Bool) { - activeCall.wrappedValue?.connection.transceivers - .compactMap { $0.sender.track as? T } - .forEach { $0.isEnabled = enabled } + startCaptureLocalVideo( + activeCall?.device ?? notConnectedCall?.device, + (activeCall?.localCamera ?? notConnectedCall?.localCameraAndTrack?.0) as? RTCCameraVideoCapturer + ) } } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIGroupInvitationView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIGroupInvitationView.swift index ef0fec5dfe..1a77b36d6f 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIGroupInvitationView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIGroupInvitationView.swift @@ -12,6 +12,7 @@ import SimpleXChat struct CIGroupInvitationView: View { @EnvironmentObject var chatModel: ChatModel @EnvironmentObject var theme: AppTheme + @Environment(\.showTimestamp) var showTimestamp: Bool @ObservedObject var chat: Chat var chatItem: ChatItem var groupInvitation: CIGroupInvitation @@ -45,7 +46,7 @@ struct CIGroupInvitationView: View { .foregroundColor(inProgress ? theme.colors.secondary : chatIncognito ? .indigo : theme.colors.primary) .font(.callout) + Text(" ") - + ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true, showStatus: false, showEdited: false, showViaProxy: showSentViaProxy) + + ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, colorMode: .transparent, showStatus: false, showEdited: false, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp) ) .overlay(DetermineWidth()) } @@ -53,7 +54,7 @@ struct CIGroupInvitationView: View { ( groupInvitationText() + Text(" ") - + ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true, showStatus: false, showEdited: false, showViaProxy: showSentViaProxy) + + ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, colorMode: .transparent, showStatus: false, showEdited: false, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp) ) .overlay(DetermineWidth()) } @@ -69,7 +70,7 @@ struct CIGroupInvitationView: View { } .padding(.horizontal, 12) .padding(.vertical, 6) - .background(chatItemFrameColor(chatItem, theme)) + .background { chatItemFrameColor(chatItem, theme).modifier(ChatTailPadding()) } .textSelection(.disabled) .onPreferenceChange(DetermineWidth.Key.self) { frameWidth = $0 } .onChange(of: inProgress) { inProgress in diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift index 3966d7e258..b06c6df48c 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift @@ -165,9 +165,9 @@ struct CIImageView: View { private func fileIcon(_ icon: String, _ size: CGFloat, _ padding: CGFloat) -> some View { Image(systemName: icon) .resizable() + .invertedForegroundStyle() .aspectRatio(contentMode: .fit) .frame(width: size, height: size) - .foregroundColor(.white) .padding(padding) } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift index 3c864ab172..692e6bb8a6 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift @@ -16,7 +16,7 @@ struct CILinkView: View { var body: some View { VStack(alignment: .center, spacing: 6) { - if let uiImage = UIImage(base64Encoded: linkPreview.image) { + if let uiImage = imageFromBase64(linkPreview.image) { Image(uiImage: uiImage) .resizable() .scaledToFit() diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIMetaView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIMetaView.swift index 66b810cf2f..32249506d3 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIMetaView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIMetaView.swift @@ -12,11 +12,13 @@ import SimpleXChat struct CIMetaView: View { @ObservedObject var chat: Chat @EnvironmentObject var theme: AppTheme + @Environment(\.showTimestamp) var showTimestamp: Bool var chatItem: ChatItem var metaColor: Color var paleMetaColor = Color(UIColor.tertiaryLabel) var showStatus = true var showEdited = true + var invertedMaterial = false @AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = false @@ -24,93 +26,143 @@ struct CIMetaView: View { if chatItem.isDeletedContent { chatItem.timestampText.font(.caption).foregroundColor(metaColor) } else { - let meta = chatItem.meta - let ttl = chat.chatInfo.timedMessagesTTL - let encrypted = chatItem.encryptedFile - switch meta.itemStatus { - case let .sndSent(sndProgress): - switch sndProgress { - case .complete: ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, sent: .sent, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy) - case .partial: ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: paleMetaColor, sent: .sent, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy) + ZStack { + ciMetaText( + chatItem.meta, + chatTTL: chat.chatInfo.timedMessagesTTL, + encrypted: chatItem.encryptedFile, + color: metaColor, + paleColor: paleMetaColor, + colorMode: invertedMaterial + ? .invertedMaterial + : .normal, + showStatus: showStatus, + showEdited: showEdited, + showViaProxy: showSentViaProxy, + showTimesamp: showTimestamp + ).invertedForegroundStyle(enabled: invertedMaterial) + if invertedMaterial { + ciMetaText( + chatItem.meta, + chatTTL: chat.chatInfo.timedMessagesTTL, + encrypted: chatItem.encryptedFile, + colorMode: .normal, + onlyOverrides: true, + showStatus: showStatus, + showEdited: showEdited, + showViaProxy: showSentViaProxy, + showTimesamp: showTimestamp + ) } - case let .sndRcvd(_, sndProgress): - switch sndProgress { - case .complete: - ZStack { - ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, sent: .rcvd1, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy) - ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, sent: .rcvd2, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy) - } - case .partial: - ZStack { - ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: paleMetaColor, sent: .rcvd1, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy) - ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: paleMetaColor, sent: .rcvd2, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy) - } - } - default: - ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy) } } } } -enum SentCheckmark { - case sent - case rcvd1 - case rcvd2 +enum MetaColorMode { + // Renders provided colours + case normal + // Fully transparent meta - used for reserving space + case transparent + // Renders white on dark backgrounds and black on light ones + case invertedMaterial + + func resolve(_ c: Color?) -> Color? { + switch self { + case .normal: c + case .transparent: .clear + case .invertedMaterial: nil + } + } + + func statusSpacer(_ sent: Bool) -> Text { + switch self { + case .normal, .transparent: + Text( + sent + ? Image("checkmark.wide") + : Image(systemName: "circlebadge.fill") + ).foregroundColor(.clear) + case .invertedMaterial: Text(" ").kerning(13) + } + } } func ciMetaText( _ meta: CIMeta, chatTTL: Int?, encrypted: Bool?, - color: Color = .clear, + color: Color = .clear, // we use this function to reserve space without rendering meta + paleColor: Color? = nil, primaryColor: Color = .accentColor, - transparent: Bool = false, - sent: SentCheckmark? = nil, + colorMode: MetaColorMode = .normal, + onlyOverrides: Bool = false, // only render colors that differ from base showStatus: Bool = true, showEdited: Bool = true, - showViaProxy: Bool + showViaProxy: Bool, + showTimesamp: Bool ) -> Text { var r = Text("") + var space: Text? = nil + let appendSpace = { + if let sp = space { + r = r + sp + space = nil + } + } + let resolved = colorMode.resolve(color) if showEdited, meta.itemEdited { - r = r + statusIconText("pencil", color) + r = r + statusIconText("pencil", resolved) } if meta.disappearing { - r = r + statusIconText("timer", color).font(.caption2) + r = r + statusIconText("timer", resolved).font(.caption2) let ttl = meta.itemTimed?.ttl if ttl != chatTTL { - r = r + Text(shortTimeText(ttl)).foregroundColor(color) + r = r + colored(Text(shortTimeText(ttl)), resolved) } - r = r + Text(" ") + space = Text(" ") } if showViaProxy, meta.sentViaProxy == true { - r = r + statusIconText("arrow.forward", color.opacity(0.67)).font(.caption2) + appendSpace() + r = r + statusIconText("arrow.forward", resolved?.opacity(0.67)).font(.caption2) } if showStatus { - if let (icon, statusColor) = meta.statusIcon(color, primaryColor) { - let t = Text(Image(systemName: icon)).font(.caption2) - let gap = Text(" ").kerning(-1.25) - let t1 = t.foregroundColor(transparent ? .clear : statusColor.opacity(0.67)) - switch sent { - case nil: r = r + t1 - case .sent: r = r + t1 + gap - case .rcvd1: r = r + t.foregroundColor(transparent ? .clear : statusColor.opacity(0.67)) + gap - case .rcvd2: r = r + gap + t1 + appendSpace() + if let (image, statusColor) = meta.itemStatus.statusIcon(color, paleColor ?? color, primaryColor) { + let metaColor = if onlyOverrides && statusColor == color { + Color.clear + } else { + colorMode.resolve(statusColor) } - r = r + Text(" ") + r = r + colored(Text(image), metaColor) } else if !meta.disappearing { - r = r + statusIconText("circlebadge.fill", .clear) + Text(" ") + r = r + colorMode.statusSpacer(meta.itemStatus.sent) } + space = Text(" ") } if let enc = encrypted { - r = r + statusIconText(enc ? "lock" : "lock.open", color) + Text(" ") + appendSpace() + r = r + statusIconText(enc ? "lock" : "lock.open", resolved) + space = Text(" ") + } + if showTimesamp { + appendSpace() + r = r + colored(meta.timestampText, resolved) } - r = r + meta.timestampText.foregroundColor(color) return r.font(.caption) } -private func statusIconText(_ icon: String, _ color: Color) -> Text { - Text(Image(systemName: icon)).foregroundColor(color) +private func statusIconText(_ icon: String, _ color: Color?) -> Text { + colored(Text(Image(systemName: icon)), color) +} + +// Applying `foregroundColor(nil)` breaks `.invertedForegroundStyle` modifier +private func colored(_ t: Text, _ color: Color?) -> Text { + if let color { + t.foregroundColor(color) + } else { + t + } } struct CIMetaView_Previews: PreviewProvider { diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIRcvDecryptionError.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIRcvDecryptionError.swift index 1f2e16448d..693641b1d3 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIRcvDecryptionError.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIRcvDecryptionError.swift @@ -15,6 +15,7 @@ struct CIRcvDecryptionError: View { @EnvironmentObject var m: ChatModel @EnvironmentObject var theme: AppTheme @ObservedObject var chat: Chat + @Environment(\.showTimestamp) var showTimestamp: Bool var msgDecryptError: MsgDecryptError var msgCount: UInt32 var chatItem: ChatItem @@ -47,7 +48,7 @@ struct CIRcvDecryptionError: View { if case let .group(groupInfo) = chat.chatInfo, case let .groupRcv(groupMember) = chatItem.chatDir { do { - let (member, stats) = try apiGroupMemberInfo(groupInfo.apiId, groupMember.groupMemberId) + let (member, stats) = try apiGroupMemberInfoSync(groupInfo.apiId, groupMember.groupMemberId) if let s = stats { m.updateGroupMemberConnectionStats(groupInfo, member, s) } @@ -68,37 +69,40 @@ struct CIRcvDecryptionError: View { } @ViewBuilder private func viewBody() -> some View { - if case let .direct(contact) = chat.chatInfo, - let contactStats = contact.activeConn?.connectionStats { - if contactStats.ratchetSyncAllowed { - decryptionErrorItemFixButton(syncSupported: true) { - alert = .syncAllowedAlert { syncContactConnection(contact) } + Group { + if case let .direct(contact) = chat.chatInfo, + let contactStats = contact.activeConn?.connectionStats { + if contactStats.ratchetSyncAllowed { + decryptionErrorItemFixButton(syncSupported: true) { + alert = .syncAllowedAlert { syncContactConnection(contact) } + } + } else if !contactStats.ratchetSyncSupported { + decryptionErrorItemFixButton(syncSupported: false) { + alert = .syncNotSupportedContactAlert + } + } else { + basicDecryptionErrorItem() } - } else if !contactStats.ratchetSyncSupported { - decryptionErrorItemFixButton(syncSupported: false) { - alert = .syncNotSupportedContactAlert + } else if case let .group(groupInfo) = chat.chatInfo, + case let .groupRcv(groupMember) = chatItem.chatDir, + let mem = m.getGroupMember(groupMember.groupMemberId), + let memberStats = mem.wrapped.activeConn?.connectionStats { + if memberStats.ratchetSyncAllowed { + decryptionErrorItemFixButton(syncSupported: true) { + alert = .syncAllowedAlert { syncMemberConnection(groupInfo, groupMember) } + } + } else if !memberStats.ratchetSyncSupported { + decryptionErrorItemFixButton(syncSupported: false) { + alert = .syncNotSupportedMemberAlert + } + } else { + basicDecryptionErrorItem() } } else { basicDecryptionErrorItem() } - } else if case let .group(groupInfo) = chat.chatInfo, - case let .groupRcv(groupMember) = chatItem.chatDir, - let mem = m.getGroupMember(groupMember.groupMemberId), - let memberStats = mem.wrapped.activeConn?.connectionStats { - if memberStats.ratchetSyncAllowed { - decryptionErrorItemFixButton(syncSupported: true) { - alert = .syncAllowedAlert { syncMemberConnection(groupInfo, groupMember) } - } - } else if !memberStats.ratchetSyncSupported { - decryptionErrorItemFixButton(syncSupported: false) { - alert = .syncNotSupportedMemberAlert - } - } else { - basicDecryptionErrorItem() - } - } else { - basicDecryptionErrorItem() } + .background { chatItemFrameColor(chatItem, theme).modifier(ChatTailPadding()) } } private func basicDecryptionErrorItem() -> some View { @@ -122,7 +126,7 @@ struct CIRcvDecryptionError: View { .foregroundColor(syncSupported ? theme.colors.primary : theme.colors.secondary) .font(.callout) + Text(" ") - + ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true, showViaProxy: showSentViaProxy) + + ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, colorMode: .transparent, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp) ) } .padding(.horizontal, 12) @@ -131,7 +135,6 @@ struct CIRcvDecryptionError: View { } .onTapGesture(perform: { onClick() }) .padding(.vertical, 6) - .background(Color(uiColor: .tertiarySystemGroupedBackground)) .textSelection(.disabled) } @@ -142,7 +145,7 @@ struct CIRcvDecryptionError: View { .foregroundColor(.red) .italic() + Text(" ") - + ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true, showViaProxy: showSentViaProxy) + + ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, colorMode: .transparent, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp) } .padding(.horizontal, 12) CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary) @@ -150,7 +153,6 @@ struct CIRcvDecryptionError: View { } .onTapGesture(perform: { onClick() }) .padding(.vertical, 6) - .background(Color(uiColor: .tertiarySystemGroupedBackground)) .textSelection(.disabled) } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift index 4670fc685f..851b90bc3d 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift @@ -292,30 +292,22 @@ struct CIVideoView: View { .clipShape(Circle()) } - private func durationProgress() -> some View { - HStack { - Text("\(durationText(videoPlaying ? progress : duration))") - .foregroundColor(.white) - .font(.caption) - .padding(.vertical, 3) - .padding(.horizontal, 6) - .background(Color.black.opacity(0.35)) - .cornerRadius(10) - .padding([.top, .leading], 6) - - if let file = chatItem.file, !videoPlaying { - Text("\(ByteCountFormatter.string(fromByteCount: file.fileSize, countStyle: .binary))") - .foregroundColor(.white) - .font(.caption) - .padding(.vertical, 3) - .padding(.horizontal, 6) - .background(Color.black.opacity(0.35)) - .cornerRadius(10) - .padding(.top, 6) - } + private var fileSizeString: String { + if let file = chatItem.file, !videoPlaying { + " " + ByteCountFormatter.string(fromByteCount: file.fileSize, countStyle: .binary) + } else { + "" } } + private func durationProgress() -> some View { + Text((durationText(videoPlaying ? progress : duration)) + fileSizeString) + .invertedForegroundStyle() + .font(.caption) + .padding(.vertical, 6) + .padding(.horizontal, 12) + } + private func imageView(_ img: UIImage) -> some View { let w = img.size.width <= img.size.height ? maxWidth * 0.75 : maxWidth return ZStack(alignment: .topTrailing) { @@ -411,9 +403,9 @@ struct CIVideoView: View { private func fileIcon(_ icon: String, _ size: CGFloat, _ padding: CGFloat) -> some View { Image(systemName: icon) .resizable() + .invertedForegroundStyle() .aspectRatio(contentMode: .fit) .frame(width: size, height: size) - .foregroundColor(.white) .padding(smallView ? 0 : padding) } @@ -428,10 +420,8 @@ struct CIVideoView: View { private func progressCircle(_ progress: Int64, _ total: Int64) -> some View { Circle() .trim(from: 0, to: Double(progress) / Double(total)) - .stroke( - Color(uiColor: .white), - style: StrokeStyle(lineWidth: 2) - ) + .stroke(style: StrokeStyle(lineWidth: 2)) + .invertedForegroundStyle() .rotationEffect(.degrees(-90)) .frame(width: 16, height: 16) .padding([.trailing, .top], smallView ? 0 : 11) diff --git a/apps/ios/Shared/Views/Chat/ChatItem/FramedItemView.swift b/apps/ios/Shared/Views/Chat/ChatItem/FramedItemView.swift index 313ec0d419..5f2930951f 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/FramedItemView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/FramedItemView.swift @@ -12,7 +12,7 @@ import SimpleXChat struct FramedItemView: View { @EnvironmentObject var m: ChatModel @EnvironmentObject var theme: AppTheme - @EnvironmentObject var scrollModel: ReverseListScrollModel + @EnvironmentObject var scrollModel: ReverseListScrollModel @ObservedObject var chat: Chat var chatItem: ChatItem var preview: UIImage? @@ -64,15 +64,20 @@ struct FramedItemView: View { .overlay(DetermineWidth()) } - if chatItem.content.msgContent != nil { - CIMetaView(chat: chat, chatItem: chatItem, metaColor: useWhiteMetaColor ? Color.white : theme.colors.secondary) - .padding(.horizontal, 12) - .padding(.bottom, 6) - .overlay(DetermineWidth()) - .accessibilityLabel("") + if let content = chatItem.content.msgContent { + CIMetaView( + chat: chat, + chatItem: chatItem, + metaColor: theme.colors.secondary, + invertedMaterial: useWhiteMetaColor + ) + .padding(.horizontal, 12) + .padding(.bottom, 6) + .overlay(DetermineWidth()) + .accessibilityLabel("") } - } - .background(chatItemFrameColorMaybeImageOrVideo(chatItem, theme)) + } + .background { chatItemFrameColorMaybeImageOrVideo(chatItem, theme).modifier(ChatTailPadding()) } .onPreferenceChange(DetermineWidth.Key.self) { msgWidth = $0 } if let (title, text) = chatItem.meta.itemStatus.statusInfo { @@ -185,7 +190,7 @@ struct FramedItemView: View { let v = ZStack(alignment: .topTrailing) { switch (qi.content) { case let .image(_, image): - if let uiImage = UIImage(base64Encoded: image) { + if let uiImage = imageFromBase64(image) { ciQuotedMsgView(qi) .padding(.trailing, 70).frame(minWidth: msgWidth, alignment: .leading) Image(uiImage: uiImage) @@ -197,7 +202,7 @@ struct FramedItemView: View { ciQuotedMsgView(qi) } case let .video(_, image, _): - if let uiImage = UIImage(base64Encoded: image) { + if let uiImage = imageFromBase64(image) { ciQuotedMsgView(qi) .padding(.trailing, 70).frame(minWidth: msgWidth, alignment: .leading) Image(uiImage: uiImage) diff --git a/apps/ios/Shared/Views/Chat/ChatItem/FullScreenMediaView.swift b/apps/ios/Shared/Views/Chat/ChatItem/FullScreenMediaView.swift index a80c5412b6..044ee2a26d 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/FullScreenMediaView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/FullScreenMediaView.swift @@ -13,7 +13,7 @@ import AVKit struct FullScreenMediaView: View { @EnvironmentObject var m: ChatModel - @EnvironmentObject var scrollModel: ReverseListScrollModel + @EnvironmentObject var scrollModel: ReverseListScrollModel @State var chatItem: ChatItem @State var image: UIImage? @State var player: AVPlayer? = nil diff --git a/apps/ios/Shared/Views/Chat/ChatItem/IntegrityErrorItemView.swift b/apps/ios/Shared/Views/Chat/ChatItem/IntegrityErrorItemView.swift index 822dda4d06..afeb88b05d 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/IntegrityErrorItemView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/IntegrityErrorItemView.swift @@ -69,7 +69,7 @@ struct CIMsgError: View { } .padding(.leading, 12) .padding(.vertical, 6) - .background(Color(uiColor: .tertiarySystemGroupedBackground)) + .background { chatItemFrameColor(chatItem, theme).modifier(ChatTailPadding()) } .textSelection(.disabled) .onTapGesture(perform: onTap) } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/MarkedDeletedItemView.swift b/apps/ios/Shared/Views/Chat/ChatItem/MarkedDeletedItemView.swift index 25e06b9ea4..afd817357c 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/MarkedDeletedItemView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/MarkedDeletedItemView.swift @@ -22,7 +22,7 @@ struct MarkedDeletedItemView: View { .foregroundColor(theme.colors.secondary) .padding(.horizontal, 12) .padding(.vertical, 6) - .background(chatItemFrameColor(chatItem, theme)) + .background { chatItemFrameColor(chatItem, theme).modifier(ChatTailPadding()) } .textSelection(.disabled) } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/MsgContentView.swift b/apps/ios/Shared/Views/Chat/ChatItem/MsgContentView.swift index 999f99b294..63d5dc30dc 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/MsgContentView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/MsgContentView.swift @@ -26,6 +26,7 @@ private func typing(_ w: Font.Weight = .light) -> Text { struct MsgContentView: View { @ObservedObject var chat: Chat + @Environment(\.showTimestamp) var showTimestamp: Bool @EnvironmentObject var theme: AppTheme var text: String var formattedText: [FormattedText]? = nil @@ -84,7 +85,7 @@ struct MsgContentView: View { } private func reserveSpaceForMeta(_ mt: CIMeta) -> Text { - (rightToLeft ? Text("\n") : Text(" ")) + ciMetaText(mt, chatTTL: chat.chatInfo.timedMessagesTTL, encrypted: nil, transparent: true, showViaProxy: showSentViaProxy) + (rightToLeft ? Text("\n") : Text(" ")) + ciMetaText(mt, chatTTL: chat.chatInfo.timedMessagesTTL, encrypted: nil, colorMode: .transparent, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp) } } diff --git a/apps/ios/Shared/Views/Chat/ChatItemForwardingView.swift b/apps/ios/Shared/Views/Chat/ChatItemForwardingView.swift index 32993d1a76..587957cd5d 100644 --- a/apps/ios/Shared/Views/Chat/ChatItemForwardingView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItemForwardingView.swift @@ -14,12 +14,11 @@ struct ChatItemForwardingView: View { @EnvironmentObject var theme: AppTheme @Environment(\.dismiss) var dismiss - var ci: ChatItem + var chatItems: [ChatItem] var fromChatInfo: ChatInfo @Binding var composeState: ComposeState @State private var searchText: String = "" - @FocusState private var searchFocused @State private var alert: SomeAlert? private let chatsToForwardTo = filterChatsToForwardTo(chats: ChatModel.shared.chats) @@ -46,8 +45,6 @@ struct ChatItemForwardingView: View { VStack(alignment: .leading) { if !chatsToForwardTo.isEmpty { List { - searchFieldView(text: $searchText, focussed: $searchFocused, theme.colors.onBackground, theme.colors.secondary) - .padding(.leading, 2) let s = searchText.trimmingCharacters(in: .whitespaces).localizedLowercase let chats = s == "" ? chatsToForwardTo : chatsToForwardTo.filter { foundChat($0, s) } ForEach(chats) { chat in @@ -55,6 +52,7 @@ struct ChatItemForwardingView: View { .disabled(chatModel.deletedChats.contains(chat.chatInfo.id)) } } + .searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always)) .modifier(ThemedBackground(grouped: true)) } else { ZStack { @@ -73,11 +71,14 @@ struct ChatItemForwardingView: View { } @ViewBuilder private func forwardListChatView(_ chat: Chat) -> some View { - let prohibited = chat.prohibitedByPref( - hasSimplexLink: hasSimplexLink(ci.content.msgContent?.text), - isMediaOrFileAttachment: ci.content.msgContent?.isMediaOrFileAttachment ?? false, - isVoice: ci.content.msgContent?.isVoice ?? false - ) + let prohibited = chatItems.map { ci in + chat.prohibitedByPref( + hasSimplexLink: hasSimplexLink(ci.content.msgContent?.text), + isMediaOrFileAttachment: ci.content.msgContent?.isMediaOrFileAttachment ?? false, + isVoice: ci.content.msgContent?.isVoice ?? false + ) + }.contains(true) + Button { if prohibited { alert = SomeAlert( @@ -93,10 +94,10 @@ struct ChatItemForwardingView: View { composeState = ComposeState( message: composeState.message, preview: composeState.linkPreview != nil ? composeState.preview : .noPreview, - contextItem: .forwardingItem(chatItem: ci, fromChatInfo: fromChatInfo) + contextItem: .forwardingItems(chatItems: chatItems, fromChatInfo: fromChatInfo) ) } else { - composeState = ComposeState.init(forwardingItem: ci, fromChatInfo: fromChatInfo) + composeState = ComposeState.init(forwardingItems: chatItems, fromChatInfo: fromChatInfo) ItemsModel.shared.loadOpenChat(chat.id) } } @@ -123,7 +124,7 @@ struct ChatItemForwardingView: View { #Preview { ChatItemForwardingView( - ci: ChatItem.getSample(1, .directSnd, .now, "hello"), + chatItems: [ChatItem.getSample(1, .directSnd, .now, "hello")], fromChatInfo: .direct(contact: Contact.sampleData), composeState: Binding.constant(ComposeState(message: "hello")) ).environmentObject(CurrentColors.toAppTheme()) diff --git a/apps/ios/Shared/Views/Chat/ChatItemInfoView.swift b/apps/ios/Shared/Views/Chat/ChatItemInfoView.swift index f6a856dad1..62ea607d27 100644 --- a/apps/ios/Shared/Views/Chat/ChatItemInfoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItemInfoView.swift @@ -450,20 +450,8 @@ struct ChatItemInfoView: View { .foregroundColor(theme.colors.secondary).opacity(0.67) } let v = Group { - let (icon, statusColor) = status.statusIcon(theme.colors.secondary, theme.colors.primary) - switch status { - case .rcvd: - ZStack(alignment: .trailing) { - Image(systemName: icon) - .foregroundColor(statusColor.opacity(0.67)) - .padding(.trailing, 6) - Image(systemName: icon) - .foregroundColor(statusColor.opacity(0.67)) - } - default: - Image(systemName: icon) - .foregroundColor(statusColor) - } + let (image, statusColor) = status.statusIcon(theme.colors.secondary, theme.colors.primary) + image.foregroundColor(statusColor) } if let (title, text) = status.statusInfo { diff --git a/apps/ios/Shared/Views/Chat/ChatItemView.swift b/apps/ios/Shared/Views/Chat/ChatItemView.swift index 870fe30108..bf09d15ff1 100644 --- a/apps/ios/Shared/Views/Chat/ChatItemView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItemView.swift @@ -9,9 +9,21 @@ import SwiftUI import SimpleXChat +extension EnvironmentValues { + struct ShowTimestamp: EnvironmentKey { + static let defaultValue: Bool = true + } + + var showTimestamp: Bool { + get { self[ShowTimestamp.self] } + set { self[ShowTimestamp.self] = newValue } + } +} + struct ChatItemView: View { @ObservedObject var chat: Chat @EnvironmentObject var theme: AppTheme + @Environment(\.showTimestamp) var showTimestamp: Bool var chatItem: ChatItem var maxWidth: CGFloat = .infinity @Binding var revealed: Bool @@ -60,7 +72,7 @@ struct ChatItemView: View { default: nil } } - .flatMap { UIImage(base64Encoded: $0) } + .flatMap { imageFromBase64($0) } let adjustedMaxWidth = { if let preview, preview.size.width <= preview.size.height { maxWidth * 0.75 diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index d65fbc1ed6..b3943eb066 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -22,8 +22,7 @@ struct ChatView: View { @Environment(\.presentationMode) var presentationMode @Environment(\.scenePhase) var scenePhase @State @ObservedObject var chat: Chat - @StateObject private var scrollModel = ReverseListScrollModel() - @StateObject private var floatingButtonModel = FloatingButtonModel() + @StateObject private var scrollModel = ReverseListScrollModel() @State private var showChatInfoSheet: Bool = false @State private var showAddMembersSheet: Bool = false @State private var composeState = ComposeState() @@ -43,6 +42,7 @@ struct ChatView: View { @State private var showGroupLinkSheet: Bool = false @State private var groupLink: String? @State private var groupLinkMemberRole: GroupMemberRole = .member + @State private var forwardedChatItems: [ChatItem] = [] @State private var selectedChatItems: Set? = nil @State private var showDeleteSelectedMessages: Bool = false @State private var allowToDeleteSelectedMessagesForAll: Bool = false @@ -76,7 +76,7 @@ struct ChatView: View { VStack(spacing: 0) { ZStack(alignment: .bottomTrailing) { chatItemsList() - floatingButtons(counts: floatingButtonModel.unreadChatItemCounts) + FloatingButtons(theme: theme, scrollModel: scrollModel, chat: chat) } connectingText() if selectedChatItems == nil { @@ -99,7 +99,8 @@ struct ChatView: View { if case let .group(groupInfo) = chat.chatInfo { showModerateSelectedMessagesAlert(groupInfo) } - } + }, + forwardItems: forwardSelectedMessages ) } } @@ -136,6 +137,30 @@ struct ChatView: View { } } } + // it should be presented on top level in order to prevent a bug in SwiftUI on iOS 16 related to .focused() modifier in AddGroupMembersView's search field + .appSheet(isPresented: $showAddMembersSheet) { + Group { + if case let .group(groupInfo) = cInfo { + AddGroupMembersView(chat: chat, groupInfo: groupInfo) + } + } + } + .sheet(isPresented: Binding( + get: { !forwardedChatItems.isEmpty }, + set: { isPresented in + if !isPresented { + forwardedChatItems = [] + selectedChatItems = nil + } + } + )) { + if #available(iOS 16.0, *) { + ChatItemForwardingView(chatItems: forwardedChatItems, fromChatInfo: chat.chatInfo, composeState: $composeState) + .presentationDetents([.fraction(0.8)]) + } else { + ChatItemForwardingView(chatItems: forwardedChatItems, fromChatInfo: chat.chatInfo, composeState: $composeState) + } + } .onAppear { selectedChatItems = nil initChatView() @@ -287,9 +312,6 @@ struct ChatView: View { } } else { addMembersButton() - .appSheet(isPresented: $showAddMembersSheet) { - AddGroupMembersView(chat: chat, groupInfo: groupInfo) - } } } Menu { @@ -340,6 +362,7 @@ struct ChatView: View { await markChatUnread(chat, unreadChat: false) } } + ChatView.FloatingButtonModel.shared.totalUnread = chat.chatStats.unreadCount } private func searchToolbar() -> some View { @@ -411,14 +434,9 @@ struct ChatView: View { composeState: $composeState, selectedMember: $selectedMember, revealedChatItem: $revealedChatItem, - selectedChatItems: $selectedChatItems + selectedChatItems: $selectedChatItems, + forwardedChatItems: $forwardedChatItems ) - .onAppear { - floatingButtonModel.appeared(viewId: ci.viewId) - } - .onDisappear { - floatingButtonModel.disappeared(viewId: ci.viewId) - } .id(ci.id) // Required to trigger `onAppear` on iOS15 } loadPage: { loadChatItems(cInfo) @@ -429,13 +447,10 @@ struct ChatView: View { .onChange(of: searchText) { _ in Task { await loadChat(chat: chat, search: searchText) } } - .onChange(of: im.reversedChatItems) { _ in - floatingButtonModel.chatItemsChanged() - } .onChange(of: im.itemAdded) { added in if added { im.itemAdded = false - if floatingButtonModel.unreadChatItemCounts.isReallyNearBottom { + if FloatingButtonModel.shared.isReallyNearBottom { scrollModel.scrollToBottom() } } @@ -458,103 +473,169 @@ struct ChatView: View { } class FloatingButtonModel: ObservableObject { - private enum Event { - case appeared(String) - case disappeared(String) - case chatItemsChanged - } + static let shared = FloatingButtonModel() + @Published var unreadBelow: Int = 0 + @Published var isNearBottom: Bool = true + @Published var date: Date? + @Published var isDateVisible: Bool = false + var totalUnread: Int = 0 + var isReallyNearBottom: Bool = true + var hideDateWorkItem: DispatchWorkItem? - @Published var unreadChatItemCounts: UnreadChatItemCounts - - private let events = PassthroughSubject() - private var bag = Set() - - init() { - unreadChatItemCounts = UnreadChatItemCounts( - isNearBottom: true, - isReallyNearBottom: true, - unreadBelow: 0 - ) - events - .receive(on: DispatchQueue.global(qos: .background)) - .scan(Set()) { itemsInView, event in - var updated = itemsInView - switch event { - case let .appeared(viewId): updated.insert(viewId) - case let .disappeared(viewId): updated.remove(viewId) - case .chatItemsChanged: () - } - return updated + func updateOnListChange(_ listState: ListState) { + let im = ItemsModel.shared + let unreadBelow = + if let id = listState.bottomItemId, + let index = im.reversedChatItems.firstIndex(where: { $0.id == id }) + { + im.reversedChatItems[.. 0 && listState.scrollOffset < 500 + } + + // set floating button indication mode + let nearBottom = listState.scrollOffset < 800 + if nearBottom != self.isNearBottom { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) { [weak self] in + self?.isNearBottom = nearBottom + } + } + + // hide Date indicator after 1 second of no scrolling + hideDateWorkItem?.cancel() + let workItem = DispatchWorkItem { [weak self] in + guard let it = self else { return } + it.setDate(visibility: false) + it.hideDateWorkItem = nil + } + DispatchQueue.main.async { [weak self] in + self?.hideDateWorkItem = workItem + DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: workItem) + } } - func appeared(viewId: String) { - events.send(.appeared(viewId)) + func resetDate() { + date = nil + isDateVisible = false } - func disappeared(viewId: String) { - events.send(.disappeared(viewId)) + private func setDate(visibility isVisible: Bool) { + if isVisible { + if !isNearBottom, + !isDateVisible, + let date, !Calendar.current.isDateInToday(date) { + withAnimation { self.isDateVisible = true } + } + } else if isDateVisible { + withAnimation { self.isDateVisible = false } + } } - func chatItemsChanged() { - events.send(.chatItemsChanged) - } } - private func floatingButtons(counts: UnreadChatItemCounts) -> some View { - VStack { - let unreadAbove = chat.chatStats.unreadCount - counts.unreadBelow - if unreadAbove > 0 { - circleButton { - unreadCountText(unreadAbove) - .font(.callout) - .foregroundColor(theme.colors.primary) + private struct FloatingButtons: View { + let theme: AppTheme + let scrollModel: ReverseListScrollModel + let chat: Chat + @ObservedObject var model = FloatingButtonModel.shared + + var body: some View { + ZStack(alignment: .top) { + if let date = model.date { + DateSeparator(date: date) + .padding(.vertical, 4).padding(.horizontal, 8) + .background(.thinMaterial) + .clipShape(Capsule()) + .opacity(model.isDateVisible ? 1 : 0) } - .onTapGesture { - scrollModel.scrollToNextPage() - } - .contextMenu { - Button { - Task { - await markChatRead(chat) + VStack { + let unreadAbove = model.totalUnread - model.unreadBelow + if unreadAbove > 0 { + circleButton { + unreadCountText(unreadAbove) + .font(.callout) + .foregroundColor(theme.colors.primary) } - } label: { - Label("Mark read", systemImage: "checkmark") + .onTapGesture { + scrollModel.scrollToNextPage() + } + .contextMenu { + Button { + Task { + await markChatRead(chat) + } + } label: { + Label("Mark read", systemImage: "checkmark") + } + } + } + Spacer() + if model.unreadBelow > 0 { + circleButton { + unreadCountText(model.unreadBelow) + .font(.callout) + .foregroundColor(theme.colors.primary) + } + .onTapGesture { + scrollModel.scrollToBottom() + } + } else if !model.isNearBottom { + circleButton { + Image(systemName: "chevron.down") + .foregroundColor(theme.colors.primary) + } + .onTapGesture { scrollModel.scrollToBottom() } } } + .padding() + .frame(maxWidth: .infinity, alignment: .trailing) } - Spacer() - if counts.unreadBelow > 0 { - circleButton { - unreadCountText(counts.unreadBelow) - .font(.callout) - .foregroundColor(theme.colors.primary) - } - .onTapGesture { - scrollModel.scrollToBottom() - } - } else if !counts.isNearBottom { - circleButton { - Image(systemName: "chevron.down") - .foregroundColor(theme.colors.primary) - } - .onTapGesture { scrollModel.scrollToBottom() } + .onDisappear(perform: model.resetDate) + } + + private func circleButton(_ content: @escaping () -> Content) -> some View { + ZStack { + Circle() + .foregroundColor(Color(uiColor: .tertiarySystemGroupedBackground)) + .frame(width: 44, height: 44) + content() } } - .padding() } - private func circleButton(_ content: @escaping () -> Content) -> some View { - ZStack { - Circle() - .foregroundColor(Color(uiColor: .tertiarySystemGroupedBackground)) - .frame(width: 44, height: 44) - content() + private struct DateSeparator: View { + let date: Date + + var body: some View { + Text(String.localizedStringWithFormat( + NSLocalizedString("%@, %@", comment: "format for date separator in chat"), + date.formatted(.dateTime.weekday(.abbreviated)), + date.formatted( + Calendar.current.isDate(date, equalTo: .now, toGranularity: .year) + ? .dateTime.day().month(.abbreviated) + : .dateTime.day().month(.abbreviated).year() + ) + )) + .font(.callout) + .fontWeight(.medium) + .foregroundStyle(.secondary) } } @@ -648,6 +729,116 @@ struct ChatView: View { } } + private func forwardSelectedMessages() { + Task { + do { + if let selectedChatItems { + let (validItems, confirmation) = try await apiPlanForwardChatItems( + type: chat.chatInfo.chatType, + id: chat.chatInfo.apiId, + itemIds: Array(selectedChatItems) + ) + if let confirmation { + if validItems.count > 0 { + showAlert( + String.localizedStringWithFormat( + NSLocalizedString("Forward %d message(s)?", comment: "alert title"), + validItems.count + ), + message: forwardConfirmationText(confirmation) + "\n" + + NSLocalizedString("Forward messages without files?", comment: "alert message") + ) { + switch confirmation { + case let .filesNotAccepted(fileIds): + [forwardAction(validItems), downloadAction(fileIds), cancelAlertAction] + default: + [forwardAction(validItems), cancelAlertAction] + } + } + } else { + showAlert( + NSLocalizedString("Nothing to forward!", comment: "alert title"), + message: forwardConfirmationText(confirmation) + ) { + switch confirmation { + case let .filesNotAccepted(fileIds): + [downloadAction(fileIds), cancelAlertAction] + default: + [okAlertAction] + } + } + } + } else { + await openForwardingSheet(validItems) + } + } + } catch { + logger.error("Plan forward chat items failed: \(error.localizedDescription)") + } + } + + func forwardConfirmationText(_ fc: ForwardConfirmation) -> String { + switch fc { + case let .filesNotAccepted(fileIds): + String.localizedStringWithFormat( + NSLocalizedString("%d file(s) were not downloaded.", comment: "forward confirmation reason"), + fileIds.count + ) + case let .filesInProgress(filesCount): + String.localizedStringWithFormat( + NSLocalizedString("%d file(s) are still being downloaded.", comment: "forward confirmation reason"), + filesCount + ) + case let .filesMissing(filesCount): + String.localizedStringWithFormat( + NSLocalizedString("%d file(s) were deleted.", comment: "forward confirmation reason"), + filesCount + ) + case let .filesFailed(filesCount): + String.localizedStringWithFormat( + NSLocalizedString("%d file(s) failed to download.", comment: "forward confirmation reason"), + filesCount + ) + } + } + + func forwardAction(_ items: [Int64]) -> UIAlertAction { + UIAlertAction( + title: NSLocalizedString("Forward messages", comment: "alert action"), + style: .default, + handler: { _ in Task { await openForwardingSheet(items) } } + ) + } + + func downloadAction(_ fileIds: [Int64]) -> UIAlertAction { + UIAlertAction( + title: NSLocalizedString("Download files", comment: "alert action"), + style: .default, + handler: { _ in + Task { + if let user = ChatModel.shared.currentUser { + await receiveFiles(user: user, fileIds: fileIds) + } + } + } + ) + } + + func openForwardingSheet(_ items: [Int64]) async { + let im = ItemsModel.shared + var items = Set(items) + var fci = [ChatItem]() + for reversedChatItem in im.reversedChatItems { + if items.contains(reversedChatItem.id) { + items.remove(reversedChatItem.id) + fci.insert(reversedChatItem, at: 0) + } + if items.isEmpty { break } + } + await MainActor.run { forwardedChatItems = fci } + } + } + private func loadChatItems(_ cInfo: ChatInfo) { Task { if loadingItems || firstPage { return } @@ -696,6 +887,7 @@ struct ChatView: View { @EnvironmentObject var m: ChatModel @EnvironmentObject var theme: AppTheme @Binding @ObservedObject var chat: Chat + @ObservedObject var dummyModel: ChatItemDummyModel = .shared let chatItem: ChatItem let maxWidth: CGFloat @Binding var composeState: ComposeState @@ -708,39 +900,66 @@ struct ChatView: View { @State private var showDeleteMessages = false @State private var showChatItemInfoSheet: Bool = false @State private var chatItemInfo: ChatItemInfo? - @State private var showForwardingSheet: Bool = false - + @State private var msgWidth: CGFloat = 0 + @Binding var selectedChatItems: Set? + @Binding var forwardedChatItems: [ChatItem] @State private var allowMenu: Bool = true + @State private var markedRead = false var revealed: Bool { chatItem == revealedChatItem } + typealias ItemSeparation = (timestamp: Bool, largeGap: Bool, date: Date?) + + func getItemSeparation(_ chatItem: ChatItem, at i: Int?) -> ItemSeparation { + let im = ItemsModel.shared + if let i, i > 0 && im.reversedChatItems.count >= i { + let nextItem = im.reversedChatItems[i - 1] + let largeGap = !nextItem.chatDir.sameDirection(chatItem.chatDir) || nextItem.meta.itemTs.timeIntervalSince(chatItem.meta.itemTs) > 60 + return ( + timestamp: largeGap || formatTimestampMeta(chatItem.meta.itemTs) != formatTimestampMeta(nextItem.meta.itemTs), + largeGap: largeGap, + date: Calendar.current.isDate(chatItem.meta.itemTs, inSameDayAs: nextItem.meta.itemTs) ? nil : nextItem.meta.itemTs + ) + } else { + return (timestamp: true, largeGap: true, date: nil) + } + } + var body: some View { - let (currIndex, _) = m.getNextChatItem(chatItem) + let currIndex = m.getChatItemIndex(chatItem) let ciCategory = chatItem.mergeCategory let (prevHidden, prevItem) = m.getPrevShownChatItem(currIndex, ciCategory) let range = itemsRange(currIndex, prevHidden) + let timeSeparation = getItemSeparation(chatItem, at: currIndex) let im = ItemsModel.shared Group { if revealed, let range = range { let items = Array(zip(Array(range), im.reversedChatItems[range])) - ForEach(items.reversed(), id: \.1.viewId) { (i, ci) in - let prev = i == prevHidden ? prevItem : im.reversedChatItems[i + 1] - chatItemView(ci, nil, prev) - .overlay { - if let selected = selectedChatItems, ci.canBeDeletedForSelf { - Color.clear - .contentShape(Rectangle()) - .onTapGesture { - let checked = selected.contains(ci.id) - selectUnselectChatItem(select: !checked, ci) + VStack(spacing: 0) { + ForEach(items.reversed(), id: \.1.viewId) { (i: Int, ci: ChatItem) in + let prev = i == prevHidden ? prevItem : im.reversedChatItems[i + 1] + chatItemView(ci, nil, prev, getItemSeparation(ci, at: i)) + .overlay { + if let selected = selectedChatItems, ci.canBeDeletedForSelf { + Color.clear + .contentShape(Rectangle()) + .onTapGesture { + let checked = selected.contains(ci.id) + selectUnselectChatItem(select: !checked, ci) + } } - } + } } } } else { - chatItemView(chatItem, range, prevItem) + VStack(spacing: 0) { + chatItemView(chatItem, range, prevItem, timeSeparation) + if let date = timeSeparation.date { + DateSeparator(date: date).padding(8) + } + } .overlay { if let selected = selectedChatItems, chatItem.canBeDeletedForSelf { Color.clear @@ -754,12 +973,16 @@ struct ChatView: View { } } .onAppear { + if markedRead { + return + } else { + markedRead = true + } if let range { - if let items = unreadItems(range) { + let itemIds = unreadItemIds(range) + if !itemIds.isEmpty { waitToMarkRead { - for ci in items { - await apiMarkChatItemRead(chat.chatInfo, ci) - } + await apiMarkChatItemsRead(chat.chatInfo, itemIds) } } } else if chatItem.isRcvNew { @@ -769,29 +992,75 @@ struct ChatView: View { } } } - - private func unreadItems(_ range: ClosedRange) -> [ChatItem]? { + + private func unreadItemIds(_ range: ClosedRange) -> [ChatItem.ID] { let im = ItemsModel.shared - let items = range.compactMap { i in + return range.compactMap { i in if i >= 0 && i < im.reversedChatItems.count { let ci = im.reversedChatItems[i] - return if ci.isRcvNew { ci } else { nil } + return if ci.isRcvNew { ci.id } 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) { + Task { + _ = try? await Task.sleep(nanoseconds: 600_000000) if m.chatId == chat.chatInfo.id { - Task(operation: op) + await op() } } } + - @ViewBuilder func chatItemView(_ ci: ChatItem, _ range: ClosedRange?, _ prevItem: ChatItem?) -> some View { + @available(iOS 16.0, *) + struct MemberLayout: Layout { + let spacing: Double + let msgWidth: Double + + private func sizes(subviews: Subviews, proposal: ProposedViewSize) -> (CGSize, CGSize) { + assert(subviews.count == 2, "member layout must contain exactly two subviews") + let roleSize = subviews[1].sizeThatFits(proposal) + let memberSize = subviews[0].sizeThatFits( + ProposedViewSize( + width: (proposal.width ?? msgWidth) - roleSize.width, + height: proposal.height + ) + ) + return (memberSize, roleSize) + } + + func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout Void) -> CGSize { + let (memberSize, roleSize) = sizes(subviews: subviews, proposal: proposal) + return CGSize( + width: min( + proposal.width ?? msgWidth, + max(msgWidth, roleSize.width + spacing + memberSize.width) + ), + height: max(memberSize.height, roleSize.height) + ) + } + + func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout Void) { + let (memberSize, roleSize) = sizes(subviews: subviews, proposal: proposal) + subviews[0].place( + at: CGPoint(x: bounds.minX, y: bounds.midY - memberSize.height / 2), + proposal: ProposedViewSize(memberSize) + ) + subviews[1].place( + at: CGPoint( + x: bounds.minX + max(memberSize.width + spacing, msgWidth - roleSize.width), + y: bounds.midY - roleSize.height / 2 + ), + proposal: ProposedViewSize(roleSize) + ) + } + } + + @ViewBuilder func chatItemView(_ ci: ChatItem, _ range: ClosedRange?, _ prevItem: ChatItem?, _ itemSeparation: ItemSeparation) -> some View { + let bottomPadding: Double = itemSeparation.largeGap ? 10 : 2 if case let .groupRcv(member) = ci.chatDir, case let .group(groupInfo) = chat.chatInfo { let (prevMember, memCount): (GroupMember?, Int) = @@ -803,24 +1072,49 @@ struct ChatView: View { if prevItem == nil || showMemberImage(member, prevItem) || prevMember != nil { VStack(alignment: .leading, spacing: 4) { if ci.content.showMemberName { - let t = if memCount == 1 && member.memberRole > .member { - Text(member.memberRole.text + " ").fontWeight(.semibold) + Text(member.displayName) - } else { - Text(memberNames(member, prevMember, memCount)) + Group { + if memCount == 1 && member.memberRole > .member { + Group { + if #available(iOS 16.0, *) { + MemberLayout(spacing: 16, msgWidth: msgWidth) { + Text(member.chatViewName) + .lineLimit(1) + Text(member.memberRole.text) + .fontWeight(.semibold) + .lineLimit(1) + .padding(.trailing, 8) + } + } else { + HStack(spacing: 16) { + Text(member.chatViewName) + .lineLimit(1) + Text(member.memberRole.text) + .fontWeight(.semibold) + .lineLimit(1) + .layoutPriority(1) + } + } + } + .frame( + maxWidth: maxWidth, + alignment: chatItem.chatDir.sent ? .trailing : .leading + ) + } else { + Text(memberNames(member, prevMember, memCount)) + .lineLimit(2) + } } - t .font(.caption) .foregroundStyle(.secondary) - .lineLimit(2) .padding(.leading, memberImageSize + 14 + (selectedChatItems != nil && ci.canBeDeletedForSelf ? 12 + 24 : 0)) - .padding(.top, 7) + .padding(.top, 3) // this is in addition to message sequence gap } HStack(alignment: .center, spacing: 0) { if selectedChatItems != nil && ci.canBeDeletedForSelf { SelectedChatItem(ciId: ci.id, selectedChatItems: $selectedChatItems) .padding(.trailing, 12) } - HStack(alignment: .top, spacing: 8) { + HStack(alignment: .top, spacing: 10) { MemberProfileImage(member, size: memberImageSize, backgroundColor: theme.colors.background) .onTapGesture { if let member = m.getGroupMember(member.groupMemberId) { @@ -833,11 +1127,12 @@ struct ChatView: View { } } } - chatItemWithMenu(ci, range, maxWidth) + chatItemWithMenu(ci, range, maxWidth, itemSeparation) + .onPreferenceChange(DetermineWidth.Key.self) { msgWidth = $0 } } } } - .padding(.bottom, 5) + .padding(.bottom, bottomPadding) .padding(.trailing) .padding(.leading, 12) } else { @@ -846,11 +1141,11 @@ struct ChatView: View { SelectedChatItem(ciId: ci.id, selectedChatItems: $selectedChatItems) .padding(.leading, 12) } - chatItemWithMenu(ci, range, maxWidth) + chatItemWithMenu(ci, range, maxWidth, itemSeparation) .padding(.trailing) - .padding(.leading, memberImageSize + 8 + 12) + .padding(.leading, 10 + memberImageSize + 12) } - .padding(.bottom, 5) + .padding(.bottom, bottomPadding) } } else { HStack(alignment: .center, spacing: 0) { @@ -863,10 +1158,10 @@ struct ChatView: View { .padding(.leading) } } - chatItemWithMenu(ci, range, maxWidth) + chatItemWithMenu(ci, range, maxWidth, itemSeparation) .padding(.horizontal) } - .padding(.bottom, 5) + .padding(.bottom, bottomPadding) } } @@ -881,7 +1176,7 @@ struct ChatView: View { } } - @ViewBuilder func chatItemWithMenu(_ ci: ChatItem, _ range: ClosedRange?, _ maxWidth: CGFloat) -> some View { + @ViewBuilder func chatItemWithMenu(_ ci: ChatItem, _ range: ClosedRange?, _ maxWidth: CGFloat, _ itemSeparation: ItemSeparation) -> some View { let alignment: Alignment = ci.chatDir.sent ? .trailing : .leading VStack(alignment: alignment.horizontal, spacing: 3) { ChatItemView( @@ -891,7 +1186,8 @@ struct ChatView: View { revealed: .constant(revealed), allowMenu: $allowMenu ) - .modifier(ChatItemClipped(ci)) + .environment(\.showTimestamp, itemSeparation.timestamp) + .modifier(ChatItemClipped(ci, tailVisible: itemSeparation.largeGap && (ci.meta.itemDeleted == nil || revealed))) .contextMenu { menu(ci, range, live: composeState.liveMessage != nil) } .accessibilityLabel("") if ci.content.msgContent != nil && (ci.meta.itemDeleted == nil || revealed) && ci.reactions.count > 0 { @@ -921,14 +1217,6 @@ struct ChatView: View { }) { ChatItemInfoView(ci: ci, chatItemInfo: $chatItemInfo) } - .sheet(isPresented: $showForwardingSheet) { - if #available(iOS 16.0, *) { - ChatItemForwardingView(ci: ci, fromChatInfo: chat.chatInfo, composeState: $composeState) - .presentationDetents([.fraction(0.8)]) - } else { - ChatItemForwardingView(ci: ci, fromChatInfo: chat.chatInfo, composeState: $composeState) - } - } } private func showMemberImage(_ member: GroupMember, _ prevItem: ChatItem?) -> Bool { @@ -1069,7 +1357,7 @@ struct ChatView: View { var forwardButton: Button { Button { - showForwardingSheet = true + forwardedChatItems = [chatItem] } label: { Label( NSLocalizedString("Forward", comment: "chat item action"), diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeImageView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeImageView.swift index df3a8caf55..14026d79d1 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeImageView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeImageView.swift @@ -18,7 +18,7 @@ struct ComposeImageView: View { var body: some View { HStack(alignment: .center, spacing: 8) { let imgs: [UIImage] = images.compactMap { image in - UIImage(base64Encoded: image) + imageFromBase64(image) } if imgs.count == 0 { ProgressView() diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeLinkView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeLinkView.swift index f7f1a89299..6c44aeea83 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeLinkView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeLinkView.swift @@ -40,7 +40,7 @@ struct ComposeLinkView: View { private func linkPreviewView(_ linkPreview: LinkPreview) -> some View { HStack(alignment: .center, spacing: 8) { - if let uiImage = UIImage(base64Encoded: linkPreview.image) { + if let uiImage = imageFromBase64(linkPreview.image) { Image(uiImage: uiImage) .resizable() .aspectRatio(contentMode: .fit) diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift index 78cae78cf5..971d088fe2 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ComposeView.swift @@ -23,7 +23,7 @@ enum ComposeContextItem { case noContextItem case quotedItem(chatItem: ChatItem) case editingItem(chatItem: ChatItem) - case forwardingItem(chatItem: ChatItem, fromChatInfo: ChatInfo) + case forwardingItems(chatItems: [ChatItem], fromChatInfo: ChatInfo) } enum VoiceMessageRecordingState { @@ -73,10 +73,10 @@ struct ComposeState { } } - init(forwardingItem: ChatItem, fromChatInfo: ChatInfo) { + init(forwardingItems: [ChatItem], fromChatInfo: ChatInfo) { self.message = "" self.preview = .noPreview - self.contextItem = .forwardingItem(chatItem: forwardingItem, fromChatInfo: fromChatInfo) + self.contextItem = .forwardingItems(chatItems: forwardingItems, fromChatInfo: fromChatInfo) self.voiceMessageRecordingState = .noRecording } @@ -112,7 +112,7 @@ struct ComposeState { var forwarding: Bool { switch contextItem { - case .forwardingItem: return true + case .forwardingItems: return true default: return false } } @@ -167,6 +167,13 @@ struct ComposeState { } } + var manyMediaPreviews: Bool { + switch preview { + case let .mediaPreviews(mediaPreviews): return mediaPreviews.count > 1 + default: return false + } + } + var attachmentDisabled: Bool { if editing || forwarding || liveMessage != nil || inProgress { return true } switch preview { @@ -452,7 +459,7 @@ struct ComposeView: View { Task { var media: [(String, UploadContent)] = [] for content in selected { - if let img = resizeImageToStrSize(content.uiImage, maxDataSize: 14000) { + if let img = await resizeImageToStrSize(content.uiImage, maxDataSize: 14000) { media.append((img, content)) await MainActor.run { composeState = composeState.copy(preview: .mediaPreviews(mediaPreviews: media)) @@ -544,7 +551,7 @@ struct ComposeView: View { } private func addMediaContent(_ content: UploadContent) async { - if let img = resizeImageToStrSize(content.uiImage, maxDataSize: 14000) { + if let img = await resizeImageToStrSize(content.uiImage, maxDataSize: 14000) { var newMedia: [(String, UploadContent?)] = [] if case var .mediaPreviews(media) = composeState.preview { media.append((img, content)) @@ -687,7 +694,7 @@ struct ComposeView: View { case let .quotedItem(chatItem: quotedItem): ContextItemView( chat: chat, - contextItem: quotedItem, + contextItems: [quotedItem], contextIcon: "arrowshape.turn.up.left", cancelContextItem: { composeState = composeState.copy(contextItem: .noContextItem) } ) @@ -695,18 +702,17 @@ struct ComposeView: View { case let .editingItem(chatItem: editingItem): ContextItemView( chat: chat, - contextItem: editingItem, + contextItems: [editingItem], contextIcon: "pencil", cancelContextItem: { clearState() } ) Divider() - case let .forwardingItem(chatItem: forwardedItem, _): + case let .forwardingItems(chatItems, _): ContextItemView( chat: chat, - contextItem: forwardedItem, + contextItems: chatItems, contextIcon: "arrowshape.turn.up.forward", - cancelContextItem: { composeState = composeState.copy(contextItem: .noContextItem) }, - showSender: false + cancelContextItem: { composeState = composeState.copy(contextItem: .noContextItem) } ) Divider() } @@ -730,10 +736,11 @@ struct ComposeView: View { } if chat.chatInfo.contact?.nextSendGrpInv ?? false { await sendMemberContactInvitation() - } else if case let .forwardingItem(ci, fromChatInfo) = composeState.contextItem { - sent = await forwardItem(ci, fromChatInfo, ttl) + } else if case let .forwardingItems(chatItems, fromChatInfo) = composeState.contextItem { + // Composed text is send as a reply to the last forwarded item + sent = await forwardItems(chatItems, fromChatInfo, ttl).last if !composeState.message.isEmpty { - sent = await send(checkLinkPreview(), quoted: sent?.id, live: false, ttl: ttl) + _ = await send(checkLinkPreview(), quoted: sent?.id, live: false, ttl: ttl) } } else if case let .editingItem(ci) = composeState.contextItem { sent = await updateMessage(ci, live: live) @@ -750,26 +757,28 @@ struct ComposeView: View { sent = await send(.text(msgText), quoted: quoted, live: live, ttl: ttl) case .linkPreview: sent = await send(checkLinkPreview(), quoted: quoted, live: live, ttl: ttl) - case let .mediaPreviews(mediaPreviews: media): + case let .mediaPreviews(media): let last = media.count - 1 + var msgs: [ComposedMessage] = [] if last >= 0 { for i in 0.. 0 { + // Sleep to allow `progressByTimeout` update be rendered + try? await Task.sleep(nanoseconds: 100_000000) + } + if let (fileSource, msgContent) = mediaContent(media[i], text: "") { + msgs.append(ComposedMessage(fileSource: fileSource, msgContent: msgContent)) } - _ = try? await Task.sleep(nanoseconds: 100_000000) } - if case (_, .video(_, _, _)) = media[last] { - sent = await sendVideo(media[last], text: msgText, quoted: quoted, live: live, ttl: ttl) - } else { - sent = await sendImage(media[last], text: msgText, quoted: quoted, live: live, ttl: ttl) + if let (fileSource, msgContent) = mediaContent(media[last], text: msgText) { + msgs.append(ComposedMessage(fileSource: fileSource, quotedItemId: quoted, msgContent: msgContent)) } } - if sent == nil { - sent = await send(.text(msgText), quoted: quoted, live: live, ttl: ttl) + if msgs.isEmpty { + msgs = [ComposedMessage(quotedItemId: quoted, msgContent: .text(msgText))] } + sent = await send(msgs, live: live, ttl: ttl).last + case let .voicePreview(recordingFileName, duration): stopPlayback.toggle() let file = voiceCryptoFile(recordingFileName) @@ -791,6 +800,20 @@ struct ComposeView: View { } return sent + func mediaContent(_ media: (String, UploadContent?), text: String) -> (CryptoFile?, MsgContent)? { + let (previewImage, uploadContent) = media + return switch uploadContent { + case let .simpleImage(image): + (saveImage(image), .image(text: text, image: previewImage)) + case let .animatedImage(image): + (saveAnimImage(image), .image(text: text, image: previewImage)) + case let .video(_, url, duration): + (moveTempFileFromURL(url), .video(text: text, image: previewImage, duration: duration)) + case .none: + nil + } + } + func sending() async { await MainActor.run { composeState.inProgress = true } } @@ -854,23 +877,6 @@ struct ComposeView: View { } } - func sendImage(_ imageData: (String, UploadContent?), text: String = "", quoted: Int64? = nil, live: Bool = false, ttl: Int?) async -> ChatItem? { - let (image, data) = imageData - if let data = data, let savedFile = saveAnyImage(data) { - return await send(.image(text: text, image: image), quoted: quoted, file: savedFile, live: live, ttl: ttl) - } - return nil - } - - func sendVideo(_ imageData: (String, UploadContent?), text: String = "", quoted: Int64? = nil, live: Bool = false, ttl: Int?) async -> ChatItem? { - let (image, data) = imageData - if case let .video(_, url, duration) = data, let savedFile = moveTempFileFromURL(url) { - ChatModel.shared.filesToDelete.remove(url) - return await send(.video(text: text, image: image, duration: duration), quoted: quoted, file: savedFile, live: live, ttl: ttl) - } - return nil - } - func voiceCryptoFile(_ fileName: String) -> CryptoFile? { if !privacyEncryptLocalFilesGroupDefault.get() { return CryptoFile.plain(fileName) @@ -887,44 +893,66 @@ struct ComposeView: View { } func send(_ mc: MsgContent, quoted: Int64?, file: CryptoFile? = nil, live: Bool = false, ttl: Int?) async -> ChatItem? { - if let chatItem = chat.chatInfo.chatType == .local - ? await apiCreateChatItem(noteFolderId: chat.chatInfo.apiId, file: file, msg: mc) - : await apiSendMessage( + await send( + [ComposedMessage(fileSource: file, quotedItemId: quoted, msgContent: mc)], + live: live, + ttl: ttl + ).first + } + + func send(_ msgs: [ComposedMessage], live: Bool, ttl: Int?) async -> [ChatItem] { + if let chatItems = chat.chatInfo.chatType == .local + ? await apiCreateChatItems(noteFolderId: chat.chatInfo.apiId, composedMessages: msgs) + : await apiSendMessages( type: chat.chatInfo.chatType, id: chat.chatInfo.apiId, - file: file, - quotedItemId: quoted, - msg: mc, live: live, - ttl: ttl + ttl: ttl, + composedMessages: msgs ) { await MainActor.run { chatModel.removeLiveDummy(animated: false) - chatModel.addChatItem(chat.chatInfo, chatItem) + for chatItem in chatItems { + chatModel.addChatItem(chat.chatInfo, chatItem) + } } - return chatItem + return chatItems } - if let file = file { - removeFile(file.filePath) + for msg in msgs { + if let file = msg.fileSource { + removeFile(file.filePath) + } } - return nil + return [] } - func forwardItem(_ forwardedItem: ChatItem, _ fromChatInfo: ChatInfo, _ ttl: Int?) async -> ChatItem? { - if let chatItem = await apiForwardChatItem( + func forwardItems(_ forwardedItems: [ChatItem], _ fromChatInfo: ChatInfo, _ ttl: Int?) async -> [ChatItem] { + if let chatItems = await apiForwardChatItems( toChatType: chat.chatInfo.chatType, toChatId: chat.chatInfo.apiId, fromChatType: fromChatInfo.chatType, fromChatId: fromChatInfo.apiId, - itemId: forwardedItem.id, + itemIds: forwardedItems.map { $0.id }, ttl: ttl ) { await MainActor.run { - chatModel.addChatItem(chat.chatInfo, chatItem) + for chatItem in chatItems { + chatModel.addChatItem(chat.chatInfo, chatItem) + } + if forwardedItems.count != chatItems.count { + showAlert( + String.localizedStringWithFormat( + NSLocalizedString("%d messages not forwarded", comment: "alert title"), + forwardedItems.count - chatItems.count + ), + message: NSLocalizedString("Messages were deleted after you selected them.", comment: "alert message") + ) + } } - return chatItem + return chatItems + } else { + return [] } - return nil } func checkLinkPreview() -> MsgContent { @@ -941,14 +969,6 @@ struct ComposeView: View { return .text(msgText) } } - - func saveAnyImage(_ img: UploadContent) -> CryptoFile? { - switch img { - case let .simpleImage(image): return saveImage(image) - case let .animatedImage(image): return saveAnimImage(image) - default: return nil - } - } } private func startVoiceMessageRecording() async { diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ContextItemView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ContextItemView.swift index 6245bbe21f..8b988f5624 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ContextItemView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ContextItemView.swift @@ -12,7 +12,7 @@ import SimpleXChat struct ContextItemView: View { @EnvironmentObject var theme: AppTheme @ObservedObject var chat: Chat - let contextItem: ChatItem + let contextItems: [ChatItem] let contextIcon: String let cancelContextItem: () -> Void var showSender: Bool = true @@ -24,13 +24,22 @@ struct ContextItemView: View { .aspectRatio(contentMode: .fit) .frame(width: 16, height: 16) .foregroundColor(theme.colors.secondary) - if showSender, let sender = contextItem.memberDisplayName { - VStack(alignment: .leading, spacing: 4) { - Text(sender).font(.caption).foregroundColor(theme.colors.secondary) - msgContentView(lines: 2) - } + if let singleItem = contextItems.first, contextItems.count == 1 { + if showSender, let sender = singleItem.memberDisplayName { + VStack(alignment: .leading, spacing: 4) { + Text(sender).font(.caption).foregroundColor(theme.colors.secondary) + msgContentView(lines: 2, contextItem: singleItem) + } + } else { + msgContentView(lines: 3, contextItem: singleItem) + } } else { - msgContentView(lines: 3) + Text( + chat.chatInfo.chatType == .local + ? "Saving \(contextItems.count) messages" + : "Forwarding \(contextItems.count) messages" + ) + .italic() } Spacer() Button { @@ -45,23 +54,32 @@ struct ContextItemView: View { .padding(12) .frame(minHeight: 54) .frame(maxWidth: .infinity) - .background(chatItemFrameColor(contextItem, theme)) + .background(background) } - private func msgContentView(lines: Int) -> some View { - contextMsgPreview() + private var background: Color { + contextItems.first + .map { chatItemFrameColor($0, theme) } + ?? Color(uiColor: .tertiarySystemBackground) + } + + private func msgContentView(lines: Int, contextItem: ChatItem) -> some View { + contextMsgPreview(contextItem) .multilineTextAlignment(isRightToLeft(contextItem.text) ? .trailing : .leading) .lineLimit(lines) } - private func contextMsgPreview() -> Text { + private func contextMsgPreview(_ contextItem: ChatItem) -> Text { return attachment() + messageText(contextItem.text, contextItem.formattedText, nil, preview: true, showSecrets: false, secondaryColor: theme.colors.secondary) func attachment() -> Text { + let isFileLoaded = if let fileSource = getLoadedFileSource(contextItem.file) { + FileManager.default.fileExists(atPath: getAppFilePath(fileSource.filePath).path) + } else { false } switch contextItem.content.msgContent { - case .file: return image("doc.fill") + case .file: return isFileLoaded ? image("doc.fill") : Text("") case .image: return image("photo") - case .voice: return image("play.fill") + case .voice: return isFileLoaded ? image("play.fill") : Text("") default: return Text("") } } @@ -75,6 +93,6 @@ struct ContextItemView: View { struct ContextItemView_Previews: PreviewProvider { static var previews: some View { let contextItem: ChatItem = ChatItem.getSample(1, .directSnd, .now, "hello") - return ContextItemView(chat: Chat.sampleData, contextItem: contextItem, contextIcon: "pencil.circle", cancelContextItem: {}) + return ContextItemView(chat: Chat.sampleData, contextItems: [contextItem], contextIcon: "pencil.circle", cancelContextItem: {}) } } diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift index 9ad6e986bd..50ec8f28c1 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/SendMessageView.swift @@ -227,6 +227,7 @@ struct SendMessageView: View { !composeState.editing { if case .noContextItem = composeState.contextItem, !composeState.voicePreview, + !composeState.manyMediaPreviews, let send = sendLiveMessage, let update = updateLiveMessage { Button { diff --git a/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift b/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift index 189ab95494..859d2dfd27 100644 --- a/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift +++ b/apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift @@ -105,8 +105,10 @@ struct AddGroupMembersViewCommon: View { .padding(.leading, 2) let s = searchText.trimmingCharacters(in: .whitespaces).localizedLowercase let members = s == "" ? membersToAdd : membersToAdd.filter { $0.chatViewName.localizedLowercase.contains(s) } - ForEach(members) { contact in - contactCheckView(contact) + ForEach(members + [dummyContact]) { contact in + if contact.contactId != dummyContact.contactId { + contactCheckView(contact) + } } } } @@ -130,6 +132,14 @@ struct AddGroupMembersViewCommon: View { .modifier(ThemedBackground(grouped: true)) } + // Resolves keyboard losing focus bug in iOS16 and iOS17, + // when there are no items inside `ForEach(memebers)` loop + private let dummyContact: Contact = { + var dummy = Contact.sampleData + dummy.contactId = -1 + return dummy + }() + private func inviteMembersButton() -> some View { Button { inviteMembers() diff --git a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift index ddf3b8e4b9..fd72b5b515 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupMemberInfoView.swift @@ -18,6 +18,7 @@ struct GroupMemberInfoView: View { var navigation: Bool = false @State private var connectionStats: ConnectionStats? = nil @State private var connectionCode: String? = nil + @State private var connectionLoaded: Bool = false @State private var newRole: GroupMemberRole = .member @State private var alert: GroupMemberInfoViewAlert? @State private var sheet: PlanAndConnectActionSheet? @@ -94,129 +95,137 @@ struct GroupMemberInfoView: View { .listRowSeparator(.hidden) .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) - if member.memberActive { - Section { - if let code = connectionCode { verifyCodeButton(code) } - if let connStats = connectionStats, - connStats.ratchetSyncAllowed { - synchronizeConnectionButton() - } - // } else if developerTools { - // synchronizeConnectionButtonForce() - // } - } - } + if connectionLoaded { - if let contactLink = member.contactLink { - Section { - SimpleXLinkQRCode(uri: contactLink) - Button { - showShareSheet(items: [simplexChatLink(contactLink)]) - } label: { - Label("Share address", systemImage: "square.and.arrow.up") + if member.memberActive { + Section { + if let code = connectionCode { verifyCodeButton(code) } + if let connStats = connectionStats, + connStats.ratchetSyncAllowed { + synchronizeConnectionButton() + } + // } else if developerTools { + // synchronizeConnectionButtonForce() + // } } - if let contactId = member.memberContactId { - if knownDirectChat(contactId) == nil && !groupInfo.fullGroupPreferences.directMessages.on(for: groupInfo.membership) { + } + + if let contactLink = member.contactLink { + Section { + SimpleXLinkQRCode(uri: contactLink) + Button { + showShareSheet(items: [simplexChatLink(contactLink)]) + } label: { + Label("Share address", systemImage: "square.and.arrow.up") + } + if let contactId = member.memberContactId { + if knownDirectChat(contactId) == nil && !groupInfo.fullGroupPreferences.directMessages.on(for: groupInfo.membership) { + connectViaAddressButton(contactLink) + } + } else { connectViaAddressButton(contactLink) } - } else { - connectViaAddressButton(contactLink) + } header: { + Text("Address") + .foregroundColor(theme.colors.secondary) + } footer: { + Text("You can share this address with your contacts to let them connect with **\(member.displayName)**.") + .foregroundColor(theme.colors.secondary) } - } header: { - Text("Address") - .foregroundColor(theme.colors.secondary) - } footer: { - Text("You can share this address with your contacts to let them connect with **\(member.displayName)**.") - .foregroundColor(theme.colors.secondary) } - } - Section(header: Text("Member").foregroundColor(theme.colors.secondary)) { - infoRow("Group", groupInfo.displayName) + Section(header: Text("Member").foregroundColor(theme.colors.secondary)) { + infoRow("Group", groupInfo.displayName) - if let roles = member.canChangeRoleTo(groupInfo: groupInfo) { - Picker("Change role", selection: $newRole) { - ForEach(roles) { role in - Text(role.text) + if let roles = member.canChangeRoleTo(groupInfo: groupInfo) { + Picker("Change role", selection: $newRole) { + ForEach(roles) { role in + Text(role.text) + } } + .frame(height: 36) + } else { + infoRow("Role", member.memberRole.text) } - .frame(height: 36) - } else { - infoRow("Role", member.memberRole.text) } - } - if let connStats = connectionStats { - Section(header: Text("Servers").foregroundColor(theme.colors.secondary)) { - // TODO network connection status - Button("Change receiving address") { - alert = .switchAddressAlert - } - .disabled( - connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil } - || connStats.ratchetSyncSendProhibited - ) - if connStats.rcvQueuesInfo.contains(where: { $0.rcvSwitchStatus != nil }) { - Button("Abort changing address") { - alert = .abortSwitchAddressAlert + if let connStats = connectionStats { + Section(header: Text("Servers").foregroundColor(theme.colors.secondary)) { + // TODO network connection status + Button("Change receiving address") { + alert = .switchAddressAlert } .disabled( - connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil && !$0.canAbortSwitch } + connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil } || connStats.ratchetSyncSendProhibited ) + if connStats.rcvQueuesInfo.contains(where: { $0.rcvSwitchStatus != nil }) { + Button("Abort changing address") { + alert = .abortSwitchAddressAlert + } + .disabled( + connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil && !$0.canAbortSwitch } + || connStats.ratchetSyncSendProhibited + ) + } + smpServers("Receiving via", connStats.rcvQueuesInfo.map { $0.rcvServer }, theme.colors.secondary) + smpServers("Sending via", connStats.sndQueuesInfo.map { $0.sndServer }, theme.colors.secondary) } - smpServers("Receiving via", connStats.rcvQueuesInfo.map { $0.rcvServer }, theme.colors.secondary) - smpServers("Sending via", connStats.sndQueuesInfo.map { $0.sndServer }, theme.colors.secondary) } - } - if groupInfo.membership.memberRole >= .admin { - adminDestructiveSection(member) - } else { - nonAdminBlockSection(member) - } + if groupInfo.membership.memberRole >= .admin { + adminDestructiveSection(member) + } else { + nonAdminBlockSection(member) + } - if developerTools { - Section(header: Text("For console").foregroundColor(theme.colors.secondary)) { - infoRow("Local name", member.localDisplayName) - infoRow("Database ID", "\(member.groupMemberId)") - if let conn = member.activeConn { - let connLevelDesc = conn.connLevel == 0 ? NSLocalizedString("direct", comment: "connection level description") : String.localizedStringWithFormat(NSLocalizedString("indirect (%d)", comment: "connection level description"), conn.connLevel) - infoRow("Connection", connLevelDesc) - } - Button ("Debug delivery") { - Task { - do { - let info = queueInfoText(try await apiGroupMemberQueueInfo(groupInfo.apiId, member.groupMemberId)) - await MainActor.run { alert = .queueInfo(info: info) } - } catch let e { - logger.error("apiContactQueueInfo error: \(responseError(e))") - let a = getErrorAlert(e, "Error") - await MainActor.run { alert = .error(title: a.title, error: a.message) } + if developerTools { + Section(header: Text("For console").foregroundColor(theme.colors.secondary)) { + infoRow("Local name", member.localDisplayName) + infoRow("Database ID", "\(member.groupMemberId)") + if let conn = member.activeConn { + let connLevelDesc = conn.connLevel == 0 ? NSLocalizedString("direct", comment: "connection level description") : String.localizedStringWithFormat(NSLocalizedString("indirect (%d)", comment: "connection level description"), conn.connLevel) + infoRow("Connection", connLevelDesc) + } + Button ("Debug delivery") { + Task { + do { + let info = queueInfoText(try await apiGroupMemberQueueInfo(groupInfo.apiId, member.groupMemberId)) + await MainActor.run { alert = .queueInfo(info: info) } + } catch let e { + logger.error("apiContactQueueInfo error: \(responseError(e))") + let a = getErrorAlert(e, "Error") + await MainActor.run { alert = .error(title: a.title, error: a.message) } + } } } } } + } } .navigationBarHidden(true) - .onAppear { + .task { if #unavailable(iOS 16) { // this condition prevents re-setting picker if !justOpened { return } } justOpened = false - DispatchQueue.main.async { - newRole = member.memberRole - do { - let (_, stats) = try apiGroupMemberInfo(groupInfo.apiId, member.groupMemberId) - let (mem, code) = member.memberActive ? try apiGetGroupMemberCode(groupInfo.apiId, member.groupMemberId) : (member, nil) + newRole = member.memberRole + do { + let (_, stats) = try await apiGroupMemberInfo(groupInfo.apiId, member.groupMemberId) + let (mem, code) = member.memberActive ? try await apiGetGroupMemberCode(groupInfo.apiId, member.groupMemberId) : (member, nil) + await MainActor.run { _ = chatModel.upsertGroupMember(groupInfo, mem) connectionStats = stats connectionCode = code - } catch let error { - logger.error("apiGroupMemberInfo or apiGetGroupMemberCode error: \(responseError(error))") + connectionLoaded = true } + } catch let error { + await MainActor.run { + connectionLoaded = true + } + logger.error("apiGroupMemberInfo or apiGetGroupMemberCode error: \(responseError(error))") } } .onChange(of: newRole) { newRole in diff --git a/apps/ios/Shared/Views/Chat/Group/GroupProfileView.swift b/apps/ios/Shared/Views/Chat/Group/GroupProfileView.swift index 18cc3f4d80..1617edd11f 100644 --- a/apps/ios/Shared/Views/Chat/Group/GroupProfileView.swift +++ b/apps/ios/Shared/Views/Chat/Group/GroupProfileView.swift @@ -110,10 +110,13 @@ struct GroupProfileView: View { } } .onChange(of: chosenImage) { image in - if let image = image { - groupProfile.image = resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500) - } else { - groupProfile.image = nil + Task { + let resized: String? = if let image { + await resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500) + } else { + nil + } + await MainActor.run { groupProfile.image = resized } } } .onAppear { diff --git a/apps/ios/Shared/Views/Chat/ReverseList.swift b/apps/ios/Shared/Views/Chat/ReverseList.swift index 94d160e1b4..2e09909c5e 100644 --- a/apps/ios/Shared/Views/Chat/ReverseList.swift +++ b/apps/ios/Shared/Views/Chat/ReverseList.swift @@ -8,15 +8,16 @@ import SwiftUI import Combine +import SimpleXChat /// A List, which displays it's items in reverse order - from bottom to top -struct ReverseList: UIViewControllerRepresentable { - let items: Array +struct ReverseList: UIViewControllerRepresentable { + let items: Array - @Binding var scrollState: ReverseListScrollModel.State + @Binding var scrollState: ReverseListScrollModel.State /// Closure, that returns user interface for a given item - let content: (Item) -> Content + let content: (ChatItem) -> Content let loadPage: () -> Void @@ -25,7 +26,9 @@ struct ReverseList: UIV } func updateUIViewController(_ controller: Controller, context: Context) { + controller.representer = self if case let .scrollingTo(destination) = scrollState, !items.isEmpty { + controller.view.layer.removeAllAnimations() switch destination { case .nextPage: controller.scrollToNextPage() @@ -42,9 +45,10 @@ struct ReverseList: UIV /// Controller, which hosts SwiftUI cells class Controller: UITableViewController { private enum Section { case main } - private let representer: ReverseList - private var dataSource: UITableViewDiffableDataSource! + var representer: ReverseList + private var dataSource: UITableViewDiffableDataSource! private var itemCount: Int = 0 + private let updateFloatingButtons = PassthroughSubject() private var bag = Set() init(representer: ReverseList) { @@ -71,7 +75,7 @@ struct ReverseList: UIV } // 3. Configure data source - self.dataSource = UITableViewDiffableDataSource( + self.dataSource = UITableViewDiffableDataSource( tableView: tableView ) { (tableView, indexPath, item) -> UITableViewCell? in if indexPath.item > self.itemCount - 8, self.itemCount > 8 { @@ -103,6 +107,15 @@ struct ReverseList: UIV name: notificationName, object: nil ) + + updateFloatingButtons + .throttle(for: 0.2, scheduler: DispatchQueue.global(qos: .background), latest: true) + .sink { + if let listState = DispatchQueue.main.sync(execute: { [weak self] in self?.getListState() }) { + ChatView.FloatingButtonModel.shared.updateOnListChange(listState) + } + } + .store(in: &bag) } @available(*, unavailable) @@ -171,8 +184,8 @@ struct ReverseList: UIV Task { representer.scrollState = .atDestination } } - func update(items: Array) { - var snapshot = NSDiffableDataSourceSnapshot() + func update(items: [ChatItem]) { + var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections([.main]) snapshot.appendItems(items) dataSource.defaultRowAnimation = .none @@ -188,6 +201,42 @@ struct ReverseList: UIV ) } itemCount = items.count + updateFloatingButtons.send() + } + + override func scrollViewDidScroll(_ scrollView: UIScrollView) { + updateFloatingButtons.send() + } + + func getListState() -> ListState? { + if let visibleRows = tableView.indexPathsForVisibleRows, + visibleRows.last?.item ?? 0 < representer.items.count { + let scrollOffset: Double = tableView.contentOffset.y + InvertedTableView.inset + let topItemDate: Date? = + if let lastVisible = visibleRows.last(where: { isVisible(indexPath: $0) }) { + representer.items[lastVisible.item].meta.itemTs + } else { + nil + } + let bottomItemId: ChatItem.ID? = + if let firstVisible = visibleRows.first(where: { isVisible(indexPath: $0) }) { + representer.items[firstVisible.item].id + } else { + nil + } + return (scrollOffset: scrollOffset, topItemDate: topItemDate, bottomItemId: bottomItemId) + } + return nil + } + + private func isVisible(indexPath: IndexPath) -> Bool { + if let relativeFrame = tableView.superview?.convert( + tableView.rectForRow(at: indexPath), + from: tableView + ) { + relativeFrame.maxY > InvertedTableView.inset && + relativeFrame.minY < tableView.frame.height - InvertedTableView.inset + } else { false } } } @@ -231,13 +280,19 @@ struct ReverseList: UIV } } +typealias ListState = ( + scrollOffset: Double, + topItemDate: Date?, + bottomItemId: ChatItem.ID? +) + /// Manages ``ReverseList`` scrolling -class ReverseListScrollModel: ObservableObject { +class ReverseListScrollModel: ObservableObject { /// Represents Scroll State of ``ReverseList`` enum State: Equatable { enum Destination: Equatable { case nextPage - case item(Item.ID) + case item(ChatItem.ID) case bottom } @@ -255,7 +310,7 @@ class ReverseListScrollModel: ObservableObject { state = .scrollingTo(.bottom) } - func scrollToItem(id: Item.ID) { + func scrollToItem(id: ChatItem.ID) { state = .scrollingTo(.item(id)) } } diff --git a/apps/ios/Shared/Views/Chat/SelectableChatItemToolbars.swift b/apps/ios/Shared/Views/Chat/SelectableChatItemToolbars.swift index 87bc73a60e..7b185d8211 100644 --- a/apps/ios/Shared/Views/Chat/SelectableChatItemToolbars.swift +++ b/apps/ios/Shared/Views/Chat/SelectableChatItemToolbars.swift @@ -32,13 +32,17 @@ struct SelectedItemsBottomToolbar: View { var deleteItems: (Bool) -> Void var moderateItems: () -> Void //var shareItems: () -> Void + var forwardItems: () -> Void @State var deleteEnabled: Bool = false @State var deleteForEveryoneEnabled: Bool = false @State var canModerate: Bool = false @State var moderateEnabled: Bool = false - @State var allButtonsDisabled = false + @State var forwardEnabled: Bool = false + + @State var deleteCountProhibited = false + @State var forwardCountProhibited = false var body: some View { VStack(spacing: 0) { @@ -50,10 +54,11 @@ struct SelectedItemsBottomToolbar: View { } label: { Image(systemName: "trash") .resizable() + .scaledToFit() .frame(width: 20, height: 20, alignment: .center) - .foregroundColor(!deleteEnabled || allButtonsDisabled ? theme.colors.secondary: .red) + .foregroundColor(!deleteEnabled || deleteCountProhibited ? theme.colors.secondary: .red) } - .disabled(!deleteEnabled || allButtonsDisabled) + .disabled(!deleteEnabled || deleteCountProhibited) Spacer() Button { @@ -61,24 +66,24 @@ struct SelectedItemsBottomToolbar: View { } label: { Image(systemName: "flag") .resizable() + .scaledToFit() .frame(width: 20, height: 20, alignment: .center) - .foregroundColor(!moderateEnabled || allButtonsDisabled ? theme.colors.secondary : .red) + .foregroundColor(!moderateEnabled || deleteCountProhibited ? theme.colors.secondary : .red) } - .disabled(!moderateEnabled || allButtonsDisabled) + .disabled(!moderateEnabled || deleteCountProhibited) .opacity(canModerate ? 1 : 0) - Spacer() Button { - //shareItems() + forwardItems() } label: { - Image(systemName: "square.and.arrow.up") + Image(systemName: "arrowshape.turn.up.forward") .resizable() + .scaledToFit() .frame(width: 20, height: 20, alignment: .center) - .foregroundColor(allButtonsDisabled ? theme.colors.secondary : theme.colors.primary) + .foregroundColor(!forwardEnabled || forwardCountProhibited ? theme.colors.secondary : theme.colors.primary) } - .disabled(allButtonsDisabled) - .opacity(0) + .disabled(!forwardEnabled || forwardCountProhibited) } .frame(maxHeight: .infinity) .padding([.leading, .trailing], 12) @@ -101,20 +106,22 @@ struct SelectedItemsBottomToolbar: View { private func recheckItems(_ chatInfo: ChatInfo, _ chatItems: [ChatItem], _ selectedItems: Set?) { let count = selectedItems?.count ?? 0 - allButtonsDisabled = count == 0 || count > 20 + deleteCountProhibited = count == 0 || count > 200 + forwardCountProhibited = count == 0 || count > 20 canModerate = possibleToModerate(chatInfo) if let selected = selectedItems { let me: Bool let onlyOwnGroupItems: Bool - (deleteEnabled, deleteForEveryoneEnabled, me, onlyOwnGroupItems, selectedChatItems) = chatItems.reduce((true, true, true, true, [])) { (r, ci) in + (deleteEnabled, deleteForEveryoneEnabled, me, onlyOwnGroupItems, forwardEnabled, selectedChatItems) = chatItems.reduce((true, true, true, true, true, [])) { (r, ci) in if selected.contains(ci.id) { - var (de, dee, me, onlyOwnGroupItems, sel) = r + var (de, dee, me, onlyOwnGroupItems, fe, sel) = r de = de && ci.canBeDeletedForSelf dee = dee && ci.meta.deletable && !ci.localNote onlyOwnGroupItems = onlyOwnGroupItems && ci.chatDir == .groupSnd me = me && ci.content.msgContent != nil && ci.memberToModerate(chatInfo) != nil + fe = fe && ci.content.msgContent != nil && ci.meta.itemDeleted == nil && !ci.isLiveDummy sel.insert(ci.id) // we are collecting new selected items here to account for any changes in chat items list - return (de, dee, me, onlyOwnGroupItems, sel) + return (de, dee, me, onlyOwnGroupItems, fe, sel) } else { return r } diff --git a/apps/ios/Shared/Views/ChatList/ChatHelp.swift b/apps/ios/Shared/Views/ChatList/ChatHelp.swift index c84cdb0b97..a01c81bafb 100644 --- a/apps/ios/Shared/Views/ChatList/ChatHelp.swift +++ b/apps/ios/Shared/Views/ChatList/ChatHelp.swift @@ -10,7 +10,7 @@ import SwiftUI struct ChatHelp: View { @EnvironmentObject var chatModel: ChatModel - @Binding var showSettings: Bool + let dismissSettingsSheet: DismissAction var body: some View { ScrollView { chatHelp() } @@ -23,7 +23,7 @@ struct ChatHelp: View { VStack(alignment: .leading, spacing: 0) { Text("To ask any questions and to receive updates:") Button("connect to SimpleX Chat developers.") { - showSettings = false + dismissSettingsSheet() DispatchQueue.main.async { UIApplication.shared.open(simplexTeamURL) } @@ -61,8 +61,9 @@ struct ChatHelp: View { } struct ChatHelp_Previews: PreviewProvider { + @Environment(\.dismiss) static var mockDismiss + static var previews: some View { - @State var showSettings = false - return ChatHelp(showSettings: $showSettings) + ChatHelp(dismissSettingsSheet: mockDismiss) } } diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index 8ad03236f1..a1b40aadbe 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -9,18 +9,87 @@ import SwiftUI import SimpleXChat +enum UserPickerSheet: Identifiable { + case address + case chatPreferences + case chatProfiles + case currentProfile + case useFromDesktop + case settings + + var id: Self { self } + + var navigationTitle: LocalizedStringKey { + switch self { + case .address: "SimpleX address" + case .chatPreferences: "Your preferences" + case .chatProfiles: "Your chat profiles" + case .currentProfile: "Your current profile" + case .useFromDesktop: "Connect to desktop" + case .settings: "Your settings" + } + } +} + +struct UserPickerSheetView: View { + let sheet: UserPickerSheet + @EnvironmentObject var chatModel: ChatModel + @State private var loaded = false + + var body: some View { + NavigationView { + ZStack { + if loaded, let currentUser = chatModel.currentUser { + switch sheet { + case .address: + UserAddressView(shareViaProfile: currentUser.addressShared) + case .chatPreferences: + PreferencesView( + profile: currentUser.profile, + preferences: currentUser.fullPreferences, + currentPreferences: currentUser.fullPreferences + ) + case .chatProfiles: + UserProfilesView() + case .currentProfile: + UserProfile() + case .useFromDesktop: + ConnectDesktopView() + case .settings: + SettingsView() + } + } + Color.clear // Required for list background to be rendered during loading + } + .navigationTitle(sheet.navigationTitle) + .navigationBarTitleDisplayMode(.large) + .modifier(ThemedBackground(grouped: true)) + } + .overlay { + if let la = chatModel.laRequest { + LocalAuthView(authRequest: la) + } + } + .task { + withAnimation( + .easeOut(duration: 0.1), + { loaded = true } + ) + } + } +} + struct ChatListView: View { @EnvironmentObject var chatModel: ChatModel @EnvironmentObject var theme: AppTheme - @Binding var showSettings: Bool + @Binding var activeUserPickerSheet: UserPickerSheet? @State private var searchMode = false @FocusState private var searchFocussed @State private var searchText = "" @State private var searchShowingSimplexLink = false @State private var searchChatFilteredBySimplexLink: String? = nil - @State private var userPickerVisible = false - @State private var showConnectDesktop = false @State private var scrollToSearchBar = false + @State private var userPickerShown: Bool = false @AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false @AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = true @@ -46,21 +115,21 @@ struct ChatListView: View { ), destination: chatView ) { chatListView } - if userPickerVisible { - Rectangle().fill(.white.opacity(0.001)).onTapGesture { - withAnimation { - userPickerVisible.toggle() - } + } + .modifier( + Sheet(isPresented: $userPickerShown) { + UserPicker(userPickerShown: $userPickerShown, activeSheet: $activeUserPickerSheet) + } + ) + .sheet(item: $activeUserPickerSheet) { + UserPickerSheetView(sheet: $0) + } + .onChange(of: activeUserPickerSheet) { + if $0 != nil { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + userPickerShown = false } } - UserPicker( - showSettings: $showSettings, - showConnectDesktop: $showConnectDesktop, - userPickerVisible: $userPickerVisible - ) - } - .sheet(isPresented: $showConnectDesktop) { - ConnectDesktopView() } } @@ -73,7 +142,7 @@ struct ChatListView: View { .navigationBarHidden(searchMode || oneHandUI) } .scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center) - .onDisappear() { withAnimation { userPickerVisible = false } } + .onDisappear() { activeUserPickerSheet = nil } .refreshable { AlertManager.shared.showAlert(Alert( title: Text("Reconnect servers?"), @@ -164,7 +233,7 @@ struct ChatListView: View { let user = chatModel.currentUser ?? User.sampleData ZStack(alignment: .topTrailing) { ProfileImage(imageStr: user.image, size: 32, color: Color(uiColor: .quaternaryLabel)) - .padding(.trailing, 4) + .padding([.top, .trailing], 3) let allRead = chatModel.users .filter { u in !u.user.activeUser && !u.user.hidden } .allSatisfy { u in u.unreadCount == 0 } @@ -173,13 +242,7 @@ struct ChatListView: View { } } .onTapGesture { - if chatModel.users.filter({ u in u.user.activeUser || !u.user.hidden }).count > 1 { - withAnimation { - userPickerVisible.toggle() - } - } else { - showSettings = true - } + userPickerShown = true } } @@ -269,7 +332,7 @@ struct ChatListView: View { } } - private func unreadBadge(_ text: Text? = Text(" "), size: CGFloat = 18) -> some View { + private func unreadBadge(size: CGFloat = 18) -> some View { Circle() .frame(width: size, height: size) .foregroundColor(theme.colors.primary) @@ -374,7 +437,7 @@ struct SubsStatusIndicator: View { private func startTask() { task = Task { while !Task.isCancelled { - if AppChatState.shared.value == .active { + if AppChatState.shared.value == .active, ChatModel.shared.chatRunning == true { do { let (subs, hasSess) = try await getAgentSubsTotal() await MainActor.run { @@ -516,6 +579,8 @@ func chatStoppedIcon() -> some View { } struct ChatListView_Previews: PreviewProvider { + @State static var userPickerSheet: UserPickerSheet? = .none + static var previews: some View { let chatModel = ChatModel() chatModel.updateChats([ @@ -534,9 +599,9 @@ struct ChatListView_Previews: PreviewProvider { ]) return Group { - ChatListView(showSettings: Binding.constant(false)) + ChatListView(activeUserPickerSheet: $userPickerSheet) .environmentObject(chatModel) - ChatListView(showSettings: Binding.constant(false)) + ChatListView(activeUserPickerSheet: $userPickerSheet) .environmentObject(ChatModel()) } } diff --git a/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift b/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift index 9e6d3005b6..d721d546c1 100644 --- a/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatPreviewView.swift @@ -35,11 +35,16 @@ struct ChatPreviewView: View { } .padding(.leading, 4) + let chatTs = if let cItem { + cItem.meta.itemTs + } else { + chat.chatInfo.chatTs + } VStack(spacing: 0) { HStack(alignment: .top) { chatPreviewTitle() Spacer() - (cItem?.timestampText ?? formatTimestampText(chat.chatInfo.chatTs)) + (formatTimestampText(chatTs)) .font(.subheadline) .frame(minWidth: 60, alignment: .trailing) .foregroundColor(theme.colors.secondary) @@ -302,7 +307,7 @@ struct ChatPreviewView: View { case let .link(_, preview): smallContentPreview(size: dynamicMediaSize) { ZStack(alignment: .topTrailing) { - Image(uiImage: UIImage(base64Encoded: preview.image) ?? UIImage(systemName: "arrow.up.right")!) + Image(uiImage: imageFromBase64(preview.image) ?? UIImage(systemName: "arrow.up.right")!) .resizable() .aspectRatio(contentMode: .fill) .frame(width: dynamicMediaSize, height: dynamicMediaSize) @@ -323,13 +328,13 @@ struct ChatPreviewView: View { } case let .image(_, image): smallContentPreview(size: dynamicMediaSize) { - CIImageView(chatItem: ci, preview: UIImage(base64Encoded: image), maxWidth: dynamicMediaSize, smallView: true, showFullScreenImage: $showFullscreenGallery) - .environmentObject(ReverseListScrollModel()) + CIImageView(chatItem: ci, preview: imageFromBase64(image), maxWidth: dynamicMediaSize, smallView: true, showFullScreenImage: $showFullscreenGallery) + .environmentObject(ReverseListScrollModel()) } case let .video(_,image, duration): smallContentPreview(size: dynamicMediaSize) { - CIVideoView(chatItem: ci, preview: UIImage(base64Encoded: image), duration: duration, maxWidth: dynamicMediaSize, videoWidth: nil, smallView: true, showFullscreenPlayer: $showFullscreenGallery) - .environmentObject(ReverseListScrollModel()) + CIVideoView(chatItem: ci, preview: imageFromBase64(image), duration: duration, maxWidth: dynamicMediaSize, videoWidth: nil, smallView: true, showFullscreenPlayer: $showFullscreenGallery) + .environmentObject(ReverseListScrollModel()) } case let .voice(_, duration): smallContentPreviewVoice(size: dynamicMediaSize) { diff --git a/apps/ios/Shared/Views/ChatList/UserPicker.swift b/apps/ios/Shared/Views/ChatList/UserPicker.swift index 5041e093db..cfcfe851f3 100644 --- a/apps/ios/Shared/Views/ChatList/UserPicker.swift +++ b/apps/ios/Shared/Views/ChatList/UserPicker.swift @@ -8,179 +8,275 @@ import SimpleXChat struct UserPicker: View { @EnvironmentObject var m: ChatModel - @Environment(\.scenePhase) var scenePhase @EnvironmentObject var theme: AppTheme - @Binding var showSettings: Bool - @Binding var showConnectDesktop: Bool - @Binding var userPickerVisible: Bool - @State var scrollViewContentSize: CGSize = .zero - @State var disableScrolling: Bool = true - private let menuButtonHeight: CGFloat = 68 - @State var chatViewNameWidth: CGFloat = 0 + @Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize + @Environment(\.scenePhase) private var scenePhase: ScenePhase + @Environment(\.colorScheme) private var colorScheme: ColorScheme + @Binding var userPickerShown: Bool + @Binding var activeSheet: UserPickerSheet? + @State private var currentUser: Int64? + @State private var switchingProfile = false + @State private var frameWidth: CGFloat = 0 + @State private var resetScroll = ResetScrollAction() + // Inset grouped list dimensions + private let imageSize: CGFloat = 44 + private let rowPadding: CGFloat = 16 + private let rowVerticalPadding: CGFloat = 11 + private let sectionSpacing: CGFloat = 35 + private var sectionHorizontalPadding: CGFloat { frameWidth > 375 ? 20 : 16 } + private let sectionShape = RoundedRectangle(cornerRadius: 10, style: .continuous) var body: some View { - VStack { - Spacer().frame(height: 1) + let otherUsers: [UserInfo] = m.users + .filter { u in !u.user.hidden && u.user.userId != m.currentUser?.userId } + .sorted(using: KeyPathComparator(\.user.activeOrder, order: .reverse)) + let sectionWidth = max(frameWidth - sectionHorizontalPadding * 2, 0) + let currentUserWidth = max(frameWidth - sectionHorizontalPadding - rowPadding * 2 - 14 - imageSize, 0) + let stopped = m.chatRunning != true + VStack(spacing: sectionSpacing) { + if let user = m.currentUser { + StickyScrollView(resetScroll: $resetScroll) { + HStack(spacing: rowPadding) { + HStack { + ProfileImage(imageStr: user.image, size: imageSize, color: Color(uiColor: .tertiarySystemGroupedBackground)) + .padding(.trailing, 6) + profileName(user).lineLimit(1) + } + .padding(rowPadding) + .frame(width: otherUsers.isEmpty ? sectionWidth : currentUserWidth, alignment: .leading) + .modifier(ListRow { activeSheet = .currentProfile }) + .clipShape(sectionShape) + .disabled(stopped) + .opacity(stopped ? 0.4 : 1) + ForEach(otherUsers) { u in + userView(u, size: imageSize) + .frame(maxWidth: sectionWidth * 0.618) + .fixedSize() + .disabled(stopped) + .opacity(stopped ? 0.4 : 1) + } + } + .padding(.horizontal, sectionHorizontalPadding) + } + .frame(height: 2 * rowPadding + imageSize) + .padding(.top, sectionSpacing) + .overlay(DetermineWidth()) + .onPreferenceChange(DetermineWidth.Key.self) { frameWidth = $0 } + } VStack(spacing: 0) { - ScrollView { - ScrollViewReader { sp in - let users = m.users - .filter({ u in u.user.activeUser || !u.user.hidden }) - .sorted { u, _ in u.user.activeUser } - VStack(spacing: 0) { - ForEach(users) { u in - userView(u) - Divider() - if u.user.activeUser { Divider() } + openSheetOnTap("qrcode", title: m.userAddress == nil ? "Create SimpleX address" : "Your SimpleX address", sheet: .address, disabled: stopped) + openSheetOnTap("switch.2", title: "Chat preferences", sheet: .chatPreferences, disabled: stopped) + openSheetOnTap("person.crop.rectangle.stack", title: "Your chat profiles", sheet: .chatProfiles, disabled: stopped) + openSheetOnTap("desktopcomputer", title: "Use from desktop", sheet: .useFromDesktop, disabled: stopped) + ZStack(alignment: .trailing) { + openSheetOnTap("gearshape", title: "Settings", sheet: .settings, showDivider: false) + Image(systemName: colorScheme == .light ? "sun.max" : "moon.fill") + .resizable() + .scaledToFit() + .symbolRenderingMode(.monochrome) + .foregroundColor(theme.colors.secondary) + .frame(maxWidth: 20, maxHeight: .infinity) + .padding(.horizontal, rowPadding) + .background(Color(.systemBackground).opacity(0.01)) + .onTapGesture { + if (colorScheme == .light) { + ThemeManager.applyTheme(systemDarkThemeDefault.get()) + } else { + ThemeManager.applyTheme(DefaultTheme.LIGHT.themeName) } } - .overlay { - GeometryReader { geo -> Color in - DispatchQueue.main.async { - scrollViewContentSize = geo.size - let scenes = UIApplication.shared.connectedScenes - if let windowScene = scenes.first as? UIWindowScene { - let layoutFrame = windowScene.windows[0].safeAreaLayoutGuide.layoutFrame - disableScrolling = scrollViewContentSize.height + menuButtonHeight + 10 < layoutFrame.height - } - } - return Color.clear - } + .onLongPressGesture { + ThemeManager.applyTheme(DefaultTheme.SYSTEM_THEME_NAME) } - .onChange(of: userPickerVisible) { visible in - if visible, let u = users.first { - sp.scrollTo(u.id) - } - } - } - } - .simultaneousGesture(DragGesture(minimumDistance: disableScrolling ? 0 : 10000000)) - .frame(maxHeight: scrollViewContentSize.height) - - menuButton("Use from desktop", icon: "desktopcomputer") { - showConnectDesktop = true - withAnimation { - userPickerVisible.toggle() - } - } - Divider() - menuButton("Settings", icon: "gearshape") { - showSettings = true - withAnimation { - userPickerVisible.toggle() - } } } + .clipShape(sectionShape) + .padding(.horizontal, sectionHorizontalPadding) + .padding(.bottom, sectionSpacing) } - .clipShape(RoundedRectangle(cornerRadius: 16)) - .background( - Rectangle() - .fill(theme.colors.surface) - .cornerRadius(16) - .shadow(color: .black.opacity(0.12), radius: 24, x: 0, y: 0) - ) - .onPreferenceChange(DetermineWidth.Key.self) { chatViewNameWidth = $0 } - .frame(maxWidth: chatViewNameWidth > 0 ? min(300, chatViewNameWidth + 130) : 300) - .padding(8) - .opacity(userPickerVisible ? 1.0 : 0.0) .onAppear { - // This check prevents the call of listUsers after the app is suspended, and the database is closed. - if case .active = scenePhase { - Task { - do { - let users = try await listUsersAsync() - await MainActor.run { m.users = users } - } catch { - logger.error("Error loading users \(responseError(error))") - } - } - } - } - } - - private func userView(_ u: UserInfo) -> some View { - let user = u.user - return Button(action: { - if user.activeUser { - showSettings = true - withAnimation { - userPickerVisible.toggle() - } - } else { + // This check prevents the call of listUsers after the app is suspended, and the database is closed. + if case .active = scenePhase { + currentUser = m.currentUser?.userId Task { do { - try await changeActiveUserAsync_(user.userId, viewPwd: nil) - await MainActor.run { userPickerVisible = false } - } catch { + let users = try await listUsersAsync() await MainActor.run { - AlertManager.shared.showAlertMsg( - title: "Error switching profile!", - message: "Error: \(responseError(error))" - ) + m.users = users + currentUser = m.currentUser?.userId } + } catch { + logger.error("Error loading users \(responseError(error))") } } } - }, label: { - HStack(spacing: 0) { - ProfileImage(imageStr: user.image, size: 44, color: Color(uiColor: .tertiarySystemFill)) - .padding(.trailing, 12) - Text(user.chatViewName) - .fontWeight(user.activeUser ? .medium : .regular) - .foregroundColor(theme.colors.onBackground) - .overlay(DetermineWidth()) - Spacer() - if user.activeUser { - Image(systemName: "checkmark") - } else if u.unreadCount > 0 { - unreadCounter(u.unreadCount, color: user.showNtfs ? theme.colors.primary : theme.colors.secondary) - } else if !user.showNtfs { - Image(systemName: "speaker.slash") + } + .onChange(of: userPickerShown) { + if !$0 { resetScroll() } + } + .modifier(ThemedBackground(grouped: true)) + .disabled(switchingProfile) + } + + private func userView(_ u: UserInfo, size: CGFloat) -> some View { + HStack { + ZStack(alignment: .topTrailing) { + ProfileImage(imageStr: u.user.image, size: size, color: Color(uiColor: .tertiarySystemGroupedBackground)) + if (u.unreadCount > 0) { + unreadBadge(u).offset(x: 4, y: -4) } } - .padding(.trailing) - .padding([.leading, .vertical], 12) - }) - .buttonStyle(PressedButtonStyle(defaultColor: theme.colors.surface, pressedColor: Color(uiColor: .secondarySystemFill))) - } - - private func menuButton(_ title: LocalizedStringKey, icon: String, action: @escaping () -> Void) -> some View { - Button(action: action) { - HStack(spacing: 0) { - Text(title) - .overlay(DetermineWidth()) - Spacer() - Image(systemName: icon) - .symbolRenderingMode(.monochrome) - .foregroundColor(theme.colors.secondary) - } - .padding(.horizontal) - .padding(.vertical, 22) - .frame(height: menuButtonHeight) + .padding(.trailing, 6) + Text(u.user.displayName).font(.title2).lineLimit(1) } - .buttonStyle(PressedButtonStyle(defaultColor: theme.colors.surface, pressedColor: Color(uiColor: .secondarySystemFill))) + .padding(rowPadding) + .modifier(ListRow { + switchingProfile = true + Task { + do { + try await changeActiveUserAsync_(u.user.userId, viewPwd: nil) + await MainActor.run { + switchingProfile = false + userPickerShown = false + } + } catch { + await MainActor.run { + switchingProfile = false + showAlert( + NSLocalizedString("Error switching profile!", comment: "alertTitle"), + message: String.localizedStringWithFormat(NSLocalizedString("Error: %@", comment: "alert message"), responseError(error)) + ) + } + } + } + }) + .clipShape(sectionShape) + } + + private func openSheetOnTap(_ icon: String, title: LocalizedStringKey, sheet: UserPickerSheet, showDivider: Bool = true, disabled: Bool = false) -> some View { + ZStack(alignment: .bottom) { + settingsRow(icon, color: theme.colors.secondary) { + Text(title).foregroundColor(.primary).opacity(disabled ? 0.4 : 1) + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, rowPadding) + .padding(.vertical, rowVerticalPadding) + .modifier(ListRow { activeSheet = sheet }) + .disabled(disabled) + if showDivider { + Divider().padding(.leading, 52) + } + } + } + + private func unreadBadge(_ u: UserInfo) -> some View { + let size = dynamicSize(userFont).chatInfoSize + return unreadCountText(u.unreadCount) + .font(userFont <= .xxxLarge ? .caption : .caption2) + .foregroundColor(.white) + .padding(.horizontal, dynamicSize(userFont).unreadPadding) + .frame(minWidth: size, minHeight: size) + .background(u.user.showNtfs ? theme.colors.primary : theme.colors.secondary) + .cornerRadius(dynamicSize(userFont).unreadCorner) } } -private func unreadCounter(_ unread: Int, color: Color) -> some View { - unreadCountText(unread) - .font(.caption) - .foregroundColor(.white) - .padding(.horizontal, 4) - .frame(minWidth: 18, minHeight: 18) - .background(color) - .cornerRadius(10) +struct ListRow: ViewModifier { + @Environment(\.colorScheme) private var colorScheme: ColorScheme + @State private var touchDown = false + let action: () -> Void + + func body(content: Content) -> some View { + ZStack { + elevatedSecondarySystemGroupedBackground + Color(.systemGray4).opacity(touchDown ? 1 : 0) + content + TouchOverlay(touchDown: $touchDown, action: action) + } + } + + var elevatedSecondarySystemGroupedBackground: Color { + switch colorScheme { + case .dark: Color(0xFF2C2C2E) + default: Color(0xFFFFFFFF) + } + } + + struct TouchOverlay: UIViewRepresentable { + @Binding var touchDown: Bool + let action: () -> Void + + func makeUIView(context: Context) -> TouchView { + let touchView = TouchView() + let gesture = UILongPressGestureRecognizer( + target: touchView, + action: #selector(touchView.longPress(gesture:)) + ) + gesture.delegate = touchView + gesture.minimumPressDuration = 0 + touchView.addGestureRecognizer(gesture) + return touchView + } + + func updateUIView(_ touchView: TouchView, context: Context) { + touchView.representer = self + } + + class TouchView: UIView, UIGestureRecognizerDelegate { + var representer: TouchOverlay? + private var startLocation: CGPoint? + private var task: Task? + + @objc + func longPress(gesture: UILongPressGestureRecognizer) { + switch gesture.state { + case .began: + startLocation = gesture.location(in: nil) + task = Task { + do { + try await Task.sleep(nanoseconds: 200_000000) + await MainActor.run { representer?.touchDown = true } + } catch { } + } + case .ended: + if hitTest(gesture.location(in: self), with: nil) == self { + representer?.action() + } + task?.cancel() + representer?.touchDown = false + case .changed: + if let startLocation { + let location = gesture.location(in: nil) + let dx = location.x - startLocation.x + let dy = location.y - startLocation.y + if sqrt(pow(dx, 2) + pow(dy, 2)) > 10 { gesture.state = .failed } + } + case .cancelled, .failed: + task?.cancel() + representer?.touchDown = false + default: break + } + } + + func gestureRecognizer( + _: UIGestureRecognizer, + shouldRecognizeSimultaneouslyWith: UIGestureRecognizer + ) -> Bool { true } + } + } } + struct UserPicker_Previews: PreviewProvider { static var previews: some View { + @State var activeSheet: UserPickerSheet? + let m = ChatModel() m.users = [UserInfo.sampleData, UserInfo.sampleData] return UserPicker( - showSettings: Binding.constant(false), - showConnectDesktop: Binding.constant(false), - userPickerVisible: Binding.constant(true) + userPickerShown: .constant(true), + activeSheet: $activeSheet ) .environmentObject(m) } diff --git a/apps/ios/Shared/Views/Database/DatabaseView.swift b/apps/ios/Shared/Views/Database/DatabaseView.swift index f5b5287971..804f2307ef 100644 --- a/apps/ios/Shared/Views/Database/DatabaseView.swift +++ b/apps/ios/Shared/Views/Database/DatabaseView.swift @@ -44,7 +44,7 @@ enum DatabaseAlert: Identifiable { struct DatabaseView: View { @EnvironmentObject var m: ChatModel @EnvironmentObject var theme: AppTheme - @Binding var showSettings: Bool + let dismissSettingsSheet: DismissAction @State private var runChat = false @State private var alert: DatabaseAlert? = nil @State private var showFileImporter = false @@ -270,12 +270,12 @@ struct DatabaseView: View { case let .archiveImportedWithErrors(errs): return Alert( title: Text("Chat database imported"), - message: Text("Restart the app to use imported chat database") + Text(verbatim: "\n\n") + Text("Some non-fatal errors occurred during import:") + archiveErrorsText(errs) + message: Text("Restart the app to use imported chat database") + Text(verbatim: "\n") + Text("Some non-fatal errors occurred during import:") + archiveErrorsText(errs) ) case let .archiveExportedWithErrors(archivePath, errs): return Alert( title: Text("Chat database exported"), - message: Text("You may save the exported archive.") + Text(verbatim: "\n\n") + Text("Some file(s) were not exported:") + archiveErrorsText(errs), + message: Text("You may save the exported archive.") + Text(verbatim: "\n") + Text("Some file(s) were not exported:") + archiveErrorsText(errs), dismissButton: .default(Text("Continue")) { showShareSheet(items: [archivePath]) } @@ -439,7 +439,7 @@ struct DatabaseView: View { private func startChat() { if m.chatDbChanged { - showSettings = false + dismissSettingsSheet() DispatchQueue.main.asyncAfter(deadline: .now() + 1) { resetChatCtrl() do { @@ -533,7 +533,9 @@ func deleteChatAsync() async throws { } struct DatabaseView_Previews: PreviewProvider { + @Environment(\.dismiss) static var mockDismiss + static var previews: some View { - DatabaseView(showSettings: Binding.constant(false), chatItemTTL: .none) + DatabaseView(dismissSettingsSheet: mockDismiss, chatItemTTL: .none) } } diff --git a/apps/ios/Shared/Views/Helpers/ChatItemClipShape.swift b/apps/ios/Shared/Views/Helpers/ChatItemClipShape.swift index 477dc567eb..9aa6ac86cf 100644 --- a/apps/ios/Shared/Views/Helpers/ChatItemClipShape.swift +++ b/apps/ios/Shared/Views/Helpers/ChatItemClipShape.swift @@ -14,50 +14,55 @@ import SimpleXChat /// Supports [Dynamic Type](https://developer.apple.com/documentation/uikit/uifont/scaling_fonts_automatically) /// by retaining pill shape, even when ``ChatItem``'s height is less that twice its corner radius struct ChatItemClipped: ViewModifier { - struct ClipShape: Shape { - let maxCornerRadius: Double - - func path(in rect: CGRect) -> Path { - Path( - roundedRect: rect, - cornerRadius: min((rect.height / 2), maxCornerRadius), - style: .circular - ) - } - } - + @AppStorage(DEFAULT_CHAT_ITEM_ROUNDNESS) private var roundness = defaultChatItemRoundness + @AppStorage(DEFAULT_CHAT_ITEM_TAIL) private var tailEnabled = true + private let chatItem: (content: CIContent, chatDir: CIDirection)? + private let tailVisible: Bool + init() { - clipShape = ClipShape( - maxCornerRadius: 18 - ) + self.chatItem = nil + self.tailVisible = false + } + + init(_ ci: ChatItem, tailVisible: Bool) { + self.chatItem = (ci.content, ci.chatDir) + self.tailVisible = tailVisible } - init(_ chatItem: ChatItem) { - clipShape = ClipShape( - maxCornerRadius: { - switch chatItem.content { - case - .sndMsgContent, + private func shapeStyle() -> ChatItemShape.Style { + if let ci = chatItem { + switch ci.content { + case + .sndMsgContent, .rcvMsgContent, .rcvDecryptionError, - .rcvGroupInvitation, - .sndGroupInvitation, - .sndDeleted, - .rcvDeleted, .rcvIntegrityError, - .sndModerated, - .rcvModerated, - .rcvBlocked, - .invalidJSON: 18 - default: 8 + .invalidJSON: + let tail = if let mc = ci.content.msgContent, mc.isImageOrVideo && mc.text.isEmpty { + false + } else { + tailVisible } - }() - ) + return tailEnabled + ? .bubble( + padding: ci.chatDir.sent ? .trailing : .leading, + tailVisible: tail + ) + : .roundRect(radius: msgRectMaxRadius) + case .rcvGroupInvitation, .sndGroupInvitation: + return .roundRect(radius: msgRectMaxRadius) + default: return .roundRect(radius: 8) + } + } else { + return .roundRect(radius: msgRectMaxRadius) + } } - - private let clipShape: ClipShape - + func body(content: Content) -> some View { + let clipShape = ChatItemShape( + roundness: roundness, + style: shapeStyle() + ) content .contentShape(.dragPreview, clipShape) .contentShape(.contextMenuPreview, clipShape) @@ -65,4 +70,106 @@ struct ChatItemClipped: ViewModifier { } } +struct ChatTailPadding: ViewModifier { + func body(content: Content) -> some View { + content.padding(.horizontal, -msgTailWidth) + } +} +private let msgRectMaxRadius: Double = 18 +private let msgBubbleMaxRadius: Double = msgRectMaxRadius * 1.2 +private let msgTailWidth: Double = 9 +private let msgTailMinHeight: Double = msgTailWidth * 1.254 // ~56deg +private let msgTailMaxHeight: Double = msgTailWidth * 1.732 // 60deg + +struct ChatItemShape: Shape { + fileprivate enum Style { + case bubble(padding: HorizontalEdge, tailVisible: Bool) + case roundRect(radius: Double) + } + + fileprivate let roundness: Double + fileprivate let style: Style + + func path(in rect: CGRect) -> Path { + switch style { + case let .bubble(padding, tailVisible): + let w = rect.width + let h = rect.height + let rxMax = min(msgBubbleMaxRadius, w / 2) + let ryMax = min(msgBubbleMaxRadius, h / 2) + let rx = roundness * rxMax + let ry = roundness * ryMax + let tailHeight = min(msgTailMinHeight + roundness * (msgTailMaxHeight - msgTailMinHeight), h / 2) + var path = Path() + // top side + path.move(to: CGPoint(x: rx, y: 0)) + path.addLine(to: CGPoint(x: w - rx, y: 0)) + if roundness > 0 { + // top-right corner + path.addQuadCurve(to: CGPoint(x: w, y: ry), control: CGPoint(x: w, y: 0)) + } + if rect.height > 2 * ry { + // right side + path.addLine(to: CGPoint(x: w, y: h - ry)) + } + if roundness > 0 { + // bottom-right corner + path.addQuadCurve(to: CGPoint(x: w - rx, y: h), control: CGPoint(x: w, y: h)) + } + // bottom side + if tailVisible { + path.addLine(to: CGPoint(x: -msgTailWidth, y: h)) + if roundness > 0 { + // bottom-left tail + // distance of control point from touch point, calculated via ratios + let d = tailHeight - msgTailWidth * msgTailWidth / tailHeight + // tail control point + let tc = CGPoint(x: 0, y: h - tailHeight + d * sqrt(roundness)) + // bottom-left tail curve + path.addQuadCurve(to: CGPoint(x: 0, y: h - tailHeight), control: tc) + } else { + path.addLine(to: CGPoint(x: 0, y: h - tailHeight)) + } + if rect.height > ry + tailHeight { + // left side + path.addLine(to: CGPoint(x: 0, y: ry)) + } + } else { + path.addLine(to: CGPoint(x: rx, y: h)) + path.addQuadCurve(to: CGPoint(x: 0, y: h - ry), control: CGPoint(x: 0 , y: h)) + if rect.height > 2 * ry { + // left side + path.addLine(to: CGPoint(x: 0, y: ry)) + } + } + if roundness > 0 { + // top-left corner + path.addQuadCurve(to: CGPoint(x: rx, y: 0), control: CGPoint(x: 0, y: 0)) + } + path.closeSubpath() + return switch padding { + case .leading: path + case .trailing: path + .scale(x: -1, y: 1, anchor: .center) + .path(in: rect) + } + case let .roundRect(radius): + return Path(roundedRect: rect, cornerRadius: radius * roundness) + } + } + + var offset: Double? { + switch style { + case let .bubble(padding, isTailVisible): + if isTailVisible { + switch padding { + case .leading: -msgTailWidth + case .trailing: msgTailWidth + } + } else { 0 } + case .roundRect: 0 + } + } + +} diff --git a/apps/ios/Shared/Views/Helpers/InvertedForegroundStyle.swift b/apps/ios/Shared/Views/Helpers/InvertedForegroundStyle.swift new file mode 100644 index 0000000000..dca413dafe --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/InvertedForegroundStyle.swift @@ -0,0 +1,21 @@ +// +// Test.swift +// SimpleX (iOS) +// +// Created by Levitating Pineapple on 31/08/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI + +extension View { + @ViewBuilder + func invertedForegroundStyle(enabled: Bool = true) -> some View { + if enabled { + foregroundStyle(Material.ultraThin) + .environment(\.colorScheme, .dark) + .grayscale(1) + .contrast(-20) + } else { self } + } +} diff --git a/apps/ios/Shared/Views/Helpers/ProfileImage.swift b/apps/ios/Shared/Views/Helpers/ProfileImage.swift index 248504c59b..3eedd56441 100644 --- a/apps/ios/Shared/Views/Helpers/ProfileImage.swift +++ b/apps/ios/Shared/Views/Helpers/ProfileImage.swift @@ -20,7 +20,7 @@ struct ProfileImage: View { @AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var radius = defaultProfileImageCorner var body: some View { - if let uiImage = UIImage(base64Encoded: imageStr) { + if let uiImage = imageFromBase64(imageStr) { clipProfileImage(Image(uiImage: uiImage), size: size, radius: radius, blurred: blurred) } else { let c = color.asAnotherColorFromSecondaryVariant(theme) diff --git a/apps/ios/Shared/Views/Helpers/ShareSheet.swift b/apps/ios/Shared/Views/Helpers/ShareSheet.swift index 936c6cb3ab..b8de0e4ceb 100644 --- a/apps/ios/Shared/Views/Helpers/ShareSheet.swift +++ b/apps/ios/Shared/Views/Helpers/ShareSheet.swift @@ -8,15 +8,63 @@ import SwiftUI -func showShareSheet(items: [Any], completed: (() -> Void)? = nil) { +func getTopViewController() -> UIViewController? { let keyWindowScene = UIApplication.shared.connectedScenes.first { $0.activationState == .foregroundActive } as? UIWindowScene if let keyWindow = keyWindowScene?.windows.filter(\.isKeyWindow).first, - let presentedViewController = keyWindow.rootViewController?.presentedViewController ?? keyWindow.rootViewController { + let rootViewController = keyWindow.rootViewController { + // Find the top-most presented view controller + var topController = rootViewController + while let presentedViewController = topController.presentedViewController { + topController = presentedViewController + } + return topController + } + return nil +} + +func showShareSheet(items: [Any], completed: (() -> Void)? = nil) { + if let topController = getTopViewController() { let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil) if let completed = completed { - let handler: UIActivityViewController.CompletionWithItemsHandler = { _,_,_,_ in completed() } - activityViewController.completionWithItemsHandler = handler - } - presentedViewController.present(activityViewController, animated: true) + activityViewController.completionWithItemsHandler = { _, _, _, _ in + completed() + } + } + topController.present(activityViewController, animated: true) } } + +func showAlert( + title: String, + message: String? = nil, + buttonTitle: String, + buttonAction: @escaping () -> Void, + cancelButton: Bool +) -> Void { + if let topController = getTopViewController() { + let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: buttonTitle, style: .default) { _ in + buttonAction() + }) + if cancelButton { + alert.addAction(cancelAlertAction) + } + topController.present(alert, animated: true) + } +} + +func showAlert( + _ title: String, + message: String? = nil, + actions: () -> [UIAlertAction] = { [okAlertAction] } +) { + if let topController = getTopViewController() { + let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) + for action in actions() { alert.addAction(action) } + topController.present(alert, animated: true) + } +} + +let okAlertAction = UIAlertAction(title: NSLocalizedString("Ok", comment: "alert button"), style: .default) + +let cancelAlertAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: "alert button"), style: .cancel) diff --git a/apps/ios/Shared/Views/Helpers/SheetRepresentable.swift b/apps/ios/Shared/Views/Helpers/SheetRepresentable.swift new file mode 100644 index 0000000000..841d5c7eda --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/SheetRepresentable.swift @@ -0,0 +1,188 @@ +// +// SwiftUISheet.swift +// SimpleX (iOS) +// +// Created by user on 23/09/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI + +private let sheetAnimationDuration: Double = 0.35 + +// Refrence: https://easings.net/ +private let easeOutCubic = UICubicTimingParameters( + controlPoint1: CGPoint(x: 0.215, y: 0.61), + controlPoint2: CGPoint(x: 0.355, y: 1) +) + +struct Sheet: ViewModifier { + @Binding var isPresented: Bool + @ViewBuilder let sheetContent: () -> SheetContent + + func body(content: Content) -> some View { + ZStack { + content + SheetRepresentable(isPresented: $isPresented, content: sheetContent()) + .allowsHitTesting(isPresented) + .ignoresSafeArea() + } + } +} + +struct SheetRepresentable: UIViewControllerRepresentable { + @Binding var isPresented: Bool + let content: Content + + func makeUIViewController(context: Context) -> Controller { + Controller(content: content, representer: self) + } + + func updateUIViewController(_ sheetController: Controller, context: Context) { + sheetController.animate(isPresented: isPresented) + } + + class Controller: UIViewController { + let hostingController: UIHostingController + private let animator = UIViewPropertyAnimator( + duration: sheetAnimationDuration, + timingParameters: easeOutCubic + ) + private let representer: SheetRepresentable + private var retainedFraction: CGFloat = 0 + private var sheetHeight: Double { hostingController.view.frame.height } + private var task: Task? + + init(content: C, representer: SheetRepresentable) { + self.representer = representer + self.hostingController = UIHostingController(rootView: content) + super.init(nibName: nil, bundle: nil) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { fatalError("init(coder:) missing") } + + deinit { + animator.stopAnimation(true) + animator.finishAnimation(at: .current) + } + + func animate(isPresented: Bool) { + let alreadyAnimating = animator.isRunning && isPresented != animator.isReversed + let sheetFullyDismissed = animator.fractionComplete == (animator.isReversed ? 1 : 0) + let sheetFullyPresented = animator.fractionComplete == (animator.isReversed ? 0 : 1) + + if !isPresented && sheetFullyDismissed || + isPresented && sheetFullyPresented || + alreadyAnimating { + return + } + + animator.pauseAnimation() + animator.isReversed = !isPresented + animator.continueAnimation( + withTimingParameters: isPresented + ? easeOutCubic + : UICubicTimingParameters(animationCurve: .easeIn), + durationFactor: 1 - animator.fractionComplete + ) + handleVisibility() + } + + func handleVisibility() { + if animator.isReversed { + task = Task { + do { + let sleepDuration = UInt64(sheetAnimationDuration * Double(NSEC_PER_SEC)) + try await Task.sleep(nanoseconds: sleepDuration) + view.isHidden = true + } catch { } + } + } else { + task?.cancel() + task = nil + view.isHidden = false + } + } + + override func viewDidLoad() { + view.isHidden = true + view.backgroundColor = .clear + view.addGestureRecognizer( + UITapGestureRecognizer(target: self, action: #selector(tap(gesture:))) + ) + addChild(hostingController) + hostingController.didMove(toParent: self) + if let sheet = hostingController.view { + sheet.isHidden = true + sheet.clipsToBounds = true + sheet.layer.cornerRadius = 10 + sheet.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner] + sheet.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(pan(gesture:)))) + sheet.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(sheet) + NSLayoutConstraint.activate([ + hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), + hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor), + hostingController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), + ]) + } + } + + override func viewDidAppear(_ animated: Bool) { + // Ensures animations are only setup once + // on some iOS version `viewDidAppear` can get called on each state change. + if hostingController.view.isHidden { + hostingController.view.transform = CGAffineTransform(translationX: 0, y: self.sheetHeight) + hostingController.view.isHidden = false + animator.pausesOnCompletion = true + animator.addAnimations { + self.hostingController.view.transform = .identity + self.view.backgroundColor = UIColor { + switch $0.userInterfaceStyle { + case .dark: .black.withAlphaComponent(0.290) + default: .black.withAlphaComponent(0.121) + } + } + } + animator.startAnimation() + animator.pauseAnimation() + } + } + + @objc + func pan(gesture: UIPanGestureRecognizer) { + switch gesture.state { + case .began: + animator.isReversed = false + animator.pauseAnimation() + retainedFraction = animator.fractionComplete + case .changed: + animator.fractionComplete = retainedFraction - gesture.translation(in: view).y / sheetHeight + case .ended, .cancelled: + let velocity = gesture.velocity(in: view).y + animator.isReversed = (velocity - (animator.fractionComplete - 0.5) * 100).sign == .plus + let defaultVelocity = sheetHeight / sheetAnimationDuration + let fractionRemaining = 1 - animator.fractionComplete + let durationFactor = min(max(fractionRemaining / (abs(velocity) / defaultVelocity), 0.5), 1) + animator.continueAnimation(withTimingParameters: nil, durationFactor: durationFactor * fractionRemaining) + handleVisibility() + DispatchQueue.main.asyncAfter(deadline: .now() + sheetAnimationDuration) { + self.representer.isPresented = !self.animator.isReversed + } + default: break + } + } + + @objc + func tap(gesture: UITapGestureRecognizer) { + switch gesture.state { + case .ended: + if gesture.location(in: view).y < view.frame.height - sheetHeight { + representer.isPresented = false + } + default: break + } + } + } +} diff --git a/apps/ios/Shared/Views/Helpers/StickyScrollView.swift b/apps/ios/Shared/Views/Helpers/StickyScrollView.swift new file mode 100644 index 0000000000..5799962778 --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/StickyScrollView.swift @@ -0,0 +1,61 @@ +// +// StickyScrollView.swift +// SimpleX (iOS) +// +// Created by user on 20/09/2024. +// Copyright © 2024 SimpleX Chat. All rights reserved. +// + +import SwiftUI + +struct StickyScrollView: UIViewRepresentable { + @Binding var resetScroll: ResetScrollAction + @ViewBuilder let content: () -> Content + + func makeUIView(context: Context) -> UIScrollView { + let hc = context.coordinator.hostingController + hc.view.backgroundColor = .clear + let sv = UIScrollView() + sv.showsHorizontalScrollIndicator = false + sv.addSubview(hc.view) + sv.delegate = context.coordinator + DispatchQueue.main.async { + resetScroll = ResetScrollAction { sv.setContentOffset(.zero, animated: false) } + } + return sv + } + + func updateUIView(_ scrollView: UIScrollView, context: Context) { + let hc = context.coordinator.hostingController + hc.rootView = content() + hc.view.frame.size = hc.view.intrinsicContentSize + scrollView.contentSize = hc.view.intrinsicContentSize + } + + func makeCoordinator() -> Coordinator { + Coordinator(content: content()) + } + + class Coordinator: NSObject, UIScrollViewDelegate { + let hostingController: UIHostingController + + init(content: Content) { + self.hostingController = UIHostingController(rootView: content) + } + + func scrollViewWillEndDragging( + _ scrollView: UIScrollView, + withVelocity velocity: CGPoint, + targetContentOffset: UnsafeMutablePointer + ) { + if targetContentOffset.pointee.x < 32 { + targetContentOffset.pointee.x = 0 + } + } + } +} + +struct ResetScrollAction { + var action = { } + func callAsFunction() { action() } +} diff --git a/apps/ios/Shared/Views/Migration/MigrateFromDevice.swift b/apps/ios/Shared/Views/Migration/MigrateFromDevice.swift index 3c514d3529..829cea0165 100644 --- a/apps/ios/Shared/Views/Migration/MigrateFromDevice.swift +++ b/apps/ios/Shared/Views/Migration/MigrateFromDevice.swift @@ -58,8 +58,6 @@ private enum MigrateFromDeviceViewAlert: Identifiable { struct MigrateFromDevice: View { @EnvironmentObject var m: ChatModel @EnvironmentObject var theme: AppTheme - @Environment(\.dismiss) var dismiss: DismissAction - @Binding var showSettings: Bool @Binding var showProgressOnSettings: Bool @State private var migrationState: MigrationFromState = .chatStopInProgress @State private var useKeychain = storeDBPassphraseGroupDefault.get() @@ -108,9 +106,6 @@ struct MigrateFromDevice: View { finishedView(chatDeletion) } } - .modifier(BackButton(label: "Back", disabled: $backDisabled) { - dismiss() - }) .onChange(of: migrationState) { state in backDisabled = switch migrationState { case .chatStopInProgress, .archiving, .linkShown, .finished: true @@ -182,7 +177,7 @@ struct MigrateFromDevice: View { case let .archiveExportedWithErrors(archivePath, errs): return Alert( title: Text("Chat database exported"), - message: Text("You may migrate the exported database.") + Text(verbatim: "\n\n") + Text("Some file(s) were not exported:") + archiveErrorsText(errs), + message: Text("You may migrate the exported database.") + Text(verbatim: "\n") + Text("Some file(s) were not exported:") + archiveErrorsText(errs), dismissButton: .default(Text("Continue")) { Task { await uploadArchive(path: archivePath) } } @@ -607,7 +602,7 @@ struct MigrateFromDevice: View { } catch let error { fatalError("Error starting chat \(responseError(error))") } - showSettings = false + dismissAllSheets(animated: true) } } catch let error { alert = .error(title: "Error deleting database", error: responseError(error)) @@ -630,9 +625,7 @@ struct MigrateFromDevice: View { } // Hide settings anyway if chatDbStatus is not ok, probably passphrase needs to be entered if dismiss || m.chatDbStatus != .ok { - await MainActor.run { - showSettings = false - } + dismissAllSheets(animated: true) } } @@ -784,6 +777,6 @@ private class MigrationChatReceiver { struct MigrateFromDevice_Previews: PreviewProvider { static var previews: some View { - MigrateFromDevice(showSettings: Binding.constant(true), showProgressOnSettings: Binding.constant(false)) + MigrateFromDevice(showProgressOnSettings: Binding.constant(false)) } } diff --git a/apps/ios/Shared/Views/Migration/MigrateToDevice.swift b/apps/ios/Shared/Views/Migration/MigrateToDevice.swift index e2df68a0e4..fe0eec609b 100644 --- a/apps/ios/Shared/Views/Migration/MigrateToDevice.swift +++ b/apps/ios/Shared/Views/Migration/MigrateToDevice.swift @@ -571,7 +571,7 @@ struct MigrateToDevice: View { AlertManager.shared.showAlert( Alert( title: Text("Error migrating settings"), - message: Text ("Not all settings were migrated. Repeat migration if you need them.") + Text("\n\n") + Text(responseError(error))) + message: Text ("Some app settings were not migrated.") + Text("\n") + Text(responseError(error))) ) } hideView() diff --git a/apps/ios/Shared/Views/NewChat/AddContactLearnMore.swift b/apps/ios/Shared/Views/NewChat/AddContactLearnMore.swift index 6001dff790..3a64a955c5 100644 --- a/apps/ios/Shared/Views/NewChat/AddContactLearnMore.swift +++ b/apps/ios/Shared/Views/NewChat/AddContactLearnMore.swift @@ -28,7 +28,9 @@ struct AddContactLearnMore: View { Text("If you can't meet in person, show QR code in a video call, or share the link.") Text("Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).") } + .frame(maxWidth: .infinity, alignment: .leading) .listRowBackground(Color.clear) + .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) } .modifier(ThemedBackground(grouped: true)) } diff --git a/apps/ios/Shared/Views/NewChat/AddGroupView.swift b/apps/ios/Shared/Views/NewChat/AddGroupView.swift index 33c187b64b..0c7f6136ff 100644 --- a/apps/ios/Shared/Views/NewChat/AddGroupView.swift +++ b/apps/ios/Shared/Views/NewChat/AddGroupView.swift @@ -137,10 +137,13 @@ struct AddGroupView: View { createInvalidNameAlert(mkValidName(profile.displayName), $profile.displayName) } .onChange(of: chosenImage) { image in - if let image = image { - profile.image = resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500) - } else { - profile.image = nil + Task { + let resized: String? = if let image { + await resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500) + } else { + nil + } + await MainActor.run { profile.image = resized } } } .modifier(ThemedBackground(grouped: true)) diff --git a/apps/ios/Shared/Views/NewChat/NewChatMenuButton.swift b/apps/ios/Shared/Views/NewChat/NewChatMenuButton.swift index 764c2b47b3..3ca3e0e4d8 100644 --- a/apps/ios/Shared/Views/NewChat/NewChatMenuButton.swift +++ b/apps/ios/Shared/Views/NewChat/NewChatMenuButton.swift @@ -14,9 +14,11 @@ enum ContactType: Int { } struct NewChatMenuButton: View { + // do not use chatModel here because it prevents showing AddGroupMembersView after group creation and QR code after link creation on iOS 16 +// @EnvironmentObject var chatModel: ChatModel @State private var showNewChatSheet = false @State private var alert: SomeAlert? = nil - @State private var globalAlert: SomeAlert? = nil + @State private var pendingConnection: PendingContactConnection? = nil var body: some View { Button { @@ -28,22 +30,14 @@ struct NewChatMenuButton: View { .frame(width: 24, height: 24) } .appSheet(isPresented: $showNewChatSheet) { - NewChatSheet(alert: $alert) + NewChatSheet(pendingConnection: $pendingConnection) .environment(\EnvironmentValues.refresh as! WritableKeyPath, nil) - .alert(item: $alert) { a in - return a.alert + .onDisappear { + alert = cleanupPendingConnection(contactConnection: pendingConnection) + pendingConnection = nil } } - // This is a workaround to show "Keep unused invitation" alert in both following cases: - // - on going back from NewChatView to NewChatSheet, - // - on dismissing NewChatMenuButton sheet while on NewChatView (skipping NewChatSheet) - .onChange(of: alert?.id) { a in - if !showNewChatSheet && alert != nil { - globalAlert = alert - alert = nil - } - } - .alert(item: $globalAlert) { a in + .alert(item: $alert) { a in return a.alert } } @@ -60,7 +54,8 @@ struct NewChatSheet: View { @State private var searchText = "" @State private var searchShowingSimplexLink = false @State private var searchChatFilteredBySimplexLink: String? = nil - @Binding var alert: SomeAlert? + @State private var alert: SomeAlert? + @Binding var pendingConnection: PendingContactConnection? // Sheet height management @State private var isAddContactActive = false @@ -78,6 +73,9 @@ struct NewChatSheet: View { .navigationBarTitleDisplayMode(.large) .navigationBarHidden(searchMode) .modifier(ThemedBackground(grouped: true)) + .alert(item: $alert) { a in + return a.alert + } } if #available(iOS 16.0, *), oneHandUI { let sheetHeight: CGFloat = showArchive ? 575 : 500 @@ -112,7 +110,7 @@ struct NewChatSheet: View { if (searchText.isEmpty) { Section { NavigationLink(isActive: $isAddContactActive) { - NewChatView(selection: .invite, parentAlert: $alert) + NewChatView(selection: .invite, parentAlert: $alert, contactConnection: $pendingConnection) .navigationTitle("New chat") .modifier(ThemedBackground(grouped: true)) .navigationBarTitleDisplayMode(.large) @@ -122,7 +120,7 @@ struct NewChatSheet: View { } } NavigationLink(isActive: $isScanPasteLinkActive) { - NewChatView(selection: .connect, showQRCodeScanner: true, parentAlert: $alert) + NewChatView(selection: .connect, showQRCodeScanner: true, parentAlert: $alert, contactConnection: $pendingConnection) .navigationTitle("New chat") .modifier(ThemedBackground(grouped: true)) .navigationBarTitleDisplayMode(.large) diff --git a/apps/ios/Shared/Views/NewChat/NewChatView.swift b/apps/ios/Shared/Views/NewChat/NewChatView.swift index c9670aa44a..4ca33e674d 100644 --- a/apps/ios/Shared/Views/NewChat/NewChatView.swift +++ b/apps/ios/Shared/Views/NewChat/NewChatView.swift @@ -45,18 +45,47 @@ enum NewChatOption: Identifiable { var id: Self { self } } +func cleanupPendingConnection(contactConnection: PendingContactConnection?) -> SomeAlert? { + var alert: SomeAlert? = nil + + if !(ChatModel.shared.showingInvitation?.connChatUsed ?? true), + let conn = contactConnection { + alert = SomeAlert( + alert: Alert( + title: Text("Keep unused invitation?"), + message: Text("You can view invitation link again in connection details."), + primaryButton: .default(Text("Keep")) {}, + secondaryButton: .destructive(Text("Delete")) { + Task { + await deleteChat(Chat( + chatInfo: .contactConnection(contactConnection: conn), + chatItems: [] + )) + } + } + ), + id: "keepUnusedInvitation" + ) + } + + ChatModel.shared.showingInvitation = nil + + return alert +} + struct NewChatView: View { @EnvironmentObject var m: ChatModel @EnvironmentObject var theme: AppTheme @State var selection: NewChatOption @State var showQRCodeScanner = false @State private var invitationUsed: Bool = false - @State private var contactConnection: PendingContactConnection? = nil @State private var connReqInvitation: String = "" @State private var creatingConnReq = false + @State var choosingProfile = false @State private var pastedLink: String = "" @State private var alert: NewChatViewAlert? @Binding var parentAlert: SomeAlert? + @Binding var contactConnection: PendingContactConnection? var body: some View { VStack(alignment: .leading) { @@ -68,6 +97,11 @@ struct NewChatView: View { } .pickerStyle(.segmented) .padding() + .onChange(of: $selection.wrappedValue) { opt in + if opt == NewChatOption.connect { + showQRCodeScanner = true + } + } VStack { // it seems there's a bug in iOS 15 if several views in switch (or if-else) statement have different transitions @@ -122,26 +156,10 @@ struct NewChatView: View { } } .onDisappear { - if !(m.showingInvitation?.connChatUsed ?? true), - let conn = contactConnection { - parentAlert = SomeAlert( - alert: Alert( - title: Text("Keep unused invitation?"), - message: Text("You can view invitation link again in connection details."), - primaryButton: .default(Text("Keep")) {}, - secondaryButton: .destructive(Text("Delete")) { - Task { - await deleteChat(Chat( - chatInfo: .contactConnection(contactConnection: conn), - chatItems: [] - )) - } - } - ), - id: "keepUnusedInvitation" - ) + if !choosingProfile { + parentAlert = cleanupPendingConnection(contactConnection: contactConnection) + contactConnection = nil } - m.showingInvitation = nil } .alert(item: $alert) { a in switch(a) { @@ -159,7 +177,8 @@ struct NewChatView: View { InviteView( invitationUsed: $invitationUsed, contactConnection: $contactConnection, - connReqInvitation: connReqInvitation + connReqInvitation: $connReqInvitation, + choosingProfile: $choosingProfile ) } else if creatingConnReq { creatingLinkProgressView() @@ -210,12 +229,22 @@ struct NewChatView: View { } } +private func incognitoProfileImage() -> some View { + Image(systemName: "theatermasks.fill") + .resizable() + .scaledToFit() + .frame(width: 30) + .foregroundColor(.indigo) +} + private struct InviteView: View { @EnvironmentObject var chatModel: ChatModel @EnvironmentObject var theme: AppTheme @Binding var invitationUsed: Bool @Binding var contactConnection: PendingContactConnection? - var connReqInvitation: String + @Binding var connReqInvitation: String + @Binding var choosingProfile: Bool + @AppStorage(GROUP_DEFAULT_INCOGNITO, store: groupDefaults) private var incognitoDefault = false var body: some View { @@ -226,28 +255,40 @@ private struct InviteView: View { .listRowInsets(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 10)) qrCodeView() - - Section { - IncognitoToggle(incognitoEnabled: $incognitoDefault) - } footer: { - sharedProfileInfo(incognitoDefault) - .foregroundColor(theme.colors.secondary) + if let selectedProfile = chatModel.currentUser { + Section { + NavigationLink { + ActiveProfilePicker( + contactConnection: $contactConnection, + connReqInvitation: $connReqInvitation, + incognitoEnabled: $incognitoDefault, + choosingProfile: $choosingProfile, + selectedProfile: selectedProfile + ) + } label: { + HStack { + if incognitoDefault { + incognitoProfileImage() + Text("Incognito") + } else { + ProfileImage(imageStr: chatModel.currentUser?.image, size: 30) + Text(chatModel.currentUser?.chatViewName ?? "") + } + } + } + } header: { + Text("Share profile").foregroundColor(theme.colors.secondary) + } footer: { + if incognitoDefault { + Text("A new random profile will be shared.") + } + } } } .onChange(of: incognitoDefault) { incognito in - Task { - do { - if let contactConn = contactConnection, - let conn = try await apiSetConnectionIncognito(connId: contactConn.pccConnId, incognito: incognito) { - await MainActor.run { - contactConnection = conn - chatModel.updateContactConnection(conn) - } - } - } catch { - logger.error("apiSetConnectionIncognito error: \(responseError(error))") - } - } + setInvitationUsed() + } + .onChange(of: chatModel.currentUser) { u in setInvitationUsed() } } @@ -270,6 +311,7 @@ private struct InviteView: View { private func qrCodeView() -> some View { Section(header: Text("Or show this code").foregroundColor(theme.colors.secondary)) { SimpleXLinkQRCode(uri: connReqInvitation, onShare: setInvitationUsed) + .id("simplex-qrcode-view-for-\(connReqInvitation)") .padding() .background( RoundedRectangle(cornerRadius: 12, style: .continuous) @@ -289,6 +331,257 @@ private struct InviteView: View { } } +private enum ProfileSwitchStatus { + case switchingUser + case switchingIncognito + case idle +} + +private struct ActiveProfilePicker: View { + @Environment(\.dismiss) var dismiss + @EnvironmentObject var chatModel: ChatModel + @EnvironmentObject var theme: AppTheme + @Binding var contactConnection: PendingContactConnection? + @Binding var connReqInvitation: String + @Binding var incognitoEnabled: Bool + @Binding var choosingProfile: Bool + @State private var alert: SomeAlert? + @State private var profileSwitchStatus: ProfileSwitchStatus = .idle + @State private var switchingProfileByTimeout = false + @State private var lastSwitchingProfileByTimeoutCall: Double? + @State private var profiles: [User] = [] + @State private var searchTextOrPassword = "" + @State private var showIncognitoSheet = false + @State private var incognitoFirst: Bool = false + @State var selectedProfile: User + var trimmedSearchTextOrPassword: String { searchTextOrPassword.trimmingCharacters(in: .whitespaces)} + + var body: some View { + viewBody() + .navigationTitle("Select chat profile") + .searchable(text: $searchTextOrPassword, placement: .navigationBarDrawer(displayMode: .always)) + .autocorrectionDisabled(true) + .navigationBarTitleDisplayMode(.large) + .onAppear { + profiles = chatModel.users + .map { $0.user } + .sorted { u, _ in u.activeUser } + } + .onChange(of: incognitoEnabled) { incognito in + if profileSwitchStatus != .switchingIncognito { + return + } + + Task { + do { + if let contactConn = contactConnection, + let conn = try await apiSetConnectionIncognito(connId: contactConn.pccConnId, incognito: incognito) { + await MainActor.run { + contactConnection = conn + chatModel.updateContactConnection(conn) + profileSwitchStatus = .idle + dismiss() + } + } + } catch { + profileSwitchStatus = .idle + incognitoEnabled = !incognito + logger.error("apiSetConnectionIncognito error: \(responseError(error))") + let err = getErrorAlert(error, "Error changing to incognito!") + + alert = SomeAlert( + alert: Alert( + title: Text(err.title), + message: Text(err.message ?? "Error: \(responseError(error))") + ), + id: "setConnectionIncognitoError" + ) + } + } + } + .onChange(of: profileSwitchStatus) { sp in + if sp != .idle { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + switchingProfileByTimeout = profileSwitchStatus != .idle + } + } else { + switchingProfileByTimeout = false + } + } + .onChange(of: selectedProfile) { profile in + if (profileSwitchStatus != .switchingUser) { + return + } + Task { + do { + if let contactConn = contactConnection, + let conn = try await apiChangeConnectionUser(connId: contactConn.pccConnId, userId: profile.userId) { + + await MainActor.run { + contactConnection = conn + connReqInvitation = conn.connReqInv ?? "" + incognitoEnabled = false + chatModel.updateContactConnection(conn) + } + do { + try await changeActiveUserAsync_(profile.userId, viewPwd: profile.hidden ? trimmedSearchTextOrPassword : nil ) + await MainActor.run { + profileSwitchStatus = .idle + dismiss() + } + } catch { + await MainActor.run { + profileSwitchStatus = .idle + alert = SomeAlert( + alert: Alert( + title: Text("Error switching profile"), + message: Text("Your connection was moved to \(profile.chatViewName) but an unexpected error occurred while redirecting you to the profile.") + ), + id: "switchingProfileError" + ) + } + } + } + } catch { + await MainActor.run { + profileSwitchStatus = .idle + if let currentUser = chatModel.currentUser { + selectedProfile = currentUser + } + let err = getErrorAlert(error, "Error changing connection profile") + alert = SomeAlert( + alert: Alert( + title: Text(err.title), + message: Text(err.message ?? "Error: \(responseError(error))") + ), + id: "changeConnectionUserError" + ) + } + } + } + } + .alert(item: $alert) { a in + a.alert + } + .onAppear { + incognitoFirst = incognitoEnabled + choosingProfile = true + } + .onDisappear { + choosingProfile = false + } + .sheet(isPresented: $showIncognitoSheet) { + IncognitoHelp() + } + } + + + @ViewBuilder private func viewBody() -> some View { + profilePicker() + .allowsHitTesting(!switchingProfileByTimeout) + .modifier(ThemedBackground(grouped: true)) + .overlay { + if switchingProfileByTimeout { + ProgressView() + .scaleEffect(2) + .frame(maxWidth: .infinity, maxHeight: .infinity) + } + } + } + + private func filteredProfiles() -> [User] { + let s = trimmedSearchTextOrPassword + let lower = s.localizedLowercase + + return profiles.filter { u in + if (u.activeUser || !u.hidden) && (s == "" || u.chatViewName.localizedLowercase.contains(lower)) { + return true + } + return correctPassword(u, s) + } + } + + @ViewBuilder private func profilerPickerUserOption(_ user: User) -> some View { + Button { + if selectedProfile == user && incognitoEnabled { + incognitoEnabled = false + profileSwitchStatus = .switchingIncognito + } else if selectedProfile != user { + selectedProfile = user + profileSwitchStatus = .switchingUser + } + } label: { + HStack { + ProfileImage(imageStr: user.image, size: 30) + .padding(.trailing, 2) + Text(user.chatViewName) + .foregroundColor(theme.colors.onBackground) + .lineLimit(1) + Spacer() + if selectedProfile == user, !incognitoEnabled { + Image(systemName: "checkmark") + .resizable().scaledToFit().frame(width: 16) + .foregroundColor(theme.colors.primary) + } + } + } + } + + @ViewBuilder private func profilePicker() -> some View { + let incognitoOption = Button { + if !incognitoEnabled { + incognitoEnabled = true + profileSwitchStatus = .switchingIncognito + } + } label : { + HStack { + incognitoProfileImage() + Text("Incognito") + .foregroundColor(theme.colors.onBackground) + Image(systemName: "info.circle") + .foregroundColor(theme.colors.primary) + .font(.system(size: 14)) + .onTapGesture { + showIncognitoSheet = true + } + Spacer() + if incognitoEnabled { + Image(systemName: "checkmark") + .resizable().scaledToFit().frame(width: 16) + .foregroundColor(theme.colors.primary) + } + } + } + + List { + let filteredProfiles = filteredProfiles() + let activeProfile = filteredProfiles.first { u in u.activeUser } + + if let selectedProfile = activeProfile { + let otherProfiles = filteredProfiles.filter { u in u.userId != activeProfile?.userId } + + if incognitoFirst { + incognitoOption + profilerPickerUserOption(selectedProfile) + } else { + profilerPickerUserOption(selectedProfile) + incognitoOption + } + + ForEach(otherProfiles) { p in + profilerPickerUserOption(p) + } + } else { + incognitoOption + ForEach(filteredProfiles) { p in + profilerPickerUserOption(p) + } + } + } + .opacity(switchingProfileByTimeout ? 0.4 : 1) + } +} + private struct ConnectView: View { @Environment(\.dismiss) var dismiss: DismissAction @EnvironmentObject var theme: AppTheme @@ -404,6 +697,7 @@ struct ScannerInView: View { .frame(maxWidth: .infinity, maxHeight: .infinity) .foregroundColor(Color.clear) switch cameraAuthorizationStatus { + case .authorized, nil: EmptyView() case .restricted: Text("Camera not available") case .denied: Label("Enable camera access", systemImage: "camera") default: Label("Tap to scan", systemImage: "qrcode") @@ -423,21 +717,26 @@ struct ScannerInView: View { .disabled(cameraAuthorizationStatus == .restricted) } } - .onAppear { + .task { let status = AVCaptureDevice.authorizationStatus(for: .video) cameraAuthorizationStatus = status if showQRCodeScanner { switch status { - case .notDetermined: askCameraAuthorization() + case .notDetermined: await askCameraAuthorizationAsync() case .restricted: showQRCodeScanner = false case .denied: showQRCodeScanner = false case .authorized: () - @unknown default: askCameraAuthorization() + @unknown default: await askCameraAuthorizationAsync() } } } } + func askCameraAuthorizationAsync() async { + await AVCaptureDevice.requestAccess(for: .video) + cameraAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: .video) + } + func askCameraAuthorization(_ cb: (() -> Void)? = nil) { AVCaptureDevice.requestAccess(for: .video) { allowed in cameraAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: .video) @@ -976,10 +1275,12 @@ func connReqSentAlert(_ type: ConnReqType) -> Alert { struct NewChatView_Previews: PreviewProvider { static var previews: some View { @State var parentAlert: SomeAlert? + @State var contactConnection: PendingContactConnection? = nil NewChatView( selection: .invite, - parentAlert: $parentAlert + parentAlert: $parentAlert, + contactConnection: $contactConnection ) } } diff --git a/apps/ios/Shared/Views/NewChat/QRCode.swift b/apps/ios/Shared/Views/NewChat/QRCode.swift index e3bae9287a..bc1dc4b5bc 100644 --- a/apps/ios/Shared/Views/NewChat/QRCode.swift +++ b/apps/ios/Shared/Views/NewChat/QRCode.swift @@ -49,34 +49,34 @@ struct QRCode: View { ZStack { if let image = image { qrCodeImage(image) - } - GeometryReader { geo in - ZStack { - if withLogo { - let w = geo.size.width - Image("icon-light") - .resizable() - .scaledToFit() - .frame(width: w * 0.16, height: w * 0.16) - .frame(width: w * 0.165, height: w * 0.165) - .background(.white) - .clipShape(Circle()) + GeometryReader { geo in + ZStack { + if withLogo { + let w = geo.size.width + Image("icon-light") + .resizable() + .scaledToFit() + .frame(width: w * 0.16, height: w * 0.16) + .frame(width: w * 0.165, height: w * 0.165) + .background(.white) + .clipShape(Circle()) + } } - } - .onAppear { - makeScreenshotFunc = { - let size = CGSizeMake(1024 / UIScreen.main.scale, 1024 / UIScreen.main.scale) - showShareSheet(items: [makeScreenshot(geo.frame(in: .local).origin, size)]) - onShare?() + .onAppear { + makeScreenshotFunc = { + let size = CGSizeMake(1024 / UIScreen.main.scale, 1024 / UIScreen.main.scale) + showShareSheet(items: [makeScreenshot(geo.frame(in: .local).origin, size)]) + onShare?() + } } + .frame(width: geo.size.width, height: geo.size.height) } - .frame(width: geo.size.width, height: geo.size.height) + } else { + Color.clear.aspectRatio(1, contentMode: .fit) } } .onTapGesture(perform: makeScreenshotFunc) - .onAppear { - image = image ?? generateImage(uri, tintColor: tintColor) - } + .task { image = await generateImage(uri, tintColor: tintColor) } .frame(maxWidth: .infinity, maxHeight: .infinity) } } @@ -89,7 +89,7 @@ private func qrCodeImage(_ image: UIImage) -> some View { .textSelection(.enabled) } -private func generateImage(_ uri: String, tintColor: UIColor) -> UIImage? { +private func generateImage(_ uri: String, tintColor: UIColor) async -> UIImage? { let context = CIContext() let filter = CIFilter.qrCodeGenerator() filter.message = Data(uri.utf8) diff --git a/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift b/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift index ed3adcfe7d..2ae4aa8c2b 100644 --- a/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift +++ b/apps/ios/Shared/Views/Onboarding/WhatsNewView.swift @@ -467,6 +467,39 @@ private let versionDescriptions: [VersionDescription] = [ ) ] ), + VersionDescription( + version: "v6.1", + post: URL(string: "https://simplex.chat/blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.html"), + features: [ + FeatureDescription( + icon: "checkmark.shield", + title: "Better security ✅", + description: "SimpleX protocols reviewed by Trail of Bits." + ), + FeatureDescription( + icon: "video", + title: "Better calls", + description: "Switch audio and video during the call." + ), + FeatureDescription( + icon: "bolt", + title: "Better notifications", + description: "Improved delivery, reduced traffic usage.\nMore improvements are coming soon!" + ), + FeatureDescription( + icon: nil, + title: "Better user experience", + description: nil, + subfeatures: [ + ("link", "Switch chat profile for 1-time invitations."), + ("message", "Customizable message shape."), + ("calendar", "Better message dates."), + ("arrowshape.turn.up.right", "Forward up to 20 messages at once."), + ("flag", "Delete or moderate up to 200 messages.") + ] + ), + ] + ), ] private let lastVersion = versionDescriptions.last!.version diff --git a/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift b/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift index be063334d3..b99c054abb 100644 --- a/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift +++ b/apps/ios/Shared/Views/RemoteAccess/ConnectDesktopView.swift @@ -14,7 +14,6 @@ struct ConnectDesktopView: View { @EnvironmentObject var m: ChatModel @EnvironmentObject var theme: AppTheme @Environment(\.dismiss) var dismiss: DismissAction - var viaSettings = false @AppStorage(DEFAULT_DEVICE_NAME_FOR_REMOTE_ACCESS) private var deviceName = UIDevice.current.name @AppStorage(DEFAULT_CONFIRM_REMOTE_SESSIONS) private var confirmRemoteSessions = false @AppStorage(DEFAULT_CONNECT_REMOTE_VIA_MULTICAST) private var connectRemoteViaMulticast = true @@ -57,23 +56,6 @@ struct ConnectDesktopView: View { } var body: some View { - if viaSettings { - viewBody - .modifier(BackButton(label: "Back", disabled: Binding.constant(false)) { - if m.activeRemoteCtrl { - alert = .disconnectDesktop(action: .back) - } else { - dismiss() - } - }) - } else { - NavigationView { - viewBody - } - } - } - - var viewBody: some View { Group { let discovery = m.remoteCtrlSession?.discovery if discovery == true || (discovery == nil && !showConnectScreen) { diff --git a/apps/ios/Shared/Views/TerminalView.swift b/apps/ios/Shared/Views/TerminalView.swift index d209ced128..36c05ed43d 100644 --- a/apps/ios/Shared/Views/TerminalView.swift +++ b/apps/ios/Shared/Views/TerminalView.swift @@ -160,7 +160,7 @@ struct TerminalView_Previews: PreviewProvider { let chatModel = ChatModel() chatModel.terminalItems = [ .resp(.now, ChatResponse.response(type: "contactSubscribed", json: "{}")), - .resp(.now, ChatResponse.response(type: "newChatItem", json: "{}")) + .resp(.now, ChatResponse.response(type: "newChatItems", json: "{}")) ] return NavigationView { TerminalView() diff --git a/apps/ios/Shared/Views/UserSettings/AdvancedNetworkSettings.swift b/apps/ios/Shared/Views/UserSettings/AdvancedNetworkSettings.swift index 9884c6e877..754ca3cf6b 100644 --- a/apps/ios/Shared/Views/UserSettings/AdvancedNetworkSettings.swift +++ b/apps/ios/Shared/Views/UserSettings/AdvancedNetworkSettings.swift @@ -196,11 +196,14 @@ struct AdvancedNetworkSettings: View { if developerTools { Section { Picker("Transport isolation", selection: $netCfg.sessionMode) { - ForEach(TransportSessionMode.values, id: \.self) { Text($0.text) } + let modes = TransportSessionMode.values.contains(netCfg.sessionMode) + ? TransportSessionMode.values + : TransportSessionMode.values + [netCfg.sessionMode] + ForEach(modes, id: \.self) { Text($0.text) } } .frame(height: 36) } footer: { - Text(sessionModeInfo(netCfg.sessionMode)) + sessionModeInfo(netCfg.sessionMode) .foregroundColor(theme.colors.secondary) } } @@ -353,10 +356,13 @@ struct AdvancedNetworkSettings: View { } } - private func sessionModeInfo(_ mode: TransportSessionMode) -> LocalizedStringKey { - switch mode { - case .user: return "A separate TCP connection will be used **for each chat profile you have in the app**." - case .entity: return "A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail." + private func sessionModeInfo(_ mode: TransportSessionMode) -> Text { + let userMode = Text("A separate TCP connection will be used **for each chat profile you have in the app**.") + return switch mode { + case .user: userMode + case .session: userMode + Text("\n") + Text("New SOCKS credentials will be used every time you start the app.") + case .server: userMode + Text("\n") + Text("New SOCKS credentials will be used for each server.") + case .entity: Text("A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail.") } } diff --git a/apps/ios/Shared/Views/UserSettings/AppSettings.swift b/apps/ios/Shared/Views/UserSettings/AppSettings.swift index 19260ce573..aa7f885ac6 100644 --- a/apps/ios/Shared/Views/UserSettings/AppSettings.swift +++ b/apps/ios/Shared/Views/UserSettings/AppSettings.swift @@ -58,6 +58,8 @@ extension AppSettings { profileImageCornerRadiusGroupDefault.set(val) def.setValue(val, forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) } + if let val = uiChatItemRoundness { def.setValue(val, forKey: DEFAULT_CHAT_ITEM_ROUNDNESS)} + if let val = uiChatItemTail { def.setValue(val, forKey: DEFAULT_CHAT_ITEM_TAIL)} if let val = uiColorScheme { currentThemeDefault.set(val) } if let val = uiDarkColorScheme { systemDarkThemeDefault.set(val) } if let val = uiCurrentThemeIds { currentThemeIdsDefault.set(val) } @@ -91,6 +93,8 @@ extension AppSettings { c.iosCallKitEnabled = callKitEnabledGroupDefault.get() c.iosCallKitCallsInRecents = def.bool(forKey: DEFAULT_CALL_KIT_CALLS_IN_RECENTS) c.uiProfileImageCornerRadius = def.double(forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) + c.uiChatItemRoundness = def.double(forKey: DEFAULT_CHAT_ITEM_ROUNDNESS) + c.uiChatItemTail = def.bool(forKey: DEFAULT_CHAT_ITEM_TAIL) c.uiColorScheme = currentThemeDefault.get() c.uiDarkColorScheme = systemDarkThemeDefault.get() c.uiCurrentThemeIds = currentThemeIdsDefault.get() diff --git a/apps/ios/Shared/Views/UserSettings/AppearanceSettings.swift b/apps/ios/Shared/Views/UserSettings/AppearanceSettings.swift index 73a789f108..ab3388bfce 100644 --- a/apps/ios/Shared/Views/UserSettings/AppearanceSettings.swift +++ b/apps/ios/Shared/Views/UserSettings/AppearanceSettings.swift @@ -33,6 +33,8 @@ struct AppearanceSettings: View { }() @State private var darkModeTheme: String = UserDefaults.standard.string(forKey: DEFAULT_SYSTEM_DARK_THEME) ?? DefaultTheme.DARK.themeName @AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var profileImageCornerRadius = defaultProfileImageCorner + @AppStorage(DEFAULT_CHAT_ITEM_ROUNDNESS) private var chatItemRoundness = defaultChatItemRoundness + @AppStorage(DEFAULT_CHAT_ITEM_TAIL) private var chatItemTail = true @AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = true @AppStorage(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial @@ -179,6 +181,14 @@ struct AppearanceSettings: View { } } + Section(header: Text("Message shape").foregroundColor(theme.colors.secondary)) { + HStack { + Text("Corner") + Slider(value: $chatItemRoundness, in: 0...1, step: 0.05) + } + Toggle("Tail", isOn: $chatItemTail) + } + Section(header: Text("Profile images").foregroundColor(theme.colors.secondary)) { HStack(spacing: 16) { if let img = m.currentUser?.image, img != "" { @@ -358,20 +368,21 @@ struct ChatThemePreview: View { let bob = ChatItem.getSample(2, CIDirection.directSnd, Date.now, NSLocalizedString("Good morning!", comment: "message preview"), quotedItem: CIQuote.getSample(alice.id, alice.meta.itemTs, alice.content.text, chatDir: alice.chatDir)) HStack { ChatItemView(chat: Chat.sampleData, chatItem: alice, revealed: Binding.constant(false)) - .modifier(ChatItemClipped()) + .modifier(ChatItemClipped(alice, tailVisible: true)) Spacer() } HStack { Spacer() ChatItemView(chat: Chat.sampleData, chatItem: bob, revealed: Binding.constant(false)) - .modifier(ChatItemClipped()) + .modifier(ChatItemClipped(bob, tailVisible: true)) .frame(alignment: .trailing) } } else { Rectangle().fill(.clear) } } - .padding(10) + .padding(.vertical, 10) + .padding(.horizontal, 16) .frame(maxWidth: .infinity) if let wallpaperType, let wallpaperImage = wallpaperType.image, let backgroundColor, let tintColor { @@ -797,7 +808,7 @@ struct ThemeDestinationPicker: View { @Binding var customizeThemeIsOpen: Bool var body: some View { - let values = [(nil, "All profiles")] + m.users.filter { $0.user.activeUser }.map { ($0.user.userId, $0.user.chatViewName)} + let values = [(nil, NSLocalizedString("All profiles", comment: "profile dropdown"))] + m.users.filter { $0.user.activeUser }.map { ($0.user.userId, $0.user.chatViewName)} if values.contains(where: { (userId, text) in userId == themeUserDestination?.0 }) { Picker("Apply to", selection: $themeUserDest) { diff --git a/apps/ios/Shared/Views/UserSettings/IncognitoHelp.swift b/apps/ios/Shared/Views/UserSettings/IncognitoHelp.swift index a0250afddf..d9862aaac8 100644 --- a/apps/ios/Shared/Views/UserSettings/IncognitoHelp.swift +++ b/apps/ios/Shared/Views/UserSettings/IncognitoHelp.swift @@ -26,6 +26,7 @@ struct IncognitoHelp: View { Text("Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).") } .listRowBackground(Color.clear) + .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) } .modifier(ThemedBackground()) } diff --git a/apps/ios/Shared/Views/UserSettings/PreferencesView.swift b/apps/ios/Shared/Views/UserSettings/PreferencesView.swift index 0c10da2103..bd8171623a 100644 --- a/apps/ios/Shared/Views/UserSettings/PreferencesView.swift +++ b/apps/ios/Shared/Views/UserSettings/PreferencesView.swift @@ -32,6 +32,17 @@ struct PreferencesView: View { .disabled(currentPreferences == preferences) } } + .onDisappear { + if currentPreferences != preferences { + showAlert( + title: NSLocalizedString("Your chat preferences", comment: "alert title"), + message: NSLocalizedString("Chat preferences were changed.", comment: "alert message"), + buttonTitle: NSLocalizedString("Save", comment: "alert button"), + buttonAction: savePreferences, + cancelButton: true + ) + } + } } private func featureSection(_ feature: ChatFeature, _ allowFeature: Binding) -> some View { diff --git a/apps/ios/Shared/Views/UserSettings/SettingsView.swift b/apps/ios/Shared/Views/UserSettings/SettingsView.swift index 5fb44e1ef8..a7938b86c3 100644 --- a/apps/ios/Shared/Views/UserSettings/SettingsView.swift +++ b/apps/ios/Shared/Views/UserSettings/SettingsView.swift @@ -47,6 +47,8 @@ let DEFAULT_ACCENT_COLOR_GREEN = "accentColorGreen" // deprecated, only used for let DEFAULT_ACCENT_COLOR_BLUE = "accentColorBlue" // deprecated, only used for migration let DEFAULT_USER_INTERFACE_STYLE = "userInterfaceStyle" // deprecated, only used for migration let DEFAULT_PROFILE_IMAGE_CORNER_RADIUS = "profileImageCornerRadius" +let DEFAULT_CHAT_ITEM_ROUNDNESS = "chatItemRoundness" +let DEFAULT_CHAT_ITEM_TAIL = "chatItemTail" let DEFAULT_ONE_HAND_UI_CARD_SHOWN = "oneHandUICardShown" let DEFAULT_TOOLBAR_MATERIAL = "toolbarMaterial" let DEFAULT_CONNECT_VIA_LINK_TAB = "connectViaLinkTab" @@ -77,6 +79,8 @@ let DEFAULT_NETWORK_PROXY = "networkProxy" let ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN = "androidCallOnLockScreen" +let defaultChatItemRoundness: Double = 0.75 + let appDefaults: [String: Any] = [ DEFAULT_SHOW_LA_NOTICE: false, DEFAULT_LA_NOTICE_SHOWN: false, @@ -100,6 +104,8 @@ let appDefaults: [String: Any] = [ DEFAULT_DEVELOPER_TOOLS: false, DEFAULT_ENCRYPTION_STARTED: false, DEFAULT_PROFILE_IMAGE_CORNER_RADIUS: defaultProfileImageCorner, + DEFAULT_CHAT_ITEM_ROUNDNESS: defaultChatItemRoundness, + DEFAULT_CHAT_ITEM_TAIL: true, DEFAULT_ONE_HAND_UI_CARD_SHOWN: false, DEFAULT_TOOLBAR_MATERIAL: ToolbarMaterial.defaultMaterial, DEFAULT_CONNECT_VIA_LINK_TAB: ConnectViaLinkTab.scan.rawValue, @@ -251,10 +257,10 @@ let networkProxyDefault: CodableDefault = CodableDefault(defaults: struct SettingsView: View { @Environment(\.colorScheme) var colorScheme + @Environment(\.dismiss) var dismiss @EnvironmentObject var chatModel: ChatModel @EnvironmentObject var sceneDelegate: SceneDelegate @EnvironmentObject var theme: AppTheme - @Binding var showSettings: Bool @State private var showProgress: Bool = false var body: some View { @@ -263,71 +269,12 @@ struct SettingsView: View { if showProgress { progressView() } - if let la = chatModel.laRequest { - LocalAuthView(authRequest: la) - } } } @ViewBuilder func settingsView() -> some View { let user = chatModel.currentUser - NavigationView { List { - Section(header: Text("You").foregroundColor(theme.colors.secondary)) { - if let user = user { - NavigationLink { - UserProfile() - .navigationTitle("Your current profile") - .modifier(ThemedBackground()) - } label: { - ProfilePreview(profileOf: user) - .padding(.leading, -8) - } - } - - NavigationLink { - UserProfilesView(showSettings: $showSettings) - } label: { - settingsRow("person.crop.rectangle.stack", color: theme.colors.secondary) { Text("Your chat profiles") } - } - - - if let user = user { - NavigationLink { - UserAddressView(shareViaProfile: user.addressShared) - .navigationTitle("SimpleX address") - .modifier(ThemedBackground(grouped: true)) - .navigationBarTitleDisplayMode(.large) - } label: { - settingsRow("qrcode", color: theme.colors.secondary) { Text("Your SimpleX address") } - } - - NavigationLink { - PreferencesView(profile: user.profile, preferences: user.fullPreferences, currentPreferences: user.fullPreferences) - .navigationTitle("Your preferences") - .modifier(ThemedBackground(grouped: true)) - } label: { - settingsRow("switch.2", color: theme.colors.secondary) { Text("Chat preferences") } - } - } - - NavigationLink { - ConnectDesktopView(viaSettings: true) - } label: { - settingsRow("desktopcomputer", color: theme.colors.secondary) { Text("Use from desktop") } - } - - NavigationLink { - MigrateFromDevice(showSettings: $showSettings, showProgressOnSettings: $showProgress) - .navigationTitle("Migrate device") - .modifier(ThemedBackground(grouped: true)) - .navigationBarTitleDisplayMode(.large) - } label: { - settingsRow("tray.and.arrow.up", color: theme.colors.secondary) { Text("Migrate to another device") } - } - } - .disabled(chatModel.chatRunning != true) - Section(header: Text("Settings").foregroundColor(theme.colors.secondary)) { NavigationLink { NotificationsView() @@ -378,14 +325,24 @@ struct SettingsView: View { } .disabled(chatModel.chatRunning != true) } - - chatDatabaseRow() } + Section(header: Text("Chat database").foregroundColor(theme.colors.secondary)) { + chatDatabaseRow() + NavigationLink { + MigrateFromDevice(showProgressOnSettings: $showProgress) + .navigationTitle("Migrate device") + .modifier(ThemedBackground(grouped: true)) + .navigationBarTitleDisplayMode(.large) + } label: { + settingsRow("tray.and.arrow.up", color: theme.colors.secondary) { Text("Migrate to another device") } + } + } + Section(header: Text("Help").foregroundColor(theme.colors.secondary)) { if let user = user { NavigationLink { - ChatHelp(showSettings: $showSettings) + ChatHelp(dismissSettingsSheet: dismiss) .navigationTitle("Welcome \(user.displayName)!") .modifier(ThemedBackground()) .frame(maxHeight: .infinity, alignment: .top) @@ -410,7 +367,7 @@ struct SettingsView: View { } settingsRow("number", color: theme.colors.secondary) { Button("Send questions and ideas") { - showSettings = false + dismiss() DispatchQueue.main.async { UIApplication.shared.open(simplexTeamURL) } @@ -459,16 +416,15 @@ struct SettingsView: View { } .navigationTitle("Your settings") .modifier(ThemedBackground(grouped: true)) - } - .onDisappear { - chatModel.showingTerminal = false - chatModel.terminalItems = [] - } + .onDisappear { + chatModel.showingTerminal = false + chatModel.terminalItems = [] + } } private func chatDatabaseRow() -> some View { NavigationLink { - DatabaseView(showSettings: $showSettings, chatItemTTL: chatModel.chatItemTTL) + DatabaseView(dismissSettingsSheet: dismiss, chatItemTTL: chatModel.chatItemTTL) .navigationTitle("Your chat database") .modifier(ThemedBackground(grouped: true)) } label: { @@ -546,26 +502,25 @@ struct ProfilePreview: View { HStack { ProfileImage(imageStr: profileOf.image, size: 44, color: color) .padding(.trailing, 6) - .padding(.vertical, 6) - VStack(alignment: .leading) { - Text(profileOf.displayName) - .fontWeight(.bold) - .font(.title2) - if profileOf.fullName != "" && profileOf.fullName != profileOf.displayName { - Text(profileOf.fullName) - } - } + profileName(profileOf).lineLimit(1) } } } +func profileName(_ profileOf: NamedChat) -> Text { + var t = Text(profileOf.displayName).fontWeight(.semibold).font(.title2) + if profileOf.fullName != "" && profileOf.fullName != profileOf.displayName { + t = t + Text(" (" + profileOf.fullName + ")") +// .font(.callout) + } + return t +} + struct SettingsView_Previews: PreviewProvider { static var previews: some View { let chatModel = ChatModel() chatModel.currentUser = User.sampleData - @State var showSettings = false - - return SettingsView(showSettings: $showSettings) + return SettingsView() .environmentObject(chatModel) } } diff --git a/apps/ios/Shared/Views/UserSettings/UserAddressView.swift b/apps/ios/Shared/Views/UserSettings/UserAddressView.swift index fa95c51d36..2469dc59db 100644 --- a/apps/ios/Shared/Views/UserSettings/UserAddressView.swift +++ b/apps/ios/Shared/Views/UserSettings/UserAddressView.swift @@ -14,7 +14,6 @@ struct UserAddressView: View { @Environment(\.dismiss) var dismiss: DismissAction @EnvironmentObject private var chatModel: ChatModel @EnvironmentObject var theme: AppTheme - @State var viaCreateLinkView = false @State var shareViaProfile = false @State private var aas = AutoAcceptState() @State private var savedAAS = AutoAcceptState() @@ -22,7 +21,6 @@ struct UserAddressView: View { @State private var showMailView = false @State private var mailViewResult: Result? = nil @State private var alert: UserAddressAlert? - @State private var showSaveDialogue = false @State private var progressIndicator = false @FocusState private var keyboardVisible: Bool @@ -44,26 +42,19 @@ struct UserAddressView: View { var body: some View { ZStack { - if viaCreateLinkView { - userAddressScrollView() - } else { - userAddressScrollView() - .modifier(BackButton(disabled: Binding.constant(false)) { - if savedAAS == aas { - dismiss() - } else { - keyboardVisible = false - showSaveDialogue = true - } - }) - .confirmationDialog("Save settings?", isPresented: $showSaveDialogue) { - Button("Save auto-accept settings") { - saveAAS() - dismiss() - } - Button("Exit without saving") { dismiss() } + userAddressScrollView() + .onDisappear { + if savedAAS != aas { + showAlert( + title: NSLocalizedString("Auto-accept settings", comment: "alert title"), + message: NSLocalizedString("Settings were changed.", comment: "alert message"), + buttonTitle: NSLocalizedString("Save", comment: "alert button"), + buttonAction: saveAAS, + cancelButton: true + ) } - } + } + if progressIndicator { ZStack { if chatModel.userAddress != nil { @@ -342,7 +333,7 @@ struct UserAddressView: View { } } } - + private struct AutoAcceptState: Equatable { var enable = false var incognito = false @@ -447,6 +438,8 @@ struct UserAddressView_Previews: PreviewProvider { static var previews: some View { let chatModel = ChatModel() chatModel.userAddress = UserContactLink(connReqContact: "https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D") + + return Group { UserAddressView() .environmentObject(chatModel) diff --git a/apps/ios/Shared/Views/UserSettings/UserProfile.swift b/apps/ios/Shared/Views/UserSettings/UserProfile.swift index 198fd495bd..8a70efbe82 100644 --- a/apps/ios/Shared/Views/UserSettings/UserProfile.swift +++ b/apps/ios/Shared/Views/UserSettings/UserProfile.swift @@ -11,8 +11,11 @@ import SimpleXChat struct UserProfile: View { @EnvironmentObject var chatModel: ChatModel + @EnvironmentObject var theme: AppTheme + @AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var radius = defaultProfileImageCorner @State private var profile = Profile(displayName: "", fullName: "") - @State private var editProfile = false + @State private var currentProfileHash: Int? + // Modals @State private var showChooseSource = false @State private var showImagePicker = false @State private var showTakePhoto = false @@ -21,85 +24,86 @@ struct UserProfile: View { @FocusState private var focusDisplayName var body: some View { - let user: User = chatModel.currentUser! - - return VStack(alignment: .leading) { - Text("Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile.") - .padding(.bottom) - - if editProfile { - ZStack(alignment: .center) { - ZStack(alignment: .topTrailing) { + List { + Group { + if profile.image != nil { + ZStack(alignment: .bottomTrailing) { + ZStack(alignment: .topTrailing) { + profileImageView(profile.image) + .onTapGesture { showChooseSource = true } + overlayButton("multiply", edge: .top) { profile.image = nil } + } + overlayButton("camera", edge: .bottom) { showChooseSource = true } + } + } else { + ZStack(alignment: .center) { profileImageView(profile.image) - if user.image != nil { - Button { - profile.image = nil - } label: { - Image(systemName: "multiply") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 12) - } + editImageButton { showChooseSource = true } + } + } + } + .frame(maxWidth: .infinity, alignment: .center) + .listRowBackground(Color.clear) + .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + .padding(.top) + .contentShape(Rectangle()) + + Section { + HStack { + TextField("Enter your name…", text: $profile.displayName) + .focused($focusDisplayName) + if !validDisplayName(profile.displayName) { + Button { + alert = .invalidNameError(validName: mkValidName(profile.displayName)) + } label: { + Image(systemName: "exclamationmark.circle").foregroundColor(.red) } } - - editImageButton { showChooseSource = true } } - .frame(maxWidth: .infinity, alignment: .center) - - VStack(alignment: .leading) { - ZStack(alignment: .leading) { - if !validNewProfileName(user) { - Button { - alert = .invalidNameError(validName: mkValidName(profile.displayName)) - } label: { - Image(systemName: "exclamationmark.circle").foregroundColor(.red) - } - } else { - Image(systemName: "exclamationmark.circle").foregroundColor(.clear) - } - profileNameTextEdit("Profile name", $profile.displayName) - .focused($focusDisplayName) - } - .padding(.bottom) - if showFullName(user) { - profileNameTextEdit("Full name (optional)", $profile.fullName) - .padding(.bottom) - } - HStack(spacing: 20) { - Button("Cancel") { editProfile = false } - Button("Save (and notify contacts)") { saveProfile() } - .disabled(!canSaveProfile(user)) - } + if let user = chatModel.currentUser, showFullName(user) { + TextField("Full name (optional)", text: $profile.fullName) } - .frame(maxWidth: .infinity, minHeight: 120, alignment: .leading) - } else { - ZStack(alignment: .center) { - profileImageView(user.image) - .onTapGesture { startEditingImage(user) } + } footer: { + Text("Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile.") + } - if user.image == nil { - editImageButton { startEditingImage(user) } - } + Section { + Button(action: getCurrentProfile) { + Text("Reset") } - .frame(maxWidth: .infinity, alignment: .center) - - VStack(alignment: .leading) { - profileNameView("Profile name:", user.profile.displayName) - if showFullName(user) { - profileNameView("Full name:", user.profile.fullName) - } - Button("Edit") { - profile = fromLocalProfile(user.profile) - editProfile = true - focusDisplayName = true - } + .disabled(currentProfileHash == profile.hashValue) + Button(action: saveProfile) { + Text("Save (and notify contacts)") } - .frame(maxWidth: .infinity, minHeight: 120, alignment: .leading) + .disabled(!canSaveProfile) } } - .padding() - .frame(maxHeight: .infinity, alignment: .top) + // Lifecycle + .onAppear { + getCurrentProfile() + } + .onDisappear { + if canSaveProfile { + showAlert( + title: NSLocalizedString("Save your profile?", comment: "alert title"), + message: NSLocalizedString("Your profile was changed. If you save it, the updated profile will be sent to all your contacts.", comment: "alert message"), + buttonTitle: NSLocalizedString("Save (and notify contacts)", comment: "alert button"), + buttonAction: saveProfile, + cancelButton: true + ) + } + } + .onChange(of: chosenImage) { image in + Task { + let resized: String? = if let image { + await resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500) + } else { + nil + } + await MainActor.run { profile.image = resized } + } + } + // Modals .confirmationDialog("Profile image", isPresented: $showChooseSource, titleVisibility: .visible) { Button("Take picture") { showTakePhoto = true @@ -126,57 +130,49 @@ struct UserProfile: View { } } } - .onChange(of: chosenImage) { image in - if let image = image { - profile.image = resizeImageToStrSize(cropToSquare(image), maxDataSize: 12500) - } else { - profile.image = nil - } - } .alert(item: $alert) { a in userProfileAlert(a, $profile.displayName) } } - func profileNameTextEdit(_ label: LocalizedStringKey, _ name: Binding) -> some View { - TextField(label, text: name) - .padding(.leading, 32) - } - - func profileNameView(_ label: LocalizedStringKey, _ name: String) -> some View { - HStack { - Text(label) - Text(name).fontWeight(.bold) - } - .padding(.bottom) - } - - func startEditingImage(_ user: User) { - profile = fromLocalProfile(user.profile) - editProfile = true - showChooseSource = true - } - - private func validNewProfileName(_ user: User) -> Bool { - profile.displayName == user.profile.displayName || validDisplayName(profile.displayName.trimmingCharacters(in: .whitespaces)) + @ViewBuilder + private func overlayButton( + _ systemName: String, + edge: Edge.Set, + action: @escaping () -> Void + ) -> some View { + Image(systemName: systemName) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(height: 12) + .foregroundColor(theme.colors.primary) + .padding(6) + .frame(width: 36, height: 36, alignment: .center) + .background(radius >= 20 ? Color.clear : theme.colors.background.opacity(0.5)) + .clipShape(Circle()) + .contentShape(Circle()) + .padding([.trailing, edge], -12) + .onTapGesture(perform: action) } private func showFullName(_ user: User) -> Bool { user.profile.fullName != "" && user.profile.fullName != user.profile.displayName } - - private func canSaveProfile(_ user: User) -> Bool { - profile.displayName.trimmingCharacters(in: .whitespaces) != "" && validNewProfileName(user) + + private var canSaveProfile: Bool { + currentProfileHash != profile.hashValue && + profile.displayName.trimmingCharacters(in: .whitespaces) != "" && + validDisplayName(profile.displayName) } - func saveProfile() { + private func saveProfile() { + focusDisplayName = false Task { do { profile.displayName = profile.displayName.trimmingCharacters(in: .whitespaces) if let (newProfile, _) = try await apiUpdateProfile(profile: profile) { - DispatchQueue.main.async { + await MainActor.run { chatModel.updateCurrentUser(newProfile) - profile = newProfile + getCurrentProfile() } - editProfile = false } else { alert = .duplicateUserError } @@ -185,6 +181,13 @@ struct UserProfile: View { } } } + + private func getCurrentProfile() { + if let user = chatModel.currentUser { + profile = fromLocalProfile(user.profile) + currentProfileHash = profile.hashValue + } + } } func profileImageView(_ imageStr: String?) -> some View { @@ -201,19 +204,3 @@ func editImageButton(action: @escaping () -> Void) -> some View { .frame(width: 48) } } - -struct UserProfile_Previews: PreviewProvider { - static var previews: some View { - let chatModel1 = ChatModel() - chatModel1.currentUser = User.sampleData - let chatModel2 = ChatModel() - chatModel2.currentUser = User.sampleData - chatModel2.currentUser?.profile.image = "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBMRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAAqACAAQAAAABAAAAgKADAAQAAAABAAAAgAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/+ICNElDQ19QUk9GSUxFAAEBAAACJGFwcGwEAAAAbW50clJHQiBYWVogB+EABwAHAA0AFgAgYWNzcEFQUEwAAAAAQVBQTAAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA0y1hcHBsyhqVgiV/EE04mRPV0eoVggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKZGVzYwAAAPwAAABlY3BydAAAAWQAAAAjd3RwdAAAAYgAAAAUclhZWgAAAZwAAAAUZ1hZWgAAAbAAAAAUYlhZWgAAAcQAAAAUclRSQwAAAdgAAAAgY2hhZAAAAfgAAAAsYlRSQwAAAdgAAAAgZ1RSQwAAAdgAAAAgZGVzYwAAAAAAAAALRGlzcGxheSBQMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0ZXh0AAAAAENvcHlyaWdodCBBcHBsZSBJbmMuLCAyMDE3AABYWVogAAAAAAAA81EAAQAAAAEWzFhZWiAAAAAAAACD3wAAPb////+7WFlaIAAAAAAAAEq/AACxNwAACrlYWVogAAAAAAAAKDgAABELAADIuXBhcmEAAAAAAAMAAAACZmYAAPKnAAANWQAAE9AAAApbc2YzMgAAAAAAAQxCAAAF3v//8yYAAAeTAAD9kP//+6L///2jAAAD3AAAwG7/wAARCACAAIADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9sAQwABAQEBAQECAQECAwICAgMEAwMDAwQGBAQEBAQGBwYGBgYGBgcHBwcHBwcHCAgICAgICQkJCQkLCwsLCwsLCwsL/9sAQwECAgIDAwMFAwMFCwgGCAsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL/90ABAAI/9oADAMBAAIRAxEAPwD4N1TV59SxpunRtBb/APPP/lo+eMsf4R+uKyxNa6Y32a3UTzjoi8Ip9/8AOfYV0tx4d1a8VlsojaWo6uThj+Pb6Cs2CCGyP2LQ4xPIMBpGIVVz7ngV+Ap31P2C1iSDQbnWXRtVYyMT8kSDkZ9B29zXXReD7ZVOkX0QlLgg2ycjBH8ZHXPoOK9O8L6LpljZidWMjyqMzAdc/wB3PJ+p4qjrPiuxs1a38LwLJIn35ScoP94jlm9hxW8ZKJm1fY/Gv4yeA/E37L3xf07xz4GuH0260+7i1bRLpDkwzQOHVfQ+WwAI7r1zmv7fv2Nv2nfCv7YH7PHh346+FwkD6nEYtRs1OTZ6jBhbiA98K/zJnrGynvX8u3x3+G6fFvwXcadcOZNTQebZyN1EgH3QB91W6H657VD/AMEYP2qdQ/Zb/aRuPgN8RpjZeFviJcJabJztWy1tPkgkOeFE3+ok9zGTwtfY5Nj1Vjyt6nzuZ4XlfMj+zamH5TupVYnhhgjsaRyMYNe8eEMC7jxxU+1SMYFQFyaevPWgRqaeuSVFb0SDgAZI/SsLS9w4kxux1HTNdTEAMDvQJst20UitvA4rotMh8ycbuAv6k1Rs3UgcHjrXc6Xb2iTKVIJPQEcZ96qKMW7nWabpNmzRyEE9wOlegtplzFCLiMbEcfKw5/XP51l6ZPK6b2SJsdd64A/Kr0t5fyRsqsPLU5baNo49P0q2I//Q8iuPD17eeTpVy32u2ufls5lAC5P8MmOA2O/Q/XIrHl+GWn+CGN7qyC9ugxkSID92nvz1+pwK/TKb9j34t3Pw/PjXXrpdR165L3F7pkiDz5RISzHzFIUzliXKBQCTgMGwD8P6zompRzR2V2xuLWV9sE7ggo4yPLlBxhgRgE8k8cHivyPPMl9g3iMMrw6r+X/gH6PlmZ+1tRrP3uj7/wDBPnjXdR1rXWDao5jtm4S3h43gf3jwSPyH1rW0Xw9f6uyw2MYSNAAT/Ag/qa9ii+GTWEv2nV8nfztH3m/+t/nirMsVtMPscGIYYuCqjj8fWvmo+9qz227aI5O38NeH/DeJIGE079ZW9fQf/W/Ovyx/ba+C1x/aR+K/h6FoLa5dUvDH8rRzj7kgI+7ux253DPev1yuINKtF3XriOMDlm+83+6O1eNePZoPH2h3ngWC032N7E0UhI7HuPcdQfWvQweJdKakjkxFFTjZn6+f8Eu/2yE/a+/Zss9R8TXCyeMvCpTSfECZ+eSZF/dXWPS5jG4n/AJ6Bx2r9JGbd0r+GX9jD476z/wAE5v20IL3xPM7eGdUZdK8QBeUewmYGO6A7tbviT127171/cfaXdve28d1aSJNFKqukiHcjqwyGUjggggg9xX6Dhq6q01JM+NxVF05tdCyRQCOvakY4GRTFYd66DmN2xk2sK6eE5+YVxlo5EwB4rrLZiTyePWgmSOmsAThCcZPFdxZ5KruJyprgrWQ5G3tXS21+FABzVrYyZ6ZZTTSqCR8vQ4rUudWgW1e3QMrBScj1/D+tcpp1+UXaOn09fWtKP7OAzNjK+tNiP//R/oYjkSW9NgqsWVA7HHyrk4AJ9Tzx6CvjL9qz4M+FrbRrn4q2s0Fjcs6R3ttKdsd+ZCFBUf8APx0xj/WAYOCA1fVF58Y/hbb/AAwPxlXWIH8OCHzhdKc57bAv3vM3fLsxu3cYzX58eGdH8f8A7b/xIHi/xOs2k+DNGkK28AOCgPVQejXMg++/IiU7RyefmI+Z79+qPl++0JpR/wATG7Z9M4WOQfeVv7srdT/snp+NeWa9bfZXez8KxCZQcGVhiJT/AOzH6fnX7K/Fn9mfwzf6N9r+GmnwWV3DF5UlmBiC8iAxtbPAkx0c/e6N/eH5s+IvDcuj2jWcUTJYwsYXDrtktHXgxuvBxngE9Oh9/is6yVUr4nDL3Oq7enl+R9Plmac9qNZ+90ff/gnybLoheT7XrM3nMo5JH8h2HtXJa9/aGoMYbAC0gTqwH7x1H8hXsHiWGDRUboqr/Eeck+nrXj9/d3twWmlzbQHnn77e/tXzaqXXuntuNtz4z/ay+Eul+NPAf9u+H4TLq2kqzEAfNLAeXU/T7w/Ed6/XL/giD+2n/wALr+Ck37Nnjq78zxV8PYkW0Z2+a60VjthbJ5LWzfuW/wBjyz3NfCGuJLLm30tSsT8OT/U1+b1v4w8VfsE/tXeHf2kfhqjz2Vvcl5rdDiO4tZflu7Q+zoSUz0baeq19RkWMUZexk/Q8LNMLzx51uf3yIxPXvTQuTkVw3wz+IfhH4seBNG+JngS7W+0XX7OG/sp1P34ZlDLn0Izhh2YEGu+LAHFfXo+XJ4P9cp6YNdbCWHFcerFSCK6OGcMBk0wOmtZMVswurDNcnHKB7VqxXbDGKaZEoncRXpt4iy8fWlN44XdM5+bGPauWbUAI9p5NeH/E39oTwF8OAdO1W6+06kfuWVuQ0vtvOcIPdiPalOrGC5pOyHToym7RV2f/0nXmiaPrF/ceJvC1hrUnhC11EyFGZsIN2Mtg+QLjy+A5GQcZI6V/QP8ABrWvhd4i+GmnXXwZeI6DAnkxRxgq0LL95JFb5hJnO7dyTz3qt4f8EeCPC3g5Pht4csYItKt4fKNngMpjfOd4PJLckk8k18FeKvBXj79kHxu/xW+ECte+F711XUtNdiVC54VvQj/lnL2+63FfNNqWh7rVtT9JdItdaitpV8QSxyy+a5VowVURE/KDnuB1PQ9a/OD4yfEbwv8AEP4rx6F8JNIfXb4QyQXMlqAwvmQgEBThSkQBUysQpyFBOBjE+NH7WWu/HtrH4QfACxvYpNZHl3bSr5M7kjLQqc/JGo5ml/u8DrX2X+z38A9C+B3hzyQUvNbvVX7dehcA7ekUQ/hiT+Fe/U81m1bVj1Px/wDiX4FXQ4b7WNItJXitXZLq3nU+fpzjqpQ87PQ88eowa+JdanuvP+03JzG3Kk87voP8a/pi+NPwStfiAo8V+GDHaeI7aPYsjj91dxj/AJYzjuOyv1X6V+Mfxk+By6eL7xPodhLE9kzDUNJYfvbSXqWUd4z147cjivjc3ybkviMMtOq7eaPo8tzXmtRrvXo/8z4aaC/1a3drrbDbr6nCgepPc+36V4T8Z/A/h7xz4KvPB8uGmcb4LhhxHKv3WUeh6HPY17TrMuo3dysUA3p0VUGEArCudFt7aH7bqjguOQP6V89SquLUk9T26lNNWZ7L/wAEJv2vNQ8L6xq/7BPxZma3ureafUPDHnHvy93Zg/X9/EO+XA7Cv6fFwRnNfwWftIWHi/wL4u0T9pX4Vu2ma74buobpJY+GEkDBo5CO4B+Vx3U4PFf2VfshftPeFf2tv2e/Dvx18LbYhq0G29tQcm0vovluID/uPkr6oVPev0TLsWq9FT69T43MMN7KpdbM+q1kA+WtuF8qCa5H7SD0qvrnjbw34L0KTxD4qvobCyhBLzTuFUY7DPU+wya7nNJXZwxu3ZHoqyqq5JxXnPxL+Nvw3+EemjUPHmqxWIbPlxcvNIR2WNcsfrjFflz8cf8AgpDJMZ/DvwKgwOVOq3S/rFGf0LV8MaZp/jf4j603ibxTdT3U053PdXRLu+eflB7fkK8PFZ5TheNHV/h/wT2cLlFSfvVNF+J+hnxI/ba8cfEa5fQfhnG+h6e5KCY/NeTD6jIjH0yfcV514W8HX2plrjUiWLEtIWbcSSOS7dST/k1x2g2PhrwdZhpyFbHzEnLk+5/oK6eDxRq2soYdPH2S0xjjh2H9K+erY+pVlzTdz3aWEhSjaCsf/9P+gafwFajxovjGKeVJSqrJEPuOVUoD7ZBGR32ivgn9pz9pHUfGOvP+zb8BIDrGr6kZLO/nhwUXH34UY/LwP9bJ91BxndxXyp41/ab/AGivht4c1D9mf+0La7vrOY6f/asUpe4WP7vlRzEhRnIHmMNyAkcEcfpB+zB+zBo37O/hQ3moBL3xLfxA312gyFA5EEOeRGp79Xb5j2x8wfQHyHZ/CP41fsg6lZ/GHT3tvEVvDC0WqxwIU8uGUqXXnnaCoIlHQj5vlOR+lPwv+Lngv4v+Gk8UeC7oTRBvLnib5ZYJcZKSL1B9D0YcgkU/QfEkXitbuzuLR7S5tGCTwS4bAfO3kcEEA5B/lg1+Yn7Qdtbfsd/E/TPiT8IdShs21jzDc6HIf3TRIQWyB0hYnCE8xt9044Ckr7k7H7AiUEf4V438U/hZa+O0TXNGkWy120XbDcEfJKn/ADxmA+8h7Hqp5HpWN8Efjv4N+OvhFfFHhOTy5otqXlnIR51tKRnaw7g9VccMOnOQPXZ71Yo2mdgiqMsWOAAOufasXoyrXPw++NX7P9zHdX174Q0wWOqW/wC81DSjjMe7J86HHDxtgnC5zzjkEV+Z3iOS20u7PlZupiT+9YYQH/ZWv6hvjRp3grXPAJ8c3t6lldabGZLC/j5be3KxY/jSUgAp+IwRkfzs/tYan4Vi+LM8nhzyo5bq2gnu4Iukd04PmDI6ZGGIHc18hnmW06K+s09LvVefkfRZTjZ1H7Cetlo/8z5d1bQk1m1ng1OMTRXCGOVX+7tbg5+tQf8ABPL9o/xV/wAE9vi/r3gDxhYahrPw18WSrMJbGMzvZXcYwkyxjn5k/dyr1OFI6VqBpJ8LdPiM9gOv0FWFTzJBFbJtzgADliT0H515uAzKphpNxV0z0sVhIVo8sj9rviP/AMFJPhxpuhJ/wqm2n1rUbhcqbmJreKLP95T8zEeg/GvzP8Y/Eb4vftA+Ije+Kb2XUWU/JCDstoAewH3Rj8TXmOi+HrJYTd63MII1OPLB+d8diev4DtXtWjeIrPTNNENtD9mjx8kY+V2H0/hH60YzNK2IdpPTsthYXL6VHWK17s2/C3gHQvDCLqPiKRZ7hei/wKfYdz7mu9/4TGa5lEGjREA8Z7/5+lec2Ntf65KLm+IjhXkZ4UCunt9X0zTONN56gu39K4k2dtlueh6Xpdxcz/a9UfMi84J4X+grv7fxNaaehi0oCWUDDSH7o+leNW99f30fls3l2+eT0z61oDVFgiEOngtgY3Y/kP61pEln/9T74+Ff/BPn4e6R8MnsPieWvfFF+haS+gkbbZM3RIQeHA/jLjMhznAwBufCz42+Mf2bPEsHwM/aNlMmiONmj6+cmIRg4Cuxz+7GQMn5oicNlcGvWf2ffiB418d/Dfwn4tvR9st9StTb3IVVUxSw8NK7E5O4qRgeo46msH9tXx78JfAfwS1CL4oQx30l8ki6XZ5Ama7VTtkQ9UWPIMjdNvynO4A/NHvnqP7Rn7Q/gX9nLwY3iXVGiudR1BS2n2aOA102PvkjpEowWfpjgcmviz9nH9njxT8afFEn7SX7TkJvJL8+bp+mXSfIUP3JJIyPljUf6qI9vmPOK+DfgboFl4V+LfhHxt+1DpWoW/he7iL6bJfRt9mLpgwOwbOYIyd23sSrFdvX+iZ7n7bY+fpkqHzU3RSj50IYZVuDhh34PIqG7bBufnr8Zv2fvF3wa8Vf8L8/ZgQ20sAJ1DR4lLRPF1fbGPvRHGWjHKn5kxjFe8fDD9qX4Q/FL4cXni/V7uHS2sIv+JpYXLgyQE/3RwZEc8Rso+bpwcive/E/irQPBOgXfizxTeJYafp8ZmnnkOFRR+pJPAA5J4GTX8uP7Uf7R3hHWPilqfjDwNpo02HVZ8wWqL84jAAaVlHAeUguVHAY/Unnq1oU6bnVdkuv6GtOlKclCmtWfQn7X37bl7qEqaB4HRbaCyXytOssgiBTgedL281hzg9Onrn8xl1eNpJNQ1C4M00zGSSV23M7HqST1Oa5K7Np44uf7Psmkubp3M0hCjcG9ZGzjn1r3fwR8LrDRokvNaIlmABw3IU/l1/yBXwWZY+eJnzS0itl/XU+tweEjh4WW73ZmaHpev8AiNhJCjW9vjh2+8w9hXqVnpukeGoFe4cqVIJdjyT2/X86W+8U2ljG1rpCiRxxu6jNeO+IrbX9amEzuwERy3rz9eB/M15jdztSPQhr7ahrEt/b/Ky8bXHIz0bn1HPP4CvW/CsEUKNqOqybQ3zZb77n2z/OvnvS2khv4r5wZLiLAUADbx6jvjtmvWNGinvbn7TqjlyRnGcjNNR0DmPTZtYuNSxb2KlY+w7fX3rd063toHDTAzSj+H/H0+lYulwz3Moislx2yOD+n9KzvF3xX8C/DCIwXbi+1NvuWsJzhj/fPRRxVRRV7ntNlp91eRm61F1hgUZOTtVawtT+JGiaQDYeF4hf3J+Uyn/VqT6dya+GNb+M3j74i339n3rx2ttG2PItwwT2yxALH6ce9e3eGLXyLFcofN24wf6nsPYU9gP/1fof9kb9uf4LeBf2QYLjxVctDrujNcIdJAImuJHkYoIiRjaejFsbMHI6Zf8As+/BTxt+1l4/X9qT9pSPdpW4NoukOCIpI0OYyUPS3Q8qDzK3zNkdfkv/AIJ4/s0ah+0xZWv7Q3xmjik8PCZvstqgwuoSQnYC3cwJtwSeZmBz8uc/vtp3iPQrm+k0LT50M9oMNCo27QuFIXgAheAdudp4ODXzeyPfbIviJ4C8I/FLwnceCPHFmLvTrkdOjxOPuyRt/A69iPocgkV+dehfEbxr+wf4ot/hz8W5ZtZ+Hd+7DS9VRCz2h67CvoM/PFnK/eTK5FfpHrviHR/DejXXiDxBdRWNhYxNPcXEzBI4o0GWZieAAK/mw/bP/bF1n9pvxTH4a8DxvD4X0mZjYRSAo88pBQ3Uw6jKkiOP+FSc/MxxhUqQpwc6jtFFU6cqk1GCu2W/26f269Y+Nutnwv4KElv4cs5M2ds/ytcOOPtE2O/9xP4R7kmvz00L4e614kvTqniKR087qf429h/dH616Zofg/S/D+dW16Xz7k/MXbr9AO3+ea2W1q8v/AN1pqeTE3AYj5iPb/P4V8DmWZzxU9NILZfq/M+uwWCjh495dWa2jWPh7wZaC10+FFfsqD5ifUnrn3/WpbibUtVI+0Psj/uA449z/AErPjtrTTI/tepybc8kE5Ymse78UXV0fL0hPIjHG89fw9K8u3c7W7Grd38WjOEt0Blx95v4c+i/41iW5ur+VmvHIG7IHTmqscK2ymaY5dhnLck/Qf41sWlqyqZp3EWevrRZCu2bdgoUiCIYOeT3zXp2hrp+nRfb9VmWCFerP1PsB3NeNz+K9O0eApYr58q/xN0B9f/1VzZ1q/wBQv/td07Mw6lvT2HRR+pockhpHp3jv4q6pdwnR/CObKBxgyf8ALZx7dxXz5p+i6tPqryW8WXYHLSgso7/Oe59s16Np9rNdXTG0Uh24Z++Pr2H5n6V6LZ22k+HoFudVcBs/LHjv7L1J9z+lRzGyiM8IeCI7fZfXKguFUGRjkcDnaD/WvQrrxNYaQo0rSYzLMR25wfUn/P0rift2ueJG2RB7S3PRV/1jD3PRRj/9ddh4b0C1iJKAY/MZPv8AxH9KhS1Lt3P/1v0M/YPkRP2ZNBhiARY3uVCqMAAStwAOwr6budO8L6Fe3PjW/dbUQRySzTSSlII12jzJGBIRTtQbnwOBya+Lf+CevizRdf8A2VNH1vS7lJbQT3hMmcBQshJ3Z+7t75xivy7/AG6/27G+OWpy/CP4WXTL4OgfE9wmQ2qyIeG7H7MrfcU48w4Y8bRXy9ScYRc5uyW59BGEpT5YrUs/tq/tm6r+0x4gPw3+G9xJa+CdPmDM/KNqMiHiVxwfKB5ijPX77c4C/GVlc2eip9h0SLz5z94noD/tH/J9hXJaTZXUkGxT5MA5YZxnPdm9/QV1j3WmeHoFkuPk4+Vf4mHsP4R7n8q+DzTMpYufLHSC2/zZ9XgcFHDxu/iZaj0i6uZDqGtThtvJzwoqrdeJY7RzbaYuSRw7Dt7f5xXE6h4kvNamG/5YgcqmcLj1Pc/X8qtLAwQGPDyPzk9B/n0ryuXsdzkW5LyS4k8+/kLsx4X/AB/wFdFYxXVwyxW6gMe55Ix6Cm6Z4et7JTqevzCJj1Zu/wBBUepeNba3t2svDcflL/FPJyT9BSsuormlcPYaJGHuGM0zcjJrk7vUbvUZwJD8vO1Rwo/Dv+Ncvda3AP3s7FpHOSzHLE+w7Utm+q6uTFZDyo8/Mx6/WomWkb+baDDTPlj0ReSPqRnFdBpukXeptv2iK3Xl3Y4RQPU1mWkFhpOQF+0XAwCO+TnAJ6L9OvtViJNV8RShdTcC2j5ESfLEvufU/Xn0rNstRPQI9QtwgsfCyiYr/wAvLjEQP+yv8X1P610mj+H0WcXWpO1xeMOWbl8fyQU3RbbMSiyG1EH+sbjgf3R2+tdbamytrc3KnbErANM3OWPOAP4iR0qGzdGotg2xbNBktjKJk/p1P48fSuziOn6DBtuj5twekYP3Sf7xH8q8/ttbvriUw6eGgSTv/wAtZB65/hH0P49qll1PS9FJF0RLP2jU5xn1qLiP/9k=" - return Group { - UserProfile() - .environmentObject(chatModel1) - UserProfile() - .environmentObject(chatModel2) - } - } -} diff --git a/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift b/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift index 160130bccc..330ce56e0b 100644 --- a/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift +++ b/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift @@ -9,7 +9,6 @@ import SimpleXChat struct UserProfilesView: View { @EnvironmentObject private var m: ChatModel @EnvironmentObject private var theme: AppTheme - @Binding var showSettings: Bool @Environment(\.editMode) private var editMode @AppStorage(DEFAULT_SHOW_HIDDEN_PROFILES_NOTICE) private var showHiddenProfilesNotice = true @AppStorage(DEFAULT_SHOW_MUTE_PROFILE_ALERT) private var showMuteProfileAlert = true @@ -96,8 +95,7 @@ struct UserProfilesView: View { } label: { Label("Add profile", systemImage: "plus") } - .frame(height: 44) - .padding(.vertical, 4) + .frame(height: 38) } } footer: { Text("Tap to activate profile.") @@ -285,7 +283,7 @@ struct UserProfilesView: View { await MainActor.run { onboardingStageDefault.set(.step1_SimpleXInfo) m.onboardingStage = .step1_SimpleXInfo - showSettings = false + dismissAllSheets() } } } else { @@ -308,14 +306,14 @@ struct UserProfilesView: View { Task { do { try await changeActiveUserAsync_(user.userId, viewPwd: userViewPassword(user)) + dismissAllSheets() } catch { await MainActor.run { alert = .activateUserError(error: responseError(error)) } } } } label: { HStack { - ProfileImage(imageStr: user.image, size: 44) - .padding(.vertical, 4) + ProfileImage(imageStr: user.image, size: 38) .padding(.trailing, 12) Text(user.chatViewName) Spacer() @@ -406,8 +404,15 @@ public func chatPasswordHash(_ pwd: String, _ salt: String) -> String { return hash } +public func correctPassword(_ user: User, _ pwd: String) -> Bool { + if let ph = user.viewPwdHash { + return pwd != "" && chatPasswordHash(pwd, ph.salt) == ph.hash + } + return false +} + struct UserProfilesView_Previews: PreviewProvider { static var previews: some View { - UserProfilesView(showSettings: Binding.constant(true)) + UserProfilesView() } } 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 419f0ae864..1a40820dce 100644 --- a/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff @@ -137,6 +137,10 @@ %@ иска да се свърже! notification title + + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ и %lld членове @@ -157,11 +161,31 @@ %d дни time interval + + %d file(s) are still being downloaded. + forward confirmation reason + + + %d file(s) failed to download. + forward confirmation reason + + + %d file(s) were deleted. + forward confirmation reason + + + %d file(s) were not downloaded. + forward confirmation reason + %d hours %d часа time interval + + %d messages not forwarded + alert title + %d min %d мин. @@ -713,7 +737,7 @@ All profiles - No comment provided by engineer. + profile dropdown All your contacts will remain connected. @@ -901,6 +925,10 @@ Кода за достъп до приложение се заменя с код за самоунищожение. No comment provided by engineer. + + App session + No comment provided by engineer. + App version Версия на приложението @@ -1003,6 +1031,10 @@ Автоматично приемане на изображения No comment provided by engineer. + + Auto-accept settings + alert title + Back Назад @@ -1027,11 +1059,19 @@ Лош хеш на съобщението No comment provided by engineer. + + Better calls + No comment provided by engineer. + Better groups По-добри групи No comment provided by engineer. + + Better message dates. + No comment provided by engineer. + Better messages По-добри съобщения @@ -1041,6 +1081,18 @@ Better networking No comment provided by engineer. + + Better notifications + No comment provided by engineer. + + + Better security ✅ + No comment provided by engineer. + + + Better user experience + No comment provided by engineer. + Black No comment provided by engineer. @@ -1167,7 +1219,7 @@ Cancel Отказ - No comment provided by engineer. + alert button Cancel migration @@ -1186,7 +1238,7 @@ Cannot receive file Файлът не може да бъде получен - No comment provided by engineer. + alert title Capacity exceeded - recipient did not receive previously sent messages. @@ -1310,6 +1362,15 @@ Чат настройки No comment provided by engineer. + + Chat preferences were changed. + alert message + + + Chat profile + Потребителски профил + No comment provided by engineer. + Chat theme No comment provided by engineer. @@ -1685,6 +1746,10 @@ This is your own one-time link! Версия на ядрото: v%@ No comment provided by engineer. + + Corner + No comment provided by engineer. + Correct name to %@? Поправи име на %@? @@ -1808,6 +1873,10 @@ This is your own one-time link! Персонализирано време No comment provided by engineer. + + Customizable message shape. + No comment provided by engineer. + Customize theme No comment provided by engineer. @@ -2097,6 +2166,10 @@ This is your own one-time link! Изтрий старата база данни? No comment provided by engineer. + + Delete or moderate up to 200 messages. + No comment provided by engineer. + Delete pending connection? Изтрий предстоящата връзка? @@ -2334,6 +2407,10 @@ This is your own one-time link! Не изпращай история на нови членове. No comment provided by engineer. + + Do not use credentials with proxy. + No comment provided by engineer. + Don't create address Не създавай адрес @@ -2357,7 +2434,8 @@ This is your own one-time link! Download Изтегли - chat item action + alert button + chat item action Download errors @@ -2373,6 +2451,10 @@ This is your own one-time link! Свали файл server test step + + Download files + alert action + Downloaded No comment provided by engineer. @@ -2645,6 +2727,10 @@ This is your own one-time link! Грешка при промяна на адреса No comment provided by engineer. + + Error changing connection profile + No comment provided by engineer. + Error changing role Грешка при промяна на ролята @@ -2655,6 +2741,10 @@ This is your own one-time link! Грешка при промяна на настройката No comment provided by engineer. + + Error changing to incognito! + No comment provided by engineer. + Error connecting to forwarding server %@. Please try later. No comment provided by engineer. @@ -2773,6 +2863,10 @@ This is your own one-time link! Грешка при зареждане на %@ сървъри No comment provided by engineer. + + Error migrating settings + No comment provided by engineer. + Error opening chat Грешка при отваряне на чата @@ -2781,7 +2875,7 @@ This is your own one-time link! Error receiving file Грешка при получаване на файл - No comment provided by engineer. + alert title Error reconnecting server @@ -2870,10 +2964,14 @@ This is your own one-time link! Грешка при спиране на чата No comment provided by engineer. + + Error switching profile + No comment provided by engineer. + Error switching profile! Грешка при смяна на профил! - No comment provided by engineer. + alertTitle Error synchronizing connection @@ -2918,8 +3016,7 @@ This is your own one-time link! Error: %@ Грешка: %@ - file error text - snd error text + alert message Error: URL is invalid @@ -3003,6 +3100,11 @@ This is your own one-time link! File error No comment provided by engineer. + + File errors: +%@ + alert message + File not found - most likely file was deleted or cancelled. file error text @@ -3133,11 +3235,27 @@ This is your own one-time link! Препрати chat item action + + Forward %d message(s)? + alert title + Forward and save messages Препращане и запазване на съобщения No comment provided by engineer. + + Forward messages + alert action + + + Forward messages without files? + alert message + + + Forward up to 20 messages at once. + No comment provided by engineer. + Forwarded Препратено @@ -3148,6 +3266,10 @@ This is your own one-time link! Препратено от No comment provided by engineer. + + Forwarding %lld messages + No comment provided by engineer. + Forwarding server %@ failed to connect to destination server %@. Please try later. No comment provided by engineer. @@ -3190,11 +3312,6 @@ Error: %2$@ Пълно име (незадължително) No comment provided by engineer. - - Full name: - Пълно име: - No comment provided by engineer. - Fully decentralized – visible only to members. Напълно децентрализирана – видима е само за членовете. @@ -3438,6 +3555,10 @@ Error: %2$@ ICE сървъри (по един на ред) No comment provided by engineer. + + IP address + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Ако не можете да се срещнете лично, покажете QR код във видеоразговора или споделете линка. @@ -3512,6 +3633,11 @@ Error: %2$@ Импортиране на архив No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + No comment provided by engineer. + Improved message delivery Подобрена доставка на съобщения @@ -4069,6 +4195,10 @@ This is your link for group %@! Message servers No comment provided by engineer. + + Message shape + No comment provided by engineer. + Message source remains private. Източникът на съобщението остава скрит. @@ -4115,6 +4245,10 @@ This is your link for group %@! Messages sent No comment provided by engineer. + + Messages were deleted after you selected them. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. Съобщенията, файловете и разговорите са защитени чрез **криптиране от край до край** с перфектна секретност при препращане, правдоподобно опровержение и възстановяване при взлом. @@ -4269,6 +4403,14 @@ This is your link for group %@! Нов kод за достъп No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + No comment provided by engineer. + New chat Нов чат @@ -4385,6 +4527,14 @@ This is your link for group %@! Няма мрежова връзка No comment provided by engineer. + + No permission to record speech + No comment provided by engineer. + + + No permission to record video + No comment provided by engineer. + No permission to record voice message Няма разрешение за запис на гласово съобщение @@ -4404,6 +4554,10 @@ This is your link for group %@! Nothing selected No comment provided by engineer. + + Nothing to forward! + alert title + Notifications Известия @@ -4436,7 +4590,7 @@ This is your link for group %@! Ok Ок - No comment provided by engineer. + alert button Old database @@ -4624,6 +4778,11 @@ Requires compatible VPN. Other %@ servers No comment provided by engineer. + + Other file errors: +%@ + alert message + PING count PING бройка @@ -4659,6 +4818,10 @@ Requires compatible VPN. Кодът за достъп е зададен! No comment provided by engineer. + + Password + No comment provided by engineer. + Password to show Парола за показване @@ -4803,6 +4966,10 @@ Error: %@ Полски интерфейс No comment provided by engineer. + + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Въжможно е пръстовият отпечатък на сертификата в адреса на сървъра да е неправилен @@ -4883,16 +5050,6 @@ Error: %@ Профилни изображения No comment provided by engineer. - - Profile name - Име на профила - No comment provided by engineer. - - - Profile name: - Име на профила: - No comment provided by engineer. - Profile password Профилна парола @@ -4989,6 +5146,10 @@ Enable in *Network & servers* settings. Proxied servers No comment provided by engineer. + + Proxy requires password + No comment provided by engineer. + Push notifications Push известия @@ -5200,6 +5361,10 @@ Enable in *Network & servers* settings. Премахване No comment provided by engineer. + + Remove archive? + No comment provided by engineer. + Remove image No comment provided by engineer. @@ -5373,6 +5538,10 @@ Enable in *Network & servers* settings. SMP server No comment provided by engineer. + + SOCKS proxy + No comment provided by engineer. + Safely receive files No comment provided by engineer. @@ -5385,12 +5554,13 @@ Enable in *Network & servers* settings. Save Запази - chat item action + alert button + chat item action Save (and notify contacts) Запази (и уведоми контактите) - No comment provided by engineer. + alert button Save and notify contact @@ -5416,11 +5586,6 @@ Enable in *Network & servers* settings. Запази архив No comment provided by engineer. - - Save auto-accept settings - Запази настройките за автоматично приемане - No comment provided by engineer. - Save group profile Запази профила на групата @@ -5456,16 +5621,15 @@ Enable in *Network & servers* settings. Запази сървърите? No comment provided by engineer. - - Save settings? - Запази настройките? - No comment provided by engineer. - Save welcome message? Запази съобщението при посрещане? No comment provided by engineer. + + Save your profile? + alert title + Saved Запазено @@ -5486,6 +5650,10 @@ Enable in *Network & servers* settings. Запазено съобщение message info title + + Saving %lld messages + No comment provided by engineer. + Scale No comment provided by engineer. @@ -5562,6 +5730,10 @@ Enable in *Network & servers* settings. Избери chat item action + + Select chat profile + No comment provided by engineer. + Selected %lld No comment provided by engineer. @@ -5674,7 +5846,7 @@ Enable in *Network & servers* settings. Sender cancelled file transfer. Подателят отмени прехвърлянето на файла. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -5766,6 +5938,10 @@ Enable in *Network & servers* settings. Sent via proxy No comment provided by engineer. + + Server + No comment provided by engineer. + Server address No comment provided by engineer. @@ -5877,6 +6053,10 @@ Enable in *Network & servers* settings. Настройки No comment provided by engineer. + + Settings were changed. + alert message + Shape profile images Променете формата на профилните изображения @@ -5911,6 +6091,10 @@ Enable in *Network & servers* settings. Сподели линк No comment provided by engineer. + + Share profile + No comment provided by engineer. + Share this 1-time invite link Сподели този еднократен линк за връзка @@ -6041,6 +6225,10 @@ Enable in *Network & servers* settings. Еднократна покана за SimpleX simplex link type + + SimpleX protocols reviewed by Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Опростен режим инкогнито @@ -6069,6 +6257,10 @@ Enable in *Network & servers* settings. Soft blur media + + Some app settings were not migrated. + No comment provided by engineer. + Some file(s) were not exported: No comment provided by engineer. @@ -6201,6 +6393,14 @@ Enable in *Network & servers* settings. Подкрепете SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + No comment provided by engineer. + System Системен @@ -6235,6 +6435,10 @@ Enable in *Network & servers* settings. TCP_KEEPINTVL No comment provided by engineer. + + Tail + No comment provided by engineer. + Take picture Направи снимка @@ -6423,6 +6627,10 @@ It can happen because of some bug or when the connection is compromised.Текстът, който поставихте, не е SimpleX линк за връзка. No comment provided by engineer. + + The uploaded database archive will be permanently removed from the servers. + No comment provided by engineer. + Themes No comment provided by engineer. @@ -6546,6 +6754,14 @@ You will be prompted to complete authentication before this feature is enabled.< Ще бъдете подканени да извършите идентификация, преди тази функция да бъде активирана. No comment provided by engineer. + + To record speech please grant permission to use Microphone. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. За да запишете гласово съобщение, моля, дайте разрешение за използване на микрофон. @@ -6694,7 +6910,7 @@ You will be prompted to complete authentication before this feature is enabled.< Unknown servers! - No comment provided by engineer. + alert title Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. @@ -6804,6 +7020,10 @@ To connect, please ask your contact to create another connection link and check Използвай .onion хостове No comment provided by engineer. + + Use SOCKS proxy + No comment provided by engineer. + Use SimpleX Chat servers? Използвай сървърите на SimpleX Chat? @@ -6866,15 +7086,14 @@ To connect, please ask your contact to create another connection link and check Use the app with one hand. No comment provided by engineer. - - User profile - Потребителски профил - No comment provided by engineer. - User selection No comment provided by engineer. + + Username + No comment provided by engineer. + Using SimpleX Chat servers. Използват се сървърите на SimpleX Chat. @@ -7104,7 +7323,7 @@ To connect, please ask your contact to create another connection link and check Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. - No comment provided by engineer. + alert message Wrong database passphrase @@ -7128,11 +7347,6 @@ To connect, please ask your contact to create another connection link and check XFTP server No comment provided by engineer. - - You - Вие - No comment provided by engineer. - You **must not** use the same database on two devices. **Не трябва** да използвате една и съща база данни на две устройства. @@ -7475,11 +7689,19 @@ Repeat connection request? Вашата чат база данни не е криптирана - задайте парола, за да я криптирате. No comment provided by engineer. + + Your chat preferences + alert title + Your chat profiles Вашите чат профили No comment provided by engineer. + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Вашият контакт изпрати файл, който е по-голям от поддържания в момента максимален размер (%@). @@ -7495,6 +7717,10 @@ Repeat connection request? Вашите контакти ще останат свързани. No comment provided by engineer. + + Your credentials may be sent unencrypted. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Вашата текуща чат база данни ще бъде ИЗТРИТА и ЗАМЕНЕНА с импортираната. @@ -7525,13 +7751,15 @@ Repeat connection request? Вашият профил **%@** ще бъде споделен. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Вашият профил се съхранява на вашето устройство и се споделя само с вашите контакти. -SimpleX сървърите не могат да видят вашия профил. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Вашият профил се съхранява на вашето устройство и се споделя само с вашите контакти. SimpleX сървърите не могат да видят вашия профил. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + alert message + Your profile, contacts and delivered messages are stored on your device. Вашият профил, контакти и доставени съобщения се съхраняват на вашето устройство. 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 a02203e630..af56b6631f 100644 --- a/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff +++ b/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff @@ -135,6 +135,10 @@ %@ se chce připojit! notification title + + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members No comment provided by engineer. @@ -154,11 +158,31 @@ %d dní time interval + + %d file(s) are still being downloaded. + forward confirmation reason + + + %d file(s) failed to download. + forward confirmation reason + + + %d file(s) were deleted. + forward confirmation reason + + + %d file(s) were not downloaded. + forward confirmation reason + %d hours %d hodin time interval + + %d messages not forwarded + alert title + %d min %d minuty @@ -691,7 +715,7 @@ All profiles - No comment provided by engineer. + profile dropdown All your contacts will remain connected. @@ -874,6 +898,10 @@ Přístupový kód aplikace je nahrazen sebedestrukčním přístupovým heslem. No comment provided by engineer. + + App session + No comment provided by engineer. + App version Verze aplikace @@ -973,6 +1001,10 @@ Automaticky přijímat obrázky No comment provided by engineer. + + Auto-accept settings + alert title + Back Zpět @@ -996,10 +1028,18 @@ Špatný hash zprávy No comment provided by engineer. + + Better calls + No comment provided by engineer. + Better groups No comment provided by engineer. + + Better message dates. + No comment provided by engineer. + Better messages Lepší zprávy @@ -1009,6 +1049,18 @@ Better networking No comment provided by engineer. + + Better notifications + No comment provided by engineer. + + + Better security ✅ + No comment provided by engineer. + + + Better user experience + No comment provided by engineer. + Black No comment provided by engineer. @@ -1127,7 +1179,7 @@ Cancel Zrušit - No comment provided by engineer. + alert button Cancel migration @@ -1145,7 +1197,7 @@ Cannot receive file Nelze přijmout soubor - No comment provided by engineer. + alert title Capacity exceeded - recipient did not receive previously sent messages. @@ -1266,6 +1318,15 @@ Předvolby chatu No comment provided by engineer. + + Chat preferences were changed. + alert message + + + Chat profile + Profil uživatele + No comment provided by engineer. + Chat theme No comment provided by engineer. @@ -1623,6 +1684,10 @@ This is your own one-time link! Verze jádra: v%@ No comment provided by engineer. + + Corner + No comment provided by engineer. + Correct name to %@? No comment provided by engineer. @@ -1738,6 +1803,10 @@ This is your own one-time link! Vlastní čas No comment provided by engineer. + + Customizable message shape. + No comment provided by engineer. + Customize theme No comment provided by engineer. @@ -2024,6 +2093,10 @@ This is your own one-time link! Smazat starou databázi? No comment provided by engineer. + + Delete or moderate up to 200 messages. + No comment provided by engineer. + Delete pending connection? Smazat čekající připojení? @@ -2255,6 +2328,10 @@ This is your own one-time link! Do not send history to new members. No comment provided by engineer. + + Do not use credentials with proxy. + No comment provided by engineer. + Don't create address Nevytvářet adresu @@ -2277,7 +2354,8 @@ This is your own one-time link! Download - chat item action + alert button + chat item action Download errors @@ -2292,6 +2370,10 @@ This is your own one-time link! Stáhnout soubor server test step + + Download files + alert action + Downloaded No comment provided by engineer. @@ -2552,6 +2634,10 @@ This is your own one-time link! Chuba změny adresy No comment provided by engineer. + + Error changing connection profile + No comment provided by engineer. + Error changing role Chyba při změně role @@ -2562,6 +2648,10 @@ This is your own one-time link! Chyba změny nastavení No comment provided by engineer. + + Error changing to incognito! + No comment provided by engineer. + Error connecting to forwarding server %@. Please try later. No comment provided by engineer. @@ -2678,6 +2768,10 @@ This is your own one-time link! Chyba načítání %@ serverů No comment provided by engineer. + + Error migrating settings + No comment provided by engineer. + Error opening chat No comment provided by engineer. @@ -2685,7 +2779,7 @@ This is your own one-time link! Error receiving file Chyba při příjmu souboru - No comment provided by engineer. + alert title Error reconnecting server @@ -2772,10 +2866,14 @@ This is your own one-time link! Chyba při zastavení chatu No comment provided by engineer. + + Error switching profile + No comment provided by engineer. + Error switching profile! Chyba při přepínání profilu! - No comment provided by engineer. + alertTitle Error synchronizing connection @@ -2818,8 +2916,7 @@ This is your own one-time link! Error: %@ Chyba: %@ - file error text - snd error text + alert message Error: URL is invalid @@ -2900,6 +2997,11 @@ This is your own one-time link! File error No comment provided by engineer. + + File errors: +%@ + alert message + File not found - most likely file was deleted or cancelled. file error text @@ -3026,10 +3128,26 @@ This is your own one-time link! Forward chat item action + + Forward %d message(s)? + alert title + Forward and save messages No comment provided by engineer. + + Forward messages + alert action + + + Forward messages without files? + alert message + + + Forward up to 20 messages at once. + No comment provided by engineer. + Forwarded No comment provided by engineer. @@ -3038,6 +3156,10 @@ This is your own one-time link! Forwarded from No comment provided by engineer. + + Forwarding %lld messages + No comment provided by engineer. + Forwarding server %@ failed to connect to destination server %@. Please try later. No comment provided by engineer. @@ -3079,11 +3201,6 @@ Error: %2$@ Celé jméno (volitelně) No comment provided by engineer. - - Full name: - Celé jméno: - No comment provided by engineer. - Fully decentralized – visible only to members. No comment provided by engineer. @@ -3321,6 +3438,10 @@ Error: %2$@ Servery ICE (jeden na řádek) No comment provided by engineer. + + IP address + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Pokud se nemůžete setkat osobně, zobrazte QR kód ve videohovoru nebo sdílejte odkaz. @@ -3393,6 +3514,11 @@ Error: %2$@ Importing archive No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + No comment provided by engineer. + Improved message delivery No comment provided by engineer. @@ -3928,6 +4054,10 @@ This is your link for group %@! Message servers No comment provided by engineer. + + Message shape + No comment provided by engineer. + Message source remains private. No comment provided by engineer. @@ -3971,6 +4101,10 @@ This is your link for group %@! Messages sent No comment provided by engineer. + + Messages were deleted after you selected them. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. No comment provided by engineer. @@ -4113,6 +4247,14 @@ This is your link for group %@! Nové heslo No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + No comment provided by engineer. + New chat No comment provided by engineer. @@ -4227,6 +4369,14 @@ This is your link for group %@! No network connection No comment provided by engineer. + + No permission to record speech + No comment provided by engineer. + + + No permission to record video + No comment provided by engineer. + No permission to record voice message Nemáte oprávnění nahrávat hlasové zprávy @@ -4245,6 +4395,10 @@ This is your link for group %@! Nothing selected No comment provided by engineer. + + Nothing to forward! + alert title + Notifications Oznámení @@ -4276,7 +4430,7 @@ This is your link for group %@! Ok Ok - No comment provided by engineer. + alert button Old database @@ -4456,6 +4610,11 @@ Vyžaduje povolení sítě VPN. Other %@ servers No comment provided by engineer. + + Other file errors: +%@ + alert message + PING count Počet PING @@ -4491,6 +4650,10 @@ Vyžaduje povolení sítě VPN. Heslo nastaveno! No comment provided by engineer. + + Password + No comment provided by engineer. + Password to show Heslo k zobrazení @@ -4627,6 +4790,10 @@ Error: %@ Polské rozhraní No comment provided by engineer. + + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Je možné, že otisk certifikátu v adrese serveru je nesprávný @@ -4705,14 +4872,6 @@ Error: %@ Profile images No comment provided by engineer. - - Profile name - No comment provided by engineer. - - - Profile name: - No comment provided by engineer. - Profile password Heslo profilu @@ -4808,6 +4967,10 @@ Enable in *Network & servers* settings. Proxied servers No comment provided by engineer. + + Proxy requires password + No comment provided by engineer. + Push notifications Nabízená oznámení @@ -5014,6 +5177,10 @@ Enable in *Network & servers* settings. Odstranit No comment provided by engineer. + + Remove archive? + No comment provided by engineer. + Remove image No comment provided by engineer. @@ -5181,6 +5348,10 @@ Enable in *Network & servers* settings. SMP server No comment provided by engineer. + + SOCKS proxy + No comment provided by engineer. + Safely receive files No comment provided by engineer. @@ -5192,12 +5363,13 @@ Enable in *Network & servers* settings. Save Uložit - chat item action + alert button + chat item action Save (and notify contacts) Uložit (a informovat kontakty) - No comment provided by engineer. + alert button Save and notify contact @@ -5223,11 +5395,6 @@ Enable in *Network & servers* settings. Uložit archiv No comment provided by engineer. - - Save auto-accept settings - Uložit nastavení automatického přijímání - No comment provided by engineer. - Save group profile Uložení profilu skupiny @@ -5263,16 +5430,15 @@ Enable in *Network & servers* settings. Uložit servery? No comment provided by engineer. - - Save settings? - Uložit nastavení? - No comment provided by engineer. - Save welcome message? Uložit uvítací zprávu? No comment provided by engineer. + + Save your profile? + alert title + Saved No comment provided by engineer. @@ -5290,6 +5456,10 @@ Enable in *Network & servers* settings. Saved message message info title + + Saving %lld messages + No comment provided by engineer. + Scale No comment provided by engineer. @@ -5363,6 +5533,10 @@ Enable in *Network & servers* settings. Vybrat chat item action + + Select chat profile + No comment provided by engineer. + Selected %lld No comment provided by engineer. @@ -5474,7 +5648,7 @@ Enable in *Network & servers* settings. Sender cancelled file transfer. Odesílatel zrušil přenos souboru. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -5566,6 +5740,10 @@ Enable in *Network & servers* settings. Sent via proxy No comment provided by engineer. + + Server + No comment provided by engineer. + Server address No comment provided by engineer. @@ -5675,6 +5853,10 @@ Enable in *Network & servers* settings. Nastavení No comment provided by engineer. + + Settings were changed. + alert message + Shape profile images No comment provided by engineer. @@ -5708,6 +5890,10 @@ Enable in *Network & servers* settings. Sdílet odkaz No comment provided by engineer. + + Share profile + No comment provided by engineer. + Share this 1-time invite link No comment provided by engineer. @@ -5834,6 +6020,10 @@ Enable in *Network & servers* settings. Jednorázová pozvánka SimpleX simplex link type + + SimpleX protocols reviewed by Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Zjednodušený inkognito režim @@ -5862,6 +6052,10 @@ Enable in *Network & servers* settings. Soft blur media + + Some app settings were not migrated. + No comment provided by engineer. + Some file(s) were not exported: No comment provided by engineer. @@ -5990,6 +6184,14 @@ Enable in *Network & servers* settings. Podpořte SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + No comment provided by engineer. + System Systém @@ -6024,6 +6226,10 @@ Enable in *Network & servers* settings. TCP_KEEPINTVL No comment provided by engineer. + + Tail + No comment provided by engineer. + Take picture Vyfotit @@ -6207,6 +6413,10 @@ Může se to stát kvůli nějaké chybě, nebo pokud je spojení kompromitován The text you pasted is not a SimpleX link. No comment provided by engineer. + + The uploaded database archive will be permanently removed from the servers. + No comment provided by engineer. + Themes No comment provided by engineer. @@ -6323,6 +6533,14 @@ You will be prompted to complete authentication before this feature is enabled.< Před zapnutím této funkce budete vyzváni k dokončení ověření. No comment provided by engineer. + + To record speech please grant permission to use Microphone. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Chcete-li nahrávat hlasové zprávy, udělte povolení k použití mikrofonu. @@ -6465,7 +6683,7 @@ Před zapnutím této funkce budete vyzváni k dokončení ověření. Unknown servers! - No comment provided by engineer. + alert title Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. @@ -6570,6 +6788,10 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu Použít hostitele .onion No comment provided by engineer. + + Use SOCKS proxy + No comment provided by engineer. + Use SimpleX Chat servers? Používat servery SimpleX Chat? @@ -6629,15 +6851,14 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu Use the app with one hand. No comment provided by engineer. - - User profile - Profil uživatele - No comment provided by engineer. - User selection No comment provided by engineer. + + Username + No comment provided by engineer. + Using SimpleX Chat servers. Používat servery SimpleX Chat. @@ -6850,7 +7071,7 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. - No comment provided by engineer. + alert message Wrong database passphrase @@ -6874,11 +7095,6 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu XFTP server No comment provided by engineer. - - You - Vy - No comment provided by engineer. - You **must not** use the same database on two devices. No comment provided by engineer. @@ -7204,11 +7420,19 @@ Repeat connection request? Vaše chat databáze není šifrována – nastavte přístupovou frázi pro její šifrování. No comment provided by engineer. + + Your chat preferences + alert title + Your chat profiles Vaše chat profily No comment provided by engineer. + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Kontakt odeslal soubor, který je větší než aktuálně podporovaná maximální velikost (%@). @@ -7224,6 +7448,10 @@ Repeat connection request? Vaše kontakty zůstanou připojeny. No comment provided by engineer. + + Your credentials may be sent unencrypted. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Vaše aktuální chat databáze bude ODSTRANĚNA a NAHRAZENA importovanou. @@ -7253,13 +7481,15 @@ Repeat connection request? Váš profil **%@** bude sdílen. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Váš profil je uložen ve vašem zařízení a sdílen pouze s vašimi kontakty. -Servery SimpleX nevidí váš profil. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Váš profil je uložen ve vašem zařízení a sdílen pouze s vašimi kontakty. Servery SimpleX nevidí váš profil. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + alert message + Your profile, contacts and delivered messages are stored on your device. Váš profil, kontakty a doručené zprávy jsou uloženy ve vašem zařízení. 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 29adfbabf0..7ab1e3a588 100644 --- a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff +++ b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff @@ -137,6 +137,11 @@ %@ will sich mit Ihnen verbinden! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ und %lld Mitglieder @@ -157,11 +162,36 @@ %d Tage time interval + + %d file(s) are still being downloaded. + %d Datei(en) wird/werden immer noch heruntergeladen. + forward confirmation reason + + + %d file(s) failed to download. + Bei %d Datei(en) ist das Herunterladen fehlgeschlagen. + forward confirmation reason + + + %d file(s) were deleted. + %d Datei(en) wurde(n) gelöscht. + forward confirmation reason + + + %d file(s) were not downloaded. + %d Datei(en) wurde(n) nicht heruntergeladen. + forward confirmation reason + %d hours %d Stunden time interval + + %d messages not forwarded + %d Nachrichten wurden nicht weitergeleitet + alert title + %d min %d min @@ -723,7 +753,7 @@ All profiles Alle Profile - No comment provided by engineer. + profile dropdown All your contacts will remain connected. @@ -915,6 +945,11 @@ App-Zugangscode wurde durch den Selbstzerstörungs-Zugangscode ersetzt. No comment provided by engineer. + + App session + App-Sitzung + No comment provided by engineer. + App version App Version @@ -1020,6 +1055,11 @@ Bilder automatisch akzeptieren No comment provided by engineer. + + Auto-accept settings + Einstellungen automatisch akzeptieren + alert title + Back Zurück @@ -1045,11 +1085,21 @@ Ungültiger Nachrichten-Hash No comment provided by engineer. + + Better calls + Verbesserte Anrufe + No comment provided by engineer. + Better groups Bessere Gruppen No comment provided by engineer. + + Better message dates. + Verbesserte Nachrichten-Datumsinformation + No comment provided by engineer. + Better messages Verbesserungen bei Nachrichten @@ -1060,6 +1110,21 @@ Kontrollieren Sie Ihr Netzwerk No comment provided by engineer. + + Better notifications + Verbesserte Benachrichtigungen + No comment provided by engineer. + + + Better security ✅ + Verbesserte Sicherheit ✅ + No comment provided by engineer. + + + Better user experience + Verbesserte Nutzer-Erfahrung + No comment provided by engineer. + Black Schwarz @@ -1193,7 +1258,7 @@ Cancel Abbrechen - No comment provided by engineer. + alert button Cancel migration @@ -1213,7 +1278,7 @@ Cannot receive file Datei kann nicht empfangen werden - No comment provided by engineer. + alert title Capacity exceeded - recipient did not receive previously sent messages. @@ -1341,6 +1406,16 @@ Chat-Präferenzen No comment provided by engineer. + + Chat preferences were changed. + Die Chat-Präferenzen wurden geändert. + alert message + + + Chat profile + Benutzerprofil + No comment provided by engineer. + Chat theme Chat-Design @@ -1393,22 +1468,22 @@ Clear - Löschen + Entfernen swipe action Clear conversation - Chatinhalte löschen + Chat-Inhalte entfernen No comment provided by engineer. Clear conversation? - Unterhaltung löschen? + Chat-Inhalte entfernen? No comment provided by engineer. Clear private notes? - Private Notizen löschen? + Private Notizen entfernen? No comment provided by engineer. @@ -1722,7 +1797,7 @@ Das ist Ihr eigener Einmal-Link! Conversation deleted! - Unterhaltung gelöscht! + Chat-Inhalte entfernt! No comment provided by engineer. @@ -1740,6 +1815,11 @@ Das ist Ihr eigener Einmal-Link! Core Version: v%@ No comment provided by engineer. + + Corner + Abrundung Ecken + No comment provided by engineer. + Correct name to %@? Richtiger Name für %@? @@ -1865,6 +1945,11 @@ Das ist Ihr eigener Einmal-Link! Zeit anpassen No comment provided by engineer. + + Customizable message shape. + Anpassbares Format des Nachrichtenfelds + No comment provided by engineer. + Customize theme Design anpassen @@ -2159,6 +2244,11 @@ Das ist Ihr eigener Einmal-Link! Alte Datenbank löschen? No comment provided by engineer. + + Delete or moderate up to 200 messages. + Bis zu 200 Nachrichten löschen oder moderieren + No comment provided by engineer. + Delete pending connection? Ausstehende Verbindung löschen? @@ -2409,6 +2499,11 @@ Das ist Ihr eigener Einmal-Link! Den Nachrichtenverlauf nicht an neue Mitglieder senden. No comment provided by engineer. + + Do not use credentials with proxy. + Verwenden Sie keine Anmeldeinformationen mit einem Proxy. + No comment provided by engineer. + Don't create address Keine Adresse erstellt @@ -2432,7 +2527,8 @@ Das ist Ihr eigener Einmal-Link! Download Herunterladen - chat item action + alert button + chat item action Download errors @@ -2449,6 +2545,11 @@ Das ist Ihr eigener Einmal-Link! Datei herunterladen server test step + + Download files + Dateien herunterladen + alert action + Downloaded Heruntergeladen @@ -2724,6 +2825,11 @@ Das ist Ihr eigener Einmal-Link! Fehler beim Wechseln der Empfängeradresse No comment provided by engineer. + + Error changing connection profile + Fehler beim Wechseln des Verbindungs-Profils + No comment provided by engineer. + Error changing role Fehler beim Ändern der Rolle @@ -2734,6 +2840,11 @@ Das ist Ihr eigener Einmal-Link! Fehler beim Ändern der Einstellung No comment provided by engineer. + + Error changing to incognito! + Fehler beim Wechseln zum Inkognito-Profil! + No comment provided by engineer. + Error connecting to forwarding server %@. Please try later. Fehler beim Verbinden mit dem Weiterleitungsserver %@. Bitte versuchen Sie es später erneut. @@ -2854,6 +2965,11 @@ Das ist Ihr eigener Einmal-Link! Fehler beim Laden von %@ Servern No comment provided by engineer. + + Error migrating settings + Fehler beim Migrieren der Einstellungen + No comment provided by engineer. + Error opening chat Fehler beim Öffnen des Chats @@ -2862,7 +2978,7 @@ Das ist Ihr eigener Einmal-Link! Error receiving file Fehler beim Empfangen der Datei - No comment provided by engineer. + alert title Error reconnecting server @@ -2954,10 +3070,15 @@ Das ist Ihr eigener Einmal-Link! Fehler beim Beenden des Chats No comment provided by engineer. + + Error switching profile + Fehler beim Wechseln des Profils + No comment provided by engineer. + Error switching profile! Fehler beim Umschalten des Profils! - No comment provided by engineer. + alertTitle Error synchronizing connection @@ -3002,8 +3123,7 @@ Das ist Ihr eigener Einmal-Link! Error: %@ Fehler: %@ - file error text - snd error text + alert message Error: URL is invalid @@ -3090,6 +3210,13 @@ Das ist Ihr eigener Einmal-Link! Datei-Fehler No comment provided by engineer. + + File errors: +%@ + Datei-Fehler: +%@ + alert message + File not found - most likely file was deleted or cancelled. Datei nicht gefunden - höchstwahrscheinlich wurde die Datei gelöscht oder der Transfer abgebrochen. @@ -3225,11 +3352,31 @@ Das ist Ihr eigener Einmal-Link! Weiterleiten chat item action + + Forward %d message(s)? + %d Nachricht(en) weiterleiten? + alert title + Forward and save messages Nachrichten weiterleiten und speichern No comment provided by engineer. + + Forward messages + Nachrichten weiterleiten + alert action + + + Forward messages without files? + Nachrichten ohne Dateien weiterleiten? + alert message + + + Forward up to 20 messages at once. + Bis zu 20 Nachrichten auf einmal weiterleiten + No comment provided by engineer. + Forwarded Weitergeleitet @@ -3240,6 +3387,11 @@ Das ist Ihr eigener Einmal-Link! Weitergeleitet aus No comment provided by engineer. + + Forwarding %lld messages + %lld Nachricht(en) wird/werden weitergeleitet + No comment provided by engineer. + Forwarding server %@ failed to connect to destination server %@. Please try later. Weiterleitungsserver %@ konnte sich nicht mit dem Zielserver %@ verbinden. Bitte versuchen Sie es später erneut. @@ -3289,11 +3441,6 @@ Fehler: %2$@ Vollständiger Name (optional) No comment provided by engineer. - - Full name: - Vollständiger Name: - No comment provided by engineer. - Fully decentralized – visible only to members. Vollständig dezentralisiert – nur für Mitglieder sichtbar. @@ -3451,7 +3598,7 @@ Fehler: %2$@ Group will be deleted for you - this cannot be undone! - Die Gruppe wird für Sie gelöscht. Dies kann nicht rückgängig gemacht werden! + Die Gruppe wird nur bei Ihnen gelöscht. Dies kann nicht rückgängig gemacht werden! No comment provided by engineer. @@ -3539,6 +3686,11 @@ Fehler: %2$@ ICE-Server (einer pro Zeile) No comment provided by engineer. + + IP address + IP-Adresse + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Falls Sie sich nicht persönlich treffen können, zeigen Sie den QR-Code in einem Videoanruf oder teilen Sie den Link. @@ -3614,6 +3766,13 @@ Fehler: %2$@ Archiv wird importiert No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + Verbesserte Nachrichten-Auslieferung und verringerter Datenverbrauch. +Weitere Verbesserungen sind bald verfügbar! + No comment provided by engineer. + Improved message delivery Verbesserte Zustellung von Nachrichten @@ -3906,7 +4065,7 @@ Das ist Ihr Link für die Gruppe %@! Keep conversation - Unterhaltung behalten + Chat-Inhalte beibehalten No comment provided by engineer. @@ -4184,6 +4343,11 @@ Das ist Ihr Link für die Gruppe %@! Nachrichten-Server No comment provided by engineer. + + Message shape + Nachrichten-Form + No comment provided by engineer. + Message source remains private. Die Nachrichtenquelle bleibt privat. @@ -4234,6 +4398,11 @@ Das ist Ihr Link für die Gruppe %@! Gesendete Nachrichten No comment provided by engineer. + + Messages were deleted after you selected them. + Die Nachrichten wurden gelöscht, nachdem Sie sie ausgewählt hatten. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. Nachrichten, Dateien und Anrufe sind durch **Ende-zu-Ende-Verschlüsselung** mit Perfect Forward Secrecy, Ablehnung und Einbruchs-Wiederherstellung geschützt. @@ -4389,6 +4558,16 @@ Das ist Ihr Link für die Gruppe %@! Neuer Zugangscode No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + Jedes Mal wenn Sie die App starten, werden neue SOCKS-Anmeldeinformationen genutzt + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + Für jeden Server werden neue SOCKS-Anmeldeinformationen genutzt + No comment provided by engineer. + New chat Neuer Chat @@ -4509,6 +4688,16 @@ Das ist Ihr Link für die Gruppe %@! Keine Netzwerkverbindung No comment provided by engineer. + + No permission to record speech + Keine Genehmigung für Sprach-Aufnahmen + No comment provided by engineer. + + + No permission to record video + Keine Genehmigung für Video-Aufnahmen + No comment provided by engineer. + No permission to record voice message Keine Berechtigung für das Aufnehmen von Sprachnachrichten @@ -4529,6 +4718,11 @@ Das ist Ihr Link für die Gruppe %@! Nichts ausgewählt No comment provided by engineer. + + Nothing to forward! + Es gibt nichts zum Weiterleiten! + alert title + Notifications Benachrichtigungen @@ -4561,7 +4755,7 @@ Das ist Ihr Link für die Gruppe %@! Ok Ok - No comment provided by engineer. + alert button Old database @@ -4604,7 +4798,7 @@ Dies erfordert die Aktivierung eines VPNs. Only delete conversation - Nur die Unterhaltung löschen + Nur die Chat-Inhalte löschen No comment provided by engineer. @@ -4752,6 +4946,13 @@ Dies erfordert die Aktivierung eines VPNs. Andere %@ Server No comment provided by engineer. + + Other file errors: +%@ + Andere(r) Datei-Fehler: +%@ + alert message + PING count PING-Zähler @@ -4787,6 +4988,11 @@ Dies erfordert die Aktivierung eines VPNs. Zugangscode eingestellt! No comment provided by engineer. + + Password + Passwort + No comment provided by engineer. + Password to show Passwort anzeigen @@ -4936,6 +5142,11 @@ Fehler: %@ Polnische Bedienoberfläche No comment provided by engineer. + + Port + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Der Fingerabdruck des Zertifikats in der Serveradresse ist wahrscheinlich ungültig @@ -5021,16 +5232,6 @@ Fehler: %@ Profil-Bilder No comment provided by engineer. - - Profile name - Profilname - No comment provided by engineer. - - - Profile name: - Profilname: - No comment provided by engineer. - Profile password Passwort für Profil @@ -5133,6 +5334,11 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Proxy-Server No comment provided by engineer. + + Proxy requires password + Der Proxy benötigt ein Passwort + No comment provided by engineer. + Push notifications Push-Benachrichtigungen @@ -5155,7 +5361,7 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Reachable chat toolbar - Erreichbare Chat-Symbolleiste + Chat-Symbolleiste unten No comment provided by engineer. @@ -5354,6 +5560,11 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Entfernen No comment provided by engineer. + + Remove archive? + Archiv entfernen? + No comment provided by engineer. + Remove image Bild entfernen @@ -5534,6 +5745,11 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. SMP-Server No comment provided by engineer. + + SOCKS proxy + SOCKS-Proxy + No comment provided by engineer. + Safely receive files Dateien sicher empfangen @@ -5547,12 +5763,13 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Save Speichern - chat item action + alert button + chat item action Save (and notify contacts) Speichern (und Kontakte benachrichtigen) - No comment provided by engineer. + alert button Save and notify contact @@ -5579,11 +5796,6 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Archiv speichern No comment provided by engineer. - - Save auto-accept settings - Einstellungen von "Automatisch akzeptieren" speichern - No comment provided by engineer. - Save group profile Gruppenprofil speichern @@ -5619,16 +5831,16 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Alle Server speichern? No comment provided by engineer. - - Save settings? - Einstellungen speichern? - No comment provided by engineer. - Save welcome message? Begrüßungsmeldung speichern? No comment provided by engineer. + + Save your profile? + Ihr Profil speichern? + alert title + Saved Abgespeichert @@ -5649,6 +5861,11 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Gespeicherte Nachricht message info title + + Saving %lld messages + Es wird/werden %lld Nachricht(en) gesichert + No comment provided by engineer. + Scale Skalieren @@ -5696,7 +5913,7 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Search or paste SimpleX link - Suchen oder fügen Sie den SimpleX-Link ein + Suchen oder SimpleX-Link einfügen No comment provided by engineer. @@ -5729,6 +5946,11 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Auswählen chat item action + + Select chat profile + Chat-Profil auswählen + No comment provided by engineer. + Selected %lld %lld ausgewählt @@ -5847,7 +6069,7 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Sender cancelled file transfer. Der Absender hat die Dateiübertragung abgebrochen. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -5944,6 +6166,11 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Über einen Proxy gesendet No comment provided by engineer. + + Server + Server + No comment provided by engineer. + Server address Server-Adresse @@ -6064,6 +6291,11 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Einstellungen No comment provided by engineer. + + Settings were changed. + Die Einstellungen wurden geändert. + alert message + Shape profile images Form der Profil-Bilder @@ -6099,6 +6331,11 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Link teilen No comment provided by engineer. + + Share profile + Profil teilen + No comment provided by engineer. + Share this 1-time invite link Teilen Sie diesen Einmal-Einladungslink @@ -6234,6 +6471,11 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. SimpleX-Einmal-Einladung simplex link type + + SimpleX protocols reviewed by Trail of Bits. + Die SimpleX-Protokolle wurden von Trail of Bits überprüft. + No comment provided by engineer. + Simplified incognito mode Vereinfachter Inkognito-Modus @@ -6264,6 +6506,11 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Weich blur media + + Some app settings were not migrated. + Einige App-Einstellungen wurden nicht migriert. + No comment provided by engineer. + Some file(s) were not exported: Einzelne Datei(en) wurde(n) nicht exportiert: @@ -6404,6 +6651,16 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. Unterstützung von SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + Während des Anrufs zwischen Audio und Video wechseln + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + Das Chat-Profil für Einmal-Einladungen wechseln + No comment provided by engineer. + System System @@ -6439,6 +6696,11 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. TCP_KEEPINTVL No comment provided by engineer. + + Tail + Sprechblasen-Format + No comment provided by engineer. + Take picture Machen Sie ein Foto @@ -6588,7 +6850,7 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro The messages will be deleted for all members. - Die Nachrichten werden für alle Mitglieder gelöscht werden. + Die Nachrichten werden für alle Gruppenmitglieder gelöscht. No comment provided by engineer. @@ -6631,6 +6893,11 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro Der von Ihnen eingefügte Text ist kein SimpleX-Link. No comment provided by engineer. + + The uploaded database archive will be permanently removed from the servers. + Das hochgeladene Datenbank-Archiv wird dauerhaft von den Servern entfernt. + No comment provided by engineer. + Themes Design @@ -6758,6 +7025,16 @@ You will be prompted to complete authentication before this feature is enabled.< Sie werden aufgefordert, die Authentifizierung abzuschließen, bevor diese Funktion aktiviert wird. No comment provided by engineer. + + To record speech please grant permission to use Microphone. + Bitte erteilen Sie für Sprach-Aufnahmen die Genehmigung das Mikrofon zu nutzen. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + Bitte erteilen Sie für Video-Aufnahmen die Genehmigung die Kamera zu nutzen. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Bitte erlauben Sie die Nutzung des Mikrofons, um Sprachnachrichten aufnehmen zu können. @@ -6911,7 +7188,7 @@ Sie werden aufgefordert, die Authentifizierung abzuschließen, bevor diese Funkt Unknown servers! Unbekannte Server! - No comment provided by engineer. + alert title Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. @@ -7025,6 +7302,11 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Verwende .onion-Hosts No comment provided by engineer. + + Use SOCKS proxy + SOCKS-Proxy nutzen + No comment provided by engineer. + Use SimpleX Chat servers? Verwenden Sie SimpleX-Chat-Server? @@ -7087,12 +7369,7 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Use the app with one hand. - Die App mit einer Hand nutzen. - No comment provided by engineer. - - - User profile - Benutzerprofil + Die App mit einer Hand bedienen. No comment provided by engineer. @@ -7100,6 +7377,11 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Benutzer-Auswahl No comment provided by engineer. + + Username + Benutzername + No comment provided by engineer. + Using SimpleX Chat servers. Verwendung von SimpleX-Chat-Servern. @@ -7333,7 +7615,7 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. Ohne Tor- oder VPN-Nutzung wird Ihre IP-Adresse für diese XFTP-Relais sichtbar sein: %@. - No comment provided by engineer. + alert message Wrong database passphrase @@ -7360,11 +7642,6 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s XFTP-Server No comment provided by engineer. - - You - Profil - No comment provided by engineer. - You **must not** use the same database on two devices. Sie dürfen die selbe Datenbank **nicht** auf zwei Geräten nutzen. @@ -7519,7 +7796,7 @@ Verbindungsanfrage wiederholen? You can still view conversation with %@ in the list of chats. - Sie können in der Chatliste weiterhin die Unterhaltung mit %@ einsehen. + Sie können in der Chat-Liste weiterhin die Unterhaltung mit %@ einsehen. No comment provided by engineer. @@ -7714,11 +7991,21 @@ Verbindungsanfrage wiederholen? Ihre Chat-Datenbank ist nicht verschlüsselt. Bitte legen Sie ein Passwort fest, um sie zu schützen. No comment provided by engineer. + + Your chat preferences + Ihre Chat-Präferenzen + alert title + Your chat profiles Ihre Chat-Profile No comment provided by engineer. + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + Ihre Verbindung wurde auf %@ verschoben. Während Sie auf das Profil weitergeleitet wurden trat aber ein unerwarteter Fehler auf. + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Ihr Kontakt hat eine Datei gesendet, die größer ist als die derzeit unterstützte maximale Größe (%@). @@ -7734,6 +8021,11 @@ Verbindungsanfrage wiederholen? Ihre Kontakte bleiben weiterhin verbunden. No comment provided by engineer. + + Your credentials may be sent unencrypted. + Ihre Anmeldeinformationen können unverschlüsselt versendet werden. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Ihre aktuelle Chat-Datenbank wird GELÖSCHT und durch die Importierte ERSETZT. @@ -7764,13 +8056,16 @@ Verbindungsanfrage wiederholen? Ihr Profil **%@** wird geteilt. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Ihr Profil wird auf Ihrem Gerät gespeichert und nur mit Ihren Kontakten geteilt. -SimpleX-Server können Ihr Profil nicht einsehen. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Ihr Profil wird auf Ihrem Gerät gespeichert und nur mit Ihren Kontakten geteilt. SimpleX-Server können Ihr Profil nicht einsehen. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + Ihr Profil wurde geändert. Wenn Sie es speichern, wird das aktualisierte Profil an alle Ihre Kontakte gesendet. + alert message + Your profile, contacts and delivered messages are stored on your device. Ihr Profil, Ihre Kontakte und zugestellten Nachrichten werden auf Ihrem Gerät gespeichert. @@ -8208,7 +8503,7 @@ SimpleX-Server können Ihr Profil nicht einsehen. expired - abgelaufen + Abgelaufen No comment provided by engineer. @@ -8440,7 +8735,7 @@ SimpleX-Server können Ihr Profil nicht einsehen. other - andere + Andere No comment provided by engineer. diff --git a/apps/ios/SimpleX Localizations/el.xcloc/Localized Contents/el.xliff b/apps/ios/SimpleX Localizations/el.xcloc/Localized Contents/el.xliff index b8432a33b6..799c61b448 100644 --- a/apps/ios/SimpleX Localizations/el.xcloc/Localized Contents/el.xliff +++ b/apps/ios/SimpleX Localizations/el.xcloc/Localized Contents/el.xliff @@ -4200,7 +4200,7 @@ SimpleX servers cannot see your profile. ## In reply to - ## Ως απαντηση σε + ## Ως απάντηση σε copied message info 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 c217793f03..321b430a3f 100644 --- a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff +++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff @@ -137,6 +137,11 @@ %@ wants to connect! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ and %lld members @@ -157,11 +162,36 @@ %d days time interval + + %d file(s) are still being downloaded. + %d file(s) are still being downloaded. + forward confirmation reason + + + %d file(s) failed to download. + %d file(s) failed to download. + forward confirmation reason + + + %d file(s) were deleted. + %d file(s) were deleted. + forward confirmation reason + + + %d file(s) were not downloaded. + %d file(s) were not downloaded. + forward confirmation reason + %d hours %d hours time interval + + %d messages not forwarded + %d messages not forwarded + alert title + %d min %d min @@ -723,7 +753,7 @@ All profiles All profiles - No comment provided by engineer. + profile dropdown All your contacts will remain connected. @@ -915,6 +945,11 @@ App passcode is replaced with self-destruct passcode. No comment provided by engineer. + + App session + App session + No comment provided by engineer. + App version App version @@ -1020,6 +1055,11 @@ Auto-accept images No comment provided by engineer. + + Auto-accept settings + Auto-accept settings + alert title + Back Back @@ -1045,11 +1085,21 @@ Bad message hash No comment provided by engineer. + + Better calls + Better calls + No comment provided by engineer. + Better groups Better groups No comment provided by engineer. + + Better message dates. + Better message dates. + No comment provided by engineer. + Better messages Better messages @@ -1060,6 +1110,21 @@ Better networking No comment provided by engineer. + + Better notifications + Better notifications + No comment provided by engineer. + + + Better security ✅ + Better security ✅ + No comment provided by engineer. + + + Better user experience + Better user experience + No comment provided by engineer. + Black Black @@ -1193,7 +1258,7 @@ Cancel Cancel - No comment provided by engineer. + alert button Cancel migration @@ -1213,7 +1278,7 @@ Cannot receive file Cannot receive file - No comment provided by engineer. + alert title Capacity exceeded - recipient did not receive previously sent messages. @@ -1341,6 +1406,16 @@ Chat preferences No comment provided by engineer. + + Chat preferences were changed. + Chat preferences were changed. + alert message + + + Chat profile + Chat profile + No comment provided by engineer. + Chat theme Chat theme @@ -1740,6 +1815,11 @@ This is your own one-time link! Core version: v%@ No comment provided by engineer. + + Corner + Corner + No comment provided by engineer. + Correct name to %@? Correct name to %@? @@ -1865,6 +1945,11 @@ This is your own one-time link! Custom time No comment provided by engineer. + + Customizable message shape. + Customizable message shape. + No comment provided by engineer. + Customize theme Customize theme @@ -2159,6 +2244,11 @@ This is your own one-time link! Delete old database? No comment provided by engineer. + + Delete or moderate up to 200 messages. + Delete or moderate up to 200 messages. + No comment provided by engineer. + Delete pending connection? Delete pending connection? @@ -2409,6 +2499,11 @@ This is your own one-time link! Do not send history to new members. No comment provided by engineer. + + Do not use credentials with proxy. + Do not use credentials with proxy. + No comment provided by engineer. + Don't create address Don't create address @@ -2432,7 +2527,8 @@ This is your own one-time link! Download Download - chat item action + alert button + chat item action Download errors @@ -2449,6 +2545,11 @@ This is your own one-time link! Download file server test step + + Download files + Download files + alert action + Downloaded Downloaded @@ -2724,6 +2825,11 @@ This is your own one-time link! Error changing address No comment provided by engineer. + + Error changing connection profile + Error changing connection profile + No comment provided by engineer. + Error changing role Error changing role @@ -2734,6 +2840,11 @@ This is your own one-time link! Error changing setting No comment provided by engineer. + + Error changing to incognito! + Error changing to incognito! + No comment provided by engineer. + Error connecting to forwarding server %@. Please try later. Error connecting to forwarding server %@. Please try later. @@ -2854,6 +2965,11 @@ This is your own one-time link! Error loading %@ servers No comment provided by engineer. + + Error migrating settings + Error migrating settings + No comment provided by engineer. + Error opening chat Error opening chat @@ -2862,7 +2978,7 @@ This is your own one-time link! Error receiving file Error receiving file - No comment provided by engineer. + alert title Error reconnecting server @@ -2954,10 +3070,15 @@ This is your own one-time link! Error stopping chat No comment provided by engineer. + + Error switching profile + Error switching profile + No comment provided by engineer. + Error switching profile! Error switching profile! - No comment provided by engineer. + alertTitle Error synchronizing connection @@ -3002,8 +3123,7 @@ This is your own one-time link! Error: %@ Error: %@ - file error text - snd error text + alert message Error: URL is invalid @@ -3090,6 +3210,13 @@ This is your own one-time link! File error No comment provided by engineer. + + File errors: +%@ + File errors: +%@ + alert message + File not found - most likely file was deleted or cancelled. File not found - most likely file was deleted or cancelled. @@ -3225,11 +3352,31 @@ This is your own one-time link! Forward chat item action + + Forward %d message(s)? + Forward %d message(s)? + alert title + Forward and save messages Forward and save messages No comment provided by engineer. + + Forward messages + Forward messages + alert action + + + Forward messages without files? + Forward messages without files? + alert message + + + Forward up to 20 messages at once. + Forward up to 20 messages at once. + No comment provided by engineer. + Forwarded Forwarded @@ -3240,6 +3387,11 @@ This is your own one-time link! Forwarded from No comment provided by engineer. + + Forwarding %lld messages + Forwarding %lld messages + 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. @@ -3289,11 +3441,6 @@ Error: %2$@ Full name (optional) No comment provided by engineer. - - Full name: - Full name: - No comment provided by engineer. - Fully decentralized – visible only to members. Fully decentralized – visible only to members. @@ -3539,6 +3686,11 @@ Error: %2$@ ICE servers (one per line) No comment provided by engineer. + + IP address + IP address + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. If you can't meet in person, show QR code in a video call, or share the link. @@ -3614,6 +3766,13 @@ Error: %2$@ Importing archive No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + Improved delivery, reduced traffic usage. +More improvements are coming soon! + No comment provided by engineer. + Improved message delivery Improved message delivery @@ -4184,6 +4343,11 @@ This is your link for group %@! Message servers No comment provided by engineer. + + Message shape + Message shape + No comment provided by engineer. + Message source remains private. Message source remains private. @@ -4234,6 +4398,11 @@ This is your link for group %@! Messages sent No comment provided by engineer. + + Messages were deleted after you selected them. + Messages were deleted after you selected them. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. @@ -4389,6 +4558,16 @@ This is your link for group %@! New Passcode No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + New SOCKS credentials will be used every time you start the app. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + New SOCKS credentials will be used for each server. + No comment provided by engineer. + New chat New chat @@ -4509,6 +4688,16 @@ This is your link for group %@! No network connection No comment provided by engineer. + + No permission to record speech + No permission to record speech + No comment provided by engineer. + + + No permission to record video + No permission to record video + No comment provided by engineer. + No permission to record voice message No permission to record voice message @@ -4529,6 +4718,11 @@ This is your link for group %@! Nothing selected No comment provided by engineer. + + Nothing to forward! + Nothing to forward! + alert title + Notifications Notifications @@ -4561,7 +4755,7 @@ This is your link for group %@! Ok Ok - No comment provided by engineer. + alert button Old database @@ -4752,6 +4946,13 @@ Requires compatible VPN. Other %@ servers No comment provided by engineer. + + Other file errors: +%@ + Other file errors: +%@ + alert message + PING count PING count @@ -4787,6 +4988,11 @@ Requires compatible VPN. Passcode set! No comment provided by engineer. + + Password + Password + No comment provided by engineer. + Password to show Password to show @@ -4936,6 +5142,11 @@ Error: %@ Polish interface No comment provided by engineer. + + Port + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Possibly, certificate fingerprint in server address is incorrect @@ -5021,16 +5232,6 @@ Error: %@ Profile images No comment provided by engineer. - - Profile name - Profile name - No comment provided by engineer. - - - Profile name: - Profile name: - No comment provided by engineer. - Profile password Profile password @@ -5133,6 +5334,11 @@ Enable in *Network & servers* settings. Proxied servers No comment provided by engineer. + + Proxy requires password + Proxy requires password + No comment provided by engineer. + Push notifications Push notifications @@ -5354,6 +5560,11 @@ Enable in *Network & servers* settings. Remove No comment provided by engineer. + + Remove archive? + Remove archive? + No comment provided by engineer. + Remove image Remove image @@ -5534,6 +5745,11 @@ Enable in *Network & servers* settings. SMP server No comment provided by engineer. + + SOCKS proxy + SOCKS proxy + No comment provided by engineer. + Safely receive files Safely receive files @@ -5547,12 +5763,13 @@ Enable in *Network & servers* settings. Save Save - chat item action + alert button + chat item action Save (and notify contacts) Save (and notify contacts) - No comment provided by engineer. + alert button Save and notify contact @@ -5579,11 +5796,6 @@ Enable in *Network & servers* settings. Save archive No comment provided by engineer. - - Save auto-accept settings - Save auto-accept settings - No comment provided by engineer. - Save group profile Save group profile @@ -5619,16 +5831,16 @@ Enable in *Network & servers* settings. Save servers? No comment provided by engineer. - - Save settings? - Save settings? - No comment provided by engineer. - Save welcome message? Save welcome message? No comment provided by engineer. + + Save your profile? + Save your profile? + alert title + Saved Saved @@ -5649,6 +5861,11 @@ Enable in *Network & servers* settings. Saved message message info title + + Saving %lld messages + Saving %lld messages + No comment provided by engineer. + Scale Scale @@ -5729,6 +5946,11 @@ Enable in *Network & servers* settings. Select chat item action + + Select chat profile + Select chat profile + No comment provided by engineer. + Selected %lld Selected %lld @@ -5847,7 +6069,7 @@ Enable in *Network & servers* settings. Sender cancelled file transfer. Sender cancelled file transfer. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -5944,6 +6166,11 @@ Enable in *Network & servers* settings. Sent via proxy No comment provided by engineer. + + Server + Server + No comment provided by engineer. + Server address Server address @@ -6064,6 +6291,11 @@ Enable in *Network & servers* settings. Settings No comment provided by engineer. + + Settings were changed. + Settings were changed. + alert message + Shape profile images Shape profile images @@ -6099,6 +6331,11 @@ Enable in *Network & servers* settings. Share link No comment provided by engineer. + + Share profile + Share profile + No comment provided by engineer. + Share this 1-time invite link Share this 1-time invite link @@ -6234,6 +6471,11 @@ Enable in *Network & servers* settings. SimpleX one-time invitation simplex link type + + SimpleX protocols reviewed by Trail of Bits. + SimpleX protocols reviewed by Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Simplified incognito mode @@ -6264,6 +6506,11 @@ Enable in *Network & servers* settings. Soft blur media + + Some app settings were not migrated. + Some app settings were not migrated. + No comment provided by engineer. + Some file(s) were not exported: Some file(s) were not exported: @@ -6404,6 +6651,16 @@ Enable in *Network & servers* settings. Support SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + Switch audio and video during the call. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + Switch chat profile for 1-time invitations. + No comment provided by engineer. + System System @@ -6439,6 +6696,11 @@ Enable in *Network & servers* settings. TCP_KEEPINTVL No comment provided by engineer. + + Tail + Tail + No comment provided by engineer. + Take picture Take picture @@ -6631,6 +6893,11 @@ It can happen because of some bug or when the connection is compromised.The text you pasted is not a SimpleX link. No comment provided by engineer. + + The uploaded database archive will be permanently removed from the servers. + The uploaded database archive will be permanently removed from the servers. + No comment provided by engineer. + Themes Themes @@ -6758,6 +7025,16 @@ You will be prompted to complete authentication before this feature is enabled.< You will be prompted to complete authentication before this feature is enabled. No comment provided by engineer. + + To record speech please grant permission to use Microphone. + To record speech please grant permission to use Microphone. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + To record video please grant permission to use Camera. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. To record voice message please grant permission to use Microphone. @@ -6911,7 +7188,7 @@ You will be prompted to complete authentication before this feature is enabled.< Unknown servers! Unknown servers! - No comment provided by engineer. + alert title Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. @@ -7025,6 +7302,11 @@ To connect, please ask your contact to create another connection link and check Use .onion hosts No comment provided by engineer. + + Use SOCKS proxy + Use SOCKS proxy + No comment provided by engineer. + Use SimpleX Chat servers? Use SimpleX Chat servers? @@ -7090,16 +7372,16 @@ To connect, please ask your contact to create another connection link and check Use the app with one hand. No comment provided by engineer. - - User profile - User profile - No comment provided by engineer. - User selection User selection No comment provided by engineer. + + Username + Username + No comment provided by engineer. + Using SimpleX Chat servers. Using SimpleX Chat servers. @@ -7333,7 +7615,7 @@ To connect, please ask your contact to create another connection link and check Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. - No comment provided by engineer. + alert message Wrong database passphrase @@ -7360,11 +7642,6 @@ To connect, please ask your contact to create another connection link and check XFTP server No comment provided by engineer. - - You - You - No comment provided by engineer. - You **must not** use the same database on two devices. You **must not** use the same database on two devices. @@ -7714,11 +7991,21 @@ Repeat connection request? Your chat database is not encrypted - set passphrase to encrypt it. No comment provided by engineer. + + Your chat preferences + Your chat preferences + alert title + Your chat profiles Your chat profiles No comment provided by engineer. + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Your contact sent a file that is larger than currently supported maximum size (%@). @@ -7734,6 +8021,11 @@ Repeat connection request? Your contacts will remain connected. No comment provided by engineer. + + Your credentials may be sent unencrypted. + Your credentials may be sent unencrypted. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Your current chat database will be DELETED and REPLACED with the imported one. @@ -7764,13 +8056,16 @@ Repeat connection request? Your profile **%@** will be shared. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + alert message + Your profile, contacts and delivered messages are stored on your device. Your profile, contacts and delivered messages are stored on your device. 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 6e5ec0e85a..21cd9919db 100644 --- a/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff +++ b/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff @@ -137,6 +137,11 @@ ¡ %@ quiere contactar! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ y %lld miembro(s) más @@ -157,11 +162,36 @@ %d días time interval + + %d file(s) are still being downloaded. + %d archivo(s) se está(n) descargando todavía. + forward confirmation reason + + + %d file(s) failed to download. + La descarga ha fallado para %d archivo(s). + forward confirmation reason + + + %d file(s) were deleted. + %d archivo(s) ha(n) sido eliminado(s). + forward confirmation reason + + + %d file(s) were not downloaded. + %d archivo(s) no se ha(n) descargado. + forward confirmation reason + %d hours %d horas time interval + + %d messages not forwarded + %d mensajes no enviados + alert title + %d min %d minutos @@ -507,7 +537,7 @@ A new random profile will be shared. - Se compartirá un perfil nuevo aleatorio. + Compartirás un perfil nuevo aleatorio. No comment provided by engineer. @@ -723,7 +753,7 @@ All profiles Todos los perfiles - No comment provided by engineer. + profile dropdown All your contacts will remain connected. @@ -915,6 +945,11 @@ El código de acceso será reemplazado por código de autodestrucción. No comment provided by engineer. + + App session + Sesión de aplicación + No comment provided by engineer. + App version Versión de la aplicación @@ -1020,6 +1055,11 @@ Aceptar imágenes automáticamente No comment provided by engineer. + + Auto-accept settings + Auto aceptar configuración + alert title + Back Volver @@ -1045,11 +1085,21 @@ Hash de mensaje incorrecto No comment provided by engineer. + + Better calls + Llamadas mejoradas + No comment provided by engineer. + Better groups Grupos mejorados No comment provided by engineer. + + Better message dates. + Sistema de fechas mejorado. + No comment provided by engineer. + Better messages Mensajes mejorados @@ -1060,6 +1110,21 @@ Uso de red mejorado No comment provided by engineer. + + Better notifications + Notificaciones mejoradas + No comment provided by engineer. + + + Better security ✅ + Seguridad mejorada ✅ + No comment provided by engineer. + + + Better user experience + Experiencia de usuario mejorada + No comment provided by engineer. + Black Negro @@ -1193,7 +1258,7 @@ Cancel Cancelar - No comment provided by engineer. + alert button Cancel migration @@ -1213,7 +1278,7 @@ Cannot receive file No se puede recibir el archivo - No comment provided by engineer. + alert title Capacity exceeded - recipient did not receive previously sent messages. @@ -1341,6 +1406,16 @@ Preferencias de Chat No comment provided by engineer. + + Chat preferences were changed. + Las preferencias del chat han sido modificadas. + alert message + + + Chat profile + Perfil de usuario + No comment provided by engineer. + Chat theme Tema de chat @@ -1493,7 +1568,7 @@ Confirm that you remember database passphrase to migrate it. - Para migrar confirma que recuerdas la frase de contraseña de la base de datos. + Para migrar la base de datos confirma que recuerdas la frase de contraseña. No comment provided by engineer. @@ -1740,6 +1815,11 @@ This is your own one-time link! Versión Core: v%@ No comment provided by engineer. + + Corner + Esquina + No comment provided by engineer. + Correct name to %@? ¿Corregir el nombre a %@? @@ -1865,6 +1945,11 @@ This is your own one-time link! Tiempo personalizado No comment provided by engineer. + + Customizable message shape. + Forma personalizable de los mensajes. + No comment provided by engineer. + Customize theme Personalizar tema @@ -1946,7 +2031,7 @@ This is your own one-time link! Database passphrase is different from saved in the keychain. - La contraseña es distinta a la almacenada en Keychain. + La contraseña es diferente a la almacenada en Keychain. No comment provided by engineer. @@ -2159,6 +2244,11 @@ This is your own one-time link! ¿Eliminar base de datos antigua? No comment provided by engineer. + + Delete or moderate up to 200 messages. + Borra o modera hasta 200 mensajes a la vez. + No comment provided by engineer. + Delete pending connection? ¿Eliminar conexión pendiente? @@ -2371,7 +2461,7 @@ This is your own one-time link! Disconnect desktop? - ¿Desconectar ordenador? + ¿Desconectar del ordenador? No comment provided by engineer. @@ -2409,6 +2499,11 @@ This is your own one-time link! No se envía el historial a los miembros nuevos. No comment provided by engineer. + + Do not use credentials with proxy. + No uses credenciales con proxy. + No comment provided by engineer. + Don't create address No crear dirección SimpleX @@ -2421,7 +2516,7 @@ This is your own one-time link! Don't show again - No mostrar de nuevo + No volver a mostrar No comment provided by engineer. @@ -2432,7 +2527,8 @@ This is your own one-time link! Download Descargar - chat item action + alert button + chat item action Download errors @@ -2449,6 +2545,11 @@ This is your own one-time link! Descargar archivo server test step + + Download files + Descargar archivos + alert action + Downloaded Descargado @@ -2724,6 +2825,11 @@ This is your own one-time link! Error al cambiar servidor No comment provided by engineer. + + Error changing connection profile + Error al cambiar el perfil de conexión + No comment provided by engineer. + Error changing role Error al cambiar rol @@ -2734,6 +2840,11 @@ This is your own one-time link! Error cambiando configuración No comment provided by engineer. + + Error changing to incognito! + ¡Error al cambiar a incógnito! + No comment provided by engineer. + Error connecting to forwarding server %@. Please try later. Error al conectar con el servidor de reenvío %@. Por favor, inténtalo más tarde. @@ -2854,6 +2965,11 @@ This is your own one-time link! Error al cargar servidores %@ No comment provided by engineer. + + Error migrating settings + Error al migrar la configuración + No comment provided by engineer. + Error opening chat Error al abrir chat @@ -2862,7 +2978,7 @@ This is your own one-time link! Error receiving file Error al recibir archivo - No comment provided by engineer. + alert title Error reconnecting server @@ -2954,10 +3070,15 @@ This is your own one-time link! Error al parar SimpleX No comment provided by engineer. + + Error switching profile + Error al cambiar perfil + No comment provided by engineer. + Error switching profile! ¡Error al cambiar perfil! - No comment provided by engineer. + alertTitle Error synchronizing connection @@ -3002,8 +3123,7 @@ This is your own one-time link! Error: %@ Error: %@ - file error text - snd error text + alert message Error: URL is invalid @@ -3090,6 +3210,13 @@ This is your own one-time link! Error de archivo No comment provided by engineer. + + File errors: +%@ + Error(es) de archivo +%@ + alert message + File not found - most likely file was deleted or cancelled. Archivo no encontrado, probablemente haya sido borrado o cancelado. @@ -3225,11 +3352,31 @@ This is your own one-time link! Reenviar chat item action + + Forward %d message(s)? + ¿Reenviar %d mensaje(s)? + alert title + Forward and save messages Reenviar y guardar mensajes No comment provided by engineer. + + Forward messages + Reenviar mensajes + alert action + + + Forward messages without files? + ¿Reenviar mensajes sin los archivos? + alert message + + + Forward up to 20 messages at once. + Desplazamiento de hasta 20 mensajes. + No comment provided by engineer. + Forwarded Reenviado @@ -3240,6 +3387,11 @@ This is your own one-time link! Reenviado por No comment provided by engineer. + + Forwarding %lld messages + Reenviando %lld mensajes + No comment provided by engineer. + Forwarding server %@ failed to connect to destination server %@. Please try later. El servidor de reenvío %@ no ha podido conectarse al servidor de destino %@. Por favor, intentalo más tarde. @@ -3289,19 +3441,14 @@ Error: %2$@ Nombre completo (opcional) No comment provided by engineer. - - Full name: - Nombre completo: - No comment provided by engineer. - Fully decentralized – visible only to members. - Completamente descentralizado y sólo visible para los miembros. + Totalmente descentralizado. Visible sólo para los miembros. No comment provided by engineer. Fully re-implemented - work in background! - Completamente reimplementado: ¡funciona en segundo plano! + Totalmente revisado. ¡Funciona en segundo plano! No comment provided by engineer. @@ -3539,6 +3686,11 @@ Error: %2$@ Servidores ICE (uno por línea) No comment provided by engineer. + + IP address + Dirección IP + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Si no puedes reunirte en persona, muestra el código QR por videollamada o comparte el enlace. @@ -3614,6 +3766,13 @@ Error: %2$@ Importando archivo No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + Reducción del tráfico y entrega mejorada. +¡Pronto habrá nuevas mejoras! + No comment provided by engineer. + Improved message delivery Entrega de mensajes mejorada @@ -4016,7 +4175,7 @@ This is your link for group %@! Local profile data only - Sólo datos del perfil local + Eliminar sólo el perfil No comment provided by engineer. @@ -4184,6 +4343,11 @@ This is your link for group %@! Servidores de mensajes No comment provided by engineer. + + Message shape + Forma del mensaje + No comment provided by engineer. + Message source remains private. El autor del mensaje se mantiene privado. @@ -4234,6 +4398,11 @@ This is your link for group %@! Mensajes enviados No comment provided by engineer. + + Messages were deleted after you selected them. + Los mensajes han sido borrados después de seleccionarlos. + alert message + 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. @@ -4389,6 +4558,16 @@ This is your link for group %@! Código Nuevo No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + Se usarán credenciales SOCKS nuevas cada vez que inicies la aplicación. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + Se usarán credenciales SOCKS nuevas por cada servidor. + No comment provided by engineer. + New chat Nuevo chat @@ -4509,6 +4688,16 @@ This is your link for group %@! Sin conexión de red No comment provided by engineer. + + No permission to record speech + Sin permiso para grabación de voz + No comment provided by engineer. + + + No permission to record video + Sin permiso para grabación de vídeo + No comment provided by engineer. + No permission to record voice message Sin permiso para grabar mensajes de voz @@ -4529,6 +4718,11 @@ This is your link for group %@! Nada seleccionado No comment provided by engineer. + + Nothing to forward! + ¡Nada para reenviar! + alert title + Notifications Notificaciones @@ -4561,7 +4755,7 @@ This is your link for group %@! Ok Ok - No comment provided by engineer. + alert button Old database @@ -4752,6 +4946,13 @@ Requiere activación de la VPN. Otros servidores %@ No comment provided by engineer. + + Other file errors: +%@ + Otro(s) error(es) de archivo. +%@ + alert message + PING count Contador PING @@ -4787,6 +4988,11 @@ Requiere activación de la VPN. ¡Código de acceso guardado! No comment provided by engineer. + + Password + Contraseña + No comment provided by engineer. + Password to show Contraseña para hacerlo visible @@ -4936,9 +5142,14 @@ Error: %@ Interfaz en polaco No comment provided by engineer. + + Port + Puerto + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect - Posiblemente la huella digital del certificado en la dirección del servidor es incorrecta + Posiblemente la huella del certificado en la dirección del servidor es incorrecta server test error @@ -5008,7 +5219,7 @@ Error: %@ Profile and server connections - Datos del perfil y conexiones + Eliminar perfil y conexiones No comment provided by engineer. @@ -5021,16 +5232,6 @@ Error: %@ Forma de los perfiles No comment provided by engineer. - - Profile name - Nombre del perfil - No comment provided by engineer. - - - Profile name: - Nombre del perfil: - No comment provided by engineer. - Profile password Contraseña del perfil @@ -5133,6 +5334,11 @@ Actívalo en ajustes de *Servidores y Redes*. Servidores con proxy No comment provided by engineer. + + Proxy requires password + El proxy requiere contraseña + No comment provided by engineer. + Push notifications Notificaciones automáticas @@ -5354,6 +5560,11 @@ Actívalo en ajustes de *Servidores y Redes*. Eliminar No comment provided by engineer. + + Remove archive? + ¿Eliminar archivo? + No comment provided by engineer. + Remove image Eliminar imagen @@ -5534,6 +5745,11 @@ Actívalo en ajustes de *Servidores y Redes*. Servidor SMP No comment provided by engineer. + + SOCKS proxy + Proxy SOCKS + No comment provided by engineer. + Safely receive files Recibe archivos de forma segura @@ -5547,12 +5763,13 @@ Actívalo en ajustes de *Servidores y Redes*. Save Guardar - chat item action + alert button + chat item action Save (and notify contacts) Guardar (y notificar contactos) - No comment provided by engineer. + alert button Save and notify contact @@ -5579,11 +5796,6 @@ Actívalo en ajustes de *Servidores y Redes*. Guardar archivo No comment provided by engineer. - - Save auto-accept settings - Guardar configuración de auto aceptar - No comment provided by engineer. - Save group profile Guardar perfil de grupo @@ -5619,16 +5831,16 @@ Actívalo en ajustes de *Servidores y Redes*. ¿Guardar servidores? No comment provided by engineer. - - Save settings? - ¿Guardar configuración? - No comment provided by engineer. - Save welcome message? ¿Guardar mensaje de bienvenida? No comment provided by engineer. + + Save your profile? + ¿Guardar tu perfil? + alert title + Saved Guardado @@ -5649,6 +5861,11 @@ Actívalo en ajustes de *Servidores y Redes*. Mensaje guardado message info title + + Saving %lld messages + Guardando %lld mensajes + No comment provided by engineer. + Scale Escala @@ -5729,6 +5946,11 @@ Actívalo en ajustes de *Servidores y Redes*. Seleccionar chat item action + + Select chat profile + Selecciona perfil de chat + No comment provided by engineer. + Selected %lld Seleccionados %lld @@ -5776,7 +5998,7 @@ Actívalo en ajustes de *Servidores y Redes*. Send direct message to connect - Envia un mensaje para conectar + Envía un mensaje para conectar No comment provided by engineer. @@ -5847,7 +6069,7 @@ Actívalo en ajustes de *Servidores y Redes*. Sender cancelled file transfer. El remitente ha cancelado la transferencia de archivos. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -5944,6 +6166,11 @@ Actívalo en ajustes de *Servidores y Redes*. Mediante proxy No comment provided by engineer. + + Server + Servidor + No comment provided by engineer. + Server address Dirección del servidor @@ -5971,7 +6198,7 @@ Actívalo en ajustes de *Servidores y Redes*. Server test failed! - ¡Error en prueba del servidor! + ¡Prueba no superada! No comment provided by engineer. @@ -6064,6 +6291,11 @@ Actívalo en ajustes de *Servidores y Redes*. Configuración No comment provided by engineer. + + Settings were changed. + La configuración ha sido modificada. + alert message + Shape profile images Dar forma a las imágenes de perfil @@ -6099,6 +6331,11 @@ Actívalo en ajustes de *Servidores y Redes*. Compartir enlace No comment provided by engineer. + + Share profile + Comparte perfil + No comment provided by engineer. + Share this 1-time invite link Comparte este enlace de un solo uso @@ -6234,6 +6471,11 @@ Actívalo en ajustes de *Servidores y Redes*. Invitación SimpleX de un uso simplex link type + + SimpleX protocols reviewed by Trail of Bits. + Protocolos de SimpleX auditados por Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Modo incógnito simplificado @@ -6264,6 +6506,11 @@ Actívalo en ajustes de *Servidores y Redes*. Suave blur media + + Some app settings were not migrated. + Algunas configuraciones de la app no han sido migradas. + No comment provided by engineer. + Some file(s) were not exported: Algunos archivos no han sido exportados: @@ -6404,6 +6651,16 @@ Actívalo en ajustes de *Servidores y Redes*. Soporte SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + Intercambia audio y video durante la llamada. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + Cambia el perfil de chat para invitaciones de un solo uso. + No comment provided by engineer. + System Sistema @@ -6439,6 +6696,11 @@ Actívalo en ajustes de *Servidores y Redes*. TCP_KEEPINTVL No comment provided by engineer. + + Tail + Cola + No comment provided by engineer. + Take picture Tomar foto @@ -6486,7 +6748,7 @@ Actívalo en ajustes de *Servidores y Redes*. Test failed at step %@. - La prueba ha fallado en el paso %@. + Prueba no superada en el paso %@. server test failure @@ -6501,7 +6763,7 @@ Actívalo en ajustes de *Servidores y Redes*. Tests failed! - ¡Pruebas fallidas! + ¡Pruebas no superadas! No comment provided by engineer. @@ -6631,6 +6893,11 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida. El texto pegado no es un enlace SimpleX. No comment provided by engineer. + + The uploaded database archive will be permanently removed from the servers. + El archivo de bases de datos subido será eliminado permanentemente de los servidores. + No comment provided by engineer. + Themes Temas @@ -6758,6 +7025,16 @@ You will be prompted to complete authentication before this feature is enabled.< Se te pedirá que completes la autenticación antes de activar esta función. No comment provided by engineer. + + To record speech please grant permission to use Microphone. + Para grabación de voz, por favor concede el permiso para usar el micrófono. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + Para grabación de vídeo, por favor concede el permiso para usar la cámara. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Para grabar el mensaje de voz concede permiso para usar el micrófono. @@ -6911,7 +7188,7 @@ Se te pedirá que completes la autenticación antes de activar esta función. Unknown servers! ¡Servidores desconocidos! - No comment provided by engineer. + alert title Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. @@ -7025,6 +7302,11 @@ Para conectarte pide a tu contacto que cree otro enlace y comprueba la conexión Usar hosts .onion No comment provided by engineer. + + Use SOCKS proxy + Usar proxy SOCKS + No comment provided by engineer. + Use SimpleX Chat servers? ¿Usar servidores SimpleX Chat? @@ -7090,16 +7372,16 @@ Para conectarte pide a tu contacto que cree otro enlace y comprueba la conexión Usa la aplicación con una sola mano. 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. + + Username + Nombre de usuario + No comment provided by engineer. + Using SimpleX Chat servers. Usar servidores SimpleX Chat. @@ -7333,7 +7615,7 @@ Para conectarte pide a tu contacto que cree otro enlace y comprueba la conexión Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. Sin Tor o VPN, tu dirección IP será visible para estos servidores XFTP: %@. - No comment provided by engineer. + alert message Wrong database passphrase @@ -7360,11 +7642,6 @@ Para conectarte pide a tu contacto que cree otro enlace y comprueba la conexión Servidor XFTP No comment provided by engineer. - - You - - No comment provided by engineer. - You **must not** use the same database on two devices. **No debes** usar la misma base de datos en dos dispositivos. @@ -7714,11 +7991,21 @@ Repeat connection request? La base de datos no está cifrada - establece una contraseña para cifrarla. No comment provided by engineer. + + Your chat preferences + Tus preferencias de chat + alert title + Your chat profiles Mis perfiles No comment provided by engineer. + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + Tu conexión ha sido trasladada a %@ pero ha ocurrido un error inesperado al redirigirte al perfil. + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). El contacto ha enviado un archivo mayor al máximo admitido (%@). @@ -7734,6 +8021,11 @@ Repeat connection request? Tus contactos permanecerán conectados. No comment provided by engineer. + + Your credentials may be sent unencrypted. + Tus credenciales podrían ser enviadas sin cifrar. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. La base de datos actual será ELIMINADA y SUSTITUIDA por la importada. @@ -7764,13 +8056,16 @@ Repeat connection request? El perfil **%@** será compartido. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Tu perfil es almacenado en tu dispositivo y solamente se comparte con tus contactos. -Los servidores SimpleX no pueden ver tu perfil. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Tu perfil es almacenado en tu dispositivo y solamente se comparte con tus contactos. Los servidores SimpleX no pueden ver tu perfil. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + Tu perfil ha sido modificado. Si lo guardas la actualización será enviada a todos tus contactos. + alert message + Your profile, contacts and delivered messages are stored on your device. Tu perfil, contactos y mensajes se almacenan en tu dispositivo. @@ -7788,7 +8083,7 @@ Los servidores SimpleX no pueden ver tu perfil. Your server address - Dirección de tu servidor + Dirección del servidor No comment provided by engineer. @@ -8500,7 +8795,7 @@ Los servidores SimpleX no pueden ver tu perfil. removed profile picture - imagen de perfil eliminada + ha eliminado la imagen del perfil profile update event chat item @@ -8564,7 +8859,7 @@ last received msg: %2$@ set new profile picture - nueva imagen de perfil + tiene nueva imagen del perfil profile update event chat item @@ -8912,7 +9207,7 @@ last received msg: %2$@ Database passphrase is different from saved in the keychain. - La contraseña de la base de datos es distinta a la almacenada en keychain. + La contraseña de la base de datos es diferente a la almacenada en keychain. No comment provided by engineer. 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 211e512a1e..4b384842b6 100644 --- a/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff +++ b/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff @@ -133,6 +133,10 @@ %@ haluaa muodostaa yhteyden! notification title + + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members No comment provided by engineer. @@ -152,11 +156,31 @@ %d päivää time interval + + %d file(s) are still being downloaded. + forward confirmation reason + + + %d file(s) failed to download. + forward confirmation reason + + + %d file(s) were deleted. + forward confirmation reason + + + %d file(s) were not downloaded. + forward confirmation reason + %d hours %d tuntia time interval + + %d messages not forwarded + alert title + %d min %d min @@ -686,7 +710,7 @@ All profiles - No comment provided by engineer. + profile dropdown All your contacts will remain connected. @@ -868,6 +892,10 @@ Sovelluksen pääsykoodi korvataan itsetuhoutuvalla pääsykoodilla. No comment provided by engineer. + + App session + No comment provided by engineer. + App version Sovellusversio @@ -967,6 +995,10 @@ Hyväksy kuvat automaattisesti No comment provided by engineer. + + Auto-accept settings + alert title + Back Takaisin @@ -990,10 +1022,18 @@ Virheellinen viestin tarkiste No comment provided by engineer. + + Better calls + No comment provided by engineer. + Better groups No comment provided by engineer. + + Better message dates. + No comment provided by engineer. + Better messages Parempia viestejä @@ -1003,6 +1043,18 @@ Better networking No comment provided by engineer. + + Better notifications + No comment provided by engineer. + + + Better security ✅ + No comment provided by engineer. + + + Better user experience + No comment provided by engineer. + Black No comment provided by engineer. @@ -1120,7 +1172,7 @@ Cancel Peruuta - No comment provided by engineer. + alert button Cancel migration @@ -1138,7 +1190,7 @@ Cannot receive file Tiedostoa ei voi vastaanottaa - No comment provided by engineer. + alert title Capacity exceeded - recipient did not receive previously sent messages. @@ -1259,6 +1311,15 @@ Chat-asetukset No comment provided by engineer. + + Chat preferences were changed. + alert message + + + Chat profile + Käyttäjäprofiili + No comment provided by engineer. + Chat theme No comment provided by engineer. @@ -1616,6 +1677,10 @@ This is your own one-time link! Ydinversio: v%@ No comment provided by engineer. + + Corner + No comment provided by engineer. + Correct name to %@? No comment provided by engineer. @@ -1731,6 +1796,10 @@ This is your own one-time link! Mukautettu aika No comment provided by engineer. + + Customizable message shape. + No comment provided by engineer. + Customize theme No comment provided by engineer. @@ -2017,6 +2086,10 @@ This is your own one-time link! Poista vanha tietokanta? No comment provided by engineer. + + Delete or moderate up to 200 messages. + No comment provided by engineer. + Delete pending connection? Poistetaanko odottava yhteys? @@ -2248,6 +2321,10 @@ This is your own one-time link! Do not send history to new members. No comment provided by engineer. + + Do not use credentials with proxy. + No comment provided by engineer. + Don't create address Älä luo osoitetta @@ -2270,7 +2347,8 @@ This is your own one-time link! Download - chat item action + alert button + chat item action Download errors @@ -2285,6 +2363,10 @@ This is your own one-time link! Lataa tiedosto server test step + + Download files + alert action + Downloaded No comment provided by engineer. @@ -2544,6 +2626,10 @@ This is your own one-time link! Virhe osoitteenvaihdossa No comment provided by engineer. + + Error changing connection profile + No comment provided by engineer. + Error changing role Virhe roolin vaihdossa @@ -2554,6 +2640,10 @@ This is your own one-time link! Virhe asetuksen muuttamisessa No comment provided by engineer. + + Error changing to incognito! + No comment provided by engineer. + Error connecting to forwarding server %@. Please try later. No comment provided by engineer. @@ -2669,6 +2759,10 @@ This is your own one-time link! Virhe %@-palvelimien lataamisessa No comment provided by engineer. + + Error migrating settings + No comment provided by engineer. + Error opening chat No comment provided by engineer. @@ -2676,7 +2770,7 @@ This is your own one-time link! Error receiving file Virhe tiedoston vastaanottamisessa - No comment provided by engineer. + alert title Error reconnecting server @@ -2762,10 +2856,14 @@ This is your own one-time link! Virhe keskustelun lopettamisessa No comment provided by engineer. + + Error switching profile + No comment provided by engineer. + Error switching profile! Virhe profiilin vaihdossa! - No comment provided by engineer. + alertTitle Error synchronizing connection @@ -2808,8 +2906,7 @@ This is your own one-time link! Error: %@ Virhe: %@ - file error text - snd error text + alert message Error: URL is invalid @@ -2890,6 +2987,11 @@ This is your own one-time link! File error No comment provided by engineer. + + File errors: +%@ + alert message + File not found - most likely file was deleted or cancelled. file error text @@ -3016,10 +3118,26 @@ This is your own one-time link! Forward chat item action + + Forward %d message(s)? + alert title + Forward and save messages No comment provided by engineer. + + Forward messages + alert action + + + Forward messages without files? + alert message + + + Forward up to 20 messages at once. + No comment provided by engineer. + Forwarded No comment provided by engineer. @@ -3028,6 +3146,10 @@ This is your own one-time link! Forwarded from No comment provided by engineer. + + Forwarding %lld messages + No comment provided by engineer. + Forwarding server %@ failed to connect to destination server %@. Please try later. No comment provided by engineer. @@ -3069,11 +3191,6 @@ Error: %2$@ Koko nimi (valinnainen) No comment provided by engineer. - - Full name: - Koko nimi: - No comment provided by engineer. - Fully decentralized – visible only to members. No comment provided by engineer. @@ -3311,6 +3428,10 @@ Error: %2$@ ICE-palvelimet (yksi per rivi) No comment provided by engineer. + + IP address + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Jos et voi tavata henkilökohtaisesti, näytä QR-koodi videopuhelussa tai jaa linkki. @@ -3383,6 +3504,11 @@ Error: %2$@ Importing archive No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + No comment provided by engineer. + Improved message delivery No comment provided by engineer. @@ -3918,6 +4044,10 @@ This is your link for group %@! Message servers No comment provided by engineer. + + Message shape + No comment provided by engineer. + Message source remains private. No comment provided by engineer. @@ -3961,6 +4091,10 @@ This is your link for group %@! Messages sent No comment provided by engineer. + + Messages were deleted after you selected them. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. No comment provided by engineer. @@ -4103,6 +4237,14 @@ This is your link for group %@! Uusi pääsykoodi No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + No comment provided by engineer. + New chat No comment provided by engineer. @@ -4216,6 +4358,14 @@ This is your link for group %@! No network connection No comment provided by engineer. + + No permission to record speech + No comment provided by engineer. + + + No permission to record video + No comment provided by engineer. + No permission to record voice message Ei lupaa ääniviestin tallentamiseen @@ -4234,6 +4384,10 @@ This is your link for group %@! Nothing selected No comment provided by engineer. + + Nothing to forward! + alert title + Notifications Ilmoitukset @@ -4265,7 +4419,7 @@ This is your link for group %@! Ok Ok - No comment provided by engineer. + alert button Old database @@ -4444,6 +4598,11 @@ Edellyttää VPN:n sallimista. Other %@ servers No comment provided by engineer. + + Other file errors: +%@ + alert message + PING count PING-määrä @@ -4479,6 +4638,10 @@ Edellyttää VPN:n sallimista. Pääsykoodi asetettu! No comment provided by engineer. + + Password + No comment provided by engineer. + Password to show Salasana näytettäväksi @@ -4615,6 +4778,10 @@ Error: %@ Puolalainen käyttöliittymä No comment provided by engineer. + + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Palvelimen osoitteen varmenteen sormenjälki on mahdollisesti virheellinen @@ -4693,14 +4860,6 @@ Error: %@ Profile images No comment provided by engineer. - - Profile name - No comment provided by engineer. - - - Profile name: - No comment provided by engineer. - Profile password Profiilin salasana @@ -4796,6 +4955,10 @@ Enable in *Network & servers* settings. Proxied servers No comment provided by engineer. + + Proxy requires password + No comment provided by engineer. + Push notifications Push-ilmoitukset @@ -5002,6 +5165,10 @@ Enable in *Network & servers* settings. Poista No comment provided by engineer. + + Remove archive? + No comment provided by engineer. + Remove image No comment provided by engineer. @@ -5169,6 +5336,10 @@ Enable in *Network & servers* settings. SMP server No comment provided by engineer. + + SOCKS proxy + No comment provided by engineer. + Safely receive files No comment provided by engineer. @@ -5180,12 +5351,13 @@ Enable in *Network & servers* settings. Save Tallenna - chat item action + alert button + chat item action Save (and notify contacts) Tallenna (ja ilmoita kontakteille) - No comment provided by engineer. + alert button Save and notify contact @@ -5211,11 +5383,6 @@ Enable in *Network & servers* settings. Tallenna arkisto No comment provided by engineer. - - Save auto-accept settings - Tallenna automaattisen hyväksynnän asetukset - No comment provided by engineer. - Save group profile Tallenna ryhmäprofiili @@ -5251,16 +5418,15 @@ Enable in *Network & servers* settings. Tallenna palvelimet? No comment provided by engineer. - - Save settings? - Tallenna asetukset? - No comment provided by engineer. - Save welcome message? Tallenna tervetuloviesti? No comment provided by engineer. + + Save your profile? + alert title + Saved No comment provided by engineer. @@ -5278,6 +5444,10 @@ Enable in *Network & servers* settings. Saved message message info title + + Saving %lld messages + No comment provided by engineer. + Scale No comment provided by engineer. @@ -5351,6 +5521,10 @@ Enable in *Network & servers* settings. Valitse chat item action + + Select chat profile + No comment provided by engineer. + Selected %lld No comment provided by engineer. @@ -5461,7 +5635,7 @@ Enable in *Network & servers* settings. Sender cancelled file transfer. Lähettäjä peruutti tiedoston siirron. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -5553,6 +5727,10 @@ Enable in *Network & servers* settings. Sent via proxy No comment provided by engineer. + + Server + No comment provided by engineer. + Server address No comment provided by engineer. @@ -5662,6 +5840,10 @@ Enable in *Network & servers* settings. Asetukset No comment provided by engineer. + + Settings were changed. + alert message + Shape profile images No comment provided by engineer. @@ -5695,6 +5877,10 @@ Enable in *Network & servers* settings. Jaa linkki No comment provided by engineer. + + Share profile + No comment provided by engineer. + Share this 1-time invite link No comment provided by engineer. @@ -5821,6 +6007,10 @@ Enable in *Network & servers* settings. SimpleX-kertakutsu simplex link type + + SimpleX protocols reviewed by Trail of Bits. + No comment provided by engineer. + Simplified incognito mode No comment provided by engineer. @@ -5848,6 +6038,10 @@ Enable in *Network & servers* settings. Soft blur media + + Some app settings were not migrated. + No comment provided by engineer. + Some file(s) were not exported: No comment provided by engineer. @@ -5976,6 +6170,14 @@ Enable in *Network & servers* settings. SimpleX Chat tuki No comment provided by engineer. + + Switch audio and video during the call. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + No comment provided by engineer. + System Järjestelmä @@ -6010,6 +6212,10 @@ Enable in *Network & servers* settings. TCP_KEEPINTVL No comment provided by engineer. + + Tail + No comment provided by engineer. + Take picture Ota kuva @@ -6193,6 +6399,10 @@ Tämä voi johtua jostain virheestä tai siitä, että yhteys on vaarantunut.The text you pasted is not a SimpleX link. No comment provided by engineer. + + The uploaded database archive will be permanently removed from the servers. + No comment provided by engineer. + Themes No comment provided by engineer. @@ -6309,6 +6519,14 @@ You will be prompted to complete authentication before this feature is enabled.< Sinua kehotetaan suorittamaan todennus loppuun, ennen kuin tämä ominaisuus otetaan käyttöön. No comment provided by engineer. + + To record speech please grant permission to use Microphone. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Jos haluat nauhoittaa ääniviestin, anna lupa käyttää mikrofonia. @@ -6450,7 +6668,7 @@ Sinua kehotetaan suorittamaan todennus loppuun, ennen kuin tämä ominaisuus ote Unknown servers! - No comment provided by engineer. + alert title Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. @@ -6555,6 +6773,10 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja Käytä .onion-isäntiä No comment provided by engineer. + + Use SOCKS proxy + No comment provided by engineer. + Use SimpleX Chat servers? Käytä SimpleX Chat palvelimia? @@ -6614,15 +6836,14 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja Use the app with one hand. No comment provided by engineer. - - User profile - Käyttäjäprofiili - No comment provided by engineer. - User selection No comment provided by engineer. + + Username + No comment provided by engineer. + Using SimpleX Chat servers. Käyttää SimpleX Chat -palvelimia. @@ -6835,7 +7056,7 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. - No comment provided by engineer. + alert message Wrong database passphrase @@ -6859,11 +7080,6 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja XFTP server No comment provided by engineer. - - You - Sinä - No comment provided by engineer. - You **must not** use the same database on two devices. No comment provided by engineer. @@ -7189,11 +7405,19 @@ Repeat connection request? Keskustelut-tietokantasi ei ole salattu - aseta tunnuslause sen salaamiseksi. No comment provided by engineer. + + Your chat preferences + alert title + Your chat profiles Keskusteluprofiilisi No comment provided by engineer. + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Yhteyshenkilösi lähetti tiedoston, joka on suurempi kuin tällä hetkellä tuettu enimmäiskoko (%@). @@ -7209,6 +7433,10 @@ Repeat connection request? Kontaktisi pysyvät yhdistettyinä. No comment provided by engineer. + + Your credentials may be sent unencrypted. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Nykyinen keskustelut-tietokantasi poistetaan ja korvataan tuodulla tietokannalla. @@ -7238,13 +7466,15 @@ Repeat connection request? Profiilisi **%@** jaetaan. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Profiilisi tallennetaan laitteeseesi ja jaetaan vain yhteystietojesi kanssa. -SimpleX-palvelimet eivät näe profiiliasi. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Profiilisi tallennetaan laitteeseesi ja jaetaan vain yhteystietojesi kanssa. SimpleX-palvelimet eivät näe profiiliasi. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + alert message + Your profile, contacts and delivered messages are stored on your device. Profiilisi, kontaktisi ja toimitetut viestit tallennetaan laitteellesi. 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 c05098980e..b672bebc8c 100644 --- a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff @@ -137,6 +137,10 @@ %@ veut se connecter ! notification title + + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ et %lld membres @@ -157,11 +161,31 @@ %d jours time interval + + %d file(s) are still being downloaded. + forward confirmation reason + + + %d file(s) failed to download. + forward confirmation reason + + + %d file(s) were deleted. + forward confirmation reason + + + %d file(s) were not downloaded. + forward confirmation reason + %d hours %d heures time interval + + %d messages not forwarded + alert title + %d min %d min @@ -723,7 +747,7 @@ All profiles Tous les profiles - No comment provided by engineer. + profile dropdown All your contacts will remain connected. @@ -915,6 +939,10 @@ Le code d'accès de l'application est remplacé par un code d'autodestruction. No comment provided by engineer. + + App session + No comment provided by engineer. + App version Version de l'app @@ -1020,6 +1048,10 @@ Images auto-acceptées No comment provided by engineer. + + Auto-accept settings + alert title + Back Retour @@ -1045,11 +1077,19 @@ Mauvais hash de message No comment provided by engineer. + + Better calls + No comment provided by engineer. + Better groups Des groupes plus performants No comment provided by engineer. + + Better message dates. + No comment provided by engineer. + Better messages Meilleurs messages @@ -1060,6 +1100,18 @@ Meilleure gestion de réseau No comment provided by engineer. + + Better notifications + No comment provided by engineer. + + + Better security ✅ + No comment provided by engineer. + + + Better user experience + No comment provided by engineer. + Black Noir @@ -1193,7 +1245,7 @@ Cancel Annuler - No comment provided by engineer. + alert button Cancel migration @@ -1213,7 +1265,7 @@ Cannot receive file Impossible de recevoir le fichier - No comment provided by engineer. + alert title Capacity exceeded - recipient did not receive previously sent messages. @@ -1341,6 +1393,15 @@ Préférences de chat No comment provided by engineer. + + Chat preferences were changed. + alert message + + + Chat profile + Profil d'utilisateur + No comment provided by engineer. + Chat theme Thème de chat @@ -1740,6 +1801,10 @@ Il s'agit de votre propre lien unique ! Version du cœur : v%@ No comment provided by engineer. + + Corner + No comment provided by engineer. + Correct name to %@? Corriger le nom pour %@ ? @@ -1865,6 +1930,10 @@ Il s'agit de votre propre lien unique ! Délai personnalisé No comment provided by engineer. + + Customizable message shape. + No comment provided by engineer. + Customize theme Personnaliser le thème @@ -2159,6 +2228,10 @@ Il s'agit de votre propre lien unique ! Supprimer l'ancienne base de données ? No comment provided by engineer. + + Delete or moderate up to 200 messages. + No comment provided by engineer. + Delete pending connection? Supprimer la connexion en attente ? @@ -2409,6 +2482,10 @@ Il s'agit de votre propre lien unique ! Ne pas envoyer d'historique aux nouveaux membres. No comment provided by engineer. + + Do not use credentials with proxy. + No comment provided by engineer. + Don't create address Ne pas créer d'adresse @@ -2432,7 +2509,8 @@ Il s'agit de votre propre lien unique ! Download Télécharger - chat item action + alert button + chat item action Download errors @@ -2449,6 +2527,10 @@ Il s'agit de votre propre lien unique ! Télécharger le fichier server test step + + Download files + alert action + Downloaded Téléchargé @@ -2724,6 +2806,10 @@ Il s'agit de votre propre lien unique ! Erreur de changement d'adresse No comment provided by engineer. + + Error changing connection profile + No comment provided by engineer. + Error changing role Erreur lors du changement de rôle @@ -2734,6 +2820,10 @@ Il s'agit de votre propre lien unique ! Erreur de changement de paramètre No comment provided by engineer. + + Error changing to incognito! + No comment provided by engineer. + Error connecting to forwarding server %@. Please try later. Erreur de connexion au serveur de redirection %@. Veuillez réessayer plus tard. @@ -2854,6 +2944,10 @@ Il s'agit de votre propre lien unique ! Erreur lors du chargement des serveurs %@ No comment provided by engineer. + + Error migrating settings + No comment provided by engineer. + Error opening chat Erreur lors de l'ouverture du chat @@ -2862,7 +2956,7 @@ Il s'agit de votre propre lien unique ! Error receiving file Erreur lors de la réception du fichier - No comment provided by engineer. + alert title Error reconnecting server @@ -2954,10 +3048,14 @@ Il s'agit de votre propre lien unique ! Erreur lors de l'arrêt du chat No comment provided by engineer. + + Error switching profile + No comment provided by engineer. + Error switching profile! Erreur lors du changement de profil ! - No comment provided by engineer. + alertTitle Error synchronizing connection @@ -3002,8 +3100,7 @@ Il s'agit de votre propre lien unique ! Error: %@ Erreur : %@ - file error text - snd error text + alert message Error: URL is invalid @@ -3090,6 +3187,11 @@ Il s'agit de votre propre lien unique ! Erreur de fichier No comment provided by engineer. + + File errors: +%@ + alert message + File not found - most likely file was deleted or cancelled. Fichier introuvable - le fichier a probablement été supprimé ou annulé. @@ -3225,11 +3327,27 @@ Il s'agit de votre propre lien unique ! Transférer chat item action + + Forward %d message(s)? + alert title + Forward and save messages Transférer et sauvegarder des messages No comment provided by engineer. + + Forward messages + alert action + + + Forward messages without files? + alert message + + + Forward up to 20 messages at once. + No comment provided by engineer. + Forwarded Transféré @@ -3240,6 +3358,10 @@ Il s'agit de votre propre lien unique ! Transféré depuis No comment provided by engineer. + + Forwarding %lld messages + No comment provided by engineer. + Forwarding server %@ failed to connect to destination server %@. Please try later. Le serveur de redirection %@ n'a pas réussi à se connecter au serveur de destination %@. Veuillez réessayer plus tard. @@ -3289,11 +3411,6 @@ Erreur : %2$@ Nom complet (optionnel) No comment provided by engineer. - - Full name: - Nom complet : - No comment provided by engineer. - Fully decentralized – visible only to members. Entièrement décentralisé – visible que par ses membres. @@ -3539,6 +3656,10 @@ Erreur : %2$@ Serveurs ICE (un par ligne) No comment provided by engineer. + + IP address + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Si vous ne pouvez pas vous rencontrer en personne, montrez le code QR lors d'un appel vidéo ou partagez le lien. @@ -3614,6 +3735,11 @@ Erreur : %2$@ Importation de l'archive No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + No comment provided by engineer. + Improved message delivery Amélioration de la transmission des messages @@ -4184,6 +4310,10 @@ Voici votre lien pour le groupe %@ ! Serveurs de messages No comment provided by engineer. + + Message shape + No comment provided by engineer. + Message source remains private. La source du message reste privée. @@ -4234,6 +4364,10 @@ Voici votre lien pour le groupe %@ ! Messages envoyés No comment provided by engineer. + + Messages were deleted after you selected them. + alert message + 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. @@ -4389,6 +4523,14 @@ Voici votre lien pour le groupe %@ ! Nouveau code d'accès No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + No comment provided by engineer. + New chat Nouveau chat @@ -4509,6 +4651,14 @@ Voici votre lien pour le groupe %@ ! Pas de connexion au réseau No comment provided by engineer. + + No permission to record speech + No comment provided by engineer. + + + No permission to record video + No comment provided by engineer. + No permission to record voice message Pas l'autorisation d'enregistrer un message vocal @@ -4529,6 +4679,10 @@ Voici votre lien pour le groupe %@ ! Aucune sélection No comment provided by engineer. + + Nothing to forward! + alert title + Notifications Notifications @@ -4561,7 +4715,7 @@ Voici votre lien pour le groupe %@ ! Ok Ok - No comment provided by engineer. + alert button Old database @@ -4752,6 +4906,11 @@ Nécessite l'activation d'un VPN. Autres serveurs %@ No comment provided by engineer. + + Other file errors: +%@ + alert message + PING count Nombre de PING @@ -4787,6 +4946,10 @@ Nécessite l'activation d'un VPN. Code d'accès défini ! No comment provided by engineer. + + Password + No comment provided by engineer. + Password to show Mot de passe à entrer @@ -4936,6 +5099,10 @@ Erreur : %@ Interface en polonais No comment provided by engineer. + + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Il est possible que l'empreinte du certificat dans l'adresse du serveur soit incorrecte @@ -5021,16 +5188,6 @@ Erreur : %@ Images de profil No comment provided by engineer. - - Profile name - Nom du profil - No comment provided by engineer. - - - Profile name: - Nom du profil : - No comment provided by engineer. - Profile password Mot de passe de profil @@ -5133,6 +5290,10 @@ Activez-le dans les paramètres *Réseau et serveurs*. Serveurs routés via des proxy No comment provided by engineer. + + Proxy requires password + No comment provided by engineer. + Push notifications Notifications push @@ -5354,6 +5515,10 @@ Activez-le dans les paramètres *Réseau et serveurs*. Supprimer No comment provided by engineer. + + Remove archive? + No comment provided by engineer. + Remove image Enlever l'image @@ -5534,6 +5699,10 @@ Activez-le dans les paramètres *Réseau et serveurs*. Serveur SMP No comment provided by engineer. + + SOCKS proxy + No comment provided by engineer. + Safely receive files Réception de fichiers en toute sécurité @@ -5547,12 +5716,13 @@ Activez-le dans les paramètres *Réseau et serveurs*. Save Enregistrer - chat item action + alert button + chat item action Save (and notify contacts) Enregistrer (et en informer les contacts) - No comment provided by engineer. + alert button Save and notify contact @@ -5579,11 +5749,6 @@ Activez-le dans les paramètres *Réseau et serveurs*. Enregistrer l'archive No comment provided by engineer. - - Save auto-accept settings - Enregistrer les paramètres de validation automatique - No comment provided by engineer. - Save group profile Enregistrer le profil du groupe @@ -5619,16 +5784,15 @@ Activez-le dans les paramètres *Réseau et serveurs*. Enregistrer les serveurs ? No comment provided by engineer. - - Save settings? - Enregistrer les paramètres ? - No comment provided by engineer. - Save welcome message? Enregistrer le message d'accueil ? No comment provided by engineer. + + Save your profile? + alert title + Saved Enregistré @@ -5649,6 +5813,10 @@ Activez-le dans les paramètres *Réseau et serveurs*. Message enregistré message info title + + Saving %lld messages + No comment provided by engineer. + Scale Échelle @@ -5729,6 +5897,10 @@ Activez-le dans les paramètres *Réseau et serveurs*. Choisir chat item action + + Select chat profile + No comment provided by engineer. + Selected %lld %lld sélectionné(s) @@ -5847,7 +6019,7 @@ Activez-le dans les paramètres *Réseau et serveurs*. Sender cancelled file transfer. L'expéditeur a annulé le transfert de fichiers. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -5944,6 +6116,10 @@ Activez-le dans les paramètres *Réseau et serveurs*. Envoyé via le proxy No comment provided by engineer. + + Server + No comment provided by engineer. + Server address Adresse du serveur @@ -6064,6 +6240,10 @@ Activez-le dans les paramètres *Réseau et serveurs*. Paramètres No comment provided by engineer. + + Settings were changed. + alert message + Shape profile images Images de profil modelable @@ -6099,6 +6279,10 @@ Activez-le dans les paramètres *Réseau et serveurs*. Partager le lien No comment provided by engineer. + + Share profile + No comment provided by engineer. + Share this 1-time invite link Partager ce lien d'invitation unique @@ -6234,6 +6418,10 @@ Activez-le dans les paramètres *Réseau et serveurs*. Invitation unique SimpleX simplex link type + + SimpleX protocols reviewed by Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Mode incognito simplifié @@ -6264,6 +6452,10 @@ Activez-le dans les paramètres *Réseau et serveurs*. Léger blur media + + Some app settings were not migrated. + No comment provided by engineer. + Some file(s) were not exported: Certains fichiers n'ont pas été exportés : @@ -6404,6 +6596,14 @@ Activez-le dans les paramètres *Réseau et serveurs*. Supporter SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + No comment provided by engineer. + System Système @@ -6439,6 +6639,10 @@ Activez-le dans les paramètres *Réseau et serveurs*. TCP_KEEPINTVL No comment provided by engineer. + + Tail + No comment provided by engineer. + Take picture Prendre une photo @@ -6631,6 +6835,10 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise. Le texte collé n'est pas un lien SimpleX. No comment provided by engineer. + + The uploaded database archive will be permanently removed from the servers. + No comment provided by engineer. + Themes Thèmes @@ -6758,6 +6966,14 @@ You will be prompted to complete authentication before this feature is enabled.< Vous serez invité à confirmer l'authentification avant que cette fonction ne soit activée. No comment provided by engineer. + + To record speech please grant permission to use Microphone. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Pour enregistrer un message vocal, veuillez accorder la permission d'utiliser le microphone. @@ -6911,7 +7127,7 @@ Vous serez invité à confirmer l'authentification avant que cette fonction ne s Unknown servers! Serveurs inconnus ! - No comment provided by engineer. + alert title Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. @@ -7025,6 +7241,10 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Utiliser les hôtes .onions No comment provided by engineer. + + Use SOCKS proxy + No comment provided by engineer. + Use SimpleX Chat servers? Utiliser les serveurs SimpleX Chat ? @@ -7090,16 +7310,15 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Utiliser l'application d'une main. 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. + + Username + No comment provided by engineer. + Using SimpleX Chat servers. Vous utilisez les serveurs SimpleX. @@ -7333,7 +7552,7 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. Sans Tor ni VPN, votre adresse IP sera visible par ces relais XFTP : %@. - No comment provided by engineer. + alert message Wrong database passphrase @@ -7360,11 +7579,6 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Serveur XFTP No comment provided by engineer. - - You - Vous - No comment provided by engineer. - You **must not** use the same database on two devices. Vous **ne devez pas** utiliser la même base de données sur deux appareils. @@ -7714,11 +7928,19 @@ Répéter la demande de connexion ? Votre base de données de chat n'est pas chiffrée - définisez une phrase secrète. No comment provided by engineer. + + Your chat preferences + alert title + Your chat profiles Vos profils de chat No comment provided by engineer. + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Votre contact a envoyé un fichier plus grand que la taille maximale supportée actuellement(%@). @@ -7734,6 +7956,10 @@ Répéter la demande de connexion ? Vos contacts resteront connectés. No comment provided by engineer. + + Your credentials may be sent unencrypted. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Votre base de données de chat actuelle va être SUPPRIMEE et REMPLACEE par celle importée. @@ -7764,13 +7990,15 @@ Répéter la demande de connexion ? Votre profil **%@** sera partagé. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Votre profil est stocké sur votre appareil et est seulement partagé avec vos contacts. -Les serveurs SimpleX ne peuvent pas voir votre profil. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Votre profil est stocké sur votre appareil et est seulement partagé avec vos contacts. Les serveurs SimpleX ne peuvent pas voir votre profil. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + alert message + Your profile, contacts and delivered messages are stored on your device. Votre profil, vos contacts et les messages reçus sont stockés sur votre appareil. 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 f7328eed91..0c8c1635a5 100644 --- a/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff +++ b/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff @@ -99,7 +99,7 @@ %1$@ at %2$@: - %1$@ ekkor: %2$@ + %1$@ ekkor: %2$@: copied message info, <sender> at <time> @@ -119,12 +119,12 @@ %@ is not verified - %@ nem ellenőrzött + %@ nem hitelesített No comment provided by engineer. %@ is verified - %@ ellenőrizve + %@ hitelesítve No comment provided by engineer. @@ -137,6 +137,11 @@ %@ kapcsolódni szeretne! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ és további %lld tag @@ -157,11 +162,36 @@ %d nap time interval + + %d file(s) are still being downloaded. + %d fájl letöltése még folyamatban van. + forward confirmation reason + + + %d file(s) failed to download. + %d fájlt nem sikerült letölteni. + forward confirmation reason + + + %d file(s) were deleted. + %d fájl törölve lett. + forward confirmation reason + + + %d file(s) were not downloaded. + %d fájl nem lett letöltve. + forward confirmation reason + %d hours %d óra time interval + + %d messages not forwarded + %d üzenet nem lett továbbítva + alert title + %d min %d perc @@ -224,7 +254,7 @@ %lld messages blocked by admin - %lld üzenet letiltva az admin által + %lld üzenetet letiltott az admin No comment provided by engineer. @@ -234,7 +264,7 @@ %lld messages moderated by %@ - %lld üzenet moderálva lett %@ által + %@ %lld üzenetet moderált No comment provided by engineer. @@ -324,52 +354,52 @@ **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. + **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 or link 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. + **Új ismerős hozzáadása:** egyszer használható 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. + **Csoport létrehozása:** új csoport létrehozásához. No comment provided by engineer. **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. - **Privátabb**: 20 percenként ellenőrzi az új üzeneteket. Az eszköztoken megosztásra kerül a SimpleX Chat kiszolgálóval, de az nem, hogy hány ismerőse vagy üzenete van. + **Privátabb:** 20 percenként ellenőrzi az új üzeneteket. Az eszköztoken megosztásra kerül a SimpleX Chat-kiszolgálóval, de az nem, hogy hány ismerőse vagy üzenete van. No comment provided by engineer. **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). + **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 az ismerőseitő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. **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. + **Megjegyzés:** NEM tudja visszaállítani vagy megváltoztatni jelmondatát, ha elveszíti azt. No comment provided by engineer. **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. - **Javasolt**: az eszköztoken és az értesítések elküldésre kerülnek a SimpleX Chat értesítési kiszolgálóra, kivéve az üzenet tartalma, mérete vagy az, hogy kitől származik. + **Megjegyzés:** az eszköztoken és az értesítések elküldésre kerülnek a SimpleX Chat értesítési kiszolgálóra, kivéve az üzenet tartalma, mérete vagy az, hogy kitől származik. No comment provided by engineer. **Warning**: Instant push notifications require passphrase saved in Keychain. - **Figyelmeztetés**: Az azonnali push-értesítésekhez a kulcstartóban tárolt jelmondat megadása szükséges. + **Figyelmeztetés:** Az azonnali push-értesítésekhez a kulcstartóban tárolt jelmondat megadása szükséges. No comment provided by engineer. **Warning**: the archive will be removed. - **Figyelem**: az archívum törlésre kerül. + **Figyelmeztetés:** az archívum eltávolításra kerül. No comment provided by engineer. @@ -414,8 +444,8 @@ - optionally notify deleted contacts. - profile names with spaces. - and more! - - opcionális értesítés a törölt kapcsolatokról. -- profilnevek szóközökkel. + - értesíti az ismerősöket a törlésről (nem kötelező) +- profil nevek szóközökkel - és még sok más! No comment provided by engineer. @@ -423,8 +453,8 @@ - voice messages up to 5 minutes. - custom time to disappear. - editing history. - - hangüzenetek legfeljebb 5 perces időtartamig. -- egyedi eltűnési időhatár megadása. + - 5 perc hosszúságú hangüzenetek. +- egyedi üzenet-eltűnési időkorlát. - előzmények szerkesztése. No comment provided by engineer. @@ -492,7 +522,7 @@ <p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p> <p>Üdvözlöm!</p> -<p><a href=„%@”>Csatlakozzon hozzám a SimpleX Chaten</a></p> +<p><a href="%@">Csatlakozzon hozzám a SimpleX Chaten keresztül</a></p> email text @@ -518,8 +548,8 @@ A separate TCP connection will be used **for each contact and group member**. **Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail. - A rendszer külön TCP-kapcsolatot fog használni **minden ismerőshöz és csoporttaghoz**. -**Figyelem**: sok kapcsolódás esetén, az akkumulátor- és adatforgalom fogyasztás jelentősen megnőhet, és egyes kapcsolatok meghiúsulhatnak. + **Minden egyes kapcsolathoz és csoporttaghoz** külön TCP-kapcsolat lesz használva. +**Megjegyzés:** ha sok kapcsolata van, az akkumulátor-használat és az adatforgalom jelentősen megnövekedhet, és néhány kapcsolódási kísérlet sikertelen lehet. No comment provided by engineer. @@ -539,17 +569,17 @@ About SimpleX - A SimpleX-ről + A SimpleXről No comment provided by engineer. About SimpleX Chat - A SimpleX Chat-ről + A SimpleX Chatről No comment provided by engineer. About SimpleX address - A SimpleX címről + A SimpleX-címről No comment provided by engineer. @@ -566,17 +596,17 @@ Accept connection request? - Kapcsolódási kérelem elfogadása? + Kapcsolatkérés elfogadása? No comment provided by engineer. Accept contact request from %@? - Elfogadja %@ kapcsolat kérését? + Elfogadja %@ kapcsolatkérését? notification body Accept incognito - Fogadás inkognítóban + Fogadás inkognitóban accept contact request via notification swipe action @@ -632,7 +662,7 @@ Add welcome message - Üdvözlő üzenet hozzáadása + Üdvözlőüzenet hozzáadása No comment provided by engineer. @@ -677,7 +707,7 @@ Advanced settings - Haladó beállítások + Speciális beállítások No comment provided by engineer. @@ -697,7 +727,7 @@ All data is private to your device. - Minden adat biztonságban van a készülékén. + Minden adat biztonságban van az eszközén. No comment provided by engineer. @@ -712,7 +742,7 @@ All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you. - Minden üzenet törlésre kerül - ez a művelet nem vonható vissza! Az üzenetek CSAK az ön számára törlődnek. + Minden üzenet törlésre kerül - ez a művelet nem vonható vissza! Az üzenetek CSAK az Ön számára törlődnek. No comment provided by engineer. @@ -723,21 +753,21 @@ All profiles Minden profil - No comment provided by engineer. + profile dropdown All your contacts will remain connected. - Minden ismerős kapcsolódva marad. + Minden ismerősével kapcsolatban marad. No comment provided by engineer. All your contacts will remain connected. Profile update will be sent to your contacts. - Ismerőseivel kapcsolatban marad. A profil változtatások frissítésre kerülnek az ismerősöknél. + Az ismerőseivel kapcsolatban marad. A profil-változtatások frissítésre kerülnek az ismerősöknél. No comment provided by engineer. All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. - Minden ismerőse, a beszélgetései és a fájljai biztonságosan titkosításra kerülnek, melyek részletekben feltöltődnek a beállított XFTP átjátszókra. + Minden ismerőse, a beszélgetései és a fájljai biztonságosan titkosításra kerülnek, melyek részletekben feltöltődnek a beállított XFTP-közvetítő-kiszolgálóra. No comment provided by engineer. @@ -747,7 +777,7 @@ Allow calls only if your contact allows them. - A hívások kezdeményezése kizárólag abban az esetben van engedélyezve, ha az ismerőse is engedélyezi. + A hívások kezdeményezése csak abban az esetben van engedélyezve, ha az ismerőse is engedélyezi. No comment provided by engineer. @@ -757,7 +787,7 @@ Allow disappearing messages only if your contact allows it to you. - Az eltűnő üzenetek küldése kizárólag abban az esetben van engedélyezve, ha az ismerőse is engedélyezi az ön számára. + Az eltűnő üzenetek küldése csak abban az esetben van engedélyezve, ha az ismerőse is engedélyezi az Ön számára. No comment provided by engineer. @@ -767,12 +797,12 @@ Allow irreversible message deletion only if your contact allows it to you. (24 hours) - Az üzenetek végleges törlése kizárólag abban az esetben van engedélyezve, ha az ismerőse is engedélyezi. (24 óra) + Az üzenetek végleges törlése csak abban az esetben van engedélyezve, ha az ismerőse is engedélyezi. (24 óra) No comment provided by engineer. Allow message reactions only if your contact allows them. - Az üzenetreakciók küldése kizárólag abban az esetben van engedélyezve, ha az ismerőse is engedélyezi. + Az üzenetreakciók küldése csak abban az esetben van engedélyezve, ha az ismerőse is engedélyezi. No comment provided by engineer. @@ -802,12 +832,12 @@ Allow to send SimpleX links. - A SimpleX hivatkozások küldése engedélyezve van. + A SimpleX-hivatkozások küldése engedélyezve van. No comment provided by engineer. Allow to send files and media. - Fájlok és médiatartalom küldésének engedélyezése. + Fájlok és médiatartalmak küldésének engedélyezése. No comment provided by engineer. @@ -817,7 +847,7 @@ Allow voice messages only if your contact allows them. - A hangüzenetek küldése kizárólag abban az esetben van engedélyezve, ha az ismerőse is engedélyezi. + A hangüzenetek küldése csak abban az esetben van engedélyezve, ha az ismerőse is engedélyezi. No comment provided by engineer. @@ -872,7 +902,7 @@ Always use relay - Mindig használjon átjátszó kiszolgálót + Mindig használjon közvetítő-kiszolgálót No comment provided by engineer. @@ -915,6 +945,11 @@ Az alkalmazás jelkód helyettesítésre kerül egy önmegsemmisítő jelkóddal. No comment provided by engineer. + + App session + Alkalmazás munkamenete + No comment provided by engineer. + App version Alkalmazás verzió @@ -947,7 +982,7 @@ Archive contacts to chat later. - Ismerősök archiválása a későbbi csevegéshez. + Az ismerősök archiválása a későbbi csevegéshez. No comment provided by engineer. @@ -1012,7 +1047,7 @@ Auto-accept contact requests - Kapcsolódási kérelmek automatikus elfogadása + Kapcsolatkérések automatikus elfogadása No comment provided by engineer. @@ -1020,6 +1055,11 @@ Képek automatikus elfogadása No comment provided by engineer. + + Auto-accept settings + Beállítások automatikus elfogadása + alert title + Back Vissza @@ -1042,7 +1082,12 @@ Bad message hash - Hibás az üzenet ellenőrzőösszege + Hibás az üzenet hasító értéke + No comment provided by engineer. + + + Better calls + Továbbfejlesztett hívásélmény No comment provided by engineer. @@ -1050,6 +1095,11 @@ Javított csoportok No comment provided by engineer. + + Better message dates. + Továbbfejlesztett üzenetdátumok. + No comment provided by engineer. + Better messages Jobb üzenetek @@ -1060,6 +1110,21 @@ Jobb hálózatkezelés No comment provided by engineer. + + Better notifications + Továbbfejlesztett értesítések + No comment provided by engineer. + + + Better security ✅ + Továbbfejlesztett biztonság ✅ + No comment provided by engineer. + + + Better user experience + Továbbfejlesztett felhasználói élmény + No comment provided by engineer. + Black Fekete @@ -1097,7 +1162,7 @@ Blocked by admin - Letiltva az admin által + Az admin letiltotta No comment provided by engineer. @@ -1142,7 +1207,7 @@ By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). - Csevegési profil (alapértelmezett) vagy [kapcsolat alapján] (https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BÉTA). + A csevegési profillal (alapértelmezett), vagy a [kapcsolattal] (https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BÉTA). No comment provided by engineer. @@ -1177,7 +1242,7 @@ Can't invite contact! - Ismerősök meghívása le van tiltva! + Ismerős meghívása nem lehetséges! No comment provided by engineer. @@ -1193,7 +1258,7 @@ Cancel Mégse - No comment provided by engineer. + alert button Cancel migration @@ -1202,7 +1267,7 @@ Cannot access keychain to save database password - Nem lehet hozzáférni a kulcstartóhoz az adatbázis jelszavának mentéséhez + Nem lehet hozzáférni a kulcstartóhoz az adatbázisjelszó mentéséhez No comment provided by engineer. @@ -1213,7 +1278,7 @@ Cannot receive file Nem lehet fogadni a fájlt - No comment provided by engineer. + alert title Capacity exceeded - recipient did not receive previously sent messages. @@ -1232,7 +1297,7 @@ Change database passphrase? - Adatbázis jelmondat megváltoztatása? + Adatbázis-jelmondat megváltoztatása? No comment provided by engineer. @@ -1341,6 +1406,16 @@ Csevegési beállítások No comment provided by engineer. + + Chat preferences were changed. + A csevegési beállítások megváltoztak. + alert message + + + Chat profile + Csevegési profil + No comment provided by engineer. + Chat theme Csevegés témája @@ -1363,7 +1438,7 @@ Choose _Migrate from another device_ on the new device and scan QR code. - Válassza az _Átköltöztetés egy másik eszközről_ opciót az új eszközön és olvassa be a QR-kódot. + Válassza az _Átköltöztetés egy másik eszközről_ opciót az új eszközén és olvassa be a QR-kódot. No comment provided by engineer. @@ -1433,7 +1508,7 @@ Compare security codes with your contacts. - Biztonsági kódok összehasonlítása az ismerősökkel. + Biztonsági kódok összehasonlítása az ismerősökével. No comment provided by engineer. @@ -1443,7 +1518,7 @@ Configure ICE servers - ICE kiszolgálók beállítása + ICE-kiszolgálók beállítása No comment provided by engineer. @@ -1493,7 +1568,7 @@ Confirm that you remember database passphrase to migrate it. - Erősítse meg, hogy emlékszik az adatbázis jelmondatára az átköltöztetéshez. + Az átköltöztetéshez erősítse meg, hogy emlékszik az adatbázis jelmondatára. No comment provided by engineer. @@ -1518,7 +1593,7 @@ Connect to desktop - Kapcsolódás számítógéphez + Társítás számítógéppel No comment provided by engineer. @@ -1535,14 +1610,14 @@ Connect to yourself? This is your own SimpleX address! Kapcsolódás saját magához? -Ez az ön SimpleX címe! +Ez az Ön SimpleX-címe! No comment provided by engineer. Connect to yourself? This is your own one-time link! Kapcsolódás saját magához? -Ez az egyszer használatos hivatkozása! +Ez az Ön egyszer használható hivatkozása! No comment provided by engineer. @@ -1557,7 +1632,7 @@ Ez az egyszer használatos hivatkozása! Connect via one-time link - Kapcsolódás egyszer használatos hivatkozáson keresztül + Kapcsolódás egyszer használható hivatkozáson keresztül No comment provided by engineer. @@ -1572,7 +1647,7 @@ Ez az egyszer használatos hivatkozása! Connected desktop - Csatlakoztatott számítógép + Társított számítógép No comment provided by engineer. @@ -1597,7 +1672,7 @@ Ez az egyszer használatos hivatkozása! Connecting to server… (error: %@) - Kapcsolódás a kiszolgálóhoz... (hiba: %@) + Kapcsolódás a kiszolgálóhoz… (hiba: %@) No comment provided by engineer. @@ -1637,7 +1712,7 @@ Ez az egyszer használatos hivatkozása! Connection request sent! - Kapcsolódási kérés elküldve! + Kapcsolatkérés elküldve! No comment provided by engineer. @@ -1647,7 +1722,7 @@ Ez az egyszer használatos hivatkozása! Connection timeout - Kapcsolat időtúllépés + Időtúllépés kapcsolódáskor No comment provided by engineer. @@ -1667,7 +1742,7 @@ Ez az egyszer használatos hivatkozása! Contact already exists - Létező ismerős + Az ismerős már létezik No comment provided by engineer. @@ -1692,7 +1767,7 @@ Ez az egyszer használatos hivatkozása! Contact name - Ismerős neve + Csak név No comment provided by engineer. @@ -1712,7 +1787,7 @@ Ez az egyszer használatos hivatkozása! Contacts can mark messages for deletion; you will be able to view them. - Az ismerősei törlésre jelölhetnek üzeneteket; ön majd meg tudja nézni azokat. + Az ismerősei törlésre jelölhetnek üzeneteket; Ön majd meg tudja nézni azokat. No comment provided by engineer. @@ -1740,6 +1815,11 @@ Ez az egyszer használatos hivatkozása! Alapverziószám: v%@ No comment provided by engineer. + + Corner + Sarok + No comment provided by engineer. + Correct name to %@? Név javítása erre: %@? @@ -1752,7 +1832,7 @@ Ez az egyszer használatos hivatkozása! Create SimpleX address - SimpleX cím létrehozása + SimpleX-cím létrehozása No comment provided by engineer. @@ -1762,7 +1842,7 @@ Ez az egyszer használatos hivatkozása! Create an address to let people connect with you. - Cím létrehozása, hogy az emberek kapcsolatba léphessenek önnel. + Cím létrehozása, hogy az emberek kapcsolatba léphessenek Önnel. No comment provided by engineer. @@ -1777,7 +1857,7 @@ Ez az egyszer használatos hivatkozása! Create group link - Csoportos hivatkozás létrehozása + Csoporthivatkozás létrehozása No comment provided by engineer. @@ -1787,7 +1867,7 @@ Ez az egyszer használatos hivatkozása! Create new profile in [desktop app](https://simplex.chat/downloads/). 💻 - Új profil létrehozása az [asztali kliensben](https://simplex.chat/downloads/). 💻 + Új profil létrehozása a [számítógép-alkalmazásban](https://simplex.chat/downloads/). 💻 No comment provided by engineer. @@ -1797,7 +1877,7 @@ Ez az egyszer használatos hivatkozása! Create queue - Várólista létrehozása + Sorbaállítás létrehozása server test step @@ -1832,7 +1912,7 @@ Ez az egyszer használatos hivatkozása! Creating archive link - Archív hivatkozás létrehozása + Archívum hivatkozás létrehozása No comment provided by engineer. @@ -1865,6 +1945,11 @@ Ez az egyszer használatos hivatkozása! Személyreszabott idő No comment provided by engineer. + + Customizable message shape. + Testreszabható üzenetbuborékok. + No comment provided by engineer. + Customize theme Téma személyre szabása @@ -1882,17 +1967,17 @@ Ez az egyszer használatos hivatkozása! Database ID - Adatbázis ID + Adatbázis-azonosító No comment provided by engineer. Database ID: %d - Adatbázis azonosító: %d + Adatbázis-azonosító: %d copied message info Database IDs and Transport isolation option. - Adatbázis azonosítók és átviteli izolációs beállítások. + Adatbázis-azonosítók és átvitel-izolációs beállítások. No comment provided by engineer. @@ -1908,40 +1993,40 @@ Ez az egyszer használatos hivatkozása! Database encryption passphrase will be updated and stored in the keychain. - Az adatbázis titkosítási jelmondata frissül és tárolódik a kulcstartóban. + Az adatbázis titkosítási jelmondata frissítve lesz és a kulcstartóban kerül tárolásra. No comment provided by engineer. Database encryption passphrase will be updated. - Adatbázis titkosítási jelmondat frissítve lesz. + Az datbázis titkosítási jelmondata frissítve lesz. No comment provided by engineer. Database error - Adatbázis hiba + Adatbázishiba No comment provided by engineer. Database is encrypted using a random passphrase, you can change it. - Az adatbázis egy véletlenszerű jelmondattal van titkosítva, megváltoztatható. + Az adatbázis egy véletlenszerű jelmondattal van titkosítva, ami megváltoztatható. No comment provided by engineer. Database is encrypted using a random passphrase. Please change it before exporting. - Az adatbázis egy véletlenszerű jelmondattal van titkosítva. Exportálás előtti módosítás szükséges. + Az adatbázis egy véletlenszerű jelmondattal van titkosítva. Exportálás előtt változtassa meg. No comment provided by engineer. Database passphrase - Adatbázis jelmondat + Adatbázis-jelmondat No comment provided by engineer. Database passphrase & export - Adatbázis jelmondat és exportálás + Adatbázis-jelmondat és -exportálás No comment provided by engineer. @@ -1962,7 +2047,7 @@ Ez az egyszer használatos hivatkozása! Database will be encrypted and the passphrase stored in the keychain. - Az adatbázis titkosítva lesz, a jelmondat pedig a kulcstartóban lesz tárolva. + Az adatbázis titkosítva lesz, a jelmondat pedig a kulcstartóban kerül tárolásra. No comment provided by engineer. @@ -1975,7 +2060,7 @@ Ez az egyszer használatos hivatkozása! Database will be migrated when the app restarts - Az adatbázis az alkalmazás újraindításakor migrálásra kerül + Az adatbázis az alkalmazás újraindításakor átköltöztetésre kerül No comment provided by engineer. @@ -2159,9 +2244,14 @@ Ez az egyszer használatos hivatkozása! Régi adatbázis törlése? No comment provided by engineer. + + Delete or moderate up to 200 messages. + Legfeljebb 200 üzenet egyszerre való törlése, vagy moderálása. + No comment provided by engineer. + Delete pending connection? - Függő kapcsolatfelvételi kérések törlése? + Függőben lévő ismerőskérelem törlése? No comment provided by engineer. @@ -2171,12 +2261,12 @@ Ez az egyszer használatos hivatkozása! Delete queue - Várólista törlése + Sorbaállítás törlése server test step Delete up to 20 messages at once. - Legfeljebb 20 üzenet törlése egyszerre. + Legfeljebb 20 üzenet egyszerre való törlése. No comment provided by engineer. @@ -2221,7 +2311,7 @@ Ez az egyszer használatos hivatkozása! Delivery receipts! - Üzenet kézbesítési jelentések! + Kézbesítési jelentések! No comment provided by engineer. @@ -2236,7 +2326,7 @@ Ez az egyszer használatos hivatkozása! Desktop app version %@ is not compatible with this app. - Az asztali kliens verziója %@ nem kompatibilis ezzel az alkalmazással. + A számítógép-alkalmazás verziója %@ nem kompatibilis ezzel az alkalmazással. No comment provided by engineer. @@ -2266,7 +2356,7 @@ Ez az egyszer használatos hivatkozása! Details - Részletek + További részletek No comment provided by engineer. @@ -2291,17 +2381,17 @@ Ez az egyszer használatos hivatkozá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. + Az eszközön 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. - 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. + Az eszközön 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 names, avatars and transport isolation. - Különböző nevek, avatarok és átviteli izoláció. + Különböző nevek, profilképek és átvitel-izoláció. No comment provided by engineer. @@ -2321,7 +2411,7 @@ Ez az egyszer használatos hivatkozása! Disable SimpleX Lock - SimpleX zár kikapcsolása + SimpleX-zár kikapcsolása authentication reason @@ -2386,7 +2476,7 @@ Ez az egyszer használatos hivatkozása! Do NOT send messages directly, even if your or destination server does not support private routing. - Ne küldjön üzeneteket közvetlenül, még akkor sem, ha az ön kiszolgálója vagy a célkiszolgáló nem támogatja a privát útválasztást. + Ne küldjön üzeneteket közvetlenül, még akkor sem, ha az Ön kiszolgálója vagy a célkiszolgáló nem támogatja a privát útválasztást. No comment provided by engineer. @@ -2401,7 +2491,7 @@ Ez az egyszer használatos hivatkozása! Do it later - Későbbre halaszt + Befejezés később No comment provided by engineer. @@ -2409,6 +2499,11 @@ Ez az egyszer használatos hivatkozása! Az előzmények ne kerüljenek elküldésre az új tagok számára. No comment provided by engineer. + + Do not use credentials with proxy. + Ne használja a hitelesítőadatokat proxyval. + No comment provided by engineer. + Don't create address Ne hozzon létre címet @@ -2432,7 +2527,8 @@ Ez az egyszer használatos hivatkozása! Download Letöltés - chat item action + alert button + chat item action Download errors @@ -2449,6 +2545,11 @@ Ez az egyszer használatos hivatkozása! Fájl letöltése server test step + + Download files + Fájlok letöltése + alert action + Downloaded Letöltve @@ -2471,7 +2572,7 @@ Ez az egyszer használatos hivatkozása! Duplicate display name! - Duplikált megjelenítési név! + Duplikált megjelenített név! No comment provided by engineer. @@ -2501,7 +2602,7 @@ Ez az egyszer használatos hivatkozása! Enable SimpleX Lock - SimpleX zár bekapcsolása + SimpleX-zár bekapcsolása authentication reason @@ -2586,7 +2687,7 @@ Ez az egyszer használatos hivatkozása! Encrypt stored files & media - Tárolt fájlok és médiatartalmak titkosítása + A tárolt fájlok- és a médiatartalmak titkosítása No comment provided by engineer. @@ -2606,12 +2707,12 @@ Ez az egyszer használatos hivatkozása! Encrypted message: database error - Titkosított üzenet: adatbázis hiba + Titkosított üzenet: adatbázishiba notification Encrypted message: database migration error - Titkosított üzenet: adatbázis-átköltöztetés hiba + Titkosított üzenet: adatbázis-átköltöztetési hiba notification @@ -2646,7 +2747,7 @@ Ez az egyszer használatos hivatkozása! Enter correct passphrase. - Helyes jelmondat bevitele. + Adja meg a helyes jelmondatot. No comment provided by engineer. @@ -2676,17 +2777,17 @@ Ez az egyszer használatos hivatkozása! Enter this device name… - Eszköznév megadása… + Adja meg ennek az eszköznek a nevét… No comment provided by engineer. Enter welcome message… - Üdvözlő üzenet megadása… + Üdvözlőüzenet megadása… placeholder Enter welcome message… (optional) - Üdvözlő üzenet megadása… (opcionális) + Üdvözlőüzenet megadása… (nem kötelező) placeholder @@ -2706,7 +2807,7 @@ Ez az egyszer használatos hivatkozása! Error accepting contact request - Hiba történt a kapcsolatfelvételi kérelem elfogadásakor + Hiba történt a kapcsolatkérés elfogadásakor No comment provided by engineer. @@ -2716,7 +2817,7 @@ Ez az egyszer használatos hivatkozása! Error adding member(s) - Hiba a tag(-ok) hozzáadásakor + Hiba a tag(ok) hozzáadásakor No comment provided by engineer. @@ -2724,6 +2825,11 @@ Ez az egyszer használatos hivatkozása! Hiba a cím megváltoztatásakor No comment provided by engineer. + + Error changing connection profile + Hiba a kapcsolati profilra való váltáskor + No comment provided by engineer. + Error changing role Hiba a szerepkör megváltoztatásakor @@ -2734,6 +2840,11 @@ Ez az egyszer használatos hivatkozása! Hiba a beállítás megváltoztatásakor No comment provided by engineer. + + Error changing to incognito! + Hiba az inkognitóprofilra való váltáskor! + No comment provided by engineer. + Error connecting to forwarding server %@. Please try later. Hiba a(z) %@ továbbító kiszolgálóhoz való kapcsolódáskor. Próbálja meg később. @@ -2751,7 +2862,7 @@ Ez az egyszer használatos hivatkozása! Error creating group link - Hiba a csoport hivatkozásának létrehozásakor + Hiba a csoporthivatkozás létrehozásakor No comment provided by engineer. @@ -2806,7 +2917,7 @@ Ez az egyszer használatos hivatkozása! Error deleting user profile - Hiba a felhasználói profil törlésekor + Hiba a felhasználó-profil törlésekor No comment provided by engineer. @@ -2851,7 +2962,12 @@ Ez az egyszer használatos hivatkozása! Error loading %@ servers - Hiba a %@ kiszolgálók betöltésekor + Hiba a(z) %@ -kiszolgálók betöltésekor + No comment provided by engineer. + + + Error migrating settings + Hiba a beallítások átköltöztetésekor No comment provided by engineer. @@ -2862,7 +2978,7 @@ Ez az egyszer használatos hivatkozása! Error receiving file Hiba a fájl fogadásakor - No comment provided by engineer. + alert title Error reconnecting server @@ -2886,12 +3002,12 @@ Ez az egyszer használatos hivatkozása! Error saving %@ servers - Hiba történt a %@ kiszolgálók mentése közben + Hiba történt a(z) %@ -kiszolgálók mentésekor No comment provided by engineer. Error saving ICE servers - Hiba az ICE kiszolgálók mentésekor + Hiba az ICE-kiszolgálók mentésekor No comment provided by engineer. @@ -2901,7 +3017,7 @@ Ez az egyszer használatos hivatkozása! Error saving passcode - Hiba a jelkód mentése közben + Hiba a jelkód mentésekor No comment provided by engineer. @@ -2916,12 +3032,12 @@ Ez az egyszer használatos hivatkozása! Error saving user password - Hiba a felhasználó jelszavának mentésekor + Hiba a felhasználói jelszó mentésekor No comment provided by engineer. Error scanning code: %@ - Hiba a kód beolvasása közben: %@ + Hiba a kód beolvasásakor: %@ No comment provided by engineer. @@ -2941,7 +3057,7 @@ Ez az egyszer használatos hivatkozása! Error setting delivery receipts! - Hiba történt a kézbesítési igazolások beállításakor! + Hiba történt a kézbesítési jelentések beállításakor! No comment provided by engineer. @@ -2954,19 +3070,24 @@ Ez az egyszer használatos hivatkozása! Hiba a csevegés megállításakor No comment provided by engineer. + + Error switching profile + Hiba a profilváltáskor + No comment provided by engineer. + Error switching profile! - Hiba a profil váltásakor! - No comment provided by engineer. + Hiba a profilváltásakor! + alertTitle Error synchronizing connection - Hiba a kapcsolat szinkronizálása közben + Hiba a kapcsolat szinkronizálásakor No comment provided by engineer. Error updating group link - Hiba a csoport hivatkozás frissítésekor + Hiba a csoporthivatkozás frissítésekor No comment provided by engineer. @@ -2981,7 +3102,7 @@ Ez az egyszer használatos hivatkozása! Error updating user privacy - Hiba a felhasználói beállítások frissítésekor + Hiba a felhasználói adatvédelem frissítésekor No comment provided by engineer. @@ -2991,7 +3112,7 @@ Ez az egyszer használatos hivatkozása! Error verifying passphrase: - Hiba a jelmondat ellenőrzésekor: + Hiba a jelmondat hitelesítésekor: No comment provided by engineer. @@ -3002,17 +3123,16 @@ Ez az egyszer használatos hivatkozása! Error: %@ Hiba: %@ - file error text - snd error text + alert message Error: URL is invalid - Hiba: az URL érvénytelen + Hiba: a webcím érvénytelen No comment provided by engineer. Error: no database file - Hiba: nincs adatbázis fájl + Hiba: nincs adatbázisfájl No comment provided by engineer. @@ -3062,7 +3182,7 @@ Ez az egyszer használatos hivatkozása! Exporting database archive… - Adatbázis archívum exportálása… + Adatbázis-archívum exportálása… No comment provided by engineer. @@ -3077,7 +3197,7 @@ Ez az egyszer használatos hivatkozása! Faster joining and more reliable messages. - Gyorsabb csatlakozás és megbízhatóbb üzenet kézbesítés. + Gyorsabb csatlakozás és megbízhatóbb üzenetkézbesítés. No comment provided by engineer. @@ -3090,6 +3210,13 @@ Ez az egyszer használatos hivatkozása! Fájlhiba No comment provided by engineer. + + File errors: +%@ + Fájlhiba: +%@ + alert message + File not found - most likely file was deleted or cancelled. A fájl nem található - valószínűleg a fájlt törölték vagy visszavonták. @@ -3137,27 +3264,27 @@ Ez az egyszer használatos hivatkozása! Files & media - Fájlok és média + Fájlok és médiatartalmak No comment provided by engineer. Files and media - Fájlok és médiatartalom + Fájlok és médiatartalmak chat feature Files and media are prohibited in this group. - A fájlok- és a médiatartalom küldése le van tiltva ebben a csoportban. + A fájlok- és a médiatartalmak le vannak tiltva ebben a csoportban. No comment provided by engineer. Files and media not allowed - Fájlok és média tartalom küldése le van tiltva + A fájlok- és médiatartalmak nincsenek engedélyezve No comment provided by engineer. Files and media prohibited! - A fájlok- és a médiatartalom küldése le van tiltva! + A fájlok- és a médiatartalmak küldése le van tiltva! No comment provided by engineer. @@ -3225,11 +3352,31 @@ Ez az egyszer használatos hivatkozása! Továbbítás chat item action + + Forward %d message(s)? + %d üzenet továbbítása? + alert title + Forward and save messages Üzenetek továbbítása és mentése No comment provided by engineer. + + Forward messages + Üzenetek továbbítása + alert action + + + Forward messages without files? + Üzenetek továbbítása fájlok nélkül? + alert message + + + Forward up to 20 messages at once. + Legfeljebb 20 üzenet egyszerre való továbbítása. + No comment provided by engineer. + Forwarded Továbbított @@ -3240,32 +3387,37 @@ Ez az egyszer használatos hivatkozása! Továbbítva innen: No comment provided by engineer. + + Forwarding %lld messages + %lld üzenet továbbítása + No comment provided by engineer. + Forwarding server %@ failed to connect to destination server %@. Please try later. - A(z) %@ továbbító kiszolgáló nem tudott csatlakozni a(z) %@ célkiszolgálóhoz. Próbálja meg később. + A(z) %@ továbbító-kiszolgáló nem tudott csatlakozni a(z) %@ célkiszolgálóhoz. Próbálja meg később. No comment provided by engineer. Forwarding server address is incompatible with network settings: %@. - A továbbító kiszolgáló címe nem kompatibilis a hálózati beállításokkal: %@. + A továbbító-kiszolgáló címe nem kompatibilis a hálózati beállításokkal: %@. No comment provided by engineer. Forwarding server version is incompatible with network settings: %@. - A továbbító kiszolgáló verziója nem kompatibilis a hálózati beállításokkal: %@. + A továbbító-kiszolgáló verziója nem kompatibilis a hálózati beállításokkal: %@. No comment provided by engineer. Forwarding server: %1$@ Destination server error: %2$@ - Továbbító kiszolgáló: %1$@ + Továbbító-kiszolgáló: %1$@ Célkiszolgáló hiba:%2$@ snd error text Forwarding server: %1$@ Error: %2$@ - Továbbító kiszolgáló: %1$@ + Továbbító-kiszolgáló: %1$@ Hiba: %2$@ snd error text @@ -3286,17 +3438,12 @@ Hiba: %2$@ Full name (optional) - Teljes név (opcionális) - No comment provided by engineer. - - - Full name: - Teljes név: + Teljes név (nem kötelező) No comment provided by engineer. Fully decentralized – visible only to members. - Teljesen decentralizált - kizárólag tagok számára látható. + Teljesen decentralizált - csak a tagok számára látható. No comment provided by engineer. @@ -3306,7 +3453,7 @@ Hiba: %2$@ Further reduced battery usage - Tovább csökkentett akkumulátor használat + Tovább csökkentett akkumulátor-használat No comment provided by engineer. @@ -3346,37 +3493,37 @@ Hiba: %2$@ Group full name (optional) - Csoport teljes neve (opcionális) + Csoport teljes neve (nem kötelező) No comment provided by engineer. Group image - Csoportkép + Csoport profilképe No comment provided by engineer. Group invitation - Csoportos meghívó + Csoportmeghívó No comment provided by engineer. Group invitation expired - A csoport meghívó lejárt + A csoportmeghívó lejárt No comment provided by engineer. Group invitation is no longer valid, it was removed by sender. - A csoport meghívó már nem érvényes, a küldője törölte. + A csoportmeghívó már nem érvényes, a küldője eltávolította. No comment provided by engineer. Group link - Csoport hivatkozás + Csoporthivatkozás No comment provided by engineer. Group links - Csoport hivatkozások + Csoporthivatkozások No comment provided by engineer. @@ -3391,7 +3538,7 @@ Hiba: %2$@ Group members can send SimpleX links. - A csoport tagjai küldhetnek SimpleX hivatkozásokat. + A csoport tagjai küldhetnek SimpleX-hivatkozásokat. No comment provided by engineer. @@ -3426,7 +3573,7 @@ Hiba: %2$@ Group preferences - Csoport beállítások + Csoportbeállítások No comment provided by engineer. @@ -3441,17 +3588,17 @@ Hiba: %2$@ Group welcome message - Csoport üdvözlő üzenete + A csoport üdvözlőüzenete No comment provided by engineer. Group will be deleted for all members - this cannot be undone! - Csoport törlésre kerül minden tag számára - ez a művelet nem vonható vissza! + A csoport törlésre kerül minden tag számára - ez a művelet nem vonható vissza! No comment provided by engineer. Group will be deleted for you - this cannot be undone! - A csoport törlésre kerül az ön számára - ez a művelet nem vonható vissza! + A csoport törlésre kerül az Ön számára - ez a művelet nem vonható vissza! No comment provided by engineer. @@ -3461,7 +3608,7 @@ Hiba: %2$@ Hidden - Rejtett + Se név, se üzenet No comment provided by engineer. @@ -3471,12 +3618,12 @@ Hiba: %2$@ Hidden profile password - Rejtett profil jelszó + Rejtett profiljelszó No comment provided by engineer. Hide - Elrejt + Összecsukás chat item action @@ -3491,7 +3638,7 @@ Hiba: %2$@ Hide: - Elrejt: + Elrejtés: No comment provided by engineer. @@ -3526,7 +3673,7 @@ Hiba: %2$@ How to use your servers - Kiszolgálók használata + Saját kiszolgálók használata No comment provided by engineer. @@ -3539,6 +3686,11 @@ Hiba: %2$@ ICE-kiszolgálók (soronként egy) No comment provided by engineer. + + IP address + IP-cím + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Ha nem tud személyesen találkozni, mutassa meg a QR-kódot egy videohívás közben, vagy ossza meg a hivatkozást. @@ -3546,7 +3698,7 @@ Hiba: %2$@ If you enter this passcode when opening the app, all app data will be irreversibly removed! - Ha az alkalmazás megnyitásakor megadja ezt a jelkódot, az összes alkalmazásadat véglegesen törlődik! + Ha az alkalmazás megnyitásakor megadja ezt a jelkódot, az összes alkalmazásadat véglegesen eltávolításra kerül! No comment provided by engineer. @@ -3556,12 +3708,12 @@ Hiba: %2$@ If you need to use the chat now tap **Do it later** below (you will be offered to migrate the database when you restart the app). - Ha most kell használnia a csevegést, koppintson a ** Csináld később** elemre (az alkalmazás újraindításakor felajánlásra kerül az adatbázis áttelepítése). + Ha most kell használnia a csevegést, koppintson alább a **Befejezés később** lehetőségre (az alkalmazás újraindításakor felajánlásra kerül az adatbázis átköltöztetése). No comment provided by engineer. Ignore - Figyelmen kívül hagyás + Mellőzés No comment provided by engineer. @@ -3614,9 +3766,16 @@ Hiba: %2$@ Archívum importálása No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + Továbbfejlesztett kézbesítés, csökkentett adatforgalom-használat. +További fejlesztések hamarosan! + No comment provided by engineer. + Improved message delivery - Továbbfejlesztett üzenetküldés + Továbbfejlesztett üzenetkézbesítés No comment provided by engineer. @@ -3651,12 +3810,12 @@ Hiba: %2$@ Incognito groups - Inkognitó csoportok + Inkognitócsoportok No comment provided by engineer. Incognito mode - Inkognitó mód + Inkognitómód No comment provided by engineer. @@ -3681,7 +3840,7 @@ Hiba: %2$@ Incompatible database version - Nem kompatibilis adatbázis verzió + Nem kompatibilis adatbázis-verzió No comment provided by engineer. @@ -3717,7 +3876,7 @@ Hiba: %2$@ Instant push notifications will be hidden! - Az azonnali push értesítések elrejtésre kerülnek! + Az azonnali push-értesítések elrejtésre kerülnek! No comment provided by engineer. @@ -3743,12 +3902,12 @@ Hiba: %2$@ Invalid connection link - Érvénytelen kapcsolati hivatkozás + Érvénytelen kapcsolattartási hivatkozás No comment provided by engineer. Invalid display name! - Érvénytelen megjelenítendő felhaszálónév! + Érvénytelen megjelenítendő név! No comment provided by engineer. @@ -3823,7 +3982,7 @@ Hiba: %2$@ 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. + 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. @@ -3839,7 +3998,7 @@ Hiba: %2$@ It protects your IP address and connections. - Védi az IP-címét és a kapcsolatokat. + Védi az IP-címét és a kapcsolatait. No comment provided by engineer. @@ -3891,7 +4050,7 @@ Hiba: %2$@ Join your group? This is your link for group %@! Csatlakozik a csoportjához? -Ez az ön hivatkozása a(z) %@ csoporthoz! +Ez az Ön hivatkozása a(z) %@ csoporthoz! No comment provided by engineer. @@ -3926,12 +4085,12 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! KeyChain error - Kulcstartó hiba + Kulcstartóhiba No comment provided by engineer. Keychain error - Kulcstartó hiba + Kulcstartóhiba No comment provided by engineer. @@ -3966,7 +4125,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Let's talk in SimpleX Chat - Beszélgessünk a SimpleX Chat-ben + Beszélgessünk a SimpleX Chatben email subject @@ -3981,17 +4140,17 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Link mobile and desktop apps! 🔗 - Társítsa össze a mobil és az asztali alkalmazásokat! 🔗 + Társítsa össze a hordozható eszköz- és számítógépes alkalmazásokat! 🔗 No comment provided by engineer. Linked desktop options - Összekapcsolt számítógép beállítások + Társított számítógép beállítások No comment provided by engineer. Linked desktops - Összekapcsolt számítógépek + Társított számítógépek No comment provided by engineer. @@ -4046,7 +4205,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@). - Győződjön meg arról, hogy a %@ kiszolgálócímek megfelelő formátumúak, sorszeparáltak és nincsenek duplikálva (%@). + Győződjön meg arról, hogy a(z) %@ kiszolgálócímek megfelelő formátumúak, sorszeparáltak és nincsenek duplikálva (%@). No comment provided by engineer. @@ -4056,7 +4215,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* - Sokan kérdezték: *ha a SimpleX-nek nincsenek felhasználói azonosítói, akkor hogyan tud üzeneteket kézbesíteni?* + Sokan kérdezték: *ha a SimpleX Chatnek nincsenek felhasználó-azonosítói, akkor hogyan tud üzeneteket kézbesíteni?* No comment provided by engineer. @@ -4131,12 +4290,12 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Message delivery receipts! - Üzenet kézbesítési jelentések! + Üzenetkézbesítési jelentések! No comment provided by engineer. Message delivery warning - Üzenet kézbesítési figyelmeztetés + Üzenetkézbesítési figyelmeztetés item status text @@ -4156,7 +4315,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Message queue info - Üzenet-várakoztatási információ + Üzenet-sorbaállítási információ No comment provided by engineer. @@ -4176,7 +4335,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Message reception - Üzenet kézbesítési jelentés + Üzenetjelentés No comment provided by engineer. @@ -4184,6 +4343,11 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Üzenetkiszolgálók No comment provided by engineer. + + Message shape + Üzenetbuborék formája + No comment provided by engineer. + Message source remains private. Az üzenet forrása titokban marad. @@ -4201,7 +4365,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Message text - Üzenet szövege + Név és üzenet No comment provided by engineer. @@ -4234,6 +4398,11 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Elküldött üzenetek No comment provided by engineer. + + Messages were deleted after you selected them. + Az üzeneteket törölték miután kiválasztotta őket. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. Az üzeneteket, fájlokat és hívásokat **végpontok közötti titkosítással**, sérülés utáni titkosság-védelemmel és -helyreállítással, továbbá visszautasítással védi. @@ -4276,7 +4445,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Migrating database archive… - Adatbázis archívum migrálása… + Adatbázis-archívum átköltöztetése… No comment provided by engineer. @@ -4291,7 +4460,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Migration failed. Tap **Skip** below to continue using the current database. Please report the issue to the app developers via chat or email [chat@simplex.chat](mailto:chat@simplex.chat). - Sikertelen átköltöztetés. Koppintson a **Kihagyás** lehetőségre az aktuális adatbázis használatának folytatásához. Jelentse a problémát az alkalmazás fejlesztőinek csevegésben vagy e-mailben [chat@simplex.chat](mailto:chat@simplex.chat). + Sikertelen átköltöztetés. Koppintson a **Kihagyás** lehetőségre a jelenlegi adatbázis használatának folytatásához. Jelentse a problémát az alkalmazás fejlesztőinek csevegésben vagy e-mailben [chat@simplex.chat](mailto:chat@simplex.chat). No comment provided by engineer. @@ -4311,12 +4480,12 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Moderated at - Moderálva lett ekkor: + Moderálva ekkor: No comment provided by engineer. Moderated at: %@ - Moderálva lett ekkor: %@ + Moderálva ekkor: %@ copied message info @@ -4389,6 +4558,16 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Új jelkód No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + Minden alkalommal, amikor elindítja az alkalmazást, új SOCKS-hitelesítő-adatokat fog használni. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + Minden egyes kiszolgálóhoz új SOCKS-hitelesítő-adatok legyenek használva. + No comment provided by engineer. + New chat Új beszélgetés @@ -4401,7 +4580,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! New contact request - Új kapcsolattartási kérelem + Új kapcsolatkérés notification @@ -4416,7 +4595,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! New desktop app! - Új asztali alkalmazás! + Új számítógép-alkalmazás! No comment provided by engineer. @@ -4461,7 +4640,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! No contacts selected - Nem kerültek ismerősök kiválasztásra + Nincs kiválasztva ismerős No comment provided by engineer. @@ -4476,7 +4655,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! No device token! - Nincs eszköztoken! + Nincs kiszüléktoken! No comment provided by engineer. @@ -4509,6 +4688,16 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Nincs hálózati kapcsolat No comment provided by engineer. + + No permission to record speech + Nincs jogosultság megadva a beszéd rögzítéséhez + No comment provided by engineer. + + + No permission to record video + Nincs jogosultság megadva a videó rögzítéséhez + No comment provided by engineer. + No permission to record voice message Nincs engedély a hangüzenet rögzítésére @@ -4526,9 +4715,14 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Nothing selected - Semmi sincs kiválasztva + Nincs kiválasztva semmi No comment provided by engineer. + + Nothing to forward! + Nincs mit továbbítani! + alert title + Notifications Értesítések @@ -4561,7 +4755,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Ok Rendben - No comment provided by engineer. + alert button Old database @@ -4570,36 +4764,36 @@ Ez az ön hivatkozása a(z) %@ csoporthoz! Old database archive - Régi adatbázis archívum + Régi adatbázis-archívum No comment provided by engineer. One-time invitation link - Egyszer használatos meghívó hivatkozás + Egyszer használható meghívó-hivatkozás No comment provided by engineer. Onion hosts will be **required** for connection. Requires compatible VPN. - A kapcsolódáshoz Onion kiszolgálókra lesz szükség. -VPN engedélyezése szükséges. + Onion-kiszolgálók **szükségesek** a kapcsolódáshoz. +Kompatibilis VPN szükséges. No comment provided by engineer. Onion hosts will be used when available. Requires compatible VPN. - Onion kiszolgálók használata, ha azok rendelkezésre állnak. + Onion-kiszolgálók használata, ha azok rendelkezésre állnak. VPN engedélyezése szükséges. No comment provided by engineer. Onion hosts will not be used. - Onion kiszolgálók nem lesznek használva. + Onion-kiszolgálók nem lesznek használva. No comment provided by engineer. Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. - Csak a klienseszközök tárolják a felhasználói profilokat, névjegyeket, csoportokat és a **2 rétegű végponttól-végpontig titkosítással** küldött üzeneteket. + Csak az eszközök alkalmazásai tárolják a felhasználó-profilokat, névjegyeket, csoportokat és a **2 rétegű végpontok közötti titkosítással** küldött üzeneteket. No comment provided by engineer. @@ -4624,42 +4818,42 @@ VPN engedélyezése szükséges. Only you can add message reactions. - Csak ön adhat hozzá üzenetreakciókat. + Csak Ön adhat hozzá üzenetreakciókat. No comment provided by engineer. Only you can irreversibly delete messages (your contact can mark them for deletion). (24 hours) - Véglegesen csak ön törölhet üzeneteket (ismerőse csak törlésre jelölheti meg őket ). (24 óra) + Véglegesen csak Ön törölhet üzeneteket (ismerőse csak törlésre jelölheti meg őket ). (24 óra) No comment provided by engineer. Only you can make calls. - Csak ön tud hívásokat indítani. + Csak Ön tud hívásokat indítani. No comment provided by engineer. Only you can send disappearing messages. - Csak ön tud eltűnő üzeneteket küldeni. + Csak Ön tud eltűnő üzeneteket küldeni. No comment provided by engineer. Only you can send voice messages. - Csak ön tud hangüzeneteket küldeni. + Csak Ön tud hangüzeneteket küldeni. No comment provided by engineer. Only your contact can add message reactions. - Csak az ismerős tud üzeneteakciókat adni. + Csak az ismerőse tud üzenetreakciókat küldeni. No comment provided by engineer. Only your contact can irreversibly delete messages (you can mark them for deletion). (24 hours) - Csak az ismerőse tudja az üzeneteket véglegesen törölni (ön csak törlésre jelölheti meg azokat). (24 óra) + Csak az ismerőse tudja az üzeneteket véglegesen törölni (Ön csak törlésre jelölheti meg azokat). (24 óra) No comment provided by engineer. Only your contact can make calls. - Csak az ismerős tud hívást indítani. + Csak az ismerőse tud hívást indítani. No comment provided by engineer. @@ -4669,7 +4863,7 @@ VPN engedélyezése szükséges. Only your contact can send voice messages. - Csak az ismerős tud hangüzeneteket küldeni. + Csak az ismerőse tud hangüzeneteket küldeni. No comment provided by engineer. @@ -4704,12 +4898,12 @@ VPN engedélyezése szükséges. Open server settings - Kiszolgáló beállításainak megnyitása + Kiszolgáló-beállítások megnyitása No comment provided by engineer. Open user profiles - Felhasználói profilok megnyitása + Felhasználó-profilok megnyitása authentication reason @@ -4734,7 +4928,7 @@ VPN engedélyezése szükséges. Or securely share this file link - Vagy a fájl hivítkozásának biztonságos megosztása + Vagy ossza meg biztonságosan ezt a fájlhivatkozást No comment provided by engineer. @@ -4752,14 +4946,21 @@ VPN engedélyezése szükséges. További %@ kiszolgálók No comment provided by engineer. + + Other file errors: +%@ + Egyéb fájlhiba: +%@ + alert message + PING count - PING számláló + PING-ek száma No comment provided by engineer. PING interval - PING időköze + Időtartam a PING-ek között No comment provided by engineer. @@ -4787,6 +4988,11 @@ VPN engedélyezése szükséges. A jelkód beállítva! No comment provided by engineer. + + Password + Jelszó + No comment provided by engineer. + Password to show Jelszó megjelenítése @@ -4794,7 +5000,7 @@ VPN engedélyezése szükséges. Past member %@ - Már nem tag - %@ + (Már nem tag) %@ past/unknown group member @@ -4814,17 +5020,17 @@ VPN engedélyezése szükséges. Paste the link you received - Fogadott hivatkozás beillesztése + Kapott hivatkozás beillesztése No comment provided by engineer. Pending - Függő + Függőben No comment provided by engineer. People can connect to you only via the links you share. - Az emberek csak az ön által megosztott hivatkozáson keresztül kapcsolódhatnak. + Az emberek csak az Ön által megosztott hivatkozáson keresztül kapcsolódhatnak. No comment provided by engineer. @@ -4854,34 +5060,34 @@ VPN engedélyezése szükséges. Please ask your contact to enable sending voice messages. - Ismerős felkérése, hogy engedélyezze a hangüzenetek küldését. + Kérje meg az ismerősét, hogy engedélyezze a hangüzenetek küldését. 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. Please share any other issues with the developers. - Ellenőrizze, hogy a mobil és az asztali számítógép ugyanahhoz a helyi hálózathoz csatlakozik-e, valamint az asztali számítógép tűzfalában engedélyezve van-e a kapcsolat. + Ellenőrizze, hogy a hordozható eszköz és a számítógép ugyanahhoz a helyi hálózathoz csatlakozik-e, valamint a számítógép tűzfalában engedélyezve van-e a kapcsolat. Minden további problémát osszon meg a fejlesztőkkel. No comment provided by engineer. Please check that you used the correct link or ask your contact to send you another one. - Ellenőrizze, hogy a megfelelő hivatkozást használta-e, vagy kérje meg ismerősét, hogy küldjön egy másikat. + Ellenőrizze, hogy a megfelelő hivatkozást használta-e, vagy kérje meg az ismerősét, hogy küldjön egy másikat. No comment provided by engineer. Please check your network connection with %@ and try again. - Ellenőrizze hálózati kapcsolatát a(z) %@ segítségével, és próbálja újra. + Ellenőrizze a hálózati kapcsolatát a(z) %@ segítségével, és próbálja újra. No comment provided by engineer. Please check yours and your contact preferences. - Ellenőrizze az ön és ismerőse beállításait. + Ellenőrizze a saját- és az ismerőse beállításait. No comment provided by engineer. Please confirm that network settings are correct for this device. - Ellenőrizze, hogy a hálózati beállítások megfelelőek-e ehhez az eszközhöz. + Ellenőrizze, hogy a hálózati beállítások megfelelők-e ehhez az eszközhöz. No comment provided by engineer. @@ -4898,7 +5104,7 @@ Hiba: %@ Please enter correct current passphrase. - Adja meg a helyes aktuális jelmondatát. + Adja meg a helyes, jelenlegi jelmondatát. No comment provided by engineer. @@ -4918,7 +5124,7 @@ Hiba: %@ Please restart the app and migrate the database to enable push notifications. - Indítsa újra az alkalmazást az adatbázis-átköltöztetéséhez szükséges push értesítések engedélyezéséhez. + Indítsa újra az alkalmazást az adatbázis-átköltöztetéséhez szükséges push-értesítések engedélyezéséhez. No comment provided by engineer. @@ -4936,6 +5142,11 @@ Hiba: %@ Lengyel kezelőfelület No comment provided by engineer. + + Port + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Lehetséges, hogy a kiszolgáló címében szereplő tanúsítvány-ujjlenyomat helytelen @@ -4978,17 +5189,17 @@ Hiba: %@ Private filenames - Privát fájl nevek + Privát fájlnevek No comment provided by engineer. Private message routing - Privát üzenet útválasztás + Privát üzenet-útválasztás No comment provided by engineer. Private message routing 🚀 - Privát üzenet útválasztás 🚀 + Privát üzenet-útválasztás 🚀 No comment provided by engineer. @@ -5003,7 +5214,7 @@ Hiba: %@ Private routing error - Privát útválasztási hiba + Privát útválasztáshiba No comment provided by engineer. @@ -5021,16 +5232,6 @@ Hiba: %@ Profilképek No comment provided by engineer. - - Profile name - Profilnév - No comment provided by engineer. - - - Profile name: - Profil neve: - No comment provided by engineer. - Profile password Profiljelszó @@ -5068,7 +5269,7 @@ Hiba: %@ Prohibit sending SimpleX links. - A SimpleX hivatkozások küldése le van tiltva. + A SimpleX-hivatkozások küldése le van tiltva. No comment provided by engineer. @@ -5083,7 +5284,7 @@ Hiba: %@ Prohibit sending files and media. - Fájlok- és a médiatartalom küldés letiltása. + Fájlok- és a médiatartalmak küldésének letiltása. No comment provided by engineer. @@ -5104,23 +5305,23 @@ Hiba: %@ Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings. - Védje IP-címét az ismerősei által kiválasztott üzenetküldő átjátszókkal szemben. -Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. + Védje IP-címét az ismerősei által kiválasztott üzenet-közvetítő-kiszolgálókkal szemben. +Engedélyezze a „Beállítások -> Hálózat és kiszolgálók” menüben. No comment provided by engineer. Protect your chat profiles with a password! - Csevegési profiljok védelme jelszóval! + Védje meg a csevegési profiljait egy jelszóval! No comment provided by engineer. Protocol timeout - Protokoll időtúllépés + Protokoll időtúllépése No comment provided by engineer. Protocol timeout per KB - Protokoll időkorlát KB-onként + Protokoll időtúllépése KB-onként No comment provided by engineer. @@ -5133,14 +5334,19 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Proxyzott kiszolgálók No comment provided by engineer. + + Proxy requires password + A proxy jelszót igényel + No comment provided by engineer. + Push notifications - Push értesítések + Push-értesítések No comment provided by engineer. Push server - Push kiszolgáló + Push-kiszolgáló No comment provided by engineer. @@ -5200,7 +5406,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Receipts are disabled - Üzenet kézbesítési jelentés letiltva + A kézbesítési jelentések le vannak tiltva No comment provided by engineer. @@ -5220,12 +5426,12 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Received file event - Fogadott fájl esemény + Fogadott fájlesemény notification Received message - Fogadott üzenet + Fogadott üzenetbuborék színe message info title @@ -5235,12 +5441,12 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Received reply - Fogadott válasz + Fogadott válaszüzenet-buborék színe No comment provided by engineer. Received total - Összes fogadott + Összes fogadott üzenet No comment provided by engineer. @@ -5270,7 +5476,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Recipients see updates as you type them. - A címzettek a beírás közben látják a frissítéseket. + A címzettek a beírás közben látják a szövegváltozásokat. No comment provided by engineer. @@ -5280,7 +5486,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Reconnect all connected servers to force message delivery. It uses additional traffic. - Újrakapcsolódás az összes kiszolgálóhoz az üzenetek kézbesítésének kikényszerítéséhez. Ez további forgalmat használ. + Az összes kiszolgálóhoz való újrakapcsolódás az üzenetkézbesítési jelentések kikényszerítéséhez. Ez további adatforgalmat használ. No comment provided by engineer. @@ -5295,7 +5501,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Reconnect server to force message delivery. It uses additional traffic. - A kiszolgálóhoz való újrakapcsolódás az üzenet kézbesítésének kikényszerítéséhez. Ez további adatforgalmat használ. + A kiszolgálóhoz való újrakapcsolódás az üzenetkézbesítési jelentések kikényszerítéséhez. Ez további adatforgalmat használ. No comment provided by engineer. @@ -5320,7 +5526,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Reduced battery usage - Csökkentett akkumulátorhasználat + Csökkentett akkumulátor-használat No comment provided by engineer. @@ -5336,17 +5542,17 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Reject contact request - Kapcsolatfelvételi kérelem elutasítása + Kapcsolatkérés elutasítása No comment provided by engineer. Relay server is only used if necessary. Another party can observe your IP address. - Az átjátszó kiszolgáló csak szükség esetén kerül használatra. Egy másik fél megfigyelheti az IP-címet. + A közvetítő-kiszolgáló csak szükség esetén kerül használatra. Egy másik fél megfigyelheti az IP-címet. No comment provided by engineer. Relay server protects your IP address, but it can observe the duration of the call. - Az átjátszó kiszolgáló megvédi az IP-címet, de megfigyelheti a hívás időtartamát. + A közvetítő-kiszolgáló megvédi az IP-címet, de megfigyelheti a hívás időtartamát. No comment provided by engineer. @@ -5354,6 +5560,11 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Eltávolítás No comment provided by engineer. + + Remove archive? + Archívum eltávolítása? + No comment provided by engineer. + Remove image Kép eltávolítása @@ -5391,7 +5602,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Repeat connection request? - Kapcsolódási kérés megismétlése? + Kapcsolatkérés megismétlése? No comment provided by engineer. @@ -5406,7 +5617,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Repeat join request? - Csatlakozási kérés megismétlése? + Csatlakozáskérés megismétlése? No comment provided by engineer. @@ -5426,7 +5637,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Reset - Alaphelyzetbe állítás + Visszaállítás No comment provided by engineer. @@ -5446,7 +5657,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Reset colors - Színek alaphelyzetbe állítása + Színek visszaállítása No comment provided by engineer. @@ -5456,7 +5667,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Reset to defaults - Alaphelyzetbe állítás + Visszaállítás alapértelmezettre No comment provided by engineer. @@ -5534,6 +5745,11 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. SMP-kiszolgáló No comment provided by engineer. + + SOCKS proxy + SOCKS proxy + No comment provided by engineer. + Safely receive files Fájlok biztonságos fogadása @@ -5547,16 +5763,17 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Save Mentés - chat item action + alert button + chat item action Save (and notify contacts) - Mentés (és az ismerősök értesítése) - No comment provided by engineer. + Mentés és az ismerősök értesítése + alert button Save and notify contact - Mentés és ismerős értesítése + Mentés és az ismerős értesítése No comment provided by engineer. @@ -5571,7 +5788,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Save and update group profile - Mentés és csoportprofil frissítése + Mentés és a csoportprofil frissítése No comment provided by engineer. @@ -5579,19 +5796,14 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Archívum mentése No comment provided by engineer. - - Save auto-accept settings - Automatikus elfogadási beállítások mentése - No comment provided by engineer. - Save group profile - Csoportprofil elmentése + Csoportprofil mentése No comment provided by engineer. Save passphrase and open chat - Jelmondat elmentése és csevegés megnyitása + Jelmondat mentése és a csevegés megnyitása No comment provided by engineer. @@ -5606,7 +5818,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Save profile password - Felhasználói fiók jelszavának mentése + Profiljelszó mentése No comment provided by engineer. @@ -5619,16 +5831,16 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Kiszolgálók mentése? No comment provided by engineer. - - Save settings? - Beállítások mentése? - No comment provided by engineer. - Save welcome message? - Üdvözlőszöveg mentése? + Üdvözlőüzenet mentése? No comment provided by engineer. + + Save your profile? + Profil mentése? + alert title + Saved Mentett @@ -5636,7 +5848,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Saved WebRTC ICE servers will be removed - A mentett WebRTC ICE kiszolgálók eltávolításra kerülnek + A mentett WebRTC ICE-kiszolgálók eltávolításra kerülnek No comment provided by engineer. @@ -5649,6 +5861,11 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Mentett üzenet message info title + + Saving %lld messages + %lld üzenet mentése + No comment provided by engineer. + Scale Méretezés @@ -5676,7 +5893,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Scan security code from your contact's app. - Biztonsági kód beolvasása ismerős általi alkalmazásból. + Biztonsági kód beolvasása az ismerősének alkalmazásából. No comment provided by engineer. @@ -5691,12 +5908,12 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Search bar accepts invitation links. - A keresősáv elfogadja a meghívó hivatkozásokat. + A keresősáv elfogadja a meghívó-hivatkozásokat. No comment provided by engineer. Search or paste SimpleX link - Keresés, vagy SimpleX hivatkozás beillesztése + Keresés, vagy SimpleX-hivatkozás beillesztése No comment provided by engineer. @@ -5706,7 +5923,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Secure queue - Biztonságos várólista + Biztonságos sorbaállítás server test step @@ -5726,9 +5943,14 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Select - Választás + Kiválasztás chat item action + + Select chat profile + Csevegési profil kiválasztása + No comment provided by engineer. + Selected %lld %lld kiválasztva @@ -5806,12 +6028,12 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Send messages directly when IP address is protected and your or destination server does not support private routing. - Közvetlen üzenetküldés, ha az IP-cím védett és az ön kiszolgálója vagy a célkiszolgáló nem támogatja a privát útválasztást. + Közvetlen üzenetküldés, ha az IP-cím védett és az Ön kiszolgálója vagy a célkiszolgáló nem támogatja a privát útválasztást. No comment provided by engineer. Send messages directly when your or destination server does not support private routing. - Közvetlen üzenetküldés, ha az ön kiszolgálója vagy a célkiszolgáló nem támogatja a privát útválasztást. + Közvetlen üzenetküldés, ha az Ön kiszolgálója vagy a célkiszolgáló nem támogatja a privát útválasztást. No comment provided by engineer. @@ -5831,7 +6053,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Send receipts - Üzenet kézbesítési jelentések + Kézbesítési jelentések küldése No comment provided by engineer. @@ -5847,11 +6069,11 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Sender cancelled file transfer. A fájl küldője visszavonta az átvitelt. - No comment provided by engineer. + alert message Sender may have deleted the connection request. - A küldő törölhette a kapcsolódási kérelmet. + A küldő törölhette a kapcsolatkérést. No comment provided by engineer. @@ -5861,7 +6083,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Sending delivery receipts will be enabled for all contacts. - A kézbesítési jelentés küldése minden ismerős számára engedélyezésre kerül. + A kézbesítési jelentés küldése minden ismerőse számára engedélyezésre kerül. No comment provided by engineer. @@ -5871,22 +6093,22 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Sending receipts is disabled for %lld contacts - A kézbesítési jelentések küldése le van tiltva %lld ismerősnél + A kézbesítési jelentések le vannak tiltva %lld ismerősnél No comment provided by engineer. Sending receipts is disabled for %lld groups - A kézbesítési jelentések küldése le van tiltva %lld csoportban + A kézbesítési jelentések le vannak tiltva %lld csoportban No comment provided by engineer. Sending receipts is enabled for %lld contacts - A kézbesítési jelentések küldése engedélyezve van %lld ismerős számára + A kézbesítési jelentések engedélyezve vannak %lld ismerősnél No comment provided by engineer. Sending receipts is enabled for %lld groups - A kézbesítési jelentések küldése engedélyezve van %lld csoportban + A kézbesítési jelentések engedélyezve vannak %lld csoportban No comment provided by engineer. @@ -5911,12 +6133,12 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Sent file event - Elküldött fájl esemény + Elküldött fájlesemény notification Sent message - Elküldött üzenet + Üzenetbuborék színe message info title @@ -5931,12 +6153,12 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Sent reply - Elküldött válasz + Válaszüzenet-buborék színe No comment provided by engineer. Sent total - Összes elküldött + Összes elküldött üzenet No comment provided by engineer. @@ -5944,6 +6166,11 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Proxyn keresztül küldve No comment provided by engineer. + + Server + Kiszolgáló + No comment provided by engineer. + Server address Kiszolgáló címe @@ -5961,7 +6188,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Server requires authorization to create queues, check password - A kiszolgálónak engedélyre van szüksége a várólisták létrehozásához, ellenőrizze jelszavát + A kiszolgálónak engedélyre van szüksége a sorbaállítás létrehozásához, ellenőrizze jelszavát server test error @@ -5996,12 +6223,12 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Servers info - információk a kiszolgálókról + Információk a kiszolgálókról No comment provided by engineer. Servers statistics will be reset - this cannot be undone! - A kiszolgálók statisztikái visszaállnak - ez nem vonható vissza! + A kiszolgálók statisztikái visszaállnak - ez a művelet nem vonható vissza! No comment provided by engineer. @@ -6064,6 +6291,11 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Beállítások No comment provided by engineer. + + Settings were changed. + A beállítások megváltoztak. + alert message + Shape profile images Profilkép alakzat @@ -6076,7 +6308,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Share 1-time link - Egyszer használatos hivatkozás megosztása + Egyszer használható hivatkozás megosztása No comment provided by engineer. @@ -6099,9 +6331,14 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Hivatkozás megosztása No comment provided by engineer. + + Share profile + Profil megosztása + No comment provided by engineer. + Share this 1-time invite link - Egyszer használatos meghívó hivatkozás megosztása + Egyszer használható meghívó-hivatkozás megosztása No comment provided by engineer. @@ -6131,12 +6368,12 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Show last messages - Utolsó üzenetek megjelenítése + Szobák utolsó üzeneteinek megjelenítése a listanézetben No comment provided by engineer. Show message status - Üzenet állapot megjelenítése + Üzenetállapot megjelenítése No comment provided by engineer. @@ -6146,7 +6383,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Show preview - Előnézet megjelenítése + Értesítés előnézete No comment provided by engineer. @@ -6166,7 +6403,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. SimpleX Address - SimpleX cím + SimpleX-cím No comment provided by engineer. @@ -6176,7 +6413,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. SimpleX Lock - SimpleX zár + SimpleX-zár No comment provided by engineer. @@ -6186,17 +6423,17 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. SimpleX Lock not enabled! - A SimpleX zár nincs bekapcsolva! + A SimpleX-zár nincs bekapcsolva! No comment provided by engineer. SimpleX Lock turned on - SimpleX zár bekapcsolva + SimpleX-zár bekapcsolva No comment provided by engineer. SimpleX address - SimpleX cím + SimpleX-cím No comment provided by engineer. @@ -6211,32 +6448,37 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. SimpleX group link - SimpleX csoport hivatkozás + SimpleX csoporthivatkozás simplex link type SimpleX links - SimpleX hivatkozások + SimpleX-hivatkozások chat feature SimpleX links are prohibited in this group. - A SimpleX hivatkozások küldése le van tiltva ebben a csoportban. + A SimpleX-hivatkozások küldése le van tiltva ebben a csoportban. No comment provided by engineer. SimpleX links not allowed - A SimpleX hivatkozások küldése le van tiltva + A SimpleX-hivatkozások küldése le van tiltva No comment provided by engineer. SimpleX one-time invitation - SimpleX egyszer használatos meghívó + Egyszer használható SimpleX-meghívó simplex link type + + SimpleX protocols reviewed by Trail of Bits. + A SimpleX Chat biztonsága a Trail of Bits által lett újraauditálva. + No comment provided by engineer. + Simplified incognito mode - Egyszerűsített inkognító mód + Egyszerűsített inkognitómód No comment provided by engineer. @@ -6264,6 +6506,11 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Enyhe blur media + + Some app settings were not migrated. + Egyes alkalmazásbeállítások nem lettek átköltöztetve. + No comment provided by engineer. + Some file(s) were not exported: Néhány fájl nem került exportálásra: @@ -6271,12 +6518,12 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. 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 közben – további részletekért a csevegési konzolban olvashat. + Néhány nem végzetes hiba történt az importáláskor – további részleteket a csevegési konzolban olvashat. No comment provided by engineer. Some non-fatal errors occurred during import: - Néhány nem végzetes hiba történt az importálás közben: + Néhány nem végzetes hiba történt az importáláskor: No comment provided by engineer. @@ -6336,7 +6583,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped. - A csevegés megállítása a csevegő adatbázis exportálásához, importálásához vagy törléséhez. A csevegés megállítása alatt nem tud üzeneteket fogadni és küldeni. + A csevegés megállítása a csevegési adatbázis exportálásához, importálásához vagy törléséhez. A csevegés megállítása alatt nem tud üzeneteket fogadni és küldeni. No comment provided by engineer. @@ -6351,12 +6598,12 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Stop receiving file? - Fájl fogadás megállítása? + Fájlfogadás megállítása? No comment provided by engineer. Stop sending file? - Fájl küldés megállítása? + Fájlküldés megállítása? No comment provided by engineer. @@ -6396,12 +6643,22 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Subscriptions ignored - Elutasított feliratkozások + Mellőzött feliratkozások No comment provided by engineer. Support SimpleX Chat - Támogassa a SimpleX Chatet + SimpleX Chat támogatása + No comment provided by engineer. + + + Switch audio and video during the call. + Hang/Videó váltása hívás közben. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + Csevegési profilváltás az egyszer használható meghívókhoz. No comment provided by engineer. @@ -6421,7 +6678,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. TCP connection timeout - TCP kapcsolat időtúllépés + TCP kapcsolat időtúllépése No comment provided by engineer. @@ -6439,6 +6696,11 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. TCP_KEEPINTVL No comment provided by engineer. + + Tail + Farok + No comment provided by engineer. + Take picture Kép készítése @@ -6451,7 +6713,7 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Tap to Connect - Koppintson a kapcsolódáshoz + Koppintson ide a kapcsolódáshoz No comment provided by engineer. @@ -6461,27 +6723,27 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Tap to join - Koppintson a csatlakozáshoz + Koppintson ide a csatlakozáshoz No comment provided by engineer. Tap to join incognito - Koppintson az inkognitóban való csatlakozáshoz + Koppintson ide az inkognitóban való csatlakozáshoz No comment provided by engineer. Tap to paste link - Koppintson a hivatkozás beillesztéséhez + Koppintson ide a hivatkozás beillesztéséhez No comment provided by engineer. Tap to scan - Koppintson a beolvasáshoz + Koppintson ide a QR-kód beolvasáshoz No comment provided by engineer. Temporary file error - Ideiglenes fájlhiba + Ideiglenesfájl-hiba No comment provided by engineer. @@ -6516,12 +6778,12 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben. Thanks to the users – contribute via Weblate! - Köszönet a felhasználóknak - hozzájárulás a Weblaten! + Köszönet a felhasználóknak - hozzájárulás a Weblate-en! No comment provided by engineer. The 1st platform without any user identifiers – private by design. - Az első csevegési rendszer bármiféle felhasználó azonosító nélkül - privátra lett tervezre. + Az első csevegési rendszer bármiféle felhasználó-azonosító nélkül - privátra lett tervezre. No comment provided by engineer. @@ -6533,7 +6795,7 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. The app can notify you when you receive messages or contact requests - please open settings to enable. - Az alkalmazás értesíteni fogja, amikor üzeneteket vagy kapcsolatfelvételi kéréseket kap – beállítások megnyitása az engedélyezéshez. + Az alkalmazás értesíteni fogja, amikor üzeneteket vagy kapcsolatkéréseket kap – beállítások megnyitása az engedélyezéshez. No comment provided by engineer. @@ -6548,12 +6810,12 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. The code you scanned is not a SimpleX link QR code. - A beolvasott kód nem egy SimpleX hivatkozás QR-kód. + A beolvasott QR-kód nem egy SimpleX QR-kód hivatkozás. No comment provided by engineer. The connection you accepted will be cancelled! - Az ön által elfogadott kapcsolat vissza lesz vonva! + Az Ön által elfogadott kérelem vissza lesz vonva! No comment provided by engineer. @@ -6563,7 +6825,7 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. The created archive is available via app Settings / Database / Old database archive. - A létrehozott archívum a Beállítások / Adatbázis / Régi adatbázis-archívum menüben érhető el. + A létrehozott archívum a „Beállítások / Adatbázis / Régi adatbázis-archívum” menüben érhető el. No comment provided by engineer. @@ -6573,7 +6835,7 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. The hash of the previous message is different. - Az előző üzenet ellenőrzőösszege különbözik. + Az előző üzenet hasító értéke különbözik. No comment provided by engineer. @@ -6603,12 +6865,12 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. The old database was not removed during the migration, it can be deleted. - A régi adatbázis nem került eltávolításra az átköltöztetés közben, így törölhető. + A régi adatbázis nem került eltávolításra az átköltöztetéskor, így törölhető. No comment provided by engineer. The profile is only shared with your contacts. - Profilja csak az ismerőseivel kerül megosztásra. + A profilja csak az ismerőseivel kerül megosztásra. No comment provided by engineer. @@ -6623,12 +6885,17 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. The servers for new connections of your current chat profile **%@**. - Jelenlegi profil új ismerőseinek kiszolgálói **%@**. + A jelenlegi csevegési profilhoz tartozó új kapcsolatok kiszolgálói **%@**. No comment provided by engineer. The text you pasted is not a SimpleX link. - A beillesztett szöveg nem egy SimpleX hivatkozás. + A beillesztett szöveg nem egy SimpleX-hivatkozás. + No comment provided by engineer. + + + The uploaded database archive will be permanently removed from the servers. + A feltöltött adatbázis-archívum véglegesen eltávolításra kerül a kiszolgálókról. No comment provided by engineer. @@ -6643,12 +6910,12 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. They can be overridden in contact and group settings. - Ezek felülbírálhatóak az ismerős- és csoportbeállításokban. + Ezek felülbírálhatók 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ú képek viszont megmaradnak. + Ez a művelet nem vonható vissza - az összes fogadott és küldött fájl a médiatartalmakkal együtt törlésre kerül. Az alacsony felbontású képek viszont megmaradnak. No comment provided by engineer. @@ -6678,7 +6945,7 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. This display name is invalid. Please choose another name. - Ez a megjelenített felhasználónév érvénytelen. Válasszon egy másik nevet. + Ez a megjelenített név érvénytelen. Válasszon egy másik nevet. No comment provided by engineer. @@ -6693,17 +6960,17 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. This is your own SimpleX address! - Ez az ön SimpleX címe! + Ez az Ön SimpleX-címe! No comment provided by engineer. This is your own one-time link! - Ez az egyszer használatos hivatkozása! + Ez az Ön egyszer használható hivatkozása! No comment provided by engineer. This link was used with another mobile device, please create a new link on the desktop. - Ezt a hivatkozást egy másik mobilleszközön már használták, hozzon létre egy új hivatkozást az asztali számítógépén. + Ezt a hivatkozást egy másik hordozható eszközön már használták, hozzon létre egy új hivatkozást a számítógépén. No comment provided by engineer. @@ -6738,7 +7005,7 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. - Az adatvédelem érdekében, a más csevegési platformokon megszokott felhasználói azonosítók helyett, a SimpleX üzenetsorokhoz rendel azonosítókat, minden egyes ismerőshöz egy különbözőt. + Az adatvédelem érdekében, a más csevegési platformokon megszokott felhasználó-azonosítók helyett, a SimpleX csak az üzenetek sorbaállításához használ azonosítókat, minden egyes ismerőshöz egy-egy különbözőt. No comment provided by engineer. @@ -6748,16 +7015,26 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő. 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. + 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. No comment provided by engineer. To protect your information, turn on SimpleX Lock. You will be prompted to complete authentication before this feature is enabled. - A biztonsága érdekében kapcsolja be a SimpleX zár funkciót. + 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. + + To record speech please grant permission to use Microphone. + A beszéd rögzítéséhez adjon engedélyt a Mikrofon használatára. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + A videó rögzítéséhez adjon engedélyt a Kamera használatára. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Hangüzenet rögzítéséhez adjon engedélyt a mikrofon használathoz. @@ -6770,12 +7047,12 @@ A funkció bekapcsolása előtt a rendszer felszólítja a képernyőzár beáll 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. + Az azonnali push-értesítések támogatásához a csevegési adatbázis átköltöztetése szükséges. No comment provided by engineer. To verify end-to-end encryption with your contact compare (or scan) the code on your devices. - A végpontok közötti titkosítás ellenőrzéséhez hasonlítsa össze (vagy olvassa be a QR-kódot) az ismerőse eszközén lévő kóddal. + A végpontok közötti titkosítás hitelesítéséhez hasonlítsa össze (vagy olvassa be a QR-kódot) az ismerőse eszközén lévő kóddal. No comment provided by engineer. @@ -6785,7 +7062,7 @@ A funkció bekapcsolása előtt a rendszer felszólítja a képernyőzár beáll Toggle incognito when connecting. - Inkognitó mód kapcsolódáskor. + Inkognitómód használata kapcsolódáskor. No comment provided by engineer. @@ -6795,12 +7072,12 @@ A funkció bekapcsolása előtt a rendszer felszólítja a képernyőzár beáll Total - Összesen + Összes kapcsolat No comment provided by engineer. Transport isolation - Kapcsolat izolációs mód + Átvitel-izoláció módja No comment provided by engineer. @@ -6810,12 +7087,12 @@ A funkció bekapcsolása előtt a rendszer felszólítja a képernyőzár beáll Trying to connect to the server used to receive messages from this contact (error: %@). - Kapcsolódási kísérlet ahhoz a kiszolgálóhoz, amely az adott ismerőstől érkező üzenetek fogadására szolgál (hiba: %@). + Kapcsolódási kísérlet ahhoz a kiszolgálóhoz, amely az adott ismerősétől érkező üzenetek fogadására szolgál (hiba: %@). No comment provided by engineer. Trying to connect to the server used to receive messages from this contact. - Kapcsolódási kísérlet ahhoz a kiszolgálóhoz, amely az adott ismerőstől érkező üzenetek fogadására szolgál. + Kapcsolódási kísérlet ahhoz a kiszolgálóhoz, amely az adott ismerősétől érkező üzenetek fogadására szolgál. No comment provided by engineer. @@ -6870,7 +7147,7 @@ A funkció bekapcsolása előtt a rendszer felszólítja a képernyőzár beáll Unfav. - Nem kedvelt. + Kedvenc megszüntetése swipe action @@ -6911,7 +7188,7 @@ A funkció bekapcsolása előtt a rendszer felszólítja a képernyőzár beáll Unknown servers! Ismeretlen kiszolgálók! - No comment provided by engineer. + alert title Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. @@ -6921,8 +7198,8 @@ A funkció bekapcsolása előtt a rendszer felszólítja a képernyőzár beáll Unless your contact deleted the connection or this link was already used, it might be a bug - please report it. To connect, please ask your contact to create another connection link and check that you have a stable network connection. - Hacsak az ismerőse nem törölte a kapcsolatot, vagy ez a hivatkozás már használatban volt, lehet hogy ez egy hiba – jelentse a problémát. -A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsolati hivatkozást, és ellenőrizze, hogy a hálózati kapcsolat stabil-e. + Hacsak az ismerőse nem törölte a kapcsolatot, vagy ez a hivatkozás már használatban volt egyszer, lehet hogy ez egy hiba – jelentse a problémát. +A kapcsolódáshoz kérje meg az ismerősét, hogy hozzon létre egy másik kapcsolattartási hivatkozást, és ellenőrizze, hogy a hálózati kapcsolat stabil-e. No comment provided by engineer. @@ -6947,7 +7224,7 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol Unmute - Némítás feloldása + Némítás megszüntetése swipe action @@ -6967,7 +7244,7 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol Update database passphrase - Adatbázis jelmondat megváltoztatása + Adatbázis-jelmondat megváltoztatása No comment provided by engineer. @@ -7022,12 +7299,17 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol Use .onion hosts - Tor .onion kiszolgálók használata + Onion-kiszolgálók használata + No comment provided by engineer. + + + Use SOCKS proxy + SOCKS proxy használata No comment provided by engineer. Use SimpleX Chat servers? - SimpleX Chat kiszolgálók használata? + SimpleX Chat-kiszolgálók használata? No comment provided by engineer. @@ -7047,7 +7329,7 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol Use from desktop - Használat számítógépről + Társítás számítógéppel No comment provided by engineer. @@ -7057,7 +7339,7 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol Use new incognito profile - Az új inkognító profil használata + Új inkognitóprofil használata No comment provided by engineer. @@ -7067,7 +7349,7 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol Use private routing with unknown servers when IP address is not protected. - Privát útválasztás használata ismeretlen kiszolgálókkal, ha az IP-cím nem védett. + Használjon privát útválasztást ismeretlen kiszolgálókkal, ha az IP-cím nem védett. No comment provided by engineer. @@ -7090,54 +7372,54 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol Használja az alkalmazást egy kézzel. No comment provided by engineer. - - User profile - Felhasználói profil - No comment provided by engineer. - User selection Felhasználó kiválasztása No comment provided by engineer. + + Username + Felhasználónév + No comment provided by engineer. + Using SimpleX Chat servers. - SimpleX Chat kiszolgálók használatban. + SimpleX Chat-kiszolgálók használatban. No comment provided by engineer. Verify code with desktop - Kód ellenőrzése a számítógépen + Kód hitelesítése a számítógépen No comment provided by engineer. Verify connection - Kapcsolat ellenőrzése + Kapcsolat hitelesítése No comment provided by engineer. Verify connection security - Kapcsolat biztonságának ellenőrzése + Biztonságos kapcsolat hitelesítése No comment provided by engineer. Verify connections - Kapcsolatok ellenőrzése + Kapcsolatok hitelesítése No comment provided by engineer. Verify database passphrase - Adatbázis jelmondatának ellenőrzése + Az adatbázis jelmondatának hitelesítése No comment provided by engineer. Verify passphrase - Jelmondat ellenőrzése + Jelmondat hitelesítése No comment provided by engineer. Verify security code - Biztonsági kód ellenőrzése + Biztonsági kód hitelesítése No comment provided by engineer. @@ -7147,7 +7429,7 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol Via secure quantum resistant protocol. - Biztonságos kvantum ellenálló protokoll által. + Biztonságos kvantumrezisztens-protokollon keresztül. No comment provided by engineer. @@ -7212,22 +7494,22 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol Waiting for desktop... - Várakozás az asztali kliensre... + Várakozás a számítógép-alkalmazásra… No comment provided by engineer. Waiting for file - Fájlra várakozás + Várakozás a fájlra No comment provided by engineer. Waiting for image - Képre várakozás + Várakozás a képre No comment provided by engineer. Waiting for video - Videóra várakozás + Várakozás a videóra No comment provided by engineer. @@ -7252,7 +7534,7 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol WebRTC ICE servers - WebRTC ICE kiszolgálók + WebRTC ICE-kiszolgálók No comment provided by engineer. @@ -7262,12 +7544,12 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol Welcome message - Üdvözlő üzenet + Üdvözlőüzenet No comment provided by engineer. Welcome message is too long - Az üdvözlő üzenet túl hosszú + Az üdvözlőüzenet túl hosszú No comment provided by engineer. @@ -7287,7 +7569,7 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol When people request to connect, you can accept or reject it. - Amikor az emberek kapcsolódást kérelmeznek, ön elfogadhatja vagy elutasíthatja azokat. + Amikor az emberek kapcsolatot kérnek, Ön elfogadhatja vagy elutasíthatja azokat. No comment provided by engineer. @@ -7312,17 +7594,17 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol With encrypted files and media. - Titkosított fájlokkal és médiatartalommal. + Titkosított fájlokkal és médiatartalmakkal. No comment provided by engineer. With optional welcome message. - Opcionális üdvözlő üzenettel. + Nem kötelező üdvözlőüzenettel. No comment provided by engineer. With reduced battery usage. - Csökkentett akkumulátorhasználattal. + Csökkentett akkumulátor-használattal. No comment provided by engineer. @@ -7332,12 +7614,12 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. - Tor vagy VPN nélkül az IP-címe látható lesz ezen XFTP átjátszók számára: %@. - No comment provided by engineer. + Tor vagy VPN nélkül az IP-címe látható lesz a következő XFTP-közvetítő-kiszolgálók számára: %@. + alert message Wrong database passphrase - Hibás adatbázis jelmondat + Hibás adatbázis-jelmondat No comment provided by engineer. @@ -7360,11 +7642,6 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol XFTP-kiszolgáló No comment provided by engineer. - - You - Ön - No comment provided by engineer. - You **must not** use the same database on two devices. **Nem szabad** ugyanazt az adatbázist használni egyszerre két eszközön. @@ -7387,7 +7664,7 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol You are already connected to %@. - Már kapcsolódva van hozzá: %@. + Ön már kapcsolódva van ehhez: %@. No comment provided by engineer. @@ -7397,7 +7674,7 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol You are already connecting via this one-time link! - A kapcsolódás már folyamatban van ezen az egyszer használatos hivatkozáson keresztül! + A kapcsolódás már folyamatban van ezen az egyszer használható hivatkozáson keresztül! No comment provided by engineer. @@ -7407,7 +7684,7 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol You are already joining the group %@. - A csatlakozás már folyamatban van a(z) %@ csoporthoz. + A csatlakozás már folyamatban van a(z) %@ nevű csoporthoz. No comment provided by engineer. @@ -7424,12 +7701,12 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol You are already joining the group! Repeat join request? Csatlakozás folyamatban! -Csatlakozási kérés megismétlése? +Csatlakozáskérés megismétlése? No comment provided by engineer. You are connected to the server used to receive messages from this contact. - Már kapcsolódott ahhoz a kiszolgálóhoz, amely az adott ismerőstől érkező üzenetek fogadására szolgál. + Már kapcsolódott ahhoz a kiszolgálóhoz, amely az adott ismerősétől érkező üzenetek fogadására szolgál. No comment provided by engineer. @@ -7444,7 +7721,7 @@ Csatlakozási kérés megismétlése? You can accept calls from lock screen, without device and app authentication. - Hívásokat fogadhat a lezárási képernyőről, eszköz- és alkalmazáshitelesítés nélkül. + Hívásokat fogadhat a lezárási képernyőről, eszköz- és alkalmazás-hitelesítés nélkül. No comment provided by engineer. @@ -7459,12 +7736,12 @@ Csatlakozási kérés megismétlése? You can enable later via Settings - Később engedélyezheti a Beállításokban + Később engedélyezheti a „Beállításokban” No comment provided by engineer. You can enable them later via app Privacy & Security settings. - Később engedélyezheti őket az alkalmazás „Adatvédelem és biztonság” menüben. + Később engedélyezheti őket az alkalmazás „Adatvédelem és biztonság” menüjében. No comment provided by engineer. @@ -7474,12 +7751,12 @@ Csatlakozási kérés megismétlése? 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. + 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. + Láthatóvá teheti a SimpleXbeli ismerősei számára a „Beállításokban”. No comment provided by engineer. @@ -7504,17 +7781,17 @@ Csatlakozási kérés megismétlése? You can share this address with your contacts to let them connect with **%@**. - Megoszthatja ezt a címet az ismerőseivel, hogy kapcsolatba léphessenek önnel a(z) **%@** nevű profilján keresztül. + Megoszthatja ezt a címet az ismerőseivel, hogy kapcsolatba léphessenek Önnel a(z) **%@** nevű profilján keresztül. No comment provided by engineer. You can share your address as a link or QR code - anybody can connect to you. - Megoszthatja a címét egy hivatkozásként vagy QR-kódként – így bárki kapcsolódhat önhöz. + Megoszthatja a címét egy hivatkozásként vagy QR-kódként – így bárki kapcsolódhat Önhöz. No comment provided by engineer. 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üben vagy az alkalmazás újraindításával indíthatja el + A csevegést az alkalmazás „Beállítások / Adatbázis” menüben vagy az alkalmazás újraindításával indíthatja el No comment provided by engineer. @@ -7524,7 +7801,7 @@ Csatlakozási kérés megismétlése? You can turn on SimpleX Lock via Settings. - A SimpleX zár az „Adatvédelem és biztonság” menüben kapcsolható be. + A SimpleX-zár az „Adatvédelem és biztonság” menüben kapcsolható be. No comment provided by engineer. @@ -7534,7 +7811,7 @@ Csatlakozási kérés megismétlése? You can view invitation link again in connection details. - A meghívó hivatkozást újra megtekintheti a kapcsolat részleteinél. + A meghívó-hivatkozást újra megtekintheti a kapcsolat részleteinél. No comment provided by engineer. @@ -7549,19 +7826,19 @@ Csatlakozási kérés megismétlése? You could not be verified; please try again. - Nem lehetett ellenőrizni; próbálja meg újra. + Nem sikerült hitelesíteni; próbálja meg újra. No comment provided by engineer. You have already requested connection via this address! - Már kért egy kapcsolódási kérelmet ezen a címen keresztül! + Már küldött egy kapcsolatkérést ezen a címen keresztül! No comment provided by engineer. You have already requested connection! Repeat connection request? - Már kért egy kapcsolódási kérelmet! -Kapcsolódási kérés megismétlése? + Már küldött egy kapcsolódási kérelmet! +Kapcsolatkérés megismétlése? No comment provided by engineer. @@ -7596,7 +7873,7 @@ Kapcsolódási kérés megismétlése? You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. - A csevegési adatbázis legfrissebb verzióját CSAK egy eszközön kell használnia, ellenkező esetben előfordulhat, hogy az üzeneteket nem fogja megkapni valamennyi ismerőstől. + A csevegési adatbázis legfrissebb verzióját CSAK egy eszközön kell használnia, ellenkező esetben előfordulhat, hogy az üzeneteket nem fogja megkapni valamennyi ismerősétől. No comment provided by engineer. @@ -7606,17 +7883,17 @@ Kapcsolódási kérés megismétlése? You need to allow your contact to send voice messages to be able to send them. - Hangüzeneteket küldéséhez engedélyeznie kell azok küldését az ismerősök számára. + Engedélyeznie kell a hangüzenetek küldését az ismerőse számára, hogy hangüzeneteket küldhessenek egymásnak. No comment provided by engineer. You rejected group invitation - Csoport meghívó elutasítva + Csoportmeghívó elutasítva No comment provided by engineer. You sent group invitation - Csoport meghívó elküldve + Csoportmeghívó elküldve No comment provided by engineer. @@ -7626,17 +7903,17 @@ Kapcsolódási kérés megismétlése? You will be connected when group link host's device is online, please wait or check later! - Akkor lesz kapcsolódva, amikor a csoportos hivatkozás tulajdonosának eszköze online lesz, várjon, vagy ellenőrizze később! + Akkor lesz kapcsolódva, amikor a csoporthivatkozás tulajdonosának eszköze online lesz, várjon, vagy ellenőrizze később! No comment provided by engineer. You will be connected when your connection request is accepted, please wait or check later! - Akkor lesz kapcsolódva, ha a kapcsolódási kérelme elfogadásra kerül, várjon, vagy ellenőrizze később! + Akkor lesz kapcsolódva, ha a kapcsolatkérése elfogadásra kerül, várjon, vagy ellenőrizze később! No comment provided by engineer. You will be connected when your contact's device is online, please wait or check later! - Akkor le kapcsolódva, amikor az ismerőse eszköze online lesz, várjon, vagy ellenőrizze később! + Akkor lesz kapcsolódva, amikor az ismerősének eszköze online lesz, várjon, vagy ellenőrizze később! No comment provided by engineer. @@ -7666,12 +7943,12 @@ Kapcsolódási kérés megismétlése? 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 - Egy olyan ismerőst próbál meghívni, akivel inkognító profilt osztott meg abban a csoportban, amelyben saját fő profilja van használatban + Egy olyan ismerősét próbálja meghívni, akivel inkognitóprofilt osztott meg abban a csoportban, amelyben a saját fő profilja van használatban No comment provided by engineer. You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed - Inkognító profilt használ ehhez a csoporthoz - fő profilja megosztásának elkerülése érdekében meghívók küldése tiltott + Inkognitóprofilt használ ehhez a csoporthoz - fő profilja megosztásának elkerülése érdekében a meghívók küldése le van tiltva No comment provided by engineer. @@ -7681,22 +7958,22 @@ Kapcsolódási kérés megismétlése? Your ICE servers - ICE kiszolgálók + Saját ICE-kiszolgálók No comment provided by engineer. Your SMP servers - SMP kiszolgálók + Saját SMP-kiszolgálók No comment provided by engineer. Your SimpleX address - Az ön SimpleX címe + Profil SimpleX-címe No comment provided by engineer. Your XFTP servers - XFTP kiszolgálók + Saját XFTP-kiszolgálók No comment provided by engineer. @@ -7714,19 +7991,29 @@ Kapcsolódási kérés megismétlése? A csevegési adatbázis nincs titkosítva – adjon meg egy jelmondatot a titkosításhoz. No comment provided by engineer. + + Your chat preferences + Csevegési beállítások + alert title + Your chat profiles Csevegési profilok No comment provided by engineer. + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + A kapcsolata át lett helyezve ide: %@, de egy váratlan hiba történt a profilra való átirányításkor. + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). - Ismerőse olyan fájlt küldött, amely meghaladja a jelenleg támogatott maximális méretet (%@). + Az ismerőse olyan fájlt küldött, amely meghaladja a jelenleg támogatott maximális méretet (%@). No comment provided by engineer. Your contacts can allow full message deletion. - Ismerősök engedélyezhetik a teljes üzenet törlést. + Az ismerősei engedélyezhetik a teljes üzenet törlést. No comment provided by engineer. @@ -7734,6 +8021,11 @@ Kapcsolódási kérés megismétlése? Az ismerősei továbbra is kapcsolódva maradnak. No comment provided by engineer. + + Your credentials may be sent unencrypted. + A hitelesítőadatai titkosítatlanul is elküldhetők. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. A jelenlegi csevegési adatbázis TÖRLŐDNI FOG, és a HELYÉRE az importált adatbázis kerül. @@ -7764,16 +8056,19 @@ Kapcsolódási kérés megismétlése? A(z) **%@** nevű profilja megosztásra fog kerülni. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Profilja az eszközön van tárolva, és csak az ismerősökkel kerül megosztásra. -A SimpleX kiszolgálók nem látjhatják profilját. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + A profilja az eszközön van tárolva és csak az ismerőseivel kerül megosztásra. A SimpleX-kiszolgálók nem láthatják a profilját. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + A profilja megváltozott. Ha elmenti, a frissített profil elküldésre kerül az összes ismerősének. + alert message + Your profile, contacts and delivered messages are stored on your device. - Profilja, ismerősei és az elküldött üzenetei az eszközön kerülnek tárolásra. + A profilja, az ismerősei és az elküldött üzenetei az eszközön kerülnek tárolásra. No comment provided by engineer. @@ -7783,12 +8078,12 @@ A SimpleX kiszolgálók nem látjhatják profilját. Your server - Saját kiszolgáló + Saját SMP-kiszolgáló No comment provided by engineer. Your server address - Saját kiszolgáló cím + Saját SMP-kiszolgálójának címe No comment provided by engineer. @@ -7808,7 +8103,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. [Star on GitHub](https://github.com/simplex-chat/simplex-chat) - [Csillag a GitHubon](https://github.com/simplex-chat/simplex-chat) + [Csillagozás a GitHubon](https://github.com/simplex-chat/simplex-chat) No comment provided by engineer. @@ -7843,7 +8138,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. agreeing encryption for %@… - titkosítás jóváhagyása %@ számára… + titkosítás elfogadása %@ számára… chat item text @@ -7888,7 +8183,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. bad message hash - hibás az üzenet ellenőrzőösszege + hibás az üzenet hasító értéke integrity error chat item @@ -7918,7 +8213,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. call error - hiba a hívásban + híváshiba call status @@ -7948,7 +8243,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. changed your role to %@ - megváltoztatta a szerepkörét erre: %@ + megváltoztatta az Ön szerepkörét erre: %@ rcv group event chat item @@ -7973,7 +8268,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. connect to SimpleX Chat developers. - Kapcsolódás a SimpleX Chat fejlesztőkhöz. + kapcsolódás a SimpleX Chat fejlesztőkhöz. No comment provided by engineer. @@ -7983,7 +8278,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. connected directly - közvetlenül kapcsolódva + közvetlenül kapcsolódott rcv group event chat item @@ -8008,12 +8303,12 @@ A SimpleX kiszolgálók nem látjhatják profilját. connecting (introduction invitation) - kapcsolódás (bemutatkozó meghívó) + kapcsolódás (bemutatkozó-meghívó) No comment provided by engineer. connecting call… - hívás kapcsolódik… + kapcsolódási hívás… call status @@ -8143,12 +8438,12 @@ A SimpleX kiszolgálók nem látjhatják profilját. enabled for you - engedélyezve az ön számára + engedélyezve az Ön számára enabled status encryption agreed - titkosítás egyeztetve + titkosítás elfogadva chat item text @@ -8248,17 +8543,17 @@ A SimpleX kiszolgálók nem látjhatják profilját. incognito via contact address link - inkognitó a kapcsolattartási hivatkozáson keresztül + inkognitó a kapcsolattartási címhivatkozáson keresztül chat list item description incognito via group link - inkognitó a csoportos hivatkozáson keresztül + inkognitó a csoporthivatkozáson keresztül chat list item description incognito via one-time link - inkognitó az egyszer használatos hivatkozáson keresztül + inkognitó egy egyszer használható hivatkozáson keresztül chat list item description @@ -8273,7 +8568,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. invalid chat data - érvénytelen csevegés adat + érvénytelen csevegésadat No comment provided by engineer. @@ -8308,7 +8603,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. invited via your group link - meghíva az ön csoport hivatkozásán keresztül + meghíva az Ön csoporthivatkozásán keresztül rcv group event chat item @@ -8328,7 +8623,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. marked deleted - töröltnek jelölve + törlésre jelölve marked deleted chat item preview text @@ -8393,7 +8688,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. new message - új üzenet + Rejtett üzenet notification @@ -8470,7 +8765,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. received answer… - fogadott válasz… + válasz fogadása… No comment provided by engineer. @@ -8495,17 +8790,17 @@ A SimpleX kiszolgálók nem látjhatják profilját. removed contact address - törölt kapcsolattartási cím + eltávolította a kapcsolattartási címet profile update event chat item removed profile picture - törölte a profilképét + eltávolította a profilképét profile update event chat item removed you - eltávolítottak + eltávolította Önt rcv group event chat item @@ -8552,7 +8847,7 @@ A SimpleX kiszolgálók nem látjhatják profilját. server queue info: %1$@ last received msg: %2$@ - kiszolgáló üzenet-várakotatási információ: %1$@ + a kiszolgáló üzenet-sorbaállítási információi: %1$@ utoljára fogadott üzenet: %2$@ queue info @@ -8604,12 +8899,12 @@ utoljára fogadott üzenet: %2$@ unknown status - ismeretlen státusz + ismeretlen állapot No comment provided by engineer. unmute - némítás feloldása + némítás megszüntetése No comment provided by engineer. @@ -8644,17 +8939,17 @@ utoljára fogadott üzenet: %2$@ via group link - csoport hivatkozáson keresztül + a csoporthivatkozáson keresztül chat list item description via one-time link - egyszer használatos hivatkozáson keresztül + egyszer használható hivatkozáson keresztül chat list item description via relay - átjátszón keresztül + közvetítő-kiszolgálón keresztül No comment provided by engineer. @@ -8679,7 +8974,7 @@ utoljára fogadott üzenet: %2$@ wants to connect to you! - kapcsolatba akar lépni önnel! + kapcsolatba akar lépni Önnel! No comment provided by engineer. @@ -8699,7 +8994,7 @@ utoljára fogadott üzenet: %2$@ you - ön + Ön No comment provided by engineer. @@ -8714,7 +9009,7 @@ utoljára fogadott üzenet: %2$@ you blocked %@ - ön letiltotta %@-t + Ön letiltotta őt: %@ snd group event chat item @@ -8729,42 +9024,42 @@ utoljára fogadott üzenet: %2$@ you changed role for yourself to %@ - saját szerepkör megváltoztatva erre: %@ + saját szerepköre megváltozott erre: %@ snd group event chat item you changed role of %1$@ to %2$@ - %1$@ szerepkörét megváltoztatta erre: %@ + Ön megváltoztatta %1$@ szerepkörét erre: %@ snd group event chat item you left - elhagyta a csoportot + Ön elhagyta a csoportot snd group event chat item you removed %@ - eltávolította őt: %@ + Ön eltávolította őt: %@ snd group event chat item you shared one-time link - egyszer használatos hivatkozást osztott meg + egyszer használható hivatkozást osztott meg chat list item description you shared one-time link incognito - egyszer használatos hivatkozást osztott meg inkognitóban + egyszer használható hivatkozást osztott meg inkognitóban chat list item description you unblocked %@ - ön feloldotta %@ letiltását + Ön feloldotta %@ letiltását snd group event chat item you: - ön: + Ön: No comment provided by engineer. @@ -8786,7 +9081,7 @@ utoljára fogadott üzenet: %2$@ SimpleX needs camera access to scan QR codes to connect to other users and for video calls. - A SimpleX-nek kamera-hozzáférésre van szüksége a QR-kódok beolvasásához, hogy kapcsolódhasson más felhasználókhoz és videohívásokhoz. + A SimpleXnek kamera-hozzáférésre van szüksége a QR-kódok beolvasásához, hogy kapcsolódhasson más felhasználókhoz és videohívásokhoz. Privacy - Camera Usage Description @@ -8801,12 +9096,12 @@ utoljára fogadott üzenet: %2$@ SimpleX needs microphone access for audio and video calls, and to record voice messages. - A SimpleX-nek mikrofon-hozzáférésre van szüksége hang- és videohívásokhoz, valamint hangüzenetek rögzítéséhez. + A SimpleXnek mikrofon-hozzáférésre van szüksége hang- és videohívásokhoz, valamint hangüzenetek rögzítéséhez. Privacy - Microphone Usage Description SimpleX needs access to Photo Library for saving captured and received media - A SimpleX-nek hozzáférésre van szüksége a Galériához a rögzített és fogadott média mentéséhez + A SimpleXnek galéria-hozzáférésre van szüksége a rögzített és fogadott média mentéséhez Privacy - Photo Library Additions Usage Description @@ -8892,7 +9187,7 @@ utoljára fogadott üzenet: %2$@ Currently maximum supported file size is %@. - Jelenleg a maximális támogatott fájlméret %@. + Jelenleg a maximálisan támogatott fájlméret: %@. No comment provided by engineer. @@ -8907,7 +9202,7 @@ utoljára fogadott üzenet: %2$@ Database error - Adatbázis hiba + Adatbázishiba No comment provided by engineer. @@ -8917,7 +9212,7 @@ utoljára fogadott üzenet: %2$@ Database passphrase is required to open chat. - Adatbázis jelmondat szükséges a csevegés megnyitásához. + Adatbázis-jelmondat szükséges a csevegés megnyitásához. No comment provided by engineer. @@ -8947,7 +9242,7 @@ utoljára fogadott üzenet: %2$@ Incompatible database version - Nem kompatibilis adatbázis verzió + Nem kompatibilis adatbázis-verzió No comment provided by engineer. @@ -8957,7 +9252,7 @@ utoljára fogadott üzenet: %2$@ Keychain error - Kulcstartó hiba + Kulcstartóhiba No comment provided by engineer. @@ -9022,7 +9317,7 @@ utoljára fogadott üzenet: %2$@ Unknown database error: %@ - Ismeretlen adatbázis hiba: %@ + Ismeretlen adatbázishiba: %@ No comment provided by engineer. @@ -9037,12 +9332,12 @@ utoljára fogadott üzenet: %2$@ Wrong database passphrase - Hibás adatbázis jelmondat + Hibás adatbázis-jelmondat No comment provided by engineer. You can allow sharing in Privacy & Security / SimpleX Lock settings. - A megosztást az Adatvédelem és biztonság / SimpleX zár menüben engedélyezheti. + A megosztást az Adatvédelem és biztonság / SimpleX-zár menüben engedélyezheti. No comment provided by engineer. 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 72eb3561e3..488d51d225 100644 --- a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff +++ b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff @@ -137,6 +137,11 @@ %@ si vuole connettere! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ e %lld membri @@ -157,11 +162,36 @@ %d giorni time interval + + %d file(s) are still being downloaded. + %d file è/sono ancora in scaricamento. + forward confirmation reason + + + %d file(s) failed to download. + %d file ha/hanno fallito lo scaricamento. + forward confirmation reason + + + %d file(s) were deleted. + %d file è/sono stato/i eliminato/i. + forward confirmation reason + + + %d file(s) were not downloaded. + %d file non è/sono stato/i scaricato/i. + forward confirmation reason + %d hours %d ore time interval + + %d messages not forwarded + %d messaggi non inoltrati + alert title + %d min %d min @@ -723,7 +753,7 @@ All profiles Tutti gli profili - No comment provided by engineer. + profile dropdown All your contacts will remain connected. @@ -915,6 +945,11 @@ Il codice di accesso dell'app viene sostituito da un codice di autodistruzione. No comment provided by engineer. + + App session + Sessione dell'app + No comment provided by engineer. + App version Versione dell'app @@ -1020,6 +1055,11 @@ Auto-accetta immagini No comment provided by engineer. + + Auto-accept settings + Accetta automaticamente le impostazioni + alert title + Back Indietro @@ -1045,11 +1085,21 @@ Hash del messaggio errato No comment provided by engineer. + + Better calls + Chiamate migliorate + No comment provided by engineer. + Better groups Gruppi migliorati No comment provided by engineer. + + Better message dates. + Date dei messaggi migliorate. + No comment provided by engineer. + Better messages Messaggi migliorati @@ -1060,6 +1110,21 @@ Rete migliorata No comment provided by engineer. + + Better notifications + Notifiche migliorate + No comment provided by engineer. + + + Better security ✅ + Sicurezza migliorata ✅ + No comment provided by engineer. + + + Better user experience + Esperienza utente migliorata + No comment provided by engineer. + Black Nero @@ -1193,7 +1258,7 @@ Cancel Annulla - No comment provided by engineer. + alert button Cancel migration @@ -1213,7 +1278,7 @@ Cannot receive file Impossibile ricevere il file - No comment provided by engineer. + alert title Capacity exceeded - recipient did not receive previously sent messages. @@ -1341,6 +1406,16 @@ Preferenze della chat No comment provided by engineer. + + Chat preferences were changed. + Le preferenze della chat sono state cambiate. + alert message + + + Chat profile + Profilo utente + No comment provided by engineer. + Chat theme Tema della chat @@ -1740,6 +1815,11 @@ Questo è il tuo link una tantum! Versione core: v%@ No comment provided by engineer. + + Corner + Angolo + No comment provided by engineer. + Correct name to %@? Correggere il nome a %@? @@ -1865,6 +1945,11 @@ Questo è il tuo link una tantum! Tempo personalizzato No comment provided by engineer. + + Customizable message shape. + Forma dei messaggi personalizzabile. + No comment provided by engineer. + Customize theme Personalizza il tema @@ -2159,6 +2244,11 @@ Questo è il tuo link una tantum! Eliminare il database vecchio? No comment provided by engineer. + + Delete or moderate up to 200 messages. + Elimina o modera fino a 200 messaggi. + No comment provided by engineer. + Delete pending connection? Eliminare la connessione in attesa? @@ -2409,6 +2499,11 @@ Questo è il tuo link una tantum! Non inviare la cronologia ai nuovi membri. No comment provided by engineer. + + Do not use credentials with proxy. + Non usare credenziali con proxy. + No comment provided by engineer. + Don't create address Non creare un indirizzo @@ -2432,7 +2527,8 @@ Questo è il tuo link una tantum! Download Scarica - chat item action + alert button + chat item action Download errors @@ -2449,6 +2545,11 @@ Questo è il tuo link una tantum! Scarica file server test step + + Download files + Scarica i file + alert action + Downloaded Scaricato @@ -2724,6 +2825,11 @@ Questo è il tuo link una tantum! Errore nella modifica dell'indirizzo No comment provided by engineer. + + Error changing connection profile + Errore nel cambio di profilo di connessione + No comment provided by engineer. + Error changing role Errore nel cambio di ruolo @@ -2734,6 +2840,11 @@ Questo è il tuo link una tantum! Errore nella modifica dell'impostazione No comment provided by engineer. + + Error changing to incognito! + Errore nel passaggio a incognito! + No comment provided by engineer. + Error connecting to forwarding server %@. Please try later. Errore di connessione al server di inoltro %@. Riprova più tardi. @@ -2854,6 +2965,11 @@ Questo è il tuo link una tantum! Errore nel caricamento dei server %@ No comment provided by engineer. + + Error migrating settings + Errore nella migrazione delle impostazioni + No comment provided by engineer. + Error opening chat Errore di apertura della chat @@ -2862,7 +2978,7 @@ Questo è il tuo link una tantum! Error receiving file Errore nella ricezione del file - No comment provided by engineer. + alert title Error reconnecting server @@ -2954,10 +3070,15 @@ Questo è il tuo link una tantum! Errore nell'interruzione della chat No comment provided by engineer. + + Error switching profile + Errore nel cambio di profilo + No comment provided by engineer. + Error switching profile! Errore nel cambio di profilo! - No comment provided by engineer. + alertTitle Error synchronizing connection @@ -3002,8 +3123,7 @@ Questo è il tuo link una tantum! Error: %@ Errore: %@ - file error text - snd error text + alert message Error: URL is invalid @@ -3090,6 +3210,13 @@ Questo è il tuo link una tantum! Errore del file No comment provided by engineer. + + File errors: +%@ + Errori di file: +%@ + alert message + File not found - most likely file was deleted or cancelled. File non trovato - probabilmente è stato eliminato o annullato. @@ -3225,11 +3352,31 @@ Questo è il tuo link una tantum! Inoltra chat item action + + Forward %d message(s)? + Inoltrare %d messaggio/i? + alert title + Forward and save messages Inoltra e salva i messaggi No comment provided by engineer. + + Forward messages + Inoltra i messaggi + alert action + + + Forward messages without files? + Inoltrare i messaggi senza file? + alert message + + + Forward up to 20 messages at once. + Inoltra fino a 20 messaggi alla volta. + No comment provided by engineer. + Forwarded Inoltrato @@ -3240,6 +3387,11 @@ Questo è il tuo link una tantum! Inoltrato da No comment provided by engineer. + + Forwarding %lld messages + Inoltro di %lld messaggi + No comment provided by engineer. + Forwarding server %@ failed to connect to destination server %@. Please try later. Il server di inoltro %@ non è riuscito a connettersi al server di destinazione %@. Riprova più tardi. @@ -3289,11 +3441,6 @@ Errore: %2$@ Nome completo (facoltativo) No comment provided by engineer. - - Full name: - Nome completo: - No comment provided by engineer. - Fully decentralized – visible only to members. Completamente decentralizzato: visibile solo ai membri. @@ -3539,6 +3686,11 @@ Errore: %2$@ Server ICE (uno per riga) No comment provided by engineer. + + IP address + Indirizzo IP + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Se non potete incontrarvi di persona, mostra il codice QR in una videochiamata o condividi il link. @@ -3614,6 +3766,13 @@ Errore: %2$@ Importazione archivio No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + Consegna migliorata, utilizzo di traffico ridotto. +Altri miglioramenti sono in arrivo! + No comment provided by engineer. + Improved message delivery Consegna dei messaggi migliorata @@ -4184,6 +4343,11 @@ Questo è il tuo link per il gruppo %@! Server dei messaggi No comment provided by engineer. + + Message shape + Forma del messaggio + No comment provided by engineer. + Message source remains private. La fonte del messaggio resta privata. @@ -4234,6 +4398,11 @@ Questo è il tuo link per il gruppo %@! Messaggi inviati No comment provided by engineer. + + Messages were deleted after you selected them. + I messaggi sono stati eliminati dopo che li hai selezionati. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. I messaggi, i file e le chiamate sono protetti da **crittografia end-to-end** con perfect forward secrecy, ripudio e recupero da intrusione. @@ -4389,6 +4558,16 @@ Questo è il tuo link per il gruppo %@! Nuovo codice di accesso No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + Le nuove credenziali SOCKS verranno usate ogni volta che avvii l'app. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + Le nuove credenziali SOCKS verranno usate per ogni server. + No comment provided by engineer. + New chat Nuova chat @@ -4509,6 +4688,16 @@ Questo è il tuo link per il gruppo %@! Nessuna connessione di rete No comment provided by engineer. + + No permission to record speech + Nessuna autorizzazione per registrare l'audio + No comment provided by engineer. + + + No permission to record video + Nessuna autorizzazione per registrare il video + No comment provided by engineer. + No permission to record voice message Nessuna autorizzazione per registrare messaggi vocali @@ -4529,6 +4718,11 @@ Questo è il tuo link per il gruppo %@! Nessuna selezione No comment provided by engineer. + + Nothing to forward! + Niente da inoltrare! + alert title + Notifications Notifiche @@ -4561,7 +4755,7 @@ Questo è il tuo link per il gruppo %@! Ok Ok - No comment provided by engineer. + alert button Old database @@ -4752,6 +4946,13 @@ Richiede l'attivazione della VPN. Altri %@ server No comment provided by engineer. + + Other file errors: +%@ + Altri errori di file: +%@ + alert message + PING count Conteggio PING @@ -4787,6 +4988,11 @@ Richiede l'attivazione della VPN. Codice di accesso impostato! No comment provided by engineer. + + Password + Password + No comment provided by engineer. + Password to show Password per mostrare @@ -4936,6 +5142,11 @@ Errore: %@ Interfaccia polacca No comment provided by engineer. + + Port + Porta + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Probabilmente l'impronta del certificato nell'indirizzo del server è sbagliata @@ -5021,16 +5232,6 @@ Errore: %@ Immagini del profilo No comment provided by engineer. - - Profile name - Nome del profilo - No comment provided by engineer. - - - Profile name: - Nome del profilo: - No comment provided by engineer. - Profile password Password del profilo @@ -5133,6 +5334,11 @@ Attivalo nelle impostazioni *Rete e server*. Server via proxy No comment provided by engineer. + + Proxy requires password + Il proxy richiede una password + No comment provided by engineer. + Push notifications Notifiche push @@ -5354,6 +5560,11 @@ Attivalo nelle impostazioni *Rete e server*. Rimuovi No comment provided by engineer. + + Remove archive? + Rimuovere l'archivio? + No comment provided by engineer. + Remove image Rimuovi immagine @@ -5534,6 +5745,11 @@ Attivalo nelle impostazioni *Rete e server*. Server SMP No comment provided by engineer. + + SOCKS proxy + Proxy SOCKS + No comment provided by engineer. + Safely receive files Ricevi i file in sicurezza @@ -5547,12 +5763,13 @@ Attivalo nelle impostazioni *Rete e server*. Save Salva - chat item action + alert button + chat item action Save (and notify contacts) Salva (e avvisa i contatti) - No comment provided by engineer. + alert button Save and notify contact @@ -5579,11 +5796,6 @@ Attivalo nelle impostazioni *Rete e server*. Salva archivio No comment provided by engineer. - - Save auto-accept settings - Salva le impostazioni di accettazione automatica - No comment provided by engineer. - Save group profile Salva il profilo del gruppo @@ -5619,16 +5831,16 @@ Attivalo nelle impostazioni *Rete e server*. Salvare i server? No comment provided by engineer. - - Save settings? - Salvare le impostazioni? - No comment provided by engineer. - Save welcome message? Salvare il messaggio di benvenuto? No comment provided by engineer. + + Save your profile? + Salvare il profilo? + alert title + Saved Salvato @@ -5649,6 +5861,11 @@ Attivalo nelle impostazioni *Rete e server*. Messaggio salvato message info title + + Saving %lld messages + Salvataggio di %lld messaggi + No comment provided by engineer. + Scale Scala @@ -5729,6 +5946,11 @@ Attivalo nelle impostazioni *Rete e server*. Seleziona chat item action + + Select chat profile + Seleziona il profilo di chat + No comment provided by engineer. + Selected %lld %lld selezionato @@ -5847,7 +6069,7 @@ Attivalo nelle impostazioni *Rete e server*. Sender cancelled file transfer. Il mittente ha annullato il trasferimento del file. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -5944,6 +6166,11 @@ Attivalo nelle impostazioni *Rete e server*. Inviato via proxy No comment provided by engineer. + + Server + Server + No comment provided by engineer. + Server address Indirizzo server @@ -6064,6 +6291,11 @@ Attivalo nelle impostazioni *Rete e server*. Impostazioni No comment provided by engineer. + + Settings were changed. + Le impostazioni sono state cambiate. + alert message + Shape profile images Forma delle immagini del profilo @@ -6099,6 +6331,11 @@ Attivalo nelle impostazioni *Rete e server*. Condividi link No comment provided by engineer. + + Share profile + Condividi il profilo + No comment provided by engineer. + Share this 1-time invite link Condividi questo link di invito una tantum @@ -6234,6 +6471,11 @@ Attivalo nelle impostazioni *Rete e server*. Invito SimpleX una tantum simplex link type + + SimpleX protocols reviewed by Trail of Bits. + Protocolli di SimpleX esaminati da Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Modalità incognito semplificata @@ -6264,6 +6506,11 @@ Attivalo nelle impostazioni *Rete e server*. Leggera blur media + + Some app settings were not migrated. + Alcune impostazioni dell'app non sono state migrate. + No comment provided by engineer. + Some file(s) were not exported: Alcuni file non sono stati esportati: @@ -6404,6 +6651,16 @@ Attivalo nelle impostazioni *Rete e server*. Supporta SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + Cambia tra audio e video durante la chiamata. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + Cambia profilo di chat per inviti una tantum. + No comment provided by engineer. + System Sistema @@ -6439,6 +6696,11 @@ Attivalo nelle impostazioni *Rete e server*. TCP_KEEPINTVL No comment provided by engineer. + + Tail + Coda + No comment provided by engineer. + Take picture Scatta foto @@ -6631,6 +6893,11 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa.Il testo che hai incollato non è un link SimpleX. No comment provided by engineer. + + The uploaded database archive will be permanently removed from the servers. + L'archivio del database caricato verrà rimosso definitivamente dai server. + No comment provided by engineer. + Themes Temi @@ -6758,6 +7025,16 @@ You will be prompted to complete authentication before this feature is enabled.< Ti verrà chiesto di completare l'autenticazione prima di attivare questa funzionalità. No comment provided by engineer. + + To record speech please grant permission to use Microphone. + Per registrare l'audio, concedi l'autorizzazione di usare il microfono. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + Per registrare il video, concedi l'autorizzazione di usare la fotocamera. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Per registrare un messaggio vocale, concedi l'autorizzazione all'uso del microfono. @@ -6911,7 +7188,7 @@ Ti verrà chiesto di completare l'autenticazione prima di attivare questa funzio Unknown servers! Server sconosciuti! - No comment provided by engineer. + alert title Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. @@ -7025,6 +7302,11 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Usa gli host .onion No comment provided by engineer. + + Use SOCKS proxy + Usa proxy SOCKS + No comment provided by engineer. + Use SimpleX Chat servers? Usare i server di SimpleX Chat? @@ -7090,16 +7372,16 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Usa l'app con una mano sola. No comment provided by engineer. - - User profile - Profilo utente - No comment provided by engineer. - User selection Selezione utente No comment provided by engineer. + + Username + Nome utente + No comment provided by engineer. + Using SimpleX Chat servers. Utilizzo dei server SimpleX Chat. @@ -7333,7 +7615,7 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. Senza Tor o VPN, il tuo indirizzo IP sarà visibile a questi relay XFTP: %@. - No comment provided by engineer. + alert message Wrong database passphrase @@ -7360,11 +7642,6 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Server XFTP No comment provided by engineer. - - You - Tu - No comment provided by engineer. - You **must not** use the same database on two devices. **Non devi** usare lo stesso database su due dispositivi. @@ -7714,11 +7991,21 @@ Ripetere la richiesta di connessione? Il tuo database della chat non è crittografato: imposta la password per crittografarlo. No comment provided by engineer. + + Your chat preferences + Le tue preferenze della chat + alert title + Your chat profiles I tuoi profili di chat No comment provided by engineer. + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + La tua connessione è stata spostata a %@, ma si è verificato un errore imprevisto durante il reindirizzamento al profilo. + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Il tuo contatto ha inviato un file più grande della dimensione massima attualmente supportata (%@). @@ -7734,6 +8021,11 @@ Ripetere la richiesta di connessione? I tuoi contatti resteranno connessi. No comment provided by engineer. + + Your credentials may be sent unencrypted. + Le credenziali potrebbero essere inviate in chiaro. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Il tuo attuale database della chat verrà ELIMINATO e SOSTITUITO con quello importato. @@ -7764,13 +8056,16 @@ Ripetere la richiesta di connessione? Verrà condiviso il tuo profilo **%@**. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Il tuo profilo è memorizzato sul tuo dispositivo e condiviso solo con i tuoi contatti. -I server di SimpleX non possono vedere il tuo profilo. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Il tuo profilo è memorizzato sul tuo dispositivo e condiviso solo con i tuoi contatti. I server di SimpleX non possono vedere il tuo profilo. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + Il tuo profilo è stato cambiato. Se lo salvi, il profilo aggiornato verrà inviato a tutti i tuoi contatti. + alert message + Your profile, contacts and delivered messages are stored on your device. Il tuo profilo, i contatti e i messaggi recapitati sono memorizzati sul tuo dispositivo. @@ -7948,7 +8243,7 @@ I server di SimpleX non possono vedere il tuo profilo. changed your role to %@ - cambiato il tuo ruolo in %@ + ha cambiato il tuo ruolo in %@ rcv group event chat item 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 a545f3ba05..4fa8144d91 100644 --- a/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff +++ b/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff @@ -137,6 +137,10 @@ %@ が接続を希望しています! notification title + + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@や%@など%lld人のメンバー @@ -157,11 +161,31 @@ %d 日 time interval + + %d file(s) are still being downloaded. + forward confirmation reason + + + %d file(s) failed to download. + forward confirmation reason + + + %d file(s) were deleted. + forward confirmation reason + + + %d file(s) were not downloaded. + forward confirmation reason + %d hours %d 時 time interval + + %d messages not forwarded + alert title + %d min %d 分 @@ -703,7 +727,7 @@ All profiles - No comment provided by engineer. + profile dropdown All your contacts will remain connected. @@ -891,6 +915,10 @@ アプリのパスコードは自己破壊パスコードに置き換えられます。 No comment provided by engineer. + + App session + No comment provided by engineer. + App version アプリのバージョン @@ -990,6 +1018,10 @@ 画像を自動的に受信 No comment provided by engineer. + + Auto-accept settings + alert title + Back 戻る @@ -1013,10 +1045,18 @@ メッセージのハッシュ値問題 No comment provided by engineer. + + Better calls + No comment provided by engineer. + Better groups No comment provided by engineer. + + Better message dates. + No comment provided by engineer. + Better messages より良いメッセージ @@ -1026,6 +1066,18 @@ Better networking No comment provided by engineer. + + Better notifications + No comment provided by engineer. + + + Better security ✅ + No comment provided by engineer. + + + Better user experience + No comment provided by engineer. + Black No comment provided by engineer. @@ -1144,7 +1196,7 @@ Cancel 中止 - No comment provided by engineer. + alert button Cancel migration @@ -1162,7 +1214,7 @@ Cannot receive file ファイル受信ができません - No comment provided by engineer. + alert title Capacity exceeded - recipient did not receive previously sent messages. @@ -1283,6 +1335,15 @@ チャット設定 No comment provided by engineer. + + Chat preferences were changed. + alert message + + + Chat profile + ユーザープロフィール + No comment provided by engineer. + Chat theme No comment provided by engineer. @@ -1640,6 +1701,10 @@ This is your own one-time link! コアのバージョン: v%@ No comment provided by engineer. + + Corner + No comment provided by engineer. + Correct name to %@? No comment provided by engineer. @@ -1755,6 +1820,10 @@ This is your own one-time link! カスタム時間 No comment provided by engineer. + + Customizable message shape. + No comment provided by engineer. + Customize theme No comment provided by engineer. @@ -2041,6 +2110,10 @@ This is your own one-time link! 古いデータベースを削除しますか? No comment provided by engineer. + + Delete or moderate up to 200 messages. + No comment provided by engineer. + Delete pending connection? 接続待ちの接続を削除しますか? @@ -2272,6 +2345,10 @@ This is your own one-time link! Do not send history to new members. No comment provided by engineer. + + Do not use credentials with proxy. + No comment provided by engineer. + Don't create address アドレスを作成しないでください @@ -2294,7 +2371,8 @@ This is your own one-time link! Download - chat item action + alert button + chat item action Download errors @@ -2309,6 +2387,10 @@ This is your own one-time link! ファイルをダウンロード server test step + + Download files + alert action + Downloaded No comment provided by engineer. @@ -2569,6 +2651,10 @@ This is your own one-time link! アドレス変更にエラー発生 No comment provided by engineer. + + Error changing connection profile + No comment provided by engineer. + Error changing role 役割変更にエラー発生 @@ -2579,6 +2665,10 @@ This is your own one-time link! 設定変更にエラー発生 No comment provided by engineer. + + Error changing to incognito! + No comment provided by engineer. + Error connecting to forwarding server %@. Please try later. No comment provided by engineer. @@ -2694,6 +2784,10 @@ This is your own one-time link! %@ サーバーのロード中にエラーが発生 No comment provided by engineer. + + Error migrating settings + No comment provided by engineer. + Error opening chat No comment provided by engineer. @@ -2701,7 +2795,7 @@ This is your own one-time link! Error receiving file ファイル受信にエラー発生 - No comment provided by engineer. + alert title Error reconnecting server @@ -2787,10 +2881,14 @@ This is your own one-time link! チャット停止にエラー発生 No comment provided by engineer. + + Error switching profile + No comment provided by engineer. + Error switching profile! プロフィール切り替えにエラー発生! - No comment provided by engineer. + alertTitle Error synchronizing connection @@ -2833,8 +2931,7 @@ This is your own one-time link! Error: %@ エラー : %@ - file error text - snd error text + alert message Error: URL is invalid @@ -2915,6 +3012,11 @@ This is your own one-time link! File error No comment provided by engineer. + + File errors: +%@ + alert message + File not found - most likely file was deleted or cancelled. file error text @@ -3041,10 +3143,26 @@ This is your own one-time link! Forward chat item action + + Forward %d message(s)? + alert title + Forward and save messages No comment provided by engineer. + + Forward messages + alert action + + + Forward messages without files? + alert message + + + Forward up to 20 messages at once. + No comment provided by engineer. + Forwarded No comment provided by engineer. @@ -3053,6 +3171,10 @@ This is your own one-time link! Forwarded from No comment provided by engineer. + + Forwarding %lld messages + No comment provided by engineer. + Forwarding server %@ failed to connect to destination server %@. Please try later. No comment provided by engineer. @@ -3094,11 +3216,6 @@ Error: %2$@ フルネーム (任意): No comment provided by engineer. - - Full name: - フルネーム: - No comment provided by engineer. - Fully decentralized – visible only to members. No comment provided by engineer. @@ -3336,6 +3453,10 @@ Error: %2$@ ICEサーバ (1行に1サーバ) No comment provided by engineer. + + IP address + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. 直接会えない場合は、ビデオ通話で QR コードを表示するか、リンクを共有してください。 @@ -3408,6 +3529,11 @@ Error: %2$@ Importing archive No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + No comment provided by engineer. + Improved message delivery No comment provided by engineer. @@ -3942,6 +4068,10 @@ This is your link for group %@! Message servers No comment provided by engineer. + + Message shape + No comment provided by engineer. + Message source remains private. No comment provided by engineer. @@ -3985,12 +4115,18 @@ This is your link for group %@! Messages sent No comment provided by engineer. + + Messages were deleted after you selected them. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. + メッセージ、ファイル、通話は、前方秘匿性、否認可能性および侵入復元性を備えた**エンドツーエンドの暗号化**によって保護されます。 No comment provided by engineer. Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery. + メッセージ、ファイル、通話は、前方秘匿性、否認可能性および侵入復元性を備えた**耐量子E2E暗号化**によって保護されます。 No comment provided by engineer. @@ -4127,6 +4263,14 @@ This is your link for group %@! 新しいパスコード No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + No comment provided by engineer. + New chat No comment provided by engineer. @@ -4241,6 +4385,14 @@ This is your link for group %@! No network connection No comment provided by engineer. + + No permission to record speech + No comment provided by engineer. + + + No permission to record video + No comment provided by engineer. + No permission to record voice message 音声メッセージを録音する権限がありません @@ -4259,6 +4411,10 @@ This is your link for group %@! Nothing selected No comment provided by engineer. + + Nothing to forward! + alert title + Notifications 通知 @@ -4290,7 +4446,7 @@ This is your link for group %@! Ok OK - No comment provided by engineer. + alert button Old database @@ -4470,6 +4626,11 @@ VPN を有効にする必要があります。 Other %@ servers No comment provided by engineer. + + Other file errors: +%@ + alert message + PING count PING回数 @@ -4505,6 +4666,10 @@ VPN を有効にする必要があります。 パスコードを設定しました! No comment provided by engineer. + + Password + No comment provided by engineer. + Password to show パスワードを表示する @@ -4641,6 +4806,10 @@ Error: %@ ポーランド語UI No comment provided by engineer. + + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect サーバアドレスの証明証IDが正しくないかもしれません @@ -4719,14 +4888,6 @@ Error: %@ Profile images No comment provided by engineer. - - Profile name - No comment provided by engineer. - - - Profile name: - No comment provided by engineer. - Profile password プロフィールのパスワード @@ -4822,6 +4983,10 @@ Enable in *Network & servers* settings. Proxied servers No comment provided by engineer. + + Proxy requires password + No comment provided by engineer. + Push notifications プッシュ通知 @@ -5027,6 +5192,10 @@ Enable in *Network & servers* settings. 削除 No comment provided by engineer. + + Remove archive? + No comment provided by engineer. + Remove image No comment provided by engineer. @@ -5194,6 +5363,10 @@ Enable in *Network & servers* settings. SMP server No comment provided by engineer. + + SOCKS proxy + No comment provided by engineer. + Safely receive files No comment provided by engineer. @@ -5205,12 +5378,13 @@ Enable in *Network & servers* settings. Save 保存 - chat item action + alert button + chat item action Save (and notify contacts) 保存(連絡先に通知) - No comment provided by engineer. + alert button Save and notify contact @@ -5236,11 +5410,6 @@ Enable in *Network & servers* settings. アーカイブを保存 No comment provided by engineer. - - Save auto-accept settings - 自動受け入れ設定を保存する - No comment provided by engineer. - Save group profile グループプロフィールの保存 @@ -5276,16 +5445,15 @@ Enable in *Network & servers* settings. サーバを保存しますか? No comment provided by engineer. - - Save settings? - 設定を保存しますか? - No comment provided by engineer. - Save welcome message? ウェルカムメッセージを保存しますか? No comment provided by engineer. + + Save your profile? + alert title + Saved No comment provided by engineer. @@ -5303,6 +5471,10 @@ Enable in *Network & servers* settings. Saved message message info title + + Saving %lld messages + No comment provided by engineer. + Scale No comment provided by engineer. @@ -5376,6 +5548,10 @@ Enable in *Network & servers* settings. 選択 chat item action + + Select chat profile + No comment provided by engineer. + Selected %lld No comment provided by engineer. @@ -5485,7 +5661,7 @@ Enable in *Network & servers* settings. Sender cancelled file transfer. 送信者がファイル転送をキャンセルしました。 - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -5571,6 +5747,10 @@ Enable in *Network & servers* settings. Sent via proxy No comment provided by engineer. + + Server + No comment provided by engineer. + Server address No comment provided by engineer. @@ -5680,6 +5860,10 @@ Enable in *Network & servers* settings. 設定 No comment provided by engineer. + + Settings were changed. + alert message + Shape profile images No comment provided by engineer. @@ -5713,6 +5897,10 @@ Enable in *Network & servers* settings. リンクを送る No comment provided by engineer. + + Share profile + No comment provided by engineer. + Share this 1-time invite link No comment provided by engineer. @@ -5839,6 +6027,10 @@ Enable in *Network & servers* settings. SimpleX使い捨て招待リンク simplex link type + + SimpleX protocols reviewed by Trail of Bits. + No comment provided by engineer. + Simplified incognito mode シークレットモードの簡素化 @@ -5867,6 +6059,10 @@ Enable in *Network & servers* settings. Soft blur media + + Some app settings were not migrated. + No comment provided by engineer. + Some file(s) were not exported: No comment provided by engineer. @@ -5995,6 +6191,14 @@ Enable in *Network & servers* settings. Simplex Chatを支援 No comment provided by engineer. + + Switch audio and video during the call. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + No comment provided by engineer. + System システム @@ -6029,6 +6233,10 @@ Enable in *Network & servers* settings. TCP_KEEPINTVL No comment provided by engineer. + + Tail + No comment provided by engineer. + Take picture 写真を撮影 @@ -6212,6 +6420,10 @@ It can happen because of some bug or when the connection is compromised.The text you pasted is not a SimpleX link. No comment provided by engineer. + + The uploaded database archive will be permanently removed from the servers. + No comment provided by engineer. + Themes No comment provided by engineer. @@ -6327,6 +6539,14 @@ You will be prompted to complete authentication before this feature is enabled.< オンにするには、認証ステップが行われます。 No comment provided by engineer. + + To record speech please grant permission to use Microphone. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. 音声メッセージを録音する場合は、マイクの使用を許可してください。 @@ -6468,7 +6688,7 @@ You will be prompted to complete authentication before this feature is enabled.< Unknown servers! - No comment provided by engineer. + alert title Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. @@ -6573,6 +6793,10 @@ To connect, please ask your contact to create another connection link and check .onionホストを使う No comment provided by engineer. + + Use SOCKS proxy + No comment provided by engineer. + Use SimpleX Chat servers? SimpleX チャット サーバーを使用しますか? @@ -6632,15 +6856,14 @@ To connect, please ask your contact to create another connection link and check Use the app with one hand. No comment provided by engineer. - - User profile - ユーザープロフィール - No comment provided by engineer. - User selection No comment provided by engineer. + + Username + No comment provided by engineer. + Using SimpleX Chat servers. SimpleX チャット サーバーを使用する。 @@ -6853,7 +7076,7 @@ To connect, please ask your contact to create another connection link and check Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. - No comment provided by engineer. + alert message Wrong database passphrase @@ -6877,11 +7100,6 @@ To connect, please ask your contact to create another connection link and check XFTP server No comment provided by engineer. - - You - あなた - No comment provided by engineer. - You **must not** use the same database on two devices. No comment provided by engineer. @@ -7207,11 +7425,19 @@ Repeat connection request? チャット データベースは暗号化されていません - 暗号化するにはパスフレーズを設定してください。 No comment provided by engineer. + + Your chat preferences + alert title + Your chat profiles あなたのチャットプロフィール No comment provided by engineer. + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). 連絡先が現在サポートされている最大サイズ (%@) より大きいファイルを送信しました。 @@ -7227,6 +7453,10 @@ Repeat connection request? 連絡先は接続されたままになります。 No comment provided by engineer. + + Your credentials may be sent unencrypted. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. 現在のチャット データベースは削除され、インポートされたデータベースに置き換えられます。 @@ -7256,13 +7486,15 @@ Repeat connection request? あなたのプロファイル **%@** が共有されます。 No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - プロフィールはデバイスに保存され、連絡先とのみ共有されます。 -SimpleX サーバーはあなたのプロファイルを参照できません。 + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + プロフィールはデバイスに保存され、連絡先とのみ共有されます。 SimpleX サーバーはあなたのプロファイルを参照できません。 No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + alert message + Your profile, contacts and delivered messages are stored on your device. あなたのプロフィール、連絡先、送信したメッセージがご自分の端末に保存されます。 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 15a8c01a64..ce6faeccca 100644 --- a/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff +++ b/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff @@ -137,6 +137,11 @@ %@ wil verbinding maken! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ en %lld leden @@ -157,11 +162,36 @@ %d dagen time interval + + %d file(s) are still being downloaded. + %d bestand(en) worden nog gedownload. + forward confirmation reason + + + %d file(s) failed to download. + %d bestand(en) konden niet worden gedownload. + forward confirmation reason + + + %d file(s) were deleted. + %d bestand(en) zijn verwijderd. + forward confirmation reason + + + %d file(s) were not downloaded. + %d bestand(en) zijn niet gedownload. + forward confirmation reason + %d hours %d uren time interval + + %d messages not forwarded + %d berichten niet doorgestuurd + alert title + %d min %d min @@ -512,7 +542,7 @@ A separate TCP connection will be used **for each chat profile you have in the app**. - Er wordt een aparte TCP-verbinding gebruikt **voor elk chat profiel dat je in de app hebt**. + Er wordt een aparte TCP-verbinding gebruikt **voor elk chatprofiel dat je in de app hebt**. No comment provided by engineer. @@ -723,7 +753,7 @@ All profiles Alle profielen - No comment provided by engineer. + profile dropdown All your contacts will remain connected. @@ -915,6 +945,11 @@ De app-toegangscode wordt vervangen door een zelfvernietigings wachtwoord. No comment provided by engineer. + + App session + Appsessie + No comment provided by engineer. + App version App versie @@ -1020,6 +1055,11 @@ Afbeeldingen automatisch accepteren No comment provided by engineer. + + Auto-accept settings + Instellingen automatisch accepteren + alert title + Back Terug @@ -1045,11 +1085,21 @@ Onjuiste bericht hash No comment provided by engineer. + + Better calls + Betere gesprekken + No comment provided by engineer. + Better groups Betere groepen No comment provided by engineer. + + Better message dates. + Betere datums voor berichten. + No comment provided by engineer. + Better messages Betere berichten @@ -1060,6 +1110,21 @@ Beter netwerk No comment provided by engineer. + + Better notifications + Betere meldingen + No comment provided by engineer. + + + Better security ✅ + Betere beveiliging ✅ + No comment provided by engineer. + + + Better user experience + Betere gebruikerservaring + No comment provided by engineer. + Black Zwart @@ -1142,7 +1207,7 @@ By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). - Via chat profiel (standaard) of [via verbinding](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). + Via chatprofiel (standaard) of [via verbinding](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). No comment provided by engineer. @@ -1193,7 +1258,7 @@ Cancel Annuleren - No comment provided by engineer. + alert button Cancel migration @@ -1213,7 +1278,7 @@ Cannot receive file Kan bestand niet ontvangen - No comment provided by engineer. + alert title Capacity exceeded - recipient did not receive previously sent messages. @@ -1341,6 +1406,16 @@ Gesprek voorkeuren No comment provided by engineer. + + Chat preferences were changed. + Chatvoorkeuren zijn gewijzigd. + alert message + + + Chat profile + Gebruikers profiel + No comment provided by engineer. + Chat theme Chat thema @@ -1348,7 +1423,7 @@ Chats - Gesprekken + Chats No comment provided by engineer. @@ -1438,7 +1513,7 @@ Completed - voltooid + Voltooid No comment provided by engineer. @@ -1740,6 +1815,11 @@ Dit is uw eigen eenmalige link! Core versie: v% @ No comment provided by engineer. + + Corner + Hoek + No comment provided by engineer. + Correct name to %@? Juiste naam voor %@? @@ -1865,6 +1945,11 @@ Dit is uw eigen eenmalige link! Aangepaste tijd No comment provided by engineer. + + Customizable message shape. + Aanpasbare berichtvorm. + No comment provided by engineer. + Customize theme Thema aanpassen @@ -1951,7 +2036,7 @@ Dit is uw eigen eenmalige link! Database passphrase is required to open chat. - Database wachtwoord is vereist om je gesprekken te openen. + Database wachtwoord is vereist om je chats te openen. No comment provided by engineer. @@ -2046,12 +2131,12 @@ Dit is uw eigen eenmalige link! Delete chat profile - Chat profiel verwijderen + Chatprofiel verwijderen No comment provided by engineer. Delete chat profile? - Chat profiel verwijderen? + Chatprofiel verwijderen? No comment provided by engineer. @@ -2091,7 +2176,7 @@ Dit is uw eigen eenmalige link! Delete files for all chat profiles - Verwijder bestanden voor alle chat profielen + Verwijder bestanden voor alle chatprofielen No comment provided by engineer. @@ -2159,6 +2244,11 @@ Dit is uw eigen eenmalige link! Oude database verwijderen? No comment provided by engineer. + + Delete or moderate up to 200 messages. + Maximaal 200 berichten verwijderen of modereren. + No comment provided by engineer. + Delete pending connection? Wachtende verbinding verwijderen? @@ -2409,6 +2499,11 @@ Dit is uw eigen eenmalige link! Stuur geen geschiedenis naar nieuwe leden. No comment provided by engineer. + + Do not use credentials with proxy. + Gebruik geen inloggegevens met proxy. + No comment provided by engineer. + Don't create address Maak geen adres aan @@ -2432,7 +2527,8 @@ Dit is uw eigen eenmalige link! Download Downloaden - chat item action + alert button + chat item action Download errors @@ -2449,6 +2545,11 @@ Dit is uw eigen eenmalige link! Download bestand server test step + + Download files + ‐Bestanden downloaden + alert action + Downloaded Gedownload @@ -2724,6 +2825,11 @@ Dit is uw eigen eenmalige link! Fout bij wijzigen van adres No comment provided by engineer. + + Error changing connection profile + Fout bij wijzigen van verbindingsprofiel + No comment provided by engineer. + Error changing role Fout bij wisselen van rol @@ -2734,6 +2840,11 @@ Dit is uw eigen eenmalige link! Fout bij wijzigen van instelling No comment provided by engineer. + + Error changing to incognito! + Fout bij het overschakelen naar incognito! + No comment provided by engineer. + Error connecting to forwarding server %@. Please try later. Fout bij het verbinden met doorstuurserver %@. Probeer het later opnieuw. @@ -2854,6 +2965,11 @@ Dit is uw eigen eenmalige link! Fout bij het laden van %@ servers No comment provided by engineer. + + Error migrating settings + Fout bij migreren van instellingen + No comment provided by engineer. + Error opening chat Fout bij het openen van de chat @@ -2862,7 +2978,7 @@ Dit is uw eigen eenmalige link! Error receiving file Fout bij ontvangen van bestand - No comment provided by engineer. + alert title Error reconnecting server @@ -2954,10 +3070,15 @@ Dit is uw eigen eenmalige link! Fout bij het stoppen van de chat No comment provided by engineer. + + Error switching profile + Fout bij wisselen van profiel + No comment provided by engineer. + Error switching profile! Fout bij wisselen van profiel! - No comment provided by engineer. + alertTitle Error synchronizing connection @@ -3002,8 +3123,7 @@ Dit is uw eigen eenmalige link! Error: %@ Fout: %@ - file error text - snd error text + alert message Error: URL is invalid @@ -3090,6 +3210,13 @@ Dit is uw eigen eenmalige link! Bestandsfout No comment provided by engineer. + + File errors: +%@ + Bestandsfouten: +%@ + alert message + File not found - most likely file was deleted or cancelled. Bestand niet gevonden - hoogstwaarschijnlijk is het bestand verwijderd of geannuleerd. @@ -3182,7 +3309,7 @@ Dit is uw eigen eenmalige link! Find chats faster - Vind gesprekken sneller + Vind chats sneller No comment provided by engineer. @@ -3225,11 +3352,31 @@ Dit is uw eigen eenmalige link! Doorsturen chat item action + + Forward %d message(s)? + %d bericht(en) doorsturen? + alert title + Forward and save messages Berichten doorsturen en opslaan No comment provided by engineer. + + Forward messages + Berichten doorsturen + alert action + + + Forward messages without files? + Berichten doorsturen zonder bestanden? + alert message + + + Forward up to 20 messages at once. + Stuur maximaal 20 berichten tegelijk door. + No comment provided by engineer. + Forwarded Doorgestuurd @@ -3240,6 +3387,11 @@ Dit is uw eigen eenmalige link! Doorgestuurd vanuit No comment provided by engineer. + + Forwarding %lld messages + %lld berichten doorsturen + No comment provided by engineer. + Forwarding server %@ failed to connect to destination server %@. Please try later. De doorstuurserver %@ kon geen verbinding maken met de bestemmingsserver %@. Probeer het later opnieuw. @@ -3289,11 +3441,6 @@ Fout: %2$@ Volledige naam (optioneel) No comment provided by engineer. - - Full name: - Volledige naam: - No comment provided by engineer. - Fully decentralized – visible only to members. Volledig gedecentraliseerd – alleen zichtbaar voor leden. @@ -3466,7 +3613,7 @@ Fout: %2$@ Hidden chat profiles - Verborgen chat profielen + Verborgen chatprofielen No comment provided by engineer. @@ -3539,6 +3686,11 @@ Fout: %2$@ ICE servers (één per lijn) No comment provided by engineer. + + IP address + IP-adres + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Als je elkaar niet persoonlijk kunt ontmoeten, laat dan de QR-code zien in een videogesprek of deel de link. @@ -3614,6 +3766,13 @@ Fout: %2$@ Archief importeren No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + Verbeterde levering, minder data gebruik. +Binnenkort meer verbeteringen! + No comment provided by engineer. + Improved message delivery Verbeterde berichtbezorging @@ -3818,7 +3977,7 @@ Fout: %2$@ It allows having many anonymous connections without any shared data between them in a single chat profile. - Het maakt het mogelijk om veel anonieme verbindingen te hebben zonder enige gedeelde gegevens tussen hen in een enkel chat profiel. + Het maakt het mogelijk om veel anonieme verbindingen te hebben zonder enige gedeelde gegevens tussen hen in een enkel chatprofiel. No comment provided by engineer. @@ -4184,6 +4343,11 @@ Dit is jouw link voor groep %@! Berichtservers No comment provided by engineer. + + Message shape + Berichtvorm + No comment provided by engineer. + Message source remains private. Berichtbron blijft privé. @@ -4234,6 +4398,11 @@ Dit is jouw link voor groep %@! Berichten verzonden No comment provided by engineer. + + Messages were deleted after you selected them. + Berichten zijn verwijderd nadat u ze had geselecteerd. + alert message + 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. @@ -4336,7 +4505,7 @@ Dit is jouw link voor groep %@! Multiple chat profiles - Meerdere chat profielen + Meerdere chatprofielen No comment provided by engineer. @@ -4389,6 +4558,16 @@ Dit is jouw link voor groep %@! Nieuwe toegangscode No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + Elke keer dat u de app start, worden er nieuwe SOCKS-inloggegevens gebruikt. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + Voor elke server worden nieuwe SOCKS-inloggegevens gebruikt. + No comment provided by engineer. + New chat Nieuw gesprek @@ -4486,7 +4665,7 @@ Dit is jouw link voor groep %@! No filtered chats - Geen gefilterde gesprekken + Geen gefilterde chats No comment provided by engineer. @@ -4509,6 +4688,16 @@ Dit is jouw link voor groep %@! Geen netwerkverbinding No comment provided by engineer. + + No permission to record speech + Geen toestemming om spraak op te nemen + No comment provided by engineer. + + + No permission to record video + Geen toestemming om video op te nemen + No comment provided by engineer. + No permission to record voice message Geen toestemming om spraakbericht op te nemen @@ -4529,6 +4718,11 @@ Dit is jouw link voor groep %@! Niets geselecteerd No comment provided by engineer. + + Nothing to forward! + Niets om door te sturen! + alert title + Notifications Meldingen @@ -4561,7 +4755,7 @@ Dit is jouw link voor groep %@! Ok OK - No comment provided by engineer. + alert button Old database @@ -4752,6 +4946,13 @@ Vereist het inschakelen van VPN. Andere %@ servers No comment provided by engineer. + + Other file errors: +%@ + Andere bestandsfouten: +%@ + alert message + PING count PING count @@ -4787,6 +4988,11 @@ Vereist het inschakelen van VPN. Toegangscode ingesteld! No comment provided by engineer. + + Password + Wachtwoord + No comment provided by engineer. + Password to show Wachtwoord om weer te geven @@ -4844,7 +5050,7 @@ Vereist het inschakelen van VPN. Play from the chat list. - Afspelen via de gesprekken lijst. + Afspelen via de chat lijst. No comment provided by engineer. @@ -4923,7 +5129,7 @@ Fout: %@ Please store passphrase securely, you will NOT be able to access chat if you lose it. - Sla het wachtwoord veilig op. Als u deze kwijtraakt, heeft u GEEN toegang tot de gesprekken. + Sla het wachtwoord veilig op. Als u deze kwijtraakt, heeft u GEEN toegang tot de chats. No comment provided by engineer. @@ -4936,6 +5142,11 @@ Fout: %@ Poolse interface No comment provided by engineer. + + Port + Poort + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Mogelijk is de certificaat vingerafdruk in het server adres onjuist @@ -5021,16 +5232,6 @@ Fout: %@ Profiel afbeeldingen No comment provided by engineer. - - Profile name - Profielnaam - No comment provided by engineer. - - - Profile name: - Profielnaam: - No comment provided by engineer. - Profile password Profiel wachtwoord @@ -5110,7 +5311,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Protect your chat profiles with a password! - Bescherm je chat profielen met een wachtwoord! + Bescherm je chatprofielen met een wachtwoord! No comment provided by engineer. @@ -5133,6 +5334,11 @@ Schakel dit in in *Netwerk en servers*-instellingen. Proxied servers No comment provided by engineer. + + Proxy requires password + Proxy vereist wachtwoord + No comment provided by engineer. + Push notifications Push meldingen @@ -5354,6 +5560,11 @@ Schakel dit in in *Netwerk en servers*-instellingen. Verwijderen No comment provided by engineer. + + Remove archive? + Archief verwijderen? + No comment provided by engineer. + Remove image Verwijder afbeelding @@ -5466,7 +5677,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Restart the app to create a new chat profile - Start de app opnieuw om een nieuw chat profiel aan te maken + Start de app opnieuw om een nieuw chatprofiel aan te maken No comment provided by engineer. @@ -5534,6 +5745,11 @@ Schakel dit in in *Netwerk en servers*-instellingen. SMP server No comment provided by engineer. + + SOCKS proxy + SOCKS proxy + No comment provided by engineer. + Safely receive files Veilig bestanden ontvangen @@ -5547,12 +5763,13 @@ Schakel dit in in *Netwerk en servers*-instellingen. Save Opslaan - chat item action + alert button + chat item action Save (and notify contacts) Bewaar (en informeer contacten) - No comment provided by engineer. + alert button Save and notify contact @@ -5579,11 +5796,6 @@ Schakel dit in in *Netwerk en servers*-instellingen. Bewaar archief No comment provided by engineer. - - Save auto-accept settings - Sla instellingen voor automatisch accepteren op - No comment provided by engineer. - Save group profile Groep profiel opslaan @@ -5591,7 +5803,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Save passphrase and open chat - Bewaar het wachtwoord en open je gesprekken + Bewaar het wachtwoord en open je chats No comment provided by engineer. @@ -5611,7 +5823,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Save servers - Bewaar servers + Servers opslaan No comment provided by engineer. @@ -5619,16 +5831,16 @@ Schakel dit in in *Netwerk en servers*-instellingen. Servers opslaan? No comment provided by engineer. - - Save settings? - Instellingen opslaan? - No comment provided by engineer. - Save welcome message? Welkom bericht opslaan? No comment provided by engineer. + + Save your profile? + Uw profiel opslaan? + alert title + Saved Opgeslagen @@ -5649,6 +5861,11 @@ Schakel dit in in *Netwerk en servers*-instellingen. Opgeslagen bericht message info title + + Saving %lld messages + %lld berichten opslaan + No comment provided by engineer. + Scale Schaal @@ -5729,6 +5946,11 @@ Schakel dit in in *Netwerk en servers*-instellingen. Selecteer chat item action + + Select chat profile + Selecteer chatprofiel + No comment provided by engineer. + Selected %lld %lld geselecteerd @@ -5847,7 +6069,7 @@ Schakel dit in in *Netwerk en servers*-instellingen. Sender cancelled file transfer. Afzender heeft bestandsoverdracht geannuleerd. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -5944,6 +6166,11 @@ Schakel dit in in *Netwerk en servers*-instellingen. Verzonden via proxy No comment provided by engineer. + + Server + Server + No comment provided by engineer. + Server address Server adres @@ -6064,6 +6291,11 @@ Schakel dit in in *Netwerk en servers*-instellingen. Instellingen No comment provided by engineer. + + Settings were changed. + Instellingen zijn gewijzigd. + alert message + Shape profile images Vorm profiel afbeeldingen @@ -6099,6 +6331,11 @@ Schakel dit in in *Netwerk en servers*-instellingen. Deel link No comment provided by engineer. + + Share profile + Profiel delen + No comment provided by engineer. + Share this 1-time invite link Deel deze eenmalige uitnodigingslink @@ -6234,6 +6471,11 @@ Schakel dit in in *Netwerk en servers*-instellingen. Eenmalige SimpleX uitnodiging simplex link type + + SimpleX protocols reviewed by Trail of Bits. + SimpleX-protocollen beoordeeld door Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Vereenvoudigde incognitomodus @@ -6264,6 +6506,11 @@ Schakel dit in in *Netwerk en servers*-instellingen. Soft blur media + + Some app settings were not migrated. + Sommige app-instellingen zijn niet gemigreerd. + No comment provided by engineer. + Some file(s) were not exported: Sommige bestanden zijn niet geëxporteerd: @@ -6386,17 +6633,17 @@ Schakel dit in in *Netwerk en servers*-instellingen. Subscribed - Ingeschreven + Subscribed No comment provided by engineer. Subscription errors - Inschrijving fouten + Subscription fouten No comment provided by engineer. Subscriptions ignored - Inschrijvingen genegeerd + Subscriptions genegeerd No comment provided by engineer. @@ -6404,6 +6651,16 @@ Schakel dit in in *Netwerk en servers*-instellingen. Ondersteuning van SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + Wisselen tussen audio en video tijdens het gesprek. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + Wijzig chatprofiel voor eenmalige uitnodigingen. + No comment provided by engineer. + System Systeem @@ -6439,6 +6696,11 @@ Schakel dit in in *Netwerk en servers*-instellingen. TCP_KEEPINTVL No comment provided by engineer. + + Tail + Staart + No comment provided by engineer. + Take picture Foto nemen @@ -6623,7 +6885,7 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast. The servers for new connections of your current chat profile **%@**. - De servers voor nieuwe verbindingen van uw huidige chat profiel **%@**. + De servers voor nieuwe verbindingen van uw huidige chatprofiel **%@**. No comment provided by engineer. @@ -6631,6 +6893,11 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast. De tekst die u hebt geplakt is geen SimpleX link. No comment provided by engineer. + + The uploaded database archive will be permanently removed from the servers. + Het geüploade databasearchief wordt permanent van de servers verwijderd. + No comment provided by engineer. + Themes Thema's @@ -6708,7 +6975,7 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast. This setting applies to messages in your current chat profile **%@**. - Deze instelling is van toepassing op berichten in je huidige chat profiel **%@**. + Deze instelling is van toepassing op berichten in je huidige chatprofiel **%@**. No comment provided by engineer. @@ -6758,6 +7025,16 @@ You will be prompted to complete authentication before this feature is enabled.< U wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingeschakeld. No comment provided by engineer. + + To record speech please grant permission to use Microphone. + Geef toestemming om de microfoon te gebruiken om spraak op te nemen. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + Om video op te nemen, dient u toestemming te geven om de camera te gebruiken. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Geef toestemming om de microfoon te gebruiken om een spraakbericht op te nemen. @@ -6765,7 +7042,7 @@ U wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingesc To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page. - Om uw verborgen profiel te onthullen, voert u een volledig wachtwoord in een zoek veld in op de pagina **Uw chat profielen**. + Om uw verborgen profiel te onthullen, voert u een volledig wachtwoord in een zoek veld in op de pagina **Uw chatprofielen**. No comment provided by engineer. @@ -6880,7 +7157,7 @@ U wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingesc Unhide chat profile - Chat profiel zichtbaar maken + Chatprofiel zichtbaar maken No comment provided by engineer. @@ -6911,7 +7188,7 @@ U wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingesc Unknown servers! Onbekende servers! - No comment provided by engineer. + alert title Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. @@ -7025,6 +7302,11 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Gebruik .onion-hosts No comment provided by engineer. + + Use SOCKS proxy + Gebruik SOCKS proxy + No comment provided by engineer. + Use SimpleX Chat servers? SimpleX Chat servers gebruiken? @@ -7090,16 +7372,16 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Gebruik de app met één hand. No comment provided by engineer. - - User profile - Gebruikers profiel - No comment provided by engineer. - User selection Gebruikersselectie No comment provided by engineer. + + Username + Gebruikersnaam + No comment provided by engineer. + Using SimpleX Chat servers. SimpleX Chat servers gebruiken. @@ -7333,7 +7615,7 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. Zonder Tor of VPN zal uw IP-adres zichtbaar zijn voor deze XFTP-relays: %@. - No comment provided by engineer. + alert message Wrong database passphrase @@ -7360,11 +7642,6 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak XFTP server No comment provided by engineer. - - You - Jij - No comment provided by engineer. - You **must not** use the same database on two devices. U **mag** niet dezelfde database op twee apparaten gebruiken. @@ -7382,7 +7659,7 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak You already have a chat profile with the same display name. Please choose another name. - Je hebt al een chat profiel met dezelfde weergave naam. Kies een andere naam. + Je hebt al een chatprofiel met dezelfde weergave naam. Kies een andere naam. No comment provided by engineer. @@ -7714,11 +7991,21 @@ Verbindingsverzoek herhalen? Uw chat database is niet versleuteld, stel een wachtwoord in om deze te versleutelen. No comment provided by engineer. + + Your chat preferences + Uw chat voorkeuren + alert title + Your chat profiles Uw chat profielen No comment provided by engineer. + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + Uw verbinding is verplaatst naar %@, maar er is een onverwachte fout opgetreden tijdens het omleiden naar het profiel. + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Uw contact heeft een bestand verzonden dat groter is dan de momenteel ondersteunde maximale grootte (%@). @@ -7734,6 +8021,11 @@ Verbindingsverzoek herhalen? Uw contacten blijven verbonden. No comment provided by engineer. + + Your credentials may be sent unencrypted. + Uw inloggegevens worden mogelijk niet-versleuteld verzonden. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Uw huidige chat database wordt VERWIJDERD en VERVANGEN door de geïmporteerde. @@ -7764,13 +8056,16 @@ Verbindingsverzoek herhalen? Uw profiel **%@** wordt gedeeld. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Uw profiel wordt op uw apparaat opgeslagen en alleen gedeeld met uw contacten. -SimpleX servers kunnen uw profiel niet zien. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Uw profiel wordt op uw apparaat opgeslagen en alleen gedeeld met uw contacten. SimpleX servers kunnen uw profiel niet zien. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + Je profiel is gewijzigd. Als je het opslaat, wordt het bijgewerkte profiel naar al je contacten verzonden. + alert message + Your profile, contacts and delivered messages are stored on your device. Uw profiel, contacten en afgeleverde berichten worden op uw apparaat opgeslagen. @@ -8709,7 +9004,7 @@ laatst ontvangen bericht: %2$@ you are observer - jij bent waarnemer + je bent waarnemer No comment provided by engineer. @@ -8739,7 +9034,7 @@ laatst ontvangen bericht: %2$@ you left - jij bent vertrokken + je bent vertrokken snd group event chat item @@ -8917,7 +9212,7 @@ laatst ontvangen bericht: %2$@ Database passphrase is required to open chat. - Database wachtwoord is vereist om je gesprekken te openen. + Database wachtwoord is vereist om je chats te openen. No comment provided by engineer. 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 525d30daa6..ee3ef5b12e 100644 --- a/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff +++ b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff @@ -137,6 +137,11 @@ %@ chce się połączyć! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ i %lld członków @@ -157,11 +162,36 @@ %d dni time interval + + %d file(s) are still being downloaded. + %d plik(ów) jest dalej pobieranych. + forward confirmation reason + + + %d file(s) failed to download. + %d plik(ów) nie udało się pobrać. + forward confirmation reason + + + %d file(s) were deleted. + %d plik(ów) zostało usuniętych. + forward confirmation reason + + + %d file(s) were not downloaded. + %d plik(ów) nie zostało pobranych. + forward confirmation reason + %d hours %d godzin time interval + + %d messages not forwarded + %d wiadomości nie przekazanych + alert title + %d min %d min @@ -723,7 +753,7 @@ All profiles Wszystkie profile - No comment provided by engineer. + profile dropdown All your contacts will remain connected. @@ -915,6 +945,11 @@ Pin aplikacji został zastąpiony pinem samozniszczenia. No comment provided by engineer. + + App session + Sesja aplikacji + No comment provided by engineer. + App version Wersja aplikacji @@ -1020,6 +1055,11 @@ Automatyczne akceptowanie obrazów No comment provided by engineer. + + Auto-accept settings + Ustawienia automatycznej akceptacji + alert title + Back Wstecz @@ -1045,11 +1085,19 @@ Zły hash wiadomości No comment provided by engineer. + + Better calls + No comment provided by engineer. + Better groups Lepsze grupy No comment provided by engineer. + + Better message dates. + No comment provided by engineer. + Better messages Lepsze wiadomości @@ -1060,6 +1108,18 @@ Lepsze sieciowanie No comment provided by engineer. + + Better notifications + No comment provided by engineer. + + + Better security ✅ + No comment provided by engineer. + + + Better user experience + No comment provided by engineer. + Black Czarny @@ -1193,7 +1253,7 @@ Cancel Anuluj - No comment provided by engineer. + alert button Cancel migration @@ -1213,7 +1273,7 @@ Cannot receive file Nie można odebrać pliku - No comment provided by engineer. + alert title Capacity exceeded - recipient did not receive previously sent messages. @@ -1341,6 +1401,16 @@ Preferencje czatu No comment provided by engineer. + + Chat preferences were changed. + Preferencje czatu zostały zmienione. + alert message + + + Chat profile + Profil użytkownika + No comment provided by engineer. + Chat theme Motyw czatu @@ -1740,6 +1810,11 @@ To jest twój jednorazowy link! Wersja rdzenia: v%@ No comment provided by engineer. + + Corner + Róg + No comment provided by engineer. + Correct name to %@? Poprawić imię na %@? @@ -1865,6 +1940,10 @@ To jest twój jednorazowy link! Niestandardowy czas No comment provided by engineer. + + Customizable message shape. + No comment provided by engineer. + Customize theme Dostosuj motyw @@ -2159,6 +2238,10 @@ To jest twój jednorazowy link! Usunąć starą bazę danych? No comment provided by engineer. + + Delete or moderate up to 200 messages. + No comment provided by engineer. + Delete pending connection? Usunąć oczekujące połączenie? @@ -2409,6 +2492,11 @@ To jest twój jednorazowy link! Nie wysyłaj historii do nowych członków. No comment provided by engineer. + + Do not use credentials with proxy. + Nie używaj danych logowania do proxy. + No comment provided by engineer. + Don't create address Nie twórz adresu @@ -2432,7 +2520,8 @@ To jest twój jednorazowy link! Download Pobierz - chat item action + alert button + chat item action Download errors @@ -2449,6 +2538,11 @@ To jest twój jednorazowy link! Pobierz plik server test step + + Download files + Pobierz pliki + alert action + Downloaded Pobrane @@ -2724,6 +2818,11 @@ To jest twój jednorazowy link! Błąd zmiany adresu No comment provided by engineer. + + Error changing connection profile + Błąd zmiany połączenia profilu + No comment provided by engineer. + Error changing role Błąd zmiany roli @@ -2734,6 +2833,11 @@ To jest twój jednorazowy link! Błąd zmiany ustawienia No comment provided by engineer. + + Error changing to incognito! + Błąd zmiany na incognito! + No comment provided by engineer. + Error connecting to forwarding server %@. Please try later. Błąd połączenia z serwerem przekierowania %@. Spróbuj ponownie później. @@ -2854,6 +2958,11 @@ To jest twój jednorazowy link! Błąd ładowania %@ serwerów No comment provided by engineer. + + Error migrating settings + Błąd migracji ustawień + No comment provided by engineer. + Error opening chat Błąd otwierania czatu @@ -2862,7 +2971,7 @@ To jest twój jednorazowy link! Error receiving file Błąd odbioru pliku - No comment provided by engineer. + alert title Error reconnecting server @@ -2954,10 +3063,15 @@ To jest twój jednorazowy link! Błąd zatrzymania czatu No comment provided by engineer. + + Error switching profile + Błąd zmiany profilu + No comment provided by engineer. + Error switching profile! Błąd przełączania profilu! - No comment provided by engineer. + alertTitle Error synchronizing connection @@ -3002,8 +3116,7 @@ To jest twój jednorazowy link! Error: %@ Błąd: %@ - file error text - snd error text + alert message Error: URL is invalid @@ -3090,6 +3203,13 @@ To jest twój jednorazowy link! Błąd pliku No comment provided by engineer. + + File errors: +%@ + Błędy pliku: +%@ + alert message + File not found - most likely file was deleted or cancelled. Nie odnaleziono pliku - najprawdopodobniej plik został usunięty lub anulowany. @@ -3225,11 +3345,30 @@ To jest twój jednorazowy link! Przekaż dalej chat item action + + Forward %d message(s)? + Przekazać %d wiadomość(i)? + alert title + Forward and save messages Przesyłaj dalej i zapisuj wiadomości No comment provided by engineer. + + Forward messages + Przekaż wiadomości + alert action + + + Forward messages without files? + Przekazać wiadomości bez plików? + alert message + + + Forward up to 20 messages at once. + No comment provided by engineer. + Forwarded Przekazane dalej @@ -3240,6 +3379,11 @@ To jest twój jednorazowy link! Przekazane dalej od No comment provided by engineer. + + Forwarding %lld messages + Przekazywanie %lld wiadomości + No comment provided by engineer. + Forwarding server %@ failed to connect to destination server %@. Please try later. Serwer przekazujący %@ nie mógł połączyć się z serwerem docelowym %@. Spróbuj ponownie później. @@ -3289,11 +3433,6 @@ Błąd: %2$@ Pełna nazwa (opcjonalna) No comment provided by engineer. - - Full name: - Pełna nazwa: - No comment provided by engineer. - Fully decentralized – visible only to members. W pełni zdecentralizowana – widoczna tylko dla członków. @@ -3539,6 +3678,11 @@ Błąd: %2$@ Serwery ICE (po jednym na linię) No comment provided by engineer. + + IP address + Adres IP + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Jeśli nie możesz spotkać się osobiście, pokaż kod QR w rozmowie wideo lub udostępnij link. @@ -3614,6 +3758,11 @@ Błąd: %2$@ Importowanie archiwum No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + No comment provided by engineer. + Improved message delivery Ulepszona dostawa wiadomości @@ -4184,6 +4333,11 @@ To jest twój link do grupy %@! Serwery wiadomości No comment provided by engineer. + + Message shape + Kształt wiadomości + No comment provided by engineer. + Message source remains private. Źródło wiadomości pozostaje prywatne. @@ -4234,6 +4388,11 @@ To jest twój link do grupy %@! Wysłane wiadomości No comment provided by engineer. + + Messages were deleted after you selected them. + Wiadomości zostały usunięte po wybraniu ich. + alert message + 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. @@ -4389,6 +4548,16 @@ To jest twój link do grupy %@! Nowy Pin No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + Nowe poświadczenia SOCKS będą używane przy każdym uruchomieniu aplikacji. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + Dla każdego serwera zostaną użyte nowe poświadczenia SOCKS. + No comment provided by engineer. + New chat Nowy czat @@ -4509,6 +4678,16 @@ To jest twój link do grupy %@! Brak połączenia z siecią No comment provided by engineer. + + No permission to record speech + Brak zezwoleń do nagrania rozmowy + No comment provided by engineer. + + + No permission to record video + Brak zezwoleń do nagrania wideo + No comment provided by engineer. + No permission to record voice message Brak uprawnień do nagrywania wiadomości głosowej @@ -4529,6 +4708,11 @@ To jest twój link do grupy %@! Nic nie jest zaznaczone No comment provided by engineer. + + Nothing to forward! + Nic do przekazania! + alert title + Notifications Powiadomienia @@ -4561,7 +4745,7 @@ To jest twój link do grupy %@! Ok Ok - No comment provided by engineer. + alert button Old database @@ -4752,6 +4936,13 @@ Wymaga włączenia VPN. Inne %@ serwery No comment provided by engineer. + + Other file errors: +%@ + Inne błędy pliku: +%@ + alert message + PING count Liczba PINGÓW @@ -4787,6 +4978,11 @@ Wymaga włączenia VPN. Pin ustawiony! No comment provided by engineer. + + Password + Hasło + No comment provided by engineer. + Password to show Hasło do wyświetlenia @@ -4936,6 +5132,11 @@ Błąd: %@ Polski interfejs No comment provided by engineer. + + Port + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Możliwe, że odcisk palca certyfikatu w adresie serwera jest nieprawidłowy @@ -5021,16 +5222,6 @@ Błąd: %@ Zdjęcia profilowe No comment provided by engineer. - - Profile name - Nazwa profilu - No comment provided by engineer. - - - Profile name: - Nazwa profilu: - No comment provided by engineer. - Profile password Hasło profilu @@ -5133,6 +5324,11 @@ Włącz w ustawianiach *Sieć i serwery* . Serwery trasowane przez proxy No comment provided by engineer. + + Proxy requires password + Proxy wymaga hasła + No comment provided by engineer. + Push notifications Powiadomienia push @@ -5354,6 +5550,11 @@ Włącz w ustawianiach *Sieć i serwery* . Usuń No comment provided by engineer. + + Remove archive? + Usunąć archiwum? + No comment provided by engineer. + Remove image Usuń obraz @@ -5534,6 +5735,11 @@ Włącz w ustawianiach *Sieć i serwery* . Serwer SMP No comment provided by engineer. + + SOCKS proxy + Proxy SOCKS + No comment provided by engineer. + Safely receive files Bezpiecznie otrzymuj pliki @@ -5547,12 +5753,13 @@ Włącz w ustawianiach *Sieć i serwery* . Save Zapisz - chat item action + alert button + chat item action Save (and notify contacts) Zapisz (i powiadom kontakty) - No comment provided by engineer. + alert button Save and notify contact @@ -5579,11 +5786,6 @@ Włącz w ustawianiach *Sieć i serwery* . Zapisz archiwum No comment provided by engineer. - - Save auto-accept settings - Zapisz ustawienia automatycznej akceptacji - No comment provided by engineer. - Save group profile Zapisz profil grupy @@ -5619,16 +5821,16 @@ Włącz w ustawianiach *Sieć i serwery* . Zapisać serwery? No comment provided by engineer. - - Save settings? - Zapisać ustawienia? - No comment provided by engineer. - Save welcome message? Zapisać wiadomość powitalną? No comment provided by engineer. + + Save your profile? + Zapisać Twój profil? + alert title + Saved Zapisane @@ -5649,6 +5851,11 @@ Włącz w ustawianiach *Sieć i serwery* . Zachowano wiadomość message info title + + Saving %lld messages + Zapisywanie %lld wiadomości + No comment provided by engineer. + Scale Skaluj @@ -5729,6 +5936,11 @@ Włącz w ustawianiach *Sieć i serwery* . Wybierz chat item action + + Select chat profile + Wybierz profil czatu + No comment provided by engineer. + Selected %lld Zaznaczono %lld @@ -5847,7 +6059,7 @@ Włącz w ustawianiach *Sieć i serwery* . Sender cancelled file transfer. Nadawca anulował transfer pliku. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -5944,6 +6156,11 @@ Włącz w ustawianiach *Sieć i serwery* . Wysłano przez proxy No comment provided by engineer. + + Server + Serwer + No comment provided by engineer. + Server address Adres serwera @@ -6064,6 +6281,11 @@ Włącz w ustawianiach *Sieć i serwery* . Ustawienia No comment provided by engineer. + + Settings were changed. + Ustawienia zostały zmienione. + alert message + Shape profile images Kształtuj obrazy profilowe @@ -6099,6 +6321,11 @@ Włącz w ustawianiach *Sieć i serwery* . Udostępnij link No comment provided by engineer. + + Share profile + Udostępnij profil + No comment provided by engineer. + Share this 1-time invite link Udostępnij ten jednorazowy link @@ -6234,6 +6461,10 @@ Włącz w ustawianiach *Sieć i serwery* . Zaproszenie jednorazowe SimpleX simplex link type + + SimpleX protocols reviewed by Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Uproszczony tryb incognito @@ -6264,6 +6495,11 @@ Włącz w ustawianiach *Sieć i serwery* . Łagodny blur media + + Some app settings were not migrated. + Niektóre ustawienia aplikacji nie zostały zmigrowane. + No comment provided by engineer. + Some file(s) were not exported: Niektóre plik(i) nie zostały wyeksportowane: @@ -6404,6 +6640,14 @@ Włącz w ustawianiach *Sieć i serwery* . Wspieraj SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + No comment provided by engineer. + System System @@ -6439,6 +6683,11 @@ Włącz w ustawianiach *Sieć i serwery* . TCP_KEEPINTVL No comment provided by engineer. + + Tail + Ogon + No comment provided by engineer. + Take picture Zrób zdjęcie @@ -6631,6 +6880,11 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom Tekst, który wkleiłeś nie jest linkiem SimpleX. No comment provided by engineer. + + The uploaded database archive will be permanently removed from the servers. + Przesłane archiwum bazy danych zostanie trwale usunięte z serwerów. + No comment provided by engineer. + Themes Motywy @@ -6758,6 +7012,16 @@ You will be prompted to complete authentication before this feature is enabled.< Przed włączeniem tej funkcji zostanie wyświetlony monit uwierzytelniania. No comment provided by engineer. + + To record speech please grant permission to use Microphone. + Aby nagrać rozmowę, proszę zezwolić na użycie Mikrofonu. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + Aby nagrać wideo, proszę zezwolić na użycie Aparatu. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Aby nagrać wiadomość głosową należy udzielić zgody na użycie Mikrofonu. @@ -6911,7 +7175,7 @@ Przed włączeniem tej funkcji zostanie wyświetlony monit uwierzytelniania. Unknown servers! Nieznane serwery! - No comment provided by engineer. + alert title Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. @@ -7025,6 +7289,11 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Użyj hostów .onion No comment provided by engineer. + + Use SOCKS proxy + Użyj proxy SOCKS + No comment provided by engineer. + Use SimpleX Chat servers? Użyć serwerów SimpleX Chat? @@ -7090,16 +7359,16 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Korzystaj z aplikacji jedną ręką. 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. + + Username + Nazwa użytkownika + No comment provided by engineer. + Using SimpleX Chat servers. Używanie serwerów SimpleX Chat. @@ -7333,7 +7602,7 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. Bez Tor lub VPN, Twój adres IP będzie widoczny dla tych przekaźników XFTP: %@. - No comment provided by engineer. + alert message Wrong database passphrase @@ -7360,11 +7629,6 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Serwer XFTP No comment provided by engineer. - - You - Ty - No comment provided by engineer. - You **must not** use the same database on two devices. **Nie możesz** używać tej samej bazy na dwóch urządzeniach. @@ -7714,11 +7978,21 @@ Powtórzyć prośbę połączenia? Baza danych czatu nie jest szyfrowana - ustaw hasło, aby ją zaszyfrować. No comment provided by engineer. + + Your chat preferences + Twoje preferencje czatu + alert title + Your chat profiles Twoje profile czatu No comment provided by engineer. + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + Twoje połączenie zostało przeniesione do %@, ale podczas przekierowywania do profilu wystąpił nieoczekiwany błąd. + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Twój kontakt wysłał plik, który jest większy niż obecnie obsługiwany maksymalny rozmiar (%@). @@ -7734,6 +8008,11 @@ Powtórzyć prośbę połączenia? Twoje kontakty pozostaną połączone. No comment provided by engineer. + + Your credentials may be sent unencrypted. + Twoje poświadczenia mogą zostać wysłane niezaszyfrowane. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Twoja obecna baza danych czatu zostanie usunięta i zastąpiona zaimportowaną. @@ -7764,13 +8043,16 @@ Powtórzyć prośbę połączenia? Twój profil **%@** zostanie udostępniony. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Twój profil jest przechowywany na urządzeniu i udostępniany tylko Twoim kontaktom. -Serwery SimpleX nie mogą zobaczyć Twojego profilu. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Twój profil jest przechowywany na urządzeniu i udostępniany tylko Twoim kontaktom. Serwery SimpleX nie mogą zobaczyć Twojego profilu. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + Twój profil został zmieniony. Jeśli go zapiszesz, zaktualizowany profil zostanie wysłany do wszystkich kontaktów. + alert message + Your profile, contacts and delivered messages are stored on your device. Twój profil, kontakty i dostarczone wiadomości są przechowywane na Twoim urządzeniu. diff --git a/apps/ios/SimpleX Localizations/pt-BR.xcloc/Localized Contents/pt-BR.xliff b/apps/ios/SimpleX Localizations/pt-BR.xcloc/Localized Contents/pt-BR.xliff index 5f6cbc3b8f..c63fec4a08 100644 --- a/apps/ios/SimpleX Localizations/pt-BR.xcloc/Localized Contents/pt-BR.xliff +++ b/apps/ios/SimpleX Localizations/pt-BR.xcloc/Localized Contents/pt-BR.xliff @@ -5502,6 +5502,74 @@ Isso pode acontecer por causa de algum bug ou quando a conexão está comprometi Archived contacts Contatos arquivados + + Cellular + Rede móvel + + + %d file(s) failed to download. + %d arquivo(s) falharam ao ser baixados. + + + %d file(s) were deleted. + %d arquivo(s) foram apagados. + + + %d messages not forwarded + %d mensagens não encaminhadas + + + Bad desktop address + Endereço de desktop incorreto + + + Blur for better privacy. + Borrar para melhor privacidade. + + + %d file(s) are still being downloaded. + %d arquivo(s) ainda estão sendo baixados. + + + %d file(s) were not downloaded. + %d arquivo(s) não foram baixados. + + + Chat colors + Cores do chat + + + %lld group events + %lld eventos do grupo + + + %lld messages moderated by %@ + %lld mensagens moderadas por %@ + + + %1$@, %2$@ + %1$@, %2$@ + + + %lld new interface languages + %lld novos idiomas de interface + + + Better networking + Melhores redes + + + Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! + Búlgaro, Finlandês, Tailandês e Ucraniano - obrigado aos usuários e [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)! + + + Better groups + Melhores grupos + + + Capacity exceeded - recipient did not receive previously sent messages. + Capacidade excedida - o destinatário não recebeu as mensagens enviadas anteriormente. + 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 969a7d68e0..ced93b4c12 100644 --- a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff @@ -137,6 +137,11 @@ %@ хочет соединиться! notification title + + %1$@, %2$@ + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ и %lld членов группы @@ -157,11 +162,36 @@ %d дней time interval + + %d file(s) are still being downloaded. + %d файл(ов) загружаются. + forward confirmation reason + + + %d file(s) failed to download. + %d файл(ов) не удалось загрузить. + forward confirmation reason + + + %d file(s) were deleted. + %d файлов было удалено. + forward confirmation reason + + + %d file(s) were not downloaded. + %d файлов не было загружено. + forward confirmation reason + %d hours %d ч. time interval + + %d messages not forwarded + %d сообщений не переслано + alert title + %d min %d мин @@ -723,7 +753,7 @@ All profiles Все профили - No comment provided by engineer. + profile dropdown All your contacts will remain connected. @@ -915,6 +945,11 @@ Код доступа в приложение будет заменен кодом самоуничтожения. No comment provided by engineer. + + App session + Сессия приложения + No comment provided by engineer. + App version Версия приложения @@ -1020,6 +1055,11 @@ Автоприем изображений No comment provided by engineer. + + Auto-accept settings + Настройки автоприема + alert title + Back Назад @@ -1045,11 +1085,21 @@ Ошибка хэш сообщения No comment provided by engineer. + + Better calls + Улучшенные звонки + No comment provided by engineer. + Better groups Улучшенные группы No comment provided by engineer. + + Better message dates. + Улучшенные даты сообщений. + No comment provided by engineer. + Better messages Улучшенные сообщения @@ -1060,6 +1110,21 @@ Улучшенные сетевые функции No comment provided by engineer. + + Better notifications + Улучшенные уведомления + No comment provided by engineer. + + + Better security ✅ + Улучшенная безопасность ✅ + No comment provided by engineer. + + + Better user experience + Улучшенный интерфейс + No comment provided by engineer. + Black Черная @@ -1193,7 +1258,7 @@ Cancel Отменить - No comment provided by engineer. + alert button Cancel migration @@ -1213,7 +1278,7 @@ Cannot receive file Невозможно получить файл - No comment provided by engineer. + alert title Capacity exceeded - recipient did not receive previously sent messages. @@ -1341,6 +1406,16 @@ Предпочтения No comment provided by engineer. + + Chat preferences were changed. + Настройки чата были изменены. + alert message + + + Chat profile + Профиль чата + No comment provided by engineer. + Chat theme Тема чата @@ -1740,6 +1815,11 @@ This is your own one-time link! Версия ядра: v%@ No comment provided by engineer. + + Corner + Угол + No comment provided by engineer. + Correct name to %@? Исправить имя на %@? @@ -1865,6 +1945,11 @@ This is your own one-time link! Пользовательское время No comment provided by engineer. + + Customizable message shape. + Настраиваемая форма сообщений. + No comment provided by engineer. + Customize theme Настроить тему @@ -2159,6 +2244,11 @@ This is your own one-time link! Удалить предыдущую версию данных? No comment provided by engineer. + + Delete or moderate up to 200 messages. + Удаляйте или модерируйте до 200 сообщений. + No comment provided by engineer. + Delete pending connection? Удалить ожидаемое соединение? @@ -2409,6 +2499,11 @@ This is your own one-time link! Не отправлять историю новым членам. No comment provided by engineer. + + Do not use credentials with proxy. + Не использовать учетные данные с прокси. + No comment provided by engineer. + Don't create address Не создавать адрес @@ -2432,7 +2527,8 @@ This is your own one-time link! Download Загрузить - chat item action + alert button + chat item action Download errors @@ -2449,6 +2545,11 @@ This is your own one-time link! Загрузка файла server test step + + Download files + Загрузить файлы + alert action + Downloaded Принято @@ -2724,6 +2825,11 @@ This is your own one-time link! Ошибка при изменении адреса No comment provided by engineer. + + Error changing connection profile + Ошибка при изменении профиля соединения + No comment provided by engineer. + Error changing role Ошибка при изменении роли @@ -2734,6 +2840,11 @@ This is your own one-time link! Ошибка при изменении настройки No comment provided by engineer. + + Error changing to incognito! + Ошибка при смене на Инкогнито! + No comment provided by engineer. + Error connecting to forwarding server %@. Please try later. Ошибка подключения к пересылающему серверу %@. Попробуйте позже. @@ -2854,6 +2965,11 @@ This is your own one-time link! Ошибка загрузки %@ серверов No comment provided by engineer. + + Error migrating settings + Ошибка миграции настроек + No comment provided by engineer. + Error opening chat Ошибка доступа к чату @@ -2862,7 +2978,7 @@ This is your own one-time link! Error receiving file Ошибка при получении файла - No comment provided by engineer. + alert title Error reconnecting server @@ -2954,10 +3070,15 @@ This is your own one-time link! Ошибка при остановке чата No comment provided by engineer. + + Error switching profile + Ошибка переключения профиля + No comment provided by engineer. + Error switching profile! Ошибка выбора профиля! - No comment provided by engineer. + alertTitle Error synchronizing connection @@ -3002,8 +3123,7 @@ This is your own one-time link! Error: %@ Ошибка: %@ - file error text - snd error text + alert message Error: URL is invalid @@ -3090,6 +3210,13 @@ This is your own one-time link! Ошибка файла No comment provided by engineer. + + File errors: +%@ + Ошибки файлов: +%@ + alert message + File not found - most likely file was deleted or cancelled. Файл не найден - скорее всего, файл был удален или отменен. @@ -3225,11 +3352,31 @@ This is your own one-time link! Переслать chat item action + + Forward %d message(s)? + Переслать %d сообщение(й)? + alert title + Forward and save messages Переслать и сохранить сообщение No comment provided by engineer. + + Forward messages + Переслать сообщения + alert action + + + Forward messages without files? + Переслать сообщения без файлов? + alert message + + + Forward up to 20 messages at once. + Пересылайте до 20 сообщений за раз. + No comment provided by engineer. + Forwarded Переслано @@ -3240,6 +3387,11 @@ This is your own one-time link! Переслано из No comment provided by engineer. + + Forwarding %lld messages + Пересылка %lld сообщений + No comment provided by engineer. + Forwarding server %@ failed to connect to destination server %@. Please try later. Пересылающий сервер %@ не смог подключиться к серверу назначения %@. Попробуйте позже. @@ -3289,11 +3441,6 @@ Error: %2$@ Полное имя (не обязательно) No comment provided by engineer. - - Full name: - Полное имя: - No comment provided by engineer. - Fully decentralized – visible only to members. Группа полностью децентрализована – она видна только членам. @@ -3539,6 +3686,11 @@ Error: %2$@ ICE серверы (один на строке) No comment provided by engineer. + + IP address + IP адрес + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Если Вы не можете встретиться лично, покажите QR-код во время видеозвонка или поделитесь ссылкой. @@ -3614,6 +3766,12 @@ Error: %2$@ Импорт архива No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + Улучшенная доставка, меньше трафик. + No comment provided by engineer. + Improved message delivery Улучшенная доставка сообщений @@ -4184,6 +4342,11 @@ This is your link for group %@! Серверы сообщений No comment provided by engineer. + + Message shape + Форма сообщений + No comment provided by engineer. + Message source remains private. Источник сообщения остаётся конфиденциальным. @@ -4234,6 +4397,11 @@ This is your link for group %@! Сообщений отправлено No comment provided by engineer. + + Messages were deleted after you selected them. + Сообщения были удалены после того, как вы их выбрали. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. Сообщения, файлы и звонки защищены **end-to-end шифрованием** с прямой секретностью (PFS), правдоподобным отрицанием и восстановлением от взлома. @@ -4389,6 +4557,16 @@ This is your link for group %@! Новый Код No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + Новые учетные данные SOCKS будут использоваться при каждом запуске приложения. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + Новые учетные данные SOCKS будут использоваться для каждого сервера. + No comment provided by engineer. + New chat Новый чат @@ -4509,6 +4687,16 @@ This is your link for group %@! Нет интернет-соединения No comment provided by engineer. + + No permission to record speech + Нет разрешения на запись речи + No comment provided by engineer. + + + No permission to record video + Нет разрешения на запись видео + No comment provided by engineer. + No permission to record voice message Нет разрешения для записи голосового сообщения @@ -4529,6 +4717,11 @@ This is your link for group %@! Ничего не выбрано No comment provided by engineer. + + Nothing to forward! + Нет сообщений, которые можно переслать! + alert title + Notifications Уведомления @@ -4561,7 +4754,7 @@ This is your link for group %@! Ok Ок - No comment provided by engineer. + alert button Old database @@ -4752,6 +4945,13 @@ Requires compatible VPN. Другие %@ серверы No comment provided by engineer. + + Other file errors: +%@ + Другие ошибки файлов: +%@ + alert message + PING count Количество PING @@ -4787,6 +4987,11 @@ Requires compatible VPN. Код доступа установлен! No comment provided by engineer. + + Password + Пароль + No comment provided by engineer. + Password to show Пароль чтобы раскрыть @@ -4936,6 +5141,11 @@ Error: %@ Польский интерфейс No comment provided by engineer. + + Port + Порт + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Возможно, хэш сертификата в адресе сервера неверный @@ -5021,16 +5231,6 @@ Error: %@ Картинки профилей No comment provided by engineer. - - Profile name - Имя профиля - No comment provided by engineer. - - - Profile name: - Имя профиля: - No comment provided by engineer. - Profile password Пароль профиля @@ -5133,6 +5333,11 @@ Enable in *Network & servers* settings. Проксированные серверы No comment provided by engineer. + + Proxy requires password + Прокси требует пароль + No comment provided by engineer. + Push notifications Доставка уведомлений @@ -5354,6 +5559,11 @@ Enable in *Network & servers* settings. Удалить No comment provided by engineer. + + Remove archive? + Удалить архив? + No comment provided by engineer. + Remove image Удалить изображение @@ -5534,6 +5744,11 @@ Enable in *Network & servers* settings. SMP сервер No comment provided by engineer. + + SOCKS proxy + SOCKS прокси + No comment provided by engineer. + Safely receive files Получайте файлы безопасно @@ -5547,12 +5762,13 @@ Enable in *Network & servers* settings. Save Сохранить - chat item action + alert button + chat item action Save (and notify contacts) Сохранить (и уведомить контакты) - No comment provided by engineer. + alert button Save and notify contact @@ -5579,11 +5795,6 @@ Enable in *Network & servers* settings. Сохранить архив No comment provided by engineer. - - Save auto-accept settings - Сохранить настройки автоприема - No comment provided by engineer. - Save group profile Сохранить профиль группы @@ -5619,16 +5830,16 @@ Enable in *Network & servers* settings. Сохранить серверы? No comment provided by engineer. - - Save settings? - Сохранить настройки? - No comment provided by engineer. - Save welcome message? Сохранить приветственное сообщение? No comment provided by engineer. + + Save your profile? + Сохранить ваш профиль? + alert title + Saved Сохранено @@ -5649,6 +5860,11 @@ Enable in *Network & servers* settings. Сохраненное сообщение message info title + + Saving %lld messages + Сохранение %lld сообщений + No comment provided by engineer. + Scale Масштаб @@ -5729,6 +5945,11 @@ Enable in *Network & servers* settings. Выбрать chat item action + + Select chat profile + Выберите профиль чата + No comment provided by engineer. + Selected %lld Выбрано %lld @@ -5847,7 +6068,7 @@ Enable in *Network & servers* settings. Sender cancelled file transfer. Отправитель отменил передачу файла. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -5944,6 +6165,11 @@ Enable in *Network & servers* settings. Отправлено через прокси No comment provided by engineer. + + Server + Сервер + No comment provided by engineer. + Server address Адрес сервера @@ -6064,6 +6290,11 @@ Enable in *Network & servers* settings. Настройки No comment provided by engineer. + + Settings were changed. + Настройки были изменены. + alert message + Shape profile images Форма картинок профилей @@ -6099,6 +6330,11 @@ Enable in *Network & servers* settings. Поделиться ссылкой No comment provided by engineer. + + Share profile + Поделиться профилем + No comment provided by engineer. + Share this 1-time invite link Поделиться одноразовой ссылкой-приглашением @@ -6234,6 +6470,11 @@ Enable in *Network & servers* settings. SimpleX одноразовая ссылка simplex link type + + SimpleX protocols reviewed by Trail of Bits. + Аудит SimpleX протоколов от Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Упрощенный режим Инкогнито @@ -6264,6 +6505,11 @@ Enable in *Network & servers* settings. Слабое blur media + + Some app settings were not migrated. + Некоторые настройки приложения не были перенесены. + No comment provided by engineer. + Some file(s) were not exported: Некоторые файл(ы) не были экспортированы: @@ -6404,6 +6650,16 @@ Enable in *Network & servers* settings. Поддержать SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + Переключайте звук и видео во время звонка. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + Переключайте профиль чата для одноразовых приглашений. + No comment provided by engineer. + System Системная @@ -6439,6 +6695,11 @@ Enable in *Network & servers* settings. TCP_KEEPINTVL No comment provided by engineer. + + Tail + Хвост + No comment provided by engineer. + Take picture Сделать фото @@ -6631,6 +6892,11 @@ It can happen because of some bug or when the connection is compromised.Вставленный текст не является SimpleX-ссылкой. No comment provided by engineer. + + The uploaded database archive will be permanently removed from the servers. + Загруженный архив базы данных будет навсегда удален с серверов. + No comment provided by engineer. + Themes Темы @@ -6758,6 +7024,16 @@ You will be prompted to complete authentication before this feature is enabled.< Вам будет нужно пройти аутентификацию для включения блокировки. No comment provided by engineer. + + To record speech please grant permission to use Microphone. + Для записи речи, пожалуйста, дайте разрешение на использование микрофона. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + Для записи видео, пожалуйста, дайте разрешение на использование камеры. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Для записи голосового сообщения, пожалуйста разрешите доступ к микрофону. @@ -6911,7 +7187,7 @@ You will be prompted to complete authentication before this feature is enabled.< Unknown servers! Неизвестные серверы! - No comment provided by engineer. + alert title Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. @@ -7025,6 +7301,11 @@ To connect, please ask your contact to create another connection link and check Использовать .onion хосты No comment provided by engineer. + + Use SOCKS proxy + Использовать SOCKS прокси + No comment provided by engineer. + Use SimpleX Chat servers? Использовать серверы предосталенные SimpleX Chat? @@ -7090,16 +7371,16 @@ To connect, please ask your contact to create another connection link and check Используйте приложение одной рукой. No comment provided by engineer. - - User profile - Профиль чата - No comment provided by engineer. - User selection Выбор пользователя No comment provided by engineer. + + Username + Имя пользователя + No comment provided by engineer. + Using SimpleX Chat servers. Используются серверы, предоставленные SimpleX Chat. @@ -7333,7 +7614,7 @@ To connect, please ask your contact to create another connection link and check Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. Без Тора или ВПН, Ваш IP адрес будет доступен этим серверам файлов: %@. - No comment provided by engineer. + alert message Wrong database passphrase @@ -7360,11 +7641,6 @@ To connect, please ask your contact to create another connection link and check XFTP сервер No comment provided by engineer. - - You - Вы - No comment provided by engineer. - You **must not** use the same database on two devices. Вы **не должны** использовать одну и ту же базу данных на двух устройствах. @@ -7714,11 +7990,21 @@ Repeat connection request? База данных НЕ зашифрована. Установите пароль, чтобы защитить Ваши данные. No comment provided by engineer. + + Your chat preferences + Ваши настройки чата + alert title + Your chat profiles Ваши профили чата No comment provided by engineer. + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + Соединение было перемещено на %@, но при смене профиля произошла неожиданная ошибка. + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Ваш контакт отправил файл, размер которого превышает максимальный размер (%@). @@ -7734,6 +8020,11 @@ Repeat connection request? Ваши контакты сохранятся. No comment provided by engineer. + + Your credentials may be sent unencrypted. + Ваши учетные данные могут быть отправлены в незашифрованном виде. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Текущие данные Вашего чата будет УДАЛЕНЫ и ЗАМЕНЕНЫ импортированными. @@ -7764,13 +8055,16 @@ Repeat connection request? Будет отправлен Ваш профиль **%@**. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Ваш профиль хранится на Вашем устройстве и отправляется только Вашим контактам. -SimpleX серверы не могут получить доступ к Вашему профилю. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Ваш профиль хранится на Вашем устройстве и отправляется только Вашим контактам. SimpleX серверы не могут получить доступ к Вашему профилю. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + Ваш профиль был изменен. Если вы сохраните его, обновленный профиль будет отправлен всем вашим контактам. + alert message + Your profile, contacts and delivered messages are stored on your device. Ваш профиль, контакты и доставленные сообщения хранятся на Вашем устройстве. 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 646a94a337..37ade821f0 100644 --- a/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff +++ b/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff @@ -129,6 +129,10 @@ %@ อยากเชื่อมต่อ! notification title + + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members No comment provided by engineer. @@ -147,11 +151,31 @@ %d วัน time interval + + %d file(s) are still being downloaded. + forward confirmation reason + + + %d file(s) failed to download. + forward confirmation reason + + + %d file(s) were deleted. + forward confirmation reason + + + %d file(s) were not downloaded. + forward confirmation reason + %d hours %d ชั่วโมง time interval + + %d messages not forwarded + alert title + %d min %d นาที @@ -678,7 +702,7 @@ All profiles - No comment provided by engineer. + profile dropdown All your contacts will remain connected. @@ -860,6 +884,10 @@ รหัสผ่านแอปจะถูกแทนที่ด้วยรหัสผ่านที่ทำลายตัวเอง No comment provided by engineer. + + App session + No comment provided by engineer. + App version เวอร์ชันแอป @@ -959,6 +987,10 @@ ยอมรับภาพอัตโนมัติ No comment provided by engineer. + + Auto-accept settings + alert title + Back กลับ @@ -982,10 +1014,18 @@ แฮชข้อความไม่ดี No comment provided by engineer. + + Better calls + No comment provided by engineer. + Better groups No comment provided by engineer. + + Better message dates. + No comment provided by engineer. + Better messages ข้อความที่ดีขึ้น @@ -995,6 +1035,18 @@ Better networking No comment provided by engineer. + + Better notifications + No comment provided by engineer. + + + Better security ✅ + No comment provided by engineer. + + + Better user experience + No comment provided by engineer. + Black No comment provided by engineer. @@ -1112,7 +1164,7 @@ Cancel ยกเลิก - No comment provided by engineer. + alert button Cancel migration @@ -1130,7 +1182,7 @@ Cannot receive file ไม่สามารถรับไฟล์ได้ - No comment provided by engineer. + alert title Capacity exceeded - recipient did not receive previously sent messages. @@ -1251,6 +1303,15 @@ ค่ากําหนดในการแชท No comment provided by engineer. + + Chat preferences were changed. + alert message + + + Chat profile + โปรไฟล์ผู้ใช้ + No comment provided by engineer. + Chat theme No comment provided by engineer. @@ -1606,6 +1667,10 @@ This is your own one-time link! รุ่นหลัก: v%@ No comment provided by engineer. + + Corner + No comment provided by engineer. + Correct name to %@? No comment provided by engineer. @@ -1720,6 +1785,10 @@ This is your own one-time link! เวลาที่กําหนดเอง No comment provided by engineer. + + Customizable message shape. + No comment provided by engineer. + Customize theme No comment provided by engineer. @@ -2006,6 +2075,10 @@ This is your own one-time link! ลบฐานข้อมูลเก่า? No comment provided by engineer. + + Delete or moderate up to 200 messages. + No comment provided by engineer. + Delete pending connection? ลบการเชื่อมต่อที่รอดำเนินการหรือไม่? @@ -2235,6 +2308,10 @@ This is your own one-time link! Do not send history to new members. No comment provided by engineer. + + Do not use credentials with proxy. + No comment provided by engineer. + Don't create address อย่าสร้างที่อยู่ @@ -2257,7 +2334,8 @@ This is your own one-time link! Download - chat item action + alert button + chat item action Download errors @@ -2272,6 +2350,10 @@ This is your own one-time link! ดาวน์โหลดไฟล์ server test step + + Download files + alert action + Downloaded No comment provided by engineer. @@ -2530,6 +2612,10 @@ This is your own one-time link! เกิดข้อผิดพลาดในการเปลี่ยนที่อยู่ No comment provided by engineer. + + Error changing connection profile + No comment provided by engineer. + Error changing role เกิดข้อผิดพลาดในการเปลี่ยนบทบาท @@ -2540,6 +2626,10 @@ This is your own one-time link! เกิดข้อผิดพลาดในการเปลี่ยนการตั้งค่า No comment provided by engineer. + + Error changing to incognito! + No comment provided by engineer. + Error connecting to forwarding server %@. Please try later. No comment provided by engineer. @@ -2654,6 +2744,10 @@ This is your own one-time link! โหลดเซิร์ฟเวอร์ %@ ผิดพลาด No comment provided by engineer. + + Error migrating settings + No comment provided by engineer. + Error opening chat No comment provided by engineer. @@ -2661,7 +2755,7 @@ This is your own one-time link! Error receiving file เกิดข้อผิดพลาดในการรับไฟล์ - No comment provided by engineer. + alert title Error reconnecting server @@ -2747,10 +2841,14 @@ This is your own one-time link! เกิดข้อผิดพลาดในการหยุดแชท No comment provided by engineer. + + Error switching profile + No comment provided by engineer. + Error switching profile! เกิดข้อผิดพลาดในการเปลี่ยนโปรไฟล์! - No comment provided by engineer. + alertTitle Error synchronizing connection @@ -2793,8 +2891,7 @@ This is your own one-time link! Error: %@ ข้อผิดพลาด: % @ - file error text - snd error text + alert message Error: URL is invalid @@ -2875,6 +2972,11 @@ This is your own one-time link! File error No comment provided by engineer. + + File errors: +%@ + alert message + File not found - most likely file was deleted or cancelled. file error text @@ -3001,10 +3103,26 @@ This is your own one-time link! Forward chat item action + + Forward %d message(s)? + alert title + Forward and save messages No comment provided by engineer. + + Forward messages + alert action + + + Forward messages without files? + alert message + + + Forward up to 20 messages at once. + No comment provided by engineer. + Forwarded No comment provided by engineer. @@ -3013,6 +3131,10 @@ This is your own one-time link! Forwarded from No comment provided by engineer. + + Forwarding %lld messages + No comment provided by engineer. + Forwarding server %@ failed to connect to destination server %@. Please try later. No comment provided by engineer. @@ -3054,11 +3176,6 @@ Error: %2$@ ชื่อเต็ม (ไม่บังคับ) No comment provided by engineer. - - Full name: - ชื่อเต็ม: - No comment provided by engineer. - Fully decentralized – visible only to members. No comment provided by engineer. @@ -3296,6 +3413,10 @@ Error: %2$@ เซิร์ฟเวอร์ ICE (หนึ่งเครื่องต่อสาย) No comment provided by engineer. + + IP address + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. หากคุณไม่สามารถพบกันในชีวิตจริงได้ ให้แสดงคิวอาร์โค้ดในวิดีโอคอล หรือแชร์ลิงก์ @@ -3368,6 +3489,11 @@ Error: %2$@ Importing archive No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + No comment provided by engineer. + Improved message delivery No comment provided by engineer. @@ -3901,6 +4027,10 @@ This is your link for group %@! Message servers No comment provided by engineer. + + Message shape + No comment provided by engineer. + Message source remains private. No comment provided by engineer. @@ -3944,6 +4074,10 @@ This is your link for group %@! Messages sent No comment provided by engineer. + + Messages were deleted after you selected them. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. No comment provided by engineer. @@ -4085,6 +4219,14 @@ This is your link for group %@! รหัสผ่านใหม่ No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + No comment provided by engineer. + New chat No comment provided by engineer. @@ -4197,6 +4339,14 @@ This is your link for group %@! No network connection No comment provided by engineer. + + No permission to record speech + No comment provided by engineer. + + + No permission to record video + No comment provided by engineer. + No permission to record voice message ไม่อนุญาตให้บันทึกข้อความเสียง @@ -4215,6 +4365,10 @@ This is your link for group %@! Nothing selected No comment provided by engineer. + + Nothing to forward! + alert title + Notifications การแจ้งเตือน @@ -4246,7 +4400,7 @@ This is your link for group %@! Ok ตกลง - No comment provided by engineer. + alert button Old database @@ -4423,6 +4577,11 @@ Requires compatible VPN. Other %@ servers No comment provided by engineer. + + Other file errors: +%@ + alert message + PING count จํานวน PING @@ -4458,6 +4617,10 @@ Requires compatible VPN. ตั้งรหัสผ่านเรียบร้อยแล้ว! No comment provided by engineer. + + Password + No comment provided by engineer. + Password to show รหัสผ่านที่จะแสดง @@ -4594,6 +4757,10 @@ Error: %@ อินเตอร์เฟซภาษาโปแลนด์ No comment provided by engineer. + + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect อาจเป็นไปได้ว่าลายนิ้วมือของ certificate ในที่อยู่เซิร์ฟเวอร์ไม่ถูกต้อง @@ -4672,14 +4839,6 @@ Error: %@ Profile images No comment provided by engineer. - - Profile name - No comment provided by engineer. - - - Profile name: - No comment provided by engineer. - Profile password รหัสผ่านโปรไฟล์ @@ -4775,6 +4934,10 @@ Enable in *Network & servers* settings. Proxied servers No comment provided by engineer. + + Proxy requires password + No comment provided by engineer. + Push notifications การแจ้งเตือนแบบทันที @@ -4979,6 +5142,10 @@ Enable in *Network & servers* settings. ลบ No comment provided by engineer. + + Remove archive? + No comment provided by engineer. + Remove image No comment provided by engineer. @@ -5146,6 +5313,10 @@ Enable in *Network & servers* settings. SMP server No comment provided by engineer. + + SOCKS proxy + No comment provided by engineer. + Safely receive files No comment provided by engineer. @@ -5157,12 +5328,13 @@ Enable in *Network & servers* settings. Save บันทึก - chat item action + alert button + chat item action Save (and notify contacts) บันทึก (และแจ้งผู้ติดต่อ) - No comment provided by engineer. + alert button Save and notify contact @@ -5188,11 +5360,6 @@ Enable in *Network & servers* settings. บันทึกไฟล์เก็บถาวร No comment provided by engineer. - - Save auto-accept settings - บันทึกการตั้งค่าการยอมรับอัตโนมัติ - No comment provided by engineer. - Save group profile บันทึกโปรไฟล์กลุ่ม @@ -5228,16 +5395,15 @@ Enable in *Network & servers* settings. บันทึกเซิร์ฟเวอร์? No comment provided by engineer. - - Save settings? - บันทึกการตั้งค่า? - No comment provided by engineer. - Save welcome message? บันทึกข้อความต้อนรับ? No comment provided by engineer. + + Save your profile? + alert title + Saved No comment provided by engineer. @@ -5255,6 +5421,10 @@ Enable in *Network & servers* settings. Saved message message info title + + Saving %lld messages + No comment provided by engineer. + Scale No comment provided by engineer. @@ -5328,6 +5498,10 @@ Enable in *Network & servers* settings. เลือก chat item action + + Select chat profile + No comment provided by engineer. + Selected %lld No comment provided by engineer. @@ -5438,7 +5612,7 @@ Enable in *Network & servers* settings. Sender cancelled file transfer. ผู้ส่งยกเลิกการโอนไฟล์ - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -5528,6 +5702,10 @@ Enable in *Network & servers* settings. Sent via proxy No comment provided by engineer. + + Server + No comment provided by engineer. + Server address No comment provided by engineer. @@ -5637,6 +5815,10 @@ Enable in *Network & servers* settings. การตั้งค่า No comment provided by engineer. + + Settings were changed. + alert message + Shape profile images No comment provided by engineer. @@ -5670,6 +5852,10 @@ Enable in *Network & servers* settings. แชร์ลิงก์ No comment provided by engineer. + + Share profile + No comment provided by engineer. + Share this 1-time invite link No comment provided by engineer. @@ -5795,6 +5981,10 @@ Enable in *Network & servers* settings. คำเชิญ SimpleX แบบครั้งเดียว simplex link type + + SimpleX protocols reviewed by Trail of Bits. + No comment provided by engineer. + Simplified incognito mode No comment provided by engineer. @@ -5821,6 +6011,10 @@ Enable in *Network & servers* settings. Soft blur media + + Some app settings were not migrated. + No comment provided by engineer. + Some file(s) were not exported: No comment provided by engineer. @@ -5949,6 +6143,14 @@ Enable in *Network & servers* settings. สนับสนุน SimpleX แชท No comment provided by engineer. + + Switch audio and video during the call. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + No comment provided by engineer. + System ระบบ @@ -5983,6 +6185,10 @@ Enable in *Network & servers* settings. TCP_KEEPINTVL No comment provided by engineer. + + Tail + No comment provided by engineer. + Take picture ถ่ายภาพ @@ -6167,6 +6373,10 @@ It can happen because of some bug or when the connection is compromised.The text you pasted is not a SimpleX link. No comment provided by engineer. + + The uploaded database archive will be permanently removed from the servers. + No comment provided by engineer. + Themes No comment provided by engineer. @@ -6281,6 +6491,14 @@ You will be prompted to complete authentication before this feature is enabled.< คุณจะได้รับแจ้งให้ยืนยันตัวตนให้เสร็จสมบูรณ์ก่อนที่จะเปิดใช้งานคุณลักษณะนี้ No comment provided by engineer. + + To record speech please grant permission to use Microphone. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. ในการบันทึกข้อความเสียง โปรดให้สิทธิ์ในการใช้ไมโครโฟน @@ -6422,7 +6640,7 @@ You will be prompted to complete authentication before this feature is enabled.< Unknown servers! - No comment provided by engineer. + alert title Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. @@ -6527,6 +6745,10 @@ To connect, please ask your contact to create another connection link and check ใช้โฮสต์ .onion No comment provided by engineer. + + Use SOCKS proxy + No comment provided by engineer. + Use SimpleX Chat servers? ใช้เซิร์ฟเวอร์ SimpleX Chat ไหม? @@ -6584,15 +6806,14 @@ To connect, please ask your contact to create another connection link and check Use the app with one hand. No comment provided by engineer. - - User profile - โปรไฟล์ผู้ใช้ - No comment provided by engineer. - User selection No comment provided by engineer. + + Username + No comment provided by engineer. + Using SimpleX Chat servers. กำลังใช้เซิร์ฟเวอร์ SimpleX Chat อยู่ @@ -6805,7 +7026,7 @@ To connect, please ask your contact to create another connection link and check Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. - No comment provided by engineer. + alert message Wrong database passphrase @@ -6829,11 +7050,6 @@ To connect, please ask your contact to create another connection link and check XFTP server No comment provided by engineer. - - You - คุณ - No comment provided by engineer. - You **must not** use the same database on two devices. No comment provided by engineer. @@ -7158,11 +7374,19 @@ Repeat connection request? ฐานข้อมูลการแชทของคุณไม่ได้ถูก encrypt - ตั้งรหัสผ่านเพื่อ encrypt No comment provided by engineer. + + Your chat preferences + alert title + Your chat profiles โปรไฟล์แชทของคุณ No comment provided by engineer. + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). ผู้ติดต่อของคุณส่งไฟล์ที่ใหญ่กว่าขนาดสูงสุดที่รองรับในปัจจุบัน (%@) @@ -7178,6 +7402,10 @@ Repeat connection request? ผู้ติดต่อของคุณจะยังคงเชื่อมต่ออยู่ No comment provided by engineer. + + Your credentials may be sent unencrypted. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. ฐานข้อมูลแชทปัจจุบันของคุณจะถูกลบและแทนที่ด้วยฐานข้อมูลที่นำเข้า @@ -7206,13 +7434,15 @@ Repeat connection request? Your profile **%@** will be shared. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - โปรไฟล์ของคุณจะถูกจัดเก็บไว้ในอุปกรณ์ของคุณและแชร์กับผู้ติดต่อของคุณเท่านั้น -เซิร์ฟเวอร์ SimpleX ไม่สามารถดูโปรไฟล์ของคุณได้ + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + โปรไฟล์ของคุณจะถูกจัดเก็บไว้ในอุปกรณ์ของคุณและแชร์กับผู้ติดต่อของคุณเท่านั้น เซิร์ฟเวอร์ SimpleX ไม่สามารถดูโปรไฟล์ของคุณได้ No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + alert message + Your profile, contacts and delivered messages are stored on your device. โปรไฟล์ รายชื่อผู้ติดต่อ และข้อความที่ส่งของคุณจะถูกจัดเก็บไว้ในอุปกรณ์ของคุณ 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 054f65110f..b911eb1220 100644 --- a/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff +++ b/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff @@ -137,6 +137,11 @@ %@ bağlanmak istiyor! notification title + + %1$@, %2$@ + %1$@,%2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ ve %lld üyeleri @@ -157,11 +162,36 @@ %d gün time interval + + %d file(s) are still being downloaded. + %d dosyası(ları) hala indiriliyor. + forward confirmation reason + + + %d file(s) failed to download. + %d dosyası(ları) indirilemedi. + forward confirmation reason + + + %d file(s) were deleted. + %d dosyası(ları) silindi. + forward confirmation reason + + + %d file(s) were not downloaded. + %d dosyası(ları) indirilmedi. + forward confirmation reason + %d hours %d saat time interval + + %d messages not forwarded + %d mesajı iletilmeyedi + alert title + %d min %d dakika @@ -554,6 +584,7 @@ Accent + Ana renk No comment provided by engineer. @@ -581,14 +612,17 @@ Acknowledged + Onaylandı No comment provided by engineer. Acknowledgement errors + Onay hataları No comment provided by engineer. Active connections + Aktif bağlantılar No comment provided by engineer. @@ -633,14 +667,17 @@ Additional accent + Ek ana renk No comment provided by engineer. Additional accent 2 + Ek vurgu 2 No comment provided by engineer. Additional secondary + Ek ikincil renk No comment provided by engineer. @@ -670,6 +707,7 @@ Advanced settings + Gelişmiş ayarlar No comment provided by engineer. @@ -689,6 +727,7 @@ All data is private to your device. + Tüm veriler cihazınıza özeldir. No comment provided by engineer. @@ -713,7 +752,8 @@ All profiles - No comment provided by engineer. + Tüm Profiller + profile dropdown All your contacts will remain connected. @@ -742,6 +782,7 @@ Allow calls? + Aramalara izin verilsin mi ? No comment provided by engineer. @@ -781,6 +822,7 @@ Allow sharing + Paylaşıma izin ver No comment provided by engineer. @@ -903,6 +945,11 @@ Uygulama parolası kendi kendini imha eden parolayla değiştirildi. No comment provided by engineer. + + App session + Uygulama oturumu + No comment provided by engineer. + App version Uygulama sürümü @@ -925,6 +972,7 @@ Apply to + Şuna uygula No comment provided by engineer. @@ -934,10 +982,12 @@ Archive contacts to chat later. + Daha sonra görüşmek için kişileri arşivleyin. No comment provided by engineer. Archived contacts + Arşivli kişiler No comment provided by engineer. @@ -1005,6 +1055,11 @@ Fotoğrafları otomatik kabul et No comment provided by engineer. + + Auto-accept settings + Ayarları otomatik olarak kabul et + alert title + Back Geri @@ -1012,6 +1067,7 @@ Background + Arka plan No comment provided by engineer. @@ -1029,11 +1085,21 @@ Kötü mesaj karması No comment provided by engineer. + + Better calls + Daha iyi aramalar + No comment provided by engineer. + Better groups Daha iyi gruplar No comment provided by engineer. + + Better message dates. + Daha iyi mesaj tarihleri. + No comment provided by engineer. + Better messages Daha iyi mesajlar @@ -1041,10 +1107,27 @@ Better networking + Daha iyi ağ oluşturma + No comment provided by engineer. + + + Better notifications + Daha iyi bildirimler + No comment provided by engineer. + + + Better security ✅ + Daha iyi güvenlik ✅ + No comment provided by engineer. + + + Better user experience + Daha iyi kullanıcı deneyimi No comment provided by engineer. Black + Siyah No comment provided by engineer. @@ -1084,10 +1167,12 @@ Blur for better privacy. + Daha iyi gizlilik için bulanıklaştır. No comment provided by engineer. Blur media + Medyayı bulanıklaştır No comment provided by engineer. @@ -1137,6 +1222,7 @@ Calls prohibited! + Aramalara izin verilmiyor! No comment provided by engineer. @@ -1146,10 +1232,12 @@ Can't call contact + Kişi aranamıyor No comment provided by engineer. Can't call member + Üye aranamaz No comment provided by engineer. @@ -1164,12 +1252,13 @@ Can't message member + Üyeye mesaj gönderilemiyor No comment provided by engineer. Cancel İptal et - No comment provided by engineer. + alert button Cancel migration @@ -1183,12 +1272,13 @@ Cannot forward message + Mesaj iletilemiyor No comment provided by engineer. Cannot receive file Dosya alınamıyor - No comment provided by engineer. + alert title Capacity exceeded - recipient did not receive previously sent messages. @@ -1258,6 +1348,7 @@ Chat colors + Sohbet renkleri No comment provided by engineer. @@ -1277,6 +1368,7 @@ Chat database exported + Veritabanı dışa aktarıldı No comment provided by engineer. @@ -1301,6 +1393,7 @@ Chat list + Sohbet listesi No comment provided by engineer. @@ -1313,8 +1406,19 @@ Sohbet tercihleri No comment provided by engineer. + + Chat preferences were changed. + Sohbet tercihleri değiştirildi. + alert message + + + Chat profile + Kullanıcı profili + No comment provided by engineer. + Chat theme + Sohbet teması No comment provided by engineer. @@ -1349,14 +1453,17 @@ Chunks deleted + Parçalar silindi No comment provided by engineer. Chunks downloaded + Parçalar indirildi No comment provided by engineer. Chunks uploaded + Parçalar yüklendi No comment provided by engineer. @@ -1386,10 +1493,12 @@ Color chats with the new themes. + Yeni temalarla renkli sohbetler. No comment provided by engineer. Color mode + Renk modu No comment provided by engineer. @@ -1404,6 +1513,7 @@ Completed + Tamamlandı No comment provided by engineer. @@ -1413,6 +1523,7 @@ Configured %@ servers + Yapılandırılmış %@ sunucuları No comment provided by engineer. @@ -1427,6 +1538,7 @@ Confirm contact deletion? + Kişiyi silmek istediğinizden emin misiniz ? No comment provided by engineer. @@ -1486,6 +1598,7 @@ Connect to your friends faster. + Arkadaşlarınıza daha hızlı bağlanın. No comment provided by engineer. @@ -1529,6 +1642,7 @@ Bu senin kendi tek kullanımlık bağlantın! Connected + Bağlandı No comment provided by engineer. @@ -1538,15 +1652,17 @@ Bu senin kendi tek kullanımlık bağlantın! Connected servers + Bağlı sunucular No comment provided by engineer. Connected to desktop - Bilgisayara bağlanıldı + Masaüstüne bağlandı No comment provided by engineer. Connecting + Bağlanıyor No comment provided by engineer. @@ -1561,6 +1677,7 @@ Bu senin kendi tek kullanımlık bağlantın! Connecting to contact, please wait or check later! + Kişiye bağlanılıyor, lütfen bekleyin ya da daha sonra kontrol edin! No comment provided by engineer. @@ -1575,6 +1692,7 @@ Bu senin kendi tek kullanımlık bağlantın! Connection and servers status. + Bağlantı ve sunucuların durumu. No comment provided by engineer. @@ -1589,6 +1707,7 @@ Bu senin kendi tek kullanımlık bağlantın! Connection notifications + Bağlantı bildirimleri No comment provided by engineer. @@ -1608,10 +1727,12 @@ Bu senin kendi tek kullanımlık bağlantın! Connection with desktop stopped + Masaüstü ile bağlantı durduruldu No comment provided by engineer. Connections + Bağlantılar No comment provided by engineer. @@ -1626,6 +1747,7 @@ Bu senin kendi tek kullanımlık bağlantın! Contact deleted! + Kişiler silindi! No comment provided by engineer. @@ -1640,6 +1762,7 @@ Bu senin kendi tek kullanımlık bağlantın! Contact is deleted. + Kişi silindi. No comment provided by engineer. @@ -1654,6 +1777,7 @@ Bu senin kendi tek kullanımlık bağlantın! Contact will be deleted - this cannot be undone! + Kişiler silinecek - bu geri alınamaz ! No comment provided by engineer. @@ -1673,6 +1797,7 @@ Bu senin kendi tek kullanımlık bağlantın! Conversation deleted! + Sohbet silindi! No comment provided by engineer. @@ -1682,6 +1807,7 @@ Bu senin kendi tek kullanımlık bağlantın! Copy error + Kopyalama hatası No comment provided by engineer. @@ -1689,6 +1815,11 @@ Bu senin kendi tek kullanımlık bağlantın! Çekirdek sürümü: v%@ No comment provided by engineer. + + Corner + Köşeleri yuvarlama + No comment provided by engineer. + Correct name to %@? İsim %@ olarak düzeltilsin mi? @@ -1761,6 +1892,7 @@ Bu senin kendi tek kullanımlık bağlantın! Created + Yaratıldı No comment provided by engineer. @@ -1800,6 +1932,7 @@ Bu senin kendi tek kullanımlık bağlantın! Current profile + Aktif profil No comment provided by engineer. @@ -1812,8 +1945,14 @@ Bu senin kendi tek kullanımlık bağlantın! Özel saat No comment provided by engineer. + + Customizable message shape. + Özelleştirilebilir mesaj şekli. + No comment provided by engineer. + Customize theme + Renk temalarını kişiselleştir No comment provided by engineer. @@ -1823,6 +1962,7 @@ Bu senin kendi tek kullanımlık bağlantın! Dark mode colors + Karanlık mod renkleri No comment provided by engineer. @@ -1946,6 +2086,7 @@ Bu senin kendi tek kullanımlık bağlantın! Delete %lld messages of members? + Üyelerin %lld mesajları silinsin mi? No comment provided by engineer. @@ -2010,6 +2151,7 @@ Bu senin kendi tek kullanımlık bağlantın! Delete contact? + Kişiyi sil? No comment provided by engineer. @@ -2102,6 +2244,11 @@ Bu senin kendi tek kullanımlık bağlantın! Eski veritabanı silinsin mi? No comment provided by engineer. + + Delete or moderate up to 200 messages. + 200'e kadar mesajı silin veya düzenleyin. + No comment provided by engineer. + Delete pending connection? Bekleyen bağlantı silinsin mi? @@ -2119,6 +2266,7 @@ Bu senin kendi tek kullanımlık bağlantın! Delete up to 20 messages at once. + Tek seferde en fazla 20 mesaj silin. No comment provided by engineer. @@ -2128,10 +2276,12 @@ Bu senin kendi tek kullanımlık bağlantın! Delete without notification + Bildirim göndermeden sil No comment provided by engineer. Deleted + Silindi No comment provided by engineer. @@ -2146,6 +2296,7 @@ Bu senin kendi tek kullanımlık bağlantın! Deletion errors + Silme hatası No comment provided by engineer. @@ -2185,6 +2336,7 @@ Bu senin kendi tek kullanımlık bağlantın! Destination server address of %@ is incompatible with forwarding server %@ settings. + Hedef sunucu adresi %@, yönlendirme sunucusu %@ ayarlarıyla uyumlu değil. No comment provided by engineer. @@ -2194,14 +2346,17 @@ Bu senin kendi tek kullanımlık bağlantın! Destination server version of %@ is incompatible with forwarding server %@. + Hedef sunucu %@ sürümü, yönlendirme sunucusu %@ ile uyumlu değil. No comment provided by engineer. Detailed statistics + Detaylı istatistikler No comment provided by engineer. Details + Detaylar No comment provided by engineer. @@ -2211,6 +2366,7 @@ Bu senin kendi tek kullanımlık bağlantın! Developer options + Geliştirici seçenekleri No comment provided by engineer. @@ -2265,6 +2421,7 @@ Bu senin kendi tek kullanımlık bağlantın! Disabled + Devre dışı No comment provided by engineer. @@ -2342,6 +2499,11 @@ Bu senin kendi tek kullanımlık bağlantın! Yeni üyelere geçmişi gönderme. No comment provided by engineer. + + Do not use credentials with proxy. + Kimlik bilgilerini proxy ile kullanmayın. + No comment provided by engineer. + Don't create address Adres oluşturma @@ -2365,10 +2527,12 @@ Bu senin kendi tek kullanımlık bağlantın! Download İndir - chat item action + alert button + chat item action Download errors + İndirme hataları No comment provided by engineer. @@ -2381,12 +2545,19 @@ Bu senin kendi tek kullanımlık bağlantın! Dosya indir server test step + + Download files + Dosyaları indirin + alert action + Downloaded + İndirildi No comment provided by engineer. Downloaded files + Dosyalar İndirildi No comment provided by engineer. @@ -2491,6 +2662,7 @@ Bu senin kendi tek kullanımlık bağlantın! Enabled + Etkin No comment provided by engineer. @@ -2653,6 +2825,11 @@ Bu senin kendi tek kullanımlık bağlantın! Adres değiştirilirken hata oluştu No comment provided by engineer. + + Error changing connection profile + Bağlantı profili değiştirilirken hata oluştu + No comment provided by engineer. + Error changing role Rol değiştirilirken hata oluştu @@ -2663,8 +2840,14 @@ Bu senin kendi tek kullanımlık bağlantın! Ayar değiştirilirken hata oluştu No comment provided by engineer. + + Error changing to incognito! + Gizli moduna geçerken hata oluştu! + No comment provided by engineer. + Error connecting to forwarding server %@. Please try later. + Yönlendirme sunucusu %@'ya bağlanırken hata oluştu. Lütfen daha sonra deneyin. No comment provided by engineer. @@ -2764,6 +2947,7 @@ Bu senin kendi tek kullanımlık bağlantın! Error exporting theme: %@ + Tema dışa aktarılırken hata oluştu: %@ No comment provided by engineer. @@ -2781,6 +2965,11 @@ Bu senin kendi tek kullanımlık bağlantın! %@ sunucuları yüklenirken hata oluştu No comment provided by engineer. + + Error migrating settings + Ayarlar taşınırken hata oluştu + No comment provided by engineer. + Error opening chat Sohbeti açarken sorun oluştu @@ -2789,14 +2978,16 @@ Bu senin kendi tek kullanımlık bağlantın! Error receiving file Dosya alınırken sorun oluştu - No comment provided by engineer. + alert title Error reconnecting server + Hata, sunucuya yeniden bağlanılıyor No comment provided by engineer. Error reconnecting servers + Hata sunuculara yeniden bağlanılıyor No comment provided by engineer. @@ -2806,6 +2997,7 @@ Bu senin kendi tek kullanımlık bağlantın! Error resetting statistics + Hata istatistikler sıfırlanıyor No comment provided by engineer. @@ -2878,10 +3070,15 @@ Bu senin kendi tek kullanımlık bağlantın! Sohbet durdurulurken hata oluştu No comment provided by engineer. + + Error switching profile + Profil değiştirme sırasında hata oluştu + No comment provided by engineer. + Error switching profile! Profil değiştirilirken hata oluştu! - No comment provided by engineer. + alertTitle Error synchronizing connection @@ -2926,8 +3123,7 @@ Bu senin kendi tek kullanımlık bağlantın! Error: %@ Hata: %@ - file error text - snd error text + alert message Error: URL is invalid @@ -2941,6 +3137,7 @@ Bu senin kendi tek kullanımlık bağlantın! Errors + Hatalar No comment provided by engineer. @@ -2970,6 +3167,7 @@ Bu senin kendi tek kullanımlık bağlantın! Export theme + Temayı dışa aktar No comment provided by engineer. @@ -3009,22 +3207,34 @@ Bu senin kendi tek kullanımlık bağlantın! File error + Dosya hatası No comment provided by engineer. + + File errors: +%@ + Dosya hataları: +%@ + alert message + File not found - most likely file was deleted or cancelled. + Dosya bulunamadı - muhtemelen dosya silindi veya göderim iptal edildi. file error text File server error: %@ + Dosya sunucusu hatası: %@ file error text File status + Dosya durumu No comment provided by engineer. File status: %@ + Dosya durumu: %@ copied message info @@ -3142,11 +3352,31 @@ Bu senin kendi tek kullanımlık bağlantın! İlet chat item action + + Forward %d message(s)? + %d mesaj(lar)ı iletilsin mi? + alert title + Forward and save messages Mesajları ilet ve kaydet No comment provided by engineer. + + Forward messages + İletileri ilet + alert action + + + Forward messages without files? + Mesajlar dosyalar olmadan iletilsin mi ? + alert message + + + Forward up to 20 messages at once. + Aynı anda en fazla 20 mesaj iletin. + No comment provided by engineer. + Forwarded İletildi @@ -3157,16 +3387,24 @@ Bu senin kendi tek kullanımlık bağlantın! Şuradan iletildi No comment provided by engineer. + + Forwarding %lld messages + %lld mesajlarını ilet + No comment provided by engineer. + Forwarding server %@ failed to connect to destination server %@. Please try later. + Yönlendirme sunucusu %@, hedef sunucu %@'ya bağlanamadı. Lütfen daha sonra deneyin. No comment provided by engineer. Forwarding server address is incompatible with network settings: %@. + Yönlendirme sunucusu adresi ağ ayarlarıyla uyumsuz: %@. No comment provided by engineer. Forwarding server version is incompatible with network settings: %@. + Yönlendirme sunucusu sürümü ağ ayarlarıyla uyumsuz: %@. No comment provided by engineer. @@ -3203,11 +3441,6 @@ Hata: %2$@ Bütün isim (opsiyonel) No comment provided by engineer. - - Full name: - Bütün isim: - No comment provided by engineer. - Fully decentralized – visible only to members. Tamamiyle merkezi olmayan - sadece kişilere görünür. @@ -3230,10 +3463,12 @@ Hata: %2$@ Good afternoon! + İyi öğlenler! message preview Good morning! + Günaydın! message preview @@ -3451,6 +3686,11 @@ Hata: %2$@ ICE sunucuları (her satıra bir tane) No comment provided by engineer. + + IP address + IP adresi + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Eğer onunla buluşamıyorsan görüntülü aramada QR kod göster veya bağlantığı paylaş. @@ -3518,6 +3758,7 @@ Hata: %2$@ Import theme + Temayı içe aktar No comment provided by engineer. @@ -3525,6 +3766,13 @@ Hata: %2$@ Arşiv içe aktarılıyor No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + İyileştirilmiş teslimat, azaltılmış trafik kullanımı. +Daha fazla iyileştirme yakında geliyor! + No comment provided by engineer. + Improved message delivery İyileştirilmiş mesaj iletimi @@ -3644,6 +3892,7 @@ Hata: %2$@ Interface colors + Arayüz renkleri No comment provided by engineer. @@ -3749,6 +3998,7 @@ Hata: %2$@ It protects your IP address and connections. + IP adresinizi ve bağlantılarınızı korur. No comment provided by engineer. @@ -3815,6 +4065,7 @@ Bu senin grup için bağlantın %@! Keep conversation + Sohbeti sakla No comment provided by engineer. @@ -3994,10 +4245,12 @@ Bu senin grup için bağlantın %@! Media & file servers + Medya ve dosya sunucuları No comment provided by engineer. Medium + Orta blur media @@ -4007,6 +4260,7 @@ Bu senin grup için bağlantın %@! Member inactive + Üye inaktif item status text @@ -4026,6 +4280,7 @@ Bu senin grup için bağlantın %@! Menus + Menüler No comment provided by engineer. @@ -4050,10 +4305,12 @@ Bu senin grup için bağlantın %@! Message forwarded + Mesaj iletildi item status text Message may be delivered later if member becomes active. + Kullanıcı aktif olursa mesaj iletilebilir. item status description @@ -4078,10 +4335,17 @@ Bu senin grup için bağlantın %@! Message reception + Mesaj alındısı No comment provided by engineer. Message servers + Mesaj sunucuları + No comment provided by engineer. + + + Message shape + Mesaj şekli No comment provided by engineer. @@ -4091,10 +4355,12 @@ Bu senin grup için bağlantın %@! Message status + Mesaj durumu No comment provided by engineer. Message status: %@ + Mesaj durumu: %@ copied message info @@ -4124,12 +4390,19 @@ Bu senin grup için bağlantın %@! Messages received + Mesajlar alındı No comment provided by engineer. Messages sent + Mesajlar gönderildi No comment provided by engineer. + + Messages were deleted after you selected them. + Mesajlar siz seçtikten sonra silindi. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. Mesajlar, dosyalar ve aramalar **uçtan uca şifreleme** ile mükemmel ileri gizlilik, inkar ve izinsiz giriş kurtarma ile korunur. @@ -4285,6 +4558,16 @@ Bu senin grup için bağlantın %@! Yeni şifre No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + Uygulamayı her başlattığınızda yeni SOCKS kimlik bilgileri kullanılacaktır. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + Her sunucu için yeni SOCKS kimlik bilgileri kullanılacaktır. + No comment provided by engineer. + New chat Yeni sohbet @@ -4292,6 +4575,7 @@ Bu senin grup için bağlantın %@! New chat experience 🎉 + Yeni bir sohbet deneyimi 🎉 No comment provided by engineer. @@ -4326,6 +4610,7 @@ Bu senin grup için bağlantın %@! New media options + Yeni medya seçenekleri No comment provided by engineer. @@ -4375,6 +4660,7 @@ Bu senin grup için bağlantın %@! No direct connection yet, message is forwarded by admin. + Henüz direkt bağlantı yok mesaj admin tarafından yönlendirildi. item status description @@ -4394,6 +4680,7 @@ Bu senin grup için bağlantın %@! No info, try to reload + Bilgi yok, yenilemeyi deneyin No comment provided by engineer. @@ -4401,6 +4688,16 @@ Bu senin grup için bağlantın %@! Ağ bağlantısı yok No comment provided by engineer. + + No permission to record speech + Konuşma kaydetme izni yok + No comment provided by engineer. + + + No permission to record video + Video kaydı için izin yok + No comment provided by engineer. + No permission to record voice message Sesli mesaj kaydetmek için izin yok @@ -4418,8 +4715,14 @@ Bu senin grup için bağlantın %@! Nothing selected + Hiçbir şey seçilmedi No comment provided by engineer. + + Nothing to forward! + Yönlendirilecek bir şey yok! + alert title + Notifications Bildirimler @@ -4452,7 +4755,7 @@ Bu senin grup için bağlantın %@! Ok Tamam - No comment provided by engineer. + alert button Old database @@ -4495,6 +4798,7 @@ VPN'nin etkinleştirilmesi gerekir. Only delete conversation + Sadece sohbeti sil No comment provided by engineer. @@ -4594,6 +4898,7 @@ VPN'nin etkinleştirilmesi gerekir. Open server settings + Sunucu ayarlarını aç No comment provided by engineer. @@ -4638,8 +4943,16 @@ VPN'nin etkinleştirilmesi gerekir. Other %@ servers + Diğer %@ sunucuları No comment provided by engineer. + + Other file errors: +%@ + Diğer dosya hataları: +%@ + alert message + PING count PING sayısı @@ -4675,6 +4988,11 @@ VPN'nin etkinleştirilmesi gerekir. Şifre ayarlandı! No comment provided by engineer. + + Password + Şifre + No comment provided by engineer. + Password to show Gösterilecek şifre @@ -4707,6 +5025,7 @@ VPN'nin etkinleştirilmesi gerekir. Pending + Bekleniyor No comment provided by engineer. @@ -4731,10 +5050,12 @@ VPN'nin etkinleştirilmesi gerekir. Play from the chat list. + Sohbet listesinden oynat. No comment provided by engineer. Please ask your contact to enable calls. + Lütfen kişinizden çağrılara izin vermesini isteyin. No comment provided by engineer. @@ -4745,6 +5066,8 @@ VPN'nin etkinleştirilmesi gerekir. 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. + Lütfen telefonun ve bilgisayarın aynı lokal ağa bağlı olduğundan ve bilgisayar güvenlik duvarının bağlantıya izin verdiğinden emin olun. +Lütfen diğer herhangi bir sorunu geliştiricilerle paylaşın. No comment provided by engineer. @@ -4819,6 +5142,11 @@ Hata: %@ Lehçe arayüz No comment provided by engineer. + + Port + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Muhtemelen, sunucu adresindeki parmakizi sertifikası doğru değil @@ -4846,6 +5174,7 @@ Hata: %@ Previously connected servers + Önceden bağlanılmış sunucular No comment provided by engineer. @@ -4885,6 +5214,7 @@ Hata: %@ Private routing error + Gizli yönlendirme hatası No comment provided by engineer. @@ -4902,16 +5232,6 @@ Hata: %@ Profil resimleri No comment provided by engineer. - - Profile name - Profil ismi - No comment provided by engineer. - - - Profile name: - Profil ismi: - No comment provided by engineer. - Profile password Profil parolası @@ -4919,6 +5239,7 @@ Hata: %@ Profile theme + Profil teması No comment provided by engineer. @@ -5005,10 +5326,17 @@ Enable in *Network & servers* settings. Proxied + Proxyli No comment provided by engineer. Proxied servers + Proxy sunucuları + No comment provided by engineer. + + + Proxy requires password + Proxy şifre gerektirir No comment provided by engineer. @@ -5033,6 +5361,7 @@ Enable in *Network & servers* settings. Reachable chat toolbar + Erişilebilir sohbet araç çubuğu No comment provided by engineer. @@ -5077,11 +5406,12 @@ Enable in *Network & servers* settings. Receipts are disabled - Gönderildi bilgisi devre dışı bırakıldı + Alıcılar devre dışı bırakıldı No comment provided by engineer. Receive errors + Alım sırasında hata No comment provided by engineer. @@ -5106,14 +5436,17 @@ Enable in *Network & servers* settings. Received messages + Alınan mesajlar No comment provided by engineer. Received reply + Alınan cevap No comment provided by engineer. Received total + Toplam alınan No comment provided by engineer. @@ -5148,6 +5481,7 @@ Enable in *Network & servers* settings. Reconnect + Yeniden bağlan No comment provided by engineer. @@ -5157,18 +5491,22 @@ Enable in *Network & servers* settings. Reconnect all servers + Tüm sunuculara yeniden bağlan No comment provided by engineer. Reconnect all servers? + Tüm sunuculara yeniden bağlansın mı? No comment provided by engineer. Reconnect server to force message delivery. It uses additional traffic. + Mesajı göndermeye zorlamak için sunucuya yeniden bağlan. Bu ekstra internet kullanır. No comment provided by engineer. Reconnect server? + Sunucuya yeniden bağlansın mı ? No comment provided by engineer. @@ -5222,8 +5560,14 @@ Enable in *Network & servers* settings. Sil No comment provided by engineer. + + Remove archive? + Arşiv kaldırılsın mı ? + No comment provided by engineer. + Remove image + Resmi kaldır No comment provided by engineer. @@ -5298,14 +5642,17 @@ Enable in *Network & servers* settings. Reset all hints + Tüm ip uçlarını sıfırla No comment provided by engineer. Reset all statistics + Tüm istatistikleri sıfırla No comment provided by engineer. Reset all statistics? + Tüm istatistikler sıfırlansın mı ? No comment provided by engineer. @@ -5315,6 +5662,7 @@ Enable in *Network & servers* settings. Reset to app theme + Uygulama temasına sıfırla No comment provided by engineer. @@ -5324,6 +5672,7 @@ Enable in *Network & servers* settings. Reset to user theme + Kullanıcı temasına sıfırla No comment provided by engineer. @@ -5393,6 +5742,12 @@ Enable in *Network & servers* settings. SMP server + SMP sunucusu + No comment provided by engineer. + + + SOCKS proxy + SOCKS vekili No comment provided by engineer. @@ -5408,12 +5763,13 @@ Enable in *Network & servers* settings. Save Kaydet - chat item action + alert button + chat item action Save (and notify contacts) Kaydet (ve kişilere bildir) - No comment provided by engineer. + alert button Save and notify contact @@ -5427,6 +5783,7 @@ Enable in *Network & servers* settings. Save and reconnect + Kayıt et ve yeniden bağlan No comment provided by engineer. @@ -5439,11 +5796,6 @@ Enable in *Network & servers* settings. Arşivi kaydet No comment provided by engineer. - - Save auto-accept settings - Otomatik kabul et ayarlarını kaydet - No comment provided by engineer. - Save group profile Grup profilini kaydet @@ -5479,16 +5831,16 @@ Enable in *Network & servers* settings. Sunucular kaydedilsin mi? No comment provided by engineer. - - Save settings? - Ayarlar kaydedilsin mi? - No comment provided by engineer. - Save welcome message? Hoşgeldin mesajı kaydedilsin mi? No comment provided by engineer. + + Save your profile? + Profiliniz kaydedilsin mi? + alert title + Saved Kaydedildi @@ -5509,12 +5861,19 @@ Enable in *Network & servers* settings. Kaydedilmiş mesaj message info title + + Saving %lld messages + %lld mesajlarını kaydet + No comment provided by engineer. + Scale + Ölçeklendir No comment provided by engineer. Scan / Paste link + Tara / Bağlantı yapıştır No comment provided by engineer. @@ -5559,6 +5918,7 @@ Enable in *Network & servers* settings. Secondary + İkincil renk No comment provided by engineer. @@ -5568,6 +5928,7 @@ Enable in *Network & servers* settings. Secured + Güvenli No comment provided by engineer. @@ -5585,12 +5946,19 @@ Enable in *Network & servers* settings. Seç chat item action + + Select chat profile + Sohbet profili seç + No comment provided by engineer. + Selected %lld + Seçilen %lld No comment provided by engineer. Selected chat preferences prohibit this message. + Seçilen sohbet tercihleri bu mesajı yasakladı. No comment provided by engineer. @@ -5640,6 +6008,7 @@ Enable in *Network & servers* settings. Send errors + Gönderme hataları No comment provided by engineer. @@ -5654,6 +6023,7 @@ Enable in *Network & servers* settings. Send message to enable calls. + Çağrıları aktif etmek için mesaj gönder. No comment provided by engineer. @@ -5699,7 +6069,7 @@ Enable in *Network & servers* settings. Sender cancelled file transfer. Gönderici dosya gönderimini iptal etti. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -5713,7 +6083,7 @@ Enable in *Network & servers* settings. Sending delivery receipts will be enabled for all contacts. - Gönderildi bilgisi tüm kişiler için etkinleştirilecektir. + Tüm kişiler için iletim bilgisi gönderme özelliği etkinleştirilecek. No comment provided by engineer. @@ -5758,6 +6128,7 @@ Enable in *Network & servers* settings. Sent directly + Direkt gönderildi No comment provided by engineer. @@ -5772,6 +6143,7 @@ Enable in *Network & servers* settings. Sent messages + Gönderilen mesajlar No comment provided by engineer. @@ -5781,18 +6153,27 @@ Enable in *Network & servers* settings. Sent reply + Gönderilen cevap No comment provided by engineer. Sent total + Gönderilen tüm mesajların toplamı No comment provided by engineer. Sent via proxy + Bir proxy aracılığıyla gönderildi + No comment provided by engineer. + + + Server + Sunucu No comment provided by engineer. Server address + Sunucu adresi No comment provided by engineer. @@ -5802,6 +6183,7 @@ Enable in *Network & servers* settings. Server address is incompatible with network settings: %@. + Sunucu adresi ağ ayarlarıyla uyumsuz: %@. No comment provided by engineer. @@ -5821,6 +6203,7 @@ Enable in *Network & servers* settings. Server type + Sunucu tipi No comment provided by engineer. @@ -5830,6 +6213,7 @@ Enable in *Network & servers* settings. Server version is incompatible with your app: %@. + Sunucu sürümü uygulamanızla uyumlu değil: %@. No comment provided by engineer. @@ -5839,10 +6223,12 @@ Enable in *Network & servers* settings. Servers info + Sunucu bilgileri No comment provided by engineer. Servers statistics will be reset - this cannot be undone! + Sunucu istatistikleri sıfırlanacaktır - bu geri alınamaz! No comment provided by engineer. @@ -5862,6 +6248,7 @@ Enable in *Network & servers* settings. Set default theme + Varsayılan temaya ayarla No comment provided by engineer. @@ -5904,6 +6291,11 @@ Enable in *Network & servers* settings. Ayarlar No comment provided by engineer. + + Settings were changed. + Ayarlar değiştirildi. + alert message + Shape profile images Profil resimlerini şekillendir @@ -5931,6 +6323,7 @@ Enable in *Network & servers* settings. Share from other apps. + Diğer uygulamalardan paylaşın. No comment provided by engineer. @@ -5938,6 +6331,11 @@ Enable in *Network & servers* settings. Bağlantıyı paylaş No comment provided by engineer. + + Share profile + Profil paylaş + No comment provided by engineer. + Share this 1-time invite link Bu tek kullanımlık bağlantı davetini paylaş @@ -5945,6 +6343,7 @@ Enable in *Network & servers* settings. Share to SimpleX + SimpleX ile paylaş No comment provided by engineer. @@ -5979,6 +6378,7 @@ Enable in *Network & servers* settings. Show percentage + Yüzdeyi göster No comment provided by engineer. @@ -5998,6 +6398,7 @@ Enable in *Network & servers* settings. SimpleX + SimpleX No comment provided by engineer. @@ -6070,6 +6471,11 @@ Enable in *Network & servers* settings. SimpleX tek kullanımlık davet simplex link type + + SimpleX protocols reviewed by Trail of Bits. + SimpleX protokolleri Trail of Bits tarafından incelenmiştir. + No comment provided by engineer. + Simplified incognito mode Basitleştirilmiş gizli mod @@ -6077,6 +6483,7 @@ Enable in *Network & servers* settings. Size + Boyut No comment provided by engineer. @@ -6096,10 +6503,17 @@ Enable in *Network & servers* settings. Soft + Yumuşak blur media + + Some app settings were not migrated. + Bazı uygulama ayarları taşınamadı. + No comment provided by engineer. + Some file(s) were not exported: + Bazı dosya(lar) dışa aktarılmadı: No comment provided by engineer. @@ -6109,6 +6523,7 @@ Enable in *Network & servers* settings. Some non-fatal errors occurred during import: + İçe aktarma sırasında bazı önemli olmayan hatalar oluştu: No comment provided by engineer. @@ -6138,10 +6553,12 @@ Enable in *Network & servers* settings. Starting from %@. + %@'dan başlayarak. No comment provided by engineer. Statistics + İstatistikler No comment provided by engineer. @@ -6206,6 +6623,7 @@ Enable in *Network & servers* settings. Strong + Güçlü blur media @@ -6215,14 +6633,17 @@ Enable in *Network & servers* settings. Subscribed + Abone olundu No comment provided by engineer. Subscription errors + Abone olurken hata No comment provided by engineer. Subscriptions ignored + Abonelikler göz ardı edildi No comment provided by engineer. @@ -6230,6 +6651,16 @@ Enable in *Network & servers* settings. SimpleX Chat'e destek ol No comment provided by engineer. + + Switch audio and video during the call. + Görüşme sırasında ses ve görüntüyü değiştirin. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + Sohbet profilini 1 kerelik davetler için değiştirin. + No comment provided by engineer. + System Sistem @@ -6242,6 +6673,7 @@ Enable in *Network & servers* settings. TCP connection + TCP bağlantısı No comment provided by engineer. @@ -6264,6 +6696,11 @@ Enable in *Network & servers* settings. TCP_TVLDEKAL No comment provided by engineer. + + Tail + Konuşma balonu + No comment provided by engineer. + Take picture Fotoğraf çek @@ -6306,6 +6743,7 @@ Enable in *Network & servers* settings. Temporary file error + Geçici dosya hatası No comment provided by engineer. @@ -6412,10 +6850,12 @@ Bazı hatalar nedeniyle veya bağlantı tehlikeye girdiğinde meydana gelebilir. The messages will be deleted for all members. + Mesajlar tüm üyeler için silinecektir. No comment provided by engineer. The messages will be marked as moderated for all members. + Mesajlar tüm üyeler için moderasyonlu olarak işaretlenecektir. No comment provided by engineer. @@ -6453,8 +6893,14 @@ Bazı hatalar nedeniyle veya bağlantı tehlikeye girdiğinde meydana gelebilir. Yapıştırdığın metin bir SimpleX bağlantısı değildir. No comment provided by engineer. + + The uploaded database archive will be permanently removed from the servers. + Yüklenen veritabanı arşivi sunuculardan kalıcı olarak kaldırılacaktır. + No comment provided by engineer. + Themes + Temalar No comment provided by engineer. @@ -6524,6 +6970,7 @@ Bazı hatalar nedeniyle veya bağlantı tehlikeye girdiğinde meydana gelebilir. This link was used with another mobile device, please create a new link on the desktop. + Bu bağlantı başka bir mobil cihazda kullanıldı, lütfen masaüstünde yeni bir bağlantı oluşturun. No comment provided by engineer. @@ -6533,6 +6980,7 @@ Bazı hatalar nedeniyle veya bağlantı tehlikeye girdiğinde meydana gelebilir. Title + Başlık No comment provided by engineer. @@ -6577,6 +7025,16 @@ You will be prompted to complete authentication before this feature is enabled.< Bu özellik etkinleştirilmeden önce kimlik doğrulamayı tamamlamanız istenecektir. No comment provided by engineer. + + To record speech please grant permission to use Microphone. + Konuşmayı kaydetmek için lütfen Mikrofon kullanma izni verin. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + Video kaydetmek için lütfen Kamera kullanım izni verin. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Sesli mesaj kaydetmek için lütfen Mikrofon kullanım izni verin. @@ -6599,6 +7057,7 @@ Bu özellik etkinleştirilmeden önce kimlik doğrulamayı tamamlamanız istenec Toggle chat list: + Sohbet listesini değiştir: No comment provided by engineer. @@ -6608,10 +7067,12 @@ Bu özellik etkinleştirilmeden önce kimlik doğrulamayı tamamlamanız istenec Toolbar opacity + Araç çubuğu opaklığı No comment provided by engineer. Total + Toplam No comment provided by engineer. @@ -6621,6 +7082,7 @@ Bu özellik etkinleştirilmeden önce kimlik doğrulamayı tamamlamanız istenec Transport sessions + Taşıma oturumları No comment provided by engineer. @@ -6726,7 +7188,7 @@ Bu özellik etkinleştirilmeden önce kimlik doğrulamayı tamamlamanız istenec Unknown servers! Bilinmeyen sunucular! - No comment provided by engineer. + alert title Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. @@ -6792,6 +7254,7 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Update settings? + Ayarları güncelleyelim mi? No comment provided by engineer. @@ -6806,6 +7269,7 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Upload errors + Yükleme hataları No comment provided by engineer. @@ -6820,10 +7284,12 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Uploaded + Yüklendi No comment provided by engineer. Uploaded files + Yüklenen dosyalar No comment provided by engineer. @@ -6836,6 +7302,11 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste .onion ana bilgisayarlarını kullan No comment provided by engineer. + + Use SOCKS proxy + SOCKS vekili kullan + No comment provided by engineer. + Use SimpleX Chat servers? SimpleX Chat sunucuları kullanılsın mı? @@ -6898,15 +7369,17 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Use the app with one hand. - No comment provided by engineer. - - - User profile - Kullanıcı profili + Uygulamayı tek elle kullan. No comment provided by engineer. User selection + Kullanıcı seçimi + No comment provided by engineer. + + + Username + Kullanıcı Adı No comment provided by engineer. @@ -7041,10 +7514,12 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Wallpaper accent + Duvar kağıdı vurgusu No comment provided by engineer. Wallpaper background + Duvar kağıdı arkaplanı No comment provided by engineer. @@ -7140,7 +7615,7 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. Tor veya VPN olmadan, IP adresiniz bu XFTP aktarıcıları tarafından görülebilir: %@. - No comment provided by engineer. + alert message Wrong database passphrase @@ -7154,6 +7629,7 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Wrong key or unknown file chunk address - most likely file is deleted. + Yanlış anahtar veya bilinmeyen dosya yığın adresi - büyük olasılıkla dosya silinmiştir. file error text @@ -7163,11 +7639,7 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste XFTP server - No comment provided by engineer. - - - You - Sen + XFTP sunucusu No comment provided by engineer. @@ -7244,6 +7716,7 @@ Katılma isteği tekrarlansın mı? You are not connected to these servers. Private routing is used to deliver messages to them. + Bu sunuculara bağlı değilsiniz. Mesajları onlara iletmek için özel yönlendirme kullanılır. No comment provided by engineer. @@ -7253,6 +7726,7 @@ Katılma isteği tekrarlansın mı? You can change it in Appearance settings. + Görünüm ayarlarından değiştirebilirsiniz. No comment provided by engineer. @@ -7292,6 +7766,7 @@ Katılma isteği tekrarlansın mı? You can send messages to %@ from Archived contacts. + Arşivlenen kişilerden %@'ya mesaj gönderebilirsiniz. No comment provided by engineer. @@ -7321,6 +7796,7 @@ Katılma isteği tekrarlansın mı? You can still view conversation with %@ in the list of chats. + Sohbet listesinde %@ ile konuşmayı görüntülemeye devam edebilirsiniz. No comment provided by engineer. @@ -7387,10 +7863,12 @@ Bağlantı isteği tekrarlansın mı? You may migrate the exported database. + Dışa aktarılan veritabanını taşıyabilirsiniz. No comment provided by engineer. You may save the exported archive. + Dışa aktarılan arşivi kaydedebilirsiniz. No comment provided by engineer. @@ -7400,6 +7878,7 @@ Bağlantı isteği tekrarlansın mı? You need to allow your contact to call to be able to call them. + Kendiniz arayabilmeniz için önce irtibat kişinizin sizi aramasına izin vermelisiniz. No comment provided by engineer. @@ -7512,11 +7991,21 @@ Bağlantı isteği tekrarlansın mı? Sohbet veritabanınız şifrelenmemiş - şifrelemek için parola ayarlayın. No comment provided by engineer. + + Your chat preferences + Sohbet tercihleriniz + alert title + Your chat profiles Sohbet profillerin No comment provided by engineer. + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + Bağlantınız %@ adresine taşındı ancak sizi profile yönlendirirken beklenmedik bir hata oluştu. + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Kişiniz şu anda desteklenen maksimum boyuttan (%@) daha büyük bir dosya gönderdi. @@ -7532,6 +8021,11 @@ Bağlantı isteği tekrarlansın mı? Kişileriniz bağlı kalacaktır. No comment provided by engineer. + + Your credentials may be sent unencrypted. + Kimlik bilgileriniz şifrelenmeden gönderilebilir. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Mevcut sohbet veritabanınız SİLİNECEK ve içe aktarılan veritabanıyla DEĞİŞTİRİLECEKTİR. @@ -7562,13 +8056,16 @@ Bağlantı isteği tekrarlansın mı? Profiliniz **%@** paylaşılacaktır. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Profiliniz cihazınızda saklanır ve sadece kişilerinizle paylaşılır. -SimpleX sunucuları profilinizi göremez. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Profiliniz cihazınızda saklanır ve sadece kişilerinizle paylaşılır. SimpleX sunucuları profilinizi göremez. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + Profiliniz değiştirildi. Kaydederseniz, güncellenmiş profil tüm kişilerinize gönderilecektir. + alert message + Your profile, contacts and delivered messages are stored on your device. Profiliniz, kişileriniz ve gönderilmiş mesajlar cihazınızda saklanır. @@ -7666,6 +8163,7 @@ SimpleX sunucuları profilinizi göremez. attempts + denemeler No comment provided by engineer. @@ -7710,6 +8208,7 @@ SimpleX sunucuları profilinizi göremez. call + Ara No comment provided by engineer. @@ -7864,6 +8363,7 @@ SimpleX sunucuları profilinizi göremez. decryption errors + Şifre çözme hataları No comment provided by engineer. @@ -7918,6 +8418,7 @@ SimpleX sunucuları profilinizi göremez. duplicates + Kopyalar No comment provided by engineer. @@ -8002,6 +8503,7 @@ SimpleX sunucuları profilinizi göremez. expired + Süresi dolmuş No comment provided by engineer. @@ -8036,6 +8538,7 @@ SimpleX sunucuları profilinizi göremez. inactive + inaktif No comment provided by engineer. @@ -8080,6 +8583,7 @@ SimpleX sunucuları profilinizi göremez. invite + davet No comment provided by engineer. @@ -8139,6 +8643,7 @@ SimpleX sunucuları profilinizi göremez. message + mesaj No comment provided by engineer. @@ -8173,6 +8678,7 @@ SimpleX sunucuları profilinizi göremez. mute + Sessiz No comment provided by engineer. @@ -8229,10 +8735,12 @@ SimpleX sunucuları profilinizi göremez. other + diğer No comment provided by engineer. other errors + diğer hatalar No comment provided by engineer. @@ -8307,6 +8815,7 @@ SimpleX sunucuları profilinizi göremez. search + ara No comment provided by engineer. @@ -8395,6 +8904,7 @@ son alınan msj: %2$@ unmute + susturmayı kaldır No comment provided by engineer. @@ -8444,6 +8954,7 @@ son alınan msj: %2$@ video + video No comment provided by engineer. @@ -8624,14 +9135,17 @@ son alınan msj: %2$@ SimpleX SE + SimpleX SE Bundle display name SimpleX SE + SimpleX SE Bundle name Copyright © 2024 SimpleX Chat. All rights reserved. + Telif Hakkı © 2024 SimpleX Chat. Tüm hakları saklıdır. Copyright (human-readable) @@ -8643,150 +9157,187 @@ son alınan msj: %2$@ %@ + %@ No comment provided by engineer. App is locked! + Uygulama kilitlendi! No comment provided by engineer. Cancel + İptal et No comment provided by engineer. Cannot access keychain to save database password + Veritabanı şifresini kaydetmek için Anahtar Zinciri'ne erişilemiyor No comment provided by engineer. Cannot forward message + Mesaj iletilemiyor No comment provided by engineer. Comment + Yorum No comment provided by engineer. Currently maximum supported file size is %@. + Şu anki maksimum desteklenen dosya boyutu %@ kadardır. No comment provided by engineer. Database downgrade required + Veritabanı sürüm düşürme gerekli No comment provided by engineer. Database encrypted! + Veritabanı şifrelendi! No comment provided by engineer. Database error + Veritabanı hatası No comment provided by engineer. Database passphrase is different from saved in the keychain. + Veritabanı parolası Anahtar Zinciri'nde kayıtlı olandan farklıdır. No comment provided by engineer. Database passphrase is required to open chat. + Konuşmayı açmak için veri tabanı parolası gerekli. No comment provided by engineer. Database upgrade required + Veritabanı yükseltmesi gerekli No comment provided by engineer. Error preparing file + Dosya hazırlanırken hata oluştu No comment provided by engineer. Error preparing message + Mesaj hazırlanırken hata oluştu No comment provided by engineer. Error: %@ + Hata: %@ No comment provided by engineer. File error + Dosya hatası No comment provided by engineer. Incompatible database version + Uyumsuz veritabanı sürümü No comment provided by engineer. Invalid migration confirmation + Geçerli olmayan taşıma onayı No comment provided by engineer. Keychain error + Anahtarlık hatası No comment provided by engineer. Large file! + Büyük dosya! No comment provided by engineer. No active profile + Aktif profil yok No comment provided by engineer. Ok + Tamam No comment provided by engineer. Open the app to downgrade the database. + Veritabanının sürümünü düşürmek için uygulamayı açın. No comment provided by engineer. Open the app to upgrade the database. + Veritabanını güncellemek için uygulamayı açın. No comment provided by engineer. Passphrase + Parola No comment provided by engineer. Please create a profile in the SimpleX app + Lütfen SimpleX uygulamasında bir profil oluşturun No comment provided by engineer. Selected chat preferences prohibit this message. + Seçilen sohbet tercihleri bu mesajı yasakladı. No comment provided by engineer. Sending a message takes longer than expected. + Mesaj göndermek beklenenden daha uzun sürüyor. No comment provided by engineer. Sending message… + Mesaj gönderiliyor… No comment provided by engineer. Share + Paylaş No comment provided by engineer. Slow network? + Ağ yavaş mı? No comment provided by engineer. Unknown database error: %@ + Bilinmeyen veritabanı hatası: %@ No comment provided by engineer. Unsupported format + Desteklenmeyen format No comment provided by engineer. Wait + Bekleyin No comment provided by engineer. Wrong database passphrase + Yanlış veritabanı parolası No comment provided by engineer. You can allow sharing in Privacy & Security / SimpleX Lock settings. + Gizlilik ve Güvenlik / SimpleX Lock ayarlarından paylaşıma izin verebilirsiniz. No comment provided by engineer. 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 7bcb30c1db..ce37b43c23 100644 --- a/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff +++ b/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff @@ -137,6 +137,10 @@ %@ хоче підключитися! notification title + + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ та %lld учасників @@ -157,11 +161,31 @@ %d днів time interval + + %d file(s) are still being downloaded. + forward confirmation reason + + + %d file(s) failed to download. + forward confirmation reason + + + %d file(s) were deleted. + forward confirmation reason + + + %d file(s) were not downloaded. + forward confirmation reason + %d hours %d годин time interval + + %d messages not forwarded + alert title + %d min %d хв @@ -723,7 +747,7 @@ All profiles Всі профілі - No comment provided by engineer. + profile dropdown All your contacts will remain connected. @@ -915,6 +939,10 @@ Пароль програми замінено на пароль самознищення. No comment provided by engineer. + + App session + No comment provided by engineer. + App version Версія програми @@ -1020,6 +1048,10 @@ Автоматичне прийняття зображень No comment provided by engineer. + + Auto-accept settings + alert title + Back Назад @@ -1045,11 +1077,19 @@ Поганий хеш повідомлення No comment provided by engineer. + + Better calls + No comment provided by engineer. + Better groups Кращі групи No comment provided by engineer. + + Better message dates. + No comment provided by engineer. + Better messages Кращі повідомлення @@ -1060,6 +1100,18 @@ Краща мережа No comment provided by engineer. + + Better notifications + No comment provided by engineer. + + + Better security ✅ + No comment provided by engineer. + + + Better user experience + No comment provided by engineer. + Black Чорний @@ -1193,7 +1245,7 @@ Cancel Скасувати - No comment provided by engineer. + alert button Cancel migration @@ -1213,7 +1265,7 @@ Cannot receive file Не вдається отримати файл - No comment provided by engineer. + alert title Capacity exceeded - recipient did not receive previously sent messages. @@ -1341,6 +1393,15 @@ Налаштування чату No comment provided by engineer. + + Chat preferences were changed. + alert message + + + Chat profile + Профіль користувача + No comment provided by engineer. + Chat theme Тема чату @@ -1740,6 +1801,10 @@ This is your own one-time link! Основна версія: v%@ No comment provided by engineer. + + Corner + No comment provided by engineer. + Correct name to %@? Виправити ім'я на %@? @@ -1865,6 +1930,10 @@ This is your own one-time link! Індивідуальний час No comment provided by engineer. + + Customizable message shape. + No comment provided by engineer. + Customize theme Налаштувати тему @@ -2159,6 +2228,10 @@ This is your own one-time link! Видалити стару базу даних? No comment provided by engineer. + + Delete or moderate up to 200 messages. + No comment provided by engineer. + Delete pending connection? Видалити очікуване з'єднання? @@ -2409,6 +2482,10 @@ This is your own one-time link! Не надсилайте історію новим користувачам. No comment provided by engineer. + + Do not use credentials with proxy. + No comment provided by engineer. + Don't create address Не створювати адресу @@ -2432,7 +2509,8 @@ This is your own one-time link! Download Завантажити - chat item action + alert button + chat item action Download errors @@ -2449,6 +2527,10 @@ This is your own one-time link! Завантажити файл server test step + + Download files + alert action + Downloaded Завантажено @@ -2724,6 +2806,10 @@ This is your own one-time link! Помилка зміни адреси No comment provided by engineer. + + Error changing connection profile + No comment provided by engineer. + Error changing role Помилка зміни ролі @@ -2734,6 +2820,10 @@ This is your own one-time link! Помилка зміни налаштування No comment provided by engineer. + + Error changing to incognito! + No comment provided by engineer. + Error connecting to forwarding server %@. Please try later. Помилка підключення до сервера переадресації %@. Спробуйте пізніше. @@ -2854,6 +2944,10 @@ This is your own one-time link! Помилка завантаження %@ серверів No comment provided by engineer. + + Error migrating settings + No comment provided by engineer. + Error opening chat Помилка відкриття чату @@ -2862,7 +2956,7 @@ This is your own one-time link! Error receiving file Помилка отримання файлу - No comment provided by engineer. + alert title Error reconnecting server @@ -2954,10 +3048,14 @@ This is your own one-time link! Помилка зупинки чату No comment provided by engineer. + + Error switching profile + No comment provided by engineer. + Error switching profile! Помилка перемикання профілю! - No comment provided by engineer. + alertTitle Error synchronizing connection @@ -3002,8 +3100,7 @@ This is your own one-time link! Error: %@ Помилка: %@ - file error text - snd error text + alert message Error: URL is invalid @@ -3090,6 +3187,11 @@ This is your own one-time link! Помилка файлу No comment provided by engineer. + + File errors: +%@ + alert message + File not found - most likely file was deleted or cancelled. Файл не знайдено - найімовірніше, файл було видалено або скасовано. @@ -3225,11 +3327,27 @@ This is your own one-time link! Пересилання chat item action + + Forward %d message(s)? + alert title + Forward and save messages Пересилання та збереження повідомлень No comment provided by engineer. + + Forward messages + alert action + + + Forward messages without files? + alert message + + + Forward up to 20 messages at once. + No comment provided by engineer. + Forwarded Переслано @@ -3240,6 +3358,10 @@ This is your own one-time link! Переслано з No comment provided by engineer. + + Forwarding %lld messages + No comment provided by engineer. + Forwarding server %@ failed to connect to destination server %@. Please try later. Серверу переадресації %@ не вдалося з'єднатися з сервером призначення %@. Спробуйте пізніше. @@ -3289,11 +3411,6 @@ Error: %2$@ Повне ім'я (необов'язково) No comment provided by engineer. - - Full name: - Повне ім'я: - No comment provided by engineer. - Fully decentralized – visible only to members. Повністю децентралізована - видима лише для учасників. @@ -3539,6 +3656,10 @@ Error: %2$@ Сервери ICE (по одному на лінію) No comment provided by engineer. + + IP address + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. Якщо ви не можете зустрітися особисто, покажіть QR-код у відеодзвінку або поділіться посиланням. @@ -3614,6 +3735,11 @@ Error: %2$@ Імпорт архіву No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + No comment provided by engineer. + Improved message delivery Покращена доставка повідомлень @@ -4184,6 +4310,10 @@ This is your link for group %@! Сервери повідомлень No comment provided by engineer. + + Message shape + No comment provided by engineer. + Message source remains private. Джерело повідомлення залишається приватним. @@ -4234,6 +4364,10 @@ This is your link for group %@! Надіслані повідомлення No comment provided by engineer. + + Messages were deleted after you selected them. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. Повідомлення, файли та дзвінки захищені **наскрізним шифруванням** з ідеальною секретністю переадресації, відмовою та відновленням після злому. @@ -4389,6 +4523,14 @@ This is your link for group %@! Новий пароль No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + No comment provided by engineer. + New chat Новий чат @@ -4509,6 +4651,14 @@ This is your link for group %@! Немає підключення до мережі No comment provided by engineer. + + No permission to record speech + No comment provided by engineer. + + + No permission to record video + No comment provided by engineer. + No permission to record voice message Немає дозволу на запис голосового повідомлення @@ -4529,6 +4679,10 @@ This is your link for group %@! Нічого не вибрано No comment provided by engineer. + + Nothing to forward! + alert title + Notifications Сповіщення @@ -4561,7 +4715,7 @@ This is your link for group %@! Ok Гаразд - No comment provided by engineer. + alert button Old database @@ -4752,6 +4906,11 @@ Requires compatible VPN. Інші сервери %@ No comment provided by engineer. + + Other file errors: +%@ + alert message + PING count Кількість PING @@ -4787,6 +4946,10 @@ Requires compatible VPN. Пароль встановлено! No comment provided by engineer. + + Password + No comment provided by engineer. + Password to show Показати пароль @@ -4936,6 +5099,10 @@ Error: %@ Польський інтерфейс No comment provided by engineer. + + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect Можливо, в адресі сервера неправильно вказано відбиток сертифіката @@ -5021,16 +5188,6 @@ Error: %@ Зображення профілю No comment provided by engineer. - - Profile name - Назва профілю - No comment provided by engineer. - - - Profile name: - Ім'я профілю: - No comment provided by engineer. - Profile password Пароль до профілю @@ -5133,6 +5290,10 @@ Enable in *Network & servers* settings. Проксі-сервери No comment provided by engineer. + + Proxy requires password + No comment provided by engineer. + Push notifications Push-повідомлення @@ -5354,6 +5515,10 @@ Enable in *Network & servers* settings. Видалити No comment provided by engineer. + + Remove archive? + No comment provided by engineer. + Remove image Видалити зображення @@ -5534,6 +5699,10 @@ Enable in *Network & servers* settings. Сервер SMP No comment provided by engineer. + + SOCKS proxy + No comment provided by engineer. + Safely receive files Безпечне отримання файлів @@ -5547,12 +5716,13 @@ Enable in *Network & servers* settings. Save Зберегти - chat item action + alert button + chat item action Save (and notify contacts) Зберегти (і повідомити контактам) - No comment provided by engineer. + alert button Save and notify contact @@ -5579,11 +5749,6 @@ Enable in *Network & servers* settings. Зберегти архів No comment provided by engineer. - - Save auto-accept settings - Зберегти налаштування автоприйому - No comment provided by engineer. - Save group profile Зберегти профіль групи @@ -5619,16 +5784,15 @@ Enable in *Network & servers* settings. Зберегти сервери? No comment provided by engineer. - - Save settings? - Зберегти налаштування? - No comment provided by engineer. - Save welcome message? Зберегти вітальне повідомлення? No comment provided by engineer. + + Save your profile? + alert title + Saved Збережено @@ -5649,6 +5813,10 @@ Enable in *Network & servers* settings. Збережене повідомлення message info title + + Saving %lld messages + No comment provided by engineer. + Scale Масштаб @@ -5729,6 +5897,10 @@ Enable in *Network & servers* settings. Виберіть chat item action + + Select chat profile + No comment provided by engineer. + Selected %lld Вибрано %lld @@ -5847,7 +6019,7 @@ Enable in *Network & servers* settings. Sender cancelled file transfer. Відправник скасував передачу файлу. - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -5944,6 +6116,10 @@ Enable in *Network & servers* settings. Відправлено через проксі No comment provided by engineer. + + Server + No comment provided by engineer. + Server address Адреса сервера @@ -6064,6 +6240,10 @@ Enable in *Network & servers* settings. Налаштування No comment provided by engineer. + + Settings were changed. + alert message + Shape profile images Сформуйте зображення профілю @@ -6099,6 +6279,10 @@ Enable in *Network & servers* settings. Поділіться посиланням No comment provided by engineer. + + Share profile + No comment provided by engineer. + Share this 1-time invite link Поділіться цим одноразовим посиланням-запрошенням @@ -6234,6 +6418,10 @@ Enable in *Network & servers* settings. Одноразове запрошення SimpleX simplex link type + + SimpleX protocols reviewed by Trail of Bits. + No comment provided by engineer. + Simplified incognito mode Спрощений режим інкогніто @@ -6264,6 +6452,10 @@ Enable in *Network & servers* settings. М'який blur media + + Some app settings were not migrated. + No comment provided by engineer. + Some file(s) were not exported: Деякі файли не було експортовано: @@ -6404,6 +6596,14 @@ Enable in *Network & servers* settings. Підтримка чату SimpleX No comment provided by engineer. + + Switch audio and video during the call. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + No comment provided by engineer. + System Система @@ -6439,6 +6639,10 @@ Enable in *Network & servers* settings. TCP_KEEPINTVL No comment provided by engineer. + + Tail + No comment provided by engineer. + Take picture Сфотографуйте @@ -6631,6 +6835,10 @@ It can happen because of some bug or when the connection is compromised.Текст, який ви вставили, не є посиланням SimpleX. No comment provided by engineer. + + The uploaded database archive will be permanently removed from the servers. + No comment provided by engineer. + Themes Теми @@ -6758,6 +6966,14 @@ You will be prompted to complete authentication before this feature is enabled.< Перед увімкненням цієї функції вам буде запропоновано пройти автентифікацію. No comment provided by engineer. + + To record speech please grant permission to use Microphone. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. Щоб записати голосове повідомлення, будь ласка, надайте дозвіл на використання мікрофону. @@ -6911,7 +7127,7 @@ You will be prompted to complete authentication before this feature is enabled.< Unknown servers! Невідомі сервери! - No comment provided by engineer. + alert title Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. @@ -7025,6 +7241,10 @@ To connect, please ask your contact to create another connection link and check Використовуйте хости .onion No comment provided by engineer. + + Use SOCKS proxy + No comment provided by engineer. + Use SimpleX Chat servers? Використовувати сервери SimpleX Chat? @@ -7090,16 +7310,15 @@ To connect, please ask your contact to create another connection link and check Використовуйте додаток однією рукою. No comment provided by engineer. - - User profile - Профіль користувача - No comment provided by engineer. - User selection Вибір користувача No comment provided by engineer. + + Username + No comment provided by engineer. + Using SimpleX Chat servers. Використання серверів SimpleX Chat. @@ -7333,7 +7552,7 @@ To connect, please ask your contact to create another connection link and check Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. Без Tor або VPN ваша IP-адреса буде видимою для цих XFTP-ретрансляторів: %@. - No comment provided by engineer. + alert message Wrong database passphrase @@ -7360,11 +7579,6 @@ To connect, please ask your contact to create another connection link and check XFTP-сервер No comment provided by engineer. - - You - Ти - No comment provided by engineer. - You **must not** use the same database on two devices. Ви **не повинні використовувати** одну і ту ж базу даних на двох пристроях. @@ -7714,11 +7928,19 @@ Repeat connection request? Ваша база даних чату не зашифрована - встановіть ключову фразу, щоб зашифрувати її. No comment provided by engineer. + + Your chat preferences + alert title + Your chat profiles Ваші профілі чату No comment provided by engineer. + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). Ваш контакт надіслав файл, розмір якого перевищує підтримуваний на цей момент максимальний розмір (%@). @@ -7734,6 +7956,10 @@ Repeat connection request? Ваші контакти залишаться на зв'язку. No comment provided by engineer. + + Your credentials may be sent unencrypted. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. Ваша поточна база даних чату буде ВИДАЛЕНА і ЗАМІНЕНА імпортованою. @@ -7764,13 +7990,15 @@ Repeat connection request? Ваш профіль **%@** буде опублікований. No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - Ваш профіль зберігається на вашому пристрої і доступний лише вашим контактам. -Сервери SimpleX не бачать ваш профіль. + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + Ваш профіль зберігається на вашому пристрої і доступний лише вашим контактам. Сервери SimpleX не бачать ваш профіль. No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + alert message + Your profile, contacts and delivered messages are stored on your device. Ваш профіль, контакти та доставлені повідомлення зберігаються на вашому пристрої. 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 8c3641549d..91893dd939 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 @@ -109,6 +109,7 @@ %@ downloaded + %@ 已下载 No comment provided by engineer. @@ -128,6 +129,7 @@ %@ uploaded + %@ 已上传 No comment provided by engineer. @@ -135,6 +137,10 @@ %@ 要连接! notification title + + %1$@, %2$@ + format for date separator in chat + %@, %@ and %lld members %@, %@ 和 %lld 成员 @@ -155,11 +161,31 @@ %d 天 time interval + + %d file(s) are still being downloaded. + forward confirmation reason + + + %d file(s) failed to download. + forward confirmation reason + + + %d file(s) were deleted. + forward confirmation reason + + + %d file(s) were not downloaded. + forward confirmation reason + %d hours %d 小时 time interval + + %d messages not forwarded + alert title + %d min %d 分钟 @@ -222,14 +248,17 @@ %lld messages blocked by admin + %lld 被管理员阻止的消息 No comment provided by engineer. %lld messages marked deleted + %lld 标记为已删除的消息 No comment provided by engineer. %lld messages moderated by %@ + %lld 审核的留言 by %@ No comment provided by engineer. @@ -304,10 +333,12 @@ (new) + (新) No comment provided by engineer. (this device v%@) + (此设备 v%@) No comment provided by engineer. @@ -317,6 +348,7 @@ **Add contact**: to create a new invitation link, or connect via a link you received. + **添加联系人**: 创建新的邀请链接,或通过您收到的链接进行连接. No comment provided by engineer. @@ -326,6 +358,7 @@ **Create group**: to create a new group. + **创建群组**: 创建一个新群组. No comment provided by engineer. @@ -340,6 +373,7 @@ **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. + **请注意**: 在两台设备上使用相同的数据库将破坏来自您的连接的消息解密,作为一种安全保护. No comment provided by engineer. @@ -359,6 +393,7 @@ **Warning**: the archive will be removed. + **警告**: 存档将被删除. No comment provided by engineer. @@ -424,6 +459,7 @@ 0 sec + 0 秒 time to disappear @@ -542,6 +578,7 @@ Accent + 强调 No comment provided by engineer. @@ -569,14 +606,17 @@ Acknowledged + 确认 No comment provided by engineer. Acknowledgement errors + 确认错误 No comment provided by engineer. Active connections + 活动连接 No comment provided by engineer. @@ -621,14 +661,17 @@ Additional accent + 附加重音 No comment provided by engineer. Additional accent 2 + 附加重音 2 No comment provided by engineer. Additional secondary + 附加二级 No comment provided by engineer. @@ -658,6 +701,7 @@ Advanced settings + 高级设置 No comment provided by engineer. @@ -677,6 +721,7 @@ All data is private to your device. + 所有数据都是您设备的私有数据. No comment provided by engineer. @@ -696,11 +741,13 @@ All new messages from %@ will be hidden! + 来自 %@ 的所有新消息都将被隐藏! No comment provided by engineer. All profiles - No comment provided by engineer. + 所有配置文件 + profile dropdown All your contacts will remain connected. @@ -729,6 +776,7 @@ Allow calls? + 允许通话? No comment provided by engineer. @@ -738,11 +786,12 @@ Allow downgrade + 允许降级 No comment provided by engineer. Allow irreversible message deletion only if your contact allows it to you. (24 hours) - 仅有您的联系人许可后才允许不可撤回消息移除。 + 仅有您的联系人许可后才允许不可撤回消息移除 No comment provided by engineer. @@ -767,11 +816,12 @@ Allow sharing + 允许共享 No comment provided by engineer. Allow to irreversibly delete sent messages. (24 hours) - 允许不可撤回地删除已发送消息。 + 允许不可撤回地删除已发送消息 No comment provided by engineer. @@ -811,7 +861,7 @@ Allow your contacts to irreversibly delete sent messages. (24 hours) - 允许您的联系人不可撤回地删除已发送消息。 + 允许您的联系人不可撤回地删除已发送消息 No comment provided by engineer. @@ -841,6 +891,7 @@ Always use private routing. + 始终使用私有路由。 No comment provided by engineer. @@ -888,6 +939,10 @@ 应用程序密码被替换为自毁密码。 No comment provided by engineer. + + App session + No comment provided by engineer. + App version 应用程序版本 @@ -910,6 +965,7 @@ Apply to + 应用于 No comment provided by engineer. @@ -919,10 +975,12 @@ Archive contacts to chat later. + 存档联系人以便稍后聊天. No comment provided by engineer. Archived contacts + 已存档的联系人 No comment provided by engineer. @@ -990,6 +1048,10 @@ 自动接受图片 No comment provided by engineer. + + Auto-accept settings + alert title + Back 返回 @@ -997,6 +1059,7 @@ Background + 背景 No comment provided by engineer. @@ -1014,11 +1077,19 @@ 错误消息散列 No comment provided by engineer. + + Better calls + No comment provided by engineer. + Better groups 更佳的群组 No comment provided by engineer. + + Better message dates. + No comment provided by engineer. + Better messages 更好的消息 @@ -1026,10 +1097,24 @@ Better networking + 更好的网络 + No comment provided by engineer. + + + Better notifications + No comment provided by engineer. + + + Better security ✅ + No comment provided by engineer. + + + Better user experience No comment provided by engineer. Black + 黑色 No comment provided by engineer. @@ -1069,10 +1154,12 @@ Blur for better privacy. + 模糊处理,提高私密性. No comment provided by engineer. Blur media + 模糊媒体 No comment provided by engineer. @@ -1082,7 +1169,7 @@ Both you and your contact can irreversibly delete sent messages. (24 hours) - 您和您的联系人都可以不可逆转地删除已发送的消息。 + 您和您的联系人都可以不可逆转地删除已发送的消息 No comment provided by engineer. @@ -1122,6 +1209,7 @@ Calls prohibited! + 禁止来电! No comment provided by engineer. @@ -1131,10 +1219,12 @@ Can't call contact + 无法呼叫联系人 No comment provided by engineer. Can't call member + 无法呼叫成员 No comment provided by engineer. @@ -1149,12 +1239,13 @@ Can't message member + 无法向成员发送消息 No comment provided by engineer. Cancel 取消 - No comment provided by engineer. + alert button Cancel migration @@ -1168,15 +1259,17 @@ Cannot forward message + 无法转发消息 No comment provided by engineer. Cannot receive file 无法接收文件 - No comment provided by engineer. + alert title Capacity exceeded - recipient did not receive previously sent messages. + 超出容量-收件人未收到以前发送的邮件。 snd error text @@ -1242,6 +1335,7 @@ Chat colors + 聊天颜色 No comment provided by engineer. @@ -1261,6 +1355,7 @@ Chat database exported + 导出的聊天数据库 No comment provided by engineer. @@ -1285,6 +1380,7 @@ Chat list + 聊天列表 No comment provided by engineer. @@ -1297,8 +1393,18 @@ 聊天偏好设置 No comment provided by engineer. + + Chat preferences were changed. + alert message + + + Chat profile + 用户资料 + No comment provided by engineer. + Chat theme + 聊天主题 No comment provided by engineer. @@ -1318,6 +1424,7 @@ Choose _Migrate from another device_ on the new device and scan QR code. + 在新设备上选择“从另一个设备迁移”并扫描二维码。 No comment provided by engineer. @@ -1332,14 +1439,17 @@ Chunks deleted + 已删除的块 No comment provided by engineer. Chunks downloaded + 下载的块 No comment provided by engineer. Chunks uploaded + 已下载的区块 No comment provided by engineer. @@ -1369,10 +1479,12 @@ Color chats with the new themes. + 使用新主题为聊天着色。 No comment provided by engineer. Color mode + 颜色模式 No comment provided by engineer. @@ -1387,6 +1499,7 @@ Completed + 已完成 No comment provided by engineer. @@ -1396,6 +1509,7 @@ Configured %@ servers + 已配置 %@ 服务器 No comment provided by engineer. @@ -1410,6 +1524,7 @@ Confirm contact deletion? + 确认删除联系人? No comment provided by engineer. @@ -1419,6 +1534,7 @@ Confirm files from unknown servers. + 确认来自未知服务器的文件。 No comment provided by engineer. @@ -1468,6 +1584,7 @@ Connect to your friends faster. + 更快地与您的朋友联系。 No comment provided by engineer. @@ -1478,15 +1595,20 @@ Connect to yourself? This is your own SimpleX address! + 与自己建立联系? +这是您自己的 SimpleX 地址! No comment provided by engineer. Connect to yourself? This is your own one-time link! + 与自己建立联系? +这是您自己的一次性链接! No comment provided by engineer. Connect via contact address + 通过联系地址连接 No comment provided by engineer. @@ -1501,10 +1623,12 @@ This is your own one-time link! Connect with %@ + 与 %@连接 No comment provided by engineer. Connected + 已连接 No comment provided by engineer. @@ -1514,6 +1638,7 @@ This is your own one-time link! Connected servers + 已连接的服务器 No comment provided by engineer. @@ -1523,6 +1648,7 @@ This is your own one-time link! Connecting + 正在连接 No comment provided by engineer. @@ -1537,6 +1663,7 @@ This is your own one-time link! Connecting to contact, please wait or check later! + 正在连接到联系人,请稍候或稍后检查! No comment provided by engineer. @@ -1551,6 +1678,7 @@ This is your own one-time link! Connection and servers status. + 连接和服务器状态。 No comment provided by engineer. @@ -1565,6 +1693,7 @@ This is your own one-time link! Connection notifications + 连接通知 No comment provided by engineer. @@ -1584,10 +1713,12 @@ This is your own one-time link! Connection with desktop stopped + 与桌面的连接已停止 No comment provided by engineer. Connections + 连接 No comment provided by engineer. @@ -1602,6 +1733,7 @@ This is your own one-time link! Contact deleted! + 联系人已删除! No comment provided by engineer. @@ -1616,6 +1748,7 @@ This is your own one-time link! Contact is deleted. + 联系人被删除。 No comment provided by engineer. @@ -1630,6 +1763,7 @@ This is your own one-time link! Contact will be deleted - this cannot be undone! + 联系人将被删除-这是无法撤消的! No comment provided by engineer. @@ -1649,6 +1783,7 @@ This is your own one-time link! Conversation deleted! + 对话已删除! No comment provided by engineer. @@ -1658,6 +1793,7 @@ This is your own one-time link! Copy error + 复制错误 No comment provided by engineer. @@ -1665,8 +1801,13 @@ This is your own one-time link! 核心版本: v%@ No comment provided by engineer. + + Corner + No comment provided by engineer. + Correct name to %@? + 将名称更正为 %@? No comment provided by engineer. @@ -1681,7 +1822,7 @@ This is your own one-time link! Create a group using a random profile. - 使用随机身份创建群组 + 使用随机身份创建群组. No comment provided by engineer. @@ -1736,6 +1877,7 @@ This is your own one-time link! Created + 已创建 No comment provided by engineer. @@ -1745,6 +1887,7 @@ This is your own one-time link! Created at: %@ + 创建于:%@ copied message info @@ -1774,6 +1917,7 @@ This is your own one-time link! Current profile + 当前配置文件 No comment provided by engineer. @@ -1786,8 +1930,13 @@ This is your own one-time link! 自定义时间 No comment provided by engineer. + + Customizable message shape. + No comment provided by engineer. + Customize theme + 自定义主题 No comment provided by engineer. @@ -1797,6 +1946,7 @@ This is your own one-time link! Dark mode colors + 深色模式颜色 No comment provided by engineer. @@ -1899,6 +2049,7 @@ This is your own one-time link! Debug delivery + 调试交付 No comment provided by engineer. @@ -1919,10 +2070,12 @@ This is your own one-time link! Delete %lld messages of members? + 删除成员的 %lld 消息? No comment provided by engineer. Delete %lld messages? + 删除 %lld 消息? No comment provided by engineer. @@ -1982,6 +2135,7 @@ This is your own one-time link! Delete contact? + 删除联系人? No comment provided by engineer. @@ -2074,6 +2228,10 @@ This is your own one-time link! 删除旧数据库吗? No comment provided by engineer. + + Delete or moderate up to 200 messages. + No comment provided by engineer. + Delete pending connection? 删除待定连接? @@ -2091,6 +2249,7 @@ This is your own one-time link! Delete up to 20 messages at once. + 一次最多删除 20 条信息。 No comment provided by engineer. @@ -2100,10 +2259,12 @@ This is your own one-time link! Delete without notification + 删除而不通知 No comment provided by engineer. Deleted + 已删除 No comment provided by engineer. @@ -2118,6 +2279,7 @@ This is your own one-time link! Deletion errors + 删除错误 No comment provided by engineer. @@ -2147,6 +2309,7 @@ This is your own one-time link! Desktop app version %@ is not compatible with this app. + 桌面应用程序版本 %@ 与此应用程序不兼容。 No comment provided by engineer. @@ -2156,22 +2319,27 @@ This is your own one-time link! 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. Details + 详细信息 No comment provided by engineer. @@ -2181,6 +2349,7 @@ This is your own one-time link! Developer options + 开发者选项 No comment provided by engineer. @@ -2235,6 +2404,7 @@ This is your own one-time link! Disabled + 禁用 No comment provided by engineer. @@ -2289,6 +2459,7 @@ This is your own one-time link! Do NOT send messages directly, even if your or destination server does not support private routing. + 请勿直接发送消息,即使您的服务器或目标服务器不支持私有路由。 No comment provided by engineer. @@ -2298,6 +2469,7 @@ This is your own one-time link! Do NOT use private routing. + 不要使用私有路由。 No comment provided by engineer. @@ -2310,6 +2482,10 @@ This is your own one-time link! 不给新成员发送历史消息。 No comment provided by engineer. + + Do not use credentials with proxy. + No comment provided by engineer. + Don't create address 不创建地址 @@ -2333,10 +2509,12 @@ This is your own one-time link! Download 下载 - chat item action + alert button + chat item action Download errors + 下载错误 No comment provided by engineer. @@ -2349,12 +2527,18 @@ This is your own one-time link! 下载文件 server test step + + Download files + alert action + Downloaded + 已下载 No comment provided by engineer. Downloaded files + 下载的文件 No comment provided by engineer. @@ -2459,6 +2643,7 @@ This is your own one-time link! Enabled + 已启用 No comment provided by engineer. @@ -2498,6 +2683,7 @@ This is your own one-time link! Encrypted message: app is stopped + 加密消息:应用程序已停止 notification @@ -2547,6 +2733,7 @@ This is your own one-time link! Enter group name… + 输入组名称… No comment provided by engineer. @@ -2586,6 +2773,7 @@ This is your own one-time link! Enter your name… + 请输入您的姓名… No comment provided by engineer. @@ -2618,6 +2806,10 @@ This is your own one-time link! 更改地址错误 No comment provided by engineer. + + Error changing connection profile + No comment provided by engineer. + Error changing role 更改角色错误 @@ -2628,8 +2820,13 @@ This is your own one-time link! 更改设置错误 No comment provided by engineer. + + Error changing to incognito! + No comment provided by engineer. + Error connecting to forwarding server %@. Please try later. + 连接到转发服务器 %@ 时出错。请稍后尝试。 No comment provided by engineer. @@ -2729,6 +2926,7 @@ This is your own one-time link! Error exporting theme: %@ + 导出主题时出错: %@ No comment provided by engineer. @@ -2746,21 +2944,28 @@ This is your own one-time link! 加载 %@ 服务器错误 No comment provided by engineer. + + Error migrating settings + No comment provided by engineer. + Error opening chat + 打开聊天时出错 No comment provided by engineer. Error receiving file 接收文件错误 - No comment provided by engineer. + alert title Error reconnecting server + 重新连接服务器时出错 No comment provided by engineer. Error reconnecting servers + 重新连接服务器时出错 No comment provided by engineer. @@ -2770,6 +2975,7 @@ This is your own one-time link! Error resetting statistics + 重置统计信息时出错 No comment provided by engineer. @@ -2809,6 +3015,7 @@ This is your own one-time link! Error scanning code: %@ + 扫描代码时出错:%@ No comment provided by engineer. @@ -2841,10 +3048,14 @@ This is your own one-time link! 停止聊天错误 No comment provided by engineer. + + Error switching profile + No comment provided by engineer. + Error switching profile! 切换资料错误! - No comment provided by engineer. + alertTitle Error synchronizing connection @@ -2889,8 +3100,7 @@ This is your own one-time link! Error: %@ 错误: %@ - file error text - snd error text + alert message Error: URL is invalid @@ -2904,6 +3114,7 @@ This is your own one-time link! Errors + 错误 No comment provided by engineer. @@ -2933,6 +3144,7 @@ This is your own one-time link! Export theme + 导出主题 No comment provided by engineer. @@ -2972,22 +3184,32 @@ This is your own one-time link! File error + 文件错误 No comment provided by engineer. + + File errors: +%@ + alert message + File not found - most likely file was deleted or cancelled. + 找不到文件 - 很可能文件已被删除或取消。 file error text File server error: %@ + 文件服务器错误:%@ file error text File status + 文件状态 No comment provided by engineer. File status: %@ + 文件状态:%@ copied message info @@ -3012,6 +3234,7 @@ This is your own one-time link! Files + 文件 No comment provided by engineer. @@ -3051,7 +3274,7 @@ This is your own one-time link! Finalize migration on another device. - 在另一部设备上完成迁移 + 在另一部设备上完成迁移. No comment provided by engineer. @@ -3104,11 +3327,27 @@ This is your own one-time link! 转发 chat item action + + Forward %d message(s)? + alert title + Forward and save messages 转发并保存消息 No comment provided by engineer. + + Forward messages + alert action + + + Forward messages without files? + alert message + + + Forward up to 20 messages at once. + No comment provided by engineer. + Forwarded 已转发 @@ -3119,26 +3358,37 @@ This is your own one-time link! 转发自 No comment provided by engineer. + + Forwarding %lld messages + 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$@ + 转发服务器: %1$@ +目标服务器错误: %2$@ snd error text Forwarding server: %1$@ Error: %2$@ + 转发服务器: %1$@ +错误: %2$@ snd error text @@ -3161,11 +3411,6 @@ Error: %2$@ 全名(可选) No comment provided by engineer. - - Full name: - 全名: - No comment provided by engineer. - Fully decentralized – visible only to members. 完全去中心化 - 仅对成员可见。 @@ -3188,10 +3433,12 @@ Error: %2$@ Good afternoon! + 下午好! message preview Good morning! + 早上好! message preview @@ -3201,6 +3448,7 @@ Error: %2$@ Group already exists + 群组已存在 No comment provided by engineer. @@ -3255,7 +3503,7 @@ Error: %2$@ Group members can irreversibly delete sent messages. (24 hours) - 群组成员可以不可撤回地删除已发送的消息。 + 群组成员可以不可撤回地删除已发送的消息 No comment provided by engineer. @@ -3400,6 +3648,7 @@ Error: %2$@ Hungarian interface + 匈牙利语界面 No comment provided by engineer. @@ -3407,6 +3656,10 @@ Error: %2$@ ICE 服务器(每行一个) No comment provided by engineer. + + IP address + No comment provided by engineer. + If you can't meet in person, show QR code in a video call, or share the link. 如果您不能亲自见面,可以在视频通话中展示二维码,或分享链接。 @@ -3474,6 +3727,7 @@ Error: %2$@ Import theme + 导入主题 No comment provided by engineer. @@ -3481,6 +3735,11 @@ Error: %2$@ 正在导入存档 No comment provided by engineer. + + Improved delivery, reduced traffic usage. +More improvements are coming soon! + No comment provided by engineer. + Improved message delivery 改进了消息传递 @@ -3600,6 +3859,7 @@ Error: %2$@ Interface colors + 界面颜色 No comment provided by engineer. @@ -3634,6 +3894,7 @@ Error: %2$@ Invalid response + 无效的响应 No comment provided by engineer. @@ -3704,6 +3965,7 @@ Error: %2$@ It protects your IP address and connections. + 它可以保护您的 IP 地址和连接。 No comment provided by engineer. @@ -3748,11 +4010,14 @@ Error: %2$@ Join with current profile + 使用当前档案加入 No comment provided by engineer. Join your group? This is your link for group %@! + 加入您的群组? +这是您组 %@ 的链接! No comment provided by engineer. @@ -3767,10 +4032,12 @@ This is your link for group %@! Keep conversation + 保持对话 No comment provided by engineer. Keep the app open to use it from desktop + 保持应用程序打开状态以从桌面使用它 No comment provided by engineer. @@ -3945,10 +4212,12 @@ This is your link for group %@! Media & file servers + Media & file servers No comment provided by engineer. Medium + 中等 blur media @@ -3958,6 +4227,7 @@ This is your link for group %@! Member inactive + 成员不活跃 item status text @@ -3977,6 +4247,7 @@ This is your link for group %@! Menus + 菜单 No comment provided by engineer. @@ -3991,6 +4262,7 @@ This is your link for group %@! Message delivery warning + 消息传递警告 item status text @@ -4000,14 +4272,17 @@ This is your link for group %@! Message forwarded + 消息已转发 item status text Message may be delivered later if member becomes active. + 如果 member 变为活动状态,则稍后可能会发送消息。 item status description Message queue info + 消息队列信息 No comment provided by engineer. @@ -4027,10 +4302,16 @@ This is your link for group %@! Message reception + 消息接收 No comment provided by engineer. Message servers + 消息服务器 + No comment provided by engineer. + + + Message shape No comment provided by engineer. @@ -4040,10 +4321,12 @@ This is your link for group %@! Message status + 消息状态 No comment provided by engineer. Message status: %@ + 消息状态:%@ copied message info @@ -4068,22 +4351,31 @@ This is your link for group %@! Messages from %@ will be shown! + 将显示来自 %@ 的消息! No comment provided by engineer. Messages received + 收到的消息 No comment provided by engineer. Messages sent + 已发送的消息 No comment provided by engineer. + + Messages were deleted after you selected them. + alert message + Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery. + 消息、文件和通话受到 **端到端加密** 的保护,具有完全正向保密、否认和闯入恢复。 No comment provided by engineer. Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery. + 消息、文件和通话受到 **抗量子 e2e 加密** 的保护,具有完全正向保密、否认和闯入恢复。 No comment provided by engineer. @@ -4208,6 +4500,7 @@ This is your link for group %@! Network issues - message expired after many attempts to send it. + 网络问题 - 消息在多次尝试发送后过期。 snd error text @@ -4230,6 +4523,14 @@ This is your link for group %@! 新密码 No comment provided by engineer. + + New SOCKS credentials will be used every time you start the app. + No comment provided by engineer. + + + New SOCKS credentials will be used for each server. + No comment provided by engineer. + New chat 新聊天 @@ -4237,6 +4538,7 @@ This is your link for group %@! New chat experience 🎉 + 新的聊天体验 🎉 No comment provided by engineer. @@ -4271,6 +4573,7 @@ This is your link for group %@! New media options + 新媒体选项 No comment provided by engineer. @@ -4320,6 +4623,7 @@ This is your link for group %@! No direct connection yet, message is forwarded by admin. + 还没有直接连接,消息由管理员转发。 item status description @@ -4339,6 +4643,7 @@ This is your link for group %@! No info, try to reload + 无信息,尝试重新加载 No comment provided by engineer. @@ -4346,6 +4651,14 @@ This is your link for group %@! 无网络连接 No comment provided by engineer. + + No permission to record speech + No comment provided by engineer. + + + No permission to record video + No comment provided by engineer. + No permission to record voice message 没有录制语音消息的权限 @@ -4363,8 +4676,13 @@ This is your link for group %@! Nothing selected + 未选中任何内容 No comment provided by engineer. + + Nothing to forward! + alert title + Notifications 通知 @@ -4397,7 +4715,7 @@ This is your link for group %@! Ok 好的 - No comment provided by engineer. + alert button Old database @@ -4417,13 +4735,15 @@ This is your link for group %@! Onion hosts will be **required** for connection. Requires compatible VPN. - Onion 主机将用于连接。需要启用 VPN。 + Onion 主机将是连接所必需的。 +需要兼容的 VPN。 No comment provided by engineer. Onion hosts will be used when available. Requires compatible VPN. - 当可用时,将使用 Onion 主机。需要启用 VPN。 + 如果可用,将使用洋葱主机。 +需要兼容的 VPN。 No comment provided by engineer. @@ -4438,6 +4758,7 @@ Requires compatible VPN. Only delete conversation + 仅删除对话 No comment provided by engineer. @@ -4462,7 +4783,7 @@ Requires compatible VPN. Only you can irreversibly delete messages (your contact can mark them for deletion). (24 hours) - 只有您可以不可撤回地删除消息(您的联系人可以将它们标记为删除)。 + 只有您可以不可撤回地删除消息(您的联系人可以将它们标记为删除) No comment provided by engineer. @@ -4487,7 +4808,7 @@ Requires compatible VPN. Only your contact can irreversibly delete messages (you can mark them for deletion). (24 hours) - 只有您的联系人才能不可撤回地删除消息(您可以将它们标记为删除)。 + 只有您的联系人才能不可撤回地删除消息(您可以将它们标记为删除) No comment provided by engineer. @@ -4532,10 +4853,12 @@ Requires compatible VPN. Open migration to another device + 打开迁移到另一台设备 authentication reason Open server settings + 打开服务器设置 No comment provided by engineer. @@ -4550,6 +4873,7 @@ Requires compatible VPN. Opening app… + 正在打开应用程序… No comment provided by engineer. @@ -4579,8 +4903,14 @@ Requires compatible VPN. Other %@ servers + 其他 %@ 服务器 No comment provided by engineer. + + Other file errors: +%@ + alert message + PING count PING 次数 @@ -4616,6 +4946,10 @@ Requires compatible VPN. 密码已设置! No comment provided by engineer. + + Password + No comment provided by engineer. + Password to show 显示密码 @@ -4623,6 +4957,7 @@ Requires compatible VPN. Past member %@ + 前任成员 %@ past/unknown group member @@ -4647,6 +4982,7 @@ Requires compatible VPN. Pending + 待定 No comment provided by engineer. @@ -4671,10 +5007,12 @@ Requires compatible VPN. Play from the chat list. + 从聊天列表播放。 No comment provided by engineer. Please ask your contact to enable calls. + 请要求您的联系人开通通话功能。 No comment provided by engineer. @@ -4685,6 +5023,8 @@ Requires compatible VPN. 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. + 请检查移动设备和桌面是否连接到同一本地网络,以及桌面防火墙是否允许连接。 +请与开发人员分享任何其他问题。 No comment provided by engineer. @@ -4710,6 +5050,8 @@ Please share any other issues with the developers. Please contact developers. Error: %@ + 请联系开发人员。 +错误:%@ No comment provided by engineer. @@ -4757,6 +5099,10 @@ Error: %@ 波兰语界面 No comment provided by engineer. + + Port + No comment provided by engineer. + Possibly, certificate fingerprint in server address is incorrect 服务器地址中的证书指纹可能不正确 @@ -4784,6 +5130,7 @@ Error: %@ Previously connected servers + 以前连接的服务器 No comment provided by engineer. @@ -4803,10 +5150,12 @@ Error: %@ Private message routing + 私有消息路由 No comment provided by engineer. Private message routing 🚀 + 私有消息路由 🚀 No comment provided by engineer. @@ -4816,10 +5165,12 @@ Error: %@ Private routing + 专用路由 No comment provided by engineer. Private routing error + 专用路由错误 No comment provided by engineer. @@ -4837,15 +5188,6 @@ Error: %@ 个人资料图 No comment provided by engineer. - - Profile name - No comment provided by engineer. - - - Profile name: - 显示名: - No comment provided by engineer. - Profile password 个人资料密码 @@ -4853,6 +5195,7 @@ Error: %@ Profile theme + 个人资料主题 No comment provided by engineer. @@ -4882,6 +5225,7 @@ Error: %@ Prohibit sending SimpleX links. + 禁止发送 SimpleX 链接。 No comment provided by engineer. @@ -4906,6 +5250,7 @@ Error: %@ Protect IP address + 保护 IP 地址 No comment provided by engineer. @@ -4916,6 +5261,8 @@ Error: %@ Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings. + 保护您的 IP 地址免受联系人选择的消息中继的攻击。 +在*网络和服务器*设置中启用。 No comment provided by engineer. @@ -4935,10 +5282,16 @@ Enable in *Network & servers* settings. Proxied + 代理 No comment provided by engineer. Proxied servers + 代理服务器 + No comment provided by engineer. + + + Proxy requires password No comment provided by engineer. @@ -4948,6 +5301,7 @@ Enable in *Network & servers* settings. Push server + 推送服务器 No comment provided by engineer. @@ -4962,6 +5316,7 @@ Enable in *Network & servers* settings. Reachable chat toolbar + 可访问的聊天工具栏 No comment provided by engineer. @@ -5011,6 +5366,7 @@ Enable in *Network & servers* settings. Receive errors + 接收错误 No comment provided by engineer. @@ -5035,14 +5391,17 @@ Enable in *Network & servers* settings. Received messages + 收到的消息 No comment provided by engineer. Received reply + 已收到回复 No comment provided by engineer. Received total + 接收总数 No comment provided by engineer. @@ -5062,6 +5421,7 @@ Enable in *Network & servers* settings. Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). + 最近的历史记录和改进的 [目录机器人](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion). No comment provided by engineer. @@ -5076,6 +5436,7 @@ Enable in *Network & servers* settings. Reconnect + 重新连接 No comment provided by engineer. @@ -5085,18 +5446,22 @@ Enable in *Network & servers* settings. Reconnect all servers + 重新连接所有服务器 No comment provided by engineer. Reconnect all servers? + 重新连接所有服务器? No comment provided by engineer. Reconnect server to force message delivery. It uses additional traffic. + 重新连接服务器以强制发送信息。它使用额外的流量。 No comment provided by engineer. Reconnect server? + 重新连接服务器? No comment provided by engineer. @@ -5150,8 +5515,13 @@ Enable in *Network & servers* settings. 移除 No comment provided by engineer. + + Remove archive? + No comment provided by engineer. + Remove image + 移除图片 No comment provided by engineer. @@ -5226,14 +5596,17 @@ Enable in *Network & servers* settings. Reset all hints + 重置所有提示 No comment provided by engineer. Reset all statistics + 重置所有统计信息 No comment provided by engineer. Reset all statistics? + 重置所有统计信息? No comment provided by engineer. @@ -5243,6 +5616,7 @@ Enable in *Network & servers* settings. Reset to app theme + 重置为应用程序主题 No comment provided by engineer. @@ -5252,6 +5626,7 @@ Enable in *Network & servers* settings. Reset to user theme + 重置为用户主题 No comment provided by engineer. @@ -5321,10 +5696,16 @@ Enable in *Network & servers* settings. SMP server + SMP 服务器 + No comment provided by engineer. + + + SOCKS proxy No comment provided by engineer. Safely receive files + 安全接收文件 No comment provided by engineer. @@ -5335,12 +5716,13 @@ Enable in *Network & servers* settings. Save 保存 - chat item action + alert button + chat item action Save (and notify contacts) 保存(并通知联系人) - No comment provided by engineer. + alert button Save and notify contact @@ -5354,6 +5736,7 @@ Enable in *Network & servers* settings. Save and reconnect + 保存并重新连接 No comment provided by engineer. @@ -5366,11 +5749,6 @@ Enable in *Network & servers* settings. 保存存档 No comment provided by engineer. - - Save auto-accept settings - 保存自动接受设置 - No comment provided by engineer. - Save group profile 保存群组资料 @@ -5406,16 +5784,15 @@ Enable in *Network & servers* settings. 保存服务器? No comment provided by engineer. - - Save settings? - 保存设置? - No comment provided by engineer. - Save welcome message? 保存欢迎信息? No comment provided by engineer. + + Save your profile? + alert title + Saved 已保存 @@ -5436,12 +5813,18 @@ Enable in *Network & servers* settings. 已保存的消息 message info title + + Saving %lld messages + No comment provided by engineer. + Scale + 规模 No comment provided by engineer. Scan / Paste link + 扫描 / 粘贴链接 No comment provided by engineer. @@ -5486,6 +5869,7 @@ Enable in *Network & servers* settings. Secondary + 二级 No comment provided by engineer. @@ -5495,6 +5879,7 @@ Enable in *Network & servers* settings. Secured + 担保 No comment provided by engineer. @@ -5512,12 +5897,18 @@ Enable in *Network & servers* settings. 选择 chat item action + + Select chat profile + No comment provided by engineer. + Selected %lld + 选定的 %lld No comment provided by engineer. Selected chat preferences prohibit this message. + 选定的聊天首选项禁止此消息。 No comment provided by engineer. @@ -5567,6 +5958,7 @@ Enable in *Network & servers* settings. Send errors + 发送错误 No comment provided by engineer. @@ -5581,14 +5973,17 @@ Enable in *Network & servers* settings. Send message to enable calls. + 发送消息以启用呼叫。 No comment provided by engineer. Send messages directly when IP address is protected and your or destination server does not support private routing. + 当 IP 地址受到保护并且您或目标服务器不支持私有路由时,直接发送消息。 No comment provided by engineer. Send messages directly when your or destination server does not support private routing. + 当您或目标服务器不支持私有路由时,直接发送消息。 No comment provided by engineer. @@ -5624,7 +6019,7 @@ Enable in *Network & servers* settings. Sender cancelled file transfer. 发送人已取消文件传输。 - No comment provided by engineer. + alert message Sender may have deleted the connection request. @@ -5683,6 +6078,7 @@ Enable in *Network & servers* settings. Sent directly + 直接发送 No comment provided by engineer. @@ -5697,6 +6093,7 @@ Enable in *Network & servers* settings. Sent messages + 已发送的消息 No comment provided by engineer. @@ -5706,26 +6103,36 @@ Enable in *Network & servers* settings. Sent reply + 已发送回复 No comment provided by engineer. Sent total + 发送总数 No comment provided by engineer. Sent via proxy + 通过代理发送 + No comment provided by engineer. + + + Server No comment provided by engineer. Server address + 服务器地址 No comment provided by engineer. Server address is incompatible with network settings. + 服务器地址与网络设置不兼容。 srv error text. Server address is incompatible with network settings: %@. + 服务器地址与网络设置不兼容:%@。 No comment provided by engineer. @@ -5745,14 +6152,17 @@ Enable in *Network & servers* settings. Server type + 服务器类型 No comment provided by engineer. Server version is incompatible with network settings. + 服务器版本与网络设置不兼容。 srv error text Server version is incompatible with your app: %@. + 服务器版本与你的应用程序不兼容:%@。 No comment provided by engineer. @@ -5762,10 +6172,12 @@ Enable in *Network & servers* settings. Servers info + 服务器信息 No comment provided by engineer. Servers statistics will be reset - this cannot be undone! + 服务器统计信息将被重置 - 此操作无法撤消! No comment provided by engineer. @@ -5785,6 +6197,7 @@ Enable in *Network & servers* settings. Set default theme + 设置默认主题 No comment provided by engineer. @@ -5827,6 +6240,10 @@ Enable in *Network & servers* settings. 设置 No comment provided by engineer. + + Settings were changed. + alert message + Shape profile images 改变个人资料图形状 @@ -5854,6 +6271,7 @@ Enable in *Network & servers* settings. Share from other apps. + 从其他应用程序共享。 No comment provided by engineer. @@ -5861,6 +6279,10 @@ Enable in *Network & servers* settings. 分享链接 No comment provided by engineer. + + Share profile + No comment provided by engineer. + Share this 1-time invite link 分享此一次性邀请链接 @@ -5868,6 +6290,7 @@ Enable in *Network & servers* settings. Share to SimpleX + 分享到 SimpleX No comment provided by engineer. @@ -5897,10 +6320,12 @@ Enable in *Network & servers* settings. Show message status + 显示消息状态 No comment provided by engineer. Show percentage + 显示百分比 No comment provided by engineer. @@ -5910,6 +6335,7 @@ Enable in *Network & servers* settings. Show → on messages sent via private routing. + 显示 → 通过专用路由发送的信息. No comment provided by engineer. @@ -5919,6 +6345,7 @@ Enable in *Network & servers* settings. SimpleX + SimpleX No comment provided by engineer. @@ -5991,6 +6418,10 @@ Enable in *Network & servers* settings. SimpleX 一次性邀请 simplex link type + + SimpleX protocols reviewed by Trail of Bits. + No comment provided by engineer. + Simplified incognito mode 简化的隐身模式 @@ -5998,6 +6429,7 @@ Enable in *Network & servers* settings. Size + 大小 No comment provided by engineer. @@ -6017,10 +6449,16 @@ Enable in *Network & servers* settings. Soft + blur media + + Some app settings were not migrated. + No comment provided by engineer. + Some file(s) were not exported: + 某些文件未导出: No comment provided by engineer. @@ -6030,6 +6468,7 @@ Enable in *Network & servers* settings. Some non-fatal errors occurred during import: + 导入过程中出现一些非致命错误: No comment provided by engineer. @@ -6039,7 +6478,7 @@ Enable in *Network & servers* settings. Square, circle, or anything in between. - 方形、圆形、或两者之间的任意形状 + 方形、圆形、或两者之间的任意形状. No comment provided by engineer. @@ -6059,10 +6498,12 @@ Enable in *Network & servers* settings. Starting from %@. + 从 %@ 开始。 No comment provided by engineer. Statistics + 统计 No comment provided by engineer. @@ -6127,6 +6568,7 @@ Enable in *Network & servers* settings. Strong + 加粗 blur media @@ -6136,14 +6578,17 @@ Enable in *Network & servers* settings. Subscribed + 已订阅 No comment provided by engineer. Subscription errors + 订阅错误 No comment provided by engineer. Subscriptions ignored + 忽略订阅 No comment provided by engineer. @@ -6151,6 +6596,14 @@ Enable in *Network & servers* settings. 支持 SimpleX Chat No comment provided by engineer. + + Switch audio and video during the call. + No comment provided by engineer. + + + Switch chat profile for 1-time invitations. + No comment provided by engineer. + System 系统 @@ -6163,6 +6616,7 @@ Enable in *Network & servers* settings. TCP connection + TCP 连接 No comment provided by engineer. @@ -6185,6 +6639,10 @@ Enable in *Network & servers* settings. TCP_KEEPINTVL No comment provided by engineer. + + Tail + No comment provided by engineer. + Take picture 拍照 @@ -6227,6 +6685,7 @@ Enable in *Network & servers* settings. Temporary file error + 临时文件错误 No comment provided by engineer. @@ -6283,6 +6742,7 @@ It can happen because of some bug or when the connection is compromised. The app will ask to confirm downloads from unknown file servers (except .onion). + 该应用程序将要求确认从未知文件服务器(.onion 除外)下载。 No comment provided by engineer. @@ -6332,10 +6792,12 @@ It can happen because of some bug or when the connection is compromised. The messages will be deleted for all members. + 将删除所有成员的消息。 No comment provided by engineer. The messages will be marked as moderated for all members. + 对于所有成员,这些消息将被标记为已审核。 No comment provided by engineer. @@ -6373,8 +6835,13 @@ It can happen because of some bug or when the connection is compromised.您粘贴的文本不是 SimpleX 链接。 No comment provided by engineer. + + The uploaded database archive will be permanently removed from the servers. + No comment provided by engineer. + Themes + 主题 No comment provided by engineer. @@ -6444,6 +6911,7 @@ It can happen because of some bug or when the connection is compromised. This link was used with another mobile device, please create a new link on the desktop. + 此链接已在其他移动设备上使用,请在桌面上创建新链接。 No comment provided by engineer. @@ -6453,6 +6921,7 @@ It can happen because of some bug or when the connection is compromised. Title + 标题 No comment provided by engineer. @@ -6487,6 +6956,7 @@ It can happen because of some bug or when the connection is compromised. To protect your IP address, private routing uses your SMP servers to deliver messages. + 为了保护您的 IP 地址,私有路由使用您的 SMP 服务器来传递邮件。 No comment provided by engineer. @@ -6496,6 +6966,14 @@ You will be prompted to complete authentication before this feature is enabled.< 在启用此功能之前,系统将提示您完成身份验证。 No comment provided by engineer. + + To record speech please grant permission to use Microphone. + No comment provided by engineer. + + + To record video please grant permission to use Camera. + No comment provided by engineer. + To record voice message please grant permission to use Microphone. 请授权使用麦克风以录制语音消息。 @@ -6518,6 +6996,7 @@ You will be prompted to complete authentication before this feature is enabled.< Toggle chat list: + 切换聊天列表: No comment provided by engineer. @@ -6527,10 +7006,12 @@ You will be prompted to complete authentication before this feature is enabled.< Toolbar opacity + 工具栏不透明度 No comment provided by engineer. Total + 共计 No comment provided by engineer. @@ -6540,6 +7021,7 @@ You will be prompted to complete authentication before this feature is enabled.< Transport sessions + 传输会话 No comment provided by engineer. @@ -6554,6 +7036,7 @@ You will be prompted to complete authentication before this feature is enabled.< Turkish interface + 土耳其语界面 No comment provided by engineer. @@ -6643,7 +7126,8 @@ You will be prompted to complete authentication before this feature is enabled.< Unknown servers! - No comment provided by engineer. + 未知服务器! + alert title Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. @@ -6709,6 +7193,7 @@ To connect, please ask your contact to create another connection link and check Update settings? + 更新设置? No comment provided by engineer. @@ -6723,6 +7208,7 @@ To connect, please ask your contact to create another connection link and check Upload errors + 上传错误 No comment provided by engineer. @@ -6737,10 +7223,12 @@ To connect, please ask your contact to create another connection link and check Uploaded + 已上传 No comment provided by engineer. Uploaded files + 已上传的文件 No comment provided by engineer. @@ -6753,6 +7241,10 @@ To connect, please ask your contact to create another connection link and check 使用 .onion 主机 No comment provided by engineer. + + Use SOCKS proxy + No comment provided by engineer. + Use SimpleX Chat servers? 使用 SimpleX Chat 服务器? @@ -6790,14 +7282,17 @@ To connect, please ask your contact to create another connection link and check Use only local notifications? + 仅使用本地通知? No comment provided by engineer. Use private routing with unknown servers when IP address is not protected. + 当 IP 地址不受保护时,对未知服务器使用私有路由。 No comment provided by engineer. Use private routing with unknown servers. + 对未知服务器使用私有路由。 No comment provided by engineer. @@ -6807,20 +7302,21 @@ To connect, please ask your contact to create another connection link and check Use the app while in the call. - 通话时使用本应用 + 通话时使用本应用. No comment provided by engineer. Use the app with one hand. - No comment provided by engineer. - - - User profile - 用户资料 + 用一只手使用应用程序。 No comment provided by engineer. User selection + 用户选择 + No comment provided by engineer. + + + Username No comment provided by engineer. @@ -6935,6 +7431,7 @@ To connect, please ask your contact to create another connection link and check Waiting for desktop... + 正在等待桌面... No comment provided by engineer. @@ -6954,15 +7451,17 @@ To connect, please ask your contact to create another connection link and check Wallpaper accent + 壁纸装饰 No comment provided by engineer. Wallpaper background + 壁纸背景 No comment provided by engineer. Warning: starting chat on multiple devices is not supported and will cause message delivery failures - 警告:不支持在多部设备上启动聊天,这么做会导致消息传送失败。 + 警告:不支持在多部设备上启动聊天,这么做会导致消息传送失败 No comment provided by engineer. @@ -7047,11 +7546,13 @@ To connect, please ask your contact to create another connection link and check Without Tor or VPN, your IP address will be visible to file servers. + 如果没有 Tor 或 VPN,您的 IP 地址将对文件服务器可见。 No comment provided by engineer. Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. - No comment provided by engineer. + 如果没有 Tor 或 VPN,您的 IP 地址将对以下 XFTP 中继可见:%@。 + alert message Wrong database passphrase @@ -7060,10 +7561,12 @@ To connect, please ask your contact to create another connection link and check Wrong key or unknown connection - most likely this connection is deleted. + 密钥错误或连接未知 - 很可能此连接已被删除。 snd error text Wrong key or unknown file chunk address - most likely file is deleted. + 密钥错误或文件块地址未知 - 很可能文件已删除。 file error text @@ -7073,15 +7576,12 @@ To connect, please ask your contact to create another connection link and check XFTP server - No comment provided by engineer. - - - You - + XFTP 服务器 No comment provided by engineer. You **must not** use the same database on two devices. + 您 **不得** 在两台设备上使用相同的数据库。 No comment provided by engineer. @@ -7106,6 +7606,7 @@ To connect, please ask your contact to create another connection link and check You are already connecting to %@. + 您已连接到 %@。 No comment provided by engineer. @@ -7115,14 +7616,17 @@ To connect, please ask your contact to create another connection link and check You are already in group %@. + 您已在组 %@ 中。 No comment provided by engineer. You are already joining the group %@. + 您已加入组 %@。 No comment provided by engineer. You are already joining the group via this link! + 您已经通过此链接加入群组! No comment provided by engineer. @@ -7133,6 +7637,8 @@ To connect, please ask your contact to create another connection link and check You are already joining the group! Repeat join request? + 您已经加入了这个群组! +重复加入请求? No comment provided by engineer. @@ -7147,6 +7653,7 @@ Repeat join request? You are not connected to these servers. Private routing is used to deliver messages to them. + 您未连接到这些服务器。私有路由用于向他们发送消息。 No comment provided by engineer. @@ -7156,6 +7663,7 @@ Repeat join request? You can change it in Appearance settings. + 您可以在外观设置中更改它。 No comment provided by engineer. @@ -7195,6 +7703,7 @@ Repeat join request? You can send messages to %@ from Archived contacts. + 您可以从存档的联系人向%@发送消息。 No comment provided by engineer. @@ -7224,6 +7733,7 @@ Repeat join request? You can still view conversation with %@ in the list of chats. + 您仍然可以在聊天列表中查看与 %@的对话。 No comment provided by engineer. @@ -7264,6 +7774,8 @@ Repeat join request? You have already requested connection! Repeat connection request? + 您已经请求连接了! +重复连接请求? No comment provided by engineer. @@ -7288,10 +7800,12 @@ Repeat connection request? You may migrate the exported database. + 您可以迁移导出的数据库。 No comment provided by engineer. You may save the exported archive. + 您可以保存导出的档案。 No comment provided by engineer. @@ -7301,6 +7815,7 @@ Repeat connection request? You need to allow your contact to call to be able to call them. + 您需要允许您的联系人呼叫才能呼叫他们。 No comment provided by engineer. @@ -7325,6 +7840,7 @@ Repeat connection request? You will be connected when group link host's device is online, please wait or check later! + 当 Group Link Host 的设备在线时,您将被连接,请稍候或稍后检查! No comment provided by engineer. @@ -7412,11 +7928,19 @@ Repeat connection request? 您的聊天数据库未加密——设置密码来加密。 No comment provided by engineer. + + Your chat preferences + alert title + Your chat profiles 您的聊天资料 No comment provided by engineer. + + Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile. + No comment provided by engineer. + Your contact sent a file that is larger than currently supported maximum size (%@). 您的联系人发送的文件大于当前支持的最大大小 (%@)。 @@ -7432,6 +7956,10 @@ Repeat connection request? 与您的联系人保持连接。 No comment provided by engineer. + + Your credentials may be sent unencrypted. + No comment provided by engineer. + Your current chat database will be DELETED and REPLACED with the imported one. 您当前的聊天数据库将被删除并替换为导入的数据库。 @@ -7454,6 +7982,7 @@ Repeat connection request? Your profile + 您的个人资料 No comment provided by engineer. @@ -7461,13 +7990,15 @@ Repeat connection request? 您的个人资料 **%@** 将被共享。 No comment provided by engineer. - - Your profile is stored on your device and shared only with your contacts. -SimpleX servers cannot see your profile. - 您的资料存储在您的设备上并仅与您的联系人共享。 -SimpleX 服务器无法看到您的资料。 + + Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile. + 您的资料存储在您的设备上并仅与您的联系人共享。 SimpleX 服务器无法看到您的资料。 No comment provided by engineer. + + Your profile was changed. If you save it, the updated profile will be sent to all your contacts. + alert message + Your profile, contacts and delivered messages are stored on your device. 您的资料、联系人和发送的消息存储在您的设备上。 @@ -7560,10 +8091,12 @@ SimpleX 服务器无法看到您的资料。 and %lld other events + 和 %lld 其他事件 No comment provided by engineer. attempts + 尝试 No comment provided by engineer. @@ -7608,6 +8141,7 @@ SimpleX 服务器无法看到您的资料。 call + 呼叫 No comment provided by engineer. @@ -7727,6 +8261,7 @@ SimpleX 服务器无法看到您的资料。 contact %1$@ changed to %2$@ + 联系人 %1$@ 已更改为 %2$@ profile update event chat item @@ -7761,6 +8296,7 @@ SimpleX 服务器无法看到您的资料。 decryption errors + 解密错误 No comment provided by engineer. @@ -7815,6 +8351,7 @@ SimpleX 服务器无法看到您的资料。 duplicates + 复本 No comment provided by engineer. @@ -7899,6 +8436,7 @@ SimpleX 服务器无法看到您的资料。 expired + 过期 No comment provided by engineer. @@ -7933,6 +8471,7 @@ SimpleX 服务器无法看到您的资料。 inactive + 无效 No comment provided by engineer. @@ -7977,6 +8516,7 @@ SimpleX 服务器无法看到您的资料。 invite + 邀请 No comment provided by engineer. @@ -8026,6 +8566,7 @@ SimpleX 服务器无法看到您的资料。 member %1$@ changed to %2$@ + 成员 %1$@ 已更改为 %2$@ profile update event chat item @@ -8035,6 +8576,7 @@ SimpleX 服务器无法看到您的资料。 message + 消息 No comment provided by engineer. @@ -8069,6 +8611,7 @@ SimpleX 服务器无法看到您的资料。 mute + 静音 No comment provided by engineer. @@ -8125,10 +8668,12 @@ SimpleX 服务器无法看到您的资料。 other + 其他 No comment provided by engineer. other errors + 其他错误 No comment provided by engineer. @@ -8198,10 +8743,12 @@ SimpleX 服务器无法看到您的资料。 saved from %@ + 保存自 %@ No comment provided by engineer. search + 搜索 No comment provided by engineer. @@ -8233,6 +8780,9 @@ SimpleX 服务器无法看到您的资料。 server queue info: %1$@ last received msg: %2$@ + 服务器队列信息: %1$@ + +上次收到的消息: %2$@ queue info @@ -8267,6 +8817,7 @@ last received msg: %2$@ unblocked %@ + 未阻止 %@ rcv group event chat item @@ -8276,6 +8827,7 @@ last received msg: %2$@ unknown servers + 未知服务器 No comment provided by engineer. @@ -8285,10 +8837,12 @@ last received msg: %2$@ unmute + 取消静音 No comment provided by engineer. unprotected + 未受保护 No comment provided by engineer. @@ -8303,6 +8857,7 @@ last received msg: %2$@ v%@ + v%@ No comment provided by engineer. @@ -8332,6 +8887,7 @@ last received msg: %2$@ video + 视频 No comment provided by engineer. @@ -8361,6 +8917,7 @@ last received msg: %2$@ when IP hidden + 当 IP 隐藏时 No comment provided by engineer. @@ -8385,6 +8942,7 @@ last received msg: %2$@ you blocked %@ + 你阻止了%@ snd group event chat item @@ -8429,6 +8987,7 @@ last received msg: %2$@ you unblocked %@ + 您解封了 %@ snd group event chat item @@ -8465,6 +9024,7 @@ last received msg: %2$@ SimpleX uses local network access to allow using user chat profile via desktop app on the same network. + SimpleX 使用本地网络访问,允许通过同一网络上的桌面应用程序使用用户聊天配置文件。 Privacy - Local Network Usage Description @@ -8508,14 +9068,17 @@ last received msg: %2$@ SimpleX SE + SimpleX SE Bundle display name SimpleX SE + SimpleX SE Bundle name Copyright © 2024 SimpleX Chat. All rights reserved. + 版权所有 © 2024 SimpleX Chat。保留所有权利。 Copyright (human-readable) @@ -8527,150 +9090,187 @@ last received msg: %2$@ %@ + %@ No comment provided by engineer. App is locked! + 应用程序已锁定! No comment provided by engineer. Cancel + 取消 No comment provided by engineer. Cannot access keychain to save database password + 无法访问钥匙串以保存数据库密码 No comment provided by engineer. Cannot forward message + 无法转发消息 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 encrypted! + 数据库已加密! 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 passphrase is required to open chat. + 需要数据库密码才能打开聊天。 No comment provided by engineer. Database upgrade required + 需要升级数据库 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. Keychain error + 钥匙串错误 No comment provided by engineer. Large file! + 大文件! No comment provided by engineer. No active profile + 无活动配置文件 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. Passphrase + 密码 No comment provided by engineer. Please create a profile in the SimpleX app + 请在 SimpleX 应用程序中创建配置文件 No comment provided by engineer. Selected chat preferences prohibit this message. + 选定的聊天首选项禁止此消息。 No comment provided by engineer. Sending a message takes longer than expected. + 发送消息所需的时间比预期的要长。 No comment provided by engineer. Sending message… + 正在发送消息… No comment provided by engineer. Share + 共享 No comment provided by engineer. Slow network? + 网络速度慢? No comment provided by engineer. Unknown database error: %@ + 未知数据库错误: %@ No comment provided by engineer. Unsupported format + 不支持的格式 No comment provided by engineer. Wait + 等待 No comment provided by engineer. Wrong database passphrase + 数据库密码错误 No comment provided by engineer. You can allow sharing in Privacy & Security / SimpleX Lock settings. + 您可以在 "隐私与安全"/"SimpleX Lock "设置中允许共享。 No comment provided by engineer. diff --git a/apps/ios/SimpleX NSE/NotificationService.swift b/apps/ios/SimpleX NSE/NotificationService.swift index 81d0c9eac1..99bedb891f 100644 --- a/apps/ios/SimpleX NSE/NotificationService.swift +++ b/apps/ios/SimpleX NSE/NotificationService.swift @@ -26,7 +26,7 @@ enum NSENotification { case nse(UNMutableNotificationContent) case callkit(RcvCallInvitation) case empty - case msgInfo(NtfMsgInfo) + case msgInfo(NtfMsgAckInfo) var isCallInvitation: Bool { switch self { @@ -119,7 +119,9 @@ class NotificationService: UNNotificationServiceExtension { var threadId: UUID? = NSEThreads.shared.newThread() var notificationInfo: NtfMessages? var receiveEntityId: String? + var receiveConnId: String? var expectedMessage: String? + var allowedGetNextAttempts: Int = 3 // return true if the message is taken - it prevents sending it to another NotificationService instance for processing var shouldProcessNtf = false var appSubscriber: AppSubscriber? @@ -191,17 +193,21 @@ class NotificationService: UNNotificationServiceExtension { let dbStatus = startChat() if case .ok = dbStatus, let ntfInfo = apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo) { - logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfInfo.ntfMessage_ == nil ? 0 : 1))") + logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfInfo.receivedMsg_ == nil ? 0 : 1))") if let connEntity = ntfInfo.connEntity_ { setBestAttemptNtf( ntfInfo.ntfsEnabled ? .nse(createConnectionEventNtf(ntfInfo.user, connEntity)) : .empty ) - if let id = connEntity.id, ntfInfo.msgTs != nil { + if let id = connEntity.id, ntfInfo.expectedMsg_ != nil { notificationInfo = ntfInfo receiveEntityId = id - expectedMessage = ntfInfo.ntfMessage_.flatMap { $0.msgId } + receiveConnId = connEntity.conn.agentConnId + let expectedMsgId = ntfInfo.expectedMsg_?.msgId + let receivedMsgId = ntfInfo.receivedMsg_?.msgId + logger.debug("NotificationService: receiveNtfMessages: expectedMsgId = \(expectedMsgId ?? "nil", privacy: .private), receivedMsgId = \(receivedMsgId ?? "nil", privacy: .private)") + expectedMessage = expectedMsgId shouldProcessNtf = true return } @@ -219,22 +225,34 @@ class NotificationService: UNNotificationServiceExtension { } func processReceivedNtf(_ ntf: NSENotification) -> Bool { - guard let ntfInfo = notificationInfo, let msgTs = ntfInfo.msgTs else { return false } + guard let ntfInfo = notificationInfo, let expectedMsgTs = ntfInfo.expectedMsg_?.msgTs else { return false } if !ntfInfo.user.showNotifications { self.setBestAttemptNtf(.empty) } if case let .msgInfo(info) = ntf { if info.msgId == expectedMessage { expectedMessage = nil - logger.debug("NotificationService processNtf: msgInfo") + logger.debug("NotificationService processNtf: msgInfo msgId = \(info.msgId, privacy: .private): expected") self.deliverBestAttemptNtf() return true - } else if info.msgTs > msgTs { - logger.debug("NotificationService processNtf: unexpected msgInfo, let other instance to process it, stopping this one") + } else if let msgTs = info.msgTs_, msgTs > expectedMsgTs { + logger.debug("NotificationService processNtf: msgInfo msgId = \(info.msgId, privacy: .private): unexpected msgInfo, let other instance to process it, stopping this one") self.deliverBestAttemptNtf() return false + } else if allowedGetNextAttempts > 0, let receiveConnId = receiveConnId { + logger.debug("NotificationService processNtf: msgInfo msgId = \(info.msgId, privacy: .private): unexpected msgInfo, get next message") + allowedGetNextAttempts -= 1 + if let receivedMsg = apiGetConnNtfMessage(connId: receiveConnId) { + logger.debug("NotificationService processNtf, on apiGetConnNtfMessage: msgInfo msgId = \(info.msgId, privacy: .private), receivedMsg msgId = \(receivedMsg.msgId, privacy: .private)") + return true + } else { + logger.debug("NotificationService processNtf, on apiGetConnNtfMessage: msgInfo msgId = \(info.msgId, privacy: .private): no next message, deliver best attempt") + self.deliverBestAttemptNtf() + return false + } } else { - logger.debug("NotificationService processNtf: unknown message, let other instance to process it") + logger.debug("NotificationService processNtf: msgInfo msgId = \(info.msgId, privacy: .private): unknown message, let other instance to process it") + self.deliverBestAttemptNtf() return false } } else if ntfInfo.user.showNotifications { @@ -573,17 +591,22 @@ func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? { // TODO profile update case let .receivedContactRequest(user, contactRequest): return (UserContact(contactRequest: contactRequest).id, .nse(createContactRequestNtf(user, contactRequest))) - case let .newChatItem(user, aChatItem): - let cInfo = aChatItem.chatInfo - var cItem = aChatItem.chatItem - if !cInfo.ntfsEnabled { - ntfBadgeCountGroupDefault.set(max(0, ntfBadgeCountGroupDefault.get() - 1)) + case let .newChatItems(user, chatItems): + // Received items are created one at a time + if let chatItem = chatItems.first { + let cInfo = chatItem.chatInfo + var cItem = chatItem.chatItem + if !cInfo.ntfsEnabled { + ntfBadgeCountGroupDefault.set(max(0, ntfBadgeCountGroupDefault.get() - 1)) + } + if let file = cItem.autoReceiveFile() { + cItem = autoReceiveFile(file) ?? cItem + } + let ntf: NSENotification = cInfo.ntfsEnabled ? .nse(createMessageReceivedNtf(user, cInfo, cItem)) : .empty + return cItem.showNotification ? (chatItem.chatId, ntf) : nil + } else { + return nil } - if let file = cItem.autoReceiveFile() { - cItem = autoReceiveFile(file) ?? cItem - } - let ntf: NSENotification = cInfo.ntfsEnabled ? .nse(createMessageReceivedNtf(user, cInfo, cItem)) : .empty - return cItem.showNotification ? (aChatItem.chatId, ntf) : nil case let .rcvFileSndCancelled(_, aChatItem, _): cleanupFile(aChatItem) return nil @@ -687,9 +710,9 @@ func apiGetNtfMessage(nonce: String, encNtfInfo: String) -> NtfMessages? { return nil } let r = sendSimpleXCmd(.apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo)) - if case let .ntfMessages(user, connEntity_, msgTs, ntfMessage_) = r, let user = user { - logger.debug("apiGetNtfMessage response ntfMessages: \(ntfMessage_ == nil ? 0 : 1)") - return NtfMessages(user: user, connEntity_: connEntity_, msgTs: msgTs, ntfMessage_: ntfMessage_) + if case let .ntfMessages(user, connEntity_, expectedMsg_, receivedMsg_) = r, let user = user { + logger.debug("apiGetNtfMessage response ntfMessages: \(receivedMsg_ == nil ? 0 : 1)") + return NtfMessages(user: user, connEntity_: connEntity_, expectedMsg_: expectedMsg_, receivedMsg_: receivedMsg_) } else if case let .chatCmdError(_, error) = r { logger.debug("apiGetNtfMessage error response: \(String.init(describing: error))") } else { @@ -698,6 +721,20 @@ func apiGetNtfMessage(nonce: String, encNtfInfo: String) -> NtfMessages? { return nil } +func apiGetConnNtfMessage(connId: String) -> NtfMsgInfo? { + guard apiGetActiveUser() != nil else { + logger.debug("no active user") + return nil + } + let r = sendSimpleXCmd(.apiGetConnNtfMessage(connId: connId)) + if case let .connNtfMessage(receivedMsg_) = r { + logger.debug("apiGetConnNtfMessage response receivedMsg_: \(receivedMsg_ == nil ? 0 : 1)") + return receivedMsg_ + } + logger.debug("apiGetConnNtfMessage error: \(responseError(r))") + return nil +} + func apiReceiveFile(fileId: Int64, encrypted: Bool, inline: Bool? = nil) -> AChatItem? { let userApprovedRelays = !privacyAskToApproveRelaysGroupDefault.get() let r = sendSimpleXCmd(.receiveFile(fileId: fileId, userApprovedRelays: userApprovedRelays, encrypted: encrypted, inline: inline)) @@ -735,8 +772,8 @@ func setNetworkConfig(_ cfg: NetCfg) throws { struct NtfMessages { var user: User var connEntity_: ConnectionEntity? - var msgTs: Date? - var ntfMessage_: NtfMsgInfo? + var expectedMsg_: NtfMsgInfo? + var receivedMsg_: NtfMsgInfo? var ntfsEnabled: Bool { user.showNotifications && (connEntity_?.ntfsEnabled ?? false) diff --git a/apps/ios/SimpleX SE/ShareAPI.swift b/apps/ios/SimpleX SE/ShareAPI.swift index 47e072ae78..fcb78c64b1 100644 --- a/apps/ios/SimpleX SE/ShareAPI.swift +++ b/apps/ios/SimpleX SE/ShareAPI.swift @@ -54,32 +54,30 @@ func apiGetChats(userId: User.ID) throws -> Array { throw r } -func apiSendMessage( +func apiSendMessages( chatInfo: ChatInfo, - cryptoFile: CryptoFile?, - msgContent: MsgContent -) throws -> AChatItem { + composedMessages: [ComposedMessage] +) throws -> [AChatItem] { let r = sendSimpleXCmd( chatInfo.chatType == .local - ? .apiCreateChatItem( + ? .apiCreateChatItems( noteFolderId: chatInfo.apiId, - file: cryptoFile, - msg: msgContent + composedMessages: composedMessages ) - : .apiSendMessage( + : .apiSendMessages( type: chatInfo.chatType, id: chatInfo.apiId, - file: cryptoFile, - quotedItemId: nil, - msg: msgContent, live: false, - ttl: nil + ttl: nil, + composedMessages: composedMessages ) ) - if case let .newChatItem(_, chatItem) = r { - return chatItem + if case let .newChatItems(_, chatItems) = r { + return chatItems } else { - if let filePath = cryptoFile?.filePath { removeFile(filePath) } + for composedMessage in composedMessages { + if let filePath = composedMessage.fileSource?.filePath { removeFile(filePath) } + } throw r } } diff --git a/apps/ios/SimpleX SE/ShareModel.swift b/apps/ios/SimpleX SE/ShareModel.swift index 5bda361126..21908026b2 100644 --- a/apps/ios/SimpleX SE/ShareModel.swift +++ b/apps/ios/SimpleX SE/ShareModel.swift @@ -104,7 +104,7 @@ class ShareModel: ObservableObject { // 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) { + let uiImage = imageFromBase64(profileImage) { dict[chatData.id] = uiImage } } @@ -141,23 +141,25 @@ class ShareModel: ObservableObject { do { SEChatState.shared.set(.sendingMessage) await waitForOtherProcessesToSuspend() - let ci = try apiSendMessage( + let chatItems = try apiSendMessages( chatInfo: selected.chatInfo, - cryptoFile: sharedContent.cryptoFile, - msgContent: sharedContent.msgContent(comment: self.comment) + composedMessages: [ComposedMessage(fileSource: sharedContent.cryptoFile, msgContent: sharedContent.msgContent(comment: self.comment))] ) if selected.chatInfo.chatType == .local { completion() } else { - await MainActor.run { self.bottomBar = .loadingBar(progress: 0) } - 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() + // TODO batch send: share multiple items + if let ci = chatItems.first { + await MainActor.run { self.bottomBar = .loadingBar(progress: 0) } + 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 { @@ -318,7 +320,8 @@ class ShareModel: ObservableObject { } await ch.completeFile() if await !ch.isRunning { break } - case let .chatItemStatusUpdated(_, ci): + case let .chatItemsStatusesUpdated(_, chatItems): + guard let ci = chatItems.last else { continue } guard isMessage(for: ci) else { continue } if let (title, message) = ci.chatItem.meta.itemStatus.statusInfo { // `title` and `message` already localized and interpolated @@ -414,7 +417,7 @@ fileprivate func getSharedContent(_ ip: NSItemProvider) async -> Result Result Result some View { - if let img = UIImage(base64Encoded: img) { + @ViewBuilder private func imagePreview(_ imgStr: String) -> some View { + if let img = imageFromBase64(imgStr) { previewArea { Image(uiImage: img) .resizable() @@ -163,7 +163,7 @@ struct ShareView: View { @ViewBuilder private func linkPreview(_ linkPreview: LinkPreview) -> some View { previewArea { HStack(alignment: .center, spacing: 8) { - if let uiImage = UIImage(base64Encoded: linkPreview.image) { + if let uiImage = imageFromBase64(linkPreview.image) { Image(uiImage: uiImage) .resizable() .aspectRatio(contentMode: .fit) diff --git a/apps/ios/SimpleX SE/es.lproj/Localizable.strings b/apps/ios/SimpleX SE/es.lproj/Localizable.strings index 5ecdd410df..4cc5029537 100644 --- a/apps/ios/SimpleX SE/es.lproj/Localizable.strings +++ b/apps/ios/SimpleX SE/es.lproj/Localizable.strings @@ -29,7 +29,7 @@ "Database error" = "Error en base de datos"; /* No comment provided by engineer. */ -"Database passphrase is different from saved in the keychain." = "La contraseña de la base de datos es distinta a la almacenada en keychain."; +"Database passphrase is different from saved in the keychain." = "La contraseña de la base de datos es diferente a la almacenada en keychain."; /* No comment provided by engineer. */ "Database passphrase is required to open chat." = "Se requiere la contraseña de la base de datos para abrir la aplicación."; diff --git a/apps/ios/SimpleX SE/hu.lproj/Localizable.strings b/apps/ios/SimpleX SE/hu.lproj/Localizable.strings index 8ce4317a9b..94f18db853 100644 --- a/apps/ios/SimpleX SE/hu.lproj/Localizable.strings +++ b/apps/ios/SimpleX SE/hu.lproj/Localizable.strings @@ -17,7 +17,7 @@ "Comment" = "Hozzászólás"; /* No comment provided by engineer. */ -"Currently maximum supported file size is %@." = "Jelenleg a maximális támogatott fájlméret %@."; +"Currently maximum supported file size is %@." = "Jelenleg a maximálisan támogatott fájlméret: %@."; /* No comment provided by engineer. */ "Database downgrade required" = "Adatbázis visszafejlesztése szükséges"; @@ -26,13 +26,13 @@ "Database encrypted!" = "Adatbázis titkosítva!"; /* No comment provided by engineer. */ -"Database error" = "Adatbázis hiba"; +"Database error" = "Adatbázishiba"; /* No comment provided by engineer. */ "Database passphrase is different from saved in the keychain." = "Az adatbázis jelmondata eltér a kulcstartóban lévőtől."; /* No comment provided by engineer. */ -"Database passphrase is required to open chat." = "Adatbázis jelmondat szükséges a csevegés megnyitásához."; +"Database passphrase is required to open chat." = "Adatbázis-jelmondat szükséges a csevegés megnyitásához."; /* No comment provided by engineer. */ "Database upgrade required" = "Adatbázis fejlesztése szükséges"; @@ -50,13 +50,13 @@ "File error" = "Fájlhiba"; /* No comment provided by engineer. */ -"Incompatible database version" = "Nem kompatibilis adatbázis verzió"; +"Incompatible database version" = "Nem kompatibilis adatbázis-verzió"; /* No comment provided by engineer. */ "Invalid migration confirmation" = "Érvénytelen átköltöztetési visszaigazolás"; /* No comment provided by engineer. */ -"Keychain error" = "Kulcstartó hiba"; +"Keychain error" = "Kulcstartóhiba"; /* No comment provided by engineer. */ "Large file!" = "Nagy fájl!"; @@ -95,7 +95,7 @@ "Slow network?" = "Lassú internetkapcsolat?"; /* No comment provided by engineer. */ -"Unknown database error: %@" = "Ismeretlen adatbázis hiba: %@"; +"Unknown database error: %@" = "Ismeretlen adatbázishiba: %@"; /* No comment provided by engineer. */ "Unsupported format" = "Nem támogatott formátum"; @@ -104,8 +104,8 @@ "Wait" = "Várjon"; /* No comment provided by engineer. */ -"Wrong database passphrase" = "Hibás adatbázis jelmondat"; +"Wrong database passphrase" = "Hibás adatbázis-jelmondat"; /* No comment provided by engineer. */ -"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "A megosztást az Adatvédelem és biztonság / SimpleX zár menüben engedélyezheti."; +"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "A megosztást az Adatvédelem és biztonság / SimpleX-zár menüben engedélyezheti."; diff --git a/apps/ios/SimpleX SE/nl.lproj/Localizable.strings b/apps/ios/SimpleX SE/nl.lproj/Localizable.strings index 8412c88ea6..e5d2487b54 100644 --- a/apps/ios/SimpleX SE/nl.lproj/Localizable.strings +++ b/apps/ios/SimpleX SE/nl.lproj/Localizable.strings @@ -32,7 +32,7 @@ "Database passphrase is different from saved in the keychain." = "Het wachtwoord van de database verschilt van het wachtwoord die in de keychain is opgeslagen."; /* No comment provided by engineer. */ -"Database passphrase is required to open chat." = "Database wachtwoord is vereist om je gesprekken te openen."; +"Database passphrase is required to open chat." = "Database wachtwoord is vereist om je chats te openen."; /* No comment provided by engineer. */ "Database upgrade required" = "Database upgrade vereist"; diff --git a/apps/ios/SimpleX SE/tr.lproj/InfoPlist.strings b/apps/ios/SimpleX SE/tr.lproj/InfoPlist.strings index 388ac01f7f..cf1ca31f53 100644 --- a/apps/ios/SimpleX SE/tr.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX SE/tr.lproj/InfoPlist.strings @@ -1,7 +1,9 @@ -/* - InfoPlist.strings - SimpleX +/* Bundle display name */ +"CFBundleDisplayName" = "SimpleX SE"; + +/* Bundle name */ +"CFBundleName" = "SimpleX SE"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Telif Hakkı © 2024 SimpleX Chat. Tüm hakları saklıdır."; - 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 index 5ef592ec70..baef71c127 100644 --- a/apps/ios/SimpleX SE/tr.lproj/Localizable.strings +++ b/apps/ios/SimpleX SE/tr.lproj/Localizable.strings @@ -1,7 +1,111 @@ -/* - Localizable.strings - SimpleX +/* No comment provided by engineer. */ +"%@" = "%@"; + +/* No comment provided by engineer. */ +"App is locked!" = "Uygulama kilitlendi!"; + +/* No comment provided by engineer. */ +"Cancel" = "İptal et"; + +/* No comment provided by engineer. */ +"Cannot access keychain to save database password" = "Veritabanı şifresini kaydetmek için Anahtar Zinciri'ne erişilemiyor"; + +/* No comment provided by engineer. */ +"Cannot forward message" = "Mesaj iletilemiyor"; + +/* No comment provided by engineer. */ +"Comment" = "Yorum"; + +/* No comment provided by engineer. */ +"Currently maximum supported file size is %@." = "Şu anki maksimum desteklenen dosya boyutu %@ kadardır."; + +/* No comment provided by engineer. */ +"Database downgrade required" = "Veritabanı sürüm düşürme gerekli"; + +/* No comment provided by engineer. */ +"Database encrypted!" = "Veritabanı şifrelendi!"; + +/* No comment provided by engineer. */ +"Database error" = "Veritabanı hatası"; + +/* No comment provided by engineer. */ +"Database passphrase is different from saved in the keychain." = "Veritabanı parolası Anahtar Zinciri'nde kayıtlı olandan farklıdır."; + +/* No comment provided by engineer. */ +"Database passphrase is required to open chat." = "Konuşmayı açmak için veri tabanı parolası gerekli."; + +/* No comment provided by engineer. */ +"Database upgrade required" = "Veritabanı yükseltmesi gerekli"; + +/* No comment provided by engineer. */ +"Error preparing file" = "Dosya hazırlanırken hata oluştu"; + +/* No comment provided by engineer. */ +"Error preparing message" = "Mesaj hazırlanırken hata oluştu"; + +/* No comment provided by engineer. */ +"Error: %@" = "Hata: %@"; + +/* No comment provided by engineer. */ +"File error" = "Dosya hatası"; + +/* No comment provided by engineer. */ +"Incompatible database version" = "Uyumsuz veritabanı sürümü"; + +/* No comment provided by engineer. */ +"Invalid migration confirmation" = "Geçerli olmayan taşıma onayı"; + +/* No comment provided by engineer. */ +"Keychain error" = "Anahtarlık hatası"; + +/* No comment provided by engineer. */ +"Large file!" = "Büyük dosya!"; + +/* No comment provided by engineer. */ +"No active profile" = "Aktif profil yok"; + +/* No comment provided by engineer. */ +"Ok" = "Tamam"; + +/* No comment provided by engineer. */ +"Open the app to downgrade the database." = "Veritabanının sürümünü düşürmek için uygulamayı açın."; + +/* No comment provided by engineer. */ +"Open the app to upgrade the database." = "Veritabanını güncellemek için uygulamayı açın."; + +/* No comment provided by engineer. */ +"Passphrase" = "Parola"; + +/* No comment provided by engineer. */ +"Please create a profile in the SimpleX app" = "Lütfen SimpleX uygulamasında bir profil oluşturun"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "Seçilen sohbet tercihleri bu mesajı yasakladı."; + +/* No comment provided by engineer. */ +"Sending a message takes longer than expected." = "Mesaj göndermek beklenenden daha uzun sürüyor."; + +/* No comment provided by engineer. */ +"Sending message…" = "Mesaj gönderiliyor…"; + +/* No comment provided by engineer. */ +"Share" = "Paylaş"; + +/* No comment provided by engineer. */ +"Slow network?" = "Ağ yavaş mı?"; + +/* No comment provided by engineer. */ +"Unknown database error: %@" = "Bilinmeyen veritabanı hatası: %@"; + +/* No comment provided by engineer. */ +"Unsupported format" = "Desteklenmeyen format"; + +/* No comment provided by engineer. */ +"Wait" = "Bekleyin"; + +/* No comment provided by engineer. */ +"Wrong database passphrase" = "Yanlış veritabanı parolası"; + +/* No comment provided by engineer. */ +"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "Gizlilik ve Güvenlik / SimpleX Lock ayarlarından paylaşıma izin verebilirsiniz."; - 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 index 388ac01f7f..760be62885 100644 --- a/apps/ios/SimpleX SE/zh-Hans.lproj/InfoPlist.strings +++ b/apps/ios/SimpleX SE/zh-Hans.lproj/InfoPlist.strings @@ -1,7 +1,9 @@ -/* - InfoPlist.strings - SimpleX +/* Bundle display name */ +"CFBundleDisplayName" = "SimpleX SE"; + +/* Bundle name */ +"CFBundleName" = "SimpleX SE"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "版权所有 © 2024 SimpleX Chat。保留所有权利。"; - 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 index 5ef592ec70..362e2edb74 100644 --- a/apps/ios/SimpleX SE/zh-Hans.lproj/Localizable.strings +++ b/apps/ios/SimpleX SE/zh-Hans.lproj/Localizable.strings @@ -1,7 +1,111 @@ -/* - Localizable.strings - SimpleX +/* No comment provided by engineer. */ +"%@" = "%@"; + +/* No comment provided by engineer. */ +"App is locked!" = "应用程序已锁定!"; + +/* No comment provided by engineer. */ +"Cancel" = "取消"; + +/* No comment provided by engineer. */ +"Cannot access keychain to save database password" = "无法访问钥匙串以保存数据库密码"; + +/* No comment provided by engineer. */ +"Cannot forward message" = "无法转发消息"; + +/* 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 encrypted!" = "数据库已加密!"; + +/* 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 passphrase is required to open chat." = "需要数据库密码才能打开聊天。"; + +/* No comment provided by engineer. */ +"Database upgrade required" = "需要升级数据库"; + +/* 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. */ +"Keychain error" = "钥匙串错误"; + +/* No comment provided by engineer. */ +"Large file!" = "大文件!"; + +/* No comment provided by engineer. */ +"No active profile" = "无活动配置文件"; + +/* 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. */ +"Passphrase" = "密码"; + +/* No comment provided by engineer. */ +"Please create a profile in the SimpleX app" = "请在 SimpleX 应用程序中创建配置文件"; + +/* No comment provided by engineer. */ +"Selected chat preferences prohibit this message." = "选定的聊天首选项禁止此消息。"; + +/* No comment provided by engineer. */ +"Sending a message takes longer than expected." = "发送消息所需的时间比预期的要长。"; + +/* No comment provided by engineer. */ +"Sending message…" = "正在发送消息…"; + +/* No comment provided by engineer. */ +"Share" = "共享"; + +/* No comment provided by engineer. */ +"Slow network?" = "网络速度慢?"; + +/* No comment provided by engineer. */ +"Unknown database error: %@" = "未知数据库错误: %@"; + +/* No comment provided by engineer. */ +"Unsupported format" = "不支持的格式"; + +/* No comment provided by engineer. */ +"Wait" = "等待"; + +/* No comment provided by engineer. */ +"Wrong database passphrase" = "数据库密码错误"; + +/* No comment provided by engineer. */ +"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "您可以在 \"隐私与安全\"/\"SimpleX Lock \"设置中允许共享。"; - 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 3f35bc3904..2903388fb9 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -196,6 +196,7 @@ 8CC956EE2BC0041000412A11 /* NetworkObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC956ED2BC0041000412A11 /* NetworkObserver.swift */; }; 8CE848A32C5A0FA000D5C7C8 /* SelectableChatItemToolbars.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE848A22C5A0FA000D5C7C8 /* SelectableChatItemToolbars.swift */; }; B76E6C312C5C41D900EC11AA /* ContactListNavLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = B76E6C302C5C41D900EC11AA /* ContactListNavLink.swift */; }; + CE176F202C87014C00145DBC /* InvertedForegroundStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE176F1F2C87014C00145DBC /* InvertedForegroundStyle.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 */; }; @@ -203,12 +204,14 @@ CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */ = {isa = PBXBuildFile; productRef = CE38A29B2C3FCD72005ED185 /* SwiftyGif */; }; CE75480A2C622630009579B7 /* SwipeLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7548092C622630009579B7 /* SwipeLabel.swift */; }; CE984D4B2C36C5D500E3AEFF /* ChatItemClipShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE984D4A2C36C5D500E3AEFF /* ChatItemClipShape.swift */; }; + CEDB245B2C9CD71800FBC5F6 /* StickyScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEDB245A2C9CD71800FBC5F6 /* StickyScrollView.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 */; }; + CEFB2EDF2CA1BCC7004B1ECE /* SheetRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFB2EDE2CA1BCC7004B1ECE /* SheetRepresentable.swift */; }; D7197A1829AE89660055C05A /* WebRTC in Frameworks */ = {isa = PBXBuildFile; productRef = D7197A1729AE89660055C05A /* WebRTC */; }; D72A9088294BD7A70047C86D /* NativeTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72A9087294BD7A70047C86D /* NativeTextEditor.swift */; }; D741547829AF89AF0022400A /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D741547729AF89AF0022400A /* StoreKit.framework */; }; @@ -216,15 +219,15 @@ D77B92DC2952372200A5A1CC /* SwiftyGif in Frameworks */ = {isa = PBXBuildFile; productRef = D77B92DB2952372200A5A1CC /* SwiftyGif */; }; D7F0E33929964E7E0068AF69 /* LZString in Frameworks */ = {isa = PBXBuildFile; productRef = D7F0E33829964E7E0068AF69 /* LZString */; }; E51CC1E62C62085600DB91FE /* OneHandUICard.swift in Sources */ = {isa = PBXBuildFile; fileRef = E51CC1E52C62085600DB91FE /* OneHandUICard.swift */; }; - E5CC47842CA31C3A00551ACF /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5CC477F2CA31C3900551ACF /* libgmpxx.a */; }; - E5CC47852CA31C3A00551ACF /* libHSsimplex-chat-6.0.5.0-3qcee2iGFVOIynW0cRTIaP.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5CC47802CA31C3900551ACF /* libHSsimplex-chat-6.0.5.0-3qcee2iGFVOIynW0cRTIaP.a */; }; - E5CC47862CA31C3A00551ACF /* libHSsimplex-chat-6.0.5.0-3qcee2iGFVOIynW0cRTIaP-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5CC47812CA31C3900551ACF /* libHSsimplex-chat-6.0.5.0-3qcee2iGFVOIynW0cRTIaP-ghc9.6.3.a */; }; - E5CC47872CA31C3A00551ACF /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5CC47822CA31C3A00551ACF /* libgmp.a */; }; - E5CC47882CA31C3A00551ACF /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5CC47832CA31C3A00551ACF /* libffi.a */; }; E5DCF8DB2C56FAC1007928CC /* SimpleXChat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; }; 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 */; }; + E5E997C92CBA891A00D7A2FA /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E997C42CBA891A00D7A2FA /* libgmpxx.a */; }; + E5E997CA2CBA891A00D7A2FA /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E997C52CBA891A00D7A2FA /* libgmp.a */; }; + E5E997CB2CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E997C62CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv-ghc9.6.3.a */; }; + E5E997CC2CBA891A00D7A2FA /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E997C72CBA891A00D7A2FA /* libffi.a */; }; + E5E997CD2CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E997C82CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -536,11 +539,13 @@ 8CC956ED2BC0041000412A11 /* NetworkObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkObserver.swift; sourceTree = ""; }; 8CE848A22C5A0FA000D5C7C8 /* SelectableChatItemToolbars.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableChatItemToolbars.swift; sourceTree = ""; }; B76E6C302C5C41D900EC11AA /* ContactListNavLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactListNavLink.swift; sourceTree = ""; }; + CE176F1F2C87014C00145DBC /* InvertedForegroundStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvertedForegroundStyle.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 = ""; }; CE7548092C622630009579B7 /* SwipeLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeLabel.swift; sourceTree = ""; }; CE984D4A2C36C5D500E3AEFF /* ChatItemClipShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemClipShape.swift; sourceTree = ""; }; + CEDB245A2C9CD71800FBC5F6 /* StickyScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickyScrollView.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 = ""; }; @@ -549,16 +554,12 @@ 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 = ""; }; + CEFB2EDE2CA1BCC7004B1ECE /* SheetRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SheetRepresentable.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; }; E51CC1E52C62085600DB91FE /* OneHandUICard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OneHandUICard.swift; sourceTree = ""; }; - E5CC477F2CA31C3900551ACF /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; - E5CC47802CA31C3900551ACF /* libHSsimplex-chat-6.0.5.0-3qcee2iGFVOIynW0cRTIaP.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.0.5.0-3qcee2iGFVOIynW0cRTIaP.a"; sourceTree = ""; }; - E5CC47812CA31C3900551ACF /* libHSsimplex-chat-6.0.5.0-3qcee2iGFVOIynW0cRTIaP-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.0.5.0-3qcee2iGFVOIynW0cRTIaP-ghc9.6.3.a"; sourceTree = ""; }; - E5CC47822CA31C3A00551ACF /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; - E5CC47832CA31C3A00551ACF /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.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 = ""; }; @@ -611,6 +612,11 @@ 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 = ""; }; + E5E997C42CBA891A00D7A2FA /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; + E5E997C52CBA891A00D7A2FA /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; + E5E997C62CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv-ghc9.6.3.a"; sourceTree = ""; }; + E5E997C72CBA891A00D7A2FA /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; + E5E997C82CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv.a"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -649,14 +655,14 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - E5CC47852CA31C3A00551ACF /* libHSsimplex-chat-6.0.5.0-3qcee2iGFVOIynW0cRTIaP.a in Frameworks */, - E5CC47872CA31C3A00551ACF /* libgmp.a in Frameworks */, - E5CC47842CA31C3A00551ACF /* libgmpxx.a in Frameworks */, - E5CC47882CA31C3A00551ACF /* libffi.a in Frameworks */, + E5E997C92CBA891A00D7A2FA /* libgmpxx.a in Frameworks */, + E5E997CB2CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv-ghc9.6.3.a in Frameworks */, + E5E997CA2CBA891A00D7A2FA /* libgmp.a in Frameworks */, 5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */, - E5CC47862CA31C3A00551ACF /* libHSsimplex-chat-6.0.5.0-3qcee2iGFVOIynW0cRTIaP-ghc9.6.3.a in Frameworks */, + E5E997CC2CBA891A00D7A2FA /* libffi.a in Frameworks */, 5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */, CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */, + E5E997CD2CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -733,11 +739,11 @@ 5C764E5C279C70B7000C6508 /* Libraries */ = { isa = PBXGroup; children = ( - E5CC47832CA31C3A00551ACF /* libffi.a */, - E5CC47822CA31C3A00551ACF /* libgmp.a */, - E5CC477F2CA31C3900551ACF /* libgmpxx.a */, - E5CC47812CA31C3900551ACF /* libHSsimplex-chat-6.0.5.0-3qcee2iGFVOIynW0cRTIaP-ghc9.6.3.a */, - E5CC47802CA31C3900551ACF /* libHSsimplex-chat-6.0.5.0-3qcee2iGFVOIynW0cRTIaP.a */, + E5E997C72CBA891A00D7A2FA /* libffi.a */, + E5E997C52CBA891A00D7A2FA /* libgmp.a */, + E5E997C42CBA891A00D7A2FA /* libgmpxx.a */, + E5E997C62CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv-ghc9.6.3.a */, + E5E997C82CBA891A00D7A2FA /* libHSsimplex-chat-6.1.0.9-3X73OucN19a19eYgUK66sv.a */, ); path = Libraries; sourceTree = ""; @@ -793,6 +799,9 @@ 8C9BC2642C240D5100875A27 /* ThemeModeEditor.swift */, CE984D4A2C36C5D500E3AEFF /* ChatItemClipShape.swift */, CE7548092C622630009579B7 /* SwipeLabel.swift */, + CE176F1F2C87014C00145DBC /* InvertedForegroundStyle.swift */, + CEDB245A2C9CD71800FBC5F6 /* StickyScrollView.swift */, + CEFB2EDE2CA1BCC7004B1ECE /* SheetRepresentable.swift */, ); path = Helpers; sourceTree = ""; @@ -1443,6 +1452,7 @@ 5CB634B129E5EFEA0066AD6B /* PasscodeView.swift in Sources */, 8C69FE7D2B8C7D2700267E38 /* AppSettings.swift in Sources */, 5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */, + CEFB2EDF2CA1BCC7004B1ECE /* SheetRepresentable.swift in Sources */, 5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */, 6442E0BA287F169300CEC0F9 /* AddGroupView.swift in Sources */, 5CF937232B2503D000E1D781 /* NSESubscriber.swift in Sources */, @@ -1493,9 +1503,11 @@ 5C93293F2928E0FD0090FFF9 /* AudioRecPlay.swift in Sources */, 5C029EA82837DBB3004A9677 /* CICallItemView.swift in Sources */, 5CE4407227ADB1D0007B033A /* Emoji.swift in Sources */, + CEDB245B2C9CD71800FBC5F6 /* StickyScrollView.swift in Sources */, 5C9CC7A928C532AB00BEF955 /* DatabaseErrorView.swift in Sources */, 5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */, 64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */, + CE176F202C87014C00145DBC /* InvertedForegroundStyle.swift in Sources */, 5CEBD7482A5F115D00665FE2 /* SetDeliveryReceiptsView.swift in Sources */, 5C9C2DA7289957AE00CC63B1 /* AdvancedNetworkSettings.swift in Sources */, 5CADE79A29211BB900072E13 /* PreferencesView.swift in Sources */, @@ -1887,7 +1899,7 @@ CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 239; + CURRENT_PROJECT_VERSION = 244; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; @@ -1912,7 +1924,7 @@ "@executable_path/Frameworks", ); LLVM_LTO = YES_THIN; - MARKETING_VERSION = 6.0.5; + MARKETING_VERSION = 6.1; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_NAME = SimpleX; SDKROOT = iphoneos; @@ -1936,7 +1948,7 @@ CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 239; + CURRENT_PROJECT_VERSION = 244; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; @@ -1961,7 +1973,7 @@ "@executable_path/Frameworks", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.0.5; + MARKETING_VERSION = 6.1; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_NAME = SimpleX; SDKROOT = iphoneos; @@ -1977,11 +1989,11 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 239; + CURRENT_PROJECT_VERSION = 244; DEVELOPMENT_TEAM = 5NN7GUYB6T; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 6.0.5; + MARKETING_VERSION = 6.1; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -1997,11 +2009,11 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 239; + CURRENT_PROJECT_VERSION = 244; DEVELOPMENT_TEAM = 5NN7GUYB6T; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 6.0.5; + MARKETING_VERSION = 6.1; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2022,7 +2034,7 @@ CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 239; + CURRENT_PROJECT_VERSION = 244; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; GCC_OPTIMIZATION_LEVEL = s; @@ -2037,7 +2049,7 @@ "@executable_path/../../Frameworks", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.0.5; + MARKETING_VERSION = 6.1; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2059,7 +2071,7 @@ CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 239; + CURRENT_PROJECT_VERSION = 244; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; ENABLE_CODE_COVERAGE = NO; @@ -2074,7 +2086,7 @@ "@executable_path/../../Frameworks", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.0.5; + MARKETING_VERSION = 6.1; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2096,7 +2108,7 @@ CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 239; + CURRENT_PROJECT_VERSION = 244; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; DYLIB_COMPATIBILITY_VERSION = 1; @@ -2122,7 +2134,7 @@ "$(PROJECT_DIR)/Libraries/sim", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.0.5; + MARKETING_VERSION = 6.1; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; @@ -2147,7 +2159,7 @@ CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 239; + CURRENT_PROJECT_VERSION = 244; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; DYLIB_COMPATIBILITY_VERSION = 1; @@ -2173,7 +2185,7 @@ "$(PROJECT_DIR)/Libraries/sim", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.0.5; + MARKETING_VERSION = 6.1; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; @@ -2198,7 +2210,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 239; + CURRENT_PROJECT_VERSION = 244; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -2213,7 +2225,7 @@ "@executable_path/../../Frameworks", ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 6.0.5; + MARKETING_VERSION = 6.1; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2232,7 +2244,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 239; + CURRENT_PROJECT_VERSION = 244; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -2247,7 +2259,7 @@ "@executable_path/../../Frameworks", ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 6.0.5; + MARKETING_VERSION = 6.1; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index 8245230771..fae6d2293f 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -43,18 +43,20 @@ public enum ChatCommand { case apiGetChats(userId: Int64) case apiGetChat(type: ChatType, id: Int64, pagination: ChatPagination, search: String) case apiGetChatItemInfo(type: ChatType, id: Int64, itemId: Int64) - 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 apiSendMessages(type: ChatType, id: Int64, live: Bool, ttl: Int?, composedMessages: [ComposedMessage]) + case apiCreateChatItems(noteFolderId: Int64, composedMessages: [ComposedMessage]) case apiUpdateChatItem(type: ChatType, id: Int64, itemId: Int64, msg: MsgContent, live: Bool) 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 apiPlanForwardChatItems(toChatType: ChatType, toChatId: Int64, itemIds: [Int64]) + case apiForwardChatItems(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemIds: [Int64], ttl: Int?) case apiGetNtfToken case apiRegisterToken(token: DeviceToken, notificationMode: NotificationsMode) case apiVerifyToken(token: DeviceToken, nonce: String, code: String) case apiDeleteToken(token: DeviceToken) case apiGetNtfMessage(nonce: String, encNtfInfo: String) + case apiGetConnNtfMessage(connId: String) case apiNewGroup(userId: Int64, incognito: Bool, groupProfile: GroupProfile) case apiAddMember(groupId: Int64, contactId: Int64, memberRole: GroupMemberRole) case apiJoinGroup(groupId: Int64) @@ -98,6 +100,7 @@ public enum ChatCommand { case apiVerifyGroupMember(groupId: Int64, groupMemberId: Int64, connectionCode: String?) case apiAddContact(userId: Int64, incognito: Bool) case apiSetConnectionIncognito(connId: Int64, incognito: Bool) + case apiChangeConnectionUser(connId: Int64, userId: Int64) case apiConnectPlan(userId: Int64, connReq: String) case apiConnect(userId: Int64, incognito: Bool, connReq: String) case apiConnectContactViaAddress(userId: Int64, incognito: Bool, contactId: Int64) @@ -129,6 +132,7 @@ public enum ChatCommand { // WebRTC calls / case apiGetNetworkStatuses case apiChatRead(type: ChatType, id: Int64, itemRange: (Int64, Int64)) + case apiChatItemsRead(type: ChatType, id: Int64, itemIds: [Int64]) case apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool) case receiveFile(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool?, inline: Bool?) case setFileToReceive(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool?) @@ -191,25 +195,27 @@ public enum ChatCommand { case let .apiGetChat(type, id, pagination, search): return "/_get chat \(ref(type, id)) \(pagination.cmdString)" + (search == "" ? "" : " search=\(search)") case let .apiGetChatItemInfo(type, id, itemId): return "/_get item info \(ref(type, id)) \(itemId)" - case let .apiSendMessage(type, id, file, quotedItemId, mc, live, ttl): - let msg = encodeJSON(ComposedMessage(fileSource: file, quotedItemId: quotedItemId, msgContent: mc)) + case let .apiSendMessages(type, id, live, ttl, composedMessages): + let msgs = encodeJSON(composedMessages) let ttlStr = ttl != nil ? "\(ttl!)" : "default" - return "/_send \(ref(type, id)) live=\(onOff(live)) ttl=\(ttlStr) json \(msg)" - case let .apiCreateChatItem(noteFolderId, file, mc): - let msg = encodeJSON(ComposedMessage(fileSource: file, msgContent: mc)) - return "/_create *\(noteFolderId) json \(msg)" + return "/_send \(ref(type, id)) live=\(onOff(live)) ttl=\(ttlStr) json \(msgs)" + case let .apiCreateChatItems(noteFolderId, composedMessages): + let msgs = encodeJSON(composedMessages) + return "/_create *\(noteFolderId) json \(msgs)" 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, 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): + case let .apiPlanForwardChatItems(type, id, itemIds): return "/_forward plan \(ref(type, id)) \(itemIds.map({ "\($0)" }).joined(separator: ","))" + case let .apiForwardChatItems(toChatType, toChatId, fromChatType, fromChatId, itemIds, ttl): let ttlStr = ttl != nil ? "\(ttl!)" : "default" - return "/_forward \(ref(toChatType, toChatId)) \(ref(fromChatType, fromChatId)) \(itemId) ttl=\(ttlStr)" + return "/_forward \(ref(toChatType, toChatId)) \(ref(fromChatType, fromChatId)) \(itemIds.map({ "\($0)" }).joined(separator: ",")) ttl=\(ttlStr)" case .apiGetNtfToken: return "/_ntf get " case let .apiRegisterToken(token, notificationMode): return "/_ntf register \(token.cmdString) \(notificationMode.rawValue)" case let .apiVerifyToken(token, nonce, code): return "/_ntf verify \(token.cmdString) \(nonce) \(code)" case let .apiDeleteToken(token): return "/_ntf delete \(token.cmdString)" case let .apiGetNtfMessage(nonce, encNtfInfo): return "/_ntf message \(nonce) \(encNtfInfo)" + case let .apiGetConnNtfMessage(connId): return "/_ntf conn message \(connId)" case let .apiNewGroup(userId, incognito, groupProfile): return "/_group \(userId) incognito=\(onOff(incognito)) \(encodeJSON(groupProfile))" case let .apiAddMember(groupId, contactId, memberRole): return "/_add #\(groupId) \(contactId) \(memberRole)" case let .apiJoinGroup(groupId): return "/_join #\(groupId)" @@ -263,6 +269,7 @@ public enum ChatCommand { case let .apiVerifyGroupMember(groupId, groupMemberId, .none): return "/_verify code #\(groupId) \(groupMemberId)" case let .apiAddContact(userId, incognito): return "/_connect \(userId) incognito=\(onOff(incognito))" case let .apiSetConnectionIncognito(connId, incognito): return "/_set incognito :\(connId) \(onOff(incognito))" + case let .apiChangeConnectionUser(connId, userId): return "/_set conn user :\(connId) \(userId)" case let .apiConnectPlan(userId, connReq): return "/_connect plan \(userId) \(connReq)" case let .apiConnect(userId, incognito, connReq): return "/_connect \(userId) incognito=\(onOff(incognito)) \(connReq)" case let .apiConnectContactViaAddress(userId, incognito, contactId): return "/_connect contact \(userId) incognito=\(onOff(incognito)) \(contactId)" @@ -292,6 +299,7 @@ public enum ChatCommand { case let .apiCallStatus(contact, callStatus): return "/_call status @\(contact.apiId) \(callStatus.rawValue)" case .apiGetNetworkStatuses: return "/_network_statuses" case let .apiChatRead(type, id, itemRange: (from, to)): return "/_read chat \(ref(type, id)) from=\(from) to=\(to)" + case let .apiChatItemsRead(type, id, itemIds): return "/_read chat items \(ref(type, id)) \(joinedIds(itemIds))" case let .apiChatUnread(type, id, unreadChat): return "/_unread chat \(ref(type, id)) \(onOff(unreadChat))" case let .receiveFile(fileId, userApprovedRelays, encrypt, inline): return "/freceive \(fileId)\(onOffParam("approved_relays", userApprovedRelays))\(onOffParam("encrypt", encrypt))\(onOffParam("inline", inline))" case let .setFileToReceive(fileId, userApprovedRelays, encrypt): return "/_set_file_to_receive \(fileId)\(onOffParam("approved_relays", userApprovedRelays))\(onOffParam("encrypt", encrypt))" @@ -348,19 +356,21 @@ public enum ChatCommand { case .apiGetChats: return "apiGetChats" case .apiGetChat: return "apiGetChat" case .apiGetChatItemInfo: return "apiGetChatItemInfo" - case .apiSendMessage: return "apiSendMessage" - case .apiCreateChatItem: return "apiCreateChatItem" + case .apiSendMessages: return "apiSendMessages" + case .apiCreateChatItems: return "apiCreateChatItems" case .apiUpdateChatItem: return "apiUpdateChatItem" case .apiDeleteChatItem: return "apiDeleteChatItem" case .apiConnectContactViaAddress: return "apiConnectContactViaAddress" case .apiDeleteMemberChatItem: return "apiDeleteMemberChatItem" case .apiChatItemReaction: return "apiChatItemReaction" - case .apiForwardChatItem: return "apiForwardChatItem" + case .apiPlanForwardChatItems: return "apiPlanForwardChatItems" + case .apiForwardChatItems: return "apiForwardChatItems" case .apiGetNtfToken: return "apiGetNtfToken" case .apiRegisterToken: return "apiRegisterToken" case .apiVerifyToken: return "apiVerifyToken" case .apiDeleteToken: return "apiDeleteToken" case .apiGetNtfMessage: return "apiGetNtfMessage" + case .apiGetConnNtfMessage: return "apiGetConnNtfMessage" case .apiNewGroup: return "apiNewGroup" case .apiAddMember: return "apiAddMember" case .apiJoinGroup: return "apiJoinGroup" @@ -404,6 +414,7 @@ public enum ChatCommand { case .apiVerifyGroupMember: return "apiVerifyGroupMember" case .apiAddContact: return "apiAddContact" case .apiSetConnectionIncognito: return "apiSetConnectionIncognito" + case .apiChangeConnectionUser: return "apiChangeConnectionUser" case .apiConnectPlan: return "apiConnectPlan" case .apiConnect: return "apiConnect" case .apiDeleteChat: return "apiDeleteChat" @@ -432,6 +443,7 @@ public enum ChatCommand { case .apiCallStatus: return "apiCallStatus" case .apiGetNetworkStatuses: return "apiGetNetworkStatuses" case .apiChatRead: return "apiChatRead" + case .apiChatItemsRead: return "apiChatItemsRead" case .apiChatUnread: return "apiChatUnread" case .receiveFile: return "receiveFile" case .setFileToReceive: return "setFileToReceive" @@ -460,6 +472,10 @@ public enum ChatCommand { "\(type.rawValue)\(id)" } + func joinedIds(_ ids: [Int64]) -> String { + ids.map { "\($0)" }.joined(separator: ",") + } + func protoServersStr(_ servers: [ServerCfg]) -> String { encodeJSON(ProtoServersConfig(servers: servers)) } @@ -556,6 +572,7 @@ public enum ChatResponse: Decodable, Error { case connectionVerified(user: UserRef, verified: Bool, expectedCode: String) case invitation(user: UserRef, connReqInvitation: String, connection: PendingContactConnection) case connectionIncognitoUpdated(user: UserRef, toConnection: PendingContactConnection) + case connectionUserChanged(user: UserRef, fromConnection: PendingContactConnection, toConnection: PendingContactConnection, newUser: UserRef) case connectionPlan(user: UserRef, connectionPlan: ConnectionPlan) case sentConfirmation(user: UserRef, connection: PendingContactConnection) case sentInvitation(user: UserRef, connection: PendingContactConnection) @@ -589,8 +606,9 @@ public enum ChatResponse: Decodable, Error { case memberSubErrors(user: UserRef, memberSubErrors: [MemberSubError]) case groupEmpty(user: UserRef, groupInfo: GroupInfo) case userContactLinkSubscribed - case newChatItem(user: UserRef, chatItem: AChatItem) - case chatItemStatusUpdated(user: UserRef, chatItem: AChatItem) + case newChatItems(user: UserRef, chatItems: [AChatItem]) + case forwardPlan(user: UserRef, chatItemIds: [Int64], forwardConfirmation: ForwardConfirmation?) + case chatItemsStatusesUpdated(user: UserRef, chatItems: [AChatItem]) case chatItemUpdated(user: UserRef, chatItem: AChatItem) case chatItemNotChanged(user: UserRef, chatItem: AChatItem) case chatItemReaction(user: UserRef, added: Bool, reaction: ACIReaction) @@ -664,8 +682,9 @@ public enum ChatResponse: Decodable, Error { case callInvitations(callInvitations: [RcvCallInvitation]) case ntfTokenStatus(status: NtfTknStatus) case ntfToken(token: DeviceToken, status: NtfTknStatus, ntfMode: NotificationsMode, ntfServer: String) - case ntfMessages(user_: User?, connEntity_: ConnectionEntity?, msgTs: Date?, ntfMessage_: NtfMsgInfo?) - case ntfMessage(user: UserRef, connEntity: ConnectionEntity, ntfMessage: NtfMsgInfo) + case ntfMessages(user_: User?, connEntity_: ConnectionEntity?, expectedMsg_: NtfMsgInfo?, receivedMsg_: NtfMsgInfo?) + case connNtfMessage(receivedMsg_: NtfMsgInfo?) + case ntfMessage(user: UserRef, connEntity: ConnectionEntity, ntfMessage: NtfMsgAckInfo) case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection) case contactDisabled(user: UserRef, contact: Contact) // remote desktop responses/events @@ -726,6 +745,7 @@ public enum ChatResponse: Decodable, Error { case .connectionVerified: return "connectionVerified" case .invitation: return "invitation" case .connectionIncognitoUpdated: return "connectionIncognitoUpdated" + case .connectionUserChanged: return "connectionUserChanged" case .connectionPlan: return "connectionPlan" case .sentConfirmation: return "sentConfirmation" case .sentInvitation: return "sentInvitation" @@ -759,8 +779,9 @@ public enum ChatResponse: Decodable, Error { case .memberSubErrors: return "memberSubErrors" case .groupEmpty: return "groupEmpty" case .userContactLinkSubscribed: return "userContactLinkSubscribed" - case .newChatItem: return "newChatItem" - case .chatItemStatusUpdated: return "chatItemStatusUpdated" + case .newChatItems: return "newChatItems" + case .forwardPlan: return "forwardPlan" + case .chatItemsStatusesUpdated: return "chatItemsStatusesUpdated" case .chatItemUpdated: return "chatItemUpdated" case .chatItemNotChanged: return "chatItemNotChanged" case .chatItemReaction: return "chatItemReaction" @@ -831,6 +852,7 @@ public enum ChatResponse: Decodable, Error { case .ntfTokenStatus: return "ntfTokenStatus" case .ntfToken: return "ntfToken" case .ntfMessages: return "ntfMessages" + case .connNtfMessage: return "connNtfMessage" case .ntfMessage: return "ntfMessage" case .contactConnectionDeleted: return "contactConnectionDeleted" case .contactDisabled: return "contactDisabled" @@ -894,6 +916,7 @@ public enum ChatResponse: Decodable, Error { case let .connectionVerified(u, verified, expectedCode): return withUser(u, "verified: \(verified)\nconnectionCode: \(expectedCode)") case let .invitation(u, connReqInvitation, connection): return withUser(u, "connReqInvitation: \(connReqInvitation)\nconnection: \(connection)") case let .connectionIncognitoUpdated(u, toConnection): return withUser(u, String(describing: toConnection)) + case let .connectionUserChanged(u, fromConnection, toConnection, newUser): return withUser(u, "fromConnection: \(String(describing: fromConnection))\ntoConnection: \(String(describing: toConnection))\newUserId: \(String(describing: newUser.userId))") case let .connectionPlan(u, connectionPlan): return withUser(u, String(describing: connectionPlan)) case let .sentConfirmation(u, connection): return withUser(u, String(describing: connection)) case let .sentInvitation(u, connection): return withUser(u, String(describing: connection)) @@ -927,8 +950,13 @@ public enum ChatResponse: Decodable, Error { case let .memberSubErrors(u, memberSubErrors): return withUser(u, String(describing: memberSubErrors)) case let .groupEmpty(u, groupInfo): return withUser(u, String(describing: groupInfo)) case .userContactLinkSubscribed: return noDetails - case let .newChatItem(u, chatItem): return withUser(u, String(describing: chatItem)) - case let .chatItemStatusUpdated(u, chatItem): return withUser(u, String(describing: chatItem)) + case let .newChatItems(u, chatItems): + let itemsString = chatItems.map { chatItem in String(describing: chatItem) }.joined(separator: "\n") + return withUser(u, itemsString) + case let .forwardPlan(u, chatItemIds, forwardConfirmation): return withUser(u, "items: \(chatItemIds) forwardConfirmation: \(String(describing: forwardConfirmation))") + case let .chatItemsStatusesUpdated(u, chatItems): + let itemsString = chatItems.map { chatItem in String(describing: chatItem) }.joined(separator: "\n") + return withUser(u, itemsString) 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))") @@ -1001,7 +1029,8 @@ public enum ChatResponse: Decodable, Error { case let .callInvitations(invs): return String(describing: invs) case let .ntfTokenStatus(status): return String(describing: status) case let .ntfToken(token, status, ntfMode, ntfServer): return "token: \(token)\nstatus: \(status.rawValue)\nntfMode: \(ntfMode.rawValue)\nntfServer: \(ntfServer)" - case let .ntfMessages(u, connEntity, msgTs, ntfMessages): return withUser(u, "connEntity: \(String(describing: connEntity))\nmsgTs: \(String(describing: msgTs))\nntfMessages: \(String(describing: ntfMessages))") + case let .ntfMessages(u, connEntity, expectedMsg_, receivedMsg_): return withUser(u, "connEntity: \(String(describing: connEntity))\nexpectedMsg_: \(String(describing: expectedMsg_))\nreceivedMsg_: \(String(describing: receivedMsg_))") + case let .connNtfMessage(receivedMsg_): return "receivedMsg_: \(String(describing: receivedMsg_))" case let .ntfMessage(u, connEntity, ntfMessage): return withUser(u, "connEntity: \(String(describing: connEntity))\nntfMessage: \(String(describing: ntfMessage))") case let .contactConnectionDeleted(u, connection): return withUser(u, String(describing: connection)) case let .contactDisabled(u, contact): return withUser(u, String(describing: contact)) @@ -1114,10 +1143,16 @@ public enum ChatPagination { } } -struct ComposedMessage: Encodable { - var fileSource: CryptoFile? +public struct ComposedMessage: Encodable { + public var fileSource: CryptoFile? var quotedItemId: Int64? - var msgContent: MsgContent + public var msgContent: MsgContent + + public init(fileSource: CryptoFile? = nil, quotedItemId: Int64? = nil, msgContent: MsgContent) { + self.fileSource = fileSource + self.quotedItemId = quotedItemId + self.msgContent = msgContent + } } public struct ArchiveConfig: Encodable { @@ -1324,6 +1359,7 @@ public struct NetCfg: Codable, Equatable { public var sessionMode = TransportSessionMode.user public var smpProxyMode: SMPProxyMode = .unknown public var smpProxyFallback: SMPProxyFallback = .allowProtected + public var smpWebPort = false public var tcpConnectTimeout: Int // microseconds public var tcpTimeout: Int // microseconds public var tcpTimeoutPerKb: Int // microseconds @@ -1455,18 +1491,22 @@ public enum OnionHosts: String, Identifiable { public enum TransportSessionMode: String, Codable, Identifiable { case user + case session + case server case entity public var text: LocalizedStringKey { switch self { - case .user: return "User profile" + case .user: return "Chat profile" + case .session: return "App session" + case .server: return "Server" case .entity: return "Connection" } } public var id: TransportSessionMode { self } - public static let values: [TransportSessionMode] = [.user, .entity] + public static let values: [TransportSessionMode] = [.user, .session, .server, .entity] } public struct KeepAliveOpts: Codable, Equatable { @@ -1572,6 +1612,13 @@ public enum NetworkStatus: Decodable, Equatable { } } +public enum ForwardConfirmation: Decodable, Hashable { + case filesNotAccepted(fileIds: [Int64]) + case filesInProgress(filesCount: Int) + case filesMissing(filesCount: Int) + case filesFailed(filesCount: Int) +} + public struct ConnNetworkStatus: Decodable { public var agentConnId: String public var networkStatus: NetworkStatus @@ -1900,7 +1947,6 @@ public enum ChatErrorType: Decodable, Hashable { case inlineFileProhibited(fileId: Int64) case invalidQuote case invalidForward - case forwardNoFile case invalidChatItemUpdate case invalidChatItemDelete case hasCurrentCall @@ -1915,6 +1961,7 @@ public enum ChatErrorType: Decodable, Hashable { case agentCommandError(message: String) case invalidFileDescription(message: String) case connectionIncognitoChangeProhibited + case connectionUserChangeProhibited case peerChatVRangeIncompatible case internalError(message: String) case exception(message: String) @@ -1994,7 +2041,7 @@ public enum SQLiteError: Decodable, Hashable { } public enum AgentErrorType: Decodable, Hashable { - case CMD(cmdErr: CommandErrorType) + case CMD(cmdErr: CommandErrorType, errContext: String) case CONN(connErr: ConnectionErrorType) case SMP(serverAddress: String, smpErr: ProtocolErrorType) case NTF(ntfErr: ProtocolErrorType) @@ -2214,6 +2261,8 @@ public struct AppSettings: Codable, Equatable { public var iosCallKitEnabled: Bool? = nil public var iosCallKitCallsInRecents: Bool? = nil public var uiProfileImageCornerRadius: Double? = nil + public var uiChatItemRoundness: Double? = nil + public var uiChatItemTail: Bool? = nil public var uiColorScheme: String? = nil public var uiDarkColorScheme: String? = nil public var uiCurrentThemeIds: [String: String]? = nil @@ -2246,6 +2295,8 @@ public struct AppSettings: Codable, Equatable { if iosCallKitEnabled != def.iosCallKitEnabled { empty.iosCallKitEnabled = iosCallKitEnabled } if iosCallKitCallsInRecents != def.iosCallKitCallsInRecents { empty.iosCallKitCallsInRecents = iosCallKitCallsInRecents } if uiProfileImageCornerRadius != def.uiProfileImageCornerRadius { empty.uiProfileImageCornerRadius = uiProfileImageCornerRadius } + if uiChatItemRoundness != def.uiChatItemRoundness { empty.uiChatItemRoundness = uiChatItemRoundness } + if uiChatItemTail != def.uiChatItemTail { empty.uiChatItemTail = uiChatItemTail } if uiColorScheme != def.uiColorScheme { empty.uiColorScheme = uiColorScheme } if uiDarkColorScheme != def.uiDarkColorScheme { empty.uiDarkColorScheme = uiDarkColorScheme } if uiCurrentThemeIds != def.uiCurrentThemeIds { empty.uiCurrentThemeIds = uiCurrentThemeIds } @@ -2279,6 +2330,8 @@ public struct AppSettings: Codable, Equatable { iosCallKitEnabled: true, iosCallKitCallsInRecents: false, uiProfileImageCornerRadius: 22.5, + uiChatItemRoundness: 0.75, + uiChatItemTail: true, uiColorScheme: DefaultTheme.SYSTEM_THEME_NAME, uiDarkColorScheme: DefaultTheme.SIMPLEX.themeName, uiCurrentThemeIds: nil as [String: String]?, diff --git a/apps/ios/SimpleXChat/AppGroup.swift b/apps/ios/SimpleXChat/AppGroup.swift index 455607ddea..5ae3c9b901 100644 --- a/apps/ios/SimpleXChat/AppGroup.swift +++ b/apps/ios/SimpleXChat/AppGroup.swift @@ -67,7 +67,7 @@ public func registerGroupDefaults() { GROUP_DEFAULT_NTF_ENABLE_LOCAL: false, GROUP_DEFAULT_NTF_ENABLE_PERIODIC: false, GROUP_DEFAULT_NETWORK_USE_ONION_HOSTS: OnionHosts.no.rawValue, - GROUP_DEFAULT_NETWORK_SESSION_MODE: TransportSessionMode.user.rawValue, + GROUP_DEFAULT_NETWORK_SESSION_MODE: TransportSessionMode.session.rawValue, GROUP_DEFAULT_NETWORK_SMP_PROXY_MODE: SMPProxyMode.unknown.rawValue, GROUP_DEFAULT_NETWORK_SMP_PROXY_FALLBACK: SMPProxyFallback.allowProtected.rawValue, GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT: NetCfg.defaults.tcpConnectTimeout, @@ -85,7 +85,7 @@ public func registerGroupDefaults() { GROUP_DEFAULT_INITIAL_RANDOM_DB_PASSPHRASE: false, GROUP_DEFAULT_APP_LOCAL_AUTH_ENABLED: true, GROUP_DEFAULT_ALLOW_SHARE_EXTENSION: false, - GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS: false, + GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS: true, GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES: true, GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE: false, GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES: true, @@ -232,7 +232,7 @@ public let networkUseOnionHostsGroupDefault = EnumDefault( public let networkSessionModeGroupDefault = EnumDefault( defaults: groupDefaults, forKey: GROUP_DEFAULT_NETWORK_SESSION_MODE, - withDefault: .user + withDefault: .session ) public let networkSMPProxyModeGroupDefault = EnumDefault( @@ -357,6 +357,7 @@ public func getNetCfg() -> NetCfg { sessionMode: sessionMode, smpProxyMode: smpProxyMode, smpProxyFallback: smpProxyFallback, + smpWebPort: false, tcpConnectTimeout: tcpConnectTimeout, tcpTimeout: tcpTimeout, tcpTimeoutPerKb: tcpTimeoutPerKb, diff --git a/apps/ios/SimpleXChat/CallTypes.swift b/apps/ios/SimpleXChat/CallTypes.swift index 9f6d98e518..da1720c134 100644 --- a/apps/ios/SimpleXChat/CallTypes.swift +++ b/apps/ios/SimpleXChat/CallTypes.swift @@ -80,6 +80,14 @@ public enum CallMediaType: String, Codable, Equatable { case audio = "audio" } +public enum CallMediaSource: String, Codable, Equatable { + case mic = "mic" + case camera = "camera" + case screenAudio = "screenAudio" + case screenVideo = "screenVideo" + case unknown = "unknown" +} + public enum VideoCamera: String, Codable, Equatable { case user = "user" case environment = "environment" diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index 1a9cf4a216..45dab17cf2 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -17,6 +17,7 @@ public struct User: Identifiable, Decodable, UserLike, NamedChat, Hashable { public var profile: LocalProfile public var fullPreferences: FullPreferences public var activeUser: Bool + public var activeOrder: Int64 public var displayName: String { get { profile.displayName } } public var fullName: String { get { profile.fullName } } @@ -49,6 +50,7 @@ public struct User: Identifiable, Decodable, UserLike, NamedChat, Hashable { profile: LocalProfile.sampleData, fullPreferences: FullPreferences.sampleData, activeUser: true, + activeOrder: 0, showNtfs: true, sendRcptsContacts: true, sendRcptsSmallGroups: false @@ -1850,6 +1852,7 @@ public struct PendingContactConnection: Decodable, NamedChat, Hashable { public enum ConnStatus: String, Decodable, Hashable { case new = "new" + case prepared = "prepared" case joined = "joined" case requested = "requested" case accepted = "accepted" @@ -1861,6 +1864,7 @@ public enum ConnStatus: String, Decodable, Hashable { get { switch self { case .new: return true + case .prepared: return false case .joined: return false case .requested: return true case .accepted: return true @@ -2238,32 +2242,42 @@ public struct MemberSubError: Decodable, Hashable { } public enum ConnectionEntity: Decodable, Hashable { - case rcvDirectMsgConnection(contact: Contact?) - case rcvGroupMsgConnection(groupInfo: GroupInfo, groupMember: GroupMember) - case sndFileConnection(sndFileTransfer: SndFileTransfer) - case rcvFileConnection(rcvFileTransfer: RcvFileTransfer) - case userContactConnection(userContact: UserContact) + case rcvDirectMsgConnection(entityConnection: Connection, contact: Contact?) + case rcvGroupMsgConnection(entityConnection: Connection, groupInfo: GroupInfo, groupMember: GroupMember) + case sndFileConnection(entityConnection: Connection, sndFileTransfer: SndFileTransfer) + case rcvFileConnection(entityConnection: Connection, rcvFileTransfer: RcvFileTransfer) + case userContactConnection(entityConnection: Connection, userContact: UserContact) public var id: String? { switch self { - case let .rcvDirectMsgConnection(contact): + case let .rcvDirectMsgConnection(_, contact): return contact?.id - case let .rcvGroupMsgConnection(_, groupMember): + case let .rcvGroupMsgConnection(_, _, groupMember): return groupMember.id - case let .userContactConnection(userContact): + case let .userContactConnection(_, userContact): return userContact.id default: return nil } } + public var conn: Connection { + switch self { + case let .rcvDirectMsgConnection(entityConnection, _): entityConnection + case let .rcvGroupMsgConnection(entityConnection, _, _): entityConnection + case let .sndFileConnection(entityConnection, _): entityConnection + case let .rcvFileConnection(entityConnection, _): entityConnection + case let .userContactConnection(entityConnection, _): entityConnection + } + } + public var ntfsEnabled: Bool { switch self { - case let .rcvDirectMsgConnection(contact): return contact?.chatSettings.enableNtfs == .all - case let .rcvGroupMsgConnection(groupInfo, _): return groupInfo.chatSettings.enableNtfs == .all + case let .rcvDirectMsgConnection(_, contact): return contact?.chatSettings.enableNtfs == .all + case let .rcvGroupMsgConnection(_, groupInfo, _): return groupInfo.chatSettings.enableNtfs == .all case .sndFileConnection: return false case .rcvFileConnection: return false - case let .userContactConnection(userContact): return userContact.groupId == nil + case let .userContactConnection(_, userContact): return userContact.groupId == nil } } } @@ -2273,6 +2287,11 @@ public struct NtfMsgInfo: Decodable, Hashable { public var msgTs: Date } +public struct NtfMsgAckInfo: Decodable, Hashable { + public var msgId: String + public var msgTs_: Date? +} + public struct ChatItemDeletion: Decodable, Hashable { public var deletedChatItem: AChatItem public var toChatItem: AChatItem? = nil @@ -2688,6 +2707,13 @@ public enum CIDirection: Decodable, Hashable { } } } + + public func sameDirection(_ dir: CIDirection) -> Bool { + switch (self, dir) { + case let (.groupRcv(m1), .groupRcv(m2)): m1.groupMemberId == m2.groupMemberId + default: sent == dir.sent + } + } } public struct CIMeta: Decodable, Hashable { @@ -2706,7 +2732,7 @@ public struct CIMeta: Decodable, Hashable { public var deletable: Bool public var editable: Bool - public var timestampText: Text { get { formatTimestampText(itemTs) } } + public var timestampText: Text { Text(formatTimestampMeta(itemTs)) } public var recent: Bool { updatedAt + 10 > .now } public var isLive: Bool { itemLive == true } public var disappearing: Bool { !isRcvNew && itemTimed?.deleteAt != nil } @@ -2716,10 +2742,6 @@ public struct CIMeta: Decodable, Hashable { return false } - public func statusIcon(_ metaColor: Color/* = .secondary*/, _ primaryColor: Color = .accentColor) -> (String, Color)? { - itemStatus.statusIcon(metaColor, primaryColor) - } - public static func getSample(_ id: Int64, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, deletable: Bool = true, editable: Bool = true) -> CIMeta { CIMeta( itemId: id, @@ -2760,9 +2782,20 @@ public struct CITimed: Decodable, Hashable { let msgTimeFormat = Date.FormatStyle.dateTime.hour().minute() let msgDateFormat = Date.FormatStyle.dateTime.day(.twoDigits).month(.twoDigits) +let msgDateYearFormat = Date.FormatStyle.dateTime.day(.twoDigits).month(.twoDigits).year(.twoDigits) public func formatTimestampText(_ date: Date) -> Text { - return Text(date, format: recent(date) ? msgTimeFormat : msgDateFormat) + Text(verbatim: date.formatted( + recent(date) + ? msgTimeFormat + : Calendar.current.isDate(date, equalTo: .now, toGranularity: .year) + ? msgDateFormat + : msgDateYearFormat + )) +} + +public func formatTimestampMeta(_ date: Date) -> String { + date.formatted(date: .omitted, time: .shortened) } private func recent(_ date: Date) -> Bool { @@ -2804,22 +2837,37 @@ public enum CIStatus: Decodable, Hashable { case .invalid: return "invalid" } } - - public func statusIcon(_ metaColor: Color/* = .secondary*/, _ primaryColor: Color = .accentColor) -> (String, Color)? { + + public var sent: Bool { switch self { - case .sndNew: return nil - case .sndSent: return ("checkmark", metaColor) - case let .sndRcvd(msgRcptStatus, _): + case .sndNew: true + case .sndSent: true + case .sndRcvd: true + case .sndErrorAuth: true + case .sndError: true + case .sndWarning: true + case .rcvNew: false + case .rcvRead: false + case .invalid: false + } + } + + public func statusIcon(_ metaColor: Color, _ paleMetaColor: Color, _ primaryColor: Color = .accentColor) -> (Image, Color)? { + switch self { + case .sndNew: nil + case let .sndSent(sndProgress): + (Image("checkmark.wide"), sndProgress == .partial ? paleMetaColor : metaColor) + case let .sndRcvd(msgRcptStatus, sndProgress): switch msgRcptStatus { - case .ok: return ("checkmark", metaColor) - case .badMsgHash: return ("checkmark", .red) + case .ok: (Image("checkmark.2"), sndProgress == .partial ? paleMetaColor : metaColor) + case .badMsgHash: (Image("checkmark.2"), .red) } - case .sndErrorAuth: return ("multiply", .red) - case .sndError: return ("multiply", .red) - case .sndWarning: return ("exclamationmark.triangle.fill", .orange) - case .rcvNew: return ("circlebadge.fill", primaryColor) - case .rcvRead: return nil - case .invalid: return ("questionmark", metaColor) + case .sndErrorAuth: (Image(systemName: "multiply"), .red) + case .sndError: (Image(systemName: "multiply"), .red) + case .sndWarning: (Image(systemName: "exclamationmark.triangle.fill"), .orange) + case .rcvNew: (Image(systemName: "circlebadge.fill"), primaryColor) + case .rcvRead: nil + case .invalid: (Image(systemName: "questionmark"), metaColor) } } @@ -2848,6 +2896,13 @@ public enum CIStatus: Decodable, Hashable { ) } } + + public var isSndRcvd: Bool { + switch self { + case .sndRcvd: return true + default: return false + } + } } public enum SndError: Decodable, Hashable { @@ -2914,20 +2969,20 @@ public enum GroupSndStatus: Decodable, Hashable { case warning(agentError: SndError) case invalid(text: String) - public func statusIcon(_ metaColor: Color/* = .secondary*/, _ primaryColor: Color = .accentColor) -> (String, Color) { + public func statusIcon(_ metaColor: Color, _ primaryColor: Color = .accentColor) -> (Image, Color) { switch self { - case .new: return ("ellipsis", metaColor) - case .forwarded: return ("chevron.forward.2", metaColor) - case .inactive: return ("person.badge.minus", metaColor) - case .sent: return ("checkmark", metaColor) + case .new: (Image(systemName: "ellipsis"), metaColor) + case .forwarded: (Image(systemName: "chevron.forward.2"), metaColor) + case .inactive: (Image(systemName: "person.badge.minus"), metaColor) + case .sent: (Image("checkmark.wide"), metaColor) case let .rcvd(msgRcptStatus): switch msgRcptStatus { - case .ok: return ("checkmark", metaColor) - case .badMsgHash: return ("checkmark", .red) + case .ok: (Image("checkmark.2"), metaColor) + case .badMsgHash: (Image("checkmark.2"), .red) } - case .error: return ("multiply", .red) - case .warning: return ("exclamationmark.triangle.fill", .orange) - case .invalid: return ("questionmark", metaColor) + case .error: (Image(systemName: "multiply"), .red) + case .warning: (Image(systemName: "exclamationmark.triangle.fill"), .orange) + case .invalid: (Image(systemName: "questionmark"), metaColor) } } @@ -3144,6 +3199,13 @@ public enum CIContent: Decodable, ItemContent, Hashable { default: return false } } + + public var isSndCall: Bool { + switch self { + case .sndCall: return true + default: return false + } + } } public enum MsgDecryptError: String, Decodable, Hashable { diff --git a/apps/ios/SimpleXChat/ImageUtils.swift b/apps/ios/SimpleXChat/ImageUtils.swift index 67218a781e..9702408c27 100644 --- a/apps/ios/SimpleXChat/ImageUtils.swift +++ b/apps/ios/SimpleXChat/ImageUtils.swift @@ -100,7 +100,7 @@ public func resizeImageToDataSize(_ image: UIImage, maxDataSize: Int64, hasAlpha return data } -public func resizeImageToStrSize(_ image: UIImage, maxDataSize: Int64) -> String? { +public func resizeImageToStrSizeSync(_ image: UIImage, maxDataSize: Int64) -> String? { var img = image let hasAlpha = imageHasAlpha(image) var str = compressImageStr(img, hasAlpha: hasAlpha) @@ -116,7 +116,15 @@ public func resizeImageToStrSize(_ image: UIImage, maxDataSize: Int64) -> String return str } +public func resizeImageToStrSize(_ image: UIImage, maxDataSize: Int64) async -> String? { + resizeImageToStrSizeSync(image, maxDataSize: maxDataSize) +} + public func compressImageStr(_ image: UIImage, _ compressionQuality: CGFloat = 0.85, hasAlpha: Bool) -> String? { +// // Heavy workload to verify if UI gets blocked by the call +// for i in 0..<100 { +// print(image.jpegData(compressionQuality: Double(i) / 100)?.count ?? 0, terminator: ", ") +// } let ext = hasAlpha ? "png" : "jpg" if let data = hasAlpha ? image.pngData() : image.jpegData(compressionQuality: compressionQuality) { return "data:image/\(ext);base64,\(data.base64EncodedString())" @@ -383,16 +391,34 @@ extension UIImage { } return self } +} - public convenience init?(base64Encoded: String?) { - if let base64Encoded, let data = Data(base64Encoded: dropImagePrefix(base64Encoded)) { - self.init(data: data) +public func imageFromBase64(_ base64Encoded: String?) -> UIImage? { + if let base64Encoded { + if let img = imageCache.object(forKey: base64Encoded as NSString) { + return img + } else if let data = Data(base64Encoded: dropImagePrefix(base64Encoded)), + let img = UIImage(data: data) { + imageCacheQueue.async { + imageCache.setObject(img, forKey: base64Encoded as NSString) + } + return img } else { return nil } + } else { + return nil } } +private let imageCacheQueue = DispatchQueue.global(qos: .background) + +private var imageCache: NSCache = { + var cache = NSCache() + cache.countLimit = 1000 + return cache +}() + public func getLinkPreview(url: URL, cb: @escaping (LinkPreview?) -> Void) { logger.debug("getLinkMetadata: fetching URL preview") LPMetadataProvider().startFetchingMetadata(for: url){ metadata, error in @@ -408,7 +434,7 @@ public func getLinkPreview(url: URL, cb: @escaping (LinkPreview?) -> Void) { 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 resized = resizeImageToStrSizeSync(image, maxDataSize: 14000), let title = metadata.title, let uri = metadata.originalURL { linkPreview = LinkPreview(uri: uri, title: title, image: resized) diff --git a/apps/ios/SimpleXChat/Notifications.swift b/apps/ios/SimpleXChat/Notifications.swift index 4b43595372..65bb06a7e8 100644 --- a/apps/ios/SimpleXChat/Notifications.swift +++ b/apps/ios/SimpleXChat/Notifications.swift @@ -94,7 +94,7 @@ public func createConnectionEventNtf(_ user: User, _ connEntity: ConnectionEntit var body: String? = nil var targetContentIdentifier: String? = nil switch connEntity { - case let .rcvDirectMsgConnection(contact): + case let .rcvDirectMsgConnection(_, contact): if let contact = contact { title = hideContent ? contactHidden : "\(contact.chatViewName):" targetContentIdentifier = contact.id @@ -102,7 +102,7 @@ public func createConnectionEventNtf(_ user: User, _ connEntity: ConnectionEntit title = NSLocalizedString("New contact:", comment: "notification") } body = NSLocalizedString("message received", comment: "notification") - case let .rcvGroupMsgConnection(groupInfo, groupMember): + case let .rcvGroupMsgConnection(_, groupInfo, groupMember): title = groupMsgNtfTitle(groupInfo, groupMember, hideContent: hideContent) body = NSLocalizedString("message received", comment: "notification") targetContentIdentifier = groupInfo.id diff --git a/apps/ios/bg.lproj/Localizable.strings b/apps/ios/bg.lproj/Localizable.strings index aa43902c81..ff8a76828c 100644 --- a/apps/ios/bg.lproj/Localizable.strings +++ b/apps/ios/bg.lproj/Localizable.strings @@ -697,7 +697,7 @@ /* No comment provided by engineer. */ "Can't invite contacts!" = "Не може да поканят контактите!"; -/* No comment provided by engineer. */ +/* alert button */ "Cancel" = "Отказ"; /* No comment provided by engineer. */ @@ -709,7 +709,7 @@ /* No comment provided by engineer. */ "Cannot access keychain to save database password" = "Няма достъп до Keychain за запазване на паролата за базата данни"; -/* No comment provided by engineer. */ +/* alert title */ "Cannot receive file" = "Файлът не може да бъде получен"; /* No comment provided by engineer. */ @@ -791,6 +791,9 @@ /* No comment provided by engineer. */ "Chat preferences" = "Чат настройки"; +/* No comment provided by engineer. */ +"Chat profile" = "Потребителски профил"; + /* No comment provided by engineer. */ "Chats" = "Чатове"; @@ -1395,7 +1398,8 @@ /* No comment provided by engineer. */ "Downgrade and open chat" = "Понижи версията и отвори чата"; -/* chat item action */ +/* alert button + chat item action */ "Download" = "Изтегли"; /* No comment provided by engineer. */ @@ -1683,7 +1687,7 @@ /* No comment provided by engineer. */ "Error opening chat" = "Грешка при отваряне на чата"; -/* No comment provided by engineer. */ +/* alert title */ "Error receiving file" = "Грешка при получаване на файл"; /* No comment provided by engineer. */ @@ -1731,7 +1735,7 @@ /* No comment provided by engineer. */ "Error stopping chat" = "Грешка при спиране на чата"; -/* No comment provided by engineer. */ +/* alertTitle */ "Error switching profile!" = "Грешка при смяна на профил!"; /* No comment provided by engineer. */ @@ -1758,8 +1762,7 @@ /* No comment provided by engineer. */ "Error: " = "Грешка: "; -/* file error text - snd error text */ +/* alert message */ "Error: %@" = "Грешка: %@"; /* No comment provided by engineer. */ @@ -1897,9 +1900,6 @@ /* No comment provided by engineer. */ "Full name (optional)" = "Пълно име (незадължително)"; -/* No comment provided by engineer. */ -"Full name:" = "Пълно име:"; - /* No comment provided by engineer. */ "Fully decentralized – visible only to members." = "Напълно децентрализирана – видима е само за членовете."; @@ -2688,7 +2688,7 @@ /* feature offered item */ "offered %@: %@" = "предлага %1$@: %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "Ок"; /* No comment provided by engineer. */ @@ -2940,12 +2940,6 @@ /* No comment provided by engineer. */ "Profile images" = "Профилни изображения"; -/* No comment provided by engineer. */ -"Profile name" = "Име на профила"; - -/* No comment provided by engineer. */ -"Profile name:" = "Име на профила:"; - /* No comment provided by engineer. */ "Profile password" = "Профилна парола"; @@ -3211,10 +3205,11 @@ /* No comment provided by engineer. */ "Safer groups" = "По-безопасни групи"; -/* chat item action */ +/* alert button + chat item action */ "Save" = "Запази"; -/* No comment provided by engineer. */ +/* alert button */ "Save (and notify contacts)" = "Запази (и уведоми контактите)"; /* No comment provided by engineer. */ @@ -3229,9 +3224,6 @@ /* No comment provided by engineer. */ "Save archive" = "Запази архив"; -/* No comment provided by engineer. */ -"Save auto-accept settings" = "Запази настройките за автоматично приемане"; - /* No comment provided by engineer. */ "Save group profile" = "Запази профила на групата"; @@ -3253,9 +3245,6 @@ /* No comment provided by engineer. */ "Save servers?" = "Запази сървърите?"; -/* No comment provided by engineer. */ -"Save settings?" = "Запази настройките?"; - /* No comment provided by engineer. */ "Save welcome message?" = "Запази съобщението при посрещане?"; @@ -3379,7 +3368,7 @@ /* No comment provided by engineer. */ "Send up to 100 last messages to new members." = "Изпращане до последните 100 съобщения на нови членове."; -/* No comment provided by engineer. */ +/* alert message */ "Sender cancelled file transfer." = "Подателят отмени прехвърлянето на файла."; /* No comment provided by engineer. */ @@ -4003,9 +3992,6 @@ /* No comment provided by engineer. */ "Use the app while in the call." = "Използвайте приложението по време на разговора."; -/* No comment provided by engineer. */ -"User profile" = "Потребителски профил"; - /* No comment provided by engineer. */ "Using SimpleX Chat servers." = "Използват се сървърите на SimpleX Chat."; @@ -4180,9 +4166,6 @@ /* No comment provided by engineer. */ "you" = "вие"; -/* No comment provided by engineer. */ -"You" = "Вие"; - /* No comment provided by engineer. */ "You **must not** use the same database on two devices." = "**Не трябва** да използвате една и съща база данни на две устройства."; @@ -4430,7 +4413,7 @@ "Your profile **%@** will be shared." = "Вашият профил **%@** ще бъде споделен."; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Вашият профил се съхранява на вашето устройство и се споделя само с вашите контакти.\nSimpleX сървърите не могат да видят вашия профил."; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Вашият профил се съхранява на вашето устройство и се споделя само с вашите контакти. SimpleX сървърите не могат да видят вашия профил."; /* No comment provided by engineer. */ "Your profile, contacts and delivered messages are stored on your device." = "Вашият профил, контакти и доставени съобщения се съхраняват на вашето устройство."; diff --git a/apps/ios/cs.lproj/Localizable.strings b/apps/ios/cs.lproj/Localizable.strings index 220550c682..618cd90aba 100644 --- a/apps/ios/cs.lproj/Localizable.strings +++ b/apps/ios/cs.lproj/Localizable.strings @@ -562,7 +562,7 @@ /* No comment provided by engineer. */ "Can't invite contacts!" = "Nelze pozvat kontakty!"; -/* No comment provided by engineer. */ +/* alert button */ "Cancel" = "Zrušit"; /* feature offered item */ @@ -571,7 +571,7 @@ /* No comment provided by engineer. */ "Cannot access keychain to save database password" = "Nelze získat přístup ke klíčence pro uložení hesla databáze"; -/* No comment provided by engineer. */ +/* alert title */ "Cannot receive file" = "Nelze přijmout soubor"; /* No comment provided by engineer. */ @@ -644,6 +644,9 @@ /* No comment provided by engineer. */ "Chat preferences" = "Předvolby chatu"; +/* No comment provided by engineer. */ +"Chat profile" = "Profil uživatele"; + /* No comment provided by engineer. */ "Chats" = "Chaty"; @@ -1380,7 +1383,7 @@ /* No comment provided by engineer. */ "Error loading %@ servers" = "Chyba načítání %@ serverů"; -/* No comment provided by engineer. */ +/* alert title */ "Error receiving file" = "Chyba při příjmu souboru"; /* No comment provided by engineer. */ @@ -1422,7 +1425,7 @@ /* No comment provided by engineer. */ "Error stopping chat" = "Chyba při zastavení chatu"; -/* No comment provided by engineer. */ +/* alertTitle */ "Error switching profile!" = "Chyba při přepínání profilu!"; /* No comment provided by engineer. */ @@ -1443,8 +1446,7 @@ /* No comment provided by engineer. */ "Error: " = "Chyba: "; -/* file error text - snd error text */ +/* alert message */ "Error: %@" = "Chyba: %@"; /* No comment provided by engineer. */ @@ -1543,9 +1545,6 @@ /* No comment provided by engineer. */ "Full name (optional)" = "Celé jméno (volitelně)"; -/* No comment provided by engineer. */ -"Full name:" = "Celé jméno:"; - /* No comment provided by engineer. */ "Fully re-implemented - work in background!" = "Plně přepracováno, prácuje na pozadí!"; @@ -2190,7 +2189,7 @@ /* feature offered item */ "offered %@: %@" = "nabídl %1$@: %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "Ok"; /* No comment provided by engineer. */ @@ -2602,10 +2601,11 @@ /* No comment provided by engineer. */ "Run chat" = "Spustit chat"; -/* chat item action */ +/* alert button + chat item action */ "Save" = "Uložit"; -/* No comment provided by engineer. */ +/* alert button */ "Save (and notify contacts)" = "Uložit (a informovat kontakty)"; /* No comment provided by engineer. */ @@ -2620,9 +2620,6 @@ /* No comment provided by engineer. */ "Save archive" = "Uložit archiv"; -/* No comment provided by engineer. */ -"Save auto-accept settings" = "Uložit nastavení automatického přijímání"; - /* No comment provided by engineer. */ "Save group profile" = "Uložení profilu skupiny"; @@ -2644,9 +2641,6 @@ /* No comment provided by engineer. */ "Save servers?" = "Uložit servery?"; -/* No comment provided by engineer. */ -"Save settings?" = "Uložit nastavení?"; - /* No comment provided by engineer. */ "Save welcome message?" = "Uložit uvítací zprávu?"; @@ -2743,7 +2737,7 @@ /* No comment provided by engineer. */ "Send them from gallery or custom keyboards." = "Odeslat je z galerie nebo vlastní klávesnice."; -/* No comment provided by engineer. */ +/* alert message */ "Sender cancelled file transfer." = "Odesílatel zrušil přenos souboru."; /* No comment provided by engineer. */ @@ -3238,9 +3232,6 @@ /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "Používat servery SimpleX Chat?"; -/* No comment provided by engineer. */ -"User profile" = "Profil uživatele"; - /* No comment provided by engineer. */ "Using SimpleX Chat servers." = "Používat servery SimpleX Chat."; @@ -3358,9 +3349,6 @@ /* pref value */ "yes" = "ano"; -/* No comment provided by engineer. */ -"You" = "Vy"; - /* No comment provided by engineer. */ "You accepted connection" = "Přijali jste spojení"; @@ -3554,7 +3542,7 @@ "Your profile **%@** will be shared." = "Váš profil **%@** bude sdílen."; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Váš profil je uložen ve vašem zařízení a sdílen pouze s vašimi kontakty.\nServery SimpleX nevidí váš profil."; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Váš profil je uložen ve vašem zařízení a sdílen pouze s vašimi kontakty. Servery SimpleX nevidí váš profil."; /* No comment provided by engineer. */ "Your profile, contacts and delivered messages are stored on your device." = "Váš profil, kontakty a doručené zprávy jsou uloženy ve vašem zařízení."; diff --git a/apps/ios/de.lproj/Localizable.strings b/apps/ios/de.lproj/Localizable.strings index bab14eda6b..7334314c3e 100644 --- a/apps/ios/de.lproj/Localizable.strings +++ b/apps/ios/de.lproj/Localizable.strings @@ -160,6 +160,9 @@ /* notification title */ "%@ wants to connect!" = "%@ will sich mit Ihnen verbinden!"; +/* format for date separator in chat */ +"%@, %@" = "%1$@, %2$@"; + /* No comment provided by engineer. */ "%@, %@ and %lld members" = "%@, %@ und %lld Mitglieder"; @@ -172,9 +175,24 @@ /* time interval */ "%d days" = "%d Tage"; +/* forward confirmation reason */ +"%d file(s) are still being downloaded." = "%d Datei(en) wird/werden immer noch heruntergeladen."; + +/* forward confirmation reason */ +"%d file(s) failed to download." = "Bei %d Datei(en) ist das Herunterladen fehlgeschlagen."; + +/* forward confirmation reason */ +"%d file(s) were deleted." = "%d Datei(en) wurde(n) gelöscht."; + +/* forward confirmation reason */ +"%d file(s) were not downloaded." = "%d Datei(en) wurde(n) nicht heruntergeladen."; + /* time interval */ "%d hours" = "%d Stunden"; +/* alert title */ +"%d messages not forwarded" = "%d Nachrichten wurden nicht weitergeleitet"; + /* time interval */ "%d min" = "%d min"; @@ -454,7 +472,7 @@ /* No comment provided by engineer. */ "All new messages from %@ will be hidden!" = "Von %@ werden alle neuen Nachrichten ausgeblendet!"; -/* No comment provided by engineer. */ +/* profile dropdown */ "All profiles" = "Alle Profile"; /* No comment provided by engineer. */ @@ -577,6 +595,9 @@ /* No comment provided by engineer. */ "App passcode is replaced with self-destruct passcode." = "App-Zugangscode wurde durch den Selbstzerstörungs-Zugangscode ersetzt."; +/* No comment provided by engineer. */ +"App session" = "App-Sitzung"; + /* No comment provided by engineer. */ "App version" = "App Version"; @@ -649,6 +670,9 @@ /* No comment provided by engineer. */ "Auto-accept images" = "Bilder automatisch akzeptieren"; +/* alert title */ +"Auto-accept settings" = "Einstellungen automatisch akzeptieren"; + /* No comment provided by engineer. */ "Back" = "Zurück"; @@ -670,15 +694,30 @@ /* No comment provided by engineer. */ "Bad message ID" = "Falsche Nachrichten-ID"; +/* No comment provided by engineer. */ +"Better calls" = "Verbesserte Anrufe"; + /* No comment provided by engineer. */ "Better groups" = "Bessere Gruppen"; +/* No comment provided by engineer. */ +"Better message dates." = "Verbesserte Nachrichten-Datumsinformation"; + /* No comment provided by engineer. */ "Better messages" = "Verbesserungen bei Nachrichten"; /* No comment provided by engineer. */ "Better networking" = "Kontrollieren Sie Ihr Netzwerk"; +/* No comment provided by engineer. */ +"Better notifications" = "Verbesserte Benachrichtigungen"; + +/* No comment provided by engineer. */ +"Better security ✅" = "Verbesserte Sicherheit ✅"; + +/* No comment provided by engineer. */ +"Better user experience" = "Verbesserte Nutzer-Erfahrung"; + /* No comment provided by engineer. */ "Black" = "Schwarz"; @@ -781,7 +820,7 @@ /* No comment provided by engineer. */ "Can't message member" = "Mitglied kann nicht benachrichtigt werden"; -/* No comment provided by engineer. */ +/* alert button */ "Cancel" = "Abbrechen"; /* No comment provided by engineer. */ @@ -796,7 +835,7 @@ /* No comment provided by engineer. */ "Cannot forward message" = "Die Nachricht kann nicht weitergeleitet werden"; -/* No comment provided by engineer. */ +/* alert title */ "Cannot receive file" = "Datei kann nicht empfangen werden"; /* snd error text */ @@ -890,6 +929,12 @@ /* No comment provided by engineer. */ "Chat preferences" = "Chat-Präferenzen"; +/* alert message */ +"Chat preferences were changed." = "Die Chat-Präferenzen wurden geändert."; + +/* No comment provided by engineer. */ +"Chat profile" = "Benutzerprofil"; + /* No comment provided by engineer. */ "Chat theme" = "Chat-Design"; @@ -921,16 +966,16 @@ "Chunks uploaded" = "Daten-Pakete hochgeladen"; /* swipe action */ -"Clear" = "Löschen"; +"Clear" = "Entfernen"; /* No comment provided by engineer. */ -"Clear conversation" = "Chatinhalte löschen"; +"Clear conversation" = "Chat-Inhalte entfernen"; /* No comment provided by engineer. */ -"Clear conversation?" = "Unterhaltung löschen?"; +"Clear conversation?" = "Chat-Inhalte entfernen?"; /* No comment provided by engineer. */ -"Clear private notes?" = "Private Notizen löschen?"; +"Clear private notes?" = "Private Notizen entfernen?"; /* No comment provided by engineer. */ "Clear verification" = "Überprüfung zurücknehmen"; @@ -1167,7 +1212,7 @@ "Continue" = "Weiter"; /* No comment provided by engineer. */ -"Conversation deleted!" = "Unterhaltung gelöscht!"; +"Conversation deleted!" = "Chat-Inhalte entfernt!"; /* No comment provided by engineer. */ "Copy" = "Kopieren"; @@ -1178,6 +1223,9 @@ /* No comment provided by engineer. */ "Core version: v%@" = "Core Version: v%@"; +/* No comment provided by engineer. */ +"Corner" = "Abrundung Ecken"; + /* No comment provided by engineer. */ "Correct name to %@?" = "Richtiger Name für %@?"; @@ -1259,6 +1307,9 @@ /* No comment provided by engineer. */ "Custom time" = "Zeit anpassen"; +/* No comment provided by engineer. */ +"Customizable message shape." = "Anpassbares Format des Nachrichtenfelds"; + /* No comment provided by engineer. */ "Customize theme" = "Design anpassen"; @@ -1449,6 +1500,9 @@ /* No comment provided by engineer. */ "Delete old database?" = "Alte Datenbank löschen?"; +/* No comment provided by engineer. */ +"Delete or moderate up to 200 messages." = "Bis zu 200 Nachrichten löschen oder moderieren"; + /* No comment provided by engineer. */ "Delete pending connection?" = "Ausstehende Verbindung löschen?"; @@ -1611,6 +1665,9 @@ /* No comment provided by engineer. */ "Do NOT send messages directly, even if your or destination server does not support private routing." = "Nachrichten werden nicht direkt versendet, selbst wenn Ihr oder der Zielserver kein privates Routing unterstützt."; +/* No comment provided by engineer. */ +"Do not use credentials with proxy." = "Verwenden Sie keine Anmeldeinformationen mit einem Proxy."; + /* No comment provided by engineer. */ "Do NOT use private routing." = "Sie nutzen KEIN privates Routing."; @@ -1629,7 +1686,8 @@ /* No comment provided by engineer. */ "Downgrade and open chat" = "Datenbank herabstufen und den Chat öffnen"; -/* chat item action */ +/* alert button + chat item action */ "Download" = "Herunterladen"; /* No comment provided by engineer. */ @@ -1641,6 +1699,9 @@ /* server test step */ "Download file" = "Datei herunterladen"; +/* alert action */ +"Download files" = "Dateien herunterladen"; + /* No comment provided by engineer. */ "Downloaded" = "Heruntergeladen"; @@ -1857,12 +1918,18 @@ /* No comment provided by engineer. */ "Error changing address" = "Fehler beim Wechseln der Empfängeradresse"; +/* No comment provided by engineer. */ +"Error changing connection profile" = "Fehler beim Wechseln des Verbindungs-Profils"; + /* No comment provided by engineer. */ "Error changing role" = "Fehler beim Ändern der Rolle"; /* No comment provided by engineer. */ "Error changing setting" = "Fehler beim Ändern der Einstellung"; +/* No comment provided by engineer. */ +"Error changing to incognito!" = "Fehler beim Wechseln zum Inkognito-Profil!"; + /* No comment provided by engineer. */ "Error connecting to forwarding server %@. Please try later." = "Fehler beim Verbinden mit dem Weiterleitungsserver %@. Bitte versuchen Sie es später erneut."; @@ -1936,9 +2003,12 @@ "Error loading %@ servers" = "Fehler beim Laden von %@ Servern"; /* No comment provided by engineer. */ -"Error opening chat" = "Fehler beim Öffnen des Chats"; +"Error migrating settings" = "Fehler beim Migrieren der Einstellungen"; /* No comment provided by engineer. */ +"Error opening chat" = "Fehler beim Öffnen des Chats"; + +/* alert title */ "Error receiving file" = "Fehler beim Empfangen der Datei"; /* No comment provided by engineer. */ @@ -1996,6 +2066,9 @@ "Error stopping chat" = "Fehler beim Beenden des Chats"; /* No comment provided by engineer. */ +"Error switching profile" = "Fehler beim Wechseln des Profils"; + +/* alertTitle */ "Error switching profile!" = "Fehler beim Umschalten des Profils!"; /* No comment provided by engineer. */ @@ -2022,8 +2095,7 @@ /* No comment provided by engineer. */ "Error: " = "Fehler: "; -/* file error text - snd error text */ +/* alert message */ "Error: %@" = "Fehler: %@"; /* No comment provided by engineer. */ @@ -2048,7 +2120,7 @@ "Expand" = "Erweitern"; /* No comment provided by engineer. */ -"expired" = "abgelaufen"; +"expired" = "Abgelaufen"; /* No comment provided by engineer. */ "Export database" = "Datenbank exportieren"; @@ -2083,6 +2155,9 @@ /* No comment provided by engineer. */ "File error" = "Datei-Fehler"; +/* alert message */ +"File errors:\n%@" = "Datei-Fehler:\n%@"; + /* file error text */ "File not found - most likely file was deleted or cancelled." = "Datei nicht gefunden - höchstwahrscheinlich wurde die Datei gelöscht oder der Transfer abgebrochen."; @@ -2164,9 +2239,21 @@ /* chat item action */ "Forward" = "Weiterleiten"; +/* alert title */ +"Forward %d message(s)?" = "%d Nachricht(en) weiterleiten?"; + /* No comment provided by engineer. */ "Forward and save messages" = "Nachrichten weiterleiten und speichern"; +/* alert action */ +"Forward messages" = "Nachrichten weiterleiten"; + +/* alert message */ +"Forward messages without files?" = "Nachrichten ohne Dateien weiterleiten?"; + +/* No comment provided by engineer. */ +"Forward up to 20 messages at once." = "Bis zu 20 Nachrichten auf einmal weiterleiten"; + /* No comment provided by engineer. */ "forwarded" = "weitergeleitet"; @@ -2176,6 +2263,9 @@ /* No comment provided by engineer. */ "Forwarded from" = "Weitergeleitet aus"; +/* No comment provided by engineer. */ +"Forwarding %lld messages" = "%lld Nachricht(en) wird/werden weitergeleitet"; + /* No comment provided by engineer. */ "Forwarding server %@ failed to connect to destination server %@. Please try later." = "Weiterleitungsserver %@ konnte sich nicht mit dem Zielserver %@ verbinden. Bitte versuchen Sie es später erneut."; @@ -2203,9 +2293,6 @@ /* No comment provided by engineer. */ "Full name (optional)" = "Vollständiger Name (optional)"; -/* No comment provided by engineer. */ -"Full name:" = "Vollständiger Name:"; - /* No comment provided by engineer. */ "Fully decentralized – visible only to members." = "Vollständig dezentralisiert – nur für Mitglieder sichtbar."; @@ -2306,7 +2393,7 @@ "Group will be deleted for all members - this cannot be undone!" = "Die Gruppe wird für alle Mitglieder gelöscht. Dies kann nicht rückgängig gemacht werden!"; /* No comment provided by engineer. */ -"Group will be deleted for you - this cannot be undone!" = "Die Gruppe wird für Sie gelöscht. Dies kann nicht rückgängig gemacht werden!"; +"Group will be deleted for you - this cannot be undone!" = "Die Gruppe wird nur bei Ihnen gelöscht. Dies kann nicht rückgängig gemacht werden!"; /* No comment provided by engineer. */ "Help" = "Hilfe"; @@ -2407,6 +2494,9 @@ /* No comment provided by engineer. */ "Importing archive" = "Archiv wird importiert"; +/* No comment provided by engineer. */ +"Improved delivery, reduced traffic usage.\nMore improvements are coming soon!" = "Verbesserte Nachrichten-Auslieferung und verringerter Datenverbrauch.\nWeitere Verbesserungen sind bald verfügbar!"; + /* No comment provided by engineer. */ "Improved message delivery" = "Verbesserte Zustellung von Nachrichten"; @@ -2566,6 +2656,9 @@ /* No comment provided by engineer. */ "iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications." = "Für die sichere Speicherung des Passworts nach dem Neustart der App und dem Wechsel des Passworts wird der iOS Schlüsselbund verwendet - dies erlaubt den Empfang von Push-Benachrichtigungen."; +/* No comment provided by engineer. */ +"IP address" = "IP-Adresse"; + /* No comment provided by engineer. */ "Irreversible message deletion" = "Unwiederbringliches löschen einer Nachricht"; @@ -2630,7 +2723,7 @@ "Keep" = "Behalten"; /* No comment provided by engineer. */ -"Keep conversation" = "Unterhaltung behalten"; +"Keep conversation" = "Chat-Inhalte beibehalten"; /* No comment provided by engineer. */ "Keep the app open to use it from desktop" = "Die App muss geöffnet bleiben, um sie vom Desktop aus nutzen zu können"; @@ -2818,6 +2911,9 @@ /* No comment provided by engineer. */ "Message servers" = "Nachrichten-Server"; +/* No comment provided by engineer. */ +"Message shape" = "Nachrichten-Form"; + /* No comment provided by engineer. */ "Message source remains private." = "Die Nachrichtenquelle bleibt privat."; @@ -2848,6 +2944,9 @@ /* No comment provided by engineer. */ "Messages sent" = "Gesendete Nachrichten"; +/* alert message */ +"Messages were deleted after you selected them." = "Die Nachrichten wurden gelöscht, nachdem Sie sie ausgewählt hatten."; + /* 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." = "Nachrichten, Dateien und Anrufe sind durch **Ende-zu-Ende-Verschlüsselung** mit Perfect Forward Secrecy, Ablehnung und Einbruchs-Wiederherstellung geschützt."; @@ -3001,6 +3100,12 @@ /* No comment provided by engineer. */ "New passphrase…" = "Neues Passwort…"; +/* No comment provided by engineer. */ +"New SOCKS credentials will be used every time you start the app." = "Jedes Mal wenn Sie die App starten, werden neue SOCKS-Anmeldeinformationen genutzt"; + +/* No comment provided by engineer. */ +"New SOCKS credentials will be used for each server." = "Für jeden Server werden neue SOCKS-Anmeldeinformationen genutzt"; + /* pref value */ "no" = "Nein"; @@ -3043,6 +3148,12 @@ /* No comment provided by engineer. */ "No network connection" = "Keine Netzwerkverbindung"; +/* No comment provided by engineer. */ +"No permission to record speech" = "Keine Genehmigung für Sprach-Aufnahmen"; + +/* No comment provided by engineer. */ +"No permission to record video" = "Keine Genehmigung für Video-Aufnahmen"; + /* No comment provided by engineer. */ "No permission to record voice message" = "Keine Berechtigung für das Aufnehmen von Sprachnachrichten"; @@ -3058,6 +3169,9 @@ /* No comment provided by engineer. */ "Nothing selected" = "Nichts ausgewählt"; +/* alert title */ +"Nothing to forward!" = "Es gibt nichts zum Weiterleiten!"; + /* No comment provided by engineer. */ "Notifications" = "Benachrichtigungen"; @@ -3084,7 +3198,7 @@ /* feature offered item */ "offered %@: %@" = "angeboten %1$@: %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "Ok"; /* No comment provided by engineer. */ @@ -3115,7 +3229,7 @@ "Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "Nur die Endgeräte speichern die Benutzerprofile, Kontakte, Gruppen und Nachrichten, welche über eine **2-Schichten Ende-zu-Ende-Verschlüsselung** gesendet werden."; /* No comment provided by engineer. */ -"Only delete conversation" = "Nur die Unterhaltung löschen"; +"Only delete conversation" = "Nur die Chat-Inhalte löschen"; /* No comment provided by engineer. */ "Only group owners can change group preferences." = "Gruppen-Präferenzen können nur von Gruppen-Eigentümern geändert werden."; @@ -3199,7 +3313,7 @@ "Or show this code" = "Oder diesen QR-Code anzeigen"; /* No comment provided by engineer. */ -"other" = "andere"; +"other" = "Andere"; /* No comment provided by engineer. */ "Other" = "Andere"; @@ -3210,6 +3324,9 @@ /* No comment provided by engineer. */ "other errors" = "Andere Fehler"; +/* alert message */ +"Other file errors:\n%@" = "Andere(r) Datei-Fehler:\n%@"; + /* member role */ "owner" = "Eigentümer"; @@ -3231,6 +3348,9 @@ /* No comment provided by engineer. */ "Passcode set!" = "Zugangscode eingestellt!"; +/* No comment provided by engineer. */ +"Password" = "Passwort"; + /* No comment provided by engineer. */ "Password to show" = "Passwort anzeigen"; @@ -3327,6 +3447,9 @@ /* No comment provided by engineer. */ "Polish interface" = "Polnische Bedienoberfläche"; +/* No comment provided by engineer. */ +"Port" = "Port"; + /* server test error */ "Possibly, certificate fingerprint in server address is incorrect" = "Der Fingerabdruck des Zertifikats in der Serveradresse ist wahrscheinlich ungültig"; @@ -3378,12 +3501,6 @@ /* No comment provided by engineer. */ "Profile images" = "Profil-Bilder"; -/* No comment provided by engineer. */ -"Profile name" = "Profilname"; - -/* No comment provided by engineer. */ -"Profile name:" = "Profilname:"; - /* No comment provided by engineer. */ "Profile password" = "Passwort für Profil"; @@ -3444,6 +3561,9 @@ /* No comment provided by engineer. */ "Proxied servers" = "Proxy-Server"; +/* No comment provided by engineer. */ +"Proxy requires password" = "Der Proxy benötigt ein Passwort"; + /* No comment provided by engineer. */ "Push notifications" = "Push-Benachrichtigungen"; @@ -3460,7 +3580,7 @@ "Rate the app" = "Bewerten Sie die App"; /* No comment provided by engineer. */ -"Reachable chat toolbar" = "Erreichbare Chat-Symbolleiste"; +"Reachable chat toolbar" = "Chat-Symbolleiste unten"; /* chat item menu */ "React…" = "Reagiere…"; @@ -3589,6 +3709,9 @@ /* No comment provided by engineer. */ "Remove" = "Entfernen"; +/* No comment provided by engineer. */ +"Remove archive?" = "Archiv entfernen?"; + /* No comment provided by engineer. */ "Remove image" = "Bild entfernen"; @@ -3715,10 +3838,11 @@ /* No comment provided by engineer. */ "Safer groups" = "Sicherere Gruppen"; -/* chat item action */ +/* alert button + chat item action */ "Save" = "Speichern"; -/* No comment provided by engineer. */ +/* alert button */ "Save (and notify contacts)" = "Speichern (und Kontakte benachrichtigen)"; /* No comment provided by engineer. */ @@ -3736,9 +3860,6 @@ /* No comment provided by engineer. */ "Save archive" = "Archiv speichern"; -/* No comment provided by engineer. */ -"Save auto-accept settings" = "Einstellungen von \"Automatisch akzeptieren\" speichern"; - /* No comment provided by engineer. */ "Save group profile" = "Gruppenprofil speichern"; @@ -3760,12 +3881,12 @@ /* No comment provided by engineer. */ "Save servers?" = "Alle Server speichern?"; -/* No comment provided by engineer. */ -"Save settings?" = "Einstellungen speichern?"; - /* No comment provided by engineer. */ "Save welcome message?" = "Begrüßungsmeldung speichern?"; +/* alert title */ +"Save your profile?" = "Ihr Profil speichern?"; + /* No comment provided by engineer. */ "saved" = "abgespeichert"; @@ -3784,6 +3905,9 @@ /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Gespeicherte WebRTC ICE-Server werden entfernt"; +/* No comment provided by engineer. */ +"Saving %lld messages" = "Es wird/werden %lld Nachricht(en) gesichert"; + /* No comment provided by engineer. */ "Scale" = "Skalieren"; @@ -3815,7 +3939,7 @@ "Search bar accepts invitation links." = "In der Suchleiste werden nun auch Einladungslinks akzeptiert."; /* No comment provided by engineer. */ -"Search or paste SimpleX link" = "Suchen oder fügen Sie den SimpleX-Link ein"; +"Search or paste SimpleX link" = "Suchen oder SimpleX-Link einfügen"; /* network option */ "sec" = "sek"; @@ -3847,6 +3971,9 @@ /* chat item action */ "Select" = "Auswählen"; +/* No comment provided by engineer. */ +"Select chat profile" = "Chat-Profil auswählen"; + /* No comment provided by engineer. */ "Selected %lld" = "%lld ausgewählt"; @@ -3919,7 +4046,7 @@ /* No comment provided by engineer. */ "Send up to 100 last messages to new members." = "Bis zu 100 der letzten Nachrichten an neue Gruppenmitglieder senden."; -/* No comment provided by engineer. */ +/* alert message */ "Sender cancelled file transfer." = "Der Absender hat die Dateiübertragung abgebrochen."; /* No comment provided by engineer. */ @@ -3979,6 +4106,9 @@ /* No comment provided by engineer. */ "Sent via proxy" = "Über einen Proxy gesendet"; +/* No comment provided by engineer. */ +"Server" = "Server"; + /* No comment provided by engineer. */ "Server address" = "Server-Adresse"; @@ -4060,6 +4190,9 @@ /* No comment provided by engineer. */ "Settings" = "Einstellungen"; +/* alert message */ +"Settings were changed." = "Die Einstellungen wurden geändert."; + /* No comment provided by engineer. */ "Shape profile images" = "Form der Profil-Bilder"; @@ -4081,6 +4214,9 @@ /* No comment provided by engineer. */ "Share link" = "Link teilen"; +/* No comment provided by engineer. */ +"Share profile" = "Profil teilen"; + /* No comment provided by engineer. */ "Share this 1-time invite link" = "Teilen Sie diesen Einmal-Einladungslink"; @@ -4162,6 +4298,9 @@ /* simplex link type */ "SimpleX one-time invitation" = "SimpleX-Einmal-Einladung"; +/* No comment provided by engineer. */ +"SimpleX protocols reviewed by Trail of Bits." = "Die SimpleX-Protokolle wurden von Trail of Bits überprüft."; + /* No comment provided by engineer. */ "Simplified incognito mode" = "Vereinfachter Inkognito-Modus"; @@ -4180,9 +4319,15 @@ /* No comment provided by engineer. */ "SMP server" = "SMP-Server"; +/* No comment provided by engineer. */ +"SOCKS proxy" = "SOCKS-Proxy"; + /* blur media */ "Soft" = "Weich"; +/* No comment provided by engineer. */ +"Some app settings were not migrated." = "Einige App-Einstellungen wurden nicht migriert."; + /* No comment provided by engineer. */ "Some file(s) were not exported:" = "Einzelne Datei(en) wurde(n) nicht exportiert:"; @@ -4276,12 +4421,21 @@ /* No comment provided by engineer. */ "Support SimpleX Chat" = "Unterstützung von SimpleX Chat"; +/* No comment provided by engineer. */ +"Switch audio and video during the call." = "Während des Anrufs zwischen Audio und Video wechseln"; + +/* No comment provided by engineer. */ +"Switch chat profile for 1-time invitations." = "Das Chat-Profil für Einmal-Einladungen wechseln"; + /* No comment provided by engineer. */ "System" = "System"; /* No comment provided by engineer. */ "System authentication" = "System-Authentifizierung"; +/* No comment provided by engineer. */ +"Tail" = "Sprechblasen-Format"; + /* No comment provided by engineer. */ "Take picture" = "Machen Sie ein Foto"; @@ -4385,7 +4539,7 @@ "The message will be marked as moderated for all members." = "Diese Nachricht wird für alle Mitglieder als moderiert gekennzeichnet."; /* No comment provided by engineer. */ -"The messages will be deleted for all members." = "Die Nachrichten werden für alle Mitglieder gelöscht werden."; +"The messages will be deleted for all members." = "Die Nachrichten werden für alle Gruppenmitglieder gelöscht."; /* No comment provided by engineer. */ "The messages will be marked as moderated for all members." = "Die Nachrichten werden für alle Mitglieder als moderiert gekennzeichnet werden."; @@ -4411,6 +4565,9 @@ /* No comment provided by engineer. */ "The text you pasted is not a SimpleX link." = "Der von Ihnen eingefügte Text ist kein SimpleX-Link."; +/* No comment provided by engineer. */ +"The uploaded database archive will be permanently removed from the servers." = "Das hochgeladene Datenbank-Archiv wird dauerhaft von den Servern entfernt."; + /* No comment provided by engineer. */ "Themes" = "Design"; @@ -4489,6 +4646,12 @@ /* No comment provided by engineer. */ "To protect your IP address, private routing uses your SMP servers to deliver messages." = "Zum Schutz Ihrer IP-Adresse, wird für die Nachrichten-Auslieferung privates Routing über Ihre konfigurierten SMP-Server genutzt."; +/* No comment provided by engineer. */ +"To record speech please grant permission to use Microphone." = "Bitte erteilen Sie für Sprach-Aufnahmen die Genehmigung das Mikrofon zu nutzen."; + +/* No comment provided by engineer. */ +"To record video please grant permission to use Camera." = "Bitte erteilen Sie für Video-Aufnahmen die Genehmigung die Kamera zu nutzen."; + /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "Bitte erlauben Sie die Nutzung des Mikrofons, um Sprachnachrichten aufnehmen zu können."; @@ -4588,7 +4751,7 @@ /* No comment provided by engineer. */ "unknown servers" = "Unbekannte Relais"; -/* No comment provided by engineer. */ +/* alert title */ "Unknown servers!" = "Unbekannte Server!"; /* No comment provided by engineer. */ @@ -4705,18 +4868,21 @@ /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "Verwenden Sie SimpleX-Chat-Server?"; +/* No comment provided by engineer. */ +"Use SOCKS proxy" = "SOCKS-Proxy nutzen"; + /* No comment provided by engineer. */ "Use the app while in the call." = "Die App kann während eines Anrufs genutzt werden."; /* No comment provided by engineer. */ -"Use the app with one hand." = "Die App mit einer Hand nutzen."; - -/* No comment provided by engineer. */ -"User profile" = "Benutzerprofil"; +"Use the app with one hand." = "Die App mit einer Hand bedienen."; /* No comment provided by engineer. */ "User selection" = "Benutzer-Auswahl"; +/* No comment provided by engineer. */ +"Username" = "Benutzername"; + /* No comment provided by engineer. */ "Using SimpleX Chat servers." = "Verwendung von SimpleX-Chat-Servern."; @@ -4894,7 +5060,7 @@ /* No comment provided by engineer. */ "Without Tor or VPN, your IP address will be visible to file servers." = "Ohne Tor- oder VPN-Nutzung wird Ihre IP-Adresse für Datei-Server sichtbar sein."; -/* No comment provided by engineer. */ +/* alert message */ "Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." = "Ohne Tor- oder VPN-Nutzung wird Ihre IP-Adresse für diese XFTP-Relais sichtbar sein: %@."; /* No comment provided by engineer. */ @@ -4918,9 +5084,6 @@ /* No comment provided by engineer. */ "you" = "Profil"; -/* No comment provided by engineer. */ -"You" = "Profil"; - /* No comment provided by engineer. */ "You **must not** use the same database on two devices." = "Sie dürfen die selbe Datenbank **nicht** auf zwei Geräten nutzen."; @@ -5021,7 +5184,7 @@ "You can start chat via app Settings / Database or by restarting the app" = "Sie können den Chat über die App-Einstellungen / Datenbank oder durch Neustart der App starten"; /* No comment provided by engineer. */ -"You can still view conversation with %@ in the list of chats." = "Sie können in der Chatliste weiterhin die Unterhaltung mit %@ einsehen."; +"You can still view conversation with %@ in the list of chats." = "Sie können in der Chat-Liste weiterhin die Unterhaltung mit %@ einsehen."; /* No comment provided by engineer. */ "You can turn on SimpleX Lock via Settings." = "Sie können die SimpleX-Sperre über die Einstellungen aktivieren."; @@ -5155,9 +5318,15 @@ /* No comment provided by engineer. */ "Your chat database is not encrypted - set passphrase to encrypt it." = "Ihre Chat-Datenbank ist nicht verschlüsselt. Bitte legen Sie ein Passwort fest, um sie zu schützen."; +/* alert title */ +"Your chat preferences" = "Ihre Chat-Präferenzen"; + /* No comment provided by engineer. */ "Your chat profiles" = "Ihre Chat-Profile"; +/* No comment provided by engineer. */ +"Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." = "Ihre Verbindung wurde auf %@ verschoben. Während Sie auf das Profil weitergeleitet wurden trat aber ein unerwarteter Fehler auf."; + /* No comment provided by engineer. */ "Your contact sent a file that is larger than currently supported maximum size (%@)." = "Ihr Kontakt hat eine Datei gesendet, die größer ist als die derzeit unterstützte maximale Größe (%@)."; @@ -5167,6 +5336,9 @@ /* No comment provided by engineer. */ "Your contacts will remain connected." = "Ihre Kontakte bleiben weiterhin verbunden."; +/* No comment provided by engineer. */ +"Your credentials may be sent unencrypted." = "Ihre Anmeldeinformationen können unverschlüsselt versendet werden."; + /* No comment provided by engineer. */ "Your current chat database will be DELETED and REPLACED with the imported one." = "Ihre aktuelle Chat-Datenbank wird GELÖSCHT und durch die Importierte ERSETZT."; @@ -5189,7 +5361,10 @@ "Your profile **%@** will be shared." = "Ihr Profil **%@** wird geteilt."; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Ihr Profil wird auf Ihrem Gerät gespeichert und nur mit Ihren Kontakten geteilt.\nSimpleX-Server können Ihr Profil nicht einsehen."; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Ihr Profil wird auf Ihrem Gerät gespeichert und nur mit Ihren Kontakten geteilt. SimpleX-Server können Ihr Profil nicht einsehen."; + +/* alert message */ +"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Ihr Profil wurde geändert. Wenn Sie es speichern, wird das aktualisierte Profil an alle Ihre Kontakte gesendet."; /* No comment provided by engineer. */ "Your profile, contacts and delivered messages are stored on your device." = "Ihr Profil, Ihre Kontakte und zugestellten Nachrichten werden auf Ihrem Gerät gespeichert."; diff --git a/apps/ios/es.lproj/Localizable.strings b/apps/ios/es.lproj/Localizable.strings index 3dce4e4474..70c29f49e0 100644 --- a/apps/ios/es.lproj/Localizable.strings +++ b/apps/ios/es.lproj/Localizable.strings @@ -160,6 +160,9 @@ /* notification title */ "%@ wants to connect!" = "¡ %@ quiere contactar!"; +/* format for date separator in chat */ +"%@, %@" = "%1$@, %2$@"; + /* No comment provided by engineer. */ "%@, %@ and %lld members" = "%@, %@ y %lld miembro(s) más"; @@ -172,9 +175,24 @@ /* time interval */ "%d days" = "%d días"; +/* forward confirmation reason */ +"%d file(s) are still being downloaded." = "%d archivo(s) se está(n) descargando todavía."; + +/* forward confirmation reason */ +"%d file(s) failed to download." = "La descarga ha fallado para %d archivo(s)."; + +/* forward confirmation reason */ +"%d file(s) were deleted." = "%d archivo(s) ha(n) sido eliminado(s)."; + +/* forward confirmation reason */ +"%d file(s) were not downloaded." = "%d archivo(s) no se ha(n) descargado."; + /* time interval */ "%d hours" = "%d horas"; +/* alert title */ +"%d messages not forwarded" = "%d mensajes no enviados"; + /* time interval */ "%d min" = "%d minutos"; @@ -305,7 +323,7 @@ "A new contact" = "Contacto nuevo"; /* No comment provided by engineer. */ -"A new random profile will be shared." = "Se compartirá un perfil nuevo aleatorio."; +"A new random profile will be shared." = "Compartirás un perfil nuevo aleatorio."; /* No comment provided by engineer. */ "A separate TCP connection will be used **for each chat profile you have in the app**." = "Se usará una conexión TCP independiente **por cada perfil que tengas en la aplicación**."; @@ -454,7 +472,7 @@ /* No comment provided by engineer. */ "All new messages from %@ will be hidden!" = "¡Los mensajes nuevos de %@ estarán ocultos!"; -/* No comment provided by engineer. */ +/* profile dropdown */ "All profiles" = "Todos los perfiles"; /* No comment provided by engineer. */ @@ -577,6 +595,9 @@ /* No comment provided by engineer. */ "App passcode is replaced with self-destruct passcode." = "El código de acceso será reemplazado por código de autodestrucción."; +/* No comment provided by engineer. */ +"App session" = "Sesión de aplicación"; + /* No comment provided by engineer. */ "App version" = "Versión de la aplicación"; @@ -649,6 +670,9 @@ /* No comment provided by engineer. */ "Auto-accept images" = "Aceptar imágenes automáticamente"; +/* alert title */ +"Auto-accept settings" = "Auto aceptar configuración"; + /* No comment provided by engineer. */ "Back" = "Volver"; @@ -670,15 +694,30 @@ /* No comment provided by engineer. */ "Bad message ID" = "ID de mensaje incorrecto"; +/* No comment provided by engineer. */ +"Better calls" = "Llamadas mejoradas"; + /* No comment provided by engineer. */ "Better groups" = "Grupos mejorados"; +/* No comment provided by engineer. */ +"Better message dates." = "Sistema de fechas mejorado."; + /* No comment provided by engineer. */ "Better messages" = "Mensajes mejorados"; /* No comment provided by engineer. */ "Better networking" = "Uso de red mejorado"; +/* No comment provided by engineer. */ +"Better notifications" = "Notificaciones mejoradas"; + +/* No comment provided by engineer. */ +"Better security ✅" = "Seguridad mejorada ✅"; + +/* No comment provided by engineer. */ +"Better user experience" = "Experiencia de usuario mejorada"; + /* No comment provided by engineer. */ "Black" = "Negro"; @@ -781,7 +820,7 @@ /* No comment provided by engineer. */ "Can't message member" = "No se pueden enviar mensajes al miembro"; -/* No comment provided by engineer. */ +/* alert button */ "Cancel" = "Cancelar"; /* No comment provided by engineer. */ @@ -796,7 +835,7 @@ /* No comment provided by engineer. */ "Cannot forward message" = "No se puede reenviar el mensaje"; -/* No comment provided by engineer. */ +/* alert title */ "Cannot receive file" = "No se puede recibir el archivo"; /* snd error text */ @@ -890,6 +929,12 @@ /* No comment provided by engineer. */ "Chat preferences" = "Preferencias de Chat"; +/* alert message */ +"Chat preferences were changed." = "Las preferencias del chat han sido modificadas."; + +/* No comment provided by engineer. */ +"Chat profile" = "Perfil de usuario"; + /* No comment provided by engineer. */ "Chat theme" = "Tema de chat"; @@ -987,7 +1032,7 @@ "Confirm password" = "Confirmar contraseña"; /* No comment provided by engineer. */ -"Confirm that you remember database passphrase to migrate it." = "Para migrar confirma que recuerdas la frase de contraseña de la base de datos."; +"Confirm that you remember database passphrase to migrate it." = "Para migrar la base de datos confirma que recuerdas la frase de contraseña."; /* No comment provided by engineer. */ "Confirm upload" = "Confirmar subida"; @@ -1178,6 +1223,9 @@ /* No comment provided by engineer. */ "Core version: v%@" = "Versión Core: v%@"; +/* No comment provided by engineer. */ +"Corner" = "Esquina"; + /* No comment provided by engineer. */ "Correct name to %@?" = "¿Corregir el nombre a %@?"; @@ -1259,6 +1307,9 @@ /* No comment provided by engineer. */ "Custom time" = "Tiempo personalizado"; +/* No comment provided by engineer. */ +"Customizable message shape." = "Forma personalizable de los mensajes."; + /* No comment provided by engineer. */ "Customize theme" = "Personalizar tema"; @@ -1305,7 +1356,7 @@ "Database passphrase & export" = "Base de datos y contraseña"; /* No comment provided by engineer. */ -"Database passphrase is different from saved in the keychain." = "La contraseña es distinta a la almacenada en Keychain."; +"Database passphrase is different from saved in the keychain." = "La contraseña es diferente a la almacenada en Keychain."; /* No comment provided by engineer. */ "Database passphrase is required to open chat." = "Para abrir la aplicación se requiere la contraseña de la base de datos."; @@ -1449,6 +1500,9 @@ /* No comment provided by engineer. */ "Delete old database?" = "¿Eliminar base de datos antigua?"; +/* No comment provided by engineer. */ +"Delete or moderate up to 200 messages." = "Borra o modera hasta 200 mensajes a la vez."; + /* No comment provided by engineer. */ "Delete pending connection?" = "¿Eliminar conexión pendiente?"; @@ -1594,7 +1648,7 @@ "Disconnect" = "Desconectar"; /* No comment provided by engineer. */ -"Disconnect desktop?" = "¿Desconectar ordenador?"; +"Disconnect desktop?" = "¿Desconectar del ordenador?"; /* No comment provided by engineer. */ "Discover and join groups" = "Descubre y únete a grupos"; @@ -1611,6 +1665,9 @@ /* No comment provided by engineer. */ "Do NOT send messages directly, even if your or destination server does not support private routing." = "NO enviar mensajes directamente incluso si tu servidor o el de destino no soportan enrutamiento privado."; +/* No comment provided by engineer. */ +"Do not use credentials with proxy." = "No uses credenciales con proxy."; + /* No comment provided by engineer. */ "Do NOT use private routing." = "NO usar enrutamiento privado."; @@ -1624,12 +1681,13 @@ "Don't enable" = "No activar"; /* No comment provided by engineer. */ -"Don't show again" = "No mostrar de nuevo"; +"Don't show again" = "No volver a mostrar"; /* No comment provided by engineer. */ "Downgrade and open chat" = "Degradar y abrir Chat"; -/* chat item action */ +/* alert button + chat item action */ "Download" = "Descargar"; /* No comment provided by engineer. */ @@ -1641,6 +1699,9 @@ /* server test step */ "Download file" = "Descargar archivo"; +/* alert action */ +"Download files" = "Descargar archivos"; + /* No comment provided by engineer. */ "Downloaded" = "Descargado"; @@ -1857,12 +1918,18 @@ /* No comment provided by engineer. */ "Error changing address" = "Error al cambiar servidor"; +/* No comment provided by engineer. */ +"Error changing connection profile" = "Error al cambiar el perfil de conexión"; + /* No comment provided by engineer. */ "Error changing role" = "Error al cambiar rol"; /* No comment provided by engineer. */ "Error changing setting" = "Error cambiando configuración"; +/* No comment provided by engineer. */ +"Error changing to incognito!" = "¡Error al cambiar a incógnito!"; + /* No comment provided by engineer. */ "Error connecting to forwarding server %@. Please try later." = "Error al conectar con el servidor de reenvío %@. Por favor, inténtalo más tarde."; @@ -1936,9 +2003,12 @@ "Error loading %@ servers" = "Error al cargar servidores %@"; /* No comment provided by engineer. */ -"Error opening chat" = "Error al abrir chat"; +"Error migrating settings" = "Error al migrar la configuración"; /* No comment provided by engineer. */ +"Error opening chat" = "Error al abrir chat"; + +/* alert title */ "Error receiving file" = "Error al recibir archivo"; /* No comment provided by engineer. */ @@ -1996,6 +2066,9 @@ "Error stopping chat" = "Error al parar SimpleX"; /* No comment provided by engineer. */ +"Error switching profile" = "Error al cambiar perfil"; + +/* alertTitle */ "Error switching profile!" = "¡Error al cambiar perfil!"; /* No comment provided by engineer. */ @@ -2022,8 +2095,7 @@ /* No comment provided by engineer. */ "Error: " = "Error: "; -/* file error text - snd error text */ +/* alert message */ "Error: %@" = "Error: %@"; /* No comment provided by engineer. */ @@ -2083,6 +2155,9 @@ /* No comment provided by engineer. */ "File error" = "Error de archivo"; +/* alert message */ +"File errors:\n%@" = "Error(es) de archivo\n%@"; + /* file error text */ "File not found - most likely file was deleted or cancelled." = "Archivo no encontrado, probablemente haya sido borrado o cancelado."; @@ -2164,9 +2239,21 @@ /* chat item action */ "Forward" = "Reenviar"; +/* alert title */ +"Forward %d message(s)?" = "¿Reenviar %d mensaje(s)?"; + /* No comment provided by engineer. */ "Forward and save messages" = "Reenviar y guardar mensajes"; +/* alert action */ +"Forward messages" = "Reenviar mensajes"; + +/* alert message */ +"Forward messages without files?" = "¿Reenviar mensajes sin los archivos?"; + +/* No comment provided by engineer. */ +"Forward up to 20 messages at once." = "Desplazamiento de hasta 20 mensajes."; + /* No comment provided by engineer. */ "forwarded" = "reenviado"; @@ -2176,6 +2263,9 @@ /* No comment provided by engineer. */ "Forwarded from" = "Reenviado por"; +/* No comment provided by engineer. */ +"Forwarding %lld messages" = "Reenviando %lld mensajes"; + /* No comment provided by engineer. */ "Forwarding server %@ failed to connect to destination server %@. Please try later." = "El servidor de reenvío %@ no ha podido conectarse al servidor de destino %@. Por favor, intentalo más tarde."; @@ -2204,13 +2294,10 @@ "Full name (optional)" = "Nombre completo (opcional)"; /* No comment provided by engineer. */ -"Full name:" = "Nombre completo:"; +"Fully decentralized – visible only to members." = "Totalmente descentralizado. Visible sólo para los miembros."; /* No comment provided by engineer. */ -"Fully decentralized – visible only to members." = "Completamente descentralizado y sólo visible para los miembros."; - -/* No comment provided by engineer. */ -"Fully re-implemented - work in background!" = "Completamente reimplementado: ¡funciona en segundo plano!"; +"Fully re-implemented - work in background!" = "Totalmente revisado. ¡Funciona en segundo plano!"; /* No comment provided by engineer. */ "Further reduced battery usage" = "Reducción consumo de batería"; @@ -2407,6 +2494,9 @@ /* No comment provided by engineer. */ "Importing archive" = "Importando archivo"; +/* No comment provided by engineer. */ +"Improved delivery, reduced traffic usage.\nMore improvements are coming soon!" = "Reducción del tráfico y entrega mejorada.\n¡Pronto habrá nuevas mejoras!"; + /* No comment provided by engineer. */ "Improved message delivery" = "Entrega de mensajes mejorada"; @@ -2566,6 +2656,9 @@ /* No comment provided by engineer. */ "iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications." = "iOS Keychain se usará para almacenar la contraseña de forma segura después de reiniciar la aplicación o cambiar la contraseña. Esto permitirá recibir notificaciones automáticas."; +/* No comment provided by engineer. */ +"IP address" = "Dirección IP"; + /* No comment provided by engineer. */ "Irreversible message deletion" = "Eliminación irreversible del mensaje"; @@ -2699,7 +2792,7 @@ "Local name" = "Nombre local"; /* No comment provided by engineer. */ -"Local profile data only" = "Sólo datos del perfil local"; +"Local profile data only" = "Eliminar sólo el perfil"; /* No comment provided by engineer. */ "Lock after" = "Bloquear en"; @@ -2818,6 +2911,9 @@ /* No comment provided by engineer. */ "Message servers" = "Servidores de mensajes"; +/* No comment provided by engineer. */ +"Message shape" = "Forma del mensaje"; + /* No comment provided by engineer. */ "Message source remains private." = "El autor del mensaje se mantiene privado."; @@ -2848,6 +2944,9 @@ /* No comment provided by engineer. */ "Messages sent" = "Mensajes enviados"; +/* alert message */ +"Messages were deleted after you selected them." = "Los mensajes han sido borrados después de seleccionarlos."; + /* 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."; @@ -3001,6 +3100,12 @@ /* No comment provided by engineer. */ "New passphrase…" = "Contraseña nueva…"; +/* No comment provided by engineer. */ +"New SOCKS credentials will be used every time you start the app." = "Se usarán credenciales SOCKS nuevas cada vez que inicies la aplicación."; + +/* No comment provided by engineer. */ +"New SOCKS credentials will be used for each server." = "Se usarán credenciales SOCKS nuevas por cada servidor."; + /* pref value */ "no" = "no"; @@ -3043,6 +3148,12 @@ /* No comment provided by engineer. */ "No network connection" = "Sin conexión de red"; +/* No comment provided by engineer. */ +"No permission to record speech" = "Sin permiso para grabación de voz"; + +/* No comment provided by engineer. */ +"No permission to record video" = "Sin permiso para grabación de vídeo"; + /* No comment provided by engineer. */ "No permission to record voice message" = "Sin permiso para grabar mensajes de voz"; @@ -3058,6 +3169,9 @@ /* No comment provided by engineer. */ "Nothing selected" = "Nada seleccionado"; +/* alert title */ +"Nothing to forward!" = "¡Nada para reenviar!"; + /* No comment provided by engineer. */ "Notifications" = "Notificaciones"; @@ -3084,7 +3198,7 @@ /* feature offered item */ "offered %@: %@" = "ofrecido %1$@: %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "Ok"; /* No comment provided by engineer. */ @@ -3210,6 +3324,9 @@ /* No comment provided by engineer. */ "other errors" = "otros errores"; +/* alert message */ +"Other file errors:\n%@" = "Otro(s) error(es) de archivo.\n%@"; + /* member role */ "owner" = "propietario"; @@ -3231,6 +3348,9 @@ /* No comment provided by engineer. */ "Passcode set!" = "¡Código de acceso guardado!"; +/* No comment provided by engineer. */ +"Password" = "Contraseña"; + /* No comment provided by engineer. */ "Password to show" = "Contraseña para hacerlo visible"; @@ -3327,8 +3447,11 @@ /* No comment provided by engineer. */ "Polish interface" = "Interfaz en polaco"; +/* No comment provided by engineer. */ +"Port" = "Puerto"; + /* server test error */ -"Possibly, certificate fingerprint in server address is incorrect" = "Posiblemente la huella digital del certificado en la dirección del servidor es incorrecta"; +"Possibly, certificate fingerprint in server address is incorrect" = "Posiblemente la huella del certificado en la dirección del servidor es incorrecta"; /* No comment provided by engineer. */ "Preserve the last message draft, with attachments." = "Conserva el último borrador del mensaje con los datos adjuntos."; @@ -3370,7 +3493,7 @@ "Private routing error" = "Error de enrutamiento privado"; /* No comment provided by engineer. */ -"Profile and server connections" = "Datos del perfil y conexiones"; +"Profile and server connections" = "Eliminar perfil y conexiones"; /* No comment provided by engineer. */ "Profile image" = "Imagen del perfil"; @@ -3378,12 +3501,6 @@ /* No comment provided by engineer. */ "Profile images" = "Forma de los perfiles"; -/* No comment provided by engineer. */ -"Profile name" = "Nombre del perfil"; - -/* No comment provided by engineer. */ -"Profile name:" = "Nombre del perfil:"; - /* No comment provided by engineer. */ "Profile password" = "Contraseña del perfil"; @@ -3444,6 +3561,9 @@ /* No comment provided by engineer. */ "Proxied servers" = "Servidores con proxy"; +/* No comment provided by engineer. */ +"Proxy requires password" = "El proxy requiere contraseña"; + /* No comment provided by engineer. */ "Push notifications" = "Notificaciones automáticas"; @@ -3589,6 +3709,9 @@ /* No comment provided by engineer. */ "Remove" = "Eliminar"; +/* No comment provided by engineer. */ +"Remove archive?" = "¿Eliminar archivo?"; + /* No comment provided by engineer. */ "Remove image" = "Eliminar imagen"; @@ -3611,7 +3734,7 @@ "removed contact address" = "dirección de contacto eliminada"; /* profile update event chat item */ -"removed profile picture" = "imagen de perfil eliminada"; +"removed profile picture" = "ha eliminado la imagen del perfil"; /* rcv group event chat item */ "removed you" = "te ha expulsado"; @@ -3715,10 +3838,11 @@ /* No comment provided by engineer. */ "Safer groups" = "Grupos más seguros"; -/* chat item action */ +/* alert button + chat item action */ "Save" = "Guardar"; -/* No comment provided by engineer. */ +/* alert button */ "Save (and notify contacts)" = "Guardar (y notificar contactos)"; /* No comment provided by engineer. */ @@ -3736,9 +3860,6 @@ /* No comment provided by engineer. */ "Save archive" = "Guardar archivo"; -/* No comment provided by engineer. */ -"Save auto-accept settings" = "Guardar configuración de auto aceptar"; - /* No comment provided by engineer. */ "Save group profile" = "Guardar perfil de grupo"; @@ -3760,12 +3881,12 @@ /* No comment provided by engineer. */ "Save servers?" = "¿Guardar servidores?"; -/* No comment provided by engineer. */ -"Save settings?" = "¿Guardar configuración?"; - /* No comment provided by engineer. */ "Save welcome message?" = "¿Guardar mensaje de bienvenida?"; +/* alert title */ +"Save your profile?" = "¿Guardar tu perfil?"; + /* No comment provided by engineer. */ "saved" = "guardado"; @@ -3784,6 +3905,9 @@ /* 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. */ +"Saving %lld messages" = "Guardando %lld mensajes"; + /* No comment provided by engineer. */ "Scale" = "Escala"; @@ -3847,6 +3971,9 @@ /* chat item action */ "Select" = "Seleccionar"; +/* No comment provided by engineer. */ +"Select chat profile" = "Selecciona perfil de chat"; + /* No comment provided by engineer. */ "Selected %lld" = "Seleccionados %lld"; @@ -3878,7 +4005,7 @@ "send direct message" = "Enviar mensaje directo"; /* No comment provided by engineer. */ -"Send direct message to connect" = "Envia un mensaje para conectar"; +"Send direct message to connect" = "Envía un mensaje para conectar"; /* No comment provided by engineer. */ "Send disappearing message" = "Enviar mensaje temporal"; @@ -3919,7 +4046,7 @@ /* No comment provided by engineer. */ "Send up to 100 last messages to new members." = "Se envían hasta 100 mensajes más recientes a los miembros nuevos."; -/* No comment provided by engineer. */ +/* alert message */ "Sender cancelled file transfer." = "El remitente ha cancelado la transferencia de archivos."; /* No comment provided by engineer. */ @@ -3979,6 +4106,9 @@ /* No comment provided by engineer. */ "Sent via proxy" = "Mediante proxy"; +/* No comment provided by engineer. */ +"Server" = "Servidor"; + /* No comment provided by engineer. */ "Server address" = "Dirección del servidor"; @@ -3998,7 +4128,7 @@ "Server requires authorization to upload, check password" = "El servidor requiere autorización para subir, comprueba la contraseña"; /* No comment provided by engineer. */ -"Server test failed!" = "¡Error en prueba del servidor!"; +"Server test failed!" = "¡Prueba no superada!"; /* No comment provided by engineer. */ "Server type" = "Tipo de servidor"; @@ -4040,7 +4170,7 @@ "set new contact address" = "nueva dirección de contacto"; /* profile update event chat item */ -"set new profile picture" = "nueva imagen de perfil"; +"set new profile picture" = "tiene nueva imagen del perfil"; /* No comment provided by engineer. */ "Set passcode" = "Código autodestrucción"; @@ -4060,6 +4190,9 @@ /* No comment provided by engineer. */ "Settings" = "Configuración"; +/* alert message */ +"Settings were changed." = "La configuración ha sido modificada."; + /* No comment provided by engineer. */ "Shape profile images" = "Dar forma a las imágenes de perfil"; @@ -4081,6 +4214,9 @@ /* No comment provided by engineer. */ "Share link" = "Compartir enlace"; +/* No comment provided by engineer. */ +"Share profile" = "Comparte perfil"; + /* No comment provided by engineer. */ "Share this 1-time invite link" = "Comparte este enlace de un solo uso"; @@ -4162,6 +4298,9 @@ /* simplex link type */ "SimpleX one-time invitation" = "Invitación SimpleX de un uso"; +/* No comment provided by engineer. */ +"SimpleX protocols reviewed by Trail of Bits." = "Protocolos de SimpleX auditados por Trail of Bits."; + /* No comment provided by engineer. */ "Simplified incognito mode" = "Modo incógnito simplificado"; @@ -4180,9 +4319,15 @@ /* No comment provided by engineer. */ "SMP server" = "Servidor SMP"; +/* No comment provided by engineer. */ +"SOCKS proxy" = "Proxy SOCKS"; + /* blur media */ "Soft" = "Suave"; +/* No comment provided by engineer. */ +"Some app settings were not migrated." = "Algunas configuraciones de la app no han sido migradas."; + /* No comment provided by engineer. */ "Some file(s) were not exported:" = "Algunos archivos no han sido exportados:"; @@ -4276,12 +4421,21 @@ /* No comment provided by engineer. */ "Support SimpleX Chat" = "Soporte SimpleX Chat"; +/* No comment provided by engineer. */ +"Switch audio and video during the call." = "Intercambia audio y video durante la llamada."; + +/* No comment provided by engineer. */ +"Switch chat profile for 1-time invitations." = "Cambia el perfil de chat para invitaciones de un solo uso."; + /* No comment provided by engineer. */ "System" = "Sistema"; /* No comment provided by engineer. */ "System authentication" = "Autenticación del sistema"; +/* No comment provided by engineer. */ +"Tail" = "Cola"; + /* No comment provided by engineer. */ "Take picture" = "Tomar foto"; @@ -4325,7 +4479,7 @@ "Temporary file error" = "Error en archivo temporal"; /* server test failure */ -"Test failed at step %@." = "La prueba ha fallado en el paso %@."; +"Test failed at step %@." = "Prueba no superada en el paso %@."; /* No comment provided by engineer. */ "Test server" = "Probar servidor"; @@ -4334,7 +4488,7 @@ "Test servers" = "Probar servidores"; /* No comment provided by engineer. */ -"Tests failed!" = "¡Pruebas fallidas!"; +"Tests failed!" = "¡Pruebas no superadas!"; /* No comment provided by engineer. */ "Thank you for installing SimpleX Chat!" = "¡Gracias por instalar SimpleX Chat!"; @@ -4411,6 +4565,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. */ +"The uploaded database archive will be permanently removed from the servers." = "El archivo de bases de datos subido será eliminado permanentemente de los servidores."; + /* No comment provided by engineer. */ "Themes" = "Temas"; @@ -4489,6 +4646,12 @@ /* No comment provided by engineer. */ "To protect your IP address, private routing uses your SMP servers to deliver messages." = "Para proteger tu dirección IP, el enrutamiento privado usa tu lista de servidores SMP para enviar mensajes."; +/* No comment provided by engineer. */ +"To record speech please grant permission to use Microphone." = "Para grabación de voz, por favor concede el permiso para usar el micrófono."; + +/* No comment provided by engineer. */ +"To record video please grant permission to use Camera." = "Para grabación de vídeo, por favor concede el permiso para usar la cámara."; + /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "Para grabar el mensaje de voz concede permiso para usar el micrófono."; @@ -4588,7 +4751,7 @@ /* No comment provided by engineer. */ "unknown servers" = "con servidores desconocidos"; -/* No comment provided by engineer. */ +/* alert title */ "Unknown servers!" = "¡Servidores desconocidos!"; /* No comment provided by engineer. */ @@ -4705,6 +4868,9 @@ /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "¿Usar servidores SimpleX Chat?"; +/* No comment provided by engineer. */ +"Use SOCKS proxy" = "Usar proxy SOCKS"; + /* No comment provided by engineer. */ "Use the app while in the call." = "Usar la aplicación durante la llamada."; @@ -4712,10 +4878,10 @@ "Use the app with one hand." = "Usa la aplicación con una sola mano."; /* No comment provided by engineer. */ -"User profile" = "Perfil de usuario"; +"User selection" = "Selección de usuarios"; /* No comment provided by engineer. */ -"User selection" = "Selección de usuarios"; +"Username" = "Nombre de usuario"; /* No comment provided by engineer. */ "Using SimpleX Chat servers." = "Usar servidores SimpleX Chat."; @@ -4894,7 +5060,7 @@ /* No comment provided by engineer. */ "Without Tor or VPN, your IP address will be visible to file servers." = "Sin Tor o VPN, tu dirección IP será visible para los servidores de archivos."; -/* No comment provided by engineer. */ +/* alert message */ "Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." = "Sin Tor o VPN, tu dirección IP será visible para estos servidores XFTP: %@."; /* No comment provided by engineer. */ @@ -4918,9 +5084,6 @@ /* No comment provided by engineer. */ "you" = "tu"; -/* No comment provided by engineer. */ -"You" = "Tú"; - /* No comment provided by engineer. */ "You **must not** use the same database on two devices." = "**No debes** usar la misma base de datos en dos dispositivos."; @@ -5155,9 +5318,15 @@ /* No comment provided by engineer. */ "Your chat database is not encrypted - set passphrase to encrypt it." = "La base de datos no está cifrada - establece una contraseña para cifrarla."; +/* alert title */ +"Your chat preferences" = "Tus preferencias de chat"; + /* No comment provided by engineer. */ "Your chat profiles" = "Mis perfiles"; +/* No comment provided by engineer. */ +"Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." = "Tu conexión ha sido trasladada a %@ pero ha ocurrido un error inesperado al redirigirte al perfil."; + /* No comment provided by engineer. */ "Your contact sent a file that is larger than currently supported maximum size (%@)." = "El contacto ha enviado un archivo mayor al máximo admitido (%@)."; @@ -5167,6 +5336,9 @@ /* No comment provided by engineer. */ "Your contacts will remain connected." = "Tus contactos permanecerán conectados."; +/* No comment provided by engineer. */ +"Your credentials may be sent unencrypted." = "Tus credenciales podrían ser enviadas sin cifrar."; + /* No comment provided by engineer. */ "Your current chat database will be DELETED and REPLACED with the imported one." = "La base de datos actual será ELIMINADA y SUSTITUIDA por la importada."; @@ -5189,7 +5361,10 @@ "Your profile **%@** will be shared." = "El perfil **%@** será compartido."; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Tu perfil es almacenado en tu dispositivo y solamente se comparte con tus contactos.\nLos servidores SimpleX no pueden ver tu perfil."; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Tu perfil es almacenado en tu dispositivo y solamente se comparte con tus contactos. Los servidores SimpleX no pueden ver tu perfil."; + +/* alert message */ +"Your profile was changed. If you save it, the updated profile will be sent to all your contacts." = "Tu perfil ha sido modificado. Si lo guardas la actualización será enviada a todos tus contactos."; /* No comment provided by engineer. */ "Your profile, contacts and delivered messages are stored on your device." = "Tu perfil, contactos y mensajes se almacenan en tu dispositivo."; @@ -5201,7 +5376,7 @@ "Your server" = "Tu servidor"; /* No comment provided by engineer. */ -"Your server address" = "Dirección de tu servidor"; +"Your server address" = "Dirección del servidor"; /* No comment provided by engineer. */ "Your settings" = "Configuración"; diff --git a/apps/ios/fi.lproj/Localizable.strings b/apps/ios/fi.lproj/Localizable.strings index a7820e69b1..d1605152c0 100644 --- a/apps/ios/fi.lproj/Localizable.strings +++ b/apps/ios/fi.lproj/Localizable.strings @@ -547,7 +547,7 @@ /* No comment provided by engineer. */ "Can't invite contacts!" = "Kontakteja ei voi kutsua!"; -/* No comment provided by engineer. */ +/* alert button */ "Cancel" = "Peruuta"; /* feature offered item */ @@ -556,7 +556,7 @@ /* No comment provided by engineer. */ "Cannot access keychain to save database password" = "Ei pääsyä avainnippuun tietokannan salasanan tallentamiseksi"; -/* No comment provided by engineer. */ +/* alert title */ "Cannot receive file" = "Tiedostoa ei voi vastaanottaa"; /* No comment provided by engineer. */ @@ -629,6 +629,9 @@ /* No comment provided by engineer. */ "Chat preferences" = "Chat-asetukset"; +/* No comment provided by engineer. */ +"Chat profile" = "Käyttäjäprofiili"; + /* No comment provided by engineer. */ "Chats" = "Keskustelut"; @@ -1356,7 +1359,7 @@ /* No comment provided by engineer. */ "Error loading %@ servers" = "Virhe %@-palvelimien lataamisessa"; -/* No comment provided by engineer. */ +/* alert title */ "Error receiving file" = "Virhe tiedoston vastaanottamisessa"; /* No comment provided by engineer. */ @@ -1395,7 +1398,7 @@ /* No comment provided by engineer. */ "Error stopping chat" = "Virhe keskustelun lopettamisessa"; -/* No comment provided by engineer. */ +/* alertTitle */ "Error switching profile!" = "Virhe profiilin vaihdossa!"; /* No comment provided by engineer. */ @@ -1416,8 +1419,7 @@ /* No comment provided by engineer. */ "Error: " = "Virhe: "; -/* file error text - snd error text */ +/* alert message */ "Error: %@" = "Virhe: %@"; /* No comment provided by engineer. */ @@ -1519,9 +1521,6 @@ /* No comment provided by engineer. */ "Full name (optional)" = "Koko nimi (valinnainen)"; -/* No comment provided by engineer. */ -"Full name:" = "Koko nimi:"; - /* No comment provided by engineer. */ "Fully re-implemented - work in background!" = "Täysin uudistettu - toimii taustalla!"; @@ -2163,7 +2162,7 @@ /* feature offered item */ "offered %@: %@" = "tarjottu %1$@: %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "Ok"; /* No comment provided by engineer. */ @@ -2572,10 +2571,11 @@ /* No comment provided by engineer. */ "Run chat" = "Käynnistä chat"; -/* chat item action */ +/* alert button + chat item action */ "Save" = "Tallenna"; -/* No comment provided by engineer. */ +/* alert button */ "Save (and notify contacts)" = "Tallenna (ja ilmoita kontakteille)"; /* No comment provided by engineer. */ @@ -2590,9 +2590,6 @@ /* No comment provided by engineer. */ "Save archive" = "Tallenna arkisto"; -/* No comment provided by engineer. */ -"Save auto-accept settings" = "Tallenna automaattisen hyväksynnän asetukset"; - /* No comment provided by engineer. */ "Save group profile" = "Tallenna ryhmäprofiili"; @@ -2614,9 +2611,6 @@ /* No comment provided by engineer. */ "Save servers?" = "Tallenna palvelimet?"; -/* No comment provided by engineer. */ -"Save settings?" = "Tallenna asetukset?"; - /* No comment provided by engineer. */ "Save welcome message?" = "Tallenna tervetuloviesti?"; @@ -2707,7 +2701,7 @@ /* No comment provided by engineer. */ "Send them from gallery or custom keyboards." = "Lähetä ne galleriasta tai mukautetuista näppäimistöistä."; -/* No comment provided by engineer. */ +/* alert message */ "Sender cancelled file transfer." = "Lähettäjä peruutti tiedoston siirron."; /* No comment provided by engineer. */ @@ -3196,9 +3190,6 @@ /* No comment provided by engineer. */ "Use SimpleX Chat servers?" = "Käytä SimpleX Chat palvelimia?"; -/* No comment provided by engineer. */ -"User profile" = "Käyttäjäprofiili"; - /* No comment provided by engineer. */ "Using SimpleX Chat servers." = "Käyttää SimpleX Chat -palvelimia."; @@ -3316,9 +3307,6 @@ /* pref value */ "yes" = "kyllä"; -/* No comment provided by engineer. */ -"You" = "Sinä"; - /* No comment provided by engineer. */ "You accepted connection" = "Hyväksyit yhteyden"; @@ -3512,7 +3500,7 @@ "Your profile **%@** will be shared." = "Profiilisi **%@** jaetaan."; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Profiilisi tallennetaan laitteeseesi ja jaetaan vain yhteystietojesi kanssa.\nSimpleX-palvelimet eivät näe profiiliasi."; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Profiilisi tallennetaan laitteeseesi ja jaetaan vain yhteystietojesi kanssa. SimpleX-palvelimet eivät näe profiiliasi."; /* No comment provided by engineer. */ "Your profile, contacts and delivered messages are stored on your device." = "Profiilisi, kontaktisi ja toimitetut viestit tallennetaan laitteellesi."; diff --git a/apps/ios/fr.lproj/Localizable.strings b/apps/ios/fr.lproj/Localizable.strings index b4f256762c..5d08240a52 100644 --- a/apps/ios/fr.lproj/Localizable.strings +++ b/apps/ios/fr.lproj/Localizable.strings @@ -454,7 +454,7 @@ /* 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. */ +/* profile dropdown */ "All profiles" = "Tous les profiles"; /* No comment provided by engineer. */ @@ -781,7 +781,7 @@ /* No comment provided by engineer. */ "Can't message member" = "Impossible d'envoyer un message à ce membre"; -/* No comment provided by engineer. */ +/* alert button */ "Cancel" = "Annuler"; /* No comment provided by engineer. */ @@ -796,7 +796,7 @@ /* No comment provided by engineer. */ "Cannot forward message" = "Impossible de transférer le message"; -/* No comment provided by engineer. */ +/* alert title */ "Cannot receive file" = "Impossible de recevoir le fichier"; /* snd error text */ @@ -890,6 +890,9 @@ /* No comment provided by engineer. */ "Chat preferences" = "Préférences de chat"; +/* No comment provided by engineer. */ +"Chat profile" = "Profil d'utilisateur"; + /* No comment provided by engineer. */ "Chat theme" = "Thème de chat"; @@ -1629,7 +1632,8 @@ /* No comment provided by engineer. */ "Downgrade and open chat" = "Rétrograder et ouvrir le chat"; -/* chat item action */ +/* alert button + chat item action */ "Download" = "Télécharger"; /* No comment provided by engineer. */ @@ -1938,7 +1942,7 @@ /* No comment provided by engineer. */ "Error opening chat" = "Erreur lors de l'ouverture du chat"; -/* No comment provided by engineer. */ +/* alert title */ "Error receiving file" = "Erreur lors de la réception du fichier"; /* No comment provided by engineer. */ @@ -1995,7 +1999,7 @@ /* No comment provided by engineer. */ "Error stopping chat" = "Erreur lors de l'arrêt du chat"; -/* No comment provided by engineer. */ +/* alertTitle */ "Error switching profile!" = "Erreur lors du changement de profil !"; /* No comment provided by engineer. */ @@ -2022,8 +2026,7 @@ /* No comment provided by engineer. */ "Error: " = "Erreur : "; -/* file error text - snd error text */ +/* alert message */ "Error: %@" = "Erreur : %@"; /* No comment provided by engineer. */ @@ -2203,9 +2206,6 @@ /* No comment provided by engineer. */ "Full name (optional)" = "Nom complet (optionnel)"; -/* No comment provided by engineer. */ -"Full name:" = "Nom complet :"; - /* No comment provided by engineer. */ "Fully decentralized – visible only to members." = "Entièrement décentralisé – visible que par ses membres."; @@ -3084,7 +3084,7 @@ /* feature offered item */ "offered %@: %@" = "propose %1$@ : %2$@"; -/* No comment provided by engineer. */ +/* alert button */ "Ok" = "Ok"; /* No comment provided by engineer. */ @@ -3378,12 +3378,6 @@ /* No comment provided by engineer. */ "Profile images" = "Images de profil"; -/* No comment provided by engineer. */ -"Profile name" = "Nom du profil"; - -/* No comment provided by engineer. */ -"Profile name:" = "Nom du profil :"; - /* No comment provided by engineer. */ "Profile password" = "Mot de passe de profil"; @@ -3715,10 +3709,11 @@ /* No comment provided by engineer. */ "Safer groups" = "Groupes plus sûrs"; -/* chat item action */ +/* alert button + chat item action */ "Save" = "Enregistrer"; -/* No comment provided by engineer. */ +/* alert button */ "Save (and notify contacts)" = "Enregistrer (et en informer les contacts)"; /* No comment provided by engineer. */ @@ -3736,9 +3731,6 @@ /* No comment provided by engineer. */ "Save archive" = "Enregistrer l'archive"; -/* No comment provided by engineer. */ -"Save auto-accept settings" = "Enregistrer les paramètres de validation automatique"; - /* No comment provided by engineer. */ "Save group profile" = "Enregistrer le profil du groupe"; @@ -3760,9 +3752,6 @@ /* No comment provided by engineer. */ "Save servers?" = "Enregistrer les serveurs ?"; -/* No comment provided by engineer. */ -"Save settings?" = "Enregistrer les paramètres ?"; - /* No comment provided by engineer. */ "Save welcome message?" = "Enregistrer le message d'accueil ?"; @@ -3919,7 +3908,7 @@ /* No comment provided by engineer. */ "Send up to 100 last messages to new members." = "Envoi des 100 derniers messages aux nouveaux membres."; -/* No comment provided by engineer. */ +/* alert message */ "Sender cancelled file transfer." = "L'expéditeur a annulé le transfert de fichiers."; /* No comment provided by engineer. */ @@ -4588,7 +4577,7 @@ /* No comment provided by engineer. */ "unknown servers" = "relais inconnus"; -/* No comment provided by engineer. */ +/* alert title */ "Unknown servers!" = "Serveurs inconnus !"; /* No comment provided by engineer. */ @@ -4711,9 +4700,6 @@ /* No comment provided by engineer. */ "Use the app with one hand." = "Utiliser l'application d'une main."; -/* No comment provided by engineer. */ -"User profile" = "Profil d'utilisateur"; - /* No comment provided by engineer. */ "User selection" = "Sélection de l'utilisateur"; @@ -4894,7 +4880,7 @@ /* No comment provided by engineer. */ "Without Tor or VPN, your IP address will be visible to file servers." = "Sans Tor ou un VPN, votre adresse IP sera visible par les serveurs de fichiers."; -/* No comment provided by engineer. */ +/* alert message */ "Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." = "Sans Tor ni VPN, votre adresse IP sera visible par ces relais XFTP : %@."; /* No comment provided by engineer. */ @@ -4918,9 +4904,6 @@ /* No comment provided by engineer. */ "you" = "vous"; -/* No comment provided by engineer. */ -"You" = "Vous"; - /* No comment provided by engineer. */ "You **must not** use the same database on two devices." = "Vous **ne devez pas** utiliser la même base de données sur deux appareils."; @@ -5189,7 +5172,7 @@ "Your profile **%@** will be shared." = "Votre profil **%@** sera partagé."; /* No comment provided by engineer. */ -"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Votre profil est stocké sur votre appareil et est seulement partagé avec vos contacts.\nLes serveurs SimpleX ne peuvent pas voir votre profil."; +"Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." = "Votre profil est stocké sur votre appareil et est seulement partagé avec vos contacts. Les serveurs SimpleX ne peuvent pas voir votre profil."; /* No comment provided by engineer. */ "Your profile, contacts and delivered messages are stored on your device." = "Votre profil, vos contacts et les messages reçus sont stockés sur votre appareil."; diff --git a/apps/ios/hu.lproj/Localizable.strings b/apps/ios/hu.lproj/Localizable.strings index 8c70bcd626..c707f72bf6 100644 --- a/apps/ios/hu.lproj/Localizable.strings +++ b/apps/ios/hu.lproj/Localizable.strings @@ -26,10 +26,10 @@ "- more stable message delivery.\n- a bit better groups.\n- and more!" = "- stabilabb üzenetkézbesítés.\n- valamivel jobb csoportok.\n- és még sok más!"; /* No comment provided by engineer. */ -"- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!" = "- opcionális értesítés a törölt kapcsolatokról.\n- profilnevek szóközökkel.\n- és még sok más!"; +"- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!" = "- értesíti az ismerősöket a törlésről (nem kötelező)\n- profil nevek szóközökkel\n- és még sok más!"; /* No comment provided by engineer. */ -"- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- hangüzenetek legfeljebb 5 perces időtartamig.\n- egyedi eltűnési időhatár megadása.\n- előzmények szerkesztése."; +"- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- 5 perc hosszúságú hangüzenetek.\n- egyedi üzenet-eltűnési időkorlát.\n- előzmények szerkesztése."; /* No comment provided by engineer. */ ", " = ", "; @@ -62,16 +62,16 @@ "[Send us email](mailto:chat@simplex.chat)" = "[Küldjön nekünk e-mailt](mailto:chat@simplex.chat)"; /* No comment provided by engineer. */ -"[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[Csillag a GitHubon](https://github.com/simplex-chat/simplex-chat)"; +"[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[Csillagozás a GitHubon](https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"**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."; +"**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 az ismerőse számára."; +"**Add new contact**: to create your one-time QR Code for your contact." = "**Új ismerős hozzáadása:** egyszer használható 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."; +"**Create group**: to create a new group." = "**Csoport létrehozása:** új csoport létrehozásához."; /* No comment provided by engineer. */ "**e2e encrypted** audio call" = "**e2e titkosított** hanghívás"; @@ -80,25 +80,25 @@ "**e2e encrypted** video call" = "**e2e titkosított** videóhívás"; /* No comment provided by engineer. */ -"**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." = "**Privátabb**: 20 percenként ellenőrzi az új üzeneteket. Az eszköztoken megosztásra kerül a SimpleX Chat kiszolgálóval, de az nem, hogy hány ismerőse vagy üzenete van."; +"**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." = "**Privátabb:** 20 percenként ellenőrzi az új üzeneteket. Az eszköztoken megosztásra kerül a SimpleX Chat-kiszolgálóval, de az nem, hogy hány ismerőse vagy üzenete van."; /* No comment provided by engineer. */ -"**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)."; +"**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 az ismerőseitő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."; +"**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Megjegyzés:** NEM tudja visszaállítani vagy megváltoztatni jelmondatát, ha elveszíti azt."; /* No comment provided by engineer. */ -"**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." = "**Javasolt**: az eszköztoken és az értesítések elküldésre kerülnek a SimpleX Chat értesítési kiszolgálóra, kivéve az üzenet tartalma, mérete vagy az, hogy kitől származik."; +"**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." = "**Megjegyzés:** az eszköztoken és az értesítések elküldésre kerülnek a SimpleX Chat értesítési kiszolgálóra, kivéve az üzenet tartalma, mérete vagy az, hogy kitől származik."; /* No comment provided by engineer. */ -"**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Figyelmeztetés**: Az azonnali push-értesítésekhez a kulcstartóban tárolt jelmondat megadása szükséges."; +"**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Figyelmeztetés:** Az azonnali push-értesítésekhez a kulcstartóban tárolt jelmondat megadása szükséges."; /* No comment provided by engineer. */ -"**Warning**: the archive will be removed." = "**Figyelem**: az archívum törlésre kerül."; +"**Warning**: the archive will be removed." = "**Figyelmeztetés:** az archívum eltávolításra kerül."; /* No comment provided by engineer. */ "*bold*" = "\\*félkövér*"; @@ -137,7 +137,7 @@ "%@ and %@ connected" = "%@ és %@ kapcsolódott"; /* copied message info, at