Compare commits

..

1 Commits

Author SHA1 Message Date
Avently 29857e33e2 multiplatform: init haskell in different thread 2023-11-21 18:47:01 +07:00
344 changed files with 4638 additions and 13734 deletions
+1 -2
View File
@@ -9,7 +9,6 @@ on:
tags: tags:
- "v*" - "v*"
- "!*-fdroid" - "!*-fdroid"
- "!*-armv7a"
pull_request: pull_request:
jobs: jobs:
@@ -263,7 +262,7 @@ jobs:
# rm -rf dist-newstyle/src/direct-sq* is here because of the bug in cabal's dependency which prevents second build from finishing # rm -rf dist-newstyle/src/direct-sq* is here because of the bug in cabal's dependency which prevents second build from finishing
- name: 'Setup MSYS2' - name: 'Setup MSYS2'
if: matrix.os == 'windows-latest' if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'windows-latest'
uses: msys2/setup-msys2@v2 uses: msys2/setup-msys2@v2
with: with:
msystem: ucrt64 msystem: ucrt64
+4 -6
View File
@@ -232,8 +232,6 @@ You can use SimpleX with your own servers and still communicate with people usin
Recent and important updates: Recent and important updates:
[Nov 25, 2023. SimpleX Chat v5.4 released: link mobile and desktop apps via quantum resistant protocol, and much better groups](./blog/20231125-simplex-chat-v5-4-link-mobile-desktop-quantum-resistant-better-groups.md).
[Sep 25, 2023. SimpleX Chat v5.3 released: desktop app, local file encryption, improved groups and directory service](./blog/20230925-simplex-chat-v5-3-desktop-app-local-file-encryption-directory-service.md). [Sep 25, 2023. SimpleX Chat v5.3 released: desktop app, local file encryption, improved groups and directory service](./blog/20230925-simplex-chat-v5-3-desktop-app-local-file-encryption-directory-service.md).
[Jul 22, 2023. SimpleX Chat: v5.2 released with message delivery receipts](./blog/20230722-simplex-chat-v5-2-message-delivery-receipts.md). [Jul 22, 2023. SimpleX Chat: v5.2 released with message delivery receipts](./blog/20230722-simplex-chat-v5-2-message-delivery-receipts.md).
@@ -368,13 +366,13 @@ Please also join [#simplex-devs](https://simplex.chat/contact#/?v=1-2&smp=smp%3A
- ✅ Message delivery confirmation (with sender opt-out per contact). - ✅ Message delivery confirmation (with sender opt-out per contact).
- ✅ Desktop client. - ✅ Desktop client.
- ✅ Encryption of local files stored in the app. - ✅ Encryption of local files stored in the app.
- Using mobile profiles from the desktop app. - 🏗 Using mobile profiles from the desktop app.
- 🏗 Improve experience for the new users.
- 🏗 Post-quantum resistant key exchange in double ratchet protocol.
- 🏗 Large groups, communities and public channels.
- Message delivery relay for senders (to conceal IP address from the recipients' servers and to reduce the traffic). - Message delivery relay for senders (to conceal IP address from the recipients' servers and to reduce the traffic).
- Post-quantum resistant key exchange in double ratchet protocol.
- Large groups, communities and public channels.
- Privacy & security slider - a simple way to set all settings at once. - Privacy & security slider - a simple way to set all settings at once.
- Improve sending videos (including encryption of locally stored videos). - Improve sending videos (including encryption of locally stored videos).
- Improve experience for the new users.
- SMP queue redundancy and rotation (manual is supported). - SMP queue redundancy and rotation (manual is supported).
- Include optional message into connection request sent via contact address. - Include optional message into connection request sent via contact address.
- Improved navigation and search in the conversation (expand and scroll to quoted message, scroll to search results, etc.). - Improved navigation and search in the conversation (expand and scroll to quoted message, scroll to search results, etc.).
+1 -2
View File
@@ -42,7 +42,6 @@ class AppDelegate: NSObject, UIApplicationDelegate {
let m = ChatModel.shared let m = ChatModel.shared
let deviceToken = DeviceToken(pushProvider: PushProvider(env: pushEnvironment), token: token) let deviceToken = DeviceToken(pushProvider: PushProvider(env: pushEnvironment), token: token)
m.deviceToken = deviceToken m.deviceToken = deviceToken
// savedToken is set in startChat, when it is started before this method is called
if m.savedToken != nil { if m.savedToken != nil {
registerToken(token: deviceToken) registerToken(token: deviceToken)
} }
@@ -81,7 +80,7 @@ class AppDelegate: NSObject, UIApplicationDelegate {
} }
} else if let checkMessages = ntfData["checkMessages"] as? Bool, checkMessages { } else if let checkMessages = ntfData["checkMessages"] as? Bool, checkMessages {
logger.debug("AppDelegate: didReceiveRemoteNotification: checkMessages") logger.debug("AppDelegate: didReceiveRemoteNotification: checkMessages")
if m.ntfEnablePeriodic && allowBackgroundRefresh() && BGManager.shared.lastRanLongAgo { if appStateGroupDefault.get().inactive && m.ntfEnablePeriodic {
receiveMessages(completionHandler) receiveMessages(completionHandler)
} else { } else {
completionHandler(.noData) completionHandler(.noData)
+61 -96
View File
@@ -14,14 +14,11 @@ struct ContentView: View {
@ObservedObject var alertManager = AlertManager.shared @ObservedObject var alertManager = AlertManager.shared
@ObservedObject var callController = CallController.shared @ObservedObject var callController = CallController.shared
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
@Binding var doAuthenticate: Bool
var contentAccessAuthenticationExtended: Bool @Binding var userAuthorized: Bool?
@Binding var canConnectCall: Bool
@Environment(\.scenePhase) var scenePhase @Binding var lastSuccessfulUnlock: TimeInterval?
@State private var automaticAuthenticationAttempted = false @Binding var showInitializationView: Bool
@State private var canConnectViewCall = false
@State private var lastSuccessfulUnlock: TimeInterval? = nil
@AppStorage(DEFAULT_SHOW_LA_NOTICE) private var prefShowLANotice = false @AppStorage(DEFAULT_SHOW_LA_NOTICE) private var prefShowLANotice = false
@AppStorage(DEFAULT_LA_NOTICE_SHOWN) private var prefLANoticeShown = false @AppStorage(DEFAULT_LA_NOTICE_SHOWN) private var prefLANoticeShown = false
@AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false @AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false
@@ -43,19 +40,9 @@ struct ContentView: View {
} }
} }
private var accessAuthenticated: Bool {
chatModel.contentViewAccessAuthenticated || contentAccessAuthenticationExtended
}
var body: some View { var body: some View {
ZStack { ZStack {
// contentView() has to be in a single branch, so that enabling authentication doesn't trigger re-rendering and close settings. contentView()
// i.e. with separate branches like this settings are closed: `if prefPerformLA { ... contentView() ... } else { contentView() }
if !prefPerformLA || accessAuthenticated {
contentView()
} else {
lockButton()
}
if chatModel.showCallView, let call = chatModel.activeCall { if chatModel.showCallView, let call = chatModel.activeCall {
callView(call) callView(call)
} }
@@ -63,7 +50,6 @@ struct ContentView: View {
LocalAuthView(authRequest: la) LocalAuthView(authRequest: la)
} else if showSetPasscode { } else if showSetPasscode {
SetAppPasscodeView { SetAppPasscodeView {
chatModel.contentViewAccessAuthenticated = true
prefPerformLA = true prefPerformLA = true
showSetPasscode = false showSetPasscode = false
privacyLocalAuthModeDefault.set(.passcode) privacyLocalAuthModeDefault.set(.passcode)
@@ -74,9 +60,13 @@ struct ContentView: View {
alertManager.showAlert(laPasscodeNotSetAlert()) alertManager.showAlert(laPasscodeNotSetAlert())
} }
} }
if chatModel.chatDbStatus == nil { }
initializationView() .onAppear {
} if prefPerformLA { requestNtfAuthorization() }
initAuthenticate()
}
.onChange(of: doAuthenticate) { _ in
initAuthenticate()
} }
.alert(isPresented: $alertManager.presentAlert) { alertManager.alertView! } .alert(isPresented: $alertManager.presentAlert) { alertManager.alertView! }
.sheet(isPresented: $showSettings) { .sheet(isPresented: $showSettings) {
@@ -86,44 +76,14 @@ struct ContentView: View {
Button("System authentication") { initialEnableLA() } Button("System authentication") { initialEnableLA() }
Button("Passcode entry") { showSetPasscode = true } Button("Passcode entry") { showSetPasscode = true }
} }
.onChange(of: scenePhase) { phase in
logger.debug("scenePhase was \(String(describing: scenePhase)), now \(String(describing: phase))")
switch (phase) {
case .background:
// also see .onChange(of: scenePhase) in SimpleXApp: on entering background
// it remembers enteredBackgroundAuthenticated and sets chatModel.contentViewAccessAuthenticated to false
automaticAuthenticationAttempted = false
canConnectViewCall = false
case .active:
canConnectViewCall = !prefPerformLA || contentAccessAuthenticationExtended || unlockedRecently()
// condition `!chatModel.contentViewAccessAuthenticated` is required for when authentication is enabled in settings or on initial notice
if prefPerformLA && !chatModel.contentViewAccessAuthenticated {
if AppChatState.shared.value != .stopped {
if contentAccessAuthenticationExtended {
chatModel.contentViewAccessAuthenticated = true
} else {
if !automaticAuthenticationAttempted {
automaticAuthenticationAttempted = true
// authenticate if call kit call is not in progress
if !(CallController.useCallKit() && chatModel.showCallView && chatModel.activeCall != nil) {
authenticateContentViewAccess()
}
}
}
} else {
// when app is stopped automatic authentication is not attempted
chatModel.contentViewAccessAuthenticated = contentAccessAuthenticationExtended
}
}
default:
break
}
}
} }
@ViewBuilder private func contentView() -> some View { @ViewBuilder private func contentView() -> some View {
if let status = chatModel.chatDbStatus, status != .ok { if prefPerformLA && userAuthorized != true {
lockButton()
} else if chatModel.chatDbStatus == nil && showInitializationView {
initializationView()
} else if let status = chatModel.chatDbStatus, status != .ok {
DatabaseErrorView(status: status) DatabaseErrorView(status: status)
} else if !chatModel.v3DBMigration.startChat { } else if !chatModel.v3DBMigration.startChat {
MigrateToAppGroupView() MigrateToAppGroupView()
@@ -146,11 +106,11 @@ struct ContentView: View {
if CallController.useCallKit() { if CallController.useCallKit() {
ActiveCallView(call: call, canConnectCall: Binding.constant(true)) ActiveCallView(call: call, canConnectCall: Binding.constant(true))
.onDisappear { .onDisappear {
if prefPerformLA && !accessAuthenticated { authenticateContentViewAccess() } if userAuthorized == false && doAuthenticate { runAuthenticate() }
} }
} else { } else {
ActiveCallView(call: call, canConnectCall: $canConnectViewCall) ActiveCallView(call: call, canConnectCall: $canConnectCall)
if prefPerformLA && !accessAuthenticated { if prefPerformLA && userAuthorized != true {
Rectangle() Rectangle()
.fill(colorScheme == .dark ? .black : .white) .fill(colorScheme == .dark ? .black : .white)
.frame(maxWidth: .infinity, maxHeight: .infinity) .frame(maxWidth: .infinity, maxHeight: .infinity)
@@ -160,27 +120,22 @@ struct ContentView: View {
} }
private func lockButton() -> some View { private func lockButton() -> some View {
Button(action: authenticateContentViewAccess) { Label("Unlock", systemImage: "lock") } Button(action: runAuthenticate) { Label("Unlock", systemImage: "lock") }
} }
private func initializationView() -> some View { private func initializationView() -> some View {
VStack { VStack {
ProgressView().scaleEffect(2) ProgressView().scaleEffect(2)
Text("Opening app") Text("Opening database")
.padding() .padding()
} }
.frame(maxWidth: .infinity, maxHeight: .infinity )
.background(
Rectangle()
.fill(.background)
)
} }
private func mainView() -> some View { private func mainView() -> some View {
ZStack(alignment: .top) { ZStack(alignment: .top) {
ChatListView(showSettings: $showSettings).privacySensitive(protectScreen) ChatListView(showSettings: $showSettings).privacySensitive(protectScreen)
.onAppear { .onAppear {
requestNtfAuthorization() if !prefPerformLA { requestNtfAuthorization() }
// Local Authentication notice is to be shown on next start after onboarding is complete // Local Authentication notice is to be shown on next start after onboarding is complete
if (!prefLANoticeShown && prefShowLANotice && !chatModel.chats.isEmpty) { if (!prefLANoticeShown && prefShowLANotice && !chatModel.chats.isEmpty) {
prefLANoticeShown = true prefLANoticeShown = true
@@ -232,37 +187,48 @@ struct ContentView: View {
} }
} }
private func unlockedRecently() -> Bool { private func initAuthenticate() {
if let lastSuccessfulUnlock = lastSuccessfulUnlock { logger.debug("initAuthenticate")
return ProcessInfo.processInfo.systemUptime - lastSuccessfulUnlock < 2 if CallController.useCallKit() && chatModel.showCallView && chatModel.activeCall != nil {
} else { userAuthorized = false
return false } else if doAuthenticate {
runAuthenticate()
} }
} }
private func authenticateContentViewAccess() { private func runAuthenticate() {
logger.debug("DEBUGGING: authenticateContentViewAccess") logger.debug("DEBUGGING: runAuthenticate")
dismissAllSheets(animated: false) { if !prefPerformLA {
logger.debug("DEBUGGING: authenticateContentViewAccess, in dismissAllSheets callback") userAuthorized = true
chatModel.chatId = nil } else {
logger.debug("DEBUGGING: before dismissAllSheets")
dismissAllSheets(animated: false) {
logger.debug("DEBUGGING: in dismissAllSheets callback")
chatModel.chatId = nil
justAuthenticate()
}
}
}
authenticate(reason: NSLocalizedString("Unlock app", comment: "authentication reason"), selfDestruct: true) { laResult in private func justAuthenticate() {
logger.debug("DEBUGGING: authenticate callback: \(String(describing: laResult))") userAuthorized = false
switch (laResult) { let laMode = privacyLocalAuthModeDefault.get()
case .success: authenticate(reason: NSLocalizedString("Unlock app", comment: "authentication reason"), selfDestruct: true) { laResult in
chatModel.contentViewAccessAuthenticated = true logger.debug("DEBUGGING: authenticate callback: \(String(describing: laResult))")
canConnectViewCall = true switch (laResult) {
lastSuccessfulUnlock = ProcessInfo.processInfo.systemUptime case .success:
case .failed: userAuthorized = true
chatModel.contentViewAccessAuthenticated = false canConnectCall = true
if privacyLocalAuthModeDefault.get() == .passcode { lastSuccessfulUnlock = ProcessInfo.processInfo.systemUptime
AlertManager.shared.showAlert(laFailedAlert()) case .failed:
} if laMode == .passcode {
case .unavailable: AlertManager.shared.showAlert(laFailedAlert())
prefPerformLA = false
canConnectViewCall = true
AlertManager.shared.showAlert(laUnavailableTurningOffAlert())
} }
case .unavailable:
userAuthorized = true
prefPerformLA = false
canConnectCall = true
AlertManager.shared.showAlert(laUnavailableTurningOffAlert())
} }
} }
} }
@@ -293,7 +259,6 @@ struct ContentView: View {
authenticate(reason: NSLocalizedString("Enable SimpleX Lock", comment: "authentication reason")) { laResult in authenticate(reason: NSLocalizedString("Enable SimpleX Lock", comment: "authentication reason")) { laResult in
switch laResult { switch laResult {
case .success: case .success:
chatModel.contentViewAccessAuthenticated = true
prefPerformLA = true prefPerformLA = true
alertManager.showAlert(laTurnedOnAlert()) alertManager.showAlert(laTurnedOnAlert())
case .failed: case .failed:
+6 -28
View File
@@ -15,13 +15,7 @@ private let receiveTaskId = "chat.simplex.app.receive"
// TCP timeout + 2 sec // TCP timeout + 2 sec
private let waitForMessages: TimeInterval = 6 private let waitForMessages: TimeInterval = 6
// This is the smallest interval between refreshes, and also target interval in "off" mode private let bgRefreshInterval: TimeInterval = 450
private let bgRefreshInterval: TimeInterval = 600 // 10 minutes
// This intervals are used for background refresh in instant and periodic modes
private let periodicBgRefreshInterval: TimeInterval = 1200 // 20 minutes
private let maxBgRefreshInterval: TimeInterval = 2400 // 40 minutes
private let maxTimerCount = 9 private let maxTimerCount = 9
@@ -39,14 +33,14 @@ class BGManager {
} }
} }
func schedule(interval: TimeInterval? = nil) { func schedule() {
if !ChatModel.shared.ntfEnableLocal { if !ChatModel.shared.ntfEnableLocal {
logger.debug("BGManager.schedule: disabled") logger.debug("BGManager.schedule: disabled")
return return
} }
logger.debug("BGManager.schedule") logger.debug("BGManager.schedule")
let request = BGAppRefreshTaskRequest(identifier: receiveTaskId) let request = BGAppRefreshTaskRequest(identifier: receiveTaskId)
request.earliestBeginDate = Date(timeIntervalSinceNow: interval ?? runInterval) request.earliestBeginDate = Date(timeIntervalSinceNow: bgRefreshInterval)
do { do {
try BGTaskScheduler.shared.submit(request) try BGTaskScheduler.shared.submit(request)
} catch { } catch {
@@ -54,34 +48,20 @@ class BGManager {
} }
} }
var runInterval: TimeInterval {
switch ChatModel.shared.notificationMode {
case .instant: maxBgRefreshInterval
case .periodic: periodicBgRefreshInterval
case .off: bgRefreshInterval
}
}
var lastRanLongAgo: Bool {
Date.now.timeIntervalSince(chatLastBackgroundRunGroupDefault.get()) > runInterval
}
private func handleRefresh(_ task: BGAppRefreshTask) { private func handleRefresh(_ task: BGAppRefreshTask) {
if !ChatModel.shared.ntfEnableLocal { if !ChatModel.shared.ntfEnableLocal {
logger.debug("BGManager.handleRefresh: disabled") logger.debug("BGManager.handleRefresh: disabled")
return return
} }
logger.debug("BGManager.handleRefresh") logger.debug("BGManager.handleRefresh")
let shouldRun_ = lastRanLongAgo schedule()
if allowBackgroundRefresh() && shouldRun_ { if appStateGroupDefault.get().inactive {
schedule()
let completeRefresh = completionHandler { let completeRefresh = completionHandler {
task.setTaskCompleted(success: true) task.setTaskCompleted(success: true)
} }
task.expirationHandler = { completeRefresh("expirationHandler") } task.expirationHandler = { completeRefresh("expirationHandler") }
receiveMessages(completeRefresh) receiveMessages(completeRefresh)
} else { } else {
schedule(interval: shouldRun_ ? bgRefreshInterval : runInterval)
logger.debug("BGManager.completionHandler: already active, not started") logger.debug("BGManager.completionHandler: already active, not started")
task.setTaskCompleted(success: true) task.setTaskCompleted(success: true)
} }
@@ -110,22 +90,20 @@ class BGManager {
} }
self.completed = false self.completed = false
DispatchQueue.main.async { DispatchQueue.main.async {
chatLastBackgroundRunGroupDefault.set(Date.now)
let m = ChatModel.shared let m = ChatModel.shared
if (!m.chatInitialized) { if (!m.chatInitialized) {
setAppState(.bgRefresh)
do { do {
try initializeChat(start: true) try initializeChat(start: true)
} catch let error { } catch let error {
fatalError("Failed to start or load chats: \(responseError(error))") fatalError("Failed to start or load chats: \(responseError(error))")
} }
} }
activateChat(appState: .bgRefresh)
if m.currentUser == nil { if m.currentUser == nil {
completeReceiving("no current user") completeReceiving("no current user")
return return
} }
logger.debug("BGManager.receiveMessages: starting chat") logger.debug("BGManager.receiveMessages: starting chat")
activateChat(appState: .bgRefresh)
let cr = ChatReceiver() let cr = ChatReceiver()
self.chatReceiver = cr self.chatReceiver = cr
cr.start() cr.start()
+7 -26
View File
@@ -54,8 +54,6 @@ final class ChatModel: ObservableObject {
@Published var chatDbChanged = false @Published var chatDbChanged = false
@Published var chatDbEncrypted: Bool? @Published var chatDbEncrypted: Bool?
@Published var chatDbStatus: DBMigrationResult? @Published var chatDbStatus: DBMigrationResult?
// local authentication
@Published var contentViewAccessAuthenticated: Bool = false
@Published var laRequest: LocalAuthRequest? @Published var laRequest: LocalAuthRequest?
// list of chat "previews" // list of chat "previews"
@Published var chats: [Chat] = [] @Published var chats: [Chat] = []
@@ -85,7 +83,7 @@ final class ChatModel: ObservableObject {
// current WebRTC call // current WebRTC call
@Published var callInvitations: Dictionary<ChatId, RcvCallInvitation> = [:] @Published var callInvitations: Dictionary<ChatId, RcvCallInvitation> = [:]
@Published var activeCall: Call? @Published var activeCall: Call?
let callCommand: WebRTCCommandProcessor = WebRTCCommandProcessor() @Published var callCommand: WCallCommand?
@Published var showCallView = false @Published var showCallView = false
// remote desktop // remote desktop
@Published var remoteCtrlSession: RemoteCtrlSession? @Published var remoteCtrlSession: RemoteCtrlSession?
@@ -106,10 +104,12 @@ final class ChatModel: ObservableObject {
static var ok: Bool { ChatModel.shared.chatDbStatus == .ok } static var ok: Bool { ChatModel.shared.chatDbStatus == .ok }
let ntfEnableLocal = true var ntfEnableLocal: Bool {
notificationMode == .off || ntfEnableLocalGroupDefault.get()
}
var ntfEnablePeriodic: Bool { var ntfEnablePeriodic: Bool {
notificationMode != .off notificationMode == .periodic || ntfEnablePeriodicGroupDefault.get()
} }
var activeRemoteCtrl: Bool { var activeRemoteCtrl: Bool {
@@ -267,20 +267,7 @@ final class ChatModel: ObservableObject {
func addChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) { func addChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) {
// update previews // update previews
if let i = getChatIndex(cInfo.id) { if let i = getChatIndex(cInfo.id) {
chats[i].chatItems = switch cInfo { chats[i].chatItems = [cItem]
case .group:
if let currentPreviewItem = chats[i].chatItems.first {
if cItem.meta.itemTs >= currentPreviewItem.meta.itemTs {
[cItem]
} else {
[currentPreviewItem]
}
} else {
[cItem]
}
default:
[cItem]
}
if case .rcvNew = cItem.meta.itemStatus { if case .rcvNew = cItem.meta.itemStatus {
chats[i].chatStats.unreadCount = chats[i].chatStats.unreadCount + 1 chats[i].chatStats.unreadCount = chats[i].chatStats.unreadCount + 1
increaseUnreadCounter(user: currentUser!) increaseUnreadCounter(user: currentUser!)
@@ -783,7 +770,7 @@ final class GMember: ObservableObject, Identifiable {
} }
struct RemoteCtrlSession { struct RemoteCtrlSession {
var ctrlAppInfo: CtrlAppInfo? var ctrlAppInfo: CtrlAppInfo
var appVersion: String var appVersion: String
var sessionState: UIRemoteCtrlSessionState var sessionState: UIRemoteCtrlSessionState
@@ -795,10 +782,6 @@ struct RemoteCtrlSession {
if case .connected = sessionState { true } else { false } if case .connected = sessionState { true } else { false }
} }
var discovery: Bool {
if case .searching = sessionState { true } else { false }
}
var sessionCode: String? { var sessionCode: String? {
switch sessionState { switch sessionState {
case let .pendingConfirmation(_, sessionCode): sessionCode case let .pendingConfirmation(_, sessionCode): sessionCode
@@ -810,8 +793,6 @@ struct RemoteCtrlSession {
enum UIRemoteCtrlSessionState { enum UIRemoteCtrlSessionState {
case starting case starting
case searching
case found(remoteCtrl: RemoteCtrlInfo, compatible: Bool)
case connecting(remoteCtrl_: RemoteCtrlInfo?) case connecting(remoteCtrl_: RemoteCtrlInfo?)
case pendingConfirmation(remoteCtrl_: RemoteCtrlInfo?, sessionCode: String) case pendingConfirmation(remoteCtrl_: RemoteCtrlInfo?, sessionCode: String)
case connected(remoteCtrl: RemoteCtrlInfo, sessionCode: String) case connected(remoteCtrl: RemoteCtrlInfo, sessionCode: String)
+4 -4
View File
@@ -195,18 +195,18 @@ func moveTempFileFromURL(_ url: URL) -> CryptoFile? {
} }
} }
func generateNewFileName(_ prefix: String, _ ext: String, fullPath: Bool = false) -> String { func generateNewFileName(_ prefix: String, _ ext: String) -> String {
uniqueCombine("\(prefix)_\(getTimestamp()).\(ext)", fullPath: fullPath) uniqueCombine("\(prefix)_\(getTimestamp()).\(ext)")
} }
private func uniqueCombine(_ fileName: String, fullPath: Bool = false) -> String { private func uniqueCombine(_ fileName: String) -> String {
func tryCombine(_ fileName: String, _ n: Int) -> String { func tryCombine(_ fileName: String, _ n: Int) -> String {
let ns = fileName as NSString let ns = fileName as NSString
let name = ns.deletingPathExtension let name = ns.deletingPathExtension
let ext = ns.pathExtension let ext = ns.pathExtension
let suffix = (n == 0) ? "" : "_\(n)" let suffix = (n == 0) ? "" : "_\(n)"
let f = "\(name)\(suffix).\(ext)" let f = "\(name)\(suffix).\(ext)"
return (FileManager.default.fileExists(atPath: fullPath ? f : getAppFilePath(f).path)) ? tryCombine(fileName, n + 1) : f return (FileManager.default.fileExists(atPath: getAppFilePath(f).path)) ? tryCombine(fileName, n + 1) : f
} }
return tryCombine(fileName, 0) return tryCombine(fileName, 0)
} }
-83
View File
@@ -1,83 +0,0 @@
//
// NSESubscriber.swift
// SimpleXChat
//
// Created by Evgeny on 09/12/2023.
// Copyright © 2023 SimpleX Chat. All rights reserved.
//
import Foundation
import SimpleXChat
private var nseSubscribers: [UUID:NSESubscriber] = [:]
// timeout for active notification service extension going into "suspending" state.
// If in two seconds the state does not change, we assume that it was not running and proceed with app activation/answering call.
private let SUSPENDING_TIMEOUT: TimeInterval = 2
// timeout should be larger than SUSPENDING_TIMEOUT
func waitNSESuspended(timeout: TimeInterval, suspended: @escaping (Bool) -> Void) {
if timeout <= SUSPENDING_TIMEOUT {
logger.warning("waitNSESuspended: small timeout \(timeout), using \(SUSPENDING_TIMEOUT + 1)")
}
var state = nseStateGroupDefault.get()
if case .suspended = state {
DispatchQueue.main.async { suspended(true) }
return
}
let id = UUID()
var suspendedCalled = false
checkTimeout()
nseSubscribers[id] = nseMessageSubscriber { msg in
if case let .state(newState) = msg {
state = newState
logger.debug("waitNSESuspended state: \(state.rawValue)")
if case .suspended = newState {
notifySuspended(true)
}
}
}
return
func notifySuspended(_ ok: Bool) {
logger.debug("waitNSESuspended notifySuspended: \(ok)")
if !suspendedCalled {
logger.debug("waitNSESuspended notifySuspended: calling suspended(\(ok))")
suspendedCalled = true
nseSubscribers.removeValue(forKey: id)
DispatchQueue.main.async { suspended(ok) }
}
}
func checkTimeout() {
if !suspending() {
checkSuspendingTimeout()
} else if state == .suspending {
checkSuspendedTimeout()
}
}
func suspending() -> Bool {
suspendedCalled || state == .suspended || state == .suspending
}
func checkSuspendingTimeout() {
DispatchQueue.global().asyncAfter(deadline: .now() + SUSPENDING_TIMEOUT) {
logger.debug("waitNSESuspended check suspending timeout")
if !suspending() {
notifySuspended(false)
} else if state != .suspended {
checkSuspendedTimeout()
}
}
}
func checkSuspendedTimeout() {
DispatchQueue.global().asyncAfter(deadline: .now() + min(timeout - SUSPENDING_TIMEOUT, 1)) {
logger.debug("waitNSESuspended check suspended timeout")
if state != .suspended {
notifySuspended(false)
}
}
}
}
+40 -57
View File
@@ -211,7 +211,7 @@ func apiDeleteUser(_ userId: Int64, _ delSMPQueues: Bool, viewPwd: String?) asyn
} }
func apiStartChat() throws -> Bool { func apiStartChat() throws -> Bool {
let r = chatSendCmdSync(.startChat(mainApp: true)) let r = chatSendCmdSync(.startChat(subscribe: true, expire: true, xftp: true))
switch r { switch r {
case .chatStarted: return true case .chatStarted: return true
case .chatRunning: return false case .chatRunning: return false
@@ -228,8 +228,7 @@ func apiStopChat() async throws {
} }
func apiActivateChat() { func apiActivateChat() {
chatReopenStore() let r = chatSendCmdSync(.apiActivateChat)
let r = chatSendCmdSync(.apiActivateChat(restoreChat: true))
if case .cmdOk = r { return } if case .cmdOk = r { return }
logger.error("apiActivateChat error: \(String(describing: r))") logger.error("apiActivateChat error: \(String(describing: r))")
} }
@@ -403,7 +402,7 @@ func apiGetNtfToken() -> (DeviceToken?, NtfTknStatus?, NotificationsMode) {
case let .ntfToken(token, status, ntfMode): return (token, status, ntfMode) case let .ntfToken(token, status, ntfMode): return (token, status, ntfMode)
case .chatCmdError(_, .errorAgent(.CMD(.PROHIBITED))): return (nil, nil, .off) case .chatCmdError(_, .errorAgent(.CMD(.PROHIBITED))): return (nil, nil, .off)
default: default:
logger.debug("apiGetNtfToken response: \(String(describing: r))") logger.debug("apiGetNtfToken response: \(String(describing: r), privacy: .public)")
return (nil, nil, .off) return (nil, nil, .off)
} }
} }
@@ -606,29 +605,27 @@ func apiConnectPlan(connReq: String) async throws -> ConnectionPlan {
throw r throw r
} }
func apiConnect(incognito: Bool, connReq: String) async -> (ConnReqType, PendingContactConnection)? { func apiConnect(incognito: Bool, connReq: String) async -> ConnReqType? {
let (r, alert) = await apiConnect_(incognito: incognito, connReq: connReq) let (connReqType, alert) = await apiConnect_(incognito: incognito, connReq: connReq)
if let alert = alert { if let alert = alert {
AlertManager.shared.showAlert(alert) AlertManager.shared.showAlert(alert)
return nil return nil
} else { } else {
return r return connReqType
} }
} }
func apiConnect_(incognito: Bool, connReq: String) async -> ((ConnReqType, PendingContactConnection)?, Alert?) { func apiConnect_(incognito: Bool, connReq: String) async -> (ConnReqType?, Alert?) {
guard let userId = ChatModel.shared.currentUser?.userId else { guard let userId = ChatModel.shared.currentUser?.userId else {
logger.error("apiConnect: no current user") logger.error("apiConnect: no current user")
return (nil, nil) return (nil, nil)
} }
let r = await chatSendCmd(.apiConnect(userId: userId, incognito: incognito, connReq: connReq)) let r = await chatSendCmd(.apiConnect(userId: userId, incognito: incognito, connReq: connReq))
let m = ChatModel.shared
switch r { switch r {
case let .sentConfirmation(_, connection): case .sentConfirmation: return (.invitation, nil)
return ((.invitation, connection), nil) case .sentInvitation: return (.contact, nil)
case let .sentInvitation(_, connection):
return ((.contact, connection), nil)
case let .contactAlreadyExists(_, contact): case let .contactAlreadyExists(_, contact):
let m = ChatModel.shared
if let c = m.getContactChat(contact.contactId) { if let c = m.getContactChat(contact.contactId) {
await MainActor.run { m.chatId = c.id } await MainActor.run { m.chatId = c.id }
} }
@@ -922,10 +919,8 @@ func findKnownRemoteCtrl() async throws {
try await sendCommandOkResp(.findKnownRemoteCtrl) try await sendCommandOkResp(.findKnownRemoteCtrl)
} }
func confirmRemoteCtrl(_ rcId: Int64) async throws -> (RemoteCtrlInfo?, CtrlAppInfo, String) { func confirmRemoteCtrl(_ rcId: Int64) async throws {
let r = await chatSendCmd(.confirmRemoteCtrl(remoteCtrlId: rcId)) try await sendCommandOkResp(.confirmRemoteCtrl(remoteCtrlId: rcId))
if case let .remoteCtrlConnecting(rc_, ctrlAppInfo, v) = r { return (rc_, ctrlAppInfo, v) }
throw r
} }
func verifyRemoteCtrlSession(_ sessCode: String) async throws -> RemoteCtrlInfo { func verifyRemoteCtrlSession(_ sessCode: String) async throws -> RemoteCtrlInfo {
@@ -1235,9 +1230,6 @@ func initializeChat(start: Bool, dbKey: String? = nil, refreshInvitations: Bool
try startChat(refreshInvitations: refreshInvitations) try startChat(refreshInvitations: refreshInvitations)
} else { } else {
m.chatRunning = false m.chatRunning = false
try getUserChatData()
NtfManager.shared.setNtfBadgeCount(m.totalUnreadCountForAllUsers())
m.onboardingStage = onboardingStageDefault.get()
} }
} }
@@ -1254,8 +1246,6 @@ func startChat(refreshInvitations: Bool = true) throws {
try refreshCallInvitations() try refreshCallInvitations()
} }
(m.savedToken, m.tokenStatus, m.notificationMode) = apiGetNtfToken() (m.savedToken, m.tokenStatus, m.notificationMode) = apiGetNtfToken()
// deviceToken is set when AppDelegate.application(didRegisterForRemoteNotificationsWithDeviceToken:) is called,
// when it is called before startChat
if let token = m.deviceToken { if let token = m.deviceToken {
registerToken(token: token) registerToken(token: token)
} }
@@ -1370,6 +1360,18 @@ func processReceivedMsg(_ res: ChatResponse) async {
let m = ChatModel.shared let m = ChatModel.shared
logger.debug("processReceivedMsg: \(res.responseType)") logger.debug("processReceivedMsg: \(res.responseType)")
switch res { switch res {
case let .newContactConnection(user, connection):
if active(user) {
await MainActor.run {
m.updateContactConnection(connection)
}
}
case let .contactConnectionDeleted(user, connection):
if active(user) {
await MainActor.run {
m.removeChat(connection.id)
}
}
case let .contactDeletedByContact(user, contact): case let .contactDeletedByContact(user, contact):
if active(user) && contact.directOrUsed { if active(user) && contact.directOrUsed {
await MainActor.run { await MainActor.run {
@@ -1662,40 +1664,36 @@ func processReceivedMsg(_ res: ChatResponse) async {
activateCall(invitation) activateCall(invitation)
case let .callOffer(_, contact, callType, offer, sharedKey, _): case let .callOffer(_, contact, callType, offer, sharedKey, _):
await withCall(contact) { call in await withCall(contact) { call in
await MainActor.run { call.callState = .offerReceived
call.callState = .offerReceived call.peerMedia = callType.media
call.peerMedia = callType.media call.sharedKey = sharedKey
call.sharedKey = sharedKey
}
let useRelay = UserDefaults.standard.bool(forKey: DEFAULT_WEBRTC_POLICY_RELAY) let useRelay = UserDefaults.standard.bool(forKey: DEFAULT_WEBRTC_POLICY_RELAY)
let iceServers = getIceServers() let iceServers = getIceServers()
logger.debug(".callOffer useRelay \(useRelay)") logger.debug(".callOffer useRelay \(useRelay)")
logger.debug(".callOffer iceServers \(String(describing: iceServers))") logger.debug(".callOffer iceServers \(String(describing: iceServers))")
await m.callCommand.processCommand(.offer( m.callCommand = .offer(
offer: offer.rtcSession, offer: offer.rtcSession,
iceCandidates: offer.rtcIceCandidates, iceCandidates: offer.rtcIceCandidates,
media: callType.media, aesKey: sharedKey, media: callType.media, aesKey: sharedKey,
iceServers: iceServers, iceServers: iceServers,
relay: useRelay relay: useRelay
)) )
} }
case let .callAnswer(_, contact, answer): case let .callAnswer(_, contact, answer):
await withCall(contact) { call in await withCall(contact) { call in
await MainActor.run { call.callState = .answerReceived
call.callState = .answerReceived m.callCommand = .answer(answer: answer.rtcSession, iceCandidates: answer.rtcIceCandidates)
}
await m.callCommand.processCommand(.answer(answer: answer.rtcSession, iceCandidates: answer.rtcIceCandidates))
} }
case let .callExtraInfo(_, contact, extraInfo): case let .callExtraInfo(_, contact, extraInfo):
await withCall(contact) { _ in await withCall(contact) { _ in
await m.callCommand.processCommand(.ice(iceCandidates: extraInfo.rtcIceCandidates)) m.callCommand = .ice(iceCandidates: extraInfo.rtcIceCandidates)
} }
case let .callEnded(_, contact): case let .callEnded(_, contact):
if let invitation = await MainActor.run(body: { m.callInvitations.removeValue(forKey: contact.id) }) { if let invitation = await MainActor.run(body: { m.callInvitations.removeValue(forKey: contact.id) }) {
CallController.shared.reportCallRemoteEnded(invitation: invitation) CallController.shared.reportCallRemoteEnded(invitation: invitation)
} }
await withCall(contact) { call in await withCall(contact) { call in
await m.callCommand.processCommand(.end) m.callCommand = .end
CallController.shared.reportCallRemoteEnded(call: call) CallController.shared.reportCallRemoteEnded(call: call)
} }
case .chatSuspended: case .chatSuspended:
@@ -1716,17 +1714,9 @@ func processReceivedMsg(_ res: ChatResponse) async {
await MainActor.run { await MainActor.run {
m.updateGroupMemberConnectionStats(groupInfo, member, ratchetSyncProgress.connectionStats) m.updateGroupMemberConnectionStats(groupInfo, member, ratchetSyncProgress.connectionStats)
} }
case let .remoteCtrlFound(remoteCtrl, ctrlAppInfo_, appVersion, compatible): case let .remoteCtrlFound(remoteCtrl):
await MainActor.run { // TODO multicast
if let sess = m.remoteCtrlSession, case .searching = sess.sessionState { logger.debug("\(String(describing: remoteCtrl))")
let state = UIRemoteCtrlSessionState.found(remoteCtrl: remoteCtrl, compatible: compatible)
m.remoteCtrlSession = RemoteCtrlSession(
ctrlAppInfo: ctrlAppInfo_,
appVersion: appVersion,
sessionState: state
)
}
}
case let .remoteCtrlSessionCode(remoteCtrl_, sessionCode): case let .remoteCtrlSessionCode(remoteCtrl_, sessionCode):
await MainActor.run { await MainActor.run {
let state = UIRemoteCtrlSessionState.pendingConfirmation(remoteCtrl_: remoteCtrl_, sessionCode: sessionCode) let state = UIRemoteCtrlSessionState.pendingConfirmation(remoteCtrl_: remoteCtrl_, sessionCode: sessionCode)
@@ -1741,23 +1731,16 @@ func processReceivedMsg(_ res: ChatResponse) async {
case .remoteCtrlStopped: case .remoteCtrlStopped:
// This delay is needed to cancel the session that fails on network failure, // This delay is needed to cancel the session that fails on network failure,
// e.g. when user did not grant permission to access local network yet. // e.g. when user did not grant permission to access local network yet.
if let sess = m.remoteCtrlSession { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
await MainActor.run { switchToLocalSession()
m.remoteCtrlSession = nil
}
if case .connected = sess.sessionState {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
switchToLocalSession()
}
}
} }
default: default:
logger.debug("unsupported event: \(res.responseType)") logger.debug("unsupported event: \(res.responseType)")
} }
func withCall(_ contact: Contact, _ perform: (Call) async -> Void) async { func withCall(_ contact: Contact, _ perform: (Call) -> Void) async {
if let call = m.activeCall, call.contact.apiId == contact.apiId { if let call = m.activeCall, call.contact.apiId == contact.apiId {
await perform(call) await MainActor.run { perform(call) }
} else { } else {
logger.debug("processReceivedMsg: ignoring \(res.responseType), not in call with the contact \(contact.id)") logger.debug("processReceivedMsg: ignoring \(res.responseType), not in call with the contact \(contact.id)")
} }
+24 -97
View File
@@ -9,30 +9,27 @@
import Foundation import Foundation
import UIKit import UIKit
import SimpleXChat import SimpleXChat
import SwiftUI
private let suspendLockQueue = DispatchQueue(label: "chat.simplex.app.suspend.lock") private let suspendLockQueue = DispatchQueue(label: "chat.simplex.app.suspend.lock")
let appSuspendTimeout: Int = 15 // seconds
let bgSuspendTimeout: Int = 5 // seconds let bgSuspendTimeout: Int = 5 // seconds
let terminationTimeout: Int = 3 // seconds let terminationTimeout: Int = 3 // seconds
let activationDelay: TimeInterval = 1.5
let nseSuspendTimeout: TimeInterval = 5
private func _suspendChat(timeout: Int) { private func _suspendChat(timeout: Int) {
// this is a redundant check to prevent logical errors, like the one fixed in this PR // this is a redundant check to prevent logical errors, like the one fixed in this PR
let state = AppChatState.shared.value let state = appStateGroupDefault.get()
if !state.canSuspend { if !state.canSuspend {
logger.error("_suspendChat called, current state: \(state.rawValue)") logger.error("_suspendChat called, current state: \(state.rawValue, privacy: .public)")
} else if ChatModel.ok { } else if ChatModel.ok {
AppChatState.shared.set(.suspending) appStateGroupDefault.set(.suspending)
apiSuspendChat(timeoutMicroseconds: timeout * 1000000) apiSuspendChat(timeoutMicroseconds: timeout * 1000000)
let endTask = beginBGTask(chatSuspended) let endTask = beginBGTask(chatSuspended)
DispatchQueue.global().asyncAfter(deadline: .now() + Double(timeout) + 1, execute: endTask) DispatchQueue.global().asyncAfter(deadline: .now() + Double(timeout) + 1, execute: endTask)
} else { } else {
AppChatState.shared.set(.suspended) appStateGroupDefault.set(.suspended)
} }
} }
@@ -44,16 +41,18 @@ func suspendChat() {
func suspendBgRefresh() { func suspendBgRefresh() {
suspendLockQueue.sync { suspendLockQueue.sync {
if case .bgRefresh = AppChatState.shared.value { if case .bgRefresh = appStateGroupDefault.get() {
_suspendChat(timeout: bgSuspendTimeout) _suspendChat(timeout: bgSuspendTimeout)
} }
} }
} }
private var terminating = false
func terminateChat() { func terminateChat() {
logger.debug("terminateChat") logger.debug("terminateChat")
suspendLockQueue.sync { suspendLockQueue.sync {
switch AppChatState.shared.value { switch appStateGroupDefault.get() {
case .suspending: case .suspending:
// suspend instantly if already suspending // suspend instantly if already suspending
_chatSuspended() _chatSuspended()
@@ -65,6 +64,7 @@ func terminateChat() {
case .stopped: case .stopped:
chatCloseStore() chatCloseStore()
default: default:
terminating = true
// the store will be closed in _chatSuspended when event is received // the store will be closed in _chatSuspended when event is received
_suspendChat(timeout: terminationTimeout) _suspendChat(timeout: terminationTimeout)
} }
@@ -73,7 +73,7 @@ func terminateChat() {
func chatSuspended() { func chatSuspended() {
suspendLockQueue.sync { suspendLockQueue.sync {
if case .suspending = AppChatState.shared.value { if case .suspending = appStateGroupDefault.get() {
_chatSuspended() _chatSuspended()
} }
} }
@@ -81,121 +81,48 @@ func chatSuspended() {
private func _chatSuspended() { private func _chatSuspended() {
logger.debug("_chatSuspended") logger.debug("_chatSuspended")
AppChatState.shared.set(.suspended) appStateGroupDefault.set(.suspended)
if ChatModel.shared.chatRunning == true { if ChatModel.shared.chatRunning == true {
ChatReceiver.shared.stop() ChatReceiver.shared.stop()
} }
chatCloseStore() if terminating {
} chatCloseStore()
func setAppState(_ appState: AppState) {
suspendLockQueue.sync {
AppChatState.shared.set(appState)
} }
} }
func activateChat(appState: AppState = .active) { func activateChat(appState: AppState = .active) {
logger.debug("DEBUGGING: activateChat") logger.debug("DEBUGGING: activateChat")
terminating = false
suspendLockQueue.sync { suspendLockQueue.sync {
AppChatState.shared.set(appState) appStateGroupDefault.set(appState)
if ChatModel.ok { apiActivateChat() } if ChatModel.ok { apiActivateChat() }
logger.debug("DEBUGGING: activateChat: after apiActivateChat") logger.debug("DEBUGGING: activateChat: after apiActivateChat")
} }
} }
func initChatAndMigrate(refreshInvitations: Bool = true) { func initChatAndMigrate(refreshInvitations: Bool = true) {
terminating = false
let m = ChatModel.shared let m = ChatModel.shared
if (!m.chatInitialized) { if (!m.chatInitialized) {
m.v3DBMigration = v3DBMigrationDefault.get()
if AppChatState.shared.value == .stopped {
AlertManager.shared.showAlert(Alert(
title: Text("Start chat?"),
message: Text("Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat."),
primaryButton: .default(Text("Ok")) {
AppChatState.shared.set(.active)
initialize(start: true)
},
secondaryButton: .cancel {
initialize(start: false)
}
))
} else {
initialize(start: true)
}
}
func initialize(start: Bool) {
do { do {
try initializeChat(start: m.v3DBMigration.startChat && start, refreshInvitations: refreshInvitations) m.v3DBMigration = v3DBMigrationDefault.get()
try initializeChat(start: m.v3DBMigration.startChat, refreshInvitations: refreshInvitations)
} catch let error { } catch let error {
AlertManager.shared.showAlertMsg( fatalError("Failed to start or load chats: \(responseError(error))")
title: start ? "Error starting chat" : "Error opening chat",
message: "Please contact developers.\nError: \(responseError(error))"
)
} }
} }
} }
func startChatForCall() { func startChatAndActivate() {
logger.debug("DEBUGGING: startChatForCall") terminating = false
if ChatModel.shared.chatRunning == true {
ChatReceiver.shared.start()
logger.debug("DEBUGGING: startChatForCall: after ChatReceiver.shared.start")
}
if .active != AppChatState.shared.value {
logger.debug("DEBUGGING: startChatForCall: before activateChat")
activateChat()
logger.debug("DEBUGGING: startChatForCall: after activateChat")
}
}
func startChatAndActivate(_ completion: @escaping () -> Void) {
logger.debug("DEBUGGING: startChatAndActivate") logger.debug("DEBUGGING: startChatAndActivate")
if ChatModel.shared.chatRunning == true { if ChatModel.shared.chatRunning == true {
ChatReceiver.shared.start() ChatReceiver.shared.start()
logger.debug("DEBUGGING: startChatAndActivate: after ChatReceiver.shared.start") logger.debug("DEBUGGING: startChatAndActivate: after ChatReceiver.shared.start")
} }
if case .active = AppChatState.shared.value { if .active != appStateGroupDefault.get() {
completion()
} else if nseStateGroupDefault.get().inactive {
activate()
} else {
// setting app state to "activating" to notify NSE that it should suspend
setAppState(.activating)
waitNSESuspended(timeout: nseSuspendTimeout) { ok in
if !ok {
// if for some reason NSE failed to suspend,
// e.g., it crashed previously without setting its state to "suspended",
// set it to "suspended" state anyway, so that next time app
// does not have to wait when activating.
nseStateGroupDefault.set(.suspended)
}
if AppChatState.shared.value == .activating {
activate()
}
}
}
func activate() {
logger.debug("DEBUGGING: startChatAndActivate: before activateChat") logger.debug("DEBUGGING: startChatAndActivate: before activateChat")
activateChat() activateChat()
completion()
logger.debug("DEBUGGING: startChatAndActivate: after activateChat") logger.debug("DEBUGGING: startChatAndActivate: after activateChat")
} }
} }
// appStateGroupDefault must not be used in the app directly, only via this singleton
class AppChatState {
static let shared = AppChatState()
private var value_ = appStateGroupDefault.get()
var value: AppState {
value_
}
func set(_ state: AppState) {
appStateGroupDefault.set(state)
sendAppState(state)
value_ = state
}
}
+44 -34
View File
@@ -16,15 +16,17 @@ struct SimpleXApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
@StateObject private var chatModel = ChatModel.shared @StateObject private var chatModel = ChatModel.shared
@ObservedObject var alertManager = AlertManager.shared @ObservedObject var alertManager = AlertManager.shared
@Environment(\.scenePhase) var scenePhase @Environment(\.scenePhase) var scenePhase
@State private var enteredBackgroundAuthenticated: TimeInterval? = nil @AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false
@State private var userAuthorized: Bool?
@State private var doAuthenticate = false
@State private var enteredBackground: TimeInterval? = nil
@State private var canConnectCall = false
@State private var lastSuccessfulUnlock: TimeInterval? = nil
@State private var showInitializationView = false
init() { init() {
DispatchQueue.global(qos: .background).sync { hs_init(0, nil)
haskell_init()
// hs_init(0, nil)
}
UserDefaults.standard.register(defaults: appDefaults) UserDefaults.standard.register(defaults: appDefaults)
setGroupDefaults() setGroupDefaults()
registerGroupDefaults() registerGroupDefaults()
@@ -34,17 +36,22 @@ struct SimpleXApp: App {
} }
var body: some Scene { var body: some Scene {
WindowGroup { return WindowGroup {
// contentAccessAuthenticationExtended has to be passed to ContentView on view initialization, ContentView(
// so that it's computed by the time view renders, and not on event after rendering doAuthenticate: $doAuthenticate,
ContentView(contentAccessAuthenticationExtended: !authenticationExpired()) userAuthorized: $userAuthorized,
canConnectCall: $canConnectCall,
lastSuccessfulUnlock: $lastSuccessfulUnlock,
showInitializationView: $showInitializationView
)
.environmentObject(chatModel) .environmentObject(chatModel)
.onOpenURL { url in .onOpenURL { url in
logger.debug("ContentView.onOpenURL: \(url)") logger.debug("ContentView.onOpenURL: \(url)")
chatModel.appOpenUrl = url chatModel.appOpenUrl = url
} }
.onAppear() { .onAppear() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) { showInitializationView = true
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
initChatAndMigrate() initChatAndMigrate()
} }
} }
@@ -52,35 +59,30 @@ struct SimpleXApp: App {
logger.debug("scenePhase was \(String(describing: scenePhase)), now \(String(describing: phase))") logger.debug("scenePhase was \(String(describing: scenePhase)), now \(String(describing: phase))")
switch (phase) { switch (phase) {
case .background: case .background:
// --- authentication
// see ContentView .onChange(of: scenePhase) for remaining authentication logic
if chatModel.contentViewAccessAuthenticated {
enteredBackgroundAuthenticated = ProcessInfo.processInfo.systemUptime
}
chatModel.contentViewAccessAuthenticated = false
// authentication ---
if CallController.useCallKit() && chatModel.activeCall != nil { if CallController.useCallKit() && chatModel.activeCall != nil {
CallController.shared.shouldSuspendChat = true CallController.shared.shouldSuspendChat = true
} else { } else {
suspendChat() suspendChat()
BGManager.shared.schedule() BGManager.shared.schedule()
} }
if userAuthorized == true {
enteredBackground = ProcessInfo.processInfo.systemUptime
}
doAuthenticate = false
canConnectCall = false
NtfManager.shared.setNtfBadgeCount(chatModel.totalUnreadCountForAllUsers()) NtfManager.shared.setNtfBadgeCount(chatModel.totalUnreadCountForAllUsers())
case .active: case .active:
CallController.shared.shouldSuspendChat = false CallController.shared.shouldSuspendChat = false
let appState = AppChatState.shared.value let appState = appStateGroupDefault.get()
startChatAndActivate()
if appState != .stopped { if appState.inactive && chatModel.chatRunning == true {
startChatAndActivate { updateChats()
if appState.inactive && chatModel.chatRunning == true { if !chatModel.showCallView && !CallController.shared.hasActiveCalls() {
updateChats() updateCallInvitations()
if !chatModel.showCallView && !CallController.shared.hasActiveCalls() {
updateCallInvitations()
}
}
} }
} }
doAuthenticate = authenticationExpired()
canConnectCall = !(doAuthenticate && prefPerformLA) || unlockedRecently()
default: default:
break break
} }
@@ -98,12 +100,12 @@ struct SimpleXApp: App {
if legacyDatabase, case .documents = dbContainerGroupDefault.get() { if legacyDatabase, case .documents = dbContainerGroupDefault.get() {
dbContainerGroupDefault.set(.documents) dbContainerGroupDefault.set(.documents)
setMigrationState(.offer) setMigrationState(.offer)
logger.debug("SimpleXApp init: using legacy DB in documents folder: \(getAppDatabasePath())*.db") logger.debug("SimpleXApp init: using legacy DB in documents folder: \(getAppDatabasePath(), privacy: .public)*.db")
} else { } else {
dbContainerGroupDefault.set(.group) dbContainerGroupDefault.set(.group)
setMigrationState(.ready) setMigrationState(.ready)
logger.debug("SimpleXApp init: using DB in app group container: \(getAppDatabasePath())*.db") logger.debug("SimpleXApp init: using DB in app group container: \(getAppDatabasePath(), privacy: .public)*.db")
logger.debug("SimpleXApp init: legacy DB\(legacyDatabase ? "" : " not") present") logger.debug("SimpleXApp init: legacy DB\(legacyDatabase ? "" : " not", privacy: .public) present")
} }
} }
@@ -113,14 +115,22 @@ struct SimpleXApp: App {
} }
private func authenticationExpired() -> Bool { private func authenticationExpired() -> Bool {
if let enteredBackgroundAuthenticated = enteredBackgroundAuthenticated { if let enteredBackground = enteredBackground {
let delay = Double(UserDefaults.standard.integer(forKey: DEFAULT_LA_LOCK_DELAY)) let delay = Double(UserDefaults.standard.integer(forKey: DEFAULT_LA_LOCK_DELAY))
return ProcessInfo.processInfo.systemUptime - enteredBackgroundAuthenticated >= delay return ProcessInfo.processInfo.systemUptime - enteredBackground >= delay
} else { } else {
return true return true
} }
} }
private func unlockedRecently() -> Bool {
if let lastSuccessfulUnlock = lastSuccessfulUnlock {
return ProcessInfo.processInfo.systemUptime - lastSuccessfulUnlock < 2
} else {
return false
}
}
private func updateChats() { private func updateChats() {
do { do {
let chats = try apiGetChats() let chats = try apiGetChats()
@@ -38,21 +38,21 @@ struct ActiveCallView: View {
} }
} }
.onAppear { .onAppear {
logger.debug("ActiveCallView: appear client is nil \(client == nil), scenePhase \(String(describing: scenePhase)), canConnectCall \(canConnectCall)") logger.debug("ActiveCallView: appear client is nil \(client == nil), scenePhase \(String(describing: scenePhase), privacy: .public), canConnectCall \(canConnectCall)")
AppDelegate.keepScreenOn(true) AppDelegate.keepScreenOn(true)
createWebRTCClient() createWebRTCClient()
dismissAllSheets() dismissAllSheets()
} }
.onChange(of: canConnectCall) { _ in .onChange(of: canConnectCall) { _ in
logger.debug("ActiveCallView: canConnectCall changed to \(canConnectCall)") logger.debug("ActiveCallView: canConnectCall changed to \(canConnectCall, privacy: .public)")
createWebRTCClient() createWebRTCClient()
} }
.onDisappear { .onDisappear {
logger.debug("ActiveCallView: disappear") logger.debug("ActiveCallView: disappear")
Task { await m.callCommand.setClient(nil) }
AppDelegate.keepScreenOn(false) AppDelegate.keepScreenOn(false)
client?.endCall() client?.endCall()
} }
.onChange(of: m.callCommand) { _ in sendCommandToClient()}
.background(.black) .background(.black)
.preferredColorScheme(.dark) .preferredColorScheme(.dark)
} }
@@ -60,8 +60,19 @@ struct ActiveCallView: View {
private func createWebRTCClient() { private func createWebRTCClient() {
if client == nil && canConnectCall { if client == nil && canConnectCall {
client = WebRTCClient($activeCall, { msg in await MainActor.run { processRtcMessage(msg: msg) } }, $localRendererAspectRatio) client = WebRTCClient($activeCall, { msg in await MainActor.run { processRtcMessage(msg: msg) } }, $localRendererAspectRatio)
sendCommandToClient()
}
}
private func sendCommandToClient() {
if call == m.activeCall,
m.activeCall != nil,
let client = client,
let cmd = m.callCommand {
m.callCommand = nil
logger.debug("sendCallCommand: \(cmd.cmdType)")
Task { Task {
await m.callCommand.setClient(client) await client.sendCallCommand(command: cmd)
} }
} }
} }
@@ -157,10 +168,8 @@ struct ActiveCallView: View {
} }
case let .error(message): case let .error(message):
logger.debug("ActiveCallView: command error: \(message)") logger.debug("ActiveCallView: command error: \(message)")
AlertManager.shared.showAlert(Alert(title: Text("Error"), message: Text(message)))
case let .invalid(type): case let .invalid(type):
logger.debug("ActiveCallView: invalid response: \(type)") logger.debug("ActiveCallView: invalid response: \(type)")
AlertManager.shared.showAlert(Alert(title: Text("Invalid response"), message: Text(type)))
} }
} }
} }
@@ -246,6 +255,7 @@ struct ActiveCallOverlay: View {
HStack { HStack {
Text(call.encryptionStatus) Text(call.encryptionStatus)
if let connInfo = call.connectionInfo { if let connInfo = call.connectionInfo {
// Text("(") + Text(connInfo.text) + Text(", \(connInfo.protocolText))")
Text("(") + Text(connInfo.text) + Text(")") Text("(") + Text(connInfo.text) + Text(")")
} }
} }
+14 -27
View File
@@ -130,7 +130,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
// The delay allows to accept the second call before suspending a chat // The delay allows to accept the second call before suspending a chat
// see `.onChange(of: scenePhase)` in SimpleXApp // see `.onChange(of: scenePhase)` in SimpleXApp
DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in
logger.debug("CallController: shouldSuspendChat \(String(describing: self?.shouldSuspendChat))") logger.debug("CallController: shouldSuspendChat \(String(describing: self?.shouldSuspendChat), privacy: .public)")
if ChatModel.shared.activeCall == nil && self?.shouldSuspendChat == true { if ChatModel.shared.activeCall == nil && self?.shouldSuspendChat == true {
self?.shouldSuspendChat = false self?.shouldSuspendChat = false
suspendChat() suspendChat()
@@ -142,46 +142,33 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
@objc(pushRegistry:didUpdatePushCredentials:forType:) @objc(pushRegistry:didUpdatePushCredentials:forType:)
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) { func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
logger.debug("CallController: didUpdate push credentials for type \(type.rawValue)") logger.debug("CallController: didUpdate push credentials for type \(type.rawValue, privacy: .public)")
} }
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) { func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
logger.debug("CallController: did receive push with type \(type.rawValue)") logger.debug("CallController: did receive push with type \(type.rawValue, privacy: .public)")
if type != .voIP { if type != .voIP {
completion() completion()
return return
} }
if AppChatState.shared.value == .stopped { logger.debug("CallController: initializing chat")
self.reportExpiredCall(payload: payload, completion)
return
}
if (!ChatModel.shared.chatInitialized) { if (!ChatModel.shared.chatInitialized) {
logger.debug("CallController: initializing chat") initChatAndMigrate(refreshInvitations: false)
do {
try initializeChat(start: true, refreshInvitations: false)
} catch let error {
logger.error("CallController: initializing chat error: \(error)")
self.reportExpiredCall(payload: payload, completion)
return
}
} }
logger.debug("CallController: initialized chat") startChatAndActivate()
startChatForCall() shouldSuspendChat = true
logger.debug("CallController: started chat")
self.shouldSuspendChat = true
// There are no invitations in the model, as it was processed by NSE // There are no invitations in the model, as it was processed by NSE
_ = try? justRefreshCallInvitations() _ = try? justRefreshCallInvitations()
logger.debug("CallController: updated call invitations chat")
// logger.debug("CallController justRefreshCallInvitations: \(String(describing: m.callInvitations))") // logger.debug("CallController justRefreshCallInvitations: \(String(describing: m.callInvitations))")
// Extract the call information from the push notification payload // Extract the call information from the push notification payload
let m = ChatModel.shared let m = ChatModel.shared
if let contactId = payload.dictionaryPayload["contactId"] as? String, if let contactId = payload.dictionaryPayload["contactId"] as? String,
let invitation = m.callInvitations[contactId] { let invitation = m.callInvitations[contactId] {
let update = self.cxCallUpdate(invitation: invitation) let update = cxCallUpdate(invitation: invitation)
if let uuid = invitation.callkitUUID { if let uuid = invitation.callkitUUID {
logger.debug("CallController: report pushkit call via CallKit") logger.debug("CallController: report pushkit call via CallKit")
let update = self.cxCallUpdate(invitation: invitation) let update = cxCallUpdate(invitation: invitation)
self.provider.reportNewIncomingCall(with: uuid, update: update) { error in provider.reportNewIncomingCall(with: uuid, update: update) { error in
if error != nil { if error != nil {
m.callInvitations.removeValue(forKey: contactId) m.callInvitations.removeValue(forKey: contactId)
} }
@@ -189,10 +176,10 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
completion() completion()
} }
} else { } else {
self.reportExpiredCall(update: update, completion) reportExpiredCall(update: update, completion)
} }
} else { } else {
self.reportExpiredCall(payload: payload, completion) reportExpiredCall(payload: payload, completion)
} }
} }
@@ -223,7 +210,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
} }
func reportNewIncomingCall(invitation: RcvCallInvitation, completion: @escaping (Error?) -> Void) { func reportNewIncomingCall(invitation: RcvCallInvitation, completion: @escaping (Error?) -> Void) {
logger.debug("CallController.reportNewIncomingCall, UUID=\(String(describing: invitation.callkitUUID))") logger.debug("CallController.reportNewIncomingCall, UUID=\(String(describing: invitation.callkitUUID), privacy: .public)")
if CallController.useCallKit(), let uuid = invitation.callkitUUID { if CallController.useCallKit(), let uuid = invitation.callkitUUID {
if invitation.callTs.timeIntervalSinceNow >= -180 { if invitation.callTs.timeIntervalSinceNow >= -180 {
let update = cxCallUpdate(invitation: invitation) let update = cxCallUpdate(invitation: invitation)
@@ -363,7 +350,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
private func requestTransaction(with action: CXAction, onSuccess: @escaping () -> Void = {}) { private func requestTransaction(with action: CXAction, onSuccess: @escaping () -> Void = {}) {
controller.request(CXTransaction(action: action)) { error in controller.request(CXTransaction(action: action)) { error in
if let error = error { if let error = error {
logger.error("CallController.requestTransaction error requesting transaction: \(error.localizedDescription)") logger.error("CallController.requestTransaction error requesting transaction: \(error.localizedDescription, privacy: .public)")
} else { } else {
logger.debug("CallController.requestTransaction requested transaction successfully") logger.debug("CallController.requestTransaction requested transaction successfully")
onSuccess() onSuccess()
+8 -12
View File
@@ -22,7 +22,7 @@ class CallManager {
let m = ChatModel.shared let m = ChatModel.shared
if let call = m.activeCall, call.callkitUUID == callUUID { if let call = m.activeCall, call.callkitUUID == callUUID {
m.showCallView = true m.showCallView = true
Task { await m.callCommand.processCommand(.capabilities(media: call.localMedia)) } m.callCommand = .capabilities(media: call.localMedia)
return true return true
} }
return false return false
@@ -57,21 +57,19 @@ class CallManager {
m.activeCall = call m.activeCall = call
m.showCallView = true m.showCallView = true
Task { m.callCommand = .start(
await m.callCommand.processCommand(.start(
media: invitation.callType.media, media: invitation.callType.media,
aesKey: invitation.sharedKey, aesKey: invitation.sharedKey,
iceServers: iceServers, iceServers: iceServers,
relay: useRelay relay: useRelay
)) )
}
} }
} }
func enableMedia(media: CallMediaType, enable: Bool, callUUID: UUID) -> Bool { func enableMedia(media: CallMediaType, enable: Bool, callUUID: UUID) -> Bool {
if let call = ChatModel.shared.activeCall, call.callkitUUID == callUUID { if let call = ChatModel.shared.activeCall, call.callkitUUID == callUUID {
let m = ChatModel.shared let m = ChatModel.shared
Task { await m.callCommand.processCommand(.media(media: media, enable: enable)) } m.callCommand = .media(media: media, enable: enable)
return true return true
} }
return false return false
@@ -96,13 +94,11 @@ class CallManager {
completed() completed()
} else { } else {
logger.debug("CallManager.endCall: ending call...") logger.debug("CallManager.endCall: ending call...")
m.callCommand = .end
m.activeCall = nil
m.showCallView = false
completed()
Task { Task {
await m.callCommand.processCommand(.end)
await MainActor.run {
m.activeCall = nil
m.showCallView = false
completed()
}
do { do {
try await apiEndCall(call.contact) try await apiEndCall(call.contact)
} catch { } catch {
+14 -44
View File
@@ -335,50 +335,6 @@ extension WCallResponse: Encodable {
} }
} }
actor WebRTCCommandProcessor {
private var client: WebRTCClient? = nil
private var commands: [WCallCommand] = []
private var running: Bool = false
func setClient(_ client: WebRTCClient?) async {
logger.debug("WebRTC: setClient, commands count \(self.commands.count)")
self.client = client
if client != nil {
await processAllCommands()
} else {
commands.removeAll()
}
}
func processCommand(_ c: WCallCommand) async {
// logger.debug("WebRTC: process command \(c.cmdType)")
commands.append(c)
if !running && client != nil {
await processAllCommands()
}
}
func processAllCommands() async {
logger.debug("WebRTC: process all commands, commands count \(self.commands.count), client == nil \(self.client == nil)")
if let client = client {
running = true
while let c = commands.first, shouldRunCommand(client, c) {
commands.remove(at: 0)
await client.sendCallCommand(command: c)
logger.debug("WebRTC: processed cmd \(c.cmdType)")
}
running = false
}
}
func shouldRunCommand(_ client: WebRTCClient, _ c: WCallCommand) -> Bool {
switch c {
case .capabilities, .start, .offer, .end: true
default: client.activeCall.wrappedValue != nil
}
}
}
struct ConnectionState: Codable, Equatable { struct ConnectionState: Codable, Equatable {
var connectionState: String var connectionState: String
var iceConnectionState: String var iceConnectionState: String
@@ -402,12 +358,26 @@ struct ConnectionInfo: Codable, Equatable {
return "\(local?.rawValue ?? unknown) / \(remote?.rawValue ?? unknown)" return "\(local?.rawValue ?? unknown) / \(remote?.rawValue ?? unknown)"
} }
} }
var protocolText: String {
let unknown = NSLocalizedString("unknown", comment: "connection info")
let local = localCandidate?.protocol?.uppercased() ?? unknown
let localRelay = localCandidate?.relayProtocol?.uppercased() ?? unknown
let remote = remoteCandidate?.protocol?.uppercased() ?? unknown
let localText = localRelay == local || localCandidate?.relayProtocol == nil
? local
: "\(local) (\(localRelay))"
return local == remote
? localText
: "\(localText) / \(remote)"
}
} }
// https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate // https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate
struct RTCIceCandidate: Codable, Equatable { struct RTCIceCandidate: Codable, Equatable {
var candidateType: RTCIceCandidateType? var candidateType: RTCIceCandidateType?
var `protocol`: String? var `protocol`: String?
var relayProtocol: String?
var sdpMid: String? var sdpMid: String?
var sdpMLineIndex: Int? var sdpMLineIndex: Int?
var candidate: String var candidate: String
+78 -119
View File
@@ -18,11 +18,10 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
}() }()
private static let ivTagBytes: Int = 28 private static let ivTagBytes: Int = 28
private static let enableEncryption: Bool = true private static let enableEncryption: Bool = true
private var chat_ctrl = getChatCtrl()
struct Call { struct Call {
var connection: RTCPeerConnection var connection: RTCPeerConnection
var iceCandidates: IceCandidates var iceCandidates: [RTCIceCandidate]
var localMedia: CallMediaType var localMedia: CallMediaType
var localCamera: RTCVideoCapturer? var localCamera: RTCVideoCapturer?
var localVideoSource: RTCVideoSource? var localVideoSource: RTCVideoSource?
@@ -34,24 +33,10 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
var frameDecryptor: RTCFrameDecryptor? var frameDecryptor: RTCFrameDecryptor?
} }
actor IceCandidates {
private var candidates: [RTCIceCandidate] = []
func getAndClear() async -> [RTCIceCandidate] {
let cs = candidates
candidates = []
return cs
}
func append(_ c: RTCIceCandidate) async {
candidates.append(c)
}
}
private let rtcAudioSession = RTCAudioSession.sharedInstance() private let rtcAudioSession = RTCAudioSession.sharedInstance()
private let audioQueue = DispatchQueue(label: "audio") private let audioQueue = DispatchQueue(label: "audio")
private var sendCallResponse: (WVAPIMessage) async -> Void private var sendCallResponse: (WVAPIMessage) async -> Void
var activeCall: Binding<Call?> private var activeCall: Binding<Call?>
private var localRendererAspectRatio: Binding<CGFloat?> private var localRendererAspectRatio: Binding<CGFloat?>
@available(*, unavailable) @available(*, unavailable)
@@ -75,7 +60,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
WebRTC.RTCIceServer(urlStrings: ["turn:turn.simplex.im:443?transport=tcp"], username: "private", credential: "yleob6AVkiNI87hpR94Z"), WebRTC.RTCIceServer(urlStrings: ["turn:turn.simplex.im:443?transport=tcp"], username: "private", credential: "yleob6AVkiNI87hpR94Z"),
] ]
func initializeCall(_ iceServers: [WebRTC.RTCIceServer]?, _ mediaType: CallMediaType, _ aesKey: String?, _ relay: Bool?) -> Call { func initializeCall(_ iceServers: [WebRTC.RTCIceServer]?, _ remoteIceCandidates: [RTCIceCandidate], _ mediaType: CallMediaType, _ aesKey: String?, _ relay: Bool?) -> Call {
let connection = createPeerConnection(iceServers ?? getWebRTCIceServers() ?? defaultIceServers, relay) let connection = createPeerConnection(iceServers ?? getWebRTCIceServers() ?? defaultIceServers, relay)
connection.delegate = self connection.delegate = self
createAudioSender(connection) createAudioSender(connection)
@@ -102,7 +87,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
} }
return Call( return Call(
connection: connection, connection: connection,
iceCandidates: IceCandidates(), iceCandidates: remoteIceCandidates,
localMedia: mediaType, localMedia: mediaType,
localCamera: localCamera, localCamera: localCamera,
localVideoSource: localVideoSource, localVideoSource: localVideoSource,
@@ -159,18 +144,26 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
logger.debug("starting incoming call - create webrtc session") logger.debug("starting incoming call - create webrtc session")
if activeCall.wrappedValue != nil { endCall() } if activeCall.wrappedValue != nil { endCall() }
let encryption = WebRTCClient.enableEncryption let encryption = WebRTCClient.enableEncryption
let call = initializeCall(iceServers?.toWebRTCIceServers(), media, encryption ? aesKey : nil, relay) let call = initializeCall(iceServers?.toWebRTCIceServers(), [], media, encryption ? aesKey : nil, relay)
activeCall.wrappedValue = call activeCall.wrappedValue = call
let (offer, error) = await call.connection.offer() call.connection.offer { answer in
if let offer = offer { Task {
resp = .offer( let gotCandidates = await self.waitWithTimeout(10_000, stepMs: 1000, until: { self.activeCall.wrappedValue?.iceCandidates.count ?? 0 > 0 })
offer: compressToBase64(input: encodeJSON(CustomRTCSessionDescription(type: offer.type.toSdpType(), sdp: offer.sdp))), if gotCandidates {
iceCandidates: compressToBase64(input: encodeJSON(await self.getInitialIceCandidates())), await self.sendCallResponse(.init(
capabilities: CallCapabilities(encryption: encryption) corrId: nil,
) resp: .offer(
self.waitForMoreIceCandidates() offer: compressToBase64(input: encodeJSON(CustomRTCSessionDescription(type: answer.type.toSdpType(), sdp: answer.sdp))),
} else { iceCandidates: compressToBase64(input: encodeJSON(self.activeCall.wrappedValue?.iceCandidates ?? [])),
resp = .error(message: "offer error: \(error?.localizedDescription ?? "unknown error")") capabilities: CallCapabilities(encryption: encryption)
),
command: command)
)
} else {
self.endCall()
}
}
} }
case let .offer(offer, iceCandidates, media, aesKey, iceServers, relay): case let .offer(offer, iceCandidates, media, aesKey, iceServers, relay):
if activeCall.wrappedValue != nil { if activeCall.wrappedValue != nil {
@@ -179,21 +172,26 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
resp = .error(message: "accept: encryption is not supported") resp = .error(message: "accept: encryption is not supported")
} else if let offer: CustomRTCSessionDescription = decodeJSON(decompressFromBase64(input: offer)), } else if let offer: CustomRTCSessionDescription = decodeJSON(decompressFromBase64(input: offer)),
let remoteIceCandidates: [RTCIceCandidate] = decodeJSON(decompressFromBase64(input: iceCandidates)) { let remoteIceCandidates: [RTCIceCandidate] = decodeJSON(decompressFromBase64(input: iceCandidates)) {
let call = initializeCall(iceServers?.toWebRTCIceServers(), media, WebRTCClient.enableEncryption ? aesKey : nil, relay) let call = initializeCall(iceServers?.toWebRTCIceServers(), remoteIceCandidates, media, WebRTCClient.enableEncryption ? aesKey : nil, relay)
activeCall.wrappedValue = call activeCall.wrappedValue = call
let pc = call.connection let pc = call.connection
if let type = offer.type, let sdp = offer.sdp { if let type = offer.type, let sdp = offer.sdp {
if (try? await pc.setRemoteDescription(RTCSessionDescription(type: type.toWebRTCSdpType(), sdp: sdp))) != nil { if (try? await pc.setRemoteDescription(RTCSessionDescription(type: type.toWebRTCSdpType(), sdp: sdp))) != nil {
let (answer, error) = await pc.answer() pc.answer { answer in
if let answer = answer {
self.addIceCandidates(pc, remoteIceCandidates) self.addIceCandidates(pc, remoteIceCandidates)
resp = .answer( // Task {
answer: compressToBase64(input: encodeJSON(CustomRTCSessionDescription(type: answer.type.toSdpType(), sdp: answer.sdp))), // try? await Task.sleep(nanoseconds: 32_000 * 1000000)
iceCandidates: compressToBase64(input: encodeJSON(await self.getInitialIceCandidates())) Task {
) await self.sendCallResponse(.init(
self.waitForMoreIceCandidates() corrId: nil,
} else { resp: .answer(
resp = .error(message: "answer error: \(error?.localizedDescription ?? "unknown error")") answer: compressToBase64(input: encodeJSON(CustomRTCSessionDescription(type: answer.type.toSdpType(), sdp: answer.sdp))),
iceCandidates: compressToBase64(input: encodeJSON(call.iceCandidates))
),
command: command)
)
}
// }
} }
} else { } else {
resp = .error(message: "accept: remote description is not set") resp = .error(message: "accept: remote description is not set")
@@ -236,7 +234,6 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
resp = .ok resp = .ok
} }
case .end: case .end:
// TODO possibly, endCall should be called before returning .ok
await sendCallResponse(.init(corrId: nil, resp: .ok, command: command)) await sendCallResponse(.init(corrId: nil, resp: .ok, command: command))
endCall() endCall()
} }
@@ -245,33 +242,6 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
} }
} }
func getInitialIceCandidates() async -> [RTCIceCandidate] {
await untilIceComplete(timeoutMs: 750, stepMs: 150) {}
let candidates = await activeCall.wrappedValue?.iceCandidates.getAndClear() ?? []
logger.debug("WebRTCClient: sending initial ice candidates: \(candidates.count)")
return candidates
}
func waitForMoreIceCandidates() {
Task {
await untilIceComplete(timeoutMs: 12000, stepMs: 1500) {
let candidates = await self.activeCall.wrappedValue?.iceCandidates.getAndClear() ?? []
if candidates.count > 0 {
logger.debug("WebRTCClient: sending more ice candidates: \(candidates.count)")
await self.sendIceCandidates(candidates)
}
}
}
}
func sendIceCandidates(_ candidates: [RTCIceCandidate]) async {
await self.sendCallResponse(.init(
corrId: nil,
resp: .ice(iceCandidates: compressToBase64(input: encodeJSON(candidates))),
command: nil)
)
}
func enableMedia(_ media: CallMediaType, _ enable: Bool) { func enableMedia(_ media: CallMediaType, _ enable: Bool) {
logger.debug("WebRTCClient: enabling media \(media.rawValue) \(enable)") logger.debug("WebRTCClient: enabling media \(media.rawValue) \(enable)")
media == .video ? setVideoEnabled(enable) : setAudioEnabled(enable) media == .video ? setVideoEnabled(enable) : setAudioEnabled(enable)
@@ -309,7 +279,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
memcpy(pointer, (unencrypted as NSData).bytes, unencrypted.count) memcpy(pointer, (unencrypted as NSData).bytes, unencrypted.count)
let isKeyFrame = unencrypted[0] & 1 == 0 let isKeyFrame = unencrypted[0] & 1 == 0
let clearTextBytesSize = mediaType.rawValue == 0 ? 1 : isKeyFrame ? 10 : 3 let clearTextBytesSize = mediaType.rawValue == 0 ? 1 : isKeyFrame ? 10 : 3
logCrypto("encrypt", chat_encrypt_media(chat_ctrl, &key, pointer.advanced(by: clearTextBytesSize), Int32(unencrypted.count + WebRTCClient.ivTagBytes - clearTextBytesSize))) logCrypto("encrypt", chat_encrypt_media(&key, pointer.advanced(by: clearTextBytesSize), Int32(unencrypted.count + WebRTCClient.ivTagBytes - clearTextBytesSize)))
return Data(bytes: pointer, count: unencrypted.count + WebRTCClient.ivTagBytes) return Data(bytes: pointer, count: unencrypted.count + WebRTCClient.ivTagBytes)
} else { } else {
return nil return nil
@@ -417,13 +387,12 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
audioSessionToDefaults() audioSessionToDefaults()
} }
func untilIceComplete(timeoutMs: UInt64, stepMs: UInt64, action: @escaping () async -> Void) async { func waitWithTimeout(_ timeoutMs: UInt64, stepMs: UInt64, until success: () -> Bool) async -> Bool {
var t: UInt64 = 0 let startedAt = DispatchTime.now()
repeat { while !success() && startedAt.uptimeNanoseconds + timeoutMs * 1000000 > DispatchTime.now().uptimeNanoseconds {
_ = try? await Task.sleep(nanoseconds: stepMs * 1000000) guard let _ = try? await Task.sleep(nanoseconds: stepMs * 1000000) else { break }
t += stepMs }
await action() return success()
} while t < timeoutMs && activeCall.wrappedValue?.connection.iceGatheringState != .complete
} }
} }
@@ -436,33 +405,25 @@ extension WebRTC.RTCPeerConnection {
optionalConstraints: nil) optionalConstraints: nil)
} }
func offer() async -> (RTCSessionDescription?, Error?) { func offer(_ completion: @escaping (_ sdp: RTCSessionDescription) -> Void) {
await withCheckedContinuation { cont in offer(for: mediaConstraints()) { (sdp, error) in
offer(for: mediaConstraints()) { (sdp, error) in guard let sdp = sdp else {
self.processSDP(cont, sdp, error) return
} }
}
}
func answer() async -> (RTCSessionDescription?, Error?) {
await withCheckedContinuation { cont in
answer(for: mediaConstraints()) { (sdp, error) in
self.processSDP(cont, sdp, error)
}
}
}
private func processSDP(_ cont: CheckedContinuation<(RTCSessionDescription?, Error?), Never>, _ sdp: RTCSessionDescription?, _ error: Error?) {
if let sdp = sdp {
self.setLocalDescription(sdp, completionHandler: { (error) in self.setLocalDescription(sdp, completionHandler: { (error) in
if let error = error { completion(sdp)
cont.resume(returning: (nil, error)) })
} else { }
cont.resume(returning: (sdp, nil)) }
}
func answer(_ completion: @escaping (_ sdp: RTCSessionDescription) -> Void) {
answer(for: mediaConstraints()) { (sdp, error) in
guard let sdp = sdp else {
return
}
self.setLocalDescription(sdp, completionHandler: { (error) in
completion(sdp)
}) })
} else {
cont.resume(returning: (nil, error))
} }
} }
} }
@@ -518,7 +479,6 @@ extension WebRTCClient: RTCPeerConnectionDelegate {
default: enableSpeaker = false default: enableSpeaker = false
} }
setSpeakerEnabledAndConfigureSession(enableSpeaker) setSpeakerEnabledAndConfigureSession(enableSpeaker)
case .connected: sendConnectedEvent(connection)
case .disconnected, .failed: endCall() case .disconnected, .failed: endCall()
default: do {} default: do {}
} }
@@ -531,9 +491,7 @@ extension WebRTCClient: RTCPeerConnectionDelegate {
func peerConnection(_ connection: RTCPeerConnection, didGenerate candidate: WebRTC.RTCIceCandidate) { func peerConnection(_ connection: RTCPeerConnection, didGenerate candidate: WebRTC.RTCIceCandidate) {
// logger.debug("Connection generated candidate \(candidate.debugDescription)") // logger.debug("Connection generated candidate \(candidate.debugDescription)")
Task { activeCall.wrappedValue?.iceCandidates.append(candidate.toCandidate(nil, nil, nil))
await self.activeCall.wrappedValue?.iceCandidates.append(candidate.toCandidate(nil, nil))
}
} }
func peerConnection(_ connection: RTCPeerConnection, didRemove candidates: [WebRTC.RTCIceCandidate]) { func peerConnection(_ connection: RTCPeerConnection, didRemove candidates: [WebRTC.RTCIceCandidate]) {
@@ -548,9 +506,10 @@ extension WebRTCClient: RTCPeerConnectionDelegate {
lastReceivedMs lastDataReceivedMs: Int32, lastReceivedMs lastDataReceivedMs: Int32,
changeReason reason: String) { changeReason reason: String) {
// logger.debug("Connection changed candidate \(reason) \(remote.debugDescription) \(remote.description)") // logger.debug("Connection changed candidate \(reason) \(remote.debugDescription) \(remote.description)")
sendConnectedEvent(connection, local: local, remote: remote)
} }
func sendConnectedEvent(_ connection: WebRTC.RTCPeerConnection) { func sendConnectedEvent(_ connection: WebRTC.RTCPeerConnection, local: WebRTC.RTCIceCandidate, remote: WebRTC.RTCIceCandidate) {
connection.statistics { (stats: RTCStatisticsReport) in connection.statistics { (stats: RTCStatisticsReport) in
stats.statistics.values.forEach { stat in stats.statistics.values.forEach { stat in
// logger.debug("Stat \(stat.debugDescription)") // logger.debug("Stat \(stat.debugDescription)")
@@ -558,25 +517,24 @@ extension WebRTCClient: RTCPeerConnectionDelegate {
let localId = stat.values["localCandidateId"] as? String, let localId = stat.values["localCandidateId"] as? String,
let remoteId = stat.values["remoteCandidateId"] as? String, let remoteId = stat.values["remoteCandidateId"] as? String,
let localStats = stats.statistics[localId], let localStats = stats.statistics[localId],
let remoteStats = stats.statistics[remoteId] let remoteStats = stats.statistics[remoteId],
local.sdp.contains("\((localStats.values["ip"] as? String ?? "--")) \((localStats.values["port"] as? String ?? "--"))") &&
remote.sdp.contains("\((remoteStats.values["ip"] as? String ?? "--")) \((remoteStats.values["port"] as? String ?? "--"))")
{ {
Task { Task {
await self.sendCallResponse(.init( await self.sendCallResponse(.init(
corrId: nil, corrId: nil,
resp: .connected(connectionInfo: ConnectionInfo( resp: .connected(connectionInfo: ConnectionInfo(
localCandidate: RTCIceCandidate( localCandidate: local.toCandidate(
candidateType: RTCIceCandidateType.init(rawValue: localStats.values["candidateType"] as! String), RTCIceCandidateType.init(rawValue: localStats.values["candidateType"] as! String),
protocol: localStats.values["protocol"] as? String, localStats.values["protocol"] as? String,
sdpMid: nil, localStats.values["relayProtocol"] as? String
sdpMLineIndex: nil,
candidate: ""
), ),
remoteCandidate: RTCIceCandidate( remoteCandidate: remote.toCandidate(
candidateType: RTCIceCandidateType.init(rawValue: remoteStats.values["candidateType"] as! String), RTCIceCandidateType.init(rawValue: remoteStats.values["candidateType"] as! String),
protocol: remoteStats.values["protocol"] as? String, remoteStats.values["protocol"] as? String,
sdpMid: nil, remoteStats.values["relayProtocol"] as? String
sdpMLineIndex: nil, ))),
candidate: ""))),
command: nil) command: nil)
) )
} }
@@ -676,10 +634,11 @@ extension RTCIceCandidate {
} }
extension WebRTC.RTCIceCandidate { extension WebRTC.RTCIceCandidate {
func toCandidate(_ candidateType: RTCIceCandidateType?, _ protocol: String?) -> RTCIceCandidate { func toCandidate(_ candidateType: RTCIceCandidateType?, _ protocol: String?, _ relayProtocol: String?) -> RTCIceCandidate {
RTCIceCandidate( RTCIceCandidate(
candidateType: candidateType, candidateType: candidateType,
protocol: `protocol`, protocol: `protocol`,
relayProtocol: relayProtocol,
sdpMid: sdpMid, sdpMid: sdpMid,
sdpMLineIndex: Int(sdpMLineIndex), sdpMLineIndex: Int(sdpMLineIndex),
candidate: sdp candidate: sdp
+3 -8
View File
@@ -723,14 +723,9 @@ struct ChatView: View {
if ci.meta.itemDeleted == nil && !ci.isLiveDummy && !live { if ci.meta.itemDeleted == nil && !ci.isLiveDummy && !live {
menu.append(replyUIAction(ci)) menu.append(replyUIAction(ci))
} }
let fileSource = getLoadedFileSource(ci.file) menu.append(shareUIAction(ci))
let fileExists = if let fs = fileSource, FileManager.default.fileExists(atPath: getAppFilePath(fs.filePath).path) { true } else { false } menu.append(copyUIAction(ci))
let copyAndShareAllowed = !ci.content.text.isEmpty || (ci.content.msgContent?.isImage == true && fileExists) if let fileSource = getLoadedFileSource(ci.file) {
if copyAndShareAllowed {
menu.append(shareUIAction(ci))
menu.append(copyUIAction(ci))
}
if let fileSource = fileSource, fileExists {
if case .image = ci.content.msgContent, let image = getLoadedImage(ci.file) { if case .image = ci.content.msgContent, let image = getLoadedImage(ci.file) {
if image.imageData != nil { if image.imageData != nil {
menu.append(saveFileAction(fileSource)) menu.append(saveFileAction(fileSource))
@@ -104,7 +104,7 @@ struct ComposeState {
var sendEnabled: Bool { var sendEnabled: Bool {
switch preview { switch preview {
case let .mediaPreviews(media): return !media.isEmpty case .mediaPreviews: return true
case .voicePreview: return voiceMessageRecordingState == .finished case .voicePreview: return voiceMessageRecordingState == .finished
case .filePreview: return true case .filePreview: return true
default: return !message.isEmpty || liveMessage != nil default: return !message.isEmpty || liveMessage != nil
@@ -384,10 +384,10 @@ struct ComposeView: View {
} }
} }
.sheet(isPresented: $showMediaPicker) { .sheet(isPresented: $showMediaPicker) {
LibraryMediaListPicker(addMedia: addMediaContent, selectionLimit: 10, finishedPreprocessing: finishedPreprocessingMediaContent) { itemsSelected in LibraryMediaListPicker(media: $chosenMedia, selectionLimit: 10) { itemsSelected in
await MainActor.run { showMediaPicker = false
showMediaPicker = false if itemsSelected {
if itemsSelected { DispatchQueue.main.async {
composeState = composeState.copy(preview: .mediaPreviews(mediaPreviews: [])) composeState = composeState.copy(preview: .mediaPreviews(mediaPreviews: []))
} }
} }
@@ -488,30 +488,6 @@ struct ComposeView: View {
} }
} }
private func addMediaContent(_ content: UploadContent) async {
if let img = resizeImageToStrSize(content.uiImage, maxDataSize: 14000) {
var newMedia: [(String, UploadContent?)] = []
if case var .mediaPreviews(media) = composeState.preview {
media.append((img, content))
newMedia = media
} else {
newMedia = [(img, content)]
}
await MainActor.run {
composeState = composeState.copy(preview: .mediaPreviews(mediaPreviews: newMedia))
}
}
}
// When error occurs while converting video, remove media preview
private func finishedPreprocessingMediaContent() {
if case let .mediaPreviews(media) = composeState.preview, media.isEmpty {
DispatchQueue.main.async {
composeState = composeState.copy(preview: .noPreview)
}
}
}
private var maxFileSize: Int64 { private var maxFileSize: Int64 {
getMaxFileSize(.xftp) getMaxFileSize(.xftp)
} }
@@ -157,7 +157,7 @@ struct AddGroupMembersViewCommon: View {
private func rolePicker() -> some View { private func rolePicker() -> some View {
Picker("New member role", selection: $selectedRole) { Picker("New member role", selection: $selectedRole) {
ForEach(GroupMemberRole.allCases) { role in ForEach(GroupMemberRole.allCases) { role in
if role <= groupInfo.membership.memberRole && role != .author { if role <= groupInfo.membership.memberRole {
Text(role.text) Text(role.text)
} }
} }
@@ -16,6 +16,7 @@ struct GroupChatInfoView: View {
@Environment(\.dismiss) var dismiss: DismissAction @Environment(\.dismiss) var dismiss: DismissAction
@ObservedObject var chat: Chat @ObservedObject var chat: Chat
@Binding var groupInfo: GroupInfo @Binding var groupInfo: GroupInfo
@ObservedObject private var alertManager = AlertManager.shared
@State private var alert: GroupChatInfoViewAlert? = nil @State private var alert: GroupChatInfoViewAlert? = nil
@State private var groupLink: String? @State private var groupLink: String?
@State private var groupLinkMemberRole: GroupMemberRole = .member @State private var groupLinkMemberRole: GroupMemberRole = .member
@@ -188,19 +188,17 @@ struct GroupMemberInfoView: View {
// this condition prevents re-setting picker // this condition prevents re-setting picker
if !justOpened { return } if !justOpened { return }
} }
justOpened = false newRole = member.memberRole
DispatchQueue.main.async { do {
newRole = member.memberRole let (_, stats) = try apiGroupMemberInfo(groupInfo.apiId, member.groupMemberId)
do { let (mem, code) = member.memberActive ? try apiGetGroupMemberCode(groupInfo.apiId, member.groupMemberId) : (member, nil)
let (_, stats) = try apiGroupMemberInfo(groupInfo.apiId, member.groupMemberId) _ = chatModel.upsertGroupMember(groupInfo, mem)
let (mem, code) = member.memberActive ? try apiGetGroupMemberCode(groupInfo.apiId, member.groupMemberId) : (member, nil) connectionStats = stats
_ = chatModel.upsertGroupMember(groupInfo, mem) connectionCode = code
connectionStats = stats } catch let error {
connectionCode = code logger.error("apiGroupMemberInfo or apiGetGroupMemberCode error: \(responseError(error))")
} catch let error {
logger.error("apiGroupMemberInfo or apiGetGroupMemberCode error: \(responseError(error))")
}
} }
justOpened = false
} }
.onChange(of: newRole) { newRole in .onChange(of: newRole) { newRole in
if newRole != member.memberRole { if newRole != member.memberRole {
@@ -103,10 +103,8 @@ struct GroupProfileView: View {
} }
} }
.sheet(isPresented: $showImagePicker) { .sheet(isPresented: $showImagePicker) {
LibraryImagePicker(image: $chosenImage) { _ in LibraryImagePicker(image: $chosenImage) {
await MainActor.run { didSelectItem in showImagePicker = false
showImagePicker = false
}
} }
} }
.onChange(of: chosenImage) { image in .onChange(of: chosenImage) { image in
@@ -17,7 +17,7 @@ struct ScanCodeView: View {
var body: some View { var body: some View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
CodeScannerView(codeTypes: [.qr], scanMode: .oncePerCode, completion: processQRCode) CodeScannerView(codeTypes: [.qr], completion: processQRCode)
.aspectRatio(1, contentMode: .fit) .aspectRatio(1, contentMode: .fit)
.cornerRadius(12) .cornerRadius(12)
Text("Scan security code from your contact's app.") Text("Scan security code from your contact's app.")
@@ -415,7 +415,7 @@ struct DatabaseView: View {
do { do {
try initializeChat(start: true) try initializeChat(start: true)
m.chatDbChanged = false m.chatDbChanged = false
AppChatState.shared.set(.active) appStateGroupDefault.set(.active)
} catch let error { } catch let error {
fatalError("Error starting chat \(responseError(error))") fatalError("Error starting chat \(responseError(error))")
} }
@@ -427,7 +427,7 @@ struct DatabaseView: View {
m.chatRunning = true m.chatRunning = true
ChatReceiver.shared.start() ChatReceiver.shared.start()
chatLastStartGroupDefault.set(Date.now) chatLastStartGroupDefault.set(Date.now)
AppChatState.shared.set(.active) appStateGroupDefault.set(.active)
} catch let error { } catch let error {
runChat = false runChat = false
alert = .error(title: "Error starting chat", error: responseError(error)) alert = .error(title: "Error starting chat", error: responseError(error))
@@ -477,7 +477,7 @@ func stopChatAsync() async throws {
try await apiStopChat() try await apiStopChat()
ChatReceiver.shared.stop() ChatReceiver.shared.stop()
await MainActor.run { ChatModel.shared.chatRunning = false } await MainActor.run { ChatModel.shared.chatRunning = false }
AppChatState.shared.set(.stopped) appStateGroupDefault.set(.stopped)
} }
func deleteChatAsync() async throws { func deleteChatAsync() async throws {
+79 -97
View File
@@ -13,130 +13,112 @@ import SimpleXChat
struct LibraryImagePicker: View { struct LibraryImagePicker: View {
@Binding var image: UIImage? @Binding var image: UIImage?
var didFinishPicking: (_ didSelectImage: Bool) async -> Void var didFinishPicking: (_ didSelectItems: Bool) -> Void
@State var mediaAdded = false @State var images: [UploadContent] = []
var body: some View { var body: some View {
LibraryMediaListPicker(addMedia: addMedia, selectionLimit: 1, didFinishPicking: didFinishPicking) LibraryMediaListPicker(media: $images, selectionLimit: 1, didFinishPicking: didFinishPicking)
} .onChange(of: images) { _ in
if let img = images.first {
private func addMedia(_ content: UploadContent) async { image = img.uiImage
if mediaAdded { return } }
await MainActor.run { }
mediaAdded = true
image = content.uiImage
}
} }
} }
struct LibraryMediaListPicker: UIViewControllerRepresentable { struct LibraryMediaListPicker: UIViewControllerRepresentable {
typealias UIViewControllerType = PHPickerViewController typealias UIViewControllerType = PHPickerViewController
var addMedia: (_ content: UploadContent) async -> Void @Binding var media: [UploadContent]
var selectionLimit: Int var selectionLimit: Int
var finishedPreprocessing: () -> Void = {} var didFinishPicking: (_ didSelectItems: Bool) -> Void
var didFinishPicking: (_ didSelectItems: Bool) async -> Void
class Coordinator: PHPickerViewControllerDelegate { class Coordinator: PHPickerViewControllerDelegate {
let parent: LibraryMediaListPicker let parent: LibraryMediaListPicker
let dispatchQueue = DispatchQueue(label: "chat.simplex.app.LibraryMediaListPicker") let dispatchQueue = DispatchQueue(label: "chat.simplex.app.LibraryMediaListPicker")
var media: [UploadContent] = []
var mediaCount: Int = 0
init(_ parent: LibraryMediaListPicker) { init(_ parent: LibraryMediaListPicker) {
self.parent = parent self.parent = parent
} }
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
Task { parent.didFinishPicking(!results.isEmpty)
await parent.didFinishPicking(!results.isEmpty) guard !results.isEmpty else {
if results.isEmpty { return } return
for r in results {
await loadItem(r.itemProvider)
}
parent.finishedPreprocessing()
} }
}
private func loadItem(_ p: NSItemProvider) async { parent.media = []
logger.debug("LibraryMediaListPicker result") media = []
if p.hasItemConformingToTypeIdentifier(UTType.movie.identifier) { mediaCount = results.count
if let video = await loadVideo(p) { for result in results {
await self.parent.addMedia(video) logger.log("LibraryMediaListPicker result")
logger.debug("LibraryMediaListPicker: added video") let p = result.itemProvider
} if p.hasItemConformingToTypeIdentifier(UTType.movie.identifier) {
} else if p.hasItemConformingToTypeIdentifier(UTType.data.identifier) { p.loadFileRepresentation(forTypeIdentifier: UTType.movie.identifier) { url, error in
if let img = await loadImageData(p) { if let url = url {
await self.parent.addMedia(img) let tempUrl = URL(fileURLWithPath: getTempFilesDirectory().path + "/" + generateNewFileName("video", url.pathExtension))
logger.debug("LibraryMediaListPicker: added image") if ((try? FileManager.default.copyItem(at: url, to: tempUrl)) != nil) {
} ChatModel.shared.filesToDelete.insert(tempUrl)
} else if p.canLoadObject(ofClass: UIImage.self) { self.loadVideo(url: tempUrl, error: error)
if let img = await loadImage(p) {
await self.parent.addMedia(.simpleImage(image: img))
logger.debug("LibraryMediaListPicker: added image")
}
}
}
private func loadImageData(_ p: NSItemProvider) async -> UploadContent? {
await withCheckedContinuation { cont in
loadFileURL(p, type: UTType.data) { url in
if let url = url {
let img = UploadContent.loadFromURL(url: url)
cont.resume(returning: img)
} else {
cont.resume(returning: nil)
}
}
}
}
private func loadImage(_ p: NSItemProvider) async -> UIImage? {
await withCheckedContinuation { cont in
p.loadObject(ofClass: UIImage.self) { obj, err in
if let err = err {
logger.error("LibraryMediaListPicker result image error: \(err.localizedDescription)")
cont.resume(returning: nil)
} else {
cont.resume(returning: obj as? UIImage)
}
}
}
}
private func loadVideo(_ p: NSItemProvider) async -> UploadContent? {
await withCheckedContinuation { cont in
loadFileURL(p, type: UTType.movie) { url in
if let url = url {
let tempUrl = URL(fileURLWithPath: generateNewFileName(getTempFilesDirectory().path + "/" + "rawvideo", url.pathExtension, fullPath: true))
let convertedVideoUrl = URL(fileURLWithPath: generateNewFileName(getTempFilesDirectory().path + "/" + "video", "mp4", fullPath: true))
do {
// logger.debug("LibraryMediaListPicker copyItem \(url) to \(tempUrl)")
try FileManager.default.copyItem(at: url, to: tempUrl)
} catch let err {
logger.error("LibraryMediaListPicker copyItem error: \(err.localizedDescription)")
return cont.resume(returning: nil)
}
Task {
let success = await makeVideoQualityLower(tempUrl, outputUrl: convertedVideoUrl)
try? FileManager.default.removeItem(at: tempUrl)
if success {
_ = ChatModel.shared.filesToDelete.insert(convertedVideoUrl)
let video = UploadContent.loadVideoFromURL(url: convertedVideoUrl)
return cont.resume(returning: video)
} }
try? FileManager.default.removeItem(at: convertedVideoUrl)
cont.resume(returning: nil)
} }
} }
} else if p.hasItemConformingToTypeIdentifier(UTType.data.identifier) {
p.loadFileRepresentation(forTypeIdentifier: UTType.data.identifier) { url, error in
self.loadImage(object: url, error: error)
}
} else if p.canLoadObject(ofClass: UIImage.self) {
p.loadObject(ofClass: UIImage.self) { image, error in
DispatchQueue.main.async {
self.loadImage(object: image, error: error)
}
}
} else {
dispatchQueue.sync { self.mediaCount -= 1}
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
self.dispatchQueue.sync {
if self.parent.media.count == 0 {
logger.log("LibraryMediaListPicker: added \(self.media.count) images out of \(results.count)")
self.parent.media = self.media
}
} }
} }
} }
private func loadFileURL(_ p: NSItemProvider, type: UTType, completion: @escaping (URL?) -> Void) { func loadImage(object: Any?, error: Error? = nil) {
p.loadFileRepresentation(forTypeIdentifier: type.identifier) { url, err in if let error = error {
if let err = err { logger.error("LibraryMediaListPicker: couldn't load image with error: \(error.localizedDescription)")
logger.error("LibraryMediaListPicker loadFileURL error: \(err.localizedDescription)") } else if let image = object as? UIImage {
completion(nil) media.append(.simpleImage(image: image))
} else { logger.log("LibraryMediaListPicker: added image")
completion(url) } else if let url = object as? URL, let image = UploadContent.loadFromURL(url: url) {
media.append(image)
}
dispatchQueue.sync {
self.mediaCount -= 1
if self.mediaCount == 0 && self.parent.media.count == 0 {
logger.log("LibraryMediaListPicker: added all media")
self.parent.media = self.media
self.media = []
}
}
}
func loadVideo(url: URL?, error: Error? = nil) {
if let error = error {
logger.error("LibraryMediaListPicker: couldn't load video with error: \(error.localizedDescription)")
} else if let url = url as URL?, let video = UploadContent.loadVideoFromURL(url: url) {
media.append(video)
}
dispatchQueue.sync {
self.mediaCount -= 1
if self.mediaCount == 0 && self.parent.media.count == 0 {
logger.log("LibraryMediaListPicker: added all media")
self.parent.media = self.media
self.media = []
} }
} }
} }
@@ -1,26 +0,0 @@
//
// VideoUtils.swift
// SimpleX (iOS)
//
// Created by Avently on 25.12.2023.
// Copyright © 2023 SimpleX Chat. All rights reserved.
//
import AVFoundation
import Foundation
import SimpleXChat
func makeVideoQualityLower(_ input: URL, outputUrl: URL) async -> Bool {
let asset: AVURLAsset = AVURLAsset(url: input, options: nil)
if let s = AVAssetExportSession(asset: asset, presetName: AVAssetExportPreset640x480) {
s.outputURL = outputUrl
s.outputFileType = .mp4
s.metadataItemFilter = AVMetadataItemFilter.forSharing()
await s.export()
if let err = s.error {
logger.error("Failed to export video with error: \(err)")
}
return s.status == .completed
}
return false
}
@@ -52,7 +52,7 @@ struct LocalAuthView: View {
resetChatCtrl() resetChatCtrl()
try initializeChat(start: true) try initializeChat(start: true)
m.chatDbChanged = false m.chatDbChanged = false
AppChatState.shared.set(.active) appStateGroupDefault.set(.active)
if m.currentUser != nil { return } if m.currentUser != nil { return }
var profile: Profile? = nil var profile: Profile? = nil
if let displayName = displayName, displayName != "" { if let displayName = displayName, displayName != "" {
@@ -130,10 +130,8 @@ struct AddGroupView: View {
} }
} }
.sheet(isPresented: $showImagePicker) { .sheet(isPresented: $showImagePicker) {
LibraryImagePicker(image: $chosenImage) { _ in LibraryImagePicker(image: $chosenImage) {
await MainActor.run { didSelectItem in showImagePicker = false
showImagePicker = false
}
} }
} }
.alert(isPresented: $showInvalidNameAlert) { .alert(isPresented: $showInvalidNameAlert) {
@@ -73,7 +73,6 @@ struct CreateLinkView: View {
Task { Task {
if let (connReq, pcc) = await apiAddContact(incognito: incognitoGroupDefault.get()) { if let (connReq, pcc) = await apiAddContact(incognito: incognitoGroupDefault.get()) {
await MainActor.run { await MainActor.run {
m.updateContactConnection(pcc)
connReqInvitation = connReq connReqInvitation = connReq
contactConnection = pcc contactConnection = pcc
m.connReqInv = connReq m.connReqInv = connReq
@@ -52,9 +52,6 @@ struct NewChatButton: View {
func addContactAction() { func addContactAction() {
Task { Task {
if let (connReq, pcc) = await apiAddContact(incognito: incognitoGroupDefault.get()) { if let (connReq, pcc) = await apiAddContact(incognito: incognitoGroupDefault.get()) {
await MainActor.run {
ChatModel.shared.updateContactConnection(pcc)
}
actionSheet = .createLink(link: connReq, connection: pcc) actionSheet = .createLink(link: connReq, connection: pcc)
} }
} }
@@ -349,10 +346,7 @@ private func connectContactViaAddress_(_ contact: Contact, dismiss: Bool, incogn
private func connectViaLink(_ connectionLink: String, connectionPlan: ConnectionPlan?, dismiss: Bool, incognito: Bool) { private func connectViaLink(_ connectionLink: String, connectionPlan: ConnectionPlan?, dismiss: Bool, incognito: Bool) {
Task { Task {
if let (connReqType, pcc) = await apiConnect(incognito: incognito, connReq: connectionLink) { if let connReqType = await apiConnect(incognito: incognito, connReq: connectionLink) {
await MainActor.run {
ChatModel.shared.updateContactConnection(pcc)
}
let crt: ConnReqType let crt: ConnReqType
if let plan = connectionPlan { if let plan = connectionPlan {
crt = planToConnReqType(plan) crt = planToConnReqType(plan)
+20 -13
View File
@@ -11,12 +11,20 @@ import CoreImage.CIFilterBuiltins
struct MutableQRCode: View { struct MutableQRCode: View {
@Binding var uri: String @Binding var uri: String
var withLogo: Bool = true @State private var image: UIImage?
var tintColor = UIColor(red: 0.023, green: 0.176, blue: 0.337, alpha: 1)
var body: some View { var body: some View {
QRCode(uri: uri, withLogo: withLogo, tintColor: tintColor) ZStack {
.id("simplex-qrcode-view-for-\(uri)") if let image = image {
qrCodeImage(image)
}
}
.onAppear {
image = generateImage(uri)
}
.onChange(of: uri) { _ in
image = generateImage(uri)
}
} }
} }
@@ -41,7 +49,7 @@ struct QRCode: View {
var withLogo: Bool = true var withLogo: Bool = true
var tintColor = UIColor(red: 0.023, green: 0.176, blue: 0.337, alpha: 1) var tintColor = UIColor(red: 0.023, green: 0.176, blue: 0.337, alpha: 1)
@State private var image: UIImage? = nil @State private var image: UIImage? = nil
@State private var makeScreenshotFunc: () -> Void = {} @State private var makeScreenshotBinding: () -> Void = {}
var body: some View { var body: some View {
ZStack { ZStack {
@@ -62,19 +70,18 @@ struct QRCode: View {
} }
} }
.onAppear { .onAppear {
makeScreenshotFunc = { makeScreenshotBinding = {
let size = CGSizeMake(1024 / UIScreen.main.scale, 1024 / UIScreen.main.scale) let size = CGSizeMake(1024 / UIScreen.main.scale, 1024 / UIScreen.main.scale)
showShareSheet(items: [makeScreenshot(geo.frame(in: .local).origin, size)]) showShareSheet(items: [makeScreenshot(geo.frame(in: .local).origin, size)])}
}
} }
.frame(width: geo.size.width, height: geo.size.height) .frame(width: geo.size.width, height: geo.size.height)
} }
} }
.onTapGesture(perform: makeScreenshotFunc) .onTapGesture(perform: makeScreenshotBinding)
.onAppear { .onAppear {
image = image ?? generateImage(uri, tintColor: tintColor) image = image ?? generateImage(uri)?.replaceColor(UIColor.black, tintColor)
} }
.frame(maxWidth: .infinity, maxHeight: .infinity)
} }
} }
@@ -86,13 +93,13 @@ private func qrCodeImage(_ image: UIImage) -> some View {
.textSelection(.enabled) .textSelection(.enabled)
} }
private func generateImage(_ uri: String, tintColor: UIColor) -> UIImage? { private func generateImage(_ uri: String) -> UIImage? {
let context = CIContext() let context = CIContext()
let filter = CIFilter.qrCodeGenerator() let filter = CIFilter.qrCodeGenerator()
filter.message = Data(uri.utf8) filter.message = Data(uri.utf8)
if let outputImage = filter.outputImage, if let outputImage = filter.outputImage,
let cgImage = context.createCGImage(outputImage, from: outputImage.extent) { let cgImage = context.createCGImage(outputImage, from: outputImage.extent) {
return UIImage(cgImage: cgImage).replaceColor(UIColor.black, tintColor) return UIImage(cgImage: cgImage)
} }
return nil return nil
} }
@@ -25,7 +25,7 @@ struct ScanToConnectView: View {
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.padding(.vertical) .padding(.vertical)
CodeScannerView(codeTypes: [.qr], scanMode: .continuous, completion: processQRCode) CodeScannerView(codeTypes: [.qr], completion: processQRCode)
.aspectRatio(1, contentMode: .fit) .aspectRatio(1, contentMode: .fit)
.cornerRadius(12) .cornerRadius(12)
@@ -81,6 +81,11 @@ struct CreateSimpleXAddress: View {
DispatchQueue.main.async { DispatchQueue.main.async {
m.userAddress = UserContactLink(connReqContact: connReqContact) m.userAddress = UserContactLink(connReqContact: connReqContact)
} }
if let u = try await apiSetProfileAddress(on: true) {
DispatchQueue.main.async {
m.updateUser(u)
}
}
await MainActor.run { progressIndicator = false } await MainActor.run { progressIndicator = false }
} catch let error { } catch let error {
logger.error("CreateSimpleXAddress create address: \(responseError(error))") logger.error("CreateSimpleXAddress create address: \(responseError(error))")
@@ -95,7 +100,7 @@ struct CreateSimpleXAddress: View {
} label: { } label: {
Text("Create SimpleX address").font(.title) Text("Create SimpleX address").font(.title)
} }
Text("You can make it visible to your SimpleX contacts via Settings.") Text("Your contacts in SimpleX will see it.\nYou can change it in Settings.")
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
.font(.footnote) .font(.footnote)
.padding(.horizontal, 32) .padding(.horizontal, 32)
@@ -283,37 +283,6 @@ private let versionDescriptions: [VersionDescription] = [
), ),
] ]
), ),
VersionDescription(
version: "v5.4",
post: URL(string: "https://simplex.chat/blog/20231125-simplex-chat-v5-4-link-mobile-desktop-quantum-resistant-better-groups.html"),
features: [
FeatureDescription(
icon: "desktopcomputer",
title: "Link mobile and desktop apps! 🔗",
description: "Via secure quantum resistant protocol."
),
FeatureDescription(
icon: "person.2",
title: "Better groups",
description: "Faster joining and more reliable messages."
),
FeatureDescription(
icon: "theatermasks",
title: "Incognito groups",
description: "Create a group using a random profile."
),
FeatureDescription(
icon: "hand.raised",
title: "Block group members",
description: "To hide unwanted messages."
),
FeatureDescription(
icon: "gift",
title: "A few more things",
description: "- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!"
),
]
),
] ]
private let lastVersion = versionDescriptions.last!.version private let lastVersion = versionDescriptions.last!.version
@@ -16,19 +16,12 @@ struct ConnectDesktopView: View {
var viaSettings = false var viaSettings = false
@AppStorage(DEFAULT_DEVICE_NAME_FOR_REMOTE_ACCESS) private var deviceName = UIDevice.current.name @AppStorage(DEFAULT_DEVICE_NAME_FOR_REMOTE_ACCESS) private var deviceName = UIDevice.current.name
@AppStorage(DEFAULT_CONFIRM_REMOTE_SESSIONS) private var confirmRemoteSessions = false @AppStorage(DEFAULT_CONFIRM_REMOTE_SESSIONS) private var confirmRemoteSessions = false
@AppStorage(DEFAULT_CONNECT_REMOTE_VIA_MULTICAST) private var connectRemoteViaMulticast = true @AppStorage(DEFAULT_CONNECT_REMOTE_VIA_MULTICAST) private var connectRemoteViaMulticast = false
@AppStorage(DEFAULT_CONNECT_REMOTE_VIA_MULTICAST_AUTO) private var connectRemoteViaMulticastAuto = true @AppStorage(DEFAULT_OFFER_REMOTE_MULTICAST) private var offerRemoteMulticast = true
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@State private var sessionAddress: String = "" @State private var sessionAddress: String = ""
@State private var remoteCtrls: [RemoteCtrlInfo] = [] @State private var remoteCtrls: [RemoteCtrlInfo] = []
@State private var alert: ConnectDesktopAlert? @State private var alert: ConnectDesktopAlert?
@State private var showConnectScreen = true
@State private var showQRCodeScanner = true
@State private var firstAppearance = true
private var useMulticast: Bool {
connectRemoteViaMulticast && !remoteCtrls.isEmpty
}
private enum ConnectDesktopAlert: Identifiable { private enum ConnectDesktopAlert: Identifiable {
case unlinkDesktop(rc: RemoteCtrlInfo) case unlinkDesktop(rc: RemoteCtrlInfo)
@@ -74,14 +67,9 @@ struct ConnectDesktopView: View {
var viewBody: some View { var viewBody: some View {
Group { Group {
let discovery = m.remoteCtrlSession?.discovery if let session = m.remoteCtrlSession {
if discovery == true || (discovery == nil && !showConnectScreen) {
searchingDesktopView()
} else if let session = m.remoteCtrlSession {
switch session.sessionState { switch session.sessionState {
case .starting: connectingDesktopView(session, nil) case .starting: connectingDesktopView(session, nil)
case .searching: searchingDesktopView()
case let .found(rc, compatible): foundDesktopView(session, rc, compatible)
case let .connecting(rc_): connectingDesktopView(session, rc_) case let .connecting(rc_): connectingDesktopView(session, rc_)
case let .pendingConfirmation(rc_, sessCode): case let .pendingConfirmation(rc_, sessCode):
if confirmRemoteSessions || rc_ == nil { if confirmRemoteSessions || rc_ == nil {
@@ -93,35 +81,16 @@ struct ConnectDesktopView: View {
} }
case let .connected(rc, _): activeSessionView(session, rc) case let .connected(rc, _): activeSessionView(session, rc)
} }
// The hack below prevents camera freezing when exiting linked devices view.
// Using showQRCodeScanner inside connectDesktopView or passing it as parameter still results in freezing.
} else if showQRCodeScanner || firstAppearance {
connectDesktopView()
} else { } else {
connectDesktopView(showScanner: false) connectDesktopView()
} }
} }
.onAppear { .onAppear {
setDeviceName(deviceName) setDeviceName(deviceName)
updateRemoteCtrls() updateRemoteCtrls()
showConnectScreen = !useMulticast
if m.remoteCtrlSession != nil {
disconnectDesktop()
} else if useMulticast {
findKnownDesktop()
}
// The hack below prevents camera freezing when exiting linked devices view.
// `firstAppearance` prevents camera flicker when the view first opens.
// moving `showQRCodeScanner = false` to `onDisappear` (to avoid `firstAppearance`) does not prevent freeze.
showQRCodeScanner = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
firstAppearance = false
showQRCodeScanner = true
}
} }
.onDisappear { .onDisappear {
if m.remoteCtrlSession != nil { if m.remoteCtrlSession != nil {
showConnectScreen = false
disconnectDesktop() disconnectDesktop()
} }
} }
@@ -165,14 +134,12 @@ struct ConnectDesktopView: View {
.interactiveDismissDisabled(m.activeRemoteCtrl) .interactiveDismissDisabled(m.activeRemoteCtrl)
} }
private func connectDesktopView(showScanner: Bool = true) -> some View { private func connectDesktopView() -> some View {
List { List {
Section("This device name") { Section("This device name") {
devicesView() devicesView()
} }
if showScanner { scanDesctopAddressView()
scanDesctopAddressView()
}
if developerTools { if developerTools {
desktopAddressView() desktopAddressView()
} }
@@ -200,56 +167,6 @@ struct ConnectDesktopView: View {
.navigationTitle("Connecting to desktop") .navigationTitle("Connecting to desktop")
} }
private func searchingDesktopView() -> some View {
List {
Section("This device name") {
devicesView()
}
Section("Found desktop") {
Text("Waiting for desktop...").italic()
Button {
disconnectDesktop()
} label: {
Label("Scan QR code", systemImage: "qrcode")
}
}
}
.navigationTitle("Connecting to desktop")
}
@ViewBuilder private func foundDesktopView(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo, _ compatible: Bool) -> some View {
let v = List {
Section("This device name") {
devicesView()
}
Section("Found desktop") {
ctrlDeviceNameText(session, rc)
ctrlDeviceVersionText(session)
if !compatible {
Text("Not compatible!").foregroundColor(.red)
} else if !connectRemoteViaMulticastAuto {
Button {
confirmKnownDesktop(rc)
} label: {
Label("Connect", systemImage: "checkmark")
}
}
}
if !compatible && !connectRemoteViaMulticastAuto {
Section {
disconnectButton("Cancel")
}
}
}
.navigationTitle("Found desktop")
if compatible && connectRemoteViaMulticastAuto {
v.onAppear { confirmKnownDesktop(rc) }
} else {
v
}
}
private func verifySessionView(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo?, _ sessCode: String) -> some View { private func verifySessionView(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo?, _ sessCode: String) -> some View {
List { List {
Section("Connected to desktop") { Section("Connected to desktop") {
@@ -274,7 +191,7 @@ struct ConnectDesktopView: View {
} }
private func ctrlDeviceNameText(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo?) -> Text { private func ctrlDeviceNameText(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo?) -> Text {
var t = Text(rc?.deviceViewName ?? session.ctrlAppInfo?.deviceName ?? "") var t = Text(rc?.deviceViewName ?? session.ctrlAppInfo.deviceName)
if (rc == nil) { if (rc == nil) {
t = t + Text(" ") + Text("(new)").italic() t = t + Text(" ") + Text("(new)").italic()
} }
@@ -282,8 +199,8 @@ struct ConnectDesktopView: View {
} }
private func ctrlDeviceVersionText(_ session: RemoteCtrlSession) -> Text { private func ctrlDeviceVersionText(_ session: RemoteCtrlSession) -> Text {
let v = session.ctrlAppInfo?.appVersionRange.maxVersion let v = session.ctrlAppInfo.appVersionRange.maxVersion
var t = Text("v\(v ?? "")") var t = Text("v\(v)")
if v != session.appVersion { if v != session.appVersion {
t = t + Text(" ") + Text("(this device v\(session.appVersion))").italic() t = t + Text(" ") + Text("(this device v\(session.appVersion))").italic()
} }
@@ -332,7 +249,7 @@ struct ConnectDesktopView: View {
private func scanDesctopAddressView() -> some View { private func scanDesctopAddressView() -> some View {
Section("Scan QR code from desktop") { Section("Scan QR code from desktop") {
CodeScannerView(codeTypes: [.qr], scanMode: .oncePerCode, completion: processDesktopQRCode) CodeScannerView(codeTypes: [.qr], completion: processDesktopQRCode)
.aspectRatio(1, contentMode: .fit) .aspectRatio(1, contentMode: .fit)
.cornerRadius(12) .cornerRadius(12)
.listRowBackground(Color.clear) .listRowBackground(Color.clear)
@@ -384,10 +301,7 @@ struct ConnectDesktopView: View {
Section("Linked desktop options") { Section("Linked desktop options") {
Toggle("Verify connections", isOn: $confirmRemoteSessions) Toggle("Verify connections", isOn: $confirmRemoteSessions)
Toggle("Discover via local network", isOn: $connectRemoteViaMulticast) Toggle("Discover on network", isOn: $connectRemoteViaMulticast).disabled(true)
if connectRemoteViaMulticast {
Toggle("Connect automatically", isOn: $connectRemoteViaMulticastAuto)
}
} }
} }
.navigationTitle("Linked desktops") .navigationTitle("Linked desktops")
@@ -421,42 +335,10 @@ struct ConnectDesktopView: View {
} }
} }
private func findKnownDesktop() {
Task {
do {
try await findKnownRemoteCtrl()
await MainActor.run {
m.remoteCtrlSession = RemoteCtrlSession(
ctrlAppInfo: nil,
appVersion: "",
sessionState: .searching
)
showConnectScreen = true
}
} catch let e {
await MainActor.run {
errorAlert(e)
}
}
}
}
private func confirmKnownDesktop(_ rc: RemoteCtrlInfo) {
connectDesktop_ {
try await confirmRemoteCtrl(rc.remoteCtrlId)
}
}
private func connectDesktopAddress(_ addr: String) { private func connectDesktopAddress(_ addr: String) {
connectDesktop_ {
try await connectRemoteCtrl(desktopAddress: addr)
}
}
private func connectDesktop_(_ connect: @escaping () async throws -> (RemoteCtrlInfo?, CtrlAppInfo, String)) {
Task { Task {
do { do {
let (rc_, ctrlAppInfo, v) = try await connect() let (rc_, ctrlAppInfo, v) = try await connectRemoteCtrl(desktopAddress: addr)
await MainActor.run { await MainActor.run {
sessionAddress = "" sessionAddress = ""
m.remoteCtrlSession = RemoteCtrlSession( m.remoteCtrlSession = RemoteCtrlSession(
@@ -498,11 +380,11 @@ struct ConnectDesktopView: View {
} }
} }
private func disconnectButton(_ label: LocalizedStringKey = "Disconnect") -> some View { private func disconnectButton() -> some View {
Button { Button {
disconnectDesktop(.dismiss) disconnectDesktop()
} label: { } label: {
Label(label, systemImage: "multiply") Label("Disconnect", systemImage: "multiply")
} }
} }
@@ -511,11 +393,7 @@ struct ConnectDesktopView: View {
do { do {
try await stopRemoteCtrl() try await stopRemoteCtrl()
await MainActor.run { await MainActor.run {
if case .connected = m.remoteCtrlSession?.sessionState { switchToLocalSession()
switchToLocalSession()
} else {
m.remoteCtrlSession = nil
}
switch action { switch action {
case .back: dismiss() case .back: dismiss()
case .dismiss: dismiss() case .dismiss: dismiss()
@@ -51,9 +51,9 @@ struct AdvancedNetworkSettings: View {
} }
.disabled(currentNetCfg == NetCfg.proxyDefaults) .disabled(currentNetCfg == NetCfg.proxyDefaults)
timeoutSettingPicker("TCP connection timeout", selection: $netCfg.tcpConnectTimeout, values: [7_500000, 10_000000, 15_000000, 20_000000, 30_000000, 45_000000], label: secondsLabel) timeoutSettingPicker("TCP connection timeout", selection: $netCfg.tcpConnectTimeout, values: [5_000000, 7_500000, 10_000000, 15_000000, 20_000000, 30_000000, 45_000000], label: secondsLabel)
timeoutSettingPicker("Protocol timeout", selection: $netCfg.tcpTimeout, values: [5_000000, 7_000000, 10_000000, 15_000000, 20_000000, 30_000000], label: secondsLabel) timeoutSettingPicker("Protocol timeout", selection: $netCfg.tcpTimeout, values: [3_000000, 5_000000, 7_000000, 10_000000, 15_000000, 20_000000, 30_000000], label: secondsLabel)
timeoutSettingPicker("Protocol timeout per KB", selection: $netCfg.tcpTimeoutPerKb, values: [15_000, 30_000, 45_000, 60_000, 90_000, 120_000], label: secondsLabel) timeoutSettingPicker("Protocol timeout per KB", selection: $netCfg.tcpTimeoutPerKb, values: [15_000, 30_000, 60_000, 90_000, 120_000], label: secondsLabel)
timeoutSettingPicker("PING interval", selection: $netCfg.smpPingInterval, values: [120_000000, 300_000000, 600_000000, 1200_000000, 2400_000000, 3600_000000], label: secondsLabel) timeoutSettingPicker("PING interval", selection: $netCfg.smpPingInterval, values: [120_000000, 300_000000, 600_000000, 1200_000000, 2400_000000, 3600_000000], label: secondsLabel)
intSettingPicker("PING count", selection: $netCfg.smpPingCount, values: [1, 2, 3, 5, 8], label: "") intSettingPicker("PING count", selection: $netCfg.smpPingCount, values: [1, 2, 3, 5, 8], label: "")
Toggle("Enable TCP keep-alive", isOn: $enableKeepAlive) Toggle("Enable TCP keep-alive", isOn: $enableKeepAlive)
@@ -14,6 +14,9 @@ struct NotificationsView: View {
@State private var notificationMode: NotificationsMode = ChatModel.shared.notificationMode @State private var notificationMode: NotificationsMode = ChatModel.shared.notificationMode
@State private var showAlert: NotificationAlert? @State private var showAlert: NotificationAlert?
@State private var legacyDatabase = dbContainerGroupDefault.get() == .documents @State private var legacyDatabase = dbContainerGroupDefault.get() == .documents
// @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
// @AppStorage(GROUP_DEFAULT_NTF_ENABLE_LOCAL, store: groupDefaults) private var ntfEnableLocal = false
// @AppStorage(GROUP_DEFAULT_NTF_ENABLE_PERIODIC, store: groupDefaults) private var ntfEnablePeriodic = false
var body: some View { var body: some View {
List { List {
@@ -85,6 +88,13 @@ struct NotificationsView: View {
.padding(.top, 1) .padding(.top, 1)
} }
} }
// if developerTools {
// Section(String("Experimental")) {
// Toggle(String("Always enable local"), isOn: $ntfEnableLocal)
// Toggle(String("Always enable periodic"), isOn: $ntfEnablePeriodic)
// }
// }
} }
.disabled(legacyDatabase) .disabled(legacyDatabase)
} }
@@ -109,7 +119,7 @@ struct NotificationsView: View {
private func ntfModeAlertTitle(_ mode: NotificationsMode) -> LocalizedStringKey { private func ntfModeAlertTitle(_ mode: NotificationsMode) -> LocalizedStringKey {
switch mode { switch mode {
case .off: return "Use only local notifications?" case .off: return "Turn off notifications?"
case .periodic: return "Enable periodic notifications?" case .periodic: return "Enable periodic notifications?"
case .instant: return "Enable instant notifications?" case .instant: return "Enable instant notifications?"
} }
@@ -467,7 +467,6 @@ struct SimplexLockView: View {
switch a { switch a {
case .enableAuth: case .enableAuth:
SetAppPasscodeView { SetAppPasscodeView {
m.contentViewAccessAuthenticated = true
laLockDelay = 30 laLockDelay = 30
prefPerformLA = true prefPerformLA = true
showChangePassword = true showChangePassword = true
@@ -620,7 +619,6 @@ struct SimplexLockView: View {
authenticate(reason: NSLocalizedString("Enable SimpleX Lock", comment: "authentication reason")) { laResult in authenticate(reason: NSLocalizedString("Enable SimpleX Lock", comment: "authentication reason")) { laResult in
switch laResult { switch laResult {
case .success: case .success:
m.contentViewAccessAuthenticated = true
prefPerformLA = true prefPerformLA = true
laAlert = .laTurnedOnAlert laAlert = .laTurnedOnAlert
case .failed: case .failed:
@@ -21,7 +21,7 @@ struct ScanProtocolServer: View {
.font(.largeTitle) .font(.largeTitle)
.bold() .bold()
.padding(.vertical) .padding(.vertical)
CodeScannerView(codeTypes: [.qr], scanMode: .oncePerCode, completion: processQRCode) CodeScannerView(codeTypes: [.qr], completion: processQRCode)
.aspectRatio(1, contentMode: .fit) .aspectRatio(1, contentMode: .fit)
.cornerRadius(12) .cornerRadius(12)
.padding(.top) .padding(.top)
@@ -56,7 +56,7 @@ let DEFAULT_SHOW_UNREAD_AND_FAVORITES = "showUnreadAndFavorites"
let DEFAULT_DEVICE_NAME_FOR_REMOTE_ACCESS = "deviceNameForRemoteAccess" let DEFAULT_DEVICE_NAME_FOR_REMOTE_ACCESS = "deviceNameForRemoteAccess"
let DEFAULT_CONFIRM_REMOTE_SESSIONS = "confirmRemoteSessions" let DEFAULT_CONFIRM_REMOTE_SESSIONS = "confirmRemoteSessions"
let DEFAULT_CONNECT_REMOTE_VIA_MULTICAST = "connectRemoteViaMulticast" let DEFAULT_CONNECT_REMOTE_VIA_MULTICAST = "connectRemoteViaMulticast"
let DEFAULT_CONNECT_REMOTE_VIA_MULTICAST_AUTO = "connectRemoteViaMulticastAuto" let DEFAULT_OFFER_REMOTE_MULTICAST = "offerRemoteMulticast"
let appDefaults: [String: Any] = [ let appDefaults: [String: Any] = [
DEFAULT_SHOW_LA_NOTICE: false, DEFAULT_SHOW_LA_NOTICE: false,
@@ -91,8 +91,8 @@ let appDefaults: [String: Any] = [
DEFAULT_CUSTOM_DISAPPEARING_MESSAGE_TIME: 300, DEFAULT_CUSTOM_DISAPPEARING_MESSAGE_TIME: 300,
DEFAULT_SHOW_UNREAD_AND_FAVORITES: false, DEFAULT_SHOW_UNREAD_AND_FAVORITES: false,
DEFAULT_CONFIRM_REMOTE_SESSIONS: false, DEFAULT_CONFIRM_REMOTE_SESSIONS: false,
DEFAULT_CONNECT_REMOTE_VIA_MULTICAST: true, DEFAULT_CONNECT_REMOTE_VIA_MULTICAST: false,
DEFAULT_CONNECT_REMOTE_VIA_MULTICAST_AUTO: true, DEFAULT_OFFER_REMOTE_MULTICAST: true
] ]
enum SimpleXLinkMode: String, Identifiable { enum SimpleXLinkMode: String, Identifiable {
@@ -190,8 +190,7 @@ struct UserAddressView: View {
@ViewBuilder private func existingAddressView(_ userAddress: UserContactLink) -> some View { @ViewBuilder private func existingAddressView(_ userAddress: UserContactLink) -> some View {
Section { Section {
SimpleXLinkQRCode(uri: userAddress.connReqContact) MutableQRCode(uri: Binding.constant(simplexChatLink(userAddress.connReqContact)))
.id("simplex-contact-address-qrcode-\(userAddress.connReqContact)")
shareQRCodeButton(userAddress) shareQRCodeButton(userAddress)
if MFMailComposeViewController.canSendMail() { if MFMailComposeViewController.canSendMail() {
shareViaEmailButton(userAddress) shareViaEmailButton(userAddress)
@@ -120,10 +120,8 @@ struct UserProfile: View {
} }
} }
.sheet(isPresented: $showImagePicker) { .sheet(isPresented: $showImagePicker) {
LibraryImagePicker(image: $chosenImage) { _ in LibraryImagePicker(image: $chosenImage) {
await MainActor.run { didSelectItem in showImagePicker = false
showImagePicker = false
}
} }
} }
.onChange(of: chosenImage) { image in .onChange(of: chosenImage) { image in
@@ -386,12 +386,6 @@
- и още!</target> - и още!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="- optionally notify deleted contacts.&#10;- profile names with spaces.&#10;- and more!" xml:space="preserve">
<source>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve"> <trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve">
<source>- voice messages up to 5 minutes. <source>- voice messages up to 5 minutes.
- custom time to disappear. - custom time to disappear.
@@ -886,10 +880,6 @@
<target>Лош хеш на съобщението</target> <target>Лош хеш на съобщението</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Better groups" xml:space="preserve">
<source>Better groups</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better messages" xml:space="preserve"> <trans-unit id="Better messages" xml:space="preserve">
<source>Better messages</source> <source>Better messages</source>
<target>По-добри съобщения</target> <target>По-добри съобщения</target>
@@ -899,10 +889,6 @@
<source>Block</source> <source>Block</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Block group members" xml:space="preserve">
<source>Block group members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Block member" xml:space="preserve"> <trans-unit id="Block member" xml:space="preserve">
<source>Block member</source> <source>Block member</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -1172,10 +1158,6 @@
<target>Свързване</target> <target>Свързване</target>
<note>server test step</note> <note>server test step</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect automatically" xml:space="preserve">
<source>Connect automatically</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect incognito" xml:space="preserve"> <trans-unit id="Connect incognito" xml:space="preserve">
<source>Connect incognito</source> <source>Connect incognito</source>
<target>Свързване инкогнито</target> <target>Свързване инкогнито</target>
@@ -1347,10 +1329,6 @@ This is your own one-time link!</source>
<target>Създай SimpleX адрес</target> <target>Създай SimpleX адрес</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create a group using a random profile." xml:space="preserve">
<source>Create a group using a random profile.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create an address to let people connect with you." xml:space="preserve"> <trans-unit id="Create an address to let people connect with you." xml:space="preserve">
<source>Create an address to let people connect with you.</source> <source>Create an address to let people connect with you.</source>
<target>Създайте адрес, за да позволите на хората да се свързват с вас.</target> <target>Създайте адрес, за да позволите на хората да се свързват с вас.</target>
@@ -1866,8 +1844,8 @@ This cannot be undone!</source>
<target>Открийте и се присъединете към групи</target> <target>Открийте и се присъединете към групи</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover via local network" xml:space="preserve"> <trans-unit id="Discover on network" xml:space="preserve">
<source>Discover via local network</source> <source>Discover on network</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve"> <trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
@@ -2404,10 +2382,6 @@ This cannot be undone!</source>
<target>Бързо и без чакане, докато подателят е онлайн!</target> <target>Бързо и без чакане, докато подателят е онлайн!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Faster joining and more reliable messages." xml:space="preserve">
<source>Faster joining and more reliable messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Favorite" xml:space="preserve"> <trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source> <source>Favorite</source>
<target>Любим</target> <target>Любим</target>
@@ -2503,10 +2477,6 @@ This cannot be undone!</source>
<target>За конзолата</target> <target>За конзолата</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="French interface" xml:space="preserve"> <trans-unit id="French interface" xml:space="preserve">
<source>French interface</source> <source>French interface</source>
<target>Френски интерфейс</target> <target>Френски интерфейс</target>
@@ -2829,10 +2799,6 @@ This cannot be undone!</source>
<target>Инкогнито</target> <target>Инкогнито</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incognito groups" xml:space="preserve">
<source>Incognito groups</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode" xml:space="preserve"> <trans-unit id="Incognito mode" xml:space="preserve">
<source>Incognito mode</source> <source>Incognito mode</source>
<target>Режим инкогнито</target> <target>Режим инкогнито</target>
@@ -3096,10 +3062,6 @@ This is your link for group %@!</source>
<target>Ограничения</target> <target>Ограничения</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Link mobile and desktop apps! 🔗" xml:space="preserve">
<source>Link mobile and desktop apps! 🔗</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Linked desktop options" xml:space="preserve"> <trans-unit id="Linked desktop options" xml:space="preserve">
<source>Linked desktop options</source> <source>Linked desktop options</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -3457,10 +3419,6 @@ This is your link for group %@!</source>
<target>Няма получени или изпратени файлове</target> <target>Няма получени или изпратени файлове</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Not compatible!" xml:space="preserve">
<source>Not compatible!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve"> <trans-unit id="Notifications" xml:space="preserve">
<source>Notifications</source> <source>Notifications</source>
<target>Известия</target> <target>Известия</target>
@@ -5035,10 +4993,6 @@ It can happen because of some bug or when the connection is compromised.</source
<target>За да се свърже, вашият контакт може да сканира QR код или да използва линка в приложението.</target> <target>За да се свърже, вашият контакт може да сканира QR код или да използва линка в приложението.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="To hide unwanted messages." xml:space="preserve">
<source>To hide unwanted messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To make a new connection" xml:space="preserve"> <trans-unit id="To make a new connection" xml:space="preserve">
<source>To make a new connection</source> <source>To make a new connection</source>
<target>За да направите нова връзка</target> <target>За да направите нова връзка</target>
@@ -5354,10 +5308,6 @@ To connect, please ask your contact to create another connection link and check
<target>Чрез браузър</target> <target>Чрез браузър</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Via secure quantum resistant protocol." xml:space="preserve">
<source>Via secure quantum resistant protocol.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Video call" xml:space="preserve"> <trans-unit id="Video call" xml:space="preserve">
<source>Video call</source> <source>Video call</source>
<target>Видео разговор</target> <target>Видео разговор</target>
@@ -5408,10 +5358,6 @@ To connect, please ask your contact to create another connection link and check
<target>Гласово съобщение…</target> <target>Гласово съобщение…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Waiting for desktop..." xml:space="preserve">
<source>Waiting for desktop...</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve"> <trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source> <source>Waiting for file</source>
<target>Изчаква се получаването на файла</target> <target>Изчаква се получаването на файла</target>
@@ -5942,10 +5888,6 @@ SimpleX сървърите не могат да видят вашия профи
<target>аудио разговор (не е e2e криптиран)</target> <target>аудио разговор (не е e2e криптиран)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="author" xml:space="preserve">
<source>author</source>
<note>member role</note>
</trans-unit>
<trans-unit id="bad message ID" xml:space="preserve"> <trans-unit id="bad message ID" xml:space="preserve">
<source>bad message ID</source> <source>bad message ID</source>
<target>лошо ID на съобщението</target> <target>лошо ID на съобщението</target>
@@ -386,12 +386,6 @@
- a více!</target> - a více!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="- optionally notify deleted contacts.&#10;- profile names with spaces.&#10;- and more!" xml:space="preserve">
<source>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve"> <trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve">
<source>- voice messages up to 5 minutes. <source>- voice messages up to 5 minutes.
- custom time to disappear. - custom time to disappear.
@@ -886,10 +880,6 @@
<target>Špatný hash zprávy</target> <target>Špatný hash zprávy</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Better groups" xml:space="preserve">
<source>Better groups</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better messages" xml:space="preserve"> <trans-unit id="Better messages" xml:space="preserve">
<source>Better messages</source> <source>Better messages</source>
<target>Lepší zprávy</target> <target>Lepší zprávy</target>
@@ -899,10 +889,6 @@
<source>Block</source> <source>Block</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Block group members" xml:space="preserve">
<source>Block group members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Block member" xml:space="preserve"> <trans-unit id="Block member" xml:space="preserve">
<source>Block member</source> <source>Block member</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -1172,10 +1158,6 @@
<target>Připojit</target> <target>Připojit</target>
<note>server test step</note> <note>server test step</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect automatically" xml:space="preserve">
<source>Connect automatically</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect incognito" xml:space="preserve"> <trans-unit id="Connect incognito" xml:space="preserve">
<source>Connect incognito</source> <source>Connect incognito</source>
<target>Spojit se inkognito</target> <target>Spojit se inkognito</target>
@@ -1347,10 +1329,6 @@ This is your own one-time link!</source>
<target>Vytvořit SimpleX adresu</target> <target>Vytvořit SimpleX adresu</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create a group using a random profile." xml:space="preserve">
<source>Create a group using a random profile.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create an address to let people connect with you." xml:space="preserve"> <trans-unit id="Create an address to let people connect with you." xml:space="preserve">
<source>Create an address to let people connect with you.</source> <source>Create an address to let people connect with you.</source>
<target>Vytvořit adresu, aby se s vámi lidé mohli spojit.</target> <target>Vytvořit adresu, aby se s vámi lidé mohli spojit.</target>
@@ -1866,8 +1844,8 @@ This cannot be undone!</source>
<target>Objevte a připojte skupiny</target> <target>Objevte a připojte skupiny</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover via local network" xml:space="preserve"> <trans-unit id="Discover on network" xml:space="preserve">
<source>Discover via local network</source> <source>Discover on network</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve"> <trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
@@ -2404,10 +2382,6 @@ This cannot be undone!</source>
<target>Rychle a bez čekání, než bude odesílatel online!</target> <target>Rychle a bez čekání, než bude odesílatel online!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Faster joining and more reliable messages." xml:space="preserve">
<source>Faster joining and more reliable messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Favorite" xml:space="preserve"> <trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source> <source>Favorite</source>
<target>Oblíbené</target> <target>Oblíbené</target>
@@ -2503,10 +2477,6 @@ This cannot be undone!</source>
<target>Pro konzoli</target> <target>Pro konzoli</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="French interface" xml:space="preserve"> <trans-unit id="French interface" xml:space="preserve">
<source>French interface</source> <source>French interface</source>
<target>Francouzské rozhraní</target> <target>Francouzské rozhraní</target>
@@ -2829,10 +2799,6 @@ This cannot be undone!</source>
<target>Inkognito</target> <target>Inkognito</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incognito groups" xml:space="preserve">
<source>Incognito groups</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode" xml:space="preserve"> <trans-unit id="Incognito mode" xml:space="preserve">
<source>Incognito mode</source> <source>Incognito mode</source>
<target>Režim inkognito</target> <target>Režim inkognito</target>
@@ -3096,10 +3062,6 @@ This is your link for group %@!</source>
<target>Omezení</target> <target>Omezení</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Link mobile and desktop apps! 🔗" xml:space="preserve">
<source>Link mobile and desktop apps! 🔗</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Linked desktop options" xml:space="preserve"> <trans-unit id="Linked desktop options" xml:space="preserve">
<source>Linked desktop options</source> <source>Linked desktop options</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -3457,10 +3419,6 @@ This is your link for group %@!</source>
<target>Žádné přijaté ani odeslané soubory</target> <target>Žádné přijaté ani odeslané soubory</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Not compatible!" xml:space="preserve">
<source>Not compatible!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve"> <trans-unit id="Notifications" xml:space="preserve">
<source>Notifications</source> <source>Notifications</source>
<target>Oznámení</target> <target>Oznámení</target>
@@ -5035,10 +4993,6 @@ Může se to stát kvůli nějaké chybě, nebo pokud je spojení kompromitován
<target>Pro připojení může váš kontakt naskenovat QR kód, nebo použít odkaz v aplikaci.</target> <target>Pro připojení může váš kontakt naskenovat QR kód, nebo použít odkaz v aplikaci.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="To hide unwanted messages." xml:space="preserve">
<source>To hide unwanted messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To make a new connection" xml:space="preserve"> <trans-unit id="To make a new connection" xml:space="preserve">
<source>To make a new connection</source> <source>To make a new connection</source>
<target>Vytvoření nového připojení</target> <target>Vytvoření nového připojení</target>
@@ -5354,10 +5308,6 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
<target>Prostřednictvím prohlížeče</target> <target>Prostřednictvím prohlížeče</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Via secure quantum resistant protocol." xml:space="preserve">
<source>Via secure quantum resistant protocol.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Video call" xml:space="preserve"> <trans-unit id="Video call" xml:space="preserve">
<source>Video call</source> <source>Video call</source>
<target>Videohovor</target> <target>Videohovor</target>
@@ -5408,10 +5358,6 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
<target>Hlasová zpráva…</target> <target>Hlasová zpráva…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Waiting for desktop..." xml:space="preserve">
<source>Waiting for desktop...</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve"> <trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source> <source>Waiting for file</source>
<target>Čekání na soubor</target> <target>Čekání na soubor</target>
@@ -5942,10 +5888,6 @@ Servery SimpleX nevidí váš profil.</target>
<target>zvukový hovor (nešifrovaný e2e)</target> <target>zvukový hovor (nešifrovaný e2e)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="author" xml:space="preserve">
<source>author</source>
<note>member role</note>
</trans-unit>
<trans-unit id="bad message ID" xml:space="preserve"> <trans-unit id="bad message ID" xml:space="preserve">
<source>bad message ID</source> <source>bad message ID</source>
<target>špatné ID zprávy</target> <target>špatné ID zprávy</target>
@@ -299,12 +299,10 @@
</trans-unit> </trans-unit>
<trans-unit id="(new)" xml:space="preserve"> <trans-unit id="(new)" xml:space="preserve">
<source>(new)</source> <source>(new)</source>
<target>(Neu)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="(this device v%@)" xml:space="preserve"> <trans-unit id="(this device v%@)" xml:space="preserve">
<source>(this device v%@)</source> <source>(this device v%@)</source>
<target>(Dieses Gerät hat v%@)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id=")" xml:space="preserve"> <trans-unit id=")" xml:space="preserve">
@@ -395,15 +393,6 @@
- und mehr!</target> - und mehr!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="- optionally notify deleted contacts.&#10;- profile names with spaces.&#10;- and more!" xml:space="preserve">
<source>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</source>
<target>- Optionale Benachrichtigung von gelöschten Kontakten.
- Profilnamen mit Leerzeichen.
- Und mehr!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve"> <trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve">
<source>- voice messages up to 5 minutes. <source>- voice messages up to 5 minutes.
- custom time to disappear. - custom time to disappear.
@@ -890,7 +879,6 @@
</trans-unit> </trans-unit>
<trans-unit id="Bad desktop address" xml:space="preserve"> <trans-unit id="Bad desktop address" xml:space="preserve">
<source>Bad desktop address</source> <source>Bad desktop address</source>
<target>Falsche Desktop-Adresse</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Bad message ID" xml:space="preserve"> <trans-unit id="Bad message ID" xml:space="preserve">
@@ -903,11 +891,6 @@
<target>Ungültiger Nachrichten-Hash</target> <target>Ungültiger Nachrichten-Hash</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Better groups" xml:space="preserve">
<source>Better groups</source>
<target>Bessere Gruppen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better messages" xml:space="preserve"> <trans-unit id="Better messages" xml:space="preserve">
<source>Better messages</source> <source>Better messages</source>
<target>Verbesserungen bei Nachrichten</target> <target>Verbesserungen bei Nachrichten</target>
@@ -918,11 +901,6 @@
<target>Blockieren</target> <target>Blockieren</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Block group members" xml:space="preserve">
<source>Block group members</source>
<target>Gruppenmitglieder blockieren</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Block member" xml:space="preserve"> <trans-unit id="Block member" xml:space="preserve">
<source>Block member</source> <source>Block member</source>
<target>Mitglied blockieren</target> <target>Mitglied blockieren</target>
@@ -1194,11 +1172,6 @@
<target>Verbinden</target> <target>Verbinden</target>
<note>server test step</note> <note>server test step</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect automatically" xml:space="preserve">
<source>Connect automatically</source>
<target>Automatisch verbinden</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect incognito" xml:space="preserve"> <trans-unit id="Connect incognito" xml:space="preserve">
<source>Connect incognito</source> <source>Connect incognito</source>
<target>Inkognito verbinden</target> <target>Inkognito verbinden</target>
@@ -1206,7 +1179,6 @@
</trans-unit> </trans-unit>
<trans-unit id="Connect to desktop" xml:space="preserve"> <trans-unit id="Connect to desktop" xml:space="preserve">
<source>Connect to desktop</source> <source>Connect to desktop</source>
<target>Mit dem Desktop verbinden</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect to yourself?" xml:space="preserve"> <trans-unit id="Connect to yourself?" xml:space="preserve">
@@ -1255,12 +1227,10 @@ Das ist Ihr eigener Einmal-Link!</target>
</trans-unit> </trans-unit>
<trans-unit id="Connected desktop" xml:space="preserve"> <trans-unit id="Connected desktop" xml:space="preserve">
<source>Connected desktop</source> <source>Connected desktop</source>
<target>Verbundener Desktop</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connected to desktop" xml:space="preserve"> <trans-unit id="Connected to desktop" xml:space="preserve">
<source>Connected to desktop</source> <source>Connected to desktop</source>
<target>Mit dem Desktop verbunden</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connecting server…" xml:space="preserve"> <trans-unit id="Connecting server…" xml:space="preserve">
@@ -1275,7 +1245,6 @@ Das ist Ihr eigener Einmal-Link!</target>
</trans-unit> </trans-unit>
<trans-unit id="Connecting to desktop" xml:space="preserve"> <trans-unit id="Connecting to desktop" xml:space="preserve">
<source>Connecting to desktop</source> <source>Connecting to desktop</source>
<target>Mit dem Desktop verbinden</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connection" xml:space="preserve"> <trans-unit id="Connection" xml:space="preserve">
@@ -1300,7 +1269,6 @@ Das ist Ihr eigener Einmal-Link!</target>
</trans-unit> </trans-unit>
<trans-unit id="Connection terminated" xml:space="preserve"> <trans-unit id="Connection terminated" xml:space="preserve">
<source>Connection terminated</source> <source>Connection terminated</source>
<target>Verbindung beendet</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connection timeout" xml:space="preserve"> <trans-unit id="Connection timeout" xml:space="preserve">
@@ -1383,11 +1351,6 @@ Das ist Ihr eigener Einmal-Link!</target>
<target>SimpleX-Adresse erstellen</target> <target>SimpleX-Adresse erstellen</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create a group using a random profile." xml:space="preserve">
<source>Create a group using a random profile.</source>
<target>Erstellen Sie eine Gruppe mit einem zufälligen Profil.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create an address to let people connect with you." xml:space="preserve"> <trans-unit id="Create an address to let people connect with you." xml:space="preserve">
<source>Create an address to let people connect with you.</source> <source>Create an address to let people connect with you.</source>
<target>Erstellen Sie eine Adresse, damit sich Personen mit Ihnen verbinden können.</target> <target>Erstellen Sie eine Adresse, damit sich Personen mit Ihnen verbinden können.</target>
@@ -1800,17 +1763,14 @@ Das kann nicht rückgängig gemacht werden!</target>
</trans-unit> </trans-unit>
<trans-unit id="Desktop address" xml:space="preserve"> <trans-unit id="Desktop address" xml:space="preserve">
<source>Desktop address</source> <source>Desktop address</source>
<target>Desktop-Adresse</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Desktop app version %@ is not compatible with this app." xml:space="preserve"> <trans-unit id="Desktop app version %@ is not compatible with this app." xml:space="preserve">
<source>Desktop app version %@ is not compatible with this app.</source> <source>Desktop app version %@ is not compatible with this app.</source>
<target>Desktop App-Version %@ ist mit dieser App nicht kompatibel.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Desktop devices" xml:space="preserve"> <trans-unit id="Desktop devices" xml:space="preserve">
<source>Desktop devices</source> <source>Desktop devices</source>
<target>Desktop-Geräte</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Develop" xml:space="preserve"> <trans-unit id="Develop" xml:space="preserve">
@@ -1905,7 +1865,6 @@ Das kann nicht rückgängig gemacht werden!</target>
</trans-unit> </trans-unit>
<trans-unit id="Disconnect desktop?" xml:space="preserve"> <trans-unit id="Disconnect desktop?" xml:space="preserve">
<source>Disconnect desktop?</source> <source>Disconnect desktop?</source>
<target>Desktop-Verbindung trennen?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover and join groups" xml:space="preserve"> <trans-unit id="Discover and join groups" xml:space="preserve">
@@ -1913,9 +1872,8 @@ Das kann nicht rückgängig gemacht werden!</target>
<target>Gruppen entdecken und ihnen beitreten</target> <target>Gruppen entdecken und ihnen beitreten</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover via local network" xml:space="preserve"> <trans-unit id="Discover on network" xml:space="preserve">
<source>Discover via local network</source> <source>Discover on network</source>
<target>Lokales Netzwerk durchsuchen</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve"> <trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
@@ -2090,12 +2048,10 @@ Das kann nicht rückgängig gemacht werden!</target>
</trans-unit> </trans-unit>
<trans-unit id="Encryption re-negotiation error" xml:space="preserve"> <trans-unit id="Encryption re-negotiation error" xml:space="preserve">
<source>Encryption re-negotiation error</source> <source>Encryption re-negotiation error</source>
<target>Fehler bei der Neuverhandlung der Verschlüsselung</target>
<note>message decrypt error item</note> <note>message decrypt error item</note>
</trans-unit> </trans-unit>
<trans-unit id="Encryption re-negotiation failed." xml:space="preserve"> <trans-unit id="Encryption re-negotiation failed." xml:space="preserve">
<source>Encryption re-negotiation failed.</source> <source>Encryption re-negotiation failed.</source>
<target>Neuverhandlung der Verschlüsselung fehlgeschlagen.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Enter Passcode" xml:space="preserve"> <trans-unit id="Enter Passcode" xml:space="preserve">
@@ -2130,7 +2086,6 @@ Das kann nicht rückgängig gemacht werden!</target>
</trans-unit> </trans-unit>
<trans-unit id="Enter this device name…" xml:space="preserve"> <trans-unit id="Enter this device name…" xml:space="preserve">
<source>Enter this device name…</source> <source>Enter this device name…</source>
<target>Geben Sie diesen Gerätenamen ein…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Enter welcome message…" xml:space="preserve"> <trans-unit id="Enter welcome message…" xml:space="preserve">
@@ -2458,11 +2413,6 @@ Das kann nicht rückgängig gemacht werden!</target>
<target>Schnell und ohne warten auf den Absender, bis er online ist!</target> <target>Schnell und ohne warten auf den Absender, bis er online ist!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Faster joining and more reliable messages." xml:space="preserve">
<source>Faster joining and more reliable messages.</source>
<target>Schnellerer Gruppenbeitritt und zuverlässigere Nachrichtenzustellung.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Favorite" xml:space="preserve"> <trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source> <source>Favorite</source>
<target>Favorit</target> <target>Favorit</target>
@@ -2558,11 +2508,6 @@ Das kann nicht rückgängig gemacht werden!</target>
<target>Für Konsole</target> <target>Für Konsole</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<target>Gefundener Desktop</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="French interface" xml:space="preserve"> <trans-unit id="French interface" xml:space="preserve">
<source>French interface</source> <source>French interface</source>
<target>Französische Bedienoberfläche</target> <target>Französische Bedienoberfläche</target>
@@ -2888,11 +2833,6 @@ Das kann nicht rückgängig gemacht werden!</target>
<target>Inkognito</target> <target>Inkognito</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incognito groups" xml:space="preserve">
<source>Incognito groups</source>
<target>Inkognito-Gruppen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode" xml:space="preserve"> <trans-unit id="Incognito mode" xml:space="preserve">
<source>Incognito mode</source> <source>Incognito mode</source>
<target>Inkognito-Modus</target> <target>Inkognito-Modus</target>
@@ -2925,7 +2865,6 @@ Das kann nicht rückgängig gemacht werden!</target>
</trans-unit> </trans-unit>
<trans-unit id="Incompatible version" xml:space="preserve"> <trans-unit id="Incompatible version" xml:space="preserve">
<source>Incompatible version</source> <source>Incompatible version</source>
<target>Inkompatible Version</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incorrect passcode" xml:space="preserve"> <trans-unit id="Incorrect passcode" xml:space="preserve">
@@ -3100,7 +3039,6 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit> </trans-unit>
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve"> <trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
<source>Keep the app open to use it from desktop</source> <source>Keep the app open to use it from desktop</source>
<target>Die App muss geöffnet bleiben, um sie vom Desktop aus nutzen zu können</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Keep your connections" xml:space="preserve"> <trans-unit id="Keep your connections" xml:space="preserve">
@@ -3163,19 +3101,12 @@ Das ist Ihr Link für die Gruppe %@!</target>
<target>Einschränkungen</target> <target>Einschränkungen</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Link mobile and desktop apps! 🔗" xml:space="preserve">
<source>Link mobile and desktop apps! 🔗</source>
<target>Verknüpfe Mobiltelefon- und Desktop-Apps! 🔗</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Linked desktop options" xml:space="preserve"> <trans-unit id="Linked desktop options" xml:space="preserve">
<source>Linked desktop options</source> <source>Linked desktop options</source>
<target>Verknüpfte Desktop-Optionen</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Linked desktops" xml:space="preserve"> <trans-unit id="Linked desktops" xml:space="preserve">
<source>Linked desktops</source> <source>Linked desktops</source>
<target>Verknüpfte Desktops</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Live message!" xml:space="preserve"> <trans-unit id="Live message!" xml:space="preserve">
@@ -3528,11 +3459,6 @@ Das ist Ihr Link für die Gruppe %@!</target>
<target>Keine empfangenen oder gesendeten Dateien</target> <target>Keine empfangenen oder gesendeten Dateien</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Not compatible!" xml:space="preserve">
<source>Not compatible!</source>
<target>Nicht kompatibel!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve"> <trans-unit id="Notifications" xml:space="preserve">
<source>Notifications</source> <source>Notifications</source>
<target>Benachrichtigungen</target> <target>Benachrichtigungen</target>
@@ -3754,7 +3680,6 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit> </trans-unit>
<trans-unit id="Paste desktop address" xml:space="preserve"> <trans-unit id="Paste desktop address" xml:space="preserve">
<source>Paste desktop address</source> <source>Paste desktop address</source>
<target>Desktop-Adresse einfügen</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Paste image" xml:space="preserve"> <trans-unit id="Paste image" xml:space="preserve">
@@ -4354,7 +4279,6 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit> </trans-unit>
<trans-unit id="Scan QR code from desktop" xml:space="preserve"> <trans-unit id="Scan QR code from desktop" xml:space="preserve">
<source>Scan QR code from desktop</source> <source>Scan QR code from desktop</source>
<target>Den QR-Code vom Desktop scannen</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Scan code" xml:space="preserve"> <trans-unit id="Scan code" xml:space="preserve">
@@ -4579,7 +4503,6 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit> </trans-unit>
<trans-unit id="Session code" xml:space="preserve"> <trans-unit id="Session code" xml:space="preserve">
<source>Session code</source> <source>Session code</source>
<target>Sitzungscode</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Set 1 day" xml:space="preserve"> <trans-unit id="Set 1 day" xml:space="preserve">
@@ -5081,7 +5004,6 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro
</trans-unit> </trans-unit>
<trans-unit id="This device name" xml:space="preserve"> <trans-unit id="This device name" xml:space="preserve">
<source>This device name</source> <source>This device name</source>
<target>Dieser Gerätename</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="This group has over %lld members, delivery receipts are not sent." xml:space="preserve"> <trans-unit id="This group has over %lld members, delivery receipts are not sent." xml:space="preserve">
@@ -5119,11 +5041,6 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro
<target>Um eine Verbindung herzustellen, kann Ihr Kontakt den QR-Code scannen oder den Link in der App verwenden.</target> <target>Um eine Verbindung herzustellen, kann Ihr Kontakt den QR-Code scannen oder den Link in der App verwenden.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="To hide unwanted messages." xml:space="preserve">
<source>To hide unwanted messages.</source>
<target>Um unerwünschte Nachrichten zu verbergen.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To make a new connection" xml:space="preserve"> <trans-unit id="To make a new connection" xml:space="preserve">
<source>To make a new connection</source> <source>To make a new connection</source>
<target>Um eine Verbindung mit einem neuen Kontakt zu erstellen</target> <target>Um eine Verbindung mit einem neuen Kontakt zu erstellen</target>
@@ -5285,12 +5202,10 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
</trans-unit> </trans-unit>
<trans-unit id="Unlink" xml:space="preserve"> <trans-unit id="Unlink" xml:space="preserve">
<source>Unlink</source> <source>Unlink</source>
<target>Entkoppeln</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Unlink desktop?" xml:space="preserve"> <trans-unit id="Unlink desktop?" xml:space="preserve">
<source>Unlink desktop?</source> <source>Unlink desktop?</source>
<target>Desktop entkoppeln?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Unlock" xml:space="preserve"> <trans-unit id="Unlock" xml:space="preserve">
@@ -5385,7 +5300,6 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
</trans-unit> </trans-unit>
<trans-unit id="Use from desktop" xml:space="preserve"> <trans-unit id="Use from desktop" xml:space="preserve">
<source>Use from desktop</source> <source>Use from desktop</source>
<target>Vom Desktop aus nutzen</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Use iOS call interface" xml:space="preserve"> <trans-unit id="Use iOS call interface" xml:space="preserve">
@@ -5420,12 +5334,10 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
</trans-unit> </trans-unit>
<trans-unit id="Verify code with desktop" xml:space="preserve"> <trans-unit id="Verify code with desktop" xml:space="preserve">
<source>Verify code with desktop</source> <source>Verify code with desktop</source>
<target>Code mit dem Desktop überprüfen</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Verify connection" xml:space="preserve"> <trans-unit id="Verify connection" xml:space="preserve">
<source>Verify connection</source> <source>Verify connection</source>
<target>Verbindung überprüfen</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Verify connection security" xml:space="preserve"> <trans-unit id="Verify connection security" xml:space="preserve">
@@ -5435,7 +5347,6 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
</trans-unit> </trans-unit>
<trans-unit id="Verify connections" xml:space="preserve"> <trans-unit id="Verify connections" xml:space="preserve">
<source>Verify connections</source> <source>Verify connections</source>
<target>Verbindungen überprüfen</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Verify security code" xml:space="preserve"> <trans-unit id="Verify security code" xml:space="preserve">
@@ -5448,11 +5359,6 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
<target>Über den Browser</target> <target>Über den Browser</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Via secure quantum resistant protocol." xml:space="preserve">
<source>Via secure quantum resistant protocol.</source>
<target>Über ein sicheres quantenbeständiges Protokoll.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Video call" xml:space="preserve"> <trans-unit id="Video call" xml:space="preserve">
<source>Video call</source> <source>Video call</source>
<target>Videoanruf</target> <target>Videoanruf</target>
@@ -5503,11 +5409,6 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
<target>Sprachnachrichten…</target> <target>Sprachnachrichten…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Waiting for desktop..." xml:space="preserve">
<source>Waiting for desktop...</source>
<target>Es wird auf den Desktop gewartet...</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve"> <trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source> <source>Waiting for file</source>
<target>Warte auf Datei</target> <target>Warte auf Datei</target>
@@ -6053,11 +5954,6 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
<target>Audioanruf (nicht E2E verschlüsselt)</target> <target>Audioanruf (nicht E2E verschlüsselt)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="author" xml:space="preserve">
<source>author</source>
<target>Autor</target>
<note>member role</note>
</trans-unit>
<trans-unit id="bad message ID" xml:space="preserve"> <trans-unit id="bad message ID" xml:space="preserve">
<source>bad message ID</source> <source>bad message ID</source>
<target>Ungültige Nachrichten-ID</target> <target>Ungültige Nachrichten-ID</target>
@@ -6642,7 +6538,6 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
</trans-unit> </trans-unit>
<trans-unit id="v%@" xml:space="preserve"> <trans-unit id="v%@" xml:space="preserve">
<source>v%@</source> <source>v%@</source>
<target>v%@</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="v%@ (%@)" xml:space="preserve"> <trans-unit id="v%@ (%@)" xml:space="preserve">
@@ -6784,7 +6679,6 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
</trans-unit> </trans-unit>
<trans-unit id="NSLocalNetworkUsageDescription" xml:space="preserve"> <trans-unit id="NSLocalNetworkUsageDescription" xml:space="preserve">
<source>SimpleX uses local network access to allow using user chat profile via desktop app on the same network.</source> <source>SimpleX uses local network access to allow using user chat profile via desktop app on the same network.</source>
<target>SimpleX nutzt den lokalen Netzwerkzugriff, um die Nutzung von Benutzer-Chatprofilen über eine Desktop-App im gleichen Netzwerk zu erlauben.</target>
<note>Privacy - Local Network Usage Description</note> <note>Privacy - Local Network Usage Description</note>
</trans-unit> </trans-unit>
<trans-unit id="NSMicrophoneUsageDescription" xml:space="preserve"> <trans-unit id="NSMicrophoneUsageDescription" xml:space="preserve">
@@ -392,15 +392,6 @@
- and more!</source> - and more!</source>
<target>- more stable message delivery. <target>- more stable message delivery.
- a bit better groups. - a bit better groups.
- and more!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="- optionally notify deleted contacts.&#10;- profile names with spaces.&#10;- and more!" xml:space="preserve">
<source>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</source>
<target>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</target> - and more!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
@@ -903,11 +894,6 @@
<target>Bad message hash</target> <target>Bad message hash</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Better groups" xml:space="preserve">
<source>Better groups</source>
<target>Better groups</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better messages" xml:space="preserve"> <trans-unit id="Better messages" xml:space="preserve">
<source>Better messages</source> <source>Better messages</source>
<target>Better messages</target> <target>Better messages</target>
@@ -918,11 +904,6 @@
<target>Block</target> <target>Block</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Block group members" xml:space="preserve">
<source>Block group members</source>
<target>Block group members</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Block member" xml:space="preserve"> <trans-unit id="Block member" xml:space="preserve">
<source>Block member</source> <source>Block member</source>
<target>Block member</target> <target>Block member</target>
@@ -1194,11 +1175,6 @@
<target>Connect</target> <target>Connect</target>
<note>server test step</note> <note>server test step</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect automatically" xml:space="preserve">
<source>Connect automatically</source>
<target>Connect automatically</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect incognito" xml:space="preserve"> <trans-unit id="Connect incognito" xml:space="preserve">
<source>Connect incognito</source> <source>Connect incognito</source>
<target>Connect incognito</target> <target>Connect incognito</target>
@@ -1383,11 +1359,6 @@ This is your own one-time link!</target>
<target>Create SimpleX address</target> <target>Create SimpleX address</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create a group using a random profile." xml:space="preserve">
<source>Create a group using a random profile.</source>
<target>Create a group using a random profile.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create an address to let people connect with you." xml:space="preserve"> <trans-unit id="Create an address to let people connect with you." xml:space="preserve">
<source>Create an address to let people connect with you.</source> <source>Create an address to let people connect with you.</source>
<target>Create an address to let people connect with you.</target> <target>Create an address to let people connect with you.</target>
@@ -1913,9 +1884,9 @@ This cannot be undone!</target>
<target>Discover and join groups</target> <target>Discover and join groups</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover via local network" xml:space="preserve"> <trans-unit id="Discover on network" xml:space="preserve">
<source>Discover via local network</source> <source>Discover on network</source>
<target>Discover via local network</target> <target>Discover on network</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve"> <trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
@@ -2458,11 +2429,6 @@ This cannot be undone!</target>
<target>Fast and no wait until the sender is online!</target> <target>Fast and no wait until the sender is online!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Faster joining and more reliable messages." xml:space="preserve">
<source>Faster joining and more reliable messages.</source>
<target>Faster joining and more reliable messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Favorite" xml:space="preserve"> <trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source> <source>Favorite</source>
<target>Favorite</target> <target>Favorite</target>
@@ -2558,11 +2524,6 @@ This cannot be undone!</target>
<target>For console</target> <target>For console</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<target>Found desktop</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="French interface" xml:space="preserve"> <trans-unit id="French interface" xml:space="preserve">
<source>French interface</source> <source>French interface</source>
<target>French interface</target> <target>French interface</target>
@@ -2888,11 +2849,6 @@ This cannot be undone!</target>
<target>Incognito</target> <target>Incognito</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incognito groups" xml:space="preserve">
<source>Incognito groups</source>
<target>Incognito groups</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode" xml:space="preserve"> <trans-unit id="Incognito mode" xml:space="preserve">
<source>Incognito mode</source> <source>Incognito mode</source>
<target>Incognito mode</target> <target>Incognito mode</target>
@@ -3163,11 +3119,6 @@ This is your link for group %@!</target>
<target>Limitations</target> <target>Limitations</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Link mobile and desktop apps! 🔗" xml:space="preserve">
<source>Link mobile and desktop apps! 🔗</source>
<target>Link mobile and desktop apps! 🔗</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Linked desktop options" xml:space="preserve"> <trans-unit id="Linked desktop options" xml:space="preserve">
<source>Linked desktop options</source> <source>Linked desktop options</source>
<target>Linked desktop options</target> <target>Linked desktop options</target>
@@ -3528,11 +3479,6 @@ This is your link for group %@!</target>
<target>No received or sent files</target> <target>No received or sent files</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Not compatible!" xml:space="preserve">
<source>Not compatible!</source>
<target>Not compatible!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve"> <trans-unit id="Notifications" xml:space="preserve">
<source>Notifications</source> <source>Notifications</source>
<target>Notifications</target> <target>Notifications</target>
@@ -5119,11 +5065,6 @@ It can happen because of some bug or when the connection is compromised.</target
<target>To connect, your contact can scan QR code or use the link in the app.</target> <target>To connect, your contact can scan QR code or use the link in the app.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="To hide unwanted messages." xml:space="preserve">
<source>To hide unwanted messages.</source>
<target>To hide unwanted messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To make a new connection" xml:space="preserve"> <trans-unit id="To make a new connection" xml:space="preserve">
<source>To make a new connection</source> <source>To make a new connection</source>
<target>To make a new connection</target> <target>To make a new connection</target>
@@ -5448,11 +5389,6 @@ To connect, please ask your contact to create another connection link and check
<target>Via browser</target> <target>Via browser</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Via secure quantum resistant protocol." xml:space="preserve">
<source>Via secure quantum resistant protocol.</source>
<target>Via secure quantum resistant protocol.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Video call" xml:space="preserve"> <trans-unit id="Video call" xml:space="preserve">
<source>Video call</source> <source>Video call</source>
<target>Video call</target> <target>Video call</target>
@@ -5503,11 +5439,6 @@ To connect, please ask your contact to create another connection link and check
<target>Voice message…</target> <target>Voice message…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Waiting for desktop..." xml:space="preserve">
<source>Waiting for desktop...</source>
<target>Waiting for desktop...</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve"> <trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source> <source>Waiting for file</source>
<target>Waiting for file</target> <target>Waiting for file</target>
@@ -6053,11 +5984,6 @@ SimpleX servers cannot see your profile.</target>
<target>audio call (not e2e encrypted)</target> <target>audio call (not e2e encrypted)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="author" xml:space="preserve">
<source>author</source>
<target>author</target>
<note>member role</note>
</trans-unit>
<trans-unit id="bad message ID" xml:space="preserve"> <trans-unit id="bad message ID" xml:space="preserve">
<source>bad message ID</source> <source>bad message ID</source>
<target>bad message ID</target> <target>bad message ID</target>
@@ -386,12 +386,6 @@
- ¡y más!</target> - ¡y más!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="- optionally notify deleted contacts.&#10;- profile names with spaces.&#10;- and more!" xml:space="preserve">
<source>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve"> <trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve">
<source>- voice messages up to 5 minutes. <source>- voice messages up to 5 minutes.
- custom time to disappear. - custom time to disappear.
@@ -886,10 +880,6 @@
<target>Hash de mensaje incorrecto</target> <target>Hash de mensaje incorrecto</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Better groups" xml:space="preserve">
<source>Better groups</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better messages" xml:space="preserve"> <trans-unit id="Better messages" xml:space="preserve">
<source>Better messages</source> <source>Better messages</source>
<target>Mensajes mejorados</target> <target>Mensajes mejorados</target>
@@ -899,10 +889,6 @@
<source>Block</source> <source>Block</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Block group members" xml:space="preserve">
<source>Block group members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Block member" xml:space="preserve"> <trans-unit id="Block member" xml:space="preserve">
<source>Block member</source> <source>Block member</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -1172,10 +1158,6 @@
<target>Conectar</target> <target>Conectar</target>
<note>server test step</note> <note>server test step</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect automatically" xml:space="preserve">
<source>Connect automatically</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect incognito" xml:space="preserve"> <trans-unit id="Connect incognito" xml:space="preserve">
<source>Connect incognito</source> <source>Connect incognito</source>
<target>Conectar incognito</target> <target>Conectar incognito</target>
@@ -1347,10 +1329,6 @@ This is your own one-time link!</source>
<target>Crear tu dirección SimpleX</target> <target>Crear tu dirección SimpleX</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create a group using a random profile." xml:space="preserve">
<source>Create a group using a random profile.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create an address to let people connect with you." xml:space="preserve"> <trans-unit id="Create an address to let people connect with you." xml:space="preserve">
<source>Create an address to let people connect with you.</source> <source>Create an address to let people connect with you.</source>
<target>Crea una dirección para que otras personas puedan conectar contigo.</target> <target>Crea una dirección para que otras personas puedan conectar contigo.</target>
@@ -1866,8 +1844,8 @@ This cannot be undone!</source>
<target>Descubre y únete a grupos</target> <target>Descubre y únete a grupos</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover via local network" xml:space="preserve"> <trans-unit id="Discover on network" xml:space="preserve">
<source>Discover via local network</source> <source>Discover on network</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve"> <trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
@@ -2404,10 +2382,6 @@ This cannot be undone!</source>
<target>¡Rápido y sin necesidad de esperar a que el remitente esté en línea!</target> <target>¡Rápido y sin necesidad de esperar a que el remitente esté en línea!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Faster joining and more reliable messages." xml:space="preserve">
<source>Faster joining and more reliable messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Favorite" xml:space="preserve"> <trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source> <source>Favorite</source>
<target>Favoritos</target> <target>Favoritos</target>
@@ -2503,10 +2477,6 @@ This cannot be undone!</source>
<target>Para consola</target> <target>Para consola</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="French interface" xml:space="preserve"> <trans-unit id="French interface" xml:space="preserve">
<source>French interface</source> <source>French interface</source>
<target>Interfaz en francés</target> <target>Interfaz en francés</target>
@@ -2829,10 +2799,6 @@ This cannot be undone!</source>
<target>Incógnito</target> <target>Incógnito</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incognito groups" xml:space="preserve">
<source>Incognito groups</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode" xml:space="preserve"> <trans-unit id="Incognito mode" xml:space="preserve">
<source>Incognito mode</source> <source>Incognito mode</source>
<target>Modo incógnito</target> <target>Modo incógnito</target>
@@ -3096,10 +3062,6 @@ This is your link for group %@!</source>
<target>Limitaciones</target> <target>Limitaciones</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Link mobile and desktop apps! 🔗" xml:space="preserve">
<source>Link mobile and desktop apps! 🔗</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Linked desktop options" xml:space="preserve"> <trans-unit id="Linked desktop options" xml:space="preserve">
<source>Linked desktop options</source> <source>Linked desktop options</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -3457,10 +3419,6 @@ This is your link for group %@!</source>
<target>Sin archivos recibidos o enviados</target> <target>Sin archivos recibidos o enviados</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Not compatible!" xml:space="preserve">
<source>Not compatible!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve"> <trans-unit id="Notifications" xml:space="preserve">
<source>Notifications</source> <source>Notifications</source>
<target>Notificaciones</target> <target>Notificaciones</target>
@@ -5035,10 +4993,6 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida.</target>
<target>Para conectarse, tu contacto puede escanear el código QR o usar el enlace en la aplicación.</target> <target>Para conectarse, tu contacto puede escanear el código QR o usar el enlace en la aplicación.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="To hide unwanted messages." xml:space="preserve">
<source>To hide unwanted messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To make a new connection" xml:space="preserve"> <trans-unit id="To make a new connection" xml:space="preserve">
<source>To make a new connection</source> <source>To make a new connection</source>
<target>Para hacer una conexión nueva</target> <target>Para hacer una conexión nueva</target>
@@ -5355,10 +5309,6 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb
<target>Mediante navegador</target> <target>Mediante navegador</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Via secure quantum resistant protocol." xml:space="preserve">
<source>Via secure quantum resistant protocol.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Video call" xml:space="preserve"> <trans-unit id="Video call" xml:space="preserve">
<source>Video call</source> <source>Video call</source>
<target>Videollamada</target> <target>Videollamada</target>
@@ -5409,10 +5359,6 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb
<target>Mensaje de voz…</target> <target>Mensaje de voz…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Waiting for desktop..." xml:space="preserve">
<source>Waiting for desktop...</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve"> <trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source> <source>Waiting for file</source>
<target>Esperando archivo</target> <target>Esperando archivo</target>
@@ -5943,10 +5889,6 @@ Los servidores de SimpleX no pueden ver tu perfil.</target>
<target>llamada (sin cifrar)</target> <target>llamada (sin cifrar)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="author" xml:space="preserve">
<source>author</source>
<note>member role</note>
</trans-unit>
<trans-unit id="bad message ID" xml:space="preserve"> <trans-unit id="bad message ID" xml:space="preserve">
<source>bad message ID</source> <source>bad message ID</source>
<target>ID de mensaje erróneo</target> <target>ID de mensaje erróneo</target>
@@ -383,12 +383,6 @@
- ja paljon muuta!</target> - ja paljon muuta!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="- optionally notify deleted contacts.&#10;- profile names with spaces.&#10;- and more!" xml:space="preserve">
<source>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve"> <trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve">
<source>- voice messages up to 5 minutes. <source>- voice messages up to 5 minutes.
- custom time to disappear. - custom time to disappear.
@@ -882,10 +876,6 @@
<target>Virheellinen viestin tarkiste</target> <target>Virheellinen viestin tarkiste</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Better groups" xml:space="preserve">
<source>Better groups</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better messages" xml:space="preserve"> <trans-unit id="Better messages" xml:space="preserve">
<source>Better messages</source> <source>Better messages</source>
<target>Parempia viestejä</target> <target>Parempia viestejä</target>
@@ -895,10 +885,6 @@
<source>Block</source> <source>Block</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Block group members" xml:space="preserve">
<source>Block group members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Block member" xml:space="preserve"> <trans-unit id="Block member" xml:space="preserve">
<source>Block member</source> <source>Block member</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -1167,10 +1153,6 @@
<target>Yhdistä</target> <target>Yhdistä</target>
<note>server test step</note> <note>server test step</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect automatically" xml:space="preserve">
<source>Connect automatically</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect incognito" xml:space="preserve"> <trans-unit id="Connect incognito" xml:space="preserve">
<source>Connect incognito</source> <source>Connect incognito</source>
<target>Yhdistä Incognito</target> <target>Yhdistä Incognito</target>
@@ -1342,10 +1324,6 @@ This is your own one-time link!</source>
<target>Luo SimpleX-osoite</target> <target>Luo SimpleX-osoite</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create a group using a random profile." xml:space="preserve">
<source>Create a group using a random profile.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create an address to let people connect with you." xml:space="preserve"> <trans-unit id="Create an address to let people connect with you." xml:space="preserve">
<source>Create an address to let people connect with you.</source> <source>Create an address to let people connect with you.</source>
<target>Luo osoite, jolla ihmiset voivat ottaa sinuun yhteyttä.</target> <target>Luo osoite, jolla ihmiset voivat ottaa sinuun yhteyttä.</target>
@@ -1861,8 +1839,8 @@ This cannot be undone!</source>
<target>Löydä ryhmiä ja liity niihin</target> <target>Löydä ryhmiä ja liity niihin</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover via local network" xml:space="preserve"> <trans-unit id="Discover on network" xml:space="preserve">
<source>Discover via local network</source> <source>Discover on network</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve"> <trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
@@ -2396,10 +2374,6 @@ This cannot be undone!</source>
<target>Nopea ja ei odotusta, kunnes lähettäjä on online-tilassa!</target> <target>Nopea ja ei odotusta, kunnes lähettäjä on online-tilassa!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Faster joining and more reliable messages." xml:space="preserve">
<source>Faster joining and more reliable messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Favorite" xml:space="preserve"> <trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source> <source>Favorite</source>
<target>Suosikki</target> <target>Suosikki</target>
@@ -2495,10 +2469,6 @@ This cannot be undone!</source>
<target>Konsoliin</target> <target>Konsoliin</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="French interface" xml:space="preserve"> <trans-unit id="French interface" xml:space="preserve">
<source>French interface</source> <source>French interface</source>
<target>Ranskalainen käyttöliittymä</target> <target>Ranskalainen käyttöliittymä</target>
@@ -2821,10 +2791,6 @@ This cannot be undone!</source>
<target>Incognito</target> <target>Incognito</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incognito groups" xml:space="preserve">
<source>Incognito groups</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode" xml:space="preserve"> <trans-unit id="Incognito mode" xml:space="preserve">
<source>Incognito mode</source> <source>Incognito mode</source>
<target>Incognito-tila</target> <target>Incognito-tila</target>
@@ -3088,10 +3054,6 @@ This is your link for group %@!</source>
<target>Rajoitukset</target> <target>Rajoitukset</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Link mobile and desktop apps! 🔗" xml:space="preserve">
<source>Link mobile and desktop apps! 🔗</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Linked desktop options" xml:space="preserve"> <trans-unit id="Linked desktop options" xml:space="preserve">
<source>Linked desktop options</source> <source>Linked desktop options</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -3448,10 +3410,6 @@ This is your link for group %@!</source>
<target>Ei vastaanotettuja tai lähetettyjä tiedostoja</target> <target>Ei vastaanotettuja tai lähetettyjä tiedostoja</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Not compatible!" xml:space="preserve">
<source>Not compatible!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve"> <trans-unit id="Notifications" xml:space="preserve">
<source>Notifications</source> <source>Notifications</source>
<target>Ilmoitukset</target> <target>Ilmoitukset</target>
@@ -5023,10 +4981,6 @@ Tämä voi johtua jostain virheestä tai siitä, että yhteys on vaarantunut.</t
<target>Kontaktisi voi muodostaa yhteyden skannaamalla QR-koodin tai käyttämällä sovelluksessa olevaa linkkiä.</target> <target>Kontaktisi voi muodostaa yhteyden skannaamalla QR-koodin tai käyttämällä sovelluksessa olevaa linkkiä.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="To hide unwanted messages." xml:space="preserve">
<source>To hide unwanted messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To make a new connection" xml:space="preserve"> <trans-unit id="To make a new connection" xml:space="preserve">
<source>To make a new connection</source> <source>To make a new connection</source>
<target>Uuden yhteyden luominen</target> <target>Uuden yhteyden luominen</target>
@@ -5341,10 +5295,6 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
<target>Selaimella</target> <target>Selaimella</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Via secure quantum resistant protocol." xml:space="preserve">
<source>Via secure quantum resistant protocol.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Video call" xml:space="preserve"> <trans-unit id="Video call" xml:space="preserve">
<source>Video call</source> <source>Video call</source>
<target>Videopuhelu</target> <target>Videopuhelu</target>
@@ -5395,10 +5345,6 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
<target>Ääniviesti…</target> <target>Ääniviesti…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Waiting for desktop..." xml:space="preserve">
<source>Waiting for desktop...</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve"> <trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source> <source>Waiting for file</source>
<target>Odottaa tiedostoa</target> <target>Odottaa tiedostoa</target>
@@ -5929,10 +5875,6 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>äänipuhelu (ei e2e-salattu)</target> <target>äänipuhelu (ei e2e-salattu)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="author" xml:space="preserve">
<source>author</source>
<note>member role</note>
</trans-unit>
<trans-unit id="bad message ID" xml:space="preserve"> <trans-unit id="bad message ID" xml:space="preserve">
<source>bad message ID</source> <source>bad message ID</source>
<target>virheellinen viestin tunniste</target> <target>virheellinen viestin tunniste</target>
@@ -299,12 +299,10 @@
</trans-unit> </trans-unit>
<trans-unit id="(new)" xml:space="preserve"> <trans-unit id="(new)" xml:space="preserve">
<source>(new)</source> <source>(new)</source>
<target>(nouveau)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="(this device v%@)" xml:space="preserve"> <trans-unit id="(this device v%@)" xml:space="preserve">
<source>(this device v%@)</source> <source>(this device v%@)</source>
<target>(cet appareil v%@)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id=")" xml:space="preserve"> <trans-unit id=")" xml:space="preserve">
@@ -395,15 +393,6 @@
- et bien d'autres choses encore !</target> - et bien d'autres choses encore !</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="- optionally notify deleted contacts.&#10;- profile names with spaces.&#10;- and more!" xml:space="preserve">
<source>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</source>
<target>- option pour notifier les contacts supprimés.
- noms de profil avec espaces.
- et plus encore !</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve"> <trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve">
<source>- voice messages up to 5 minutes. <source>- voice messages up to 5 minutes.
- custom time to disappear. - custom time to disappear.
@@ -890,7 +879,6 @@
</trans-unit> </trans-unit>
<trans-unit id="Bad desktop address" xml:space="preserve"> <trans-unit id="Bad desktop address" xml:space="preserve">
<source>Bad desktop address</source> <source>Bad desktop address</source>
<target>Mauvaise adresse de bureau</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Bad message ID" xml:space="preserve"> <trans-unit id="Bad message ID" xml:space="preserve">
@@ -903,11 +891,6 @@
<target>Mauvais hash de message</target> <target>Mauvais hash de message</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Better groups" xml:space="preserve">
<source>Better groups</source>
<target>Des groupes plus performants</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better messages" xml:space="preserve"> <trans-unit id="Better messages" xml:space="preserve">
<source>Better messages</source> <source>Better messages</source>
<target>Meilleurs messages</target> <target>Meilleurs messages</target>
@@ -918,11 +901,6 @@
<target>Bloquer</target> <target>Bloquer</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Block group members" xml:space="preserve">
<source>Block group members</source>
<target>Bloquer des membres d'un groupe</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Block member" xml:space="preserve"> <trans-unit id="Block member" xml:space="preserve">
<source>Block member</source> <source>Block member</source>
<target>Bloquer ce membre</target> <target>Bloquer ce membre</target>
@@ -1194,11 +1172,6 @@
<target>Se connecter</target> <target>Se connecter</target>
<note>server test step</note> <note>server test step</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect automatically" xml:space="preserve">
<source>Connect automatically</source>
<target>Connexion automatique</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect incognito" xml:space="preserve"> <trans-unit id="Connect incognito" xml:space="preserve">
<source>Connect incognito</source> <source>Connect incognito</source>
<target>Se connecter incognito</target> <target>Se connecter incognito</target>
@@ -1206,7 +1179,6 @@
</trans-unit> </trans-unit>
<trans-unit id="Connect to desktop" xml:space="preserve"> <trans-unit id="Connect to desktop" xml:space="preserve">
<source>Connect to desktop</source> <source>Connect to desktop</source>
<target>Se connecter au bureau</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect to yourself?" xml:space="preserve"> <trans-unit id="Connect to yourself?" xml:space="preserve">
@@ -1255,12 +1227,10 @@ Il s'agit de votre propre lien unique !</target>
</trans-unit> </trans-unit>
<trans-unit id="Connected desktop" xml:space="preserve"> <trans-unit id="Connected desktop" xml:space="preserve">
<source>Connected desktop</source> <source>Connected desktop</source>
<target>Bureau connecté</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connected to desktop" xml:space="preserve"> <trans-unit id="Connected to desktop" xml:space="preserve">
<source>Connected to desktop</source> <source>Connected to desktop</source>
<target>Connecté au bureau</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connecting server…" xml:space="preserve"> <trans-unit id="Connecting server…" xml:space="preserve">
@@ -1275,7 +1245,6 @@ Il s'agit de votre propre lien unique !</target>
</trans-unit> </trans-unit>
<trans-unit id="Connecting to desktop" xml:space="preserve"> <trans-unit id="Connecting to desktop" xml:space="preserve">
<source>Connecting to desktop</source> <source>Connecting to desktop</source>
<target>Connexion au bureau</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connection" xml:space="preserve"> <trans-unit id="Connection" xml:space="preserve">
@@ -1300,7 +1269,6 @@ Il s'agit de votre propre lien unique !</target>
</trans-unit> </trans-unit>
<trans-unit id="Connection terminated" xml:space="preserve"> <trans-unit id="Connection terminated" xml:space="preserve">
<source>Connection terminated</source> <source>Connection terminated</source>
<target>Connexion terminée</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connection timeout" xml:space="preserve"> <trans-unit id="Connection timeout" xml:space="preserve">
@@ -1383,11 +1351,6 @@ Il s'agit de votre propre lien unique !</target>
<target>Créer une adresse SimpleX</target> <target>Créer une adresse SimpleX</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create a group using a random profile." xml:space="preserve">
<source>Create a group using a random profile.</source>
<target>Création de groupes via un profil aléatoire.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create an address to let people connect with you." xml:space="preserve"> <trans-unit id="Create an address to let people connect with you." xml:space="preserve">
<source>Create an address to let people connect with you.</source> <source>Create an address to let people connect with you.</source>
<target>Créez une adresse pour permettre aux gens de vous contacter.</target> <target>Créez une adresse pour permettre aux gens de vous contacter.</target>
@@ -1800,17 +1763,14 @@ Cette opération ne peut être annulée !</target>
</trans-unit> </trans-unit>
<trans-unit id="Desktop address" xml:space="preserve"> <trans-unit id="Desktop address" xml:space="preserve">
<source>Desktop address</source> <source>Desktop address</source>
<target>Adresse de bureau</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Desktop app version %@ is not compatible with this app." xml:space="preserve"> <trans-unit id="Desktop app version %@ is not compatible with this app." xml:space="preserve">
<source>Desktop app version %@ is not compatible with this app.</source> <source>Desktop app version %@ is not compatible with this app.</source>
<target>La version de l'application de bureau %@ n'est pas compatible avec cette application.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Desktop devices" xml:space="preserve"> <trans-unit id="Desktop devices" xml:space="preserve">
<source>Desktop devices</source> <source>Desktop devices</source>
<target>Appareils de bureau</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Develop" xml:space="preserve"> <trans-unit id="Develop" xml:space="preserve">
@@ -1905,7 +1865,6 @@ Cette opération ne peut être annulée !</target>
</trans-unit> </trans-unit>
<trans-unit id="Disconnect desktop?" xml:space="preserve"> <trans-unit id="Disconnect desktop?" xml:space="preserve">
<source>Disconnect desktop?</source> <source>Disconnect desktop?</source>
<target>Déconnecter le bureau ?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover and join groups" xml:space="preserve"> <trans-unit id="Discover and join groups" xml:space="preserve">
@@ -1913,9 +1872,8 @@ Cette opération ne peut être annulée !</target>
<target>Découvrir et rejoindre des groupes</target> <target>Découvrir et rejoindre des groupes</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover via local network" xml:space="preserve"> <trans-unit id="Discover on network" xml:space="preserve">
<source>Discover via local network</source> <source>Discover on network</source>
<target>Rechercher sur le réseau</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve"> <trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
@@ -2090,12 +2048,10 @@ Cette opération ne peut être annulée !</target>
</trans-unit> </trans-unit>
<trans-unit id="Encryption re-negotiation error" xml:space="preserve"> <trans-unit id="Encryption re-negotiation error" xml:space="preserve">
<source>Encryption re-negotiation error</source> <source>Encryption re-negotiation error</source>
<target>Erreur lors de la renégociation du chiffrement</target>
<note>message decrypt error item</note> <note>message decrypt error item</note>
</trans-unit> </trans-unit>
<trans-unit id="Encryption re-negotiation failed." xml:space="preserve"> <trans-unit id="Encryption re-negotiation failed." xml:space="preserve">
<source>Encryption re-negotiation failed.</source> <source>Encryption re-negotiation failed.</source>
<target>La renégociation du chiffrement a échoué.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Enter Passcode" xml:space="preserve"> <trans-unit id="Enter Passcode" xml:space="preserve">
@@ -2130,7 +2086,6 @@ Cette opération ne peut être annulée !</target>
</trans-unit> </trans-unit>
<trans-unit id="Enter this device name…" xml:space="preserve"> <trans-unit id="Enter this device name…" xml:space="preserve">
<source>Enter this device name…</source> <source>Enter this device name…</source>
<target>Entrez le nom de l'appareil…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Enter welcome message…" xml:space="preserve"> <trans-unit id="Enter welcome message…" xml:space="preserve">
@@ -2458,11 +2413,6 @@ Cette opération ne peut être annulée !</target>
<target>Rapide et ne nécessitant pas d'attendre que l'expéditeur soit en ligne !</target> <target>Rapide et ne nécessitant pas d'attendre que l'expéditeur soit en ligne !</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Faster joining and more reliable messages." xml:space="preserve">
<source>Faster joining and more reliable messages.</source>
<target>Connexion plus rapide et messages plus fiables.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Favorite" xml:space="preserve"> <trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source> <source>Favorite</source>
<target>Favoris</target> <target>Favoris</target>
@@ -2558,11 +2508,6 @@ Cette opération ne peut être annulée !</target>
<target>Pour la console</target> <target>Pour la console</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<target>Bureau trouvé</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="French interface" xml:space="preserve"> <trans-unit id="French interface" xml:space="preserve">
<source>French interface</source> <source>French interface</source>
<target>Interface en français</target> <target>Interface en français</target>
@@ -2888,11 +2833,6 @@ Cette opération ne peut être annulée !</target>
<target>Incognito</target> <target>Incognito</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incognito groups" xml:space="preserve">
<source>Incognito groups</source>
<target>Groupes incognito</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode" xml:space="preserve"> <trans-unit id="Incognito mode" xml:space="preserve">
<source>Incognito mode</source> <source>Incognito mode</source>
<target>Mode Incognito</target> <target>Mode Incognito</target>
@@ -2925,7 +2865,6 @@ Cette opération ne peut être annulée !</target>
</trans-unit> </trans-unit>
<trans-unit id="Incompatible version" xml:space="preserve"> <trans-unit id="Incompatible version" xml:space="preserve">
<source>Incompatible version</source> <source>Incompatible version</source>
<target>Version incompatible</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incorrect passcode" xml:space="preserve"> <trans-unit id="Incorrect passcode" xml:space="preserve">
@@ -3100,7 +3039,6 @@ Voici votre lien pour le groupe %@ !</target>
</trans-unit> </trans-unit>
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve"> <trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
<source>Keep the app open to use it from desktop</source> <source>Keep the app open to use it from desktop</source>
<target>Garder l'application ouverte pour l'utiliser depuis le bureau</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Keep your connections" xml:space="preserve"> <trans-unit id="Keep your connections" xml:space="preserve">
@@ -3163,19 +3101,12 @@ Voici votre lien pour le groupe %@ !</target>
<target>Limitations</target> <target>Limitations</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Link mobile and desktop apps! 🔗" xml:space="preserve">
<source>Link mobile and desktop apps! 🔗</source>
<target>Liez vos applications mobiles et de bureau ! 🔗</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Linked desktop options" xml:space="preserve"> <trans-unit id="Linked desktop options" xml:space="preserve">
<source>Linked desktop options</source> <source>Linked desktop options</source>
<target>Options de bureau lié</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Linked desktops" xml:space="preserve"> <trans-unit id="Linked desktops" xml:space="preserve">
<source>Linked desktops</source> <source>Linked desktops</source>
<target>Bureaux liés</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Live message!" xml:space="preserve"> <trans-unit id="Live message!" xml:space="preserve">
@@ -3528,11 +3459,6 @@ Voici votre lien pour le groupe %@ !</target>
<target>Aucun fichier reçu ou envoyé</target> <target>Aucun fichier reçu ou envoyé</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Not compatible!" xml:space="preserve">
<source>Not compatible!</source>
<target>Non compatible !</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve"> <trans-unit id="Notifications" xml:space="preserve">
<source>Notifications</source> <source>Notifications</source>
<target>Notifications</target> <target>Notifications</target>
@@ -3754,7 +3680,6 @@ Voici votre lien pour le groupe %@ !</target>
</trans-unit> </trans-unit>
<trans-unit id="Paste desktop address" xml:space="preserve"> <trans-unit id="Paste desktop address" xml:space="preserve">
<source>Paste desktop address</source> <source>Paste desktop address</source>
<target>Coller l'adresse du bureau</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Paste image" xml:space="preserve"> <trans-unit id="Paste image" xml:space="preserve">
@@ -4354,7 +4279,6 @@ Voici votre lien pour le groupe %@ !</target>
</trans-unit> </trans-unit>
<trans-unit id="Scan QR code from desktop" xml:space="preserve"> <trans-unit id="Scan QR code from desktop" xml:space="preserve">
<source>Scan QR code from desktop</source> <source>Scan QR code from desktop</source>
<target>Scanner le code QR du bureau</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Scan code" xml:space="preserve"> <trans-unit id="Scan code" xml:space="preserve">
@@ -4579,7 +4503,6 @@ Voici votre lien pour le groupe %@ !</target>
</trans-unit> </trans-unit>
<trans-unit id="Session code" xml:space="preserve"> <trans-unit id="Session code" xml:space="preserve">
<source>Session code</source> <source>Session code</source>
<target>Code de session</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Set 1 day" xml:space="preserve"> <trans-unit id="Set 1 day" xml:space="preserve">
@@ -5081,7 +5004,6 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise.
</trans-unit> </trans-unit>
<trans-unit id="This device name" xml:space="preserve"> <trans-unit id="This device name" xml:space="preserve">
<source>This device name</source> <source>This device name</source>
<target>Ce nom d'appareil</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="This group has over %lld members, delivery receipts are not sent." xml:space="preserve"> <trans-unit id="This group has over %lld members, delivery receipts are not sent." xml:space="preserve">
@@ -5119,11 +5041,6 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise.
<target>Pour se connecter, votre contact peut scanner le code QR ou utiliser le lien dans l'application.</target> <target>Pour se connecter, votre contact peut scanner le code QR ou utiliser le lien dans l'application.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="To hide unwanted messages." xml:space="preserve">
<source>To hide unwanted messages.</source>
<target>Pour cacher les messages indésirables.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To make a new connection" xml:space="preserve"> <trans-unit id="To make a new connection" xml:space="preserve">
<source>To make a new connection</source> <source>To make a new connection</source>
<target>Pour établir une nouvelle connexion</target> <target>Pour établir une nouvelle connexion</target>
@@ -5285,12 +5202,10 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien
</trans-unit> </trans-unit>
<trans-unit id="Unlink" xml:space="preserve"> <trans-unit id="Unlink" xml:space="preserve">
<source>Unlink</source> <source>Unlink</source>
<target>Délier</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Unlink desktop?" xml:space="preserve"> <trans-unit id="Unlink desktop?" xml:space="preserve">
<source>Unlink desktop?</source> <source>Unlink desktop?</source>
<target>Délier le bureau ?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Unlock" xml:space="preserve"> <trans-unit id="Unlock" xml:space="preserve">
@@ -5385,7 +5300,6 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien
</trans-unit> </trans-unit>
<trans-unit id="Use from desktop" xml:space="preserve"> <trans-unit id="Use from desktop" xml:space="preserve">
<source>Use from desktop</source> <source>Use from desktop</source>
<target>Utilisation depuis le bureau</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Use iOS call interface" xml:space="preserve"> <trans-unit id="Use iOS call interface" xml:space="preserve">
@@ -5420,12 +5334,10 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien
</trans-unit> </trans-unit>
<trans-unit id="Verify code with desktop" xml:space="preserve"> <trans-unit id="Verify code with desktop" xml:space="preserve">
<source>Verify code with desktop</source> <source>Verify code with desktop</source>
<target>Vérifier le code avec le bureau</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Verify connection" xml:space="preserve"> <trans-unit id="Verify connection" xml:space="preserve">
<source>Verify connection</source> <source>Verify connection</source>
<target>Vérifier la connexion</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Verify connection security" xml:space="preserve"> <trans-unit id="Verify connection security" xml:space="preserve">
@@ -5435,7 +5347,6 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien
</trans-unit> </trans-unit>
<trans-unit id="Verify connections" xml:space="preserve"> <trans-unit id="Verify connections" xml:space="preserve">
<source>Verify connections</source> <source>Verify connections</source>
<target>Vérifier les connexions</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Verify security code" xml:space="preserve"> <trans-unit id="Verify security code" xml:space="preserve">
@@ -5448,11 +5359,6 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien
<target>Via navigateur</target> <target>Via navigateur</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Via secure quantum resistant protocol." xml:space="preserve">
<source>Via secure quantum resistant protocol.</source>
<target>Via un protocole sécurisé de cryptographie post-quantique.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Video call" xml:space="preserve"> <trans-unit id="Video call" xml:space="preserve">
<source>Video call</source> <source>Video call</source>
<target>Appel vidéo</target> <target>Appel vidéo</target>
@@ -5503,11 +5409,6 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien
<target>Message vocal…</target> <target>Message vocal…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Waiting for desktop..." xml:space="preserve">
<source>Waiting for desktop...</source>
<target>En attente du bureau...</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve"> <trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source> <source>Waiting for file</source>
<target>En attente du fichier</target> <target>En attente du fichier</target>
@@ -6053,11 +5954,6 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
<target>appel audio (sans chiffrement)</target> <target>appel audio (sans chiffrement)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="author" xml:space="preserve">
<source>author</source>
<target>auteur</target>
<note>member role</note>
</trans-unit>
<trans-unit id="bad message ID" xml:space="preserve"> <trans-unit id="bad message ID" xml:space="preserve">
<source>bad message ID</source> <source>bad message ID</source>
<target>ID de message incorrecte</target> <target>ID de message incorrecte</target>
@@ -6642,7 +6538,6 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
</trans-unit> </trans-unit>
<trans-unit id="v%@" xml:space="preserve"> <trans-unit id="v%@" xml:space="preserve">
<source>v%@</source> <source>v%@</source>
<target>v%@</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="v%@ (%@)" xml:space="preserve"> <trans-unit id="v%@ (%@)" xml:space="preserve">
@@ -6784,7 +6679,6 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
</trans-unit> </trans-unit>
<trans-unit id="NSLocalNetworkUsageDescription" xml:space="preserve"> <trans-unit id="NSLocalNetworkUsageDescription" xml:space="preserve">
<source>SimpleX uses local network access to allow using user chat profile via desktop app on the same network.</source> <source>SimpleX uses local network access to allow using user chat profile via desktop app on the same network.</source>
<target>SimpleX utilise un accès au réseau local pour permettre l'utilisation du profil de chat de l'utilisateur via l'application de bureau au sein de ce même réseau.</target>
<note>Privacy - Local Network Usage Description</note> <note>Privacy - Local Network Usage Description</note>
</trans-unit> </trans-unit>
<trans-unit id="NSMicrophoneUsageDescription" xml:space="preserve"> <trans-unit id="NSMicrophoneUsageDescription" xml:space="preserve">
@@ -299,12 +299,10 @@
</trans-unit> </trans-unit>
<trans-unit id="(new)" xml:space="preserve"> <trans-unit id="(new)" xml:space="preserve">
<source>(new)</source> <source>(new)</source>
<target>(nuovo)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="(this device v%@)" xml:space="preserve"> <trans-unit id="(this device v%@)" xml:space="preserve">
<source>(this device v%@)</source> <source>(this device v%@)</source>
<target>(questo dispositivo v%@)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id=")" xml:space="preserve"> <trans-unit id=")" xml:space="preserve">
@@ -395,15 +393,6 @@
- e altro ancora!</target> - e altro ancora!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="- optionally notify deleted contacts.&#10;- profile names with spaces.&#10;- and more!" xml:space="preserve">
<source>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</source>
<target>- avvisa facoltativamente i contatti eliminati.
- nomi del profilo con spazi.
- e molto altro!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve"> <trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve">
<source>- voice messages up to 5 minutes. <source>- voice messages up to 5 minutes.
- custom time to disappear. - custom time to disappear.
@@ -890,7 +879,6 @@
</trans-unit> </trans-unit>
<trans-unit id="Bad desktop address" xml:space="preserve"> <trans-unit id="Bad desktop address" xml:space="preserve">
<source>Bad desktop address</source> <source>Bad desktop address</source>
<target>Indirizzo desktop errato</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Bad message ID" xml:space="preserve"> <trans-unit id="Bad message ID" xml:space="preserve">
@@ -903,11 +891,6 @@
<target>Hash del messaggio errato</target> <target>Hash del messaggio errato</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Better groups" xml:space="preserve">
<source>Better groups</source>
<target>Gruppi migliorati</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better messages" xml:space="preserve"> <trans-unit id="Better messages" xml:space="preserve">
<source>Better messages</source> <source>Better messages</source>
<target>Messaggi migliorati</target> <target>Messaggi migliorati</target>
@@ -918,11 +901,6 @@
<target>Blocca</target> <target>Blocca</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Block group members" xml:space="preserve">
<source>Block group members</source>
<target>Blocca i membri dei gruppi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Block member" xml:space="preserve"> <trans-unit id="Block member" xml:space="preserve">
<source>Block member</source> <source>Block member</source>
<target>Blocca membro</target> <target>Blocca membro</target>
@@ -1194,10 +1172,6 @@
<target>Connetti</target> <target>Connetti</target>
<note>server test step</note> <note>server test step</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect automatically" xml:space="preserve">
<source>Connect automatically</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect incognito" xml:space="preserve"> <trans-unit id="Connect incognito" xml:space="preserve">
<source>Connect incognito</source> <source>Connect incognito</source>
<target>Connetti in incognito</target> <target>Connetti in incognito</target>
@@ -1205,7 +1179,6 @@
</trans-unit> </trans-unit>
<trans-unit id="Connect to desktop" xml:space="preserve"> <trans-unit id="Connect to desktop" xml:space="preserve">
<source>Connect to desktop</source> <source>Connect to desktop</source>
<target>Connetti al desktop</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect to yourself?" xml:space="preserve"> <trans-unit id="Connect to yourself?" xml:space="preserve">
@@ -1254,12 +1227,10 @@ Questo è il tuo link una tantum!</target>
</trans-unit> </trans-unit>
<trans-unit id="Connected desktop" xml:space="preserve"> <trans-unit id="Connected desktop" xml:space="preserve">
<source>Connected desktop</source> <source>Connected desktop</source>
<target>Desktop connesso</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connected to desktop" xml:space="preserve"> <trans-unit id="Connected to desktop" xml:space="preserve">
<source>Connected to desktop</source> <source>Connected to desktop</source>
<target>Connesso al desktop</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connecting server…" xml:space="preserve"> <trans-unit id="Connecting server…" xml:space="preserve">
@@ -1274,7 +1245,6 @@ Questo è il tuo link una tantum!</target>
</trans-unit> </trans-unit>
<trans-unit id="Connecting to desktop" xml:space="preserve"> <trans-unit id="Connecting to desktop" xml:space="preserve">
<source>Connecting to desktop</source> <source>Connecting to desktop</source>
<target>Connessione al desktop</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connection" xml:space="preserve"> <trans-unit id="Connection" xml:space="preserve">
@@ -1299,7 +1269,6 @@ Questo è il tuo link una tantum!</target>
</trans-unit> </trans-unit>
<trans-unit id="Connection terminated" xml:space="preserve"> <trans-unit id="Connection terminated" xml:space="preserve">
<source>Connection terminated</source> <source>Connection terminated</source>
<target>Connessione terminata</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connection timeout" xml:space="preserve"> <trans-unit id="Connection timeout" xml:space="preserve">
@@ -1382,11 +1351,6 @@ Questo è il tuo link una tantum!</target>
<target>Crea indirizzo SimpleX</target> <target>Crea indirizzo SimpleX</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create a group using a random profile." xml:space="preserve">
<source>Create a group using a random profile.</source>
<target>Crea un gruppo usando un profilo casuale.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create an address to let people connect with you." xml:space="preserve"> <trans-unit id="Create an address to let people connect with you." xml:space="preserve">
<source>Create an address to let people connect with you.</source> <source>Create an address to let people connect with you.</source>
<target>Crea un indirizzo per consentire alle persone di connettersi con te.</target> <target>Crea un indirizzo per consentire alle persone di connettersi con te.</target>
@@ -1799,17 +1763,14 @@ Non è reversibile!</target>
</trans-unit> </trans-unit>
<trans-unit id="Desktop address" xml:space="preserve"> <trans-unit id="Desktop address" xml:space="preserve">
<source>Desktop address</source> <source>Desktop address</source>
<target>Indirizzo desktop</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Desktop app version %@ is not compatible with this app." xml:space="preserve"> <trans-unit id="Desktop app version %@ is not compatible with this app." xml:space="preserve">
<source>Desktop app version %@ is not compatible with this app.</source> <source>Desktop app version %@ is not compatible with this app.</source>
<target>La versione dell'app desktop %@ non è compatibile con questa app.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Desktop devices" xml:space="preserve"> <trans-unit id="Desktop devices" xml:space="preserve">
<source>Desktop devices</source> <source>Desktop devices</source>
<target>Dispositivi desktop</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Develop" xml:space="preserve"> <trans-unit id="Develop" xml:space="preserve">
@@ -1904,7 +1865,6 @@ Non è reversibile!</target>
</trans-unit> </trans-unit>
<trans-unit id="Disconnect desktop?" xml:space="preserve"> <trans-unit id="Disconnect desktop?" xml:space="preserve">
<source>Disconnect desktop?</source> <source>Disconnect desktop?</source>
<target>Disconnettere il desktop?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover and join groups" xml:space="preserve"> <trans-unit id="Discover and join groups" xml:space="preserve">
@@ -1912,8 +1872,8 @@ Non è reversibile!</target>
<target>Scopri ed unisciti ai gruppi</target> <target>Scopri ed unisciti ai gruppi</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover via local network" xml:space="preserve"> <trans-unit id="Discover on network" xml:space="preserve">
<source>Discover via local network</source> <source>Discover on network</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve"> <trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
@@ -2088,12 +2048,10 @@ Non è reversibile!</target>
</trans-unit> </trans-unit>
<trans-unit id="Encryption re-negotiation error" xml:space="preserve"> <trans-unit id="Encryption re-negotiation error" xml:space="preserve">
<source>Encryption re-negotiation error</source> <source>Encryption re-negotiation error</source>
<target>Errore di rinegoziazione crittografia</target>
<note>message decrypt error item</note> <note>message decrypt error item</note>
</trans-unit> </trans-unit>
<trans-unit id="Encryption re-negotiation failed." xml:space="preserve"> <trans-unit id="Encryption re-negotiation failed." xml:space="preserve">
<source>Encryption re-negotiation failed.</source> <source>Encryption re-negotiation failed.</source>
<target>Rinegoziazione crittografia fallita.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Enter Passcode" xml:space="preserve"> <trans-unit id="Enter Passcode" xml:space="preserve">
@@ -2128,7 +2086,6 @@ Non è reversibile!</target>
</trans-unit> </trans-unit>
<trans-unit id="Enter this device name…" xml:space="preserve"> <trans-unit id="Enter this device name…" xml:space="preserve">
<source>Enter this device name…</source> <source>Enter this device name…</source>
<target>Inserisci il nome di questo dispositivo…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Enter welcome message…" xml:space="preserve"> <trans-unit id="Enter welcome message…" xml:space="preserve">
@@ -2456,11 +2413,6 @@ Non è reversibile!</target>
<target>Veloce e senza aspettare che il mittente sia in linea!</target> <target>Veloce e senza aspettare che il mittente sia in linea!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Faster joining and more reliable messages." xml:space="preserve">
<source>Faster joining and more reliable messages.</source>
<target>Ingresso più veloce e messaggi più affidabili.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Favorite" xml:space="preserve"> <trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source> <source>Favorite</source>
<target>Preferito</target> <target>Preferito</target>
@@ -2556,10 +2508,6 @@ Non è reversibile!</target>
<target>Per console</target> <target>Per console</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="French interface" xml:space="preserve"> <trans-unit id="French interface" xml:space="preserve">
<source>French interface</source> <source>French interface</source>
<target>Interfaccia francese</target> <target>Interfaccia francese</target>
@@ -2885,11 +2833,6 @@ Non è reversibile!</target>
<target>Incognito</target> <target>Incognito</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incognito groups" xml:space="preserve">
<source>Incognito groups</source>
<target>Gruppi in incognito</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode" xml:space="preserve"> <trans-unit id="Incognito mode" xml:space="preserve">
<source>Incognito mode</source> <source>Incognito mode</source>
<target>Modalità incognito</target> <target>Modalità incognito</target>
@@ -2922,7 +2865,6 @@ Non è reversibile!</target>
</trans-unit> </trans-unit>
<trans-unit id="Incompatible version" xml:space="preserve"> <trans-unit id="Incompatible version" xml:space="preserve">
<source>Incompatible version</source> <source>Incompatible version</source>
<target>Versione incompatibile</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incorrect passcode" xml:space="preserve"> <trans-unit id="Incorrect passcode" xml:space="preserve">
@@ -3097,7 +3039,6 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit> </trans-unit>
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve"> <trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
<source>Keep the app open to use it from desktop</source> <source>Keep the app open to use it from desktop</source>
<target>Tieni aperta l'app per usarla dal desktop</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Keep your connections" xml:space="preserve"> <trans-unit id="Keep your connections" xml:space="preserve">
@@ -3160,19 +3101,12 @@ Questo è il tuo link per il gruppo %@!</target>
<target>Limitazioni</target> <target>Limitazioni</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Link mobile and desktop apps! 🔗" xml:space="preserve">
<source>Link mobile and desktop apps! 🔗</source>
<target>Collega le app mobile e desktop! 🔗</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Linked desktop options" xml:space="preserve"> <trans-unit id="Linked desktop options" xml:space="preserve">
<source>Linked desktop options</source> <source>Linked desktop options</source>
<target>Opzioni del desktop collegato</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Linked desktops" xml:space="preserve"> <trans-unit id="Linked desktops" xml:space="preserve">
<source>Linked desktops</source> <source>Linked desktops</source>
<target>Desktop collegati</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Live message!" xml:space="preserve"> <trans-unit id="Live message!" xml:space="preserve">
@@ -3525,10 +3459,6 @@ Questo è il tuo link per il gruppo %@!</target>
<target>Nessun file ricevuto o inviato</target> <target>Nessun file ricevuto o inviato</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Not compatible!" xml:space="preserve">
<source>Not compatible!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve"> <trans-unit id="Notifications" xml:space="preserve">
<source>Notifications</source> <source>Notifications</source>
<target>Notifiche</target> <target>Notifiche</target>
@@ -3750,7 +3680,6 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit> </trans-unit>
<trans-unit id="Paste desktop address" xml:space="preserve"> <trans-unit id="Paste desktop address" xml:space="preserve">
<source>Paste desktop address</source> <source>Paste desktop address</source>
<target>Incolla l'indirizzo desktop</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Paste image" xml:space="preserve"> <trans-unit id="Paste image" xml:space="preserve">
@@ -4350,7 +4279,6 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit> </trans-unit>
<trans-unit id="Scan QR code from desktop" xml:space="preserve"> <trans-unit id="Scan QR code from desktop" xml:space="preserve">
<source>Scan QR code from desktop</source> <source>Scan QR code from desktop</source>
<target>Scansiona codice QR dal desktop</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Scan code" xml:space="preserve"> <trans-unit id="Scan code" xml:space="preserve">
@@ -4575,7 +4503,6 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit> </trans-unit>
<trans-unit id="Session code" xml:space="preserve"> <trans-unit id="Session code" xml:space="preserve">
<source>Session code</source> <source>Session code</source>
<target>Codice di sessione</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Set 1 day" xml:space="preserve"> <trans-unit id="Set 1 day" xml:space="preserve">
@@ -5077,7 +5004,6 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa.</ta
</trans-unit> </trans-unit>
<trans-unit id="This device name" xml:space="preserve"> <trans-unit id="This device name" xml:space="preserve">
<source>This device name</source> <source>This device name</source>
<target>Il nome di questo dispositivo</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="This group has over %lld members, delivery receipts are not sent." xml:space="preserve"> <trans-unit id="This group has over %lld members, delivery receipts are not sent." xml:space="preserve">
@@ -5115,11 +5041,6 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa.</ta
<target>Per connettervi, il tuo contatto può scansionare il codice QR o usare il link nell'app.</target> <target>Per connettervi, il tuo contatto può scansionare il codice QR o usare il link nell'app.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="To hide unwanted messages." xml:space="preserve">
<source>To hide unwanted messages.</source>
<target>Per nascondere messaggi indesiderati.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To make a new connection" xml:space="preserve"> <trans-unit id="To make a new connection" xml:space="preserve">
<source>To make a new connection</source> <source>To make a new connection</source>
<target>Per creare una nuova connessione</target> <target>Per creare una nuova connessione</target>
@@ -5281,12 +5202,10 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
</trans-unit> </trans-unit>
<trans-unit id="Unlink" xml:space="preserve"> <trans-unit id="Unlink" xml:space="preserve">
<source>Unlink</source> <source>Unlink</source>
<target>Scollega</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Unlink desktop?" xml:space="preserve"> <trans-unit id="Unlink desktop?" xml:space="preserve">
<source>Unlink desktop?</source> <source>Unlink desktop?</source>
<target>Scollegare il desktop?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Unlock" xml:space="preserve"> <trans-unit id="Unlock" xml:space="preserve">
@@ -5381,7 +5300,6 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
</trans-unit> </trans-unit>
<trans-unit id="Use from desktop" xml:space="preserve"> <trans-unit id="Use from desktop" xml:space="preserve">
<source>Use from desktop</source> <source>Use from desktop</source>
<target>Usa dal desktop</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Use iOS call interface" xml:space="preserve"> <trans-unit id="Use iOS call interface" xml:space="preserve">
@@ -5416,12 +5334,10 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
</trans-unit> </trans-unit>
<trans-unit id="Verify code with desktop" xml:space="preserve"> <trans-unit id="Verify code with desktop" xml:space="preserve">
<source>Verify code with desktop</source> <source>Verify code with desktop</source>
<target>Verifica il codice con il desktop</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Verify connection" xml:space="preserve"> <trans-unit id="Verify connection" xml:space="preserve">
<source>Verify connection</source> <source>Verify connection</source>
<target>Verifica la connessione</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Verify connection security" xml:space="preserve"> <trans-unit id="Verify connection security" xml:space="preserve">
@@ -5431,7 +5347,6 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
</trans-unit> </trans-unit>
<trans-unit id="Verify connections" xml:space="preserve"> <trans-unit id="Verify connections" xml:space="preserve">
<source>Verify connections</source> <source>Verify connections</source>
<target>Verifica le connessioni</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Verify security code" xml:space="preserve"> <trans-unit id="Verify security code" xml:space="preserve">
@@ -5444,11 +5359,6 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
<target>Via browser</target> <target>Via browser</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Via secure quantum resistant protocol." xml:space="preserve">
<source>Via secure quantum resistant protocol.</source>
<target>Tramite protocollo sicuro resistente alla quantistica.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Video call" xml:space="preserve"> <trans-unit id="Video call" xml:space="preserve">
<source>Video call</source> <source>Video call</source>
<target>Videochiamata</target> <target>Videochiamata</target>
@@ -5499,10 +5409,6 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
<target>Messaggio vocale…</target> <target>Messaggio vocale…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Waiting for desktop..." xml:space="preserve">
<source>Waiting for desktop...</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve"> <trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source> <source>Waiting for file</source>
<target>In attesa del file</target> <target>In attesa del file</target>
@@ -6048,10 +5954,6 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
<target>chiamata audio (non crittografata e2e)</target> <target>chiamata audio (non crittografata e2e)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="author" xml:space="preserve">
<source>author</source>
<note>member role</note>
</trans-unit>
<trans-unit id="bad message ID" xml:space="preserve"> <trans-unit id="bad message ID" xml:space="preserve">
<source>bad message ID</source> <source>bad message ID</source>
<target>ID messaggio errato</target> <target>ID messaggio errato</target>
@@ -6636,7 +6538,6 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
</trans-unit> </trans-unit>
<trans-unit id="v%@" xml:space="preserve"> <trans-unit id="v%@" xml:space="preserve">
<source>v%@</source> <source>v%@</source>
<target>v%@</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="v%@ (%@)" xml:space="preserve"> <trans-unit id="v%@ (%@)" xml:space="preserve">
@@ -6778,7 +6679,6 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
</trans-unit> </trans-unit>
<trans-unit id="NSLocalNetworkUsageDescription" xml:space="preserve"> <trans-unit id="NSLocalNetworkUsageDescription" xml:space="preserve">
<source>SimpleX uses local network access to allow using user chat profile via desktop app on the same network.</source> <source>SimpleX uses local network access to allow using user chat profile via desktop app on the same network.</source>
<target>SimpleX usa l'accesso alla rete locale per consentire di usare il profilo di chat tramite l'app desktop sulla stessa rete.</target>
<note>Privacy - Local Network Usage Description</note> <note>Privacy - Local Network Usage Description</note>
</trans-unit> </trans-unit>
<trans-unit id="NSMicrophoneUsageDescription" xml:space="preserve"> <trans-unit id="NSMicrophoneUsageDescription" xml:space="preserve">
@@ -383,12 +383,6 @@
- などなど!</target> - などなど!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="- optionally notify deleted contacts.&#10;- profile names with spaces.&#10;- and more!" xml:space="preserve">
<source>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve"> <trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve">
<source>- voice messages up to 5 minutes. <source>- voice messages up to 5 minutes.
- custom time to disappear. - custom time to disappear.
@@ -883,10 +877,6 @@
<target>メッセージのハッシュ値問題</target> <target>メッセージのハッシュ値問題</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Better groups" xml:space="preserve">
<source>Better groups</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better messages" xml:space="preserve"> <trans-unit id="Better messages" xml:space="preserve">
<source>Better messages</source> <source>Better messages</source>
<target>より良いメッセージ</target> <target>より良いメッセージ</target>
@@ -896,10 +886,6 @@
<source>Block</source> <source>Block</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Block group members" xml:space="preserve">
<source>Block group members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Block member" xml:space="preserve"> <trans-unit id="Block member" xml:space="preserve">
<source>Block member</source> <source>Block member</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -1169,10 +1155,6 @@
<target>接続</target> <target>接続</target>
<note>server test step</note> <note>server test step</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect automatically" xml:space="preserve">
<source>Connect automatically</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect incognito" xml:space="preserve"> <trans-unit id="Connect incognito" xml:space="preserve">
<source>Connect incognito</source> <source>Connect incognito</source>
<target>シークレットモードで接続</target> <target>シークレットモードで接続</target>
@@ -1344,10 +1326,6 @@ This is your own one-time link!</source>
<target>SimpleXアドレスの作成</target> <target>SimpleXアドレスの作成</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create a group using a random profile." xml:space="preserve">
<source>Create a group using a random profile.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create an address to let people connect with you." xml:space="preserve"> <trans-unit id="Create an address to let people connect with you." xml:space="preserve">
<source>Create an address to let people connect with you.</source> <source>Create an address to let people connect with you.</source>
<target>人とつながるためのアドレスを作成する。</target> <target>人とつながるためのアドレスを作成する。</target>
@@ -1863,8 +1841,8 @@ This cannot be undone!</source>
<target>グループを見つけて参加する</target> <target>グループを見つけて参加する</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover via local network" xml:space="preserve"> <trans-unit id="Discover on network" xml:space="preserve">
<source>Discover via local network</source> <source>Discover on network</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve"> <trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
@@ -2399,10 +2377,6 @@ This cannot be undone!</source>
<target>送信者がオンラインになるまでの待ち時間がなく、速い!</target> <target>送信者がオンラインになるまでの待ち時間がなく、速い!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Faster joining and more reliable messages." xml:space="preserve">
<source>Faster joining and more reliable messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Favorite" xml:space="preserve"> <trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source> <source>Favorite</source>
<target>お気に入り</target> <target>お気に入り</target>
@@ -2498,10 +2472,6 @@ This cannot be undone!</source>
<target>コンソール</target> <target>コンソール</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="French interface" xml:space="preserve"> <trans-unit id="French interface" xml:space="preserve">
<source>French interface</source> <source>French interface</source>
<target>フランス語UI</target> <target>フランス語UI</target>
@@ -2824,10 +2794,6 @@ This cannot be undone!</source>
<target>シークレットモード</target> <target>シークレットモード</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incognito groups" xml:space="preserve">
<source>Incognito groups</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode" xml:space="preserve"> <trans-unit id="Incognito mode" xml:space="preserve">
<source>Incognito mode</source> <source>Incognito mode</source>
<target>シークレットモード</target> <target>シークレットモード</target>
@@ -3091,10 +3057,6 @@ This is your link for group %@!</source>
<target>制限事項</target> <target>制限事項</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Link mobile and desktop apps! 🔗" xml:space="preserve">
<source>Link mobile and desktop apps! 🔗</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Linked desktop options" xml:space="preserve"> <trans-unit id="Linked desktop options" xml:space="preserve">
<source>Linked desktop options</source> <source>Linked desktop options</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -3451,10 +3413,6 @@ This is your link for group %@!</source>
<target>送受信済みのファイルがありません</target> <target>送受信済みのファイルがありません</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Not compatible!" xml:space="preserve">
<source>Not compatible!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve"> <trans-unit id="Notifications" xml:space="preserve">
<source>Notifications</source> <source>Notifications</source>
<target>通知</target> <target>通知</target>
@@ -5019,10 +4977,6 @@ It can happen because of some bug or when the connection is compromised.</source
<target>接続するにはQRコードを読み込むか、アプリ内のリンクを使用する必要があります。</target> <target>接続するにはQRコードを読み込むか、アプリ内のリンクを使用する必要があります。</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="To hide unwanted messages." xml:space="preserve">
<source>To hide unwanted messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To make a new connection" xml:space="preserve"> <trans-unit id="To make a new connection" xml:space="preserve">
<source>To make a new connection</source> <source>To make a new connection</source>
<target>新規に接続する場合</target> <target>新規に接続する場合</target>
@@ -5337,10 +5291,6 @@ To connect, please ask your contact to create another connection link and check
<target>ブラウザ経由</target> <target>ブラウザ経由</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Via secure quantum resistant protocol." xml:space="preserve">
<source>Via secure quantum resistant protocol.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Video call" xml:space="preserve"> <trans-unit id="Video call" xml:space="preserve">
<source>Video call</source> <source>Video call</source>
<target>ビデオ通話</target> <target>ビデオ通話</target>
@@ -5391,10 +5341,6 @@ To connect, please ask your contact to create another connection link and check
<target>音声メッセージ…</target> <target>音声メッセージ…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Waiting for desktop..." xml:space="preserve">
<source>Waiting for desktop...</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve"> <trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source> <source>Waiting for file</source>
<target>ファイル待ち</target> <target>ファイル待ち</target>
@@ -5925,10 +5871,6 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>音声通話 (エンドツーエンド暗号化なし)</target> <target>音声通話 (エンドツーエンド暗号化なし)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="author" xml:space="preserve">
<source>author</source>
<note>member role</note>
</trans-unit>
<trans-unit id="bad message ID" xml:space="preserve"> <trans-unit id="bad message ID" xml:space="preserve">
<source>bad message ID</source> <source>bad message ID</source>
<target>メッセージ ID が正しくありません</target> <target>メッセージ ID が正しくありません</target>
@@ -299,12 +299,10 @@
</trans-unit> </trans-unit>
<trans-unit id="(new)" xml:space="preserve"> <trans-unit id="(new)" xml:space="preserve">
<source>(new)</source> <source>(new)</source>
<target>(nieuw)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="(this device v%@)" xml:space="preserve"> <trans-unit id="(this device v%@)" xml:space="preserve">
<source>(this device v%@)</source> <source>(this device v%@)</source>
<target>(dit apparaat v%@)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id=")" xml:space="preserve"> <trans-unit id=")" xml:space="preserve">
@@ -392,15 +390,6 @@
- and more!</source> - and more!</source>
<target>- stabielere berichtbezorging. <target>- stabielere berichtbezorging.
- een beetje betere groepen. - een beetje betere groepen.
- en meer!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="- optionally notify deleted contacts.&#10;- profile names with spaces.&#10;- and more!" xml:space="preserve">
<source>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</source>
<target>- optioneel verwijderde contacten op de hoogte stellen.
- profielnamen met spaties.
- en meer!</target> - en meer!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
@@ -890,7 +879,6 @@
</trans-unit> </trans-unit>
<trans-unit id="Bad desktop address" xml:space="preserve"> <trans-unit id="Bad desktop address" xml:space="preserve">
<source>Bad desktop address</source> <source>Bad desktop address</source>
<target>Onjuist desktopadres</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Bad message ID" xml:space="preserve"> <trans-unit id="Bad message ID" xml:space="preserve">
@@ -903,11 +891,6 @@
<target>Onjuiste bericht hash</target> <target>Onjuiste bericht hash</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Better groups" xml:space="preserve">
<source>Better groups</source>
<target>Betere groepen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better messages" xml:space="preserve"> <trans-unit id="Better messages" xml:space="preserve">
<source>Better messages</source> <source>Better messages</source>
<target>Betere berichten</target> <target>Betere berichten</target>
@@ -918,11 +901,6 @@
<target>Blokkeren</target> <target>Blokkeren</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Block group members" xml:space="preserve">
<source>Block group members</source>
<target>Groepsleden blokkeren</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Block member" xml:space="preserve"> <trans-unit id="Block member" xml:space="preserve">
<source>Block member</source> <source>Block member</source>
<target>Lid blokkeren</target> <target>Lid blokkeren</target>
@@ -1194,11 +1172,6 @@
<target>Verbind</target> <target>Verbind</target>
<note>server test step</note> <note>server test step</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect automatically" xml:space="preserve">
<source>Connect automatically</source>
<target>Automatisch verbinden</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect incognito" xml:space="preserve"> <trans-unit id="Connect incognito" xml:space="preserve">
<source>Connect incognito</source> <source>Connect incognito</source>
<target>Verbind incognito</target> <target>Verbind incognito</target>
@@ -1206,7 +1179,6 @@
</trans-unit> </trans-unit>
<trans-unit id="Connect to desktop" xml:space="preserve"> <trans-unit id="Connect to desktop" xml:space="preserve">
<source>Connect to desktop</source> <source>Connect to desktop</source>
<target>Verbinden met desktop</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect to yourself?" xml:space="preserve"> <trans-unit id="Connect to yourself?" xml:space="preserve">
@@ -1255,12 +1227,10 @@ Dit is uw eigen eenmalige link!</target>
</trans-unit> </trans-unit>
<trans-unit id="Connected desktop" xml:space="preserve"> <trans-unit id="Connected desktop" xml:space="preserve">
<source>Connected desktop</source> <source>Connected desktop</source>
<target>Verbonden desktop</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connected to desktop" xml:space="preserve"> <trans-unit id="Connected to desktop" xml:space="preserve">
<source>Connected to desktop</source> <source>Connected to desktop</source>
<target>Verbonden met desktop</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connecting server…" xml:space="preserve"> <trans-unit id="Connecting server…" xml:space="preserve">
@@ -1275,7 +1245,6 @@ Dit is uw eigen eenmalige link!</target>
</trans-unit> </trans-unit>
<trans-unit id="Connecting to desktop" xml:space="preserve"> <trans-unit id="Connecting to desktop" xml:space="preserve">
<source>Connecting to desktop</source> <source>Connecting to desktop</source>
<target>Verbinding maken met desktop</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connection" xml:space="preserve"> <trans-unit id="Connection" xml:space="preserve">
@@ -1300,7 +1269,6 @@ Dit is uw eigen eenmalige link!</target>
</trans-unit> </trans-unit>
<trans-unit id="Connection terminated" xml:space="preserve"> <trans-unit id="Connection terminated" xml:space="preserve">
<source>Connection terminated</source> <source>Connection terminated</source>
<target>Verbinding beëindigd</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connection timeout" xml:space="preserve"> <trans-unit id="Connection timeout" xml:space="preserve">
@@ -1383,11 +1351,6 @@ Dit is uw eigen eenmalige link!</target>
<target>Maak een SimpleX adres aan</target> <target>Maak een SimpleX adres aan</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create a group using a random profile." xml:space="preserve">
<source>Create a group using a random profile.</source>
<target>Maak een groep met een willekeurig profiel.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create an address to let people connect with you." xml:space="preserve"> <trans-unit id="Create an address to let people connect with you." xml:space="preserve">
<source>Create an address to let people connect with you.</source> <source>Create an address to let people connect with you.</source>
<target>Maak een adres aan zodat mensen contact met je kunnen opnemen.</target> <target>Maak een adres aan zodat mensen contact met je kunnen opnemen.</target>
@@ -1800,17 +1763,14 @@ Dit kan niet ongedaan gemaakt worden!</target>
</trans-unit> </trans-unit>
<trans-unit id="Desktop address" xml:space="preserve"> <trans-unit id="Desktop address" xml:space="preserve">
<source>Desktop address</source> <source>Desktop address</source>
<target>Desktop adres</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Desktop app version %@ is not compatible with this app." xml:space="preserve"> <trans-unit id="Desktop app version %@ is not compatible with this app." xml:space="preserve">
<source>Desktop app version %@ is not compatible with this app.</source> <source>Desktop app version %@ is not compatible with this app.</source>
<target>Desktop-app-versie %@ is niet compatibel met deze app.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Desktop devices" xml:space="preserve"> <trans-unit id="Desktop devices" xml:space="preserve">
<source>Desktop devices</source> <source>Desktop devices</source>
<target>Desktop apparaten</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Develop" xml:space="preserve"> <trans-unit id="Develop" xml:space="preserve">
@@ -1905,7 +1865,6 @@ Dit kan niet ongedaan gemaakt worden!</target>
</trans-unit> </trans-unit>
<trans-unit id="Disconnect desktop?" xml:space="preserve"> <trans-unit id="Disconnect desktop?" xml:space="preserve">
<source>Disconnect desktop?</source> <source>Disconnect desktop?</source>
<target>Desktop loskoppelen?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover and join groups" xml:space="preserve"> <trans-unit id="Discover and join groups" xml:space="preserve">
@@ -1913,9 +1872,8 @@ Dit kan niet ongedaan gemaakt worden!</target>
<target>Ontdek en sluit je aan bij groepen</target> <target>Ontdek en sluit je aan bij groepen</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover via local network" xml:space="preserve"> <trans-unit id="Discover on network" xml:space="preserve">
<source>Discover via local network</source> <source>Discover on network</source>
<target>Ontdek via het lokale netwerk</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve"> <trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
@@ -2090,12 +2048,10 @@ Dit kan niet ongedaan gemaakt worden!</target>
</trans-unit> </trans-unit>
<trans-unit id="Encryption re-negotiation error" xml:space="preserve"> <trans-unit id="Encryption re-negotiation error" xml:space="preserve">
<source>Encryption re-negotiation error</source> <source>Encryption re-negotiation error</source>
<target>Fout bij heronderhandeling van codering</target>
<note>message decrypt error item</note> <note>message decrypt error item</note>
</trans-unit> </trans-unit>
<trans-unit id="Encryption re-negotiation failed." xml:space="preserve"> <trans-unit id="Encryption re-negotiation failed." xml:space="preserve">
<source>Encryption re-negotiation failed.</source> <source>Encryption re-negotiation failed.</source>
<target>Opnieuw onderhandelen over de codering is mislukt.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Enter Passcode" xml:space="preserve"> <trans-unit id="Enter Passcode" xml:space="preserve">
@@ -2130,7 +2086,6 @@ Dit kan niet ongedaan gemaakt worden!</target>
</trans-unit> </trans-unit>
<trans-unit id="Enter this device name…" xml:space="preserve"> <trans-unit id="Enter this device name…" xml:space="preserve">
<source>Enter this device name…</source> <source>Enter this device name…</source>
<target>Voer deze apparaatnaam in…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Enter welcome message…" xml:space="preserve"> <trans-unit id="Enter welcome message…" xml:space="preserve">
@@ -2458,11 +2413,6 @@ Dit kan niet ongedaan gemaakt worden!</target>
<target>Snel en niet wachten tot de afzender online is!</target> <target>Snel en niet wachten tot de afzender online is!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Faster joining and more reliable messages." xml:space="preserve">
<source>Faster joining and more reliable messages.</source>
<target>Snellere deelname en betrouwbaardere berichten.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Favorite" xml:space="preserve"> <trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source> <source>Favorite</source>
<target>Favoriet</target> <target>Favoriet</target>
@@ -2558,11 +2508,6 @@ Dit kan niet ongedaan gemaakt worden!</target>
<target>Voor console</target> <target>Voor console</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<target>Desktop gevonden</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="French interface" xml:space="preserve"> <trans-unit id="French interface" xml:space="preserve">
<source>French interface</source> <source>French interface</source>
<target>Franse interface</target> <target>Franse interface</target>
@@ -2888,11 +2833,6 @@ Dit kan niet ongedaan gemaakt worden!</target>
<target>Incognito</target> <target>Incognito</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incognito groups" xml:space="preserve">
<source>Incognito groups</source>
<target>Incognitogroepen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode" xml:space="preserve"> <trans-unit id="Incognito mode" xml:space="preserve">
<source>Incognito mode</source> <source>Incognito mode</source>
<target>Incognito modus</target> <target>Incognito modus</target>
@@ -2925,7 +2865,6 @@ Dit kan niet ongedaan gemaakt worden!</target>
</trans-unit> </trans-unit>
<trans-unit id="Incompatible version" xml:space="preserve"> <trans-unit id="Incompatible version" xml:space="preserve">
<source>Incompatible version</source> <source>Incompatible version</source>
<target>Incompatibele versie</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incorrect passcode" xml:space="preserve"> <trans-unit id="Incorrect passcode" xml:space="preserve">
@@ -3100,7 +3039,6 @@ Dit is jouw link voor groep %@!</target>
</trans-unit> </trans-unit>
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve"> <trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
<source>Keep the app open to use it from desktop</source> <source>Keep the app open to use it from desktop</source>
<target>Houd de app geopend om deze vanaf de desktop te gebruiken</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Keep your connections" xml:space="preserve"> <trans-unit id="Keep your connections" xml:space="preserve">
@@ -3163,19 +3101,12 @@ Dit is jouw link voor groep %@!</target>
<target>Beperkingen</target> <target>Beperkingen</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Link mobile and desktop apps! 🔗" xml:space="preserve">
<source>Link mobile and desktop apps! 🔗</source>
<target>Koppel mobiele en desktop-apps! 🔗</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Linked desktop options" xml:space="preserve"> <trans-unit id="Linked desktop options" xml:space="preserve">
<source>Linked desktop options</source> <source>Linked desktop options</source>
<target>Gekoppelde desktop opties</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Linked desktops" xml:space="preserve"> <trans-unit id="Linked desktops" xml:space="preserve">
<source>Linked desktops</source> <source>Linked desktops</source>
<target>Gelinkte desktops</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Live message!" xml:space="preserve"> <trans-unit id="Live message!" xml:space="preserve">
@@ -3528,11 +3459,6 @@ Dit is jouw link voor groep %@!</target>
<target>Geen ontvangen of verzonden bestanden</target> <target>Geen ontvangen of verzonden bestanden</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Not compatible!" xml:space="preserve">
<source>Not compatible!</source>
<target>Niet compatibel!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve"> <trans-unit id="Notifications" xml:space="preserve">
<source>Notifications</source> <source>Notifications</source>
<target>Meldingen</target> <target>Meldingen</target>
@@ -3754,7 +3680,6 @@ Dit is jouw link voor groep %@!</target>
</trans-unit> </trans-unit>
<trans-unit id="Paste desktop address" xml:space="preserve"> <trans-unit id="Paste desktop address" xml:space="preserve">
<source>Paste desktop address</source> <source>Paste desktop address</source>
<target>Desktopadres plakken</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Paste image" xml:space="preserve"> <trans-unit id="Paste image" xml:space="preserve">
@@ -4354,7 +4279,6 @@ Dit is jouw link voor groep %@!</target>
</trans-unit> </trans-unit>
<trans-unit id="Scan QR code from desktop" xml:space="preserve"> <trans-unit id="Scan QR code from desktop" xml:space="preserve">
<source>Scan QR code from desktop</source> <source>Scan QR code from desktop</source>
<target>Scan QR-code vanaf uw desktop</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Scan code" xml:space="preserve"> <trans-unit id="Scan code" xml:space="preserve">
@@ -4579,7 +4503,6 @@ Dit is jouw link voor groep %@!</target>
</trans-unit> </trans-unit>
<trans-unit id="Session code" xml:space="preserve"> <trans-unit id="Session code" xml:space="preserve">
<source>Session code</source> <source>Session code</source>
<target>Sessie code</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Set 1 day" xml:space="preserve"> <trans-unit id="Set 1 day" xml:space="preserve">
@@ -5081,7 +5004,6 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast.</target>
</trans-unit> </trans-unit>
<trans-unit id="This device name" xml:space="preserve"> <trans-unit id="This device name" xml:space="preserve">
<source>This device name</source> <source>This device name</source>
<target>Deze apparaatnaam</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="This group has over %lld members, delivery receipts are not sent." xml:space="preserve"> <trans-unit id="This group has over %lld members, delivery receipts are not sent." xml:space="preserve">
@@ -5119,11 +5041,6 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast.</target>
<target>Om verbinding te maken, kan uw contact de QR-code scannen of de link in de app gebruiken.</target> <target>Om verbinding te maken, kan uw contact de QR-code scannen of de link in de app gebruiken.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="To hide unwanted messages." xml:space="preserve">
<source>To hide unwanted messages.</source>
<target>Om ongewenste berichten te verbergen.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To make a new connection" xml:space="preserve"> <trans-unit id="To make a new connection" xml:space="preserve">
<source>To make a new connection</source> <source>To make a new connection</source>
<target>Om een nieuwe verbinding te maken</target> <target>Om een nieuwe verbinding te maken</target>
@@ -5285,12 +5202,10 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak
</trans-unit> </trans-unit>
<trans-unit id="Unlink" xml:space="preserve"> <trans-unit id="Unlink" xml:space="preserve">
<source>Unlink</source> <source>Unlink</source>
<target>Ontkoppelen</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Unlink desktop?" xml:space="preserve"> <trans-unit id="Unlink desktop?" xml:space="preserve">
<source>Unlink desktop?</source> <source>Unlink desktop?</source>
<target>Desktop ontkoppelen?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Unlock" xml:space="preserve"> <trans-unit id="Unlock" xml:space="preserve">
@@ -5385,7 +5300,6 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak
</trans-unit> </trans-unit>
<trans-unit id="Use from desktop" xml:space="preserve"> <trans-unit id="Use from desktop" xml:space="preserve">
<source>Use from desktop</source> <source>Use from desktop</source>
<target>Gebruik vanaf desktop</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Use iOS call interface" xml:space="preserve"> <trans-unit id="Use iOS call interface" xml:space="preserve">
@@ -5420,12 +5334,10 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak
</trans-unit> </trans-unit>
<trans-unit id="Verify code with desktop" xml:space="preserve"> <trans-unit id="Verify code with desktop" xml:space="preserve">
<source>Verify code with desktop</source> <source>Verify code with desktop</source>
<target>Code verifiëren met desktop</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Verify connection" xml:space="preserve"> <trans-unit id="Verify connection" xml:space="preserve">
<source>Verify connection</source> <source>Verify connection</source>
<target>Controleer de verbinding</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Verify connection security" xml:space="preserve"> <trans-unit id="Verify connection security" xml:space="preserve">
@@ -5435,7 +5347,6 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak
</trans-unit> </trans-unit>
<trans-unit id="Verify connections" xml:space="preserve"> <trans-unit id="Verify connections" xml:space="preserve">
<source>Verify connections</source> <source>Verify connections</source>
<target>Controleer verbindingen</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Verify security code" xml:space="preserve"> <trans-unit id="Verify security code" xml:space="preserve">
@@ -5448,11 +5359,6 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak
<target>Via browser</target> <target>Via browser</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Via secure quantum resistant protocol." xml:space="preserve">
<source>Via secure quantum resistant protocol.</source>
<target>Via een beveiligd kwantumbestendig protocol.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Video call" xml:space="preserve"> <trans-unit id="Video call" xml:space="preserve">
<source>Video call</source> <source>Video call</source>
<target>video oproep</target> <target>video oproep</target>
@@ -5503,11 +5409,6 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak
<target>Spraakbericht…</target> <target>Spraakbericht…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Waiting for desktop..." xml:space="preserve">
<source>Waiting for desktop...</source>
<target>Wachten op desktop...</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve"> <trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source> <source>Waiting for file</source>
<target>Wachten op bestand</target> <target>Wachten op bestand</target>
@@ -6053,11 +5954,6 @@ SimpleX servers kunnen uw profiel niet zien.</target>
<target>audio oproep (niet e2e versleuteld)</target> <target>audio oproep (niet e2e versleuteld)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="author" xml:space="preserve">
<source>author</source>
<target>auteur</target>
<note>member role</note>
</trans-unit>
<trans-unit id="bad message ID" xml:space="preserve"> <trans-unit id="bad message ID" xml:space="preserve">
<source>bad message ID</source> <source>bad message ID</source>
<target>Onjuiste bericht-ID</target> <target>Onjuiste bericht-ID</target>
@@ -6642,7 +6538,6 @@ SimpleX servers kunnen uw profiel niet zien.</target>
</trans-unit> </trans-unit>
<trans-unit id="v%@" xml:space="preserve"> <trans-unit id="v%@" xml:space="preserve">
<source>v%@</source> <source>v%@</source>
<target>v%@</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="v%@ (%@)" xml:space="preserve"> <trans-unit id="v%@ (%@)" xml:space="preserve">
@@ -6784,7 +6679,6 @@ SimpleX servers kunnen uw profiel niet zien.</target>
</trans-unit> </trans-unit>
<trans-unit id="NSLocalNetworkUsageDescription" xml:space="preserve"> <trans-unit id="NSLocalNetworkUsageDescription" xml:space="preserve">
<source>SimpleX uses local network access to allow using user chat profile via desktop app on the same network.</source> <source>SimpleX uses local network access to allow using user chat profile via desktop app on the same network.</source>
<target>SimpleX maakt gebruik van lokale netwerktoegang om het gebruik van een gebruikerschatprofiel via de desktop-app op hetzelfde netwerk mogelijk te maken.</target>
<note>Privacy - Local Network Usage Description</note> <note>Privacy - Local Network Usage Description</note>
</trans-unit> </trans-unit>
<trans-unit id="NSMicrophoneUsageDescription" xml:space="preserve"> <trans-unit id="NSMicrophoneUsageDescription" xml:space="preserve">
@@ -299,12 +299,10 @@
</trans-unit> </trans-unit>
<trans-unit id="(new)" xml:space="preserve"> <trans-unit id="(new)" xml:space="preserve">
<source>(new)</source> <source>(new)</source>
<target>(nowy)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="(this device v%@)" xml:space="preserve"> <trans-unit id="(this device v%@)" xml:space="preserve">
<source>(this device v%@)</source> <source>(this device v%@)</source>
<target>(to urządzenie v%@)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id=")" xml:space="preserve"> <trans-unit id=")" xml:space="preserve">
@@ -395,15 +393,6 @@
- i więcej!</target> - i więcej!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="- optionally notify deleted contacts.&#10;- profile names with spaces.&#10;- and more!" xml:space="preserve">
<source>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</source>
<target>- opcjonalnie powiadamiaj usunięte kontakty.
- nazwy profili ze spacją.
- i wiele więcej!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve"> <trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve">
<source>- voice messages up to 5 minutes. <source>- voice messages up to 5 minutes.
- custom time to disappear. - custom time to disappear.
@@ -890,7 +879,6 @@
</trans-unit> </trans-unit>
<trans-unit id="Bad desktop address" xml:space="preserve"> <trans-unit id="Bad desktop address" xml:space="preserve">
<source>Bad desktop address</source> <source>Bad desktop address</source>
<target>Zły adres komputera</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Bad message ID" xml:space="preserve"> <trans-unit id="Bad message ID" xml:space="preserve">
@@ -903,11 +891,6 @@
<target>Zły hash wiadomości</target> <target>Zły hash wiadomości</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Better groups" xml:space="preserve">
<source>Better groups</source>
<target>Lepsze grupy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better messages" xml:space="preserve"> <trans-unit id="Better messages" xml:space="preserve">
<source>Better messages</source> <source>Better messages</source>
<target>Lepsze wiadomości</target> <target>Lepsze wiadomości</target>
@@ -918,11 +901,6 @@
<target>Zablokuj</target> <target>Zablokuj</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Block group members" xml:space="preserve">
<source>Block group members</source>
<target>Blokuj członków grupy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Block member" xml:space="preserve"> <trans-unit id="Block member" xml:space="preserve">
<source>Block member</source> <source>Block member</source>
<target>Zablokuj członka</target> <target>Zablokuj członka</target>
@@ -1194,11 +1172,6 @@
<target>Połącz</target> <target>Połącz</target>
<note>server test step</note> <note>server test step</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect automatically" xml:space="preserve">
<source>Connect automatically</source>
<target>Łącz automatycznie</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect incognito" xml:space="preserve"> <trans-unit id="Connect incognito" xml:space="preserve">
<source>Connect incognito</source> <source>Connect incognito</source>
<target>Połącz incognito</target> <target>Połącz incognito</target>
@@ -1206,7 +1179,6 @@
</trans-unit> </trans-unit>
<trans-unit id="Connect to desktop" xml:space="preserve"> <trans-unit id="Connect to desktop" xml:space="preserve">
<source>Connect to desktop</source> <source>Connect to desktop</source>
<target>Połącz do komputera</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect to yourself?" xml:space="preserve"> <trans-unit id="Connect to yourself?" xml:space="preserve">
@@ -1255,12 +1227,10 @@ To jest twój jednorazowy link!</target>
</trans-unit> </trans-unit>
<trans-unit id="Connected desktop" xml:space="preserve"> <trans-unit id="Connected desktop" xml:space="preserve">
<source>Connected desktop</source> <source>Connected desktop</source>
<target>Połączony komputer</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connected to desktop" xml:space="preserve"> <trans-unit id="Connected to desktop" xml:space="preserve">
<source>Connected to desktop</source> <source>Connected to desktop</source>
<target>Połączony do komputera</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connecting server…" xml:space="preserve"> <trans-unit id="Connecting server…" xml:space="preserve">
@@ -1275,7 +1245,6 @@ To jest twój jednorazowy link!</target>
</trans-unit> </trans-unit>
<trans-unit id="Connecting to desktop" xml:space="preserve"> <trans-unit id="Connecting to desktop" xml:space="preserve">
<source>Connecting to desktop</source> <source>Connecting to desktop</source>
<target>Łączenie z komputerem</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connection" xml:space="preserve"> <trans-unit id="Connection" xml:space="preserve">
@@ -1300,7 +1269,6 @@ To jest twój jednorazowy link!</target>
</trans-unit> </trans-unit>
<trans-unit id="Connection terminated" xml:space="preserve"> <trans-unit id="Connection terminated" xml:space="preserve">
<source>Connection terminated</source> <source>Connection terminated</source>
<target>Połączenie zakończone</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connection timeout" xml:space="preserve"> <trans-unit id="Connection timeout" xml:space="preserve">
@@ -1383,11 +1351,6 @@ To jest twój jednorazowy link!</target>
<target>Utwórz adres SimpleX</target> <target>Utwórz adres SimpleX</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create a group using a random profile." xml:space="preserve">
<source>Create a group using a random profile.</source>
<target>Utwórz grupę używając losowego profilu.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create an address to let people connect with you." xml:space="preserve"> <trans-unit id="Create an address to let people connect with you." xml:space="preserve">
<source>Create an address to let people connect with you.</source> <source>Create an address to let people connect with you.</source>
<target>Utwórz adres, aby ludzie mogli się z Tobą połączyć.</target> <target>Utwórz adres, aby ludzie mogli się z Tobą połączyć.</target>
@@ -1800,17 +1763,14 @@ To nie może być cofnięte!</target>
</trans-unit> </trans-unit>
<trans-unit id="Desktop address" xml:space="preserve"> <trans-unit id="Desktop address" xml:space="preserve">
<source>Desktop address</source> <source>Desktop address</source>
<target>Adres komputera</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Desktop app version %@ is not compatible with this app." xml:space="preserve"> <trans-unit id="Desktop app version %@ is not compatible with this app." xml:space="preserve">
<source>Desktop app version %@ is not compatible with this app.</source> <source>Desktop app version %@ is not compatible with this app.</source>
<target>Wersja aplikacji komputerowej %@ nie jest kompatybilna z tą aplikacją.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Desktop devices" xml:space="preserve"> <trans-unit id="Desktop devices" xml:space="preserve">
<source>Desktop devices</source> <source>Desktop devices</source>
<target>Urządzenia komputerowe</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Develop" xml:space="preserve"> <trans-unit id="Develop" xml:space="preserve">
@@ -1905,7 +1865,6 @@ To nie może być cofnięte!</target>
</trans-unit> </trans-unit>
<trans-unit id="Disconnect desktop?" xml:space="preserve"> <trans-unit id="Disconnect desktop?" xml:space="preserve">
<source>Disconnect desktop?</source> <source>Disconnect desktop?</source>
<target>Rozłączyć komputer?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover and join groups" xml:space="preserve"> <trans-unit id="Discover and join groups" xml:space="preserve">
@@ -1913,9 +1872,8 @@ To nie może być cofnięte!</target>
<target>Odkrywaj i dołączaj do grup</target> <target>Odkrywaj i dołączaj do grup</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover via local network" xml:space="preserve"> <trans-unit id="Discover on network" xml:space="preserve">
<source>Discover via local network</source> <source>Discover on network</source>
<target>Odkryj przez sieć lokalną</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve"> <trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
@@ -2090,12 +2048,10 @@ To nie może być cofnięte!</target>
</trans-unit> </trans-unit>
<trans-unit id="Encryption re-negotiation error" xml:space="preserve"> <trans-unit id="Encryption re-negotiation error" xml:space="preserve">
<source>Encryption re-negotiation error</source> <source>Encryption re-negotiation error</source>
<target>Błąd renegocjacji szyfrowania</target>
<note>message decrypt error item</note> <note>message decrypt error item</note>
</trans-unit> </trans-unit>
<trans-unit id="Encryption re-negotiation failed." xml:space="preserve"> <trans-unit id="Encryption re-negotiation failed." xml:space="preserve">
<source>Encryption re-negotiation failed.</source> <source>Encryption re-negotiation failed.</source>
<target>Renegocjacja szyfrowania nie powiodła się.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Enter Passcode" xml:space="preserve"> <trans-unit id="Enter Passcode" xml:space="preserve">
@@ -2130,7 +2086,6 @@ To nie może być cofnięte!</target>
</trans-unit> </trans-unit>
<trans-unit id="Enter this device name…" xml:space="preserve"> <trans-unit id="Enter this device name…" xml:space="preserve">
<source>Enter this device name…</source> <source>Enter this device name…</source>
<target>Podaj nazwę urządzenia…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Enter welcome message…" xml:space="preserve"> <trans-unit id="Enter welcome message…" xml:space="preserve">
@@ -2458,11 +2413,6 @@ To nie może być cofnięte!</target>
<target>Szybko i bez czekania aż nadawca będzie online!</target> <target>Szybko i bez czekania aż nadawca będzie online!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Faster joining and more reliable messages." xml:space="preserve">
<source>Faster joining and more reliable messages.</source>
<target>Szybsze dołączenie i bardziej niezawodne wiadomości.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Favorite" xml:space="preserve"> <trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source> <source>Favorite</source>
<target>Ulubione</target> <target>Ulubione</target>
@@ -2558,11 +2508,6 @@ To nie może być cofnięte!</target>
<target>Dla konsoli</target> <target>Dla konsoli</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<target>Znaleziono komputer</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="French interface" xml:space="preserve"> <trans-unit id="French interface" xml:space="preserve">
<source>French interface</source> <source>French interface</source>
<target>Francuski interfejs</target> <target>Francuski interfejs</target>
@@ -2888,11 +2833,6 @@ To nie może być cofnięte!</target>
<target>Incognito</target> <target>Incognito</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incognito groups" xml:space="preserve">
<source>Incognito groups</source>
<target>Grupy incognito</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode" xml:space="preserve"> <trans-unit id="Incognito mode" xml:space="preserve">
<source>Incognito mode</source> <source>Incognito mode</source>
<target>Tryb incognito</target> <target>Tryb incognito</target>
@@ -2925,7 +2865,6 @@ To nie może być cofnięte!</target>
</trans-unit> </trans-unit>
<trans-unit id="Incompatible version" xml:space="preserve"> <trans-unit id="Incompatible version" xml:space="preserve">
<source>Incompatible version</source> <source>Incompatible version</source>
<target>Niekompatybilna wersja</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incorrect passcode" xml:space="preserve"> <trans-unit id="Incorrect passcode" xml:space="preserve">
@@ -3100,7 +3039,6 @@ To jest twój link do grupy %@!</target>
</trans-unit> </trans-unit>
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve"> <trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
<source>Keep the app open to use it from desktop</source> <source>Keep the app open to use it from desktop</source>
<target>Zostaw aplikację otwartą i używaj ją z komputera</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Keep your connections" xml:space="preserve"> <trans-unit id="Keep your connections" xml:space="preserve">
@@ -3163,19 +3101,12 @@ To jest twój link do grupy %@!</target>
<target>Ograniczenia</target> <target>Ograniczenia</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Link mobile and desktop apps! 🔗" xml:space="preserve">
<source>Link mobile and desktop apps! 🔗</source>
<target>Połącz mobile i komputerowe aplikacje! 🔗</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Linked desktop options" xml:space="preserve"> <trans-unit id="Linked desktop options" xml:space="preserve">
<source>Linked desktop options</source> <source>Linked desktop options</source>
<target>Połączone opcje komputera</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Linked desktops" xml:space="preserve"> <trans-unit id="Linked desktops" xml:space="preserve">
<source>Linked desktops</source> <source>Linked desktops</source>
<target>Połączone komputery</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Live message!" xml:space="preserve"> <trans-unit id="Live message!" xml:space="preserve">
@@ -3528,11 +3459,6 @@ To jest twój link do grupy %@!</target>
<target>Brak odebranych lub wysłanych plików</target> <target>Brak odebranych lub wysłanych plików</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Not compatible!" xml:space="preserve">
<source>Not compatible!</source>
<target>Nie kompatybilny!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve"> <trans-unit id="Notifications" xml:space="preserve">
<source>Notifications</source> <source>Notifications</source>
<target>Powiadomienia</target> <target>Powiadomienia</target>
@@ -3754,7 +3680,6 @@ To jest twój link do grupy %@!</target>
</trans-unit> </trans-unit>
<trans-unit id="Paste desktop address" xml:space="preserve"> <trans-unit id="Paste desktop address" xml:space="preserve">
<source>Paste desktop address</source> <source>Paste desktop address</source>
<target>Wklej adres komputera</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Paste image" xml:space="preserve"> <trans-unit id="Paste image" xml:space="preserve">
@@ -4354,7 +4279,6 @@ To jest twój link do grupy %@!</target>
</trans-unit> </trans-unit>
<trans-unit id="Scan QR code from desktop" xml:space="preserve"> <trans-unit id="Scan QR code from desktop" xml:space="preserve">
<source>Scan QR code from desktop</source> <source>Scan QR code from desktop</source>
<target>Zeskanuj kod QR z komputera</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Scan code" xml:space="preserve"> <trans-unit id="Scan code" xml:space="preserve">
@@ -4579,7 +4503,6 @@ To jest twój link do grupy %@!</target>
</trans-unit> </trans-unit>
<trans-unit id="Session code" xml:space="preserve"> <trans-unit id="Session code" xml:space="preserve">
<source>Session code</source> <source>Session code</source>
<target>Kod sesji</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Set 1 day" xml:space="preserve"> <trans-unit id="Set 1 day" xml:space="preserve">
@@ -5081,7 +5004,6 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom
</trans-unit> </trans-unit>
<trans-unit id="This device name" xml:space="preserve"> <trans-unit id="This device name" xml:space="preserve">
<source>This device name</source> <source>This device name</source>
<target>Nazwa tego urządzenia</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="This group has over %lld members, delivery receipts are not sent." xml:space="preserve"> <trans-unit id="This group has over %lld members, delivery receipts are not sent." xml:space="preserve">
@@ -5119,11 +5041,6 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom
<target>Aby się połączyć, Twój kontakt może zeskanować kod QR lub skorzystać z linku w aplikacji.</target> <target>Aby się połączyć, Twój kontakt może zeskanować kod QR lub skorzystać z linku w aplikacji.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="To hide unwanted messages." xml:space="preserve">
<source>To hide unwanted messages.</source>
<target>Aby ukryć niechciane wiadomości.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To make a new connection" xml:space="preserve"> <trans-unit id="To make a new connection" xml:space="preserve">
<source>To make a new connection</source> <source>To make a new connection</source>
<target>Aby nawiązać nowe połączenie</target> <target>Aby nawiązać nowe połączenie</target>
@@ -5285,12 +5202,10 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc
</trans-unit> </trans-unit>
<trans-unit id="Unlink" xml:space="preserve"> <trans-unit id="Unlink" xml:space="preserve">
<source>Unlink</source> <source>Unlink</source>
<target>Odłącz</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Unlink desktop?" xml:space="preserve"> <trans-unit id="Unlink desktop?" xml:space="preserve">
<source>Unlink desktop?</source> <source>Unlink desktop?</source>
<target>Odłączyć komputer?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Unlock" xml:space="preserve"> <trans-unit id="Unlock" xml:space="preserve">
@@ -5385,7 +5300,6 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc
</trans-unit> </trans-unit>
<trans-unit id="Use from desktop" xml:space="preserve"> <trans-unit id="Use from desktop" xml:space="preserve">
<source>Use from desktop</source> <source>Use from desktop</source>
<target>Użyj z komputera</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Use iOS call interface" xml:space="preserve"> <trans-unit id="Use iOS call interface" xml:space="preserve">
@@ -5420,12 +5334,10 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc
</trans-unit> </trans-unit>
<trans-unit id="Verify code with desktop" xml:space="preserve"> <trans-unit id="Verify code with desktop" xml:space="preserve">
<source>Verify code with desktop</source> <source>Verify code with desktop</source>
<target>Zweryfikuj kod z komputera</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Verify connection" xml:space="preserve"> <trans-unit id="Verify connection" xml:space="preserve">
<source>Verify connection</source> <source>Verify connection</source>
<target>Zweryfikuj połączenie</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Verify connection security" xml:space="preserve"> <trans-unit id="Verify connection security" xml:space="preserve">
@@ -5435,7 +5347,6 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc
</trans-unit> </trans-unit>
<trans-unit id="Verify connections" xml:space="preserve"> <trans-unit id="Verify connections" xml:space="preserve">
<source>Verify connections</source> <source>Verify connections</source>
<target>Zweryfikuj połączenia</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Verify security code" xml:space="preserve"> <trans-unit id="Verify security code" xml:space="preserve">
@@ -5448,11 +5359,6 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc
<target>Przez przeglądarkę</target> <target>Przez przeglądarkę</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Via secure quantum resistant protocol." xml:space="preserve">
<source>Via secure quantum resistant protocol.</source>
<target>Dzięki bezpiecznemu protokołowi odpornego kwantowo.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Video call" xml:space="preserve"> <trans-unit id="Video call" xml:space="preserve">
<source>Video call</source> <source>Video call</source>
<target>Połączenie wideo</target> <target>Połączenie wideo</target>
@@ -5503,11 +5409,6 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc
<target>Wiadomość głosowa…</target> <target>Wiadomość głosowa…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Waiting for desktop..." xml:space="preserve">
<source>Waiting for desktop...</source>
<target>Oczekiwanie na komputer...</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve"> <trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source> <source>Waiting for file</source>
<target>Oczekiwanie na plik</target> <target>Oczekiwanie na plik</target>
@@ -6053,11 +5954,6 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
<target>połączenie audio (nie szyfrowane e2e)</target> <target>połączenie audio (nie szyfrowane e2e)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="author" xml:space="preserve">
<source>author</source>
<target>autor</target>
<note>member role</note>
</trans-unit>
<trans-unit id="bad message ID" xml:space="preserve"> <trans-unit id="bad message ID" xml:space="preserve">
<source>bad message ID</source> <source>bad message ID</source>
<target>zły identyfikator wiadomości</target> <target>zły identyfikator wiadomości</target>
@@ -6642,7 +6538,6 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
</trans-unit> </trans-unit>
<trans-unit id="v%@" xml:space="preserve"> <trans-unit id="v%@" xml:space="preserve">
<source>v%@</source> <source>v%@</source>
<target>v%@</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="v%@ (%@)" xml:space="preserve"> <trans-unit id="v%@ (%@)" xml:space="preserve">
@@ -6784,7 +6679,6 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
</trans-unit> </trans-unit>
<trans-unit id="NSLocalNetworkUsageDescription" xml:space="preserve"> <trans-unit id="NSLocalNetworkUsageDescription" xml:space="preserve">
<source>SimpleX uses local network access to allow using user chat profile via desktop app on the same network.</source> <source>SimpleX uses local network access to allow using user chat profile via desktop app on the same network.</source>
<target>SimpleX używa sieci lokalnej aby pozwolić na dostęp profilom czatu użytkownika przez aplikację komputerową na tej samej sieci.</target>
<note>Privacy - Local Network Usage Description</note> <note>Privacy - Local Network Usage Description</note>
</trans-unit> </trans-unit>
<trans-unit id="NSMicrophoneUsageDescription" xml:space="preserve"> <trans-unit id="NSMicrophoneUsageDescription" xml:space="preserve">
@@ -89,7 +89,6 @@
</trans-unit> </trans-unit>
<trans-unit id="%@ and %@" xml:space="preserve"> <trans-unit id="%@ and %@" xml:space="preserve">
<source>%@ and %@</source> <source>%@ and %@</source>
<target>%@ и %@</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="%@ and %@ connected" xml:space="preserve"> <trans-unit id="%@ and %@ connected" xml:space="preserve">
@@ -104,7 +103,6 @@
</trans-unit> </trans-unit>
<trans-unit id="%@ connected" xml:space="preserve"> <trans-unit id="%@ connected" xml:space="preserve">
<source>%@ connected</source> <source>%@ connected</source>
<target>%@ соединен(а)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="%@ is connected!" xml:space="preserve"> <trans-unit id="%@ is connected!" xml:space="preserve">
@@ -134,7 +132,6 @@
</trans-unit> </trans-unit>
<trans-unit id="%@, %@ and %lld members" xml:space="preserve"> <trans-unit id="%@, %@ and %lld members" xml:space="preserve">
<source>%@, %@ and %lld members</source> <source>%@, %@ and %lld members</source>
<target>%@, %@ и %lld членов группы</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="%@, %@ and %lld other members connected" xml:space="preserve"> <trans-unit id="%@, %@ and %lld other members connected" xml:space="preserve">
@@ -204,7 +201,6 @@
</trans-unit> </trans-unit>
<trans-unit id="%lld group events" xml:space="preserve"> <trans-unit id="%lld group events" xml:space="preserve">
<source>%lld group events</source> <source>%lld group events</source>
<target>%lld событий</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="%lld members" xml:space="preserve"> <trans-unit id="%lld members" xml:space="preserve">
@@ -214,17 +210,14 @@
</trans-unit> </trans-unit>
<trans-unit id="%lld messages blocked" xml:space="preserve"> <trans-unit id="%lld messages blocked" xml:space="preserve">
<source>%lld messages blocked</source> <source>%lld messages blocked</source>
<target>%lld сообщений заблокировано</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="%lld messages marked deleted" xml:space="preserve"> <trans-unit id="%lld messages marked deleted" xml:space="preserve">
<source>%lld messages marked deleted</source> <source>%lld messages marked deleted</source>
<target>%lld сообщений помечено удалёнными</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="%lld messages moderated by %@" xml:space="preserve"> <trans-unit id="%lld messages moderated by %@" xml:space="preserve">
<source>%lld messages moderated by %@</source> <source>%lld messages moderated by %@</source>
<target>%lld сообщений модерировано членом %@</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="%lld minutes" xml:space="preserve"> <trans-unit id="%lld minutes" xml:space="preserve">
@@ -299,12 +292,10 @@
</trans-unit> </trans-unit>
<trans-unit id="(new)" xml:space="preserve"> <trans-unit id="(new)" xml:space="preserve">
<source>(new)</source> <source>(new)</source>
<target>(новое)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="(this device v%@)" xml:space="preserve"> <trans-unit id="(this device v%@)" xml:space="preserve">
<source>(this device v%@)</source> <source>(this device v%@)</source>
<target>(это устройство v%@)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id=")" xml:space="preserve"> <trans-unit id=")" xml:space="preserve">
@@ -392,15 +383,6 @@
- and more!</source> - and more!</source>
<target>- более стабильная доставка сообщений. <target>- более стабильная доставка сообщений.
- немного улучшенные группы. - немного улучшенные группы.
- и прочее!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="- optionally notify deleted contacts.&#10;- profile names with spaces.&#10;- and more!" xml:space="preserve">
<source>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</source>
<target>- опционально уведомляйте удалённые контакты.
- имена профилей с пробелами.
- и прочее!</target> - и прочее!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
@@ -420,7 +402,6 @@
</trans-unit> </trans-unit>
<trans-unit id="0 sec" xml:space="preserve"> <trans-unit id="0 sec" xml:space="preserve">
<source>0 sec</source> <source>0 sec</source>
<target>0 сек</target>
<note>time to disappear</note> <note>time to disappear</note>
</trans-unit> </trans-unit>
<trans-unit id="0s" xml:space="preserve"> <trans-unit id="0s" xml:space="preserve">
@@ -650,7 +631,6 @@
</trans-unit> </trans-unit>
<trans-unit id="All new messages from %@ will be hidden!" xml:space="preserve"> <trans-unit id="All new messages from %@ will be hidden!" xml:space="preserve">
<source>All new messages from %@ will be hidden!</source> <source>All new messages from %@ will be hidden!</source>
<target>Все новые сообщения от %@ будут скрыты!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve"> <trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -760,12 +740,10 @@
</trans-unit> </trans-unit>
<trans-unit id="Already connecting!" xml:space="preserve"> <trans-unit id="Already connecting!" xml:space="preserve">
<source>Already connecting!</source> <source>Already connecting!</source>
<target>Уже соединяется!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Already joining the group!" xml:space="preserve"> <trans-unit id="Already joining the group!" xml:space="preserve">
<source>Already joining the group!</source> <source>Already joining the group!</source>
<target>Вступление в группу уже начато!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Always use relay" xml:space="preserve"> <trans-unit id="Always use relay" xml:space="preserve">
@@ -890,7 +868,6 @@
</trans-unit> </trans-unit>
<trans-unit id="Bad desktop address" xml:space="preserve"> <trans-unit id="Bad desktop address" xml:space="preserve">
<source>Bad desktop address</source> <source>Bad desktop address</source>
<target>Неверный адрес компьютера</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Bad message ID" xml:space="preserve"> <trans-unit id="Bad message ID" xml:space="preserve">
@@ -903,11 +880,6 @@
<target>Ошибка хэш сообщения</target> <target>Ошибка хэш сообщения</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Better groups" xml:space="preserve">
<source>Better groups</source>
<target>Улучшенные группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better messages" xml:space="preserve"> <trans-unit id="Better messages" xml:space="preserve">
<source>Better messages</source> <source>Better messages</source>
<target>Улучшенные сообщения</target> <target>Улучшенные сообщения</target>
@@ -915,22 +887,14 @@
</trans-unit> </trans-unit>
<trans-unit id="Block" xml:space="preserve"> <trans-unit id="Block" xml:space="preserve">
<source>Block</source> <source>Block</source>
<target>Заблокировать</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Block group members" xml:space="preserve">
<source>Block group members</source>
<target>Блокируйте членов группы</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Block member" xml:space="preserve"> <trans-unit id="Block member" xml:space="preserve">
<source>Block member</source> <source>Block member</source>
<target>Заблокировать члена группы</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Block member?" xml:space="preserve"> <trans-unit id="Block member?" xml:space="preserve">
<source>Block member?</source> <source>Block member?</source>
<target>Заблокировать члена группы?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Both you and your contact can add message reactions." xml:space="preserve"> <trans-unit id="Both you and your contact can add message reactions." xml:space="preserve">
@@ -1194,11 +1158,6 @@
<target>Соединиться</target> <target>Соединиться</target>
<note>server test step</note> <note>server test step</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect automatically" xml:space="preserve">
<source>Connect automatically</source>
<target>Соединяться автоматически</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect incognito" xml:space="preserve"> <trans-unit id="Connect incognito" xml:space="preserve">
<source>Connect incognito</source> <source>Connect incognito</source>
<target>Соединиться Инкогнито</target> <target>Соединиться Инкогнито</target>
@@ -1206,31 +1165,24 @@
</trans-unit> </trans-unit>
<trans-unit id="Connect to desktop" xml:space="preserve"> <trans-unit id="Connect to desktop" xml:space="preserve">
<source>Connect to desktop</source> <source>Connect to desktop</source>
<target>Подключиться к компьютеру</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect to yourself?" xml:space="preserve"> <trans-unit id="Connect to yourself?" xml:space="preserve">
<source>Connect to yourself?</source> <source>Connect to yourself?</source>
<target>Соединиться с самим собой?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect to yourself?&#10;This is your own SimpleX address!" xml:space="preserve"> <trans-unit id="Connect to yourself?&#10;This is your own SimpleX address!" xml:space="preserve">
<source>Connect to yourself? <source>Connect to yourself?
This is your own SimpleX address!</source> This is your own SimpleX address!</source>
<target>Соединиться с самим собой?
Это ваш собственный адрес SimpleX!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect to yourself?&#10;This is your own one-time link!" xml:space="preserve"> <trans-unit id="Connect to yourself?&#10;This is your own one-time link!" xml:space="preserve">
<source>Connect to yourself? <source>Connect to yourself?
This is your own one-time link!</source> This is your own one-time link!</source>
<target>Соединиться с самим собой?
Это ваша собственная одноразовая ссылка!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect via contact address" xml:space="preserve"> <trans-unit id="Connect via contact address" xml:space="preserve">
<source>Connect via contact address</source> <source>Connect via contact address</source>
<target>Соединиться через адрес</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect via link" xml:space="preserve"> <trans-unit id="Connect via link" xml:space="preserve">
@@ -1250,17 +1202,14 @@ This is your own one-time link!</source>
</trans-unit> </trans-unit>
<trans-unit id="Connect with %@" xml:space="preserve"> <trans-unit id="Connect with %@" xml:space="preserve">
<source>Connect with %@</source> <source>Connect with %@</source>
<target>Соединиться с %@</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connected desktop" xml:space="preserve"> <trans-unit id="Connected desktop" xml:space="preserve">
<source>Connected desktop</source> <source>Connected desktop</source>
<target>Подключенный компьютер</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connected to desktop" xml:space="preserve"> <trans-unit id="Connected to desktop" xml:space="preserve">
<source>Connected to desktop</source> <source>Connected to desktop</source>
<target>Компьютер подключен</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connecting server…" xml:space="preserve"> <trans-unit id="Connecting server…" xml:space="preserve">
@@ -1275,7 +1224,6 @@ This is your own one-time link!</source>
</trans-unit> </trans-unit>
<trans-unit id="Connecting to desktop" xml:space="preserve"> <trans-unit id="Connecting to desktop" xml:space="preserve">
<source>Connecting to desktop</source> <source>Connecting to desktop</source>
<target>Подключение к компьютеру</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connection" xml:space="preserve"> <trans-unit id="Connection" xml:space="preserve">
@@ -1300,7 +1248,6 @@ This is your own one-time link!</source>
</trans-unit> </trans-unit>
<trans-unit id="Connection terminated" xml:space="preserve"> <trans-unit id="Connection terminated" xml:space="preserve">
<source>Connection terminated</source> <source>Connection terminated</source>
<target>Подключение прервано</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Connection timeout" xml:space="preserve"> <trans-unit id="Connection timeout" xml:space="preserve">
@@ -1370,7 +1317,6 @@ This is your own one-time link!</source>
</trans-unit> </trans-unit>
<trans-unit id="Correct name to %@?" xml:space="preserve"> <trans-unit id="Correct name to %@?" xml:space="preserve">
<source>Correct name to %@?</source> <source>Correct name to %@?</source>
<target>Исправить имя на %@?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create" xml:space="preserve"> <trans-unit id="Create" xml:space="preserve">
@@ -1383,11 +1329,6 @@ This is your own one-time link!</source>
<target>Создать адрес SimpleX</target> <target>Создать адрес SimpleX</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create a group using a random profile." xml:space="preserve">
<source>Create a group using a random profile.</source>
<target>Создайте группу, используя случайный профиль.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create an address to let people connect with you." xml:space="preserve"> <trans-unit id="Create an address to let people connect with you." xml:space="preserve">
<source>Create an address to let people connect with you.</source> <source>Create an address to let people connect with you.</source>
<target>Создайте адрес, чтобы можно было соединиться с вами.</target> <target>Создайте адрес, чтобы можно было соединиться с вами.</target>
@@ -1400,7 +1341,6 @@ This is your own one-time link!</source>
</trans-unit> </trans-unit>
<trans-unit id="Create group" xml:space="preserve"> <trans-unit id="Create group" xml:space="preserve">
<source>Create group</source> <source>Create group</source>
<target>Создать группу</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create group link" xml:space="preserve"> <trans-unit id="Create group link" xml:space="preserve">
@@ -1425,7 +1365,6 @@ This is your own one-time link!</source>
</trans-unit> </trans-unit>
<trans-unit id="Create profile" xml:space="preserve"> <trans-unit id="Create profile" xml:space="preserve">
<source>Create profile</source> <source>Create profile</source>
<target>Создать профиль</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create queue" xml:space="preserve"> <trans-unit id="Create queue" xml:space="preserve">
@@ -1588,7 +1527,6 @@ This is your own one-time link!</source>
</trans-unit> </trans-unit>
<trans-unit id="Delete %lld messages?" xml:space="preserve"> <trans-unit id="Delete %lld messages?" xml:space="preserve">
<source>Delete %lld messages?</source> <source>Delete %lld messages?</source>
<target>Удалить %lld сообщений?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Delete Contact" xml:space="preserve"> <trans-unit id="Delete Contact" xml:space="preserve">
@@ -1618,7 +1556,6 @@ This is your own one-time link!</source>
</trans-unit> </trans-unit>
<trans-unit id="Delete and notify contact" xml:space="preserve"> <trans-unit id="Delete and notify contact" xml:space="preserve">
<source>Delete and notify contact</source> <source>Delete and notify contact</source>
<target>Удалить и уведомить контакт</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Delete archive" xml:space="preserve"> <trans-unit id="Delete archive" xml:space="preserve">
@@ -1654,8 +1591,6 @@ This is your own one-time link!</source>
<trans-unit id="Delete contact?&#10;This cannot be undone!" xml:space="preserve"> <trans-unit id="Delete contact?&#10;This cannot be undone!" xml:space="preserve">
<source>Delete contact? <source>Delete contact?
This cannot be undone!</source> This cannot be undone!</source>
<target>Удалить контакт?
Это не может быть отменено!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Delete database" xml:space="preserve"> <trans-unit id="Delete database" xml:space="preserve">
@@ -1800,17 +1735,14 @@ This cannot be undone!</source>
</trans-unit> </trans-unit>
<trans-unit id="Desktop address" xml:space="preserve"> <trans-unit id="Desktop address" xml:space="preserve">
<source>Desktop address</source> <source>Desktop address</source>
<target>Адрес компьютера</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Desktop app version %@ is not compatible with this app." xml:space="preserve"> <trans-unit id="Desktop app version %@ is not compatible with this app." xml:space="preserve">
<source>Desktop app version %@ is not compatible with this app.</source> <source>Desktop app version %@ is not compatible with this app.</source>
<target>Версия настольного приложения %@ несовместима с этим приложением.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Desktop devices" xml:space="preserve"> <trans-unit id="Desktop devices" xml:space="preserve">
<source>Desktop devices</source> <source>Desktop devices</source>
<target>Компьютеры</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Develop" xml:space="preserve"> <trans-unit id="Develop" xml:space="preserve">
@@ -1905,7 +1837,6 @@ This cannot be undone!</source>
</trans-unit> </trans-unit>
<trans-unit id="Disconnect desktop?" xml:space="preserve"> <trans-unit id="Disconnect desktop?" xml:space="preserve">
<source>Disconnect desktop?</source> <source>Disconnect desktop?</source>
<target>Отключить компьютер?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover and join groups" xml:space="preserve"> <trans-unit id="Discover and join groups" xml:space="preserve">
@@ -1913,9 +1844,8 @@ This cannot be undone!</source>
<target>Найдите и вступите в группы</target> <target>Найдите и вступите в группы</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover via local network" xml:space="preserve"> <trans-unit id="Discover on network" xml:space="preserve">
<source>Discover via local network</source> <source>Discover on network</source>
<target>Обнаружение по локальной сети</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve"> <trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
@@ -2090,12 +2020,10 @@ This cannot be undone!</source>
</trans-unit> </trans-unit>
<trans-unit id="Encryption re-negotiation error" xml:space="preserve"> <trans-unit id="Encryption re-negotiation error" xml:space="preserve">
<source>Encryption re-negotiation error</source> <source>Encryption re-negotiation error</source>
<target>Ошибка нового соглашения о шифровании</target>
<note>message decrypt error item</note> <note>message decrypt error item</note>
</trans-unit> </trans-unit>
<trans-unit id="Encryption re-negotiation failed." xml:space="preserve"> <trans-unit id="Encryption re-negotiation failed." xml:space="preserve">
<source>Encryption re-negotiation failed.</source> <source>Encryption re-negotiation failed.</source>
<target>Ошибка нового соглашения о шифровании.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Enter Passcode" xml:space="preserve"> <trans-unit id="Enter Passcode" xml:space="preserve">
@@ -2110,7 +2038,6 @@ This cannot be undone!</source>
</trans-unit> </trans-unit>
<trans-unit id="Enter group name…" xml:space="preserve"> <trans-unit id="Enter group name…" xml:space="preserve">
<source>Enter group name…</source> <source>Enter group name…</source>
<target>Введите имя группы…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Enter passphrase…" xml:space="preserve"> <trans-unit id="Enter passphrase…" xml:space="preserve">
@@ -2130,7 +2057,6 @@ This cannot be undone!</source>
</trans-unit> </trans-unit>
<trans-unit id="Enter this device name…" xml:space="preserve"> <trans-unit id="Enter this device name…" xml:space="preserve">
<source>Enter this device name…</source> <source>Enter this device name…</source>
<target>Введите имя этого устройства…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Enter welcome message…" xml:space="preserve"> <trans-unit id="Enter welcome message…" xml:space="preserve">
@@ -2145,7 +2071,6 @@ This cannot be undone!</source>
</trans-unit> </trans-unit>
<trans-unit id="Enter your name…" xml:space="preserve"> <trans-unit id="Enter your name…" xml:space="preserve">
<source>Enter your name…</source> <source>Enter your name…</source>
<target>Введите ваше имя…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Error" xml:space="preserve"> <trans-unit id="Error" xml:space="preserve">
@@ -2205,7 +2130,6 @@ This cannot be undone!</source>
</trans-unit> </trans-unit>
<trans-unit id="Error creating member contact" xml:space="preserve"> <trans-unit id="Error creating member contact" xml:space="preserve">
<source>Error creating member contact</source> <source>Error creating member contact</source>
<target>Ошибка создания контакта с членом группы</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Error creating profile!" xml:space="preserve"> <trans-unit id="Error creating profile!" xml:space="preserve">
@@ -2340,7 +2264,6 @@ This cannot be undone!</source>
</trans-unit> </trans-unit>
<trans-unit id="Error sending member contact invitation" xml:space="preserve"> <trans-unit id="Error sending member contact invitation" xml:space="preserve">
<source>Error sending member contact invitation</source> <source>Error sending member contact invitation</source>
<target>Ошибка отправки приглашения члену группы</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Error sending message" xml:space="preserve"> <trans-unit id="Error sending message" xml:space="preserve">
@@ -2425,7 +2348,6 @@ This cannot be undone!</source>
</trans-unit> </trans-unit>
<trans-unit id="Expand" xml:space="preserve"> <trans-unit id="Expand" xml:space="preserve">
<source>Expand</source> <source>Expand</source>
<target>Раскрыть</target>
<note>chat item action</note> <note>chat item action</note>
</trans-unit> </trans-unit>
<trans-unit id="Export database" xml:space="preserve"> <trans-unit id="Export database" xml:space="preserve">
@@ -2458,11 +2380,6 @@ This cannot be undone!</source>
<target>Быстрые и не нужно ждать, когда отправитель онлайн!</target> <target>Быстрые и не нужно ждать, когда отправитель онлайн!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Faster joining and more reliable messages." xml:space="preserve">
<source>Faster joining and more reliable messages.</source>
<target>Быстрое вступление и надежная доставка сообщений.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Favorite" xml:space="preserve"> <trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source> <source>Favorite</source>
<target>Избранный</target> <target>Избранный</target>
@@ -2558,11 +2475,6 @@ This cannot be undone!</source>
<target>Для консоли</target> <target>Для консоли</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<target>Компьютер найден</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="French interface" xml:space="preserve"> <trans-unit id="French interface" xml:space="preserve">
<source>French interface</source> <source>French interface</source>
<target>Французский интерфейс</target> <target>Французский интерфейс</target>
@@ -2585,7 +2497,6 @@ This cannot be undone!</source>
</trans-unit> </trans-unit>
<trans-unit id="Fully decentralized visible only to members." xml:space="preserve"> <trans-unit id="Fully decentralized visible only to members." xml:space="preserve">
<source>Fully decentralized visible only to members.</source> <source>Fully decentralized visible only to members.</source>
<target>Группа полностью децентрализована – она видна только членам.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Fully re-implemented - work in background!" xml:space="preserve"> <trans-unit id="Fully re-implemented - work in background!" xml:space="preserve">
@@ -2610,12 +2521,10 @@ This cannot be undone!</source>
</trans-unit> </trans-unit>
<trans-unit id="Group already exists" xml:space="preserve"> <trans-unit id="Group already exists" xml:space="preserve">
<source>Group already exists</source> <source>Group already exists</source>
<target>Группа уже существует</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group already exists!" xml:space="preserve"> <trans-unit id="Group already exists!" xml:space="preserve">
<source>Group already exists!</source> <source>Group already exists!</source>
<target>Группа уже существует!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group display name" xml:space="preserve"> <trans-unit id="Group display name" xml:space="preserve">
@@ -2888,11 +2797,6 @@ This cannot be undone!</source>
<target>Инкогнито</target> <target>Инкогнито</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incognito groups" xml:space="preserve">
<source>Incognito groups</source>
<target>Инкогнито группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode" xml:space="preserve"> <trans-unit id="Incognito mode" xml:space="preserve">
<source>Incognito mode</source> <source>Incognito mode</source>
<target>Режим Инкогнито</target> <target>Режим Инкогнито</target>
@@ -2925,7 +2829,6 @@ This cannot be undone!</source>
</trans-unit> </trans-unit>
<trans-unit id="Incompatible version" xml:space="preserve"> <trans-unit id="Incompatible version" xml:space="preserve">
<source>Incompatible version</source> <source>Incompatible version</source>
<target>Несовместимая версия</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incorrect passcode" xml:space="preserve"> <trans-unit id="Incorrect passcode" xml:space="preserve">
@@ -2977,7 +2880,6 @@ This cannot be undone!</source>
</trans-unit> </trans-unit>
<trans-unit id="Invalid name!" xml:space="preserve"> <trans-unit id="Invalid name!" xml:space="preserve">
<source>Invalid name!</source> <source>Invalid name!</source>
<target>Неверное имя!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Invalid server address!" xml:space="preserve"> <trans-unit id="Invalid server address!" xml:space="preserve">
@@ -3073,7 +2975,6 @@ This cannot be undone!</source>
</trans-unit> </trans-unit>
<trans-unit id="Join group?" xml:space="preserve"> <trans-unit id="Join group?" xml:space="preserve">
<source>Join group?</source> <source>Join group?</source>
<target>Вступить в группу?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Join incognito" xml:space="preserve"> <trans-unit id="Join incognito" xml:space="preserve">
@@ -3083,14 +2984,11 @@ This cannot be undone!</source>
</trans-unit> </trans-unit>
<trans-unit id="Join with current profile" xml:space="preserve"> <trans-unit id="Join with current profile" xml:space="preserve">
<source>Join with current profile</source> <source>Join with current profile</source>
<target>Вступить с активным профилем</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Join your group?&#10;This is your link for group %@!" xml:space="preserve"> <trans-unit id="Join your group?&#10;This is your link for group %@!" xml:space="preserve">
<source>Join your group? <source>Join your group?
This is your link for group %@!</source> This is your link for group %@!</source>
<target>Вступить в вашу группу?
Это ваша ссылка на группу %@!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Joining group" xml:space="preserve"> <trans-unit id="Joining group" xml:space="preserve">
@@ -3100,7 +2998,6 @@ This is your link for group %@!</source>
</trans-unit> </trans-unit>
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve"> <trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
<source>Keep the app open to use it from desktop</source> <source>Keep the app open to use it from desktop</source>
<target>Оставьте приложение открытым, чтобы использовать его с компьютера</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Keep your connections" xml:space="preserve"> <trans-unit id="Keep your connections" xml:space="preserve">
@@ -3163,19 +3060,12 @@ This is your link for group %@!</source>
<target>Ограничения</target> <target>Ограничения</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Link mobile and desktop apps! 🔗" xml:space="preserve">
<source>Link mobile and desktop apps! 🔗</source>
<target>Свяжите мобильное и настольное приложения! 🔗</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Linked desktop options" xml:space="preserve"> <trans-unit id="Linked desktop options" xml:space="preserve">
<source>Linked desktop options</source> <source>Linked desktop options</source>
<target>Опции связанных компьютеров</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Linked desktops" xml:space="preserve"> <trans-unit id="Linked desktops" xml:space="preserve">
<source>Linked desktops</source> <source>Linked desktops</source>
<target>Связанные компьютеры</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Live message!" xml:space="preserve"> <trans-unit id="Live message!" xml:space="preserve">
@@ -3330,7 +3220,6 @@ This is your link for group %@!</source>
</trans-unit> </trans-unit>
<trans-unit id="Messages from %@ will be shown!" xml:space="preserve"> <trans-unit id="Messages from %@ will be shown!" xml:space="preserve">
<source>Messages from %@ will be shown!</source> <source>Messages from %@ will be shown!</source>
<target>Сообщения от %@ будут показаны!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Migrating database archive…" xml:space="preserve"> <trans-unit id="Migrating database archive…" xml:space="preserve">
@@ -3528,11 +3417,6 @@ This is your link for group %@!</source>
<target>Нет полученных или отправленных файлов</target> <target>Нет полученных или отправленных файлов</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Not compatible!" xml:space="preserve">
<source>Not compatible!</source>
<target>Несовместимая версия!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve"> <trans-unit id="Notifications" xml:space="preserve">
<source>Notifications</source> <source>Notifications</source>
<target>Уведомления</target> <target>Уведомления</target>
@@ -3669,7 +3553,6 @@ This is your link for group %@!</source>
</trans-unit> </trans-unit>
<trans-unit id="Open" xml:space="preserve"> <trans-unit id="Open" xml:space="preserve">
<source>Open</source> <source>Open</source>
<target>Открыть</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Open Settings" xml:space="preserve"> <trans-unit id="Open Settings" xml:space="preserve">
@@ -3689,7 +3572,6 @@ This is your link for group %@!</source>
</trans-unit> </trans-unit>
<trans-unit id="Open group" xml:space="preserve"> <trans-unit id="Open group" xml:space="preserve">
<source>Open group</source> <source>Open group</source>
<target>Открыть группу</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Open user profiles" xml:space="preserve"> <trans-unit id="Open user profiles" xml:space="preserve">
@@ -3754,7 +3636,6 @@ This is your link for group %@!</source>
</trans-unit> </trans-unit>
<trans-unit id="Paste desktop address" xml:space="preserve"> <trans-unit id="Paste desktop address" xml:space="preserve">
<source>Paste desktop address</source> <source>Paste desktop address</source>
<target>Вставить адрес компьютера</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Paste image" xml:space="preserve"> <trans-unit id="Paste image" xml:space="preserve">
@@ -3904,12 +3785,10 @@ This is your link for group %@!</source>
</trans-unit> </trans-unit>
<trans-unit id="Profile name" xml:space="preserve"> <trans-unit id="Profile name" xml:space="preserve">
<source>Profile name</source> <source>Profile name</source>
<target>Имя профиля</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Profile name:" xml:space="preserve"> <trans-unit id="Profile name:" xml:space="preserve">
<source>Profile name:</source> <source>Profile name:</source>
<target>Имя профиля:</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Profile password" xml:space="preserve"> <trans-unit id="Profile password" xml:space="preserve">
@@ -4159,12 +4038,10 @@ This is your link for group %@!</source>
</trans-unit> </trans-unit>
<trans-unit id="Repeat connection request?" xml:space="preserve"> <trans-unit id="Repeat connection request?" xml:space="preserve">
<source>Repeat connection request?</source> <source>Repeat connection request?</source>
<target>Повторить запрос на соединение?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Repeat join request?" xml:space="preserve"> <trans-unit id="Repeat join request?" xml:space="preserve">
<source>Repeat join request?</source> <source>Repeat join request?</source>
<target>Повторить запрос на вступление?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Reply" xml:space="preserve"> <trans-unit id="Reply" xml:space="preserve">
@@ -4354,7 +4231,6 @@ This is your link for group %@!</source>
</trans-unit> </trans-unit>
<trans-unit id="Scan QR code from desktop" xml:space="preserve"> <trans-unit id="Scan QR code from desktop" xml:space="preserve">
<source>Scan QR code from desktop</source> <source>Scan QR code from desktop</source>
<target>Сканировать QR код с компьютера</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Scan code" xml:space="preserve"> <trans-unit id="Scan code" xml:space="preserve">
@@ -4439,7 +4315,6 @@ This is your link for group %@!</source>
</trans-unit> </trans-unit>
<trans-unit id="Send direct message to connect" xml:space="preserve"> <trans-unit id="Send direct message to connect" xml:space="preserve">
<source>Send direct message to connect</source> <source>Send direct message to connect</source>
<target>Отправьте сообщение чтобы соединиться</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Send disappearing message" xml:space="preserve"> <trans-unit id="Send disappearing message" xml:space="preserve">
@@ -4579,7 +4454,6 @@ This is your link for group %@!</source>
</trans-unit> </trans-unit>
<trans-unit id="Session code" xml:space="preserve"> <trans-unit id="Session code" xml:space="preserve">
<source>Session code</source> <source>Session code</source>
<target>Код сессии</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Set 1 day" xml:space="preserve"> <trans-unit id="Set 1 day" xml:space="preserve">
@@ -4894,7 +4768,6 @@ This is your link for group %@!</source>
</trans-unit> </trans-unit>
<trans-unit id="Tap to Connect" xml:space="preserve"> <trans-unit id="Tap to Connect" xml:space="preserve">
<source>Tap to Connect</source> <source>Tap to Connect</source>
<target>Нажмите чтобы соединиться</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Tap to activate profile." xml:space="preserve"> <trans-unit id="Tap to activate profile." xml:space="preserve">
@@ -5081,7 +4954,6 @@ It can happen because of some bug or when the connection is compromised.</source
</trans-unit> </trans-unit>
<trans-unit id="This device name" xml:space="preserve"> <trans-unit id="This device name" xml:space="preserve">
<source>This device name</source> <source>This device name</source>
<target>Имя этого устройства</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="This group has over %lld members, delivery receipts are not sent." xml:space="preserve"> <trans-unit id="This group has over %lld members, delivery receipts are not sent." xml:space="preserve">
@@ -5096,12 +4968,10 @@ It can happen because of some bug or when the connection is compromised.</source
</trans-unit> </trans-unit>
<trans-unit id="This is your own SimpleX address!" xml:space="preserve"> <trans-unit id="This is your own SimpleX address!" xml:space="preserve">
<source>This is your own SimpleX address!</source> <source>This is your own SimpleX address!</source>
<target>Это ваш собственный адрес SimpleX!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="This is your own one-time link!" xml:space="preserve"> <trans-unit id="This is your own one-time link!" xml:space="preserve">
<source>This is your own one-time link!</source> <source>This is your own one-time link!</source>
<target>Это ваша собственная одноразовая ссылка!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="This setting applies to messages in your current chat profile **%@**." xml:space="preserve"> <trans-unit id="This setting applies to messages in your current chat profile **%@**." xml:space="preserve">
@@ -5119,11 +4989,6 @@ It can happen because of some bug or when the connection is compromised.</source
<target>Чтобы соединиться с Вами, Ваш контакт может отсканировать QR-код или использовать ссылку в приложении.</target> <target>Чтобы соединиться с Вами, Ваш контакт может отсканировать QR-код или использовать ссылку в приложении.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="To hide unwanted messages." xml:space="preserve">
<source>To hide unwanted messages.</source>
<target>Чтобы скрыть нежелательные сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To make a new connection" xml:space="preserve"> <trans-unit id="To make a new connection" xml:space="preserve">
<source>To make a new connection</source> <source>To make a new connection</source>
<target>Чтобы соединиться</target> <target>Чтобы соединиться</target>
@@ -5208,17 +5073,14 @@ You will be prompted to complete authentication before this feature is enabled.<
</trans-unit> </trans-unit>
<trans-unit id="Unblock" xml:space="preserve"> <trans-unit id="Unblock" xml:space="preserve">
<source>Unblock</source> <source>Unblock</source>
<target>Разблокировать</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Unblock member" xml:space="preserve"> <trans-unit id="Unblock member" xml:space="preserve">
<source>Unblock member</source> <source>Unblock member</source>
<target>Разблокировать члена группы</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Unblock member?" xml:space="preserve"> <trans-unit id="Unblock member?" xml:space="preserve">
<source>Unblock member?</source> <source>Unblock member?</source>
<target>Разблокировать члена группы?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Unexpected error: %@" xml:space="preserve"> <trans-unit id="Unexpected error: %@" xml:space="preserve">
@@ -5285,12 +5147,10 @@ To connect, please ask your contact to create another connection link and check
</trans-unit> </trans-unit>
<trans-unit id="Unlink" xml:space="preserve"> <trans-unit id="Unlink" xml:space="preserve">
<source>Unlink</source> <source>Unlink</source>
<target>Забыть</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Unlink desktop?" xml:space="preserve"> <trans-unit id="Unlink desktop?" xml:space="preserve">
<source>Unlink desktop?</source> <source>Unlink desktop?</source>
<target>Забыть компьютер?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Unlock" xml:space="preserve"> <trans-unit id="Unlock" xml:space="preserve">
@@ -5385,7 +5245,6 @@ To connect, please ask your contact to create another connection link and check
</trans-unit> </trans-unit>
<trans-unit id="Use from desktop" xml:space="preserve"> <trans-unit id="Use from desktop" xml:space="preserve">
<source>Use from desktop</source> <source>Use from desktop</source>
<target>Использовать с компьютера</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Use iOS call interface" xml:space="preserve"> <trans-unit id="Use iOS call interface" xml:space="preserve">
@@ -5420,12 +5279,10 @@ To connect, please ask your contact to create another connection link and check
</trans-unit> </trans-unit>
<trans-unit id="Verify code with desktop" xml:space="preserve"> <trans-unit id="Verify code with desktop" xml:space="preserve">
<source>Verify code with desktop</source> <source>Verify code with desktop</source>
<target>Сверьте код с компьютером</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Verify connection" xml:space="preserve"> <trans-unit id="Verify connection" xml:space="preserve">
<source>Verify connection</source> <source>Verify connection</source>
<target>Проверить соединение</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Verify connection security" xml:space="preserve"> <trans-unit id="Verify connection security" xml:space="preserve">
@@ -5435,7 +5292,6 @@ To connect, please ask your contact to create another connection link and check
</trans-unit> </trans-unit>
<trans-unit id="Verify connections" xml:space="preserve"> <trans-unit id="Verify connections" xml:space="preserve">
<source>Verify connections</source> <source>Verify connections</source>
<target>Проверять соединения</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Verify security code" xml:space="preserve"> <trans-unit id="Verify security code" xml:space="preserve">
@@ -5448,11 +5304,6 @@ To connect, please ask your contact to create another connection link and check
<target>В браузере</target> <target>В браузере</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Via secure quantum resistant protocol." xml:space="preserve">
<source>Via secure quantum resistant protocol.</source>
<target>Через безопасный квантово-устойчивый протокол.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Video call" xml:space="preserve"> <trans-unit id="Video call" xml:space="preserve">
<source>Video call</source> <source>Video call</source>
<target>Видеозвонок</target> <target>Видеозвонок</target>
@@ -5503,11 +5354,6 @@ To connect, please ask your contact to create another connection link and check
<target>Голосовое сообщение…</target> <target>Голосовое сообщение…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Waiting for desktop..." xml:space="preserve">
<source>Waiting for desktop...</source>
<target>Ожидается подключение компьютера...</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve"> <trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source> <source>Waiting for file</source>
<target>Ожидается прием файла</target> <target>Ожидается прием файла</target>
@@ -5610,39 +5456,31 @@ To connect, please ask your contact to create another connection link and check
</trans-unit> </trans-unit>
<trans-unit id="You are already connecting to %@." xml:space="preserve"> <trans-unit id="You are already connecting to %@." xml:space="preserve">
<source>You are already connecting to %@.</source> <source>You are already connecting to %@.</source>
<target>Вы уже соединяетесь с %@.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="You are already connecting via this one-time link!" xml:space="preserve"> <trans-unit id="You are already connecting via this one-time link!" xml:space="preserve">
<source>You are already connecting via this one-time link!</source> <source>You are already connecting via this one-time link!</source>
<target>Вы уже соединяетесь по этой одноразовой ссылке!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="You are already in group %@." xml:space="preserve"> <trans-unit id="You are already in group %@." xml:space="preserve">
<source>You are already in group %@.</source> <source>You are already in group %@.</source>
<target>Вы уже состоите в группе %@.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="You are already joining the group %@." xml:space="preserve"> <trans-unit id="You are already joining the group %@." xml:space="preserve">
<source>You are already joining the group %@.</source> <source>You are already joining the group %@.</source>
<target>Вы уже вступаете в группу %@.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="You are already joining the group via this link!" xml:space="preserve"> <trans-unit id="You are already joining the group via this link!" xml:space="preserve">
<source>You are already joining the group via this link!</source> <source>You are already joining the group via this link!</source>
<target>Вы уже вступаете в группу по этой ссылке!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="You are already joining the group via this link." xml:space="preserve"> <trans-unit id="You are already joining the group via this link." xml:space="preserve">
<source>You are already joining the group via this link.</source> <source>You are already joining the group via this link.</source>
<target>Вы уже вступаете в группу по этой ссылке.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="You are already joining the group!&#10;Repeat join request?" xml:space="preserve"> <trans-unit id="You are already joining the group!&#10;Repeat join request?" xml:space="preserve">
<source>You are already joining the group! <source>You are already joining the group!
Repeat join request?</source> Repeat join request?</source>
<target>Вы уже вступаете в группу!
Повторить запрос на вступление?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="You are connected to the server used to receive messages from this contact." xml:space="preserve"> <trans-unit id="You are connected to the server used to receive messages from this contact." xml:space="preserve">
@@ -5742,14 +5580,11 @@ Repeat join request?</source>
</trans-unit> </trans-unit>
<trans-unit id="You have already requested connection via this address!" xml:space="preserve"> <trans-unit id="You have already requested connection via this address!" xml:space="preserve">
<source>You have already requested connection via this address!</source> <source>You have already requested connection via this address!</source>
<target>Вы уже запросили соединение через этот адрес!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="You have already requested connection!&#10;Repeat connection request?" xml:space="preserve"> <trans-unit id="You have already requested connection!&#10;Repeat connection request?" xml:space="preserve">
<source>You have already requested connection! <source>You have already requested connection!
Repeat connection request?</source> Repeat connection request?</source>
<target>Вы уже запросили соединение!
Повторить запрос?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="You have no chats" xml:space="preserve"> <trans-unit id="You have no chats" xml:space="preserve">
@@ -5804,7 +5639,6 @@ Repeat connection request?</source>
</trans-unit> </trans-unit>
<trans-unit id="You will be connected when group link host's device is online, please wait or check later!" xml:space="preserve"> <trans-unit id="You will be connected when group link host's device is online, please wait or check later!" xml:space="preserve">
<source>You will be connected when group link host's device is online, please wait or check later!</source> <source>You will be connected when group link host's device is online, please wait or check later!</source>
<target>Соединение будет установлено, когда владелец ссылки группы будет онлайн. Пожалуйста, подождите или проверьте позже!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="You will be connected when your connection request is accepted, please wait or check later!" xml:space="preserve"> <trans-unit id="You will be connected when your connection request is accepted, please wait or check later!" xml:space="preserve">
@@ -5824,7 +5658,6 @@ Repeat connection request?</source>
</trans-unit> </trans-unit>
<trans-unit id="You will connect to all group members." xml:space="preserve"> <trans-unit id="You will connect to all group members." xml:space="preserve">
<source>You will connect to all group members.</source> <source>You will connect to all group members.</source>
<target>Вы соединитесь со всеми членами группы.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="You will still receive calls and notifications from muted profiles when they are active." xml:space="preserve"> <trans-unit id="You will still receive calls and notifications from muted profiles when they are active." xml:space="preserve">
@@ -5948,7 +5781,6 @@ You can change it in Settings.</source>
</trans-unit> </trans-unit>
<trans-unit id="Your profile" xml:space="preserve"> <trans-unit id="Your profile" xml:space="preserve">
<source>Your profile</source> <source>Your profile</source>
<target>Ваш профиль</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Your profile **%@** will be shared." xml:space="preserve"> <trans-unit id="Your profile **%@** will be shared." xml:space="preserve">
@@ -6045,7 +5877,6 @@ SimpleX серверы не могут получить доступ к Ваше
</trans-unit> </trans-unit>
<trans-unit id="and %lld other events" xml:space="preserve"> <trans-unit id="and %lld other events" xml:space="preserve">
<source>and %lld other events</source> <source>and %lld other events</source>
<target>и %lld других событий</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="audio call (not e2e encrypted)" xml:space="preserve"> <trans-unit id="audio call (not e2e encrypted)" xml:space="preserve">
@@ -6053,11 +5884,6 @@ SimpleX серверы не могут получить доступ к Ваше
<target>аудиозвонок (не e2e зашифрованный)</target> <target>аудиозвонок (не e2e зашифрованный)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="author" xml:space="preserve">
<source>author</source>
<target>автор</target>
<note>member role</note>
</trans-unit>
<trans-unit id="bad message ID" xml:space="preserve"> <trans-unit id="bad message ID" xml:space="preserve">
<source>bad message ID</source> <source>bad message ID</source>
<target>ошибка ID сообщения</target> <target>ошибка ID сообщения</target>
@@ -6070,7 +5896,6 @@ SimpleX серверы не могут получить доступ к Ваше
</trans-unit> </trans-unit>
<trans-unit id="blocked" xml:space="preserve"> <trans-unit id="blocked" xml:space="preserve">
<source>blocked</source> <source>blocked</source>
<target>заблокировано</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="bold" xml:space="preserve"> <trans-unit id="bold" xml:space="preserve">
@@ -6145,7 +5970,6 @@ SimpleX серверы не могут получить доступ к Ваше
</trans-unit> </trans-unit>
<trans-unit id="connected directly" xml:space="preserve"> <trans-unit id="connected directly" xml:space="preserve">
<source>connected directly</source> <source>connected directly</source>
<target>соединен(а) напрямую</target>
<note>rcv group event chat item</note> <note>rcv group event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="connecting" xml:space="preserve"> <trans-unit id="connecting" xml:space="preserve">
@@ -6245,7 +6069,6 @@ SimpleX серверы не могут получить доступ к Ваше
</trans-unit> </trans-unit>
<trans-unit id="deleted contact" xml:space="preserve"> <trans-unit id="deleted contact" xml:space="preserve">
<source>deleted contact</source> <source>deleted contact</source>
<target>удалил(а) контакт</target>
<note>rcv direct event chat item</note> <note>rcv direct event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="deleted group" xml:space="preserve"> <trans-unit id="deleted group" xml:space="preserve">
@@ -6612,7 +6435,6 @@ SimpleX серверы не могут получить доступ к Ваше
</trans-unit> </trans-unit>
<trans-unit id="send direct message" xml:space="preserve"> <trans-unit id="send direct message" xml:space="preserve">
<source>send direct message</source> <source>send direct message</source>
<target>отправьте сообщение</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="starting…" xml:space="preserve"> <trans-unit id="starting…" xml:space="preserve">
@@ -6642,7 +6464,6 @@ SimpleX серверы не могут получить доступ к Ваше
</trans-unit> </trans-unit>
<trans-unit id="v%@" xml:space="preserve"> <trans-unit id="v%@" xml:space="preserve">
<source>v%@</source> <source>v%@</source>
<target>v%@</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="v%@ (%@)" xml:space="preserve"> <trans-unit id="v%@ (%@)" xml:space="preserve">
@@ -6784,7 +6605,6 @@ SimpleX серверы не могут получить доступ к Ваше
</trans-unit> </trans-unit>
<trans-unit id="NSLocalNetworkUsageDescription" xml:space="preserve"> <trans-unit id="NSLocalNetworkUsageDescription" xml:space="preserve">
<source>SimpleX uses local network access to allow using user chat profile via desktop app on the same network.</source> <source>SimpleX uses local network access to allow using user chat profile via desktop app on the same network.</source>
<target>SimpleX использует доступ к локальной сети, чтобы разрешить использование профиля чата через компьютер в той же сети.</target>
<note>Privacy - Local Network Usage Description</note> <note>Privacy - Local Network Usage Description</note>
</trans-unit> </trans-unit>
<trans-unit id="NSMicrophoneUsageDescription" xml:space="preserve"> <trans-unit id="NSMicrophoneUsageDescription" xml:space="preserve">
@@ -377,12 +377,6 @@
- และอื่น ๆ!</target> - และอื่น ๆ!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="- optionally notify deleted contacts.&#10;- profile names with spaces.&#10;- and more!" xml:space="preserve">
<source>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve"> <trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve">
<source>- voice messages up to 5 minutes. <source>- voice messages up to 5 minutes.
- custom time to disappear. - custom time to disappear.
@@ -874,10 +868,6 @@
<target>แฮชข้อความไม่ดี</target> <target>แฮชข้อความไม่ดี</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Better groups" xml:space="preserve">
<source>Better groups</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better messages" xml:space="preserve"> <trans-unit id="Better messages" xml:space="preserve">
<source>Better messages</source> <source>Better messages</source>
<target>ข้อความที่ดีขึ้น</target> <target>ข้อความที่ดีขึ้น</target>
@@ -887,10 +877,6 @@
<source>Block</source> <source>Block</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Block group members" xml:space="preserve">
<source>Block group members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Block member" xml:space="preserve"> <trans-unit id="Block member" xml:space="preserve">
<source>Block member</source> <source>Block member</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -1159,10 +1145,6 @@
<target>เชื่อมต่อ</target> <target>เชื่อมต่อ</target>
<note>server test step</note> <note>server test step</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect automatically" xml:space="preserve">
<source>Connect automatically</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect incognito" xml:space="preserve"> <trans-unit id="Connect incognito" xml:space="preserve">
<source>Connect incognito</source> <source>Connect incognito</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -1332,10 +1314,6 @@ This is your own one-time link!</source>
<target>สร้างที่อยู่ SimpleX</target> <target>สร้างที่อยู่ SimpleX</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create a group using a random profile." xml:space="preserve">
<source>Create a group using a random profile.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create an address to let people connect with you." xml:space="preserve"> <trans-unit id="Create an address to let people connect with you." xml:space="preserve">
<source>Create an address to let people connect with you.</source> <source>Create an address to let people connect with you.</source>
<target>สร้างที่อยู่เพื่อให้ผู้อื่นเชื่อมต่อกับคุณ</target> <target>สร้างที่อยู่เพื่อให้ผู้อื่นเชื่อมต่อกับคุณ</target>
@@ -1848,8 +1826,8 @@ This cannot be undone!</source>
<source>Discover and join groups</source> <source>Discover and join groups</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover via local network" xml:space="preserve"> <trans-unit id="Discover on network" xml:space="preserve">
<source>Discover via local network</source> <source>Discover on network</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve"> <trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
@@ -2381,10 +2359,6 @@ This cannot be undone!</source>
<target>รวดเร็วและไม่ต้องรอจนกว่าผู้ส่งจะออนไลน์!</target> <target>รวดเร็วและไม่ต้องรอจนกว่าผู้ส่งจะออนไลน์!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Faster joining and more reliable messages." xml:space="preserve">
<source>Faster joining and more reliable messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Favorite" xml:space="preserve"> <trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source> <source>Favorite</source>
<target>ที่ชอบ</target> <target>ที่ชอบ</target>
@@ -2480,10 +2454,6 @@ This cannot be undone!</source>
<target>สำหรับคอนโซล</target> <target>สำหรับคอนโซล</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="French interface" xml:space="preserve"> <trans-unit id="French interface" xml:space="preserve">
<source>French interface</source> <source>French interface</source>
<target>อินเทอร์เฟซภาษาฝรั่งเศส</target> <target>อินเทอร์เฟซภาษาฝรั่งเศส</target>
@@ -2806,10 +2776,6 @@ This cannot be undone!</source>
<target>ไม่ระบุตัวตน</target> <target>ไม่ระบุตัวตน</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incognito groups" xml:space="preserve">
<source>Incognito groups</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode" xml:space="preserve"> <trans-unit id="Incognito mode" xml:space="preserve">
<source>Incognito mode</source> <source>Incognito mode</source>
<target>โหมดไม่ระบุตัวตน</target> <target>โหมดไม่ระบุตัวตน</target>
@@ -3071,10 +3037,6 @@ This is your link for group %@!</source>
<target>ข้อจำกัด</target> <target>ข้อจำกัด</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Link mobile and desktop apps! 🔗" xml:space="preserve">
<source>Link mobile and desktop apps! 🔗</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Linked desktop options" xml:space="preserve"> <trans-unit id="Linked desktop options" xml:space="preserve">
<source>Linked desktop options</source> <source>Linked desktop options</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -3429,10 +3391,6 @@ This is your link for group %@!</source>
<target>ไม่มีไฟล์ที่ได้รับหรือส่ง</target> <target>ไม่มีไฟล์ที่ได้รับหรือส่ง</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Not compatible!" xml:space="preserve">
<source>Not compatible!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve"> <trans-unit id="Notifications" xml:space="preserve">
<source>Notifications</source> <source>Notifications</source>
<target>การแจ้งเตือน</target> <target>การแจ้งเตือน</target>
@@ -4996,10 +4954,6 @@ It can happen because of some bug or when the connection is compromised.</source
<target>เพื่อการเชื่อมต่อ ผู้ติดต่อของคุณสามารถสแกนคิวอาร์โค้ดหรือใช้ลิงก์ในแอป</target> <target>เพื่อการเชื่อมต่อ ผู้ติดต่อของคุณสามารถสแกนคิวอาร์โค้ดหรือใช้ลิงก์ในแอป</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="To hide unwanted messages." xml:space="preserve">
<source>To hide unwanted messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To make a new connection" xml:space="preserve"> <trans-unit id="To make a new connection" xml:space="preserve">
<source>To make a new connection</source> <source>To make a new connection</source>
<target>เพื่อสร้างการเชื่อมต่อใหม่</target> <target>เพื่อสร้างการเชื่อมต่อใหม่</target>
@@ -5312,10 +5266,6 @@ To connect, please ask your contact to create another connection link and check
<target>ผ่านเบราว์เซอร์</target> <target>ผ่านเบราว์เซอร์</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Via secure quantum resistant protocol." xml:space="preserve">
<source>Via secure quantum resistant protocol.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Video call" xml:space="preserve"> <trans-unit id="Video call" xml:space="preserve">
<source>Video call</source> <source>Video call</source>
<target>การสนทนาทางวิดีโอ</target> <target>การสนทนาทางวิดีโอ</target>
@@ -5366,10 +5316,6 @@ To connect, please ask your contact to create another connection link and check
<target>ข้อความเสียง…</target> <target>ข้อความเสียง…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Waiting for desktop..." xml:space="preserve">
<source>Waiting for desktop...</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve"> <trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source> <source>Waiting for file</source>
<target>กำลังรอไฟล์</target> <target>กำลังรอไฟล์</target>
@@ -5898,10 +5844,6 @@ SimpleX servers cannot see your profile.</source>
<target>การโทรด้วยเสียง (ไม่ได้ encrypt จากต้นจนจบ)</target> <target>การโทรด้วยเสียง (ไม่ได้ encrypt จากต้นจนจบ)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="author" xml:space="preserve">
<source>author</source>
<note>member role</note>
</trans-unit>
<trans-unit id="bad message ID" xml:space="preserve"> <trans-unit id="bad message ID" xml:space="preserve">
<source>bad message ID</source> <source>bad message ID</source>
<target>ID ข้อความที่ไม่ดี</target> <target>ID ข้อความที่ไม่ดี</target>
@@ -382,12 +382,6 @@
- і багато іншого!</target> - і багато іншого!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="- optionally notify deleted contacts.&#10;- profile names with spaces.&#10;- and more!" xml:space="preserve">
<source>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve"> <trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve">
<source>- voice messages up to 5 minutes. <source>- voice messages up to 5 minutes.
- custom time to disappear. - custom time to disappear.
@@ -881,10 +875,6 @@
<target>Поганий хеш повідомлення</target> <target>Поганий хеш повідомлення</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Better groups" xml:space="preserve">
<source>Better groups</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better messages" xml:space="preserve"> <trans-unit id="Better messages" xml:space="preserve">
<source>Better messages</source> <source>Better messages</source>
<target>Кращі повідомлення</target> <target>Кращі повідомлення</target>
@@ -894,10 +884,6 @@
<source>Block</source> <source>Block</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Block group members" xml:space="preserve">
<source>Block group members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Block member" xml:space="preserve"> <trans-unit id="Block member" xml:space="preserve">
<source>Block member</source> <source>Block member</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -1166,10 +1152,6 @@
<target>Підключіться</target> <target>Підключіться</target>
<note>server test step</note> <note>server test step</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect automatically" xml:space="preserve">
<source>Connect automatically</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect incognito" xml:space="preserve"> <trans-unit id="Connect incognito" xml:space="preserve">
<source>Connect incognito</source> <source>Connect incognito</source>
<target>Підключайтеся інкогніто</target> <target>Підключайтеся інкогніто</target>
@@ -1341,10 +1323,6 @@ This is your own one-time link!</source>
<target>Створіть адресу SimpleX</target> <target>Створіть адресу SimpleX</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create a group using a random profile." xml:space="preserve">
<source>Create a group using a random profile.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create an address to let people connect with you." xml:space="preserve"> <trans-unit id="Create an address to let people connect with you." xml:space="preserve">
<source>Create an address to let people connect with you.</source> <source>Create an address to let people connect with you.</source>
<target>Створіть адресу, щоб люди могли з вами зв'язатися.</target> <target>Створіть адресу, щоб люди могли з вами зв'язатися.</target>
@@ -1858,8 +1836,8 @@ This cannot be undone!</source>
<source>Discover and join groups</source> <source>Discover and join groups</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover via local network" xml:space="preserve"> <trans-unit id="Discover on network" xml:space="preserve">
<source>Discover via local network</source> <source>Discover on network</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve"> <trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
@@ -2391,10 +2369,6 @@ This cannot be undone!</source>
<target>Швидко і без очікування, поки відправник буде онлайн!</target> <target>Швидко і без очікування, поки відправник буде онлайн!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Faster joining and more reliable messages." xml:space="preserve">
<source>Faster joining and more reliable messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Favorite" xml:space="preserve"> <trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source> <source>Favorite</source>
<target>Улюблений</target> <target>Улюблений</target>
@@ -2490,10 +2464,6 @@ This cannot be undone!</source>
<target>Для консолі</target> <target>Для консолі</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="French interface" xml:space="preserve"> <trans-unit id="French interface" xml:space="preserve">
<source>French interface</source> <source>French interface</source>
<target>Французький інтерфейс</target> <target>Французький інтерфейс</target>
@@ -2816,10 +2786,6 @@ This cannot be undone!</source>
<target>Інкогніто</target> <target>Інкогніто</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incognito groups" xml:space="preserve">
<source>Incognito groups</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode" xml:space="preserve"> <trans-unit id="Incognito mode" xml:space="preserve">
<source>Incognito mode</source> <source>Incognito mode</source>
<target>Режим інкогніто</target> <target>Режим інкогніто</target>
@@ -3083,10 +3049,6 @@ This is your link for group %@!</source>
<target>Обмеження</target> <target>Обмеження</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Link mobile and desktop apps! 🔗" xml:space="preserve">
<source>Link mobile and desktop apps! 🔗</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Linked desktop options" xml:space="preserve"> <trans-unit id="Linked desktop options" xml:space="preserve">
<source>Linked desktop options</source> <source>Linked desktop options</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -3443,10 +3405,6 @@ This is your link for group %@!</source>
<target>Немає отриманих або відправлених файлів</target> <target>Немає отриманих або відправлених файлів</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Not compatible!" xml:space="preserve">
<source>Not compatible!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve"> <trans-unit id="Notifications" xml:space="preserve">
<source>Notifications</source> <source>Notifications</source>
<target>Сповіщення</target> <target>Сповіщення</target>
@@ -5018,10 +4976,6 @@ It can happen because of some bug or when the connection is compromised.</source
<target>Щоб підключитися, ваш контакт може відсканувати QR-код або скористатися посиланням у додатку.</target> <target>Щоб підключитися, ваш контакт може відсканувати QR-код або скористатися посиланням у додатку.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="To hide unwanted messages." xml:space="preserve">
<source>To hide unwanted messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To make a new connection" xml:space="preserve"> <trans-unit id="To make a new connection" xml:space="preserve">
<source>To make a new connection</source> <source>To make a new connection</source>
<target>Щоб створити нове з'єднання</target> <target>Щоб створити нове з'єднання</target>
@@ -5336,10 +5290,6 @@ To connect, please ask your contact to create another connection link and check
<target>Через браузер</target> <target>Через браузер</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Via secure quantum resistant protocol." xml:space="preserve">
<source>Via secure quantum resistant protocol.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Video call" xml:space="preserve"> <trans-unit id="Video call" xml:space="preserve">
<source>Video call</source> <source>Video call</source>
<target>Відеодзвінок</target> <target>Відеодзвінок</target>
@@ -5390,10 +5340,6 @@ To connect, please ask your contact to create another connection link and check
<target>Голосове повідомлення…</target> <target>Голосове повідомлення…</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Waiting for desktop..." xml:space="preserve">
<source>Waiting for desktop...</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve"> <trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source> <source>Waiting for file</source>
<target>Очікування файлу</target> <target>Очікування файлу</target>
@@ -5924,10 +5870,6 @@ SimpleX servers cannot see your profile.</source>
<target>аудіовиклик (без шифрування e2e)</target> <target>аудіовиклик (без шифрування e2e)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="author" xml:space="preserve">
<source>author</source>
<note>member role</note>
</trans-unit>
<trans-unit id="bad message ID" xml:space="preserve"> <trans-unit id="bad message ID" xml:space="preserve">
<source>bad message ID</source> <source>bad message ID</source>
<target>невірний ідентифікатор повідомлення</target> <target>невірний ідентифікатор повідомлення</target>
@@ -386,12 +386,6 @@
- 以及更多!</target> - 以及更多!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="- optionally notify deleted contacts.&#10;- profile names with spaces.&#10;- and more!" xml:space="preserve">
<source>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve"> <trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve">
<source>- voice messages up to 5 minutes. <source>- voice messages up to 5 minutes.
- custom time to disappear. - custom time to disappear.
@@ -886,10 +880,6 @@
<target>错误消息散列</target> <target>错误消息散列</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Better groups" xml:space="preserve">
<source>Better groups</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better messages" xml:space="preserve"> <trans-unit id="Better messages" xml:space="preserve">
<source>Better messages</source> <source>Better messages</source>
<target>更好的消息</target> <target>更好的消息</target>
@@ -899,10 +889,6 @@
<source>Block</source> <source>Block</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Block group members" xml:space="preserve">
<source>Block group members</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Block member" xml:space="preserve"> <trans-unit id="Block member" xml:space="preserve">
<source>Block member</source> <source>Block member</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -1172,10 +1158,6 @@
<target>连接</target> <target>连接</target>
<note>server test step</note> <note>server test step</note>
</trans-unit> </trans-unit>
<trans-unit id="Connect automatically" xml:space="preserve">
<source>Connect automatically</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect incognito" xml:space="preserve"> <trans-unit id="Connect incognito" xml:space="preserve">
<source>Connect incognito</source> <source>Connect incognito</source>
<target>在隐身状态下连接</target> <target>在隐身状态下连接</target>
@@ -1347,10 +1329,6 @@ This is your own one-time link!</source>
<target>创建 SimpleX 地址</target> <target>创建 SimpleX 地址</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Create a group using a random profile." xml:space="preserve">
<source>Create a group using a random profile.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Create an address to let people connect with you." xml:space="preserve"> <trans-unit id="Create an address to let people connect with you." xml:space="preserve">
<source>Create an address to let people connect with you.</source> <source>Create an address to let people connect with you.</source>
<target>创建一个地址,让人们与您联系。</target> <target>创建一个地址,让人们与您联系。</target>
@@ -1866,8 +1844,8 @@ This cannot be undone!</source>
<target>发现和加入群组</target> <target>发现和加入群组</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Discover via local network" xml:space="preserve"> <trans-unit id="Discover on network" xml:space="preserve">
<source>Discover via local network</source> <source>Discover on network</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve"> <trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
@@ -2404,10 +2382,6 @@ This cannot be undone!</source>
<target>快速且无需等待发件人在线!</target> <target>快速且无需等待发件人在线!</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Faster joining and more reliable messages." xml:space="preserve">
<source>Faster joining and more reliable messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Favorite" xml:space="preserve"> <trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source> <source>Favorite</source>
<target>最喜欢</target> <target>最喜欢</target>
@@ -2503,10 +2477,6 @@ This cannot be undone!</source>
<target>用于控制台</target> <target>用于控制台</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="French interface" xml:space="preserve"> <trans-unit id="French interface" xml:space="preserve">
<source>French interface</source> <source>French interface</source>
<target>法语界面</target> <target>法语界面</target>
@@ -2829,10 +2799,6 @@ This cannot be undone!</source>
<target>隐身聊天</target> <target>隐身聊天</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Incognito groups" xml:space="preserve">
<source>Incognito groups</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode" xml:space="preserve"> <trans-unit id="Incognito mode" xml:space="preserve">
<source>Incognito mode</source> <source>Incognito mode</source>
<target>隐身模式</target> <target>隐身模式</target>
@@ -3096,10 +3062,6 @@ This is your link for group %@!</source>
<target>限制</target> <target>限制</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Link mobile and desktop apps! 🔗" xml:space="preserve">
<source>Link mobile and desktop apps! 🔗</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Linked desktop options" xml:space="preserve"> <trans-unit id="Linked desktop options" xml:space="preserve">
<source>Linked desktop options</source> <source>Linked desktop options</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -3457,10 +3419,6 @@ This is your link for group %@!</source>
<target>未收到或发送文件</target> <target>未收到或发送文件</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Not compatible!" xml:space="preserve">
<source>Not compatible!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Notifications" xml:space="preserve"> <trans-unit id="Notifications" xml:space="preserve">
<source>Notifications</source> <source>Notifications</source>
<target>通知</target> <target>通知</target>
@@ -5035,10 +4993,6 @@ It can happen because of some bug or when the connection is compromised.</source
<target>您的联系人可以扫描二维码或使用应用程序中的链接来建立连接。</target> <target>您的联系人可以扫描二维码或使用应用程序中的链接来建立连接。</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="To hide unwanted messages." xml:space="preserve">
<source>To hide unwanted messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To make a new connection" xml:space="preserve"> <trans-unit id="To make a new connection" xml:space="preserve">
<source>To make a new connection</source> <source>To make a new connection</source>
<target>建立新连接</target> <target>建立新连接</target>
@@ -5354,10 +5308,6 @@ To connect, please ask your contact to create another connection link and check
<target>通过浏览器</target> <target>通过浏览器</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Via secure quantum resistant protocol." xml:space="preserve">
<source>Via secure quantum resistant protocol.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Video call" xml:space="preserve"> <trans-unit id="Video call" xml:space="preserve">
<source>Video call</source> <source>Video call</source>
<target>视频通话</target> <target>视频通话</target>
@@ -5408,10 +5358,6 @@ To connect, please ask your contact to create another connection link and check
<target>语音消息……</target> <target>语音消息……</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Waiting for desktop..." xml:space="preserve">
<source>Waiting for desktop...</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve"> <trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source> <source>Waiting for file</source>
<target>等待文件中</target> <target>等待文件中</target>
@@ -5942,10 +5888,6 @@ SimpleX 服务器无法看到您的资料。</target>
<target>语音通话(非端到端加密)</target> <target>语音通话(非端到端加密)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="author" xml:space="preserve">
<source>author</source>
<note>member role</note>
</trans-unit>
<trans-unit id="bad message ID" xml:space="preserve"> <trans-unit id="bad message ID" xml:space="preserve">
<source>bad message ID</source> <source>bad message ID</source>
<target>错误消息 ID</target> <target>错误消息 ID</target>
@@ -1,64 +0,0 @@
//
// ConcurrentQueue.swift
// SimpleX NSE
//
// Created by Evgeny on 08/12/2023.
// Copyright © 2023 SimpleX Chat. All rights reserved.
//
import Foundation
struct DequeueElement<T> {
var elementId: UUID?
var task: Task<T?, Never>
}
class ConcurrentQueue<T> {
private var queue: [T] = []
private var queueLock = DispatchQueue(label: "chat.simplex.app.SimpleX-NSE.concurrent-queue.lock.\(UUID())")
private var continuations = [(elementId: UUID, continuation: CheckedContinuation<T?, Never>)]()
func enqueue(_ el: T) {
resumeContinuation(el) { self.queue.append(el) }
}
func frontEnqueue(_ el: T) {
resumeContinuation(el) { self.queue.insert(el, at: 0) }
}
private func resumeContinuation(_ el: T, add: @escaping () -> Void) {
queueLock.sync {
if let (_, cont) = continuations.first {
continuations.remove(at: 0)
cont.resume(returning: el)
} else {
add()
}
}
}
func dequeue() -> DequeueElement<T> {
queueLock.sync {
if queue.isEmpty {
let elementId = UUID()
let task = Task {
await withCheckedContinuation { cont in
continuations.append((elementId, cont))
}
}
return DequeueElement(elementId: elementId, task: task)
} else {
let el = queue.remove(at: 0)
return DequeueElement(task: Task { el })
}
}
}
func cancelDequeue(_ elementId: UUID) {
queueLock.sync {
let cancelled = continuations.filter { $0.elementId == elementId }
continuations.removeAll { $0.elementId == elementId }
cancelled.forEach { $0.continuation.resume(returning: nil) }
}
}
}
+98 -486
View File
@@ -14,233 +14,91 @@ import SimpleXChat
let logger = Logger() let logger = Logger()
let appSuspendingDelay: UInt64 = 2_500_000_000 let suspendingDelay: UInt64 = 2_000_000_000
typealias SuspendSchedule = (delay: TimeInterval, timeout: Int) typealias NtfStream = AsyncStream<NSENotification>
let nseSuspendSchedule: SuspendSchedule = (2, 4)
let fastNSESuspendSchedule: SuspendSchedule = (1, 1)
typealias NtfStream = ConcurrentQueue<NSENotification>
// Notifications are delivered via concurrent queues, as they are all received from chat controller in a single loop that
// writes to ConcurrentQueue and when notification is processed, the instance of Notification service extension reads from the queue.
// One queue per connection (entity) is used.
// The concurrent queues allow read cancellation, to ensure that notifications are not lost in case the current thread completes
// before expected notification is read (multiple notifications can be expected, because one notification can be delivered for several messages).
actor PendingNtfs { actor PendingNtfs {
static let shared = PendingNtfs() static let shared = PendingNtfs()
private var ntfStreams: [String: NtfStream] = [:] private var ntfStreams: [String: NtfStream] = [:]
private var ntfConts: [String: NtfStream.Continuation] = [:]
func createStream(_ id: String) async { func createStream(_ id: String) {
logger.debug("NotificationService PendingNtfs.createStream: \(id)") logger.debug("PendingNtfs.createStream: \(id, privacy: .public)")
if ntfStreams[id] == nil { if ntfStreams.index(forKey: id) == nil {
ntfStreams[id] = ConcurrentQueue() ntfStreams[id] = AsyncStream { cont in
logger.debug("NotificationService PendingNtfs.createStream: created ConcurrentQueue") ntfConts[id] = cont
logger.debug("PendingNtfs.createStream: store continuation")
}
} }
} }
func readStream(_ id: String, for nse: NotificationService, ntfInfo: NtfMessages) async { func readStream(_ id: String, for nse: NotificationService, msgCount: Int = 1, showNotifications: Bool) async {
logger.debug("NotificationService PendingNtfs.readStream: \(id) \(ntfInfo.ntfMessages.count)") logger.debug("PendingNtfs.readStream: \(id, privacy: .public) \(msgCount, privacy: .public)")
if !ntfInfo.user.showNotifications {
nse.setBestAttemptNtf(.empty)
}
if let s = ntfStreams[id] { if let s = ntfStreams[id] {
logger.debug("NotificationService PendingNtfs.readStream: has stream") logger.debug("PendingNtfs.readStream: has stream")
var expected = Set(ntfInfo.ntfMessages.map { $0.msgId }) var rcvCount = max(1, msgCount)
logger.debug("NotificationService PendingNtfs.readStream: expecting: \(expected)") for await ntf in s {
var readCancelled = false nse.setBestAttemptNtf(showNotifications ? ntf : .empty)
var dequeued: DequeueElement<NSENotification>? rcvCount -= 1
nse.cancelRead = { if rcvCount == 0 || ntf.categoryIdentifier == ntfCategoryCallInvitation { break }
readCancelled = true
if let elementId = dequeued?.elementId {
s.cancelDequeue(elementId)
}
} }
while !readCancelled { logger.debug("PendingNtfs.readStream: exiting")
dequeued = s.dequeue()
if let ntf = await dequeued?.task.value {
if readCancelled {
logger.debug("NotificationService PendingNtfs.readStream: read cancelled, put ntf to queue front")
s.frontEnqueue(ntf)
break
} else if case let .msgInfo(info) = ntf {
let found = expected.remove(info.msgId)
if found != nil {
logger.debug("NotificationService PendingNtfs.readStream: msgInfo, last: \(expected.isEmpty)")
if expected.isEmpty { break }
} else if let msgTs = ntfInfo.msgTs, info.msgTs > msgTs {
logger.debug("NotificationService PendingNtfs.readStream: unexpected msgInfo")
s.frontEnqueue(ntf)
break
}
} else if ntfInfo.user.showNotifications {
logger.debug("NotificationService PendingNtfs.readStream: setting best attempt")
nse.setBestAttemptNtf(ntf)
if ntf.isCallInvitation { break }
}
} else {
break
}
}
nse.cancelRead = nil
logger.debug("NotificationService PendingNtfs.readStream: exiting")
} }
} }
func writeStream(_ id: String, _ ntf: NSENotification) async { func writeStream(_ id: String, _ ntf: NSENotification) {
logger.debug("NotificationService PendingNtfs.writeStream: \(id)") logger.debug("PendingNtfs.writeStream: \(id, privacy: .public)")
if let s = ntfStreams[id] { if let cont = ntfConts[id] {
logger.debug("NotificationService PendingNtfs.writeStream: writing ntf") logger.debug("PendingNtfs.writeStream: writing ntf")
s.enqueue(ntf) cont.yield(ntf)
}
}
}
// The current implementation assumes concurrent notification delivery and uses semaphores
// to process only one notification per connection (entity) at a time.
class NtfStreamSemaphores {
static let shared = NtfStreamSemaphores()
private static let queue = DispatchQueue(label: "chat.simplex.app.SimpleX-NSE.notification-semaphores.lock")
private var semaphores: [String: DispatchSemaphore] = [:]
func waitForStream(_ id: String) {
streamSemaphore(id, value: 0)?.wait()
}
func signalStreamReady(_ id: String) {
streamSemaphore(id, value: 1)?.signal()
}
// this function returns nil if semaphore is just created, so passed value shoud be coordinated with the desired end value of the semaphore
private func streamSemaphore(_ id: String, value: Int) -> DispatchSemaphore? {
NtfStreamSemaphores.queue.sync {
if let s = semaphores[id] {
return s
} else {
semaphores[id] = DispatchSemaphore(value: value)
return nil
}
} }
} }
} }
enum NSENotification { enum NSENotification {
case nse(UNMutableNotificationContent) case nse(notification: UNMutableNotificationContent)
case callkit(RcvCallInvitation) case callkit(invitation: RcvCallInvitation)
case empty case empty
case msgInfo(NtfMsgInfo)
var isCallInvitation: Bool { var categoryIdentifier: String? {
switch self { switch self {
case let .nse(ntf): ntf.categoryIdentifier == ntfCategoryCallInvitation case let .nse(ntf): return ntf.categoryIdentifier
case .callkit: true case .callkit: return ntfCategoryCallInvitation
case .empty: false case .empty: return nil
case .msgInfo: false
} }
} }
} }
// Once the last thread in the process completes processing chat controller is suspended, and the database is closed, to avoid
// background crashes and contention for database with the application (both UI and background fetch triggered either on schedule
// or when background notification is received.
class NSEThreads {
static let shared = NSEThreads()
private static let queue = DispatchQueue(label: "chat.simplex.app.SimpleX-NSE.notification-threads.lock")
private var allThreads: Set<UUID> = []
private var activeThreads: Set<UUID> = []
func newThread() -> UUID {
NSEThreads.queue.sync {
let (_, t) = allThreads.insert(UUID())
return t
}
}
func startThread(_ t: UUID) {
NSEThreads.queue.sync {
if allThreads.contains(t) {
_ = activeThreads.insert(t)
} else {
logger.warning("NotificationService startThread: thread \(t) was removed before it started")
}
}
}
func endThread(_ t: UUID) -> Bool {
NSEThreads.queue.sync {
let tActive = activeThreads.remove(t)
let t = allThreads.remove(t)
if tActive != nil && activeThreads.isEmpty {
return true
}
if t != nil && allThreads.isEmpty {
NSEChatState.shared.set(.suspended)
}
return false
}
}
var noThreads: Bool {
allThreads.isEmpty
}
}
// Notification service extension creates a new instance of the class and calls didReceive for each notification.
// Each didReceive is called in its own thread, but multiple calls can be made in one process, and, empirically, there is never
// more than one process of notification service extension exists at a time.
// Soon after notification service delivers the last notification it is either suspended or terminated.
class NotificationService: UNNotificationServiceExtension { class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)? var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptNtf: NSENotification? var bestAttemptNtf: NSENotification?
var badgeCount: Int = 0 var badgeCount: Int = 0
// thread is added to allThreads here - if thread did not start chat,
// chat does not need to be suspended but NSE state still needs to be set to "suspended".
var threadId: UUID? = NSEThreads.shared.newThread()
var receiveEntityId: String?
var cancelRead: (() -> Void)?
var appSubscriber: AppSubscriber?
var returnedSuspension = false
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
logger.debug("DEBUGGING: NotificationService.didReceive") logger.debug("DEBUGGING: NotificationService.didReceive")
let ntf = if let ntf_ = request.content.mutableCopy() as? UNMutableNotificationContent { ntf_ } else { UNMutableNotificationContent() } if let ntf = request.content.mutableCopy() as? UNMutableNotificationContent {
setBestAttemptNtf(ntf) setBestAttemptNtf(ntf)
}
self.contentHandler = contentHandler self.contentHandler = contentHandler
registerGroupDefaults() registerGroupDefaults()
let appState = appStateGroupDefault.get() let appState = appStateGroupDefault.get()
logger.debug("NotificationService: app is \(appState.rawValue)")
switch appState { switch appState {
case .stopped:
setBadgeCount()
setBestAttemptNtf(createAppStoppedNtf())
deliverBestAttemptNtf()
case .suspended: case .suspended:
logger.debug("NotificationService: app is suspended")
setBadgeCount() setBadgeCount()
receiveNtfMessages(request, contentHandler) receiveNtfMessages(request, contentHandler)
case .suspending: case .suspending:
logger.debug("NotificationService: app is suspending")
setBadgeCount() setBadgeCount()
Task { Task {
let state: AppState = await withCheckedContinuation { cont in var state = appState
appSubscriber = appStateSubscriber { s in for _ in 1...5 {
if s == .suspended { appSuspension(s) } _ = try await Task.sleep(nanoseconds: suspendingDelay)
} state = appStateGroupDefault.get()
DispatchQueue.global().asyncAfter(deadline: .now() + Double(appSuspendTimeout) + 1) { if state == .suspended || state != .suspending { break }
logger.debug("NotificationService: appSuspension timeout")
appSuspension(appStateGroupDefault.get())
}
@Sendable
func appSuspension(_ s: AppState) {
if !self.returnedSuspension {
self.returnedSuspension = true
self.appSubscriber = nil // this disposes of appStateSubscriber
cont.resume(returning: s)
}
}
} }
logger.debug("NotificationService: app state is now \(state.rawValue)") logger.debug("NotificationService: app state is \(state.rawValue, privacy: .public)")
if state.inactive { if state.inactive {
receiveNtfMessages(request, contentHandler) receiveNtfMessages(request, contentHandler)
} else { } else {
@@ -248,6 +106,7 @@ class NotificationService: UNNotificationServiceExtension {
} }
} }
default: default:
logger.debug("NotificationService: app state is \(appState.rawValue, privacy: .public)")
deliverBestAttemptNtf() deliverBestAttemptNtf()
} }
} }
@@ -262,35 +121,27 @@ class NotificationService: UNNotificationServiceExtension {
if let ntfData = userInfo["notificationData"] as? [AnyHashable : Any], if let ntfData = userInfo["notificationData"] as? [AnyHashable : Any],
let nonce = ntfData["nonce"] as? String, let nonce = ntfData["nonce"] as? String,
let encNtfInfo = ntfData["message"] as? String, let encNtfInfo = ntfData["message"] as? String,
// check it here again let dbStatus = startChat() {
appStateGroupDefault.get().inactive {
// thread is added to activeThreads tracking set here - if thread started chat it needs to be suspended
if let t = threadId { NSEThreads.shared.startThread(t) }
let dbStatus = startChat()
if case .ok = dbStatus, if case .ok = dbStatus,
let ntfInfo = apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo) { let ntfMsgInfo = apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo) {
logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfInfo.ntfMessages.count))") logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfMsgInfo), privacy: .public)")
if let connEntity = ntfInfo.connEntity_ { if let connEntity = ntfMsgInfo.connEntity {
setBestAttemptNtf( setBestAttemptNtf(
ntfInfo.ntfsEnabled ntfMsgInfo.ntfsEnabled
? .nse(createConnectionEventNtf(ntfInfo.user, connEntity)) ? .nse(notification: createConnectionEventNtf(ntfMsgInfo.user, connEntity))
: .empty : .empty
) )
if let id = connEntity.id { if let id = connEntity.id {
receiveEntityId = id Task {
NtfStreamSemaphores.shared.waitForStream(id) logger.debug("NotificationService: receiveNtfMessages: in Task, connEntity id \(id, privacy: .public)")
if receiveEntityId != nil { await PendingNtfs.shared.createStream(id)
Task { await PendingNtfs.shared.readStream(id, for: self, msgCount: ntfMsgInfo.ntfMessages.count, showNotifications: ntfMsgInfo.user.showNotifications)
logger.debug("NotificationService: receiveNtfMessages: in Task, connEntity id \(id)") deliverBestAttemptNtf()
await PendingNtfs.shared.createStream(id)
await PendingNtfs.shared.readStream(id, for: self, ntfInfo: ntfInfo)
deliverBestAttemptNtf()
}
} }
return
} }
} }
} else if let dbStatus = dbStatus { return
} else {
setBestAttemptNtf(createErrorNtf(dbStatus)) setBestAttemptNtf(createErrorNtf(dbStatus))
} }
} }
@@ -299,7 +150,7 @@ class NotificationService: UNNotificationServiceExtension {
override func serviceExtensionTimeWillExpire() { override func serviceExtensionTimeWillExpire() {
logger.debug("DEBUGGING: NotificationService.serviceExtensionTimeWillExpire") logger.debug("DEBUGGING: NotificationService.serviceExtensionTimeWillExpire")
deliverBestAttemptNtf(urgent: true) deliverBestAttemptNtf()
} }
func setBadgeCount() { func setBadgeCount() {
@@ -308,301 +159,91 @@ class NotificationService: UNNotificationServiceExtension {
} }
func setBestAttemptNtf(_ ntf: UNMutableNotificationContent) { func setBestAttemptNtf(_ ntf: UNMutableNotificationContent) {
setBestAttemptNtf(.nse(ntf)) setBestAttemptNtf(.nse(notification: ntf))
} }
func setBestAttemptNtf(_ ntf: NSENotification) { func setBestAttemptNtf(_ ntf: NSENotification) {
logger.debug("NotificationService.setBestAttemptNtf") logger.debug("NotificationService.setBestAttemptNtf")
if case let .nse(notification) = ntf { if case let .nse(notification) = ntf {
notification.badge = badgeCount as NSNumber notification.badge = badgeCount as NSNumber
bestAttemptNtf = .nse(notification) bestAttemptNtf = .nse(notification: notification)
} else { } else {
bestAttemptNtf = ntf bestAttemptNtf = ntf
} }
} }
private func deliverBestAttemptNtf(urgent: Bool = false) { private func deliverBestAttemptNtf() {
logger.debug("NotificationService.deliverBestAttemptNtf") logger.debug("NotificationService.deliverBestAttemptNtf")
if let cancel = cancelRead {
cancelRead = nil
cancel()
}
if let id = receiveEntityId {
receiveEntityId = nil
NtfStreamSemaphores.shared.signalStreamReady(id)
}
let suspend: Bool
if let t = threadId {
threadId = nil
suspend = NSEThreads.shared.endThread(t) && NSEThreads.shared.noThreads
} else {
suspend = false
}
deliverCallkitOrNotification(urgent: urgent, suspend: suspend)
}
private func deliverCallkitOrNotification(urgent: Bool, suspend: Bool = false) {
if case .callkit = bestAttemptNtf {
logger.debug("NotificationService.deliverCallkitOrNotification: will suspend, callkit")
if urgent {
// suspending NSE even though there may be other notifications
// to allow the app to process callkit call
suspendChat(0)
deliverNotification()
} else {
// suspending NSE with delay and delivering after the suspension
// because pushkit notification must be processed without delay
// to avoid app termination
DispatchQueue.global().asyncAfter(deadline: .now() + fastNSESuspendSchedule.delay) {
suspendChat(fastNSESuspendSchedule.timeout)
DispatchQueue.global().asyncAfter(deadline: .now() + Double(fastNSESuspendSchedule.timeout)) {
self.deliverNotification()
}
}
}
} else {
if suspend {
logger.debug("NotificationService.deliverCallkitOrNotification: will suspend")
if urgent {
suspendChat(0)
} else {
// suspension is delayed to allow chat core finalise any processing
// (e.g., send delivery receipts)
DispatchQueue.global().asyncAfter(deadline: .now() + nseSuspendSchedule.delay) {
if NSEThreads.shared.noThreads {
suspendChat(nseSuspendSchedule.timeout)
}
}
}
}
deliverNotification()
}
}
private func deliverNotification() {
if let handler = contentHandler, let ntf = bestAttemptNtf { if let handler = contentHandler, let ntf = bestAttemptNtf {
contentHandler = nil
bestAttemptNtf = nil
let deliver: (UNMutableNotificationContent?) -> Void = { ntf in
let useNtf = if let ntf = ntf {
appStateGroupDefault.get().running ? UNMutableNotificationContent() : ntf
} else {
UNMutableNotificationContent()
}
handler(useNtf)
}
switch ntf { switch ntf {
case let .nse(content): deliver(content) case let .nse(content): handler(content)
case let .callkit(invitation): case let .callkit(invitation):
logger.debug("NotificationService reportNewIncomingVoIPPushPayload for \(invitation.contact.id)")
CXProvider.reportNewIncomingVoIPPushPayload([ CXProvider.reportNewIncomingVoIPPushPayload([
"displayName": invitation.contact.displayName, "displayName": invitation.contact.displayName,
"contactId": invitation.contact.id, "contactId": invitation.contact.id,
"media": invitation.callType.media.rawValue "media": invitation.callType.media.rawValue
]) { error in ]) { error in
logger.debug("reportNewIncomingVoIPPushPayload result: \(error)") if error == nil {
deliver(error == nil ? nil : createCallInvitationNtf(invitation)) handler(UNMutableNotificationContent())
} else {
logger.debug("reportNewIncomingVoIPPushPayload success to CallController for \(invitation.contact.id)")
handler(createCallInvitationNtf(invitation))
}
} }
case .empty: deliver(nil) // used to mute notifications that did not unsubscribe yet case .empty: handler(UNMutableNotificationContent())
case .msgInfo: deliver(nil) // unreachable, the best attempt is never set to msgInfo
} }
bestAttemptNtf = nil
} }
} }
} }
// nseStateGroupDefault must not be used in NSE directly, only via this singleton var chatStarted = false
class NSEChatState {
static let shared = NSEChatState()
private var value_ = NSEState.created
var value: NSEState {
value_
}
func set(_ state: NSEState) {
nseStateGroupDefault.set(state)
sendNSEState(state)
value_ = state
}
init() {
// This is always set to .created state, as in case previous start of NSE crashed in .active state, it is stored correctly.
// Otherwise the app will be activating slower
set(.created)
}
}
var appSubscriber: AppSubscriber = appStateSubscriber { state in
logger.debug("NotificationService: appSubscriber")
if state.running && NSEChatState.shared.value.canSuspend {
logger.debug("NotificationService: appSubscriber app state \(state.rawValue), suspending")
suspendChat(fastNSESuspendSchedule.timeout)
}
}
func appStateSubscriber(onState: @escaping (AppState) -> Void) -> AppSubscriber {
appMessageSubscriber { msg in
if case let .state(state) = msg {
logger.debug("NotificationService: appStateSubscriber \(state.rawValue)")
onState(state)
}
}
}
var receiverStarted = false
let startLock = DispatchSemaphore(value: 1)
let suspendLock = DispatchSemaphore(value: 1)
var networkConfig: NetCfg = getNetCfg() var networkConfig: NetCfg = getNetCfg()
let xftpConfig: XFTPFileConfig? = getXFTPCfg() var xftpConfig: XFTPFileConfig? = getXFTPCfg()
// startChat uses semaphore startLock to ensure that only one didReceive thread can start chat controller
// Subsequent calls to didReceive will be waiting on semaphore and won't start chat again, as it will be .active
func startChat() -> DBMigrationResult? { func startChat() -> DBMigrationResult? {
logger.debug("NotificationService: startChat") hs_init(0, nil)
// only skip creating if there is chat controller if chatStarted { return .ok }
if case .active = NSEChatState.shared.value, hasChatCtrl() { return .ok } let (_, dbStatus) = chatMigrateInit(confirmMigrations: defaultMigrationConfirmation())
startLock.wait()
defer { startLock.signal() }
if hasChatCtrl() {
return switch NSEChatState.shared.value {
case .created: doStartChat()
case .starting: .ok // it should never get to this branch, as it would be waiting for start on startLock
case .active: .ok
case .suspending: activateChat()
case .suspended: activateChat()
}
} else {
// Ignore state in preference if there is no chat controller.
// State in preference may have failed to update e.g. because of a crash.
NSEChatState.shared.set(.created)
return doStartChat()
}
}
func doStartChat() -> DBMigrationResult? {
logger.debug("NotificationService: doStartChat")
haskell_init_nse()
let (_, dbStatus) = chatMigrateInit(confirmMigrations: defaultMigrationConfirmation(), backgroundMode: true)
logger.debug("NotificationService: doStartChat \(String(describing: dbStatus))")
if dbStatus != .ok { if dbStatus != .ok {
resetChatCtrl() resetChatCtrl()
NSEChatState.shared.set(.created)
return dbStatus return dbStatus
} }
let state = NSEChatState.shared.value
NSEChatState.shared.set(.starting)
if let user = apiGetActiveUser() { if let user = apiGetActiveUser() {
logger.debug("NotificationService active user \(String(describing: user))") logger.debug("active user \(String(describing: user))")
do { do {
try setNetworkConfig(networkConfig) try setNetworkConfig(networkConfig)
try apiSetTempFolder(tempFolder: getTempFilesDirectory().path) try apiSetTempFolder(tempFolder: getTempFilesDirectory().path)
try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path) try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path)
try setXFTPConfig(xftpConfig) try setXFTPConfig(xftpConfig)
try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get()) try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get())
// prevent suspension while starting chat let justStarted = try apiStartChat()
suspendLock.wait() chatStarted = true
defer { suspendLock.signal() } if justStarted {
if NSEChatState.shared.value == .starting { chatLastStartGroupDefault.set(Date.now)
updateNetCfg() Task { await receiveMessages() }
let justStarted = try apiStartChat()
NSEChatState.shared.set(.active)
if justStarted {
chatLastStartGroupDefault.set(Date.now)
Task {
if !receiverStarted {
receiverStarted = true
await receiveMessages()
}
}
}
return .ok
} }
return .ok
} catch { } catch {
logger.error("NotificationService startChat error: \(responseError(error))") logger.error("NotificationService startChat error: \(responseError(error), privacy: .public)")
} }
} else { } else {
logger.debug("NotificationService: no active user") logger.debug("no active user")
} }
if NSEChatState.shared.value == .starting { NSEChatState.shared.set(state) }
return nil return nil
} }
func activateChat() -> DBMigrationResult? {
logger.debug("NotificationService: activateChat")
let state = NSEChatState.shared.value
NSEChatState.shared.set(.active)
if apiActivateChat() {
logger.debug("NotificationService: activateChat: after apiActivateChat")
return .ok
} else {
NSEChatState.shared.set(state)
return nil
}
}
// suspendChat uses semaphore suspendLock to ensure that only one suspension can happen.
func suspendChat(_ timeout: Int) {
logger.debug("NotificationService: suspendChat")
let state = NSEChatState.shared.value
if !state.canSuspend {
logger.error("NotificationService suspendChat called, current state: \(state.rawValue)")
} else if hasChatCtrl() {
// only suspend if we have chat controller to avoid crashes when suspension is
// attempted when chat controller was not created
suspendLock.wait()
defer { suspendLock.signal() }
NSEChatState.shared.set(.suspending)
if apiSuspendChat(timeoutMicroseconds: timeout * 1000000) {
logger.debug("NotificationService: suspendChat: after apiSuspendChat")
DispatchQueue.global().asyncAfter(deadline: .now() + Double(timeout) + 1, execute: chatSuspended)
} else {
NSEChatState.shared.set(state)
}
}
}
func chatSuspended() {
logger.debug("NotificationService chatSuspended")
if case .suspending = NSEChatState.shared.value {
NSEChatState.shared.set(.suspended)
chatCloseStore()
logger.debug("NotificationService chatSuspended: suspended")
}
}
// A single loop is used per Notification service extension process to receive and process all messages depending on the NSE state
// If the extension is not active yet, or suspended/suspending, or the app is running, the notifications will no be received.
func receiveMessages() async { func receiveMessages() async {
logger.debug("NotificationService receiveMessages") logger.debug("NotificationService receiveMessages")
while true { while true {
switch NSEChatState.shared.value { updateNetCfg()
// it should never get to "created" and "starting" branches, as NSE state is set to .active before the loop start
case .created: await delayWhenInactive()
case .starting: await delayWhenInactive()
case .active: await receiveMsg()
case .suspending: await receiveMsg()
case .suspended: await delayWhenInactive()
}
}
func receiveMsg() async {
if let msg = await chatRecvMsg() { if let msg = await chatRecvMsg() {
logger.debug("NotificationService receiveMsg: message")
if let (id, ntf) = await receivedMsgNtf(msg) { if let (id, ntf) = await receivedMsgNtf(msg) {
logger.debug("NotificationService receiveMsg: notification")
await PendingNtfs.shared.createStream(id) await PendingNtfs.shared.createStream(id)
await PendingNtfs.shared.writeStream(id, ntf) await PendingNtfs.shared.writeStream(id, ntf)
} }
} }
} }
func delayWhenInactive() async {
logger.debug("NotificationService delayWhenInactive")
_ = try? await Task.sleep(nanoseconds: 1000_000000)
}
} }
func chatRecvMsg() async -> ChatResponse? { func chatRecvMsg() async -> ChatResponse? {
@@ -616,14 +257,14 @@ private let isInChina = SKStorefront().countryCode == "CHN"
private func useCallKit() -> Bool { !isInChina && callKitEnabledGroupDefault.get() } private func useCallKit() -> Bool { !isInChina && callKitEnabledGroupDefault.get() }
func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? { func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? {
logger.debug("NotificationService receivedMsgNtf: \(res.responseType)") logger.debug("NotificationService processReceivedMsg: \(res.responseType)")
switch res { switch res {
case let .contactConnected(user, contact, _): case let .contactConnected(user, contact, _):
return (contact.id, .nse(createContactConnectedNtf(user, contact))) return (contact.id, .nse(notification: createContactConnectedNtf(user, contact)))
// case let .contactConnecting(contact): // case let .contactConnecting(contact):
// TODO profile update // TODO profile update
case let .receivedContactRequest(user, contactRequest): case let .receivedContactRequest(user, contactRequest):
return (UserContact(contactRequest: contactRequest).id, .nse(createContactRequestNtf(user, contactRequest))) return (UserContact(contactRequest: contactRequest).id, .nse(notification: createContactRequestNtf(user, contactRequest)))
case let .newChatItem(user, aChatItem): case let .newChatItem(user, aChatItem):
let cInfo = aChatItem.chatInfo let cInfo = aChatItem.chatInfo
var cItem = aChatItem.chatItem var cItem = aChatItem.chatItem
@@ -633,7 +274,7 @@ func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? {
if let file = cItem.autoReceiveFile() { if let file = cItem.autoReceiveFile() {
cItem = autoReceiveFile(file, encrypted: cItem.encryptLocalFile) ?? cItem cItem = autoReceiveFile(file, encrypted: cItem.encryptLocalFile) ?? cItem
} }
let ntf: NSENotification = cInfo.ntfsEnabled ? .nse(createMessageReceivedNtf(user, cInfo, cItem)) : .empty let ntf: NSENotification = cInfo.ntfsEnabled ? .nse(notification: createMessageReceivedNtf(user, cInfo, cItem)) : .empty
return cItem.showNotification ? (aChatItem.chatId, ntf) : nil return cItem.showNotification ? (aChatItem.chatId, ntf) : nil
case let .rcvFileSndCancelled(_, aChatItem, _): case let .rcvFileSndCancelled(_, aChatItem, _):
cleanupFile(aChatItem) cleanupFile(aChatItem)
@@ -651,18 +292,10 @@ func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? {
// Do not post it without CallKit support, iOS will stop launching the app without showing CallKit // Do not post it without CallKit support, iOS will stop launching the app without showing CallKit
return ( return (
invitation.contact.id, invitation.contact.id,
useCallKit() ? .callkit(invitation) : .nse(createCallInvitationNtf(invitation)) useCallKit() ? .callkit(invitation: invitation) : .nse(notification: createCallInvitationNtf(invitation))
) )
case let .ntfMessage(_, connEntity, ntfMessage):
return if let id = connEntity.id { (id, .msgInfo(ntfMessage)) } else { nil }
case .chatSuspended:
chatSuspended()
return nil
case let .chatError(_, err):
logger.error("NotificationService receivedMsgNtf error: \(String(describing: err))")
return nil
default: default:
logger.debug("NotificationService receivedMsgNtf ignored event: \(res.responseType)") logger.debug("NotificationService processReceivedMsg ignored event: \(res.responseType)")
return nil return nil
} }
} }
@@ -675,22 +308,17 @@ func updateNetCfg() {
try setNetworkConfig(networkConfig) try setNetworkConfig(networkConfig)
networkConfig = newNetConfig networkConfig = newNetConfig
} catch { } catch {
logger.error("NotificationService apply changed network config error: \(responseError(error))") logger.error("NotificationService apply changed network config error: \(responseError(error), privacy: .public)")
} }
} }
} }
func apiGetActiveUser() -> User? { func apiGetActiveUser() -> User? {
let r = sendSimpleXCmd(.showActiveUser) let r = sendSimpleXCmd(.showActiveUser)
logger.debug("apiGetActiveUser sendSimpleXCmd response: \(r.responseType)") logger.debug("apiGetActiveUser sendSimpleXCmd response: \(String(describing: r))")
switch r { switch r {
case let .activeUser(user): return user case let .activeUser(user): return user
case .chatCmdError(_, .error(.noActiveUser)): case .chatCmdError(_, .error(.noActiveUser)): return nil
logger.debug("apiGetActiveUser sendSimpleXCmd no active user")
return nil
case let .chatCmdError(_, err):
logger.debug("apiGetActiveUser sendSimpleXCmd error: \(String(describing: err))")
return nil
default: default:
logger.error("NotificationService apiGetActiveUser unexpected response: \(String(describing: r))") logger.error("NotificationService apiGetActiveUser unexpected response: \(String(describing: r))")
return nil return nil
@@ -698,7 +326,7 @@ func apiGetActiveUser() -> User? {
} }
func apiStartChat() throws -> Bool { func apiStartChat() throws -> Bool {
let r = sendSimpleXCmd(.startChat(mainApp: false)) let r = sendSimpleXCmd(.startChat(subscribe: false, expire: false, xftp: false))
switch r { switch r {
case .chatStarted: return true case .chatStarted: return true
case .chatRunning: return false case .chatRunning: return false
@@ -706,21 +334,6 @@ func apiStartChat() throws -> Bool {
} }
} }
func apiActivateChat() -> Bool {
chatReopenStore()
let r = sendSimpleXCmd(.apiActivateChat(restoreChat: false))
if case .cmdOk = r { return true }
logger.error("NotificationService apiActivateChat error: \(String(describing: r))")
return false
}
func apiSuspendChat(timeoutMicroseconds: Int) -> Bool {
let r = sendSimpleXCmd(.apiSuspendChat(timeoutMicroseconds: timeoutMicroseconds))
if case .cmdOk = r { return true }
logger.error("NotificationService apiSuspendChat error: \(String(describing: r))")
return false
}
func apiSetTempFolder(tempFolder: String) throws { func apiSetTempFolder(tempFolder: String) throws {
let r = sendSimpleXCmd(.setTempFolder(tempFolder: tempFolder)) let r = sendSimpleXCmd(.setTempFolder(tempFolder: tempFolder))
if case .cmdOk = r { return } if case .cmdOk = r { return }
@@ -751,13 +364,12 @@ func apiGetNtfMessage(nonce: String, encNtfInfo: String) -> NtfMessages? {
return nil return nil
} }
let r = sendSimpleXCmd(.apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo)) let r = sendSimpleXCmd(.apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo))
if case let .ntfMessages(user, connEntity_, msgTs, ntfMessages) = r, let user = user { if case let .ntfMessages(user, connEntity, msgTs, ntfMessages) = r, let user = user {
logger.debug("apiGetNtfMessage response ntfMessages: \(ntfMessages.count)") return NtfMessages(user: user, connEntity: connEntity, msgTs: msgTs, ntfMessages: ntfMessages)
return NtfMessages(user: user, connEntity_: connEntity_, msgTs: msgTs, ntfMessages: ntfMessages)
} else if case let .chatCmdError(_, error) = r { } else if case let .chatCmdError(_, error) = r {
logger.debug("apiGetNtfMessage error response: \(String.init(describing: error))") logger.debug("apiGetNtfMessage error response: \(String.init(describing: error))")
} else { } else {
logger.debug("apiGetNtfMessage ignored response: \(r.responseType) \(String.init(describing: r))") logger.debug("apiGetNtfMessage ignored response: \(r.responseType, privacy: .public) \(String.init(describing: r), privacy: .private)")
} }
return nil return nil
} }
@@ -793,11 +405,11 @@ func setNetworkConfig(_ cfg: NetCfg) throws {
struct NtfMessages { struct NtfMessages {
var user: User var user: User
var connEntity_: ConnectionEntity? var connEntity: ConnectionEntity?
var msgTs: Date? var msgTs: Date?
var ntfMessages: [NtfMsgInfo] var ntfMessages: [NtfMsgInfo]
var ntfsEnabled: Bool { var ntfsEnabled: Bool {
user.showNotifications && (connEntity_?.ntfsEnabled ?? false) user.showNotifications && (connEntity?.ntfsEnabled ?? false)
} }
} }
+32 -56
View File
@@ -116,15 +116,13 @@
5CC2C0FF2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FD2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings */; }; 5CC2C0FF2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FD2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings */; };
5CC868F329EB540C0017BBFD /* CIRcvDecryptionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC868F229EB540C0017BBFD /* CIRcvDecryptionError.swift */; }; 5CC868F329EB540C0017BBFD /* CIRcvDecryptionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC868F229EB540C0017BBFD /* CIRcvDecryptionError.swift */; };
5CCB939C297EFCB100399E78 /* NavStackCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCB939B297EFCB100399E78 /* NavStackCompat.swift */; }; 5CCB939C297EFCB100399E78 /* NavStackCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCB939B297EFCB100399E78 /* NavStackCompat.swift */; };
5CCD2C322B5C29B400F76440 /* libHSsimplex-chat-5.4.4.0-F9Am8s51dKw4ZcRcMkpqsb-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCD2C2D2B5C29B400F76440 /* libHSsimplex-chat-5.4.4.0-F9Am8s51dKw4ZcRcMkpqsb-ghc9.6.3.a */; };
5CCD2C332B5C29B400F76440 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCD2C2E2B5C29B400F76440 /* libffi.a */; };
5CCD2C342B5C29B400F76440 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCD2C2F2B5C29B400F76440 /* libgmp.a */; };
5CCD2C352B5C29B400F76440 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCD2C302B5C29B400F76440 /* libgmpxx.a */; };
5CCD2C362B5C29B400F76440 /* libHSsimplex-chat-5.4.4.0-F9Am8s51dKw4ZcRcMkpqsb.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CCD2C312B5C29B400F76440 /* libHSsimplex-chat-5.4.4.0-F9Am8s51dKw4ZcRcMkpqsb.a */; };
5CCD403427A5F6DF00368C90 /* AddContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* AddContactView.swift */; }; 5CCD403427A5F6DF00368C90 /* AddContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* AddContactView.swift */; };
5CCD403727A5F9A200368C90 /* ScanToConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */; }; 5CCD403727A5F9A200368C90 /* ScanToConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */; };
5CD67B8F2B0E858A00C510B1 /* hs_init.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CD67B8D2B0E858A00C510B1 /* hs_init.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5CDA5A2D2B04FE2D00A71D61 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CDA5A282B04FE2D00A71D61 /* libgmp.a */; };
5CD67B902B0E858A00C510B1 /* hs_init.c in Sources */ = {isa = PBXBuildFile; fileRef = 5CD67B8E2B0E858A00C510B1 /* hs_init.c */; }; 5CDA5A2E2B04FE2D00A71D61 /* libHSsimplex-chat-5.4.0.3-rODxCBVsb2BkD1fnTAqXL-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CDA5A292B04FE2D00A71D61 /* libHSsimplex-chat-5.4.0.3-rODxCBVsb2BkD1fnTAqXL-ghc9.6.3.a */; };
5CDA5A2F2B04FE2D00A71D61 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CDA5A2A2B04FE2D00A71D61 /* libffi.a */; };
5CDA5A302B04FE2D00A71D61 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CDA5A2B2B04FE2D00A71D61 /* libgmpxx.a */; };
5CDA5A312B04FE2D00A71D61 /* libHSsimplex-chat-5.4.0.3-rODxCBVsb2BkD1fnTAqXL.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CDA5A2C2B04FE2D00A71D61 /* libHSsimplex-chat-5.4.0.3-rODxCBVsb2BkD1fnTAqXL.a */; };
5CDCAD482818589900503DA2 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CDCAD472818589900503DA2 /* NotificationService.swift */; }; 5CDCAD482818589900503DA2 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CDCAD472818589900503DA2 /* NotificationService.swift */; };
5CE2BA702845308900EC33A6 /* SimpleXChat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; }; 5CE2BA702845308900EC33A6 /* SimpleXChat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; };
5CE2BA712845308900EC33A6 /* SimpleXChat.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 5CE2BA712845308900EC33A6 /* SimpleXChat.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@@ -150,9 +148,6 @@
5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; }; 5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; };
5CEBD7462A5C0A8F00665FE2 /* KeyboardPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */; }; 5CEBD7462A5C0A8F00665FE2 /* KeyboardPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */; };
5CEBD7482A5F115D00665FE2 /* SetDeliveryReceiptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */; }; 5CEBD7482A5F115D00665FE2 /* SetDeliveryReceiptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */; };
5CF9371E2B23429500E1D781 /* ConcurrentQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF9371D2B23429500E1D781 /* ConcurrentQueue.swift */; };
5CF937202B24DE8C00E1D781 /* SharedFileSubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */; };
5CF937232B2503D000E1D781 /* NSESubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF937212B25034A00E1D781 /* NSESubscriber.swift */; };
5CFA59C42860BC6200863A68 /* MigrateToAppGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */; }; 5CFA59C42860BC6200863A68 /* MigrateToAppGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */; };
5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59CF286477B400863A68 /* ChatArchiveView.swift */; }; 5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59CF286477B400863A68 /* ChatArchiveView.swift */; };
5CFE0921282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; }; 5CFE0921282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; };
@@ -188,7 +183,6 @@
64D0C2C629FAC1EC00B38D5F /* AddContactLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C529FAC1EC00B38D5F /* AddContactLearnMore.swift */; }; 64D0C2C629FAC1EC00B38D5F /* AddContactLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C529FAC1EC00B38D5F /* AddContactLearnMore.swift */; };
64E972072881BB22008DBC02 /* CIGroupInvitationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */; }; 64E972072881BB22008DBC02 /* CIGroupInvitationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */; };
64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */; }; 64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */; };
8C05382E2B39887E006436DC /* VideoUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C05382D2B39887E006436DC /* VideoUtils.swift */; };
D7197A1829AE89660055C05A /* WebRTC in Frameworks */ = {isa = PBXBuildFile; productRef = D7197A1729AE89660055C05A /* WebRTC */; }; D7197A1829AE89660055C05A /* WebRTC in Frameworks */ = {isa = PBXBuildFile; productRef = D7197A1729AE89660055C05A /* WebRTC */; };
D72A9088294BD7A70047C86D /* NativeTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72A9087294BD7A70047C86D /* NativeTextEditor.swift */; }; D72A9088294BD7A70047C86D /* NativeTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72A9087294BD7A70047C86D /* NativeTextEditor.swift */; };
D741547829AF89AF0022400A /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D741547729AF89AF0022400A /* StoreKit.framework */; }; D741547829AF89AF0022400A /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D741547729AF89AF0022400A /* StoreKit.framework */; };
@@ -403,15 +397,13 @@
5CC2C0FE2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = "ru.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; }; 5CC2C0FE2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = "ru.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
5CC868F229EB540C0017BBFD /* CIRcvDecryptionError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIRcvDecryptionError.swift; sourceTree = "<group>"; }; 5CC868F229EB540C0017BBFD /* CIRcvDecryptionError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIRcvDecryptionError.swift; sourceTree = "<group>"; };
5CCB939B297EFCB100399E78 /* NavStackCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavStackCompat.swift; sourceTree = "<group>"; }; 5CCB939B297EFCB100399E78 /* NavStackCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavStackCompat.swift; sourceTree = "<group>"; };
5CCD2C2D2B5C29B400F76440 /* libHSsimplex-chat-5.4.4.0-F9Am8s51dKw4ZcRcMkpqsb-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.4.0-F9Am8s51dKw4ZcRcMkpqsb-ghc9.6.3.a"; sourceTree = "<group>"; };
5CCD2C2E2B5C29B400F76440 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5CCD2C2F2B5C29B400F76440 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5CCD2C302B5C29B400F76440 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5CCD2C312B5C29B400F76440 /* libHSsimplex-chat-5.4.4.0-F9Am8s51dKw4ZcRcMkpqsb.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.4.0-F9Am8s51dKw4ZcRcMkpqsb.a"; sourceTree = "<group>"; };
5CCD403327A5F6DF00368C90 /* AddContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactView.swift; sourceTree = "<group>"; }; 5CCD403327A5F6DF00368C90 /* AddContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactView.swift; sourceTree = "<group>"; };
5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanToConnectView.swift; sourceTree = "<group>"; }; 5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanToConnectView.swift; sourceTree = "<group>"; };
5CD67B8D2B0E858A00C510B1 /* hs_init.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hs_init.h; sourceTree = "<group>"; }; 5CDA5A282B04FE2D00A71D61 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5CD67B8E2B0E858A00C510B1 /* hs_init.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hs_init.c; sourceTree = "<group>"; }; 5CDA5A292B04FE2D00A71D61 /* libHSsimplex-chat-5.4.0.3-rODxCBVsb2BkD1fnTAqXL-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.0.3-rODxCBVsb2BkD1fnTAqXL-ghc9.6.3.a"; sourceTree = "<group>"; };
5CDA5A2A2B04FE2D00A71D61 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5CDA5A2B2B04FE2D00A71D61 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5CDA5A2C2B04FE2D00A71D61 /* libHSsimplex-chat-5.4.0.3-rODxCBVsb2BkD1fnTAqXL.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.0.3-rODxCBVsb2BkD1fnTAqXL.a"; sourceTree = "<group>"; };
5CDCAD452818589900503DA2 /* SimpleX NSE.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "SimpleX NSE.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 5CDCAD452818589900503DA2 /* SimpleX NSE.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "SimpleX NSE.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
5CDCAD472818589900503DA2 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; }; 5CDCAD472818589900503DA2 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
5CDCAD492818589900503DA2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 5CDCAD492818589900503DA2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -438,9 +430,6 @@
5CEACCEC27DEA495000BD591 /* MsgContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MsgContentView.swift; sourceTree = "<group>"; }; 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MsgContentView.swift; sourceTree = "<group>"; };
5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardPadding.swift; sourceTree = "<group>"; }; 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardPadding.swift; sourceTree = "<group>"; };
5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDeliveryReceiptsView.swift; sourceTree = "<group>"; }; 5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDeliveryReceiptsView.swift; sourceTree = "<group>"; };
5CF9371D2B23429500E1D781 /* ConcurrentQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConcurrentQueue.swift; sourceTree = "<group>"; };
5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedFileSubscriber.swift; sourceTree = "<group>"; };
5CF937212B25034A00E1D781 /* NSESubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSESubscriber.swift; sourceTree = "<group>"; };
5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToAppGroupView.swift; sourceTree = "<group>"; }; 5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToAppGroupView.swift; sourceTree = "<group>"; };
5CFA59CF286477B400863A68 /* ChatArchiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatArchiveView.swift; sourceTree = "<group>"; }; 5CFA59CF286477B400863A68 /* ChatArchiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatArchiveView.swift; sourceTree = "<group>"; };
5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ZoomableScrollView.swift; path = Shared/Views/ZoomableScrollView.swift; sourceTree = SOURCE_ROOT; }; 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ZoomableScrollView.swift; path = Shared/Views/ZoomableScrollView.swift; sourceTree = SOURCE_ROOT; };
@@ -477,7 +466,6 @@
64DAE1502809D9F5000DA960 /* FileUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = "<group>"; }; 64DAE1502809D9F5000DA960 /* FileUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = "<group>"; };
64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIGroupInvitationView.swift; sourceTree = "<group>"; }; 64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIGroupInvitationView.swift; sourceTree = "<group>"; };
64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncognitoHelp.swift; sourceTree = "<group>"; }; 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncognitoHelp.swift; sourceTree = "<group>"; };
8C05382D2B39887E006436DC /* VideoUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoUtils.swift; sourceTree = "<group>"; };
D72A9087294BD7A70047C86D /* NativeTextEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeTextEditor.swift; sourceTree = "<group>"; }; D72A9087294BD7A70047C86D /* NativeTextEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeTextEditor.swift; sourceTree = "<group>"; };
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; }; 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; }; 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; };
@@ -519,13 +507,13 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
5CCD2C352B5C29B400F76440 /* libgmpxx.a in Frameworks */, 5CDA5A302B04FE2D00A71D61 /* libgmpxx.a in Frameworks */,
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */, 5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
5CCD2C342B5C29B400F76440 /* libgmp.a in Frameworks */, 5CDA5A2D2B04FE2D00A71D61 /* libgmp.a in Frameworks */,
5CCD2C322B5C29B400F76440 /* libHSsimplex-chat-5.4.4.0-F9Am8s51dKw4ZcRcMkpqsb-ghc9.6.3.a in Frameworks */, 5CDA5A2E2B04FE2D00A71D61 /* libHSsimplex-chat-5.4.0.3-rODxCBVsb2BkD1fnTAqXL-ghc9.6.3.a in Frameworks */,
5CDA5A2F2B04FE2D00A71D61 /* libffi.a in Frameworks */,
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */, 5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
5CCD2C362B5C29B400F76440 /* libHSsimplex-chat-5.4.4.0-F9Am8s51dKw4ZcRcMkpqsb.a in Frameworks */, 5CDA5A312B04FE2D00A71D61 /* libHSsimplex-chat-5.4.0.3-rODxCBVsb2BkD1fnTAqXL.a in Frameworks */,
5CCD2C332B5C29B400F76440 /* libffi.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -587,11 +575,11 @@
5C764E5C279C70B7000C6508 /* Libraries */ = { 5C764E5C279C70B7000C6508 /* Libraries */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5CCD2C2E2B5C29B400F76440 /* libffi.a */, 5CDA5A2A2B04FE2D00A71D61 /* libffi.a */,
5CCD2C2F2B5C29B400F76440 /* libgmp.a */, 5CDA5A282B04FE2D00A71D61 /* libgmp.a */,
5CCD2C302B5C29B400F76440 /* libgmpxx.a */, 5CDA5A2B2B04FE2D00A71D61 /* libgmpxx.a */,
5CCD2C2D2B5C29B400F76440 /* libHSsimplex-chat-5.4.4.0-F9Am8s51dKw4ZcRcMkpqsb-ghc9.6.3.a */, 5CDA5A292B04FE2D00A71D61 /* libHSsimplex-chat-5.4.0.3-rODxCBVsb2BkD1fnTAqXL-ghc9.6.3.a */,
5CCD2C312B5C29B400F76440 /* libHSsimplex-chat-5.4.4.0-F9Am8s51dKw4ZcRcMkpqsb.a */, 5CDA5A2C2B04FE2D00A71D61 /* libHSsimplex-chat-5.4.0.3-rODxCBVsb2BkD1fnTAqXL.a */,
); );
path = Libraries; path = Libraries;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -616,7 +604,6 @@
5C35CFC727B2782E00FB6C6D /* BGManager.swift */, 5C35CFC727B2782E00FB6C6D /* BGManager.swift */,
5C35CFCA27B2E91D00FB6C6D /* NtfManager.swift */, 5C35CFCA27B2E91D00FB6C6D /* NtfManager.swift */,
5CB346E42868AA7F001FD2EF /* SuspendChat.swift */, 5CB346E42868AA7F001FD2EF /* SuspendChat.swift */,
5CF937212B25034A00E1D781 /* NSESubscriber.swift */,
5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */, 5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */,
5C93293E2928E0FD0090FFF9 /* AudioRecPlay.swift */, 5C93293E2928E0FD0090FFF9 /* AudioRecPlay.swift */,
5CBD2859295711D700EC2CF4 /* ImageUtils.swift */, 5CBD2859295711D700EC2CF4 /* ImageUtils.swift */,
@@ -644,7 +631,6 @@
64466DCB29FFE3E800E3D48D /* MailView.swift */, 64466DCB29FFE3E800E3D48D /* MailView.swift */,
64C3B0202A0D359700E19930 /* CustomTimePicker.swift */, 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */,
5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */, 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */,
8C05382D2B39887E006436DC /* VideoUtils.swift */,
); );
path = Helpers; path = Helpers;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -798,7 +784,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5CDCAD5128186DE400503DA2 /* SimpleX NSE.entitlements */, 5CDCAD5128186DE400503DA2 /* SimpleX NSE.entitlements */,
5CF9371D2B23429500E1D781 /* ConcurrentQueue.swift */,
5CDCAD472818589900503DA2 /* NotificationService.swift */, 5CDCAD472818589900503DA2 /* NotificationService.swift */,
5CDCAD492818589900503DA2 /* Info.plist */, 5CDCAD492818589900503DA2 /* Info.plist */,
5CB0BA862826CB3A00B3292C /* InfoPlist.strings */, 5CB0BA862826CB3A00B3292C /* InfoPlist.strings */,
@@ -819,13 +804,10 @@
64DAE1502809D9F5000DA960 /* FileUtils.swift */, 64DAE1502809D9F5000DA960 /* FileUtils.swift */,
5C9D81182AA7A4F1001D49FD /* CryptoFile.swift */, 5C9D81182AA7A4F1001D49FD /* CryptoFile.swift */,
5C00168028C4FE760094D739 /* KeyChain.swift */, 5C00168028C4FE760094D739 /* KeyChain.swift */,
5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */,
5CE2BA76284530BF00EC33A6 /* SimpleXChat.h */, 5CE2BA76284530BF00EC33A6 /* SimpleXChat.h */,
5CE2BA8A2845332200EC33A6 /* SimpleX.h */, 5CE2BA8A2845332200EC33A6 /* SimpleX.h */,
5CE2BA78284530CC00EC33A6 /* SimpleXChat.docc */, 5CE2BA78284530CC00EC33A6 /* SimpleXChat.docc */,
5CE2BA96284537A800EC33A6 /* dummy.m */, 5CE2BA96284537A800EC33A6 /* dummy.m */,
5CD67B8D2B0E858A00C510B1 /* hs_init.h */,
5CD67B8E2B0E858A00C510B1 /* hs_init.c */,
); );
path = SimpleXChat; path = SimpleXChat;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -910,7 +892,6 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
5CE2BA77284530BF00EC33A6 /* SimpleXChat.h in Headers */, 5CE2BA77284530BF00EC33A6 /* SimpleXChat.h in Headers */,
5CD67B8F2B0E858A00C510B1 /* hs_init.h in Headers */,
5CE2BA952845354B00EC33A6 /* SimpleX.h in Headers */, 5CE2BA952845354B00EC33A6 /* SimpleX.h in Headers */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@@ -1192,7 +1173,6 @@
5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */, 5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */,
5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */, 5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */,
6442E0BA287F169300CEC0F9 /* AddGroupView.swift in Sources */, 6442E0BA287F169300CEC0F9 /* AddGroupView.swift in Sources */,
5CF937232B2503D000E1D781 /* NSESubscriber.swift in Sources */,
6419EC582AB97507004A607A /* CIMemberCreatedContactView.swift in Sources */, 6419EC582AB97507004A607A /* CIMemberCreatedContactView.swift in Sources */,
64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */, 64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */,
64466DCC29FFE3E800E3D48D /* MailView.swift in Sources */, 64466DCC29FFE3E800E3D48D /* MailView.swift in Sources */,
@@ -1215,7 +1195,6 @@
5CCD403727A5F9A200368C90 /* ScanToConnectView.swift in Sources */, 5CCD403727A5F9A200368C90 /* ScanToConnectView.swift in Sources */,
5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */, 5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */,
649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */, 649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */,
8C05382E2B39887E006436DC /* VideoUtils.swift in Sources */,
5CADE79C292131E900072E13 /* ContactPreferencesView.swift in Sources */, 5CADE79C292131E900072E13 /* ContactPreferencesView.swift in Sources */,
5CB346E52868AA7F001FD2EF /* SuspendChat.swift in Sources */, 5CB346E52868AA7F001FD2EF /* SuspendChat.swift in Sources */,
5C9C2DA52894777E00CC63B1 /* GroupProfileView.swift in Sources */, 5C9C2DA52894777E00CC63B1 /* GroupProfileView.swift in Sources */,
@@ -1273,7 +1252,6 @@
files = ( files = (
5CDCAD482818589900503DA2 /* NotificationService.swift in Sources */, 5CDCAD482818589900503DA2 /* NotificationService.swift in Sources */,
5CFE0922282EEAF60002594B /* ZoomableScrollView.swift in Sources */, 5CFE0922282EEAF60002594B /* ZoomableScrollView.swift in Sources */,
5CF9371E2B23429500E1D781 /* ConcurrentQueue.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -1281,11 +1259,9 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
5CF937202B24DE8C00E1D781 /* SharedFileSubscriber.swift in Sources */,
5C00168128C4FE760094D739 /* KeyChain.swift in Sources */, 5C00168128C4FE760094D739 /* KeyChain.swift in Sources */,
5CE2BA97284537A800EC33A6 /* dummy.m in Sources */, 5CE2BA97284537A800EC33A6 /* dummy.m in Sources */,
5CE2BA922845340900EC33A6 /* FileUtils.swift in Sources */, 5CE2BA922845340900EC33A6 /* FileUtils.swift in Sources */,
5CD67B902B0E858A00C510B1 /* hs_init.c in Sources */,
5CE2BA91284533A300EC33A6 /* Notifications.swift in Sources */, 5CE2BA91284533A300EC33A6 /* Notifications.swift in Sources */,
5CE2BA79284530CC00EC33A6 /* SimpleXChat.docc in Sources */, 5CE2BA79284530CC00EC33A6 /* SimpleXChat.docc in Sources */,
5CE2BA90284533A300EC33A6 /* JSON.swift in Sources */, 5CE2BA90284533A300EC33A6 /* JSON.swift in Sources */,
@@ -1518,7 +1494,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 190; CURRENT_PROJECT_VERSION = 181;
DEVELOPMENT_TEAM = 5NN7GUYB6T; DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@@ -1540,7 +1516,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 5.4.4; MARKETING_VERSION = 5.4;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX; PRODUCT_NAME = SimpleX;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@@ -1561,7 +1537,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 190; CURRENT_PROJECT_VERSION = 181;
DEVELOPMENT_TEAM = 5NN7GUYB6T; DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@@ -1583,7 +1559,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 5.4.4; MARKETING_VERSION = 5.4;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX; PRODUCT_NAME = SimpleX;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@@ -1642,7 +1618,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 190; CURRENT_PROJECT_VERSION = 181;
DEVELOPMENT_TEAM = 5NN7GUYB6T; DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -1655,7 +1631,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 5.4.4; MARKETING_VERSION = 5.4;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1674,7 +1650,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 190; CURRENT_PROJECT_VERSION = 181;
DEVELOPMENT_TEAM = 5NN7GUYB6T; DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -1687,7 +1663,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 5.4.4; MARKETING_VERSION = 5.4;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1706,7 +1682,7 @@
APPLICATION_EXTENSION_API_ONLY = YES; APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 190; CURRENT_PROJECT_VERSION = 181;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T; DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1730,7 +1706,7 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Libraries/sim", "$(PROJECT_DIR)/Libraries/sim",
); );
MARKETING_VERSION = 5.4.4; MARKETING_VERSION = 5.4;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos; SDKROOT = iphoneos;
@@ -1752,7 +1728,7 @@
APPLICATION_EXTENSION_API_ONLY = YES; APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 190; CURRENT_PROJECT_VERSION = 181;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T; DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1776,7 +1752,7 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Libraries/sim", "$(PROJECT_DIR)/Libraries/sim",
); );
MARKETING_VERSION = 5.4.4; MARKETING_VERSION = 5.4;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos; SDKROOT = iphoneos;
@@ -41,7 +41,7 @@
</Testables> </Testables>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0" launchStyle = "0"
@@ -2,7 +2,7 @@
<Scheme <Scheme
LastUpgradeVersion = "1400" LastUpgradeVersion = "1400"
wasCreatedForAppExtension = "YES" wasCreatedForAppExtension = "YES"
version = "1.3"> version = "2.0">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"> buildImplicitDependencies = "YES">
@@ -47,14 +47,16 @@
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0" launchStyle = "0"
askForAppToLaunch = "Yes"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES" debugDocumentVersioning = "YES"
debugServiceExtension = "internal" debugServiceExtension = "internal"
allowLocationSimulation = "YES"> allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable <BuildableProductRunnable
runnableDebuggingMode = "0"> runnableDebuggingMode = "0">
<BuildableReference <BuildableReference
+3 -14
View File
@@ -12,16 +12,12 @@ private var chatController: chat_ctrl?
private var migrationResult: (Bool, DBMigrationResult)? private var migrationResult: (Bool, DBMigrationResult)?
public func hasChatCtrl() -> Bool { public func getChatCtrl(_ useKey: String? = nil) -> chat_ctrl {
chatController != nil
}
public func getChatCtrl() -> chat_ctrl {
if let controller = chatController { return controller } if let controller = chatController { return controller }
fatalError("chat controller not initialized") fatalError("chat controller not initialized")
} }
public func chatMigrateInit(_ useKey: String? = nil, confirmMigrations: MigrationConfirmation? = nil, backgroundMode: Bool = false) -> (Bool, DBMigrationResult) { public func chatMigrateInit(_ useKey: String? = nil, confirmMigrations: MigrationConfirmation? = nil) -> (Bool, DBMigrationResult) {
if let res = migrationResult { return res } if let res = migrationResult { return res }
let dbPath = getAppDatabasePath().path let dbPath = getAppDatabasePath().path
var dbKey = "" var dbKey = ""
@@ -45,7 +41,7 @@ public func chatMigrateInit(_ useKey: String? = nil, confirmMigrations: Migratio
var cKey = dbKey.cString(using: .utf8)! var cKey = dbKey.cString(using: .utf8)!
var cConfirm = confirm.rawValue.cString(using: .utf8)! var cConfirm = confirm.rawValue.cString(using: .utf8)!
// the last parameter of chat_migrate_init is used to return the pointer to chat controller // the last parameter of chat_migrate_init is used to return the pointer to chat controller
let cjson = chat_migrate_init_key(&cPath, &cKey, 1, &cConfirm, backgroundMode ? 1 : 0, &chatController)! let cjson = chat_migrate_init(&cPath, &cKey, &cConfirm, &chatController)!
let dbRes = dbMigrationResult(fromCString(cjson)) let dbRes = dbMigrationResult(fromCString(cjson))
let encrypted = dbKey != "" let encrypted = dbKey != ""
let keychainErr = dbRes == .ok && useKeychain && encrypted && !kcDatabasePassword.set(dbKey) let keychainErr = dbRes == .ok && useKeychain && encrypted && !kcDatabasePassword.set(dbKey)
@@ -61,13 +57,6 @@ public func chatCloseStore() {
} }
} }
public func chatReopenStore() {
let err = fromCString(chat_reopen_store(getChatCtrl()))
if err != "" {
logger.error("chatReopenStore error: \(err)")
}
}
public func resetChatCtrl() { public func resetChatCtrl() {
chatController = nil chatController = nil
migrationResult = nil migrationResult = nil
+22 -32
View File
@@ -25,9 +25,9 @@ public enum ChatCommand {
case apiMuteUser(userId: Int64) case apiMuteUser(userId: Int64)
case apiUnmuteUser(userId: Int64) case apiUnmuteUser(userId: Int64)
case apiDeleteUser(userId: Int64, delSMPQueues: Bool, viewPwd: String?) case apiDeleteUser(userId: Int64, delSMPQueues: Bool, viewPwd: String?)
case startChat(mainApp: Bool) case startChat(subscribe: Bool, expire: Bool, xftp: Bool)
case apiStopChat case apiStopChat
case apiActivateChat(restoreChat: Bool) case apiActivateChat
case apiSuspendChat(timeoutMicroseconds: Int) case apiSuspendChat(timeoutMicroseconds: Int)
case setTempFolder(tempFolder: String) case setTempFolder(tempFolder: String)
case setFilesFolder(filesFolder: String) case setFilesFolder(filesFolder: String)
@@ -154,9 +154,9 @@ public enum ChatCommand {
case let .apiMuteUser(userId): return "/_mute user \(userId)" case let .apiMuteUser(userId): return "/_mute user \(userId)"
case let .apiUnmuteUser(userId): return "/_unmute user \(userId)" case let .apiUnmuteUser(userId): return "/_unmute user \(userId)"
case let .apiDeleteUser(userId, delSMPQueues, viewPwd): return "/_delete user \(userId) del_smp=\(onOff(delSMPQueues))\(maybePwd(viewPwd))" case let .apiDeleteUser(userId, delSMPQueues, viewPwd): return "/_delete user \(userId) del_smp=\(onOff(delSMPQueues))\(maybePwd(viewPwd))"
case let .startChat(mainApp): return "/_start main=\(onOff(mainApp))" case let .startChat(subscribe, expire, xftp): return "/_start subscribe=\(onOff(subscribe)) expire=\(onOff(expire)) xftp=\(onOff(xftp))"
case .apiStopChat: return "/_stop" case .apiStopChat: return "/_stop"
case let .apiActivateChat(restore): return "/_app activate restore=\(onOff(restore))" case .apiActivateChat: return "/_app activate"
case let .apiSuspendChat(timeoutMicroseconds): return "/_app suspend \(timeoutMicroseconds)" case let .apiSuspendChat(timeoutMicroseconds): return "/_app suspend \(timeoutMicroseconds)"
case let .setTempFolder(tempFolder): return "/_temp_folder \(tempFolder)" case let .setTempFolder(tempFolder): return "/_temp_folder \(tempFolder)"
case let .setFilesFolder(filesFolder): return "/_files_folder \(filesFolder)" case let .setFilesFolder(filesFolder): return "/_files_folder \(filesFolder)"
@@ -505,8 +505,8 @@ public enum ChatResponse: Decodable, Error {
case invitation(user: UserRef, connReqInvitation: String, connection: PendingContactConnection) case invitation(user: UserRef, connReqInvitation: String, connection: PendingContactConnection)
case connectionIncognitoUpdated(user: UserRef, toConnection: PendingContactConnection) case connectionIncognitoUpdated(user: UserRef, toConnection: PendingContactConnection)
case connectionPlan(user: UserRef, connectionPlan: ConnectionPlan) case connectionPlan(user: UserRef, connectionPlan: ConnectionPlan)
case sentConfirmation(user: UserRef, connection: PendingContactConnection) case sentConfirmation(user: UserRef)
case sentInvitation(user: UserRef, connection: PendingContactConnection) case sentInvitation(user: UserRef)
case sentInvitationToContact(user: UserRef, contact: Contact, customUserProfile: Profile?) case sentInvitationToContact(user: UserRef, contact: Contact, customUserProfile: Profile?)
case contactAlreadyExists(user: UserRef, contact: Contact) case contactAlreadyExists(user: UserRef, contact: Contact)
case contactRequestAlreadyAccepted(user: UserRef, contact: Contact) case contactRequestAlreadyAccepted(user: UserRef, contact: Contact)
@@ -604,16 +604,16 @@ public enum ChatResponse: Decodable, Error {
case callInvitations(callInvitations: [RcvCallInvitation]) case callInvitations(callInvitations: [RcvCallInvitation])
case ntfTokenStatus(status: NtfTknStatus) case ntfTokenStatus(status: NtfTknStatus)
case ntfToken(token: DeviceToken, status: NtfTknStatus, ntfMode: NotificationsMode) case ntfToken(token: DeviceToken, status: NtfTknStatus, ntfMode: NotificationsMode)
case ntfMessages(user_: User?, connEntity_: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo]) case ntfMessages(user_: User?, connEntity: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo])
case ntfMessage(user: UserRef, connEntity: ConnectionEntity, ntfMessage: NtfMsgInfo) case newContactConnection(user: UserRef, connection: PendingContactConnection)
case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection) case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection)
// remote desktop responses/events // remote desktop responses/events
case remoteCtrlList(remoteCtrls: [RemoteCtrlInfo]) case remoteCtrlList(remoteCtrls: [RemoteCtrlInfo])
case remoteCtrlFound(remoteCtrl: RemoteCtrlInfo, ctrlAppInfo_: CtrlAppInfo?, appVersion: String, compatible: Bool) case remoteCtrlFound(remoteCtrl: RemoteCtrlInfo)
case remoteCtrlConnecting(remoteCtrl_: RemoteCtrlInfo?, ctrlAppInfo: CtrlAppInfo, appVersion: String) case remoteCtrlConnecting(remoteCtrl_: RemoteCtrlInfo?, ctrlAppInfo: CtrlAppInfo, appVersion: String)
case remoteCtrlSessionCode(remoteCtrl_: RemoteCtrlInfo?, sessionCode: String) case remoteCtrlSessionCode(remoteCtrl_: RemoteCtrlInfo?, sessionCode: String)
case remoteCtrlConnected(remoteCtrl: RemoteCtrlInfo) case remoteCtrlConnected(remoteCtrl: RemoteCtrlInfo)
case remoteCtrlStopped(rcsState: RemoteCtrlSessionState, rcStopReason: RemoteCtrlStopReason) case remoteCtrlStopped
// misc // misc
case versionInfo(versionInfo: CoreVersionInfo, chatMigrations: [UpMigration], agentMigrations: [UpMigration]) case versionInfo(versionInfo: CoreVersionInfo, chatMigrations: [UpMigration], agentMigrations: [UpMigration])
case cmdOk(user: UserRef?) case cmdOk(user: UserRef?)
@@ -752,7 +752,7 @@ public enum ChatResponse: Decodable, Error {
case .ntfTokenStatus: return "ntfTokenStatus" case .ntfTokenStatus: return "ntfTokenStatus"
case .ntfToken: return "ntfToken" case .ntfToken: return "ntfToken"
case .ntfMessages: return "ntfMessages" case .ntfMessages: return "ntfMessages"
case .ntfMessage: return "ntfMessage" case .newContactConnection: return "newContactConnection"
case .contactConnectionDeleted: return "contactConnectionDeleted" case .contactConnectionDeleted: return "contactConnectionDeleted"
case .remoteCtrlList: return "remoteCtrlList" case .remoteCtrlList: return "remoteCtrlList"
case .remoteCtrlFound: return "remoteCtrlFound" case .remoteCtrlFound: return "remoteCtrlFound"
@@ -803,11 +803,11 @@ public enum ChatResponse: Decodable, Error {
case let .contactCode(u, contact, connectionCode): return withUser(u, "contact: \(String(describing: contact))\nconnectionCode: \(connectionCode)") case let .contactCode(u, contact, connectionCode): return withUser(u, "contact: \(String(describing: contact))\nconnectionCode: \(connectionCode)")
case let .groupMemberCode(u, groupInfo, member, connectionCode): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionCode: \(connectionCode)") case let .groupMemberCode(u, groupInfo, member, connectionCode): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionCode: \(connectionCode)")
case let .connectionVerified(u, verified, expectedCode): return withUser(u, "verified: \(verified)\nconnectionCode: \(expectedCode)") 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 .invitation(u, connReqInvitation, _): return withUser(u, connReqInvitation)
case let .connectionIncognitoUpdated(u, toConnection): return withUser(u, String(describing: toConnection)) case let .connectionIncognitoUpdated(u, toConnection): return withUser(u, String(describing: toConnection))
case let .connectionPlan(u, connectionPlan): return withUser(u, String(describing: connectionPlan)) case let .connectionPlan(u, connectionPlan): return withUser(u, String(describing: connectionPlan))
case let .sentConfirmation(u, connection): return withUser(u, String(describing: connection)) case .sentConfirmation: return noDetails
case let .sentInvitation(u, connection): return withUser(u, String(describing: connection)) case .sentInvitation: return noDetails
case let .sentInvitationToContact(u, contact, _): return withUser(u, String(describing: contact)) case let .sentInvitationToContact(u, contact, _): return withUser(u, String(describing: contact))
case let .contactAlreadyExists(u, contact): return withUser(u, String(describing: contact)) case let .contactAlreadyExists(u, contact): return withUser(u, String(describing: contact))
case let .contactRequestAlreadyAccepted(u, contact): return withUser(u, String(describing: contact)) case let .contactRequestAlreadyAccepted(u, contact): return withUser(u, String(describing: contact))
@@ -900,10 +900,10 @@ public enum ChatResponse: Decodable, Error {
case let .ntfTokenStatus(status): return String(describing: status) case let .ntfTokenStatus(status): return String(describing: status)
case let .ntfToken(token, status, ntfMode): return "token: \(token)\nstatus: \(status.rawValue)\nntfMode: \(ntfMode.rawValue)" case let .ntfToken(token, status, ntfMode): return "token: \(token)\nstatus: \(status.rawValue)\nntfMode: \(ntfMode.rawValue)"
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, msgTs, ntfMessages): return withUser(u, "connEntity: \(String(describing: connEntity))\nmsgTs: \(String(describing: msgTs))\nntfMessages: \(String(describing: ntfMessages))")
case let .ntfMessage(u, connEntity, ntfMessage): return withUser(u, "connEntity: \(String(describing: connEntity))\nntfMessage: \(String(describing: ntfMessage))") case let .newContactConnection(u, connection): return withUser(u, String(describing: connection))
case let .contactConnectionDeleted(u, connection): return withUser(u, String(describing: connection)) case let .contactConnectionDeleted(u, connection): return withUser(u, String(describing: connection))
case let .remoteCtrlList(remoteCtrls): return String(describing: remoteCtrls) case let .remoteCtrlList(remoteCtrls): return String(describing: remoteCtrls)
case let .remoteCtrlFound(remoteCtrl, ctrlAppInfo_, appVersion, compatible): return "remoteCtrl:\n\(String(describing: remoteCtrl))\nctrlAppInfo_:\n\(String(describing: ctrlAppInfo_))\nappVersion: \(appVersion)\ncompatible: \(compatible)" case let .remoteCtrlFound(remoteCtrl): return String(describing: remoteCtrl)
case let .remoteCtrlConnecting(remoteCtrl_, ctrlAppInfo, appVersion): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nctrlAppInfo:\n\(String(describing: ctrlAppInfo))\nappVersion: \(appVersion)" case let .remoteCtrlConnecting(remoteCtrl_, ctrlAppInfo, appVersion): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nctrlAppInfo:\n\(String(describing: ctrlAppInfo))\nappVersion: \(appVersion)"
case let .remoteCtrlSessionCode(remoteCtrl_, sessionCode): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nsessionCode: \(sessionCode)" case let .remoteCtrlSessionCode(remoteCtrl_, sessionCode): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nsessionCode: \(sessionCode)"
case let .remoteCtrlConnected(remoteCtrl): return String(describing: remoteCtrl) case let .remoteCtrlConnected(remoteCtrl): return String(describing: remoteCtrl)
@@ -1207,9 +1207,9 @@ public struct NetCfg: Codable, Equatable {
public static let defaults: NetCfg = NetCfg( public static let defaults: NetCfg = NetCfg(
socksProxy: nil, socksProxy: nil,
sessionMode: TransportSessionMode.user, sessionMode: TransportSessionMode.user,
tcpConnectTimeout: 20_000_000, tcpConnectTimeout: 15_000_000,
tcpTimeout: 15_000_000, tcpTimeout: 10_000_000,
tcpTimeoutPerKb: 45_000, tcpTimeoutPerKb: 30_000,
tcpKeepAlive: KeepAliveOpts.defaults, tcpKeepAlive: KeepAliveOpts.defaults,
smpPingInterval: 1200_000_000, smpPingInterval: 1200_000_000,
smpPingCount: 3, smpPingCount: 3,
@@ -1498,8 +1498,6 @@ public enum PushProvider: String, Decodable {
} }
} }
// This notification mode is for app core, UI uses AppNotificationsMode.off to mean completely disable,
// and .local for periodic background checks
public enum NotificationsMode: String, Decodable, SelectableItem { public enum NotificationsMode: String, Decodable, SelectableItem {
case off = "OFF" case off = "OFF"
case periodic = "PERIODIC" case periodic = "PERIODIC"
@@ -1507,9 +1505,9 @@ public enum NotificationsMode: String, Decodable, SelectableItem {
public var label: LocalizedStringKey { public var label: LocalizedStringKey {
switch self { switch self {
case .off: "Local" case .off: return "Off (Local)"
case .periodic: "Periodically" case .periodic: return "Periodically"
case .instant: "Instantly" case .instant: return "Instantly"
} }
} }
@@ -1548,19 +1546,11 @@ public struct RemoteCtrlInfo: Decodable {
public enum RemoteCtrlSessionState: Decodable { public enum RemoteCtrlSessionState: Decodable {
case starting case starting
case searching
case connecting case connecting
case pendingConfirmation(sessionCode: String) case pendingConfirmation(sessionCode: String)
case connected(sessionCode: String) case connected(sessionCode: String)
} }
public enum RemoteCtrlStopReason: Decodable {
case discoveryFailed(chatError: ChatError)
case connectionFailed(chatError: ChatError)
case setupFailed(chatError: ChatError)
case disconnected
}
public struct CtrlAppInfo: Decodable { public struct CtrlAppInfo: Decodable {
public var appVersionRange: AppVersionRange public var appVersionRange: AppVersionRange
public var deviceName: String public var deviceName: String
+7 -53
View File
@@ -9,16 +9,12 @@
import Foundation import Foundation
import SwiftUI import SwiftUI
public let appSuspendTimeout: Int = 15 // seconds
let GROUP_DEFAULT_APP_STATE = "appState" let GROUP_DEFAULT_APP_STATE = "appState"
let GROUP_DEFAULT_NSE_STATE = "nseState"
let GROUP_DEFAULT_DB_CONTAINER = "dbContainer" let GROUP_DEFAULT_DB_CONTAINER = "dbContainer"
public let GROUP_DEFAULT_CHAT_LAST_START = "chatLastStart" public let GROUP_DEFAULT_CHAT_LAST_START = "chatLastStart"
public let GROUP_DEFAULT_CHAT_LAST_BACKGROUND_RUN = "chatLastBackgroundRun"
let GROUP_DEFAULT_NTF_PREVIEW_MODE = "ntfPreviewMode" let GROUP_DEFAULT_NTF_PREVIEW_MODE = "ntfPreviewMode"
public let GROUP_DEFAULT_NTF_ENABLE_LOCAL = "ntfEnableLocal" // no longer used public let GROUP_DEFAULT_NTF_ENABLE_LOCAL = "ntfEnableLocal"
public let GROUP_DEFAULT_NTF_ENABLE_PERIODIC = "ntfEnablePeriodic" // no longer used public let GROUP_DEFAULT_NTF_ENABLE_PERIODIC = "ntfEnablePeriodic"
let GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES = "privacyAcceptImages" let GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES = "privacyAcceptImages"
public let GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE = "privacyTransferImagesInline" // no longer used public let GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE = "privacyTransferImagesInline" // no longer used
public let GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES = "privacyEncryptLocalFiles" public let GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES = "privacyEncryptLocalFiles"
@@ -70,23 +66,13 @@ public func registerGroupDefaults() {
]) ])
} }
public enum AppState: String, Codable { public enum AppState: String {
case active case active
case activating
case bgRefresh case bgRefresh
case suspending case suspending
case suspended case suspended
case stopped case stopped
public var running: Bool {
switch self {
case .active: return true
case .activating: return true
case .bgRefresh: return true
default: return false
}
}
public var inactive: Bool { public var inactive: Bool {
switch self { switch self {
case .suspending: return true case .suspending: return true
@@ -98,57 +84,23 @@ public enum AppState: String, Codable {
public var canSuspend: Bool { public var canSuspend: Bool {
switch self { switch self {
case .active: return true case .active: return true
case .activating: return true
case .bgRefresh: return true case .bgRefresh: return true
default: return false default: return false
} }
} }
} }
public enum NSEState: String, Codable {
case created
case starting
case active
case suspending
case suspended
public var inactive: Bool {
switch self {
case .created: true
case .suspended: true
default: false
}
}
public var canSuspend: Bool {
if case .active = self { true } else { false }
}
}
public enum DBContainer: String { public enum DBContainer: String {
case documents case documents
case group case group
} }
// appStateGroupDefault must not be used in the app directly, only via AppChatState singleton
public let appStateGroupDefault = EnumDefault<AppState>( public let appStateGroupDefault = EnumDefault<AppState>(
defaults: groupDefaults, defaults: groupDefaults,
forKey: GROUP_DEFAULT_APP_STATE, forKey: GROUP_DEFAULT_APP_STATE,
withDefault: .active withDefault: .active
) )
// nseStateGroupDefault must not be used in NSE directly, only via NSEChatState singleton
public let nseStateGroupDefault = EnumDefault<NSEState>(
defaults: groupDefaults,
forKey: GROUP_DEFAULT_NSE_STATE,
withDefault: .suspended // so that NSE that was never launched does not delay the app from resuming
)
// inactive app states do not include "stopped" state
public func allowBackgroundRefresh() -> Bool {
appStateGroupDefault.get().inactive && nseStateGroupDefault.get().inactive
}
public let dbContainerGroupDefault = EnumDefault<DBContainer>( public let dbContainerGroupDefault = EnumDefault<DBContainer>(
defaults: groupDefaults, defaults: groupDefaults,
forKey: GROUP_DEFAULT_DB_CONTAINER, forKey: GROUP_DEFAULT_DB_CONTAINER,
@@ -157,8 +109,6 @@ public let dbContainerGroupDefault = EnumDefault<DBContainer>(
public let chatLastStartGroupDefault = DateDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_CHAT_LAST_START) public let chatLastStartGroupDefault = DateDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_CHAT_LAST_START)
public let chatLastBackgroundRunGroupDefault = DateDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_CHAT_LAST_BACKGROUND_RUN)
public let ntfPreviewModeGroupDefault = EnumDefault<NotificationPreviewMode>( public let ntfPreviewModeGroupDefault = EnumDefault<NotificationPreviewMode>(
defaults: groupDefaults, defaults: groupDefaults,
forKey: GROUP_DEFAULT_NTF_PREVIEW_MODE, forKey: GROUP_DEFAULT_NTF_PREVIEW_MODE,
@@ -167,6 +117,10 @@ public let ntfPreviewModeGroupDefault = EnumDefault<NotificationPreviewMode>(
public let incognitoGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_INCOGNITO) public let incognitoGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_INCOGNITO)
public let ntfEnableLocalGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_NTF_ENABLE_LOCAL)
public let ntfEnablePeriodicGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_NTF_ENABLE_PERIODIC)
public let privacyAcceptImagesGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES) public let privacyAcceptImagesGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES)
public let privacyEncryptLocalFilesGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES) public let privacyEncryptLocalFilesGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES)
+5 -9
View File
@@ -1847,7 +1847,7 @@ public struct GroupMember: Identifiable, Decodable {
public func canChangeRoleTo(groupInfo: GroupInfo) -> [GroupMemberRole]? { public func canChangeRoleTo(groupInfo: GroupInfo) -> [GroupMemberRole]? {
if !canBeRemoved(groupInfo: groupInfo) { return nil } if !canBeRemoved(groupInfo: groupInfo) { return nil }
let userRole = groupInfo.membership.memberRole let userRole = groupInfo.membership.memberRole
return GroupMemberRole.allCases.filter { $0 <= userRole && $0 != .author } return GroupMemberRole.allCases.filter { $0 <= userRole }
} }
public var memberIncognito: Bool { public var memberIncognito: Bool {
@@ -1887,7 +1887,6 @@ public struct GroupMemberIds: Decodable {
public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Decodable { public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Decodable {
case observer = "observer" case observer = "observer"
case author = "author"
case member = "member" case member = "member"
case admin = "admin" case admin = "admin"
case owner = "owner" case owner = "owner"
@@ -1897,7 +1896,6 @@ public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Dec
public var text: String { public var text: String {
switch self { switch self {
case .observer: return NSLocalizedString("observer", comment: "member role") case .observer: return NSLocalizedString("observer", comment: "member role")
case .author: return NSLocalizedString("author", comment: "member role")
case .member: return NSLocalizedString("member", comment: "member role") case .member: return NSLocalizedString("member", comment: "member role")
case .admin: return NSLocalizedString("admin", comment: "member role") case .admin: return NSLocalizedString("admin", comment: "member role")
case .owner: return NSLocalizedString("owner", comment: "member role") case .owner: return NSLocalizedString("owner", comment: "member role")
@@ -1907,10 +1905,9 @@ public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Dec
private var comparisonValue: Int { private var comparisonValue: Int {
switch self { switch self {
case .observer: return 0 case .observer: return 0
case .author: return 1 case .member: return 1
case .member: return 2 case .admin: return 2
case .admin: return 3 case .owner: return 3
case .owner: return 4
} }
} }
@@ -2016,8 +2013,7 @@ public enum ConnectionEntity: Decodable {
} }
public struct NtfMsgInfo: Decodable { public struct NtfMsgInfo: Decodable {
public var msgId: String
public var msgTs: Date
} }
public struct AChatItem: Decodable { public struct AChatItem: Decodable {
+2 -2
View File
@@ -17,7 +17,7 @@ public func writeCryptoFile(path: String, data: Data) throws -> CryptoFileArgs {
let ptr: UnsafeMutableRawPointer = malloc(data.count) let ptr: UnsafeMutableRawPointer = malloc(data.count)
memcpy(ptr, (data as NSData).bytes, data.count) memcpy(ptr, (data as NSData).bytes, data.count)
var cPath = path.cString(using: .utf8)! var cPath = path.cString(using: .utf8)!
let cjson = chat_write_file(getChatCtrl(), &cPath, ptr, Int32(data.count))! let cjson = chat_write_file(&cPath, ptr, Int32(data.count))!
let d = fromCString(cjson).data(using: .utf8)! let d = fromCString(cjson).data(using: .utf8)!
switch try jsonDecoder.decode(WriteFileResult.self, from: d) { switch try jsonDecoder.decode(WriteFileResult.self, from: d) {
case let .result(cfArgs): return cfArgs case let .result(cfArgs): return cfArgs
@@ -50,7 +50,7 @@ public func readCryptoFile(path: String, cryptoArgs: CryptoFileArgs) throws -> D
public func encryptCryptoFile(fromPath: String, toPath: String) throws -> CryptoFileArgs { public func encryptCryptoFile(fromPath: String, toPath: String) throws -> CryptoFileArgs {
var cFromPath = fromPath.cString(using: .utf8)! var cFromPath = fromPath.cString(using: .utf8)!
var cToPath = toPath.cString(using: .utf8)! var cToPath = toPath.cString(using: .utf8)!
let cjson = chat_encrypt_file(getChatCtrl(), &cFromPath, &cToPath)! let cjson = chat_encrypt_file(&cFromPath, &cToPath)!
let d = fromCString(cjson).data(using: .utf8)! let d = fromCString(cjson).data(using: .utf8)!
switch try jsonDecoder.decode(WriteFileResult.self, from: d) { switch try jsonDecoder.decode(WriteFileResult.self, from: d) {
case let .result(cfArgs): return cfArgs case let .result(cfArgs): return cfArgs
-7
View File
@@ -146,13 +146,6 @@ public func createErrorNtf(_ dbStatus: DBMigrationResult) -> UNMutableNotificati
) )
} }
public func createAppStoppedNtf() -> UNMutableNotificationContent {
return createNotification(
categoryIdentifier: ntfCategoryConnectionEvent,
title: NSLocalizedString("Encrypted message: app is stopped", comment: "notification")
)
}
private func groupMsgNtfTitle(_ groupInfo: GroupInfo, _ groupMember: GroupMember, hideContent: Bool) -> String { private func groupMsgNtfTitle(_ groupInfo: GroupInfo, _ groupMember: GroupMember, hideContent: Bool) -> String {
hideContent hideContent
? NSLocalizedString("Group message:", comment: "notification") ? NSLocalizedString("Group message:", comment: "notification")
@@ -1,99 +0,0 @@
//
// SharedFileSubscriber.swift
// SimpleXChat
//
// Created by Evgeny on 09/12/2023.
// Copyright © 2023 SimpleX Chat. All rights reserved.
//
import Foundation
public typealias AppSubscriber = SharedFileSubscriber<ProcessMessage<AppProcessMessage>>
public typealias NSESubscriber = SharedFileSubscriber<ProcessMessage<NSEProcessMessage>>
public class SharedFileSubscriber<Message: Codable>: NSObject, NSFilePresenter {
var fileURL: URL
public var presentedItemURL: URL?
public var presentedItemOperationQueue: OperationQueue = .main
var subscriber: (Message) -> Void
init(fileURL: URL, onMessage: @escaping (Message) -> Void) {
self.fileURL = fileURL
presentedItemURL = fileURL
subscriber = onMessage
super.init()
NSFileCoordinator.addFilePresenter(self)
}
public func presentedItemDidChange() {
do {
let data = try Data(contentsOf: fileURL)
let msg = try jsonDecoder.decode(Message.self, from: data)
subscriber(msg)
} catch let error {
logger.error("presentedItemDidChange error: \(error)")
}
}
static func notify(url: URL, message: Message) {
let fc = NSFileCoordinator(filePresenter: nil)
fc.coordinate(writingItemAt: url, options: [], error: nil) { newURL in
do {
let data = try jsonEncoder.encode(message)
try data.write(to: newURL, options: [.atomic])
} catch {
logger.error("notifyViaSharedFile error: \(error)")
}
}
}
deinit {
NSFileCoordinator.removeFilePresenter(self)
}
}
let appMessagesSharedFile = getGroupContainerDirectory().appendingPathComponent("chat.simplex.app.messages", isDirectory: false)
let nseMessagesSharedFile = getGroupContainerDirectory().appendingPathComponent("chat.simplex.app.SimpleX-NSE.messages", isDirectory: false)
public struct ProcessMessage<Message: Codable>: Codable {
var createdAt: Date = Date.now
var message: Message
}
public enum AppProcessMessage: Codable {
case state(state: AppState)
}
public enum NSEProcessMessage: Codable {
case state(state: NSEState)
}
public func sendAppProcessMessage(_ message: AppProcessMessage) {
SharedFileSubscriber.notify(url: appMessagesSharedFile, message: ProcessMessage(message: message))
}
public func sendNSEProcessMessage(_ message: NSEProcessMessage) {
SharedFileSubscriber.notify(url: nseMessagesSharedFile, message: ProcessMessage(message: message))
}
public func appMessageSubscriber(onMessage: @escaping (AppProcessMessage) -> Void) -> AppSubscriber {
SharedFileSubscriber(fileURL: appMessagesSharedFile) { (msg: ProcessMessage<AppProcessMessage>) in
onMessage(msg.message)
}
}
public func nseMessageSubscriber(onMessage: @escaping (NSEProcessMessage) -> Void) -> NSESubscriber {
SharedFileSubscriber(fileURL: nseMessagesSharedFile) { (msg: ProcessMessage<NSEProcessMessage>) in
onMessage(msg.message)
}
}
public func sendAppState(_ state: AppState) {
sendAppProcessMessage(.state(state: state))
}
public func sendNSEState(_ state: NSEState) {
sendNSEProcessMessage(.state(state: state))
}
+6 -8
View File
@@ -9,27 +9,27 @@
#ifndef SimpleX_h #ifndef SimpleX_h
#define SimpleX_h #define SimpleX_h
#include "hs_init.h" #endif /* SimpleX_h */
extern void hs_init(int argc, char **argv[]); extern void hs_init(int argc, char **argv[]);
typedef void* chat_ctrl; typedef void* chat_ctrl;
// the last parameter is used to return the pointer to chat controller // the last parameter is used to return the pointer to chat controller
extern char *chat_migrate_init_key(char *path, char *key, int keepKey, char *confirm, int backgroundMode, chat_ctrl *ctrl); extern char *chat_migrate_init(char *path, char *key, char *confirm, chat_ctrl *ctrl);
extern char *chat_close_store(chat_ctrl ctl); extern char *chat_close_store(chat_ctrl ctl);
extern char *chat_reopen_store(chat_ctrl ctl);
extern char *chat_send_cmd(chat_ctrl ctl, char *cmd); extern char *chat_send_cmd(chat_ctrl ctl, char *cmd);
extern char *chat_recv_msg(chat_ctrl ctl);
extern char *chat_recv_msg_wait(chat_ctrl ctl, int wait); extern char *chat_recv_msg_wait(chat_ctrl ctl, int wait);
extern char *chat_parse_markdown(char *str); extern char *chat_parse_markdown(char *str);
extern char *chat_parse_server(char *str); extern char *chat_parse_server(char *str);
extern char *chat_password_hash(char *pwd, char *salt); extern char *chat_password_hash(char *pwd, char *salt);
extern char *chat_valid_name(char *name); extern char *chat_valid_name(char *name);
extern char *chat_encrypt_media(chat_ctrl ctl, char *key, char *frame, int len); extern char *chat_encrypt_media(char *key, char *frame, int len);
extern char *chat_decrypt_media(char *key, char *frame, int len); extern char *chat_decrypt_media(char *key, char *frame, int len);
// chat_write_file returns null-terminated string with JSON of WriteFileResult // chat_write_file returns null-terminated string with JSON of WriteFileResult
extern char *chat_write_file(chat_ctrl ctl, char *path, char *data, int len); extern char *chat_write_file(char *path, char *data, int len);
// chat_read_file returns a buffer with: // chat_read_file returns a buffer with:
// result status (1 byte), then if // result status (1 byte), then if
@@ -38,9 +38,7 @@ extern char *chat_write_file(chat_ctrl ctl, char *path, char *data, int len);
extern char *chat_read_file(char *path, char *key, char *nonce); extern char *chat_read_file(char *path, char *key, char *nonce);
// chat_encrypt_file returns null-terminated string with JSON of WriteFileResult // chat_encrypt_file returns null-terminated string with JSON of WriteFileResult
extern char *chat_encrypt_file(chat_ctrl ctl, char *fromPath, char *toPath); extern char *chat_encrypt_file(char *fromPath, char *toPath);
// chat_decrypt_file returns null-terminated string with the error message // chat_decrypt_file returns null-terminated string with the error message
extern char *chat_decrypt_file(char *fromPath, char *key, char *nonce, char *toPath); extern char *chat_decrypt_file(char *fromPath, char *key, char *nonce, char *toPath);
#endif /* SimpleX_h */
-41
View File
@@ -1,41 +0,0 @@
//
// hs_init.c
// SimpleXChat
//
// Created by Evgeny on 22/11/2023.
// Copyright © 2023 SimpleX Chat. All rights reserved.
//
#include "hs_init.h"
extern void hs_init_with_rtsopts(int * argc, char **argv[]);
void haskell_init(void) {
int argc = 5;
char *argv[] = {
"simplex",
"+RTS", // requires `hs_init_with_rtsopts`
"-A16m", // chunk size for new allocations
"-H64m", // initial heap size
"-xn", // non-moving GC
0
};
char **pargv = argv;
hs_init_with_rtsopts(&argc, &pargv);
}
void haskell_init_nse(void) {
int argc = 7;
char *argv[] = {
"simplex",
"+RTS", // requires `hs_init_with_rtsopts`
"-A1m", // chunk size for new allocations
"-H1m", // initial heap size
"-F0.5", // heap growth triggering GC
"-Fd1", // memory return
"-c", // compacting garbage collector
0
};
char **pargv = argv;
hs_init_with_rtsopts(&argc, &pargv);
}
-16
View File
@@ -1,16 +0,0 @@
//
// hs_init.h
// SimpleXChat
//
// Created by Evgeny on 22/11/2023.
// Copyright © 2023 SimpleX Chat. All rights reserved.
//
#ifndef hs_init_h
#define hs_init_h
void haskell_init(void);
void haskell_init_nse(void);
#endif /* hs_init_h */
-135
View File
@@ -25,9 +25,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"- more stable message delivery.\n- a bit better groups.\n- and more!" = "- stabilere Zustellung von Nachrichten.\n- ein bisschen verbesserte Gruppen.\n- und mehr!"; "- more stable message delivery.\n- a bit better groups.\n- and more!" = "- stabilere Zustellung von Nachrichten.\n- ein bisschen verbesserte Gruppen.\n- und mehr!";
/* No comment provided by engineer. */
"- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!" = "- Optionale Benachrichtigung von gelöschten Kontakten.\n- Profilnamen mit Leerzeichen.\n- Und mehr!";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- Bis zu 5 Minuten lange Sprachnachrichten.\n- Zeitdauer für verschwindende Nachrichten anpassen.\n- Nachrichten-Historie bearbeiten."; "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- Bis zu 5 Minuten lange Sprachnachrichten.\n- Zeitdauer für verschwindende Nachrichten anpassen.\n- Nachrichten-Historie bearbeiten.";
@@ -46,12 +43,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"(" = "("; "(" = "(";
/* No comment provided by engineer. */
"(new)" = "(Neu)";
/* No comment provided by engineer. */
"(this device v%@)" = "(Dieses Gerät hat v%@)";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
")" = ")"; ")" = ")";
@@ -545,9 +536,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Authentication unavailable" = "Authentifizierung nicht verfügbar"; "Authentication unavailable" = "Authentifizierung nicht verfügbar";
/* member role */
"author" = "Autor";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Auto-accept" = "Automatisch akzeptieren"; "Auto-accept" = "Automatisch akzeptieren";
@@ -560,9 +548,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Back" = "Zurück"; "Back" = "Zurück";
/* No comment provided by engineer. */
"Bad desktop address" = "Falsche Desktop-Adresse";
/* integrity error chat item */ /* integrity error chat item */
"bad message hash" = "Ungültiger Nachrichten-Hash"; "bad message hash" = "Ungültiger Nachrichten-Hash";
@@ -575,18 +560,12 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Bad message ID" = "Falsche Nachrichten-ID"; "Bad message ID" = "Falsche Nachrichten-ID";
/* No comment provided by engineer. */
"Better groups" = "Bessere Gruppen";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Better messages" = "Verbesserungen bei Nachrichten"; "Better messages" = "Verbesserungen bei Nachrichten";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Block" = "Blockieren"; "Block" = "Blockieren";
/* No comment provided by engineer. */
"Block group members" = "Gruppenmitglieder blockieren";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Block member" = "Mitglied blockieren"; "Block member" = "Mitglied blockieren";
@@ -789,15 +768,9 @@
/* server test step */ /* server test step */
"Connect" = "Verbinden"; "Connect" = "Verbinden";
/* No comment provided by engineer. */
"Connect automatically" = "Automatisch verbinden";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connect incognito" = "Inkognito verbinden"; "Connect incognito" = "Inkognito verbinden";
/* No comment provided by engineer. */
"Connect to desktop" = "Mit dem Desktop verbinden";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"connect to SimpleX Chat developers." = "Mit den SimpleX Chat-Entwicklern verbinden."; "connect to SimpleX Chat developers." = "Mit den SimpleX Chat-Entwicklern verbinden.";
@@ -828,15 +801,9 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"connected" = "Verbunden"; "connected" = "Verbunden";
/* No comment provided by engineer. */
"Connected desktop" = "Verbundener Desktop";
/* rcv group event chat item */ /* rcv group event chat item */
"connected directly" = "Direkt miteinander verbunden"; "connected directly" = "Direkt miteinander verbunden";
/* No comment provided by engineer. */
"Connected to desktop" = "Mit dem Desktop verbunden";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"connecting" = "verbinde"; "connecting" = "verbinde";
@@ -861,9 +828,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connecting server… (error: %@)" = "Mit dem Server verbinden… (Fehler: %@)"; "Connecting server… (error: %@)" = "Mit dem Server verbinden… (Fehler: %@)";
/* No comment provided by engineer. */
"Connecting to desktop" = "Mit dem Desktop verbinden";
/* chat list item title */ /* chat list item title */
"connecting…" = "Verbinde…"; "connecting…" = "Verbinde…";
@@ -882,9 +846,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connection request sent!" = "Verbindungsanfrage wurde gesendet!"; "Connection request sent!" = "Verbindungsanfrage wurde gesendet!";
/* No comment provided by engineer. */
"Connection terminated" = "Verbindung beendet";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connection timeout" = "Verbindungszeitüberschreitung"; "Connection timeout" = "Verbindungszeitüberschreitung";
@@ -939,9 +900,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Create" = "Erstellen"; "Create" = "Erstellen";
/* No comment provided by engineer. */
"Create a group using a random profile." = "Erstellen Sie eine Gruppe mit einem zufälligen Profil.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Create an address to let people connect with you." = "Erstellen Sie eine Adresse, damit sich Personen mit Ihnen verbinden können."; "Create an address to let people connect with you." = "Erstellen Sie eine Adresse, damit sich Personen mit Ihnen verbinden können.";
@@ -1215,15 +1173,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Description" = "Beschreibung"; "Description" = "Beschreibung";
/* No comment provided by engineer. */
"Desktop address" = "Desktop-Adresse";
/* No comment provided by engineer. */
"Desktop app version %@ is not compatible with this app." = "Desktop App-Version %@ ist mit dieser App nicht kompatibel.";
/* No comment provided by engineer. */
"Desktop devices" = "Desktop-Geräte";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Develop" = "Entwicklung"; "Develop" = "Entwicklung";
@@ -1287,15 +1236,9 @@
/* server test step */ /* server test step */
"Disconnect" = "Trennen"; "Disconnect" = "Trennen";
/* No comment provided by engineer. */
"Disconnect desktop?" = "Desktop-Verbindung trennen?";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Discover and join groups" = "Gruppen entdecken und ihnen beitreten"; "Discover and join groups" = "Gruppen entdecken und ihnen beitreten";
/* No comment provided by engineer. */
"Discover via local network" = "Lokales Netzwerk durchsuchen";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Do it later" = "Später wiederholen"; "Do it later" = "Später wiederholen";
@@ -1431,12 +1374,6 @@
/* chat item text */ /* chat item text */
"encryption re-negotiation allowed for %@" = "Neuaushandlung der Verschlüsselung von %@ erlaubt"; "encryption re-negotiation allowed for %@" = "Neuaushandlung der Verschlüsselung von %@ erlaubt";
/* message decrypt error item */
"Encryption re-negotiation error" = "Fehler bei der Neuverhandlung der Verschlüsselung";
/* No comment provided by engineer. */
"Encryption re-negotiation failed." = "Neuverhandlung der Verschlüsselung fehlgeschlagen.";
/* chat item text */ /* chat item text */
"encryption re-negotiation required" = "Neuaushandlung der Verschlüsselung notwendig"; "encryption re-negotiation required" = "Neuaushandlung der Verschlüsselung notwendig";
@@ -1467,9 +1404,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Enter server manually" = "Geben Sie den Server manuell ein"; "Enter server manually" = "Geben Sie den Server manuell ein";
/* No comment provided by engineer. */
"Enter this device name…" = "Geben Sie diesen Gerätenamen ein…";
/* placeholder */ /* placeholder */
"Enter welcome message…" = "Geben Sie eine Begrüßungsmeldung ein …"; "Enter welcome message…" = "Geben Sie eine Begrüßungsmeldung ein …";
@@ -1671,9 +1605,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Fast and no wait until the sender is online!" = "Schnell und ohne warten auf den Absender, bis er online ist!"; "Fast and no wait until the sender is online!" = "Schnell und ohne warten auf den Absender, bis er online ist!";
/* No comment provided by engineer. */
"Faster joining and more reliable messages." = "Schnellerer Gruppenbeitritt und zuverlässigere Nachrichtenzustellung.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Favorite" = "Favorit"; "Favorite" = "Favorit";
@@ -1731,9 +1662,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"For console" = "Für Konsole"; "For console" = "Für Konsole";
/* No comment provided by engineer. */
"Found desktop" = "Gefundener Desktop";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"French interface" = "Französische Bedienoberfläche"; "French interface" = "Französische Bedienoberfläche";
@@ -1938,9 +1866,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Incognito" = "Inkognito"; "Incognito" = "Inkognito";
/* No comment provided by engineer. */
"Incognito groups" = "Inkognito-Gruppen";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Incognito mode" = "Inkognito-Modus"; "Incognito mode" = "Inkognito-Modus";
@@ -1968,9 +1893,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Incompatible database version" = "Inkompatible Datenbank-Version"; "Incompatible database version" = "Inkompatible Datenbank-Version";
/* No comment provided by engineer. */
"Incompatible version" = "Inkompatible Version";
/* PIN entry */ /* PIN entry */
"Incorrect passcode" = "Zugangscode ist falsch"; "Incorrect passcode" = "Zugangscode ist falsch";
@@ -2106,9 +2028,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Joining group" = "Der Gruppe beitreten"; "Joining group" = "Der Gruppe beitreten";
/* 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";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Keep your connections" = "Ihre Verbindungen beibehalten"; "Keep your connections" = "Ihre Verbindungen beibehalten";
@@ -2145,15 +2064,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Limitations" = "Einschränkungen"; "Limitations" = "Einschränkungen";
/* No comment provided by engineer. */
"Link mobile and desktop apps! 🔗" = "Verknüpfe Mobiltelefon- und Desktop-Apps! 🔗";
/* No comment provided by engineer. */
"Linked desktop options" = "Verknüpfte Desktop-Optionen";
/* No comment provided by engineer. */
"Linked desktops" = "Verknüpfte Desktops";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"LIVE" = "LIVE"; "LIVE" = "LIVE";
@@ -2409,9 +2319,6 @@
/* copied message info in history */ /* copied message info in history */
"no text" = "Kein Text"; "no text" = "Kein Text";
/* No comment provided by engineer. */
"Not compatible!" = "Nicht kompatibel!";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Notifications" = "Benachrichtigungen"; "Notifications" = "Benachrichtigungen";
@@ -2555,9 +2462,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Paste" = "Einfügen"; "Paste" = "Einfügen";
/* No comment provided by engineer. */
"Paste desktop address" = "Desktop-Adresse einfügen";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Paste image" = "Bild einfügen"; "Paste image" = "Bild einfügen";
@@ -2942,9 +2846,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Scan QR code" = "QR-Code scannen"; "Scan QR code" = "QR-Code scannen";
/* No comment provided by engineer. */
"Scan QR code from desktop" = "Den QR-Code vom Desktop scannen";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Scan security code from your contact's app." = "Scannen Sie den Sicherheitscode von der App Ihres Kontakts."; "Scan security code from your contact's app." = "Scannen Sie den Sicherheitscode von der App Ihres Kontakts.";
@@ -3089,9 +2990,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Servers" = "Server"; "Servers" = "Server";
/* No comment provided by engineer. */
"Session code" = "Sitzungscode";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Set 1 day" = "Einen Tag festlegen"; "Set 1 day" = "Einen Tag festlegen";
@@ -3401,9 +3299,6 @@
/* notification title */ /* notification title */
"this contact" = "Dieser Kontakt"; "this contact" = "Dieser Kontakt";
/* No comment provided by engineer. */
"This device name" = "Dieser Gerätename";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"This group has over %lld members, delivery receipts are not sent." = "Es werden keine Empfangsbestätigungen gesendet, da diese Gruppe über %lld Mitglieder hat."; "This group has over %lld members, delivery receipts are not sent." = "Es werden keine Empfangsbestätigungen gesendet, da diese Gruppe über %lld Mitglieder hat.";
@@ -3425,9 +3320,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"To connect, your contact can scan QR code or use the link in the app." = "Um eine Verbindung herzustellen, kann Ihr Kontakt den QR-Code scannen oder den Link in der App verwenden."; "To connect, your contact can scan QR code or use the link in the app." = "Um eine Verbindung herzustellen, kann Ihr Kontakt den QR-Code scannen oder den Link in der App verwenden.";
/* No comment provided by engineer. */
"To hide unwanted messages." = "Um unerwünschte Nachrichten zu verbergen.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"To make a new connection" = "Um eine Verbindung mit einem neuen Kontakt zu erstellen"; "To make a new connection" = "Um eine Verbindung mit einem neuen Kontakt zu erstellen";
@@ -3524,12 +3416,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "Entweder hat Ihr Kontakt die Verbindung gelöscht, oder dieser Link wurde bereits verwendet, es könnte sich um einen Fehler handeln - Bitte melden Sie es uns.\nBitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um sich neu verbinden zu können und stellen Sie sicher, dass Sie eine stabile Netzwerk-Verbindung haben."; "Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "Entweder hat Ihr Kontakt die Verbindung gelöscht, oder dieser Link wurde bereits verwendet, es könnte sich um einen Fehler handeln - Bitte melden Sie es uns.\nBitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um sich neu verbinden zu können und stellen Sie sicher, dass Sie eine stabile Netzwerk-Verbindung haben.";
/* No comment provided by engineer. */
"Unlink" = "Entkoppeln";
/* No comment provided by engineer. */
"Unlink desktop?" = "Desktop entkoppeln?";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Unlock" = "Entsperren"; "Unlock" = "Entsperren";
@@ -3584,9 +3470,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Use for new connections" = "Für neue Verbindungen nutzen"; "Use for new connections" = "Für neue Verbindungen nutzen";
/* No comment provided by engineer. */
"Use from desktop" = "Vom Desktop aus nutzen";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Use iOS call interface" = "iOS Anrufschnittstelle nutzen"; "Use iOS call interface" = "iOS Anrufschnittstelle nutzen";
@@ -3608,24 +3491,12 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Using SimpleX Chat servers." = "Verwendung von SimpleX-Chat-Servern."; "Using SimpleX Chat servers." = "Verwendung von SimpleX-Chat-Servern.";
/* No comment provided by engineer. */
"v%@" = "v%@";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"v%@ (%@)" = "v%@ (%@)"; "v%@ (%@)" = "v%@ (%@)";
/* No comment provided by engineer. */
"Verify code with desktop" = "Code mit dem Desktop überprüfen";
/* No comment provided by engineer. */
"Verify connection" = "Verbindung überprüfen";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Verify connection security" = "Sicherheit der Verbindung überprüfen"; "Verify connection security" = "Sicherheit der Verbindung überprüfen";
/* No comment provided by engineer. */
"Verify connections" = "Verbindungen überprüfen";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Verify security code" = "Sicherheitscode überprüfen"; "Verify security code" = "Sicherheitscode überprüfen";
@@ -3644,9 +3515,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"via relay" = "über Relais"; "via relay" = "über Relais";
/* No comment provided by engineer. */
"Via secure quantum resistant protocol." = "Über ein sicheres quantenbeständiges Protokoll.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Video call" = "Videoanruf"; "Video call" = "Videoanruf";
@@ -3686,9 +3554,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"waiting for confirmation…" = "Warten auf Bestätigung…"; "waiting for confirmation…" = "Warten auf Bestätigung…";
/* No comment provided by engineer. */
"Waiting for desktop..." = "Es wird auf den Desktop gewartet...";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Waiting for file" = "Warte auf Datei"; "Waiting for file" = "Warte auf Datei";
@@ -7,9 +7,6 @@
/* Privacy - Face ID Usage Description */ /* Privacy - Face ID Usage Description */
"NSFaceIDUsageDescription" = "Face ID wird von SimpleX für die lokale Authentifizierung genutzt"; "NSFaceIDUsageDescription" = "Face ID wird von SimpleX für die lokale Authentifizierung genutzt";
/* Privacy - Local Network Usage Description */
"NSLocalNetworkUsageDescription" = "SimpleX nutzt den lokalen Netzwerkzugriff, um die Nutzung von Benutzer-Chatprofilen über eine Desktop-App im gleichen Netzwerk zu erlauben.";
/* Privacy - Microphone Usage Description */ /* Privacy - Microphone Usage Description */
"NSMicrophoneUsageDescription" = "SimpleX benötigt Zugriff auf das Mikrofon, um Audio- und Videoanrufe und die Aufnahme von Sprachnachrichten zu ermöglichen."; "NSMicrophoneUsageDescription" = "SimpleX benötigt Zugriff auf das Mikrofon, um Audio- und Videoanrufe und die Aufnahme von Sprachnachrichten zu ermöglichen.";
-135
View File
@@ -25,9 +25,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"- more stable message delivery.\n- a bit better groups.\n- and more!" = "- une diffusion plus stable des messages.\n- des groupes un peu plus performants.\n- et bien d'autres choses encore !"; "- more stable message delivery.\n- a bit better groups.\n- and more!" = "- une diffusion plus stable des messages.\n- des groupes un peu plus performants.\n- et bien d'autres choses encore !";
/* No comment provided by engineer. */
"- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!" = "- option pour notifier les contacts supprimés.\n- noms de profil avec espaces.\n- et plus encore !";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- messages vocaux pouvant durer jusqu'à 5 minutes.\n- délai personnalisé de disparition.\n- l'historique de modification."; "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- messages vocaux pouvant durer jusqu'à 5 minutes.\n- délai personnalisé de disparition.\n- l'historique de modification.";
@@ -46,12 +43,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"(" = "("; "(" = "(";
/* No comment provided by engineer. */
"(new)" = "(nouveau)";
/* No comment provided by engineer. */
"(this device v%@)" = "(cet appareil v%@)";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
")" = ")"; ")" = ")";
@@ -545,9 +536,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Authentication unavailable" = "Authentification indisponible"; "Authentication unavailable" = "Authentification indisponible";
/* member role */
"author" = "auteur";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Auto-accept" = "Auto-accepter"; "Auto-accept" = "Auto-accepter";
@@ -560,9 +548,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Back" = "Retour"; "Back" = "Retour";
/* No comment provided by engineer. */
"Bad desktop address" = "Mauvaise adresse de bureau";
/* integrity error chat item */ /* integrity error chat item */
"bad message hash" = "hash de message incorrect"; "bad message hash" = "hash de message incorrect";
@@ -575,18 +560,12 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Bad message ID" = "Mauvais ID de message"; "Bad message ID" = "Mauvais ID de message";
/* No comment provided by engineer. */
"Better groups" = "Des groupes plus performants";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Better messages" = "Meilleurs messages"; "Better messages" = "Meilleurs messages";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Block" = "Bloquer"; "Block" = "Bloquer";
/* No comment provided by engineer. */
"Block group members" = "Bloquer des membres d'un groupe";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Block member" = "Bloquer ce membre"; "Block member" = "Bloquer ce membre";
@@ -789,15 +768,9 @@
/* server test step */ /* server test step */
"Connect" = "Se connecter"; "Connect" = "Se connecter";
/* No comment provided by engineer. */
"Connect automatically" = "Connexion automatique";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connect incognito" = "Se connecter incognito"; "Connect incognito" = "Se connecter incognito";
/* No comment provided by engineer. */
"Connect to desktop" = "Se connecter au bureau";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"connect to SimpleX Chat developers." = "se connecter aux developpeurs de SimpleX Chat."; "connect to SimpleX Chat developers." = "se connecter aux developpeurs de SimpleX Chat.";
@@ -828,15 +801,9 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"connected" = "connecté"; "connected" = "connecté";
/* No comment provided by engineer. */
"Connected desktop" = "Bureau connecté";
/* rcv group event chat item */ /* rcv group event chat item */
"connected directly" = "s'est connecté.e de manière directe"; "connected directly" = "s'est connecté.e de manière directe";
/* No comment provided by engineer. */
"Connected to desktop" = "Connecté au bureau";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"connecting" = "connexion"; "connecting" = "connexion";
@@ -861,9 +828,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connecting server… (error: %@)" = "Connexion au serveur… (erreur : %@)"; "Connecting server… (error: %@)" = "Connexion au serveur… (erreur : %@)";
/* No comment provided by engineer. */
"Connecting to desktop" = "Connexion au bureau";
/* chat list item title */ /* chat list item title */
"connecting…" = "connexion…"; "connecting…" = "connexion…";
@@ -882,9 +846,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connection request sent!" = "Demande de connexion envoyée !"; "Connection request sent!" = "Demande de connexion envoyée !";
/* No comment provided by engineer. */
"Connection terminated" = "Connexion terminée";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connection timeout" = "Délai de connexion"; "Connection timeout" = "Délai de connexion";
@@ -939,9 +900,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Create" = "Créer"; "Create" = "Créer";
/* No comment provided by engineer. */
"Create a group using a random profile." = "Création de groupes via un profil aléatoire.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Create an address to let people connect with you." = "Créez une adresse pour permettre aux gens de vous contacter."; "Create an address to let people connect with you." = "Créez une adresse pour permettre aux gens de vous contacter.";
@@ -1215,15 +1173,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Description" = "Description"; "Description" = "Description";
/* No comment provided by engineer. */
"Desktop address" = "Adresse de bureau";
/* No comment provided by engineer. */
"Desktop app version %@ is not compatible with this app." = "La version de l'application de bureau %@ n'est pas compatible avec cette application.";
/* No comment provided by engineer. */
"Desktop devices" = "Appareils de bureau";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Develop" = "Développer"; "Develop" = "Développer";
@@ -1287,15 +1236,9 @@
/* server test step */ /* server test step */
"Disconnect" = "Se déconnecter"; "Disconnect" = "Se déconnecter";
/* No comment provided by engineer. */
"Disconnect desktop?" = "Déconnecter le bureau ?";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Discover and join groups" = "Découvrir et rejoindre des groupes"; "Discover and join groups" = "Découvrir et rejoindre des groupes";
/* No comment provided by engineer. */
"Discover via local network" = "Rechercher sur le réseau";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Do it later" = "Faites-le plus tard"; "Do it later" = "Faites-le plus tard";
@@ -1431,12 +1374,6 @@
/* chat item text */ /* chat item text */
"encryption re-negotiation allowed for %@" = "renégociation de chiffrement autorisée pour %@"; "encryption re-negotiation allowed for %@" = "renégociation de chiffrement autorisée pour %@";
/* message decrypt error item */
"Encryption re-negotiation error" = "Erreur lors de la renégociation du chiffrement";
/* No comment provided by engineer. */
"Encryption re-negotiation failed." = "La renégociation du chiffrement a échoué.";
/* chat item text */ /* chat item text */
"encryption re-negotiation required" = "renégociation de chiffrement requise"; "encryption re-negotiation required" = "renégociation de chiffrement requise";
@@ -1467,9 +1404,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Enter server manually" = "Entrer un serveur manuellement"; "Enter server manually" = "Entrer un serveur manuellement";
/* No comment provided by engineer. */
"Enter this device name…" = "Entrez le nom de l'appareil…";
/* placeholder */ /* placeholder */
"Enter welcome message…" = "Entrez un message de bienvenue…"; "Enter welcome message…" = "Entrez un message de bienvenue…";
@@ -1671,9 +1605,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Fast and no wait until the sender is online!" = "Rapide et ne nécessitant pas d'attendre que l'expéditeur soit en ligne !"; "Fast and no wait until the sender is online!" = "Rapide et ne nécessitant pas d'attendre que l'expéditeur soit en ligne !";
/* No comment provided by engineer. */
"Faster joining and more reliable messages." = "Connexion plus rapide et messages plus fiables.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Favorite" = "Favoris"; "Favorite" = "Favoris";
@@ -1731,9 +1662,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"For console" = "Pour la console"; "For console" = "Pour la console";
/* No comment provided by engineer. */
"Found desktop" = "Bureau trouvé";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"French interface" = "Interface en français"; "French interface" = "Interface en français";
@@ -1938,9 +1866,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Incognito" = "Incognito"; "Incognito" = "Incognito";
/* No comment provided by engineer. */
"Incognito groups" = "Groupes incognito";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Incognito mode" = "Mode Incognito"; "Incognito mode" = "Mode Incognito";
@@ -1968,9 +1893,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Incompatible database version" = "Version de la base de données incompatible"; "Incompatible database version" = "Version de la base de données incompatible";
/* No comment provided by engineer. */
"Incompatible version" = "Version incompatible";
/* PIN entry */ /* PIN entry */
"Incorrect passcode" = "Code d'accès erroné"; "Incorrect passcode" = "Code d'accès erroné";
@@ -2106,9 +2028,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Joining group" = "Entrain de rejoindre le groupe"; "Joining group" = "Entrain de rejoindre le groupe";
/* No comment provided by engineer. */
"Keep the app open to use it from desktop" = "Garder l'application ouverte pour l'utiliser depuis le bureau";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Keep your connections" = "Conserver vos connexions"; "Keep your connections" = "Conserver vos connexions";
@@ -2145,15 +2064,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Limitations" = "Limitations"; "Limitations" = "Limitations";
/* No comment provided by engineer. */
"Link mobile and desktop apps! 🔗" = "Liez vos applications mobiles et de bureau ! 🔗";
/* No comment provided by engineer. */
"Linked desktop options" = "Options de bureau lié";
/* No comment provided by engineer. */
"Linked desktops" = "Bureaux liés";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"LIVE" = "LIVE"; "LIVE" = "LIVE";
@@ -2409,9 +2319,6 @@
/* copied message info in history */ /* copied message info in history */
"no text" = "aucun texte"; "no text" = "aucun texte";
/* No comment provided by engineer. */
"Not compatible!" = "Non compatible !";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Notifications" = "Notifications"; "Notifications" = "Notifications";
@@ -2555,9 +2462,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Paste" = "Coller"; "Paste" = "Coller";
/* No comment provided by engineer. */
"Paste desktop address" = "Coller l'adresse du bureau";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Paste image" = "Coller l'image"; "Paste image" = "Coller l'image";
@@ -2942,9 +2846,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Scan QR code" = "Scanner un code QR"; "Scan QR code" = "Scanner un code QR";
/* No comment provided by engineer. */
"Scan QR code from desktop" = "Scanner le code QR du bureau";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Scan security code from your contact's app." = "Scannez le code de sécurité depuis l'application de votre contact."; "Scan security code from your contact's app." = "Scannez le code de sécurité depuis l'application de votre contact.";
@@ -3089,9 +2990,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Servers" = "Serveurs"; "Servers" = "Serveurs";
/* No comment provided by engineer. */
"Session code" = "Code de session";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Set 1 day" = "Définir 1 jour"; "Set 1 day" = "Définir 1 jour";
@@ -3401,9 +3299,6 @@
/* notification title */ /* notification title */
"this contact" = "ce contact"; "this contact" = "ce contact";
/* No comment provided by engineer. */
"This device name" = "Ce nom d'appareil";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"This group has over %lld members, delivery receipts are not sent." = "Ce groupe compte plus de %lld membres, les accusés de réception ne sont pas envoyés."; "This group has over %lld members, delivery receipts are not sent." = "Ce groupe compte plus de %lld membres, les accusés de réception ne sont pas envoyés.";
@@ -3425,9 +3320,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"To connect, your contact can scan QR code or use the link in the app." = "Pour se connecter, votre contact peut scanner le code QR ou utiliser le lien dans l'application."; "To connect, your contact can scan QR code or use the link in the app." = "Pour se connecter, votre contact peut scanner le code QR ou utiliser le lien dans l'application.";
/* No comment provided by engineer. */
"To hide unwanted messages." = "Pour cacher les messages indésirables.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"To make a new connection" = "Pour établir une nouvelle connexion"; "To make a new connection" = "Pour établir une nouvelle connexion";
@@ -3524,12 +3416,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "A moins que votre contact ait supprimé la connexion ou que ce lien ait déjà été utilisé, il peut s'agir d'un bug - veuillez le signaler.\nPour vous connecter, veuillez demander à votre contact de créer un autre lien de connexion et vérifiez que vous disposez d'une connexion réseau stable."; "Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "A moins que votre contact ait supprimé la connexion ou que ce lien ait déjà été utilisé, il peut s'agir d'un bug - veuillez le signaler.\nPour vous connecter, veuillez demander à votre contact de créer un autre lien de connexion et vérifiez que vous disposez d'une connexion réseau stable.";
/* No comment provided by engineer. */
"Unlink" = "Délier";
/* No comment provided by engineer. */
"Unlink desktop?" = "Délier le bureau ?";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Unlock" = "Déverrouiller"; "Unlock" = "Déverrouiller";
@@ -3584,9 +3470,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Use for new connections" = "Utiliser pour les nouvelles connexions"; "Use for new connections" = "Utiliser pour les nouvelles connexions";
/* No comment provided by engineer. */
"Use from desktop" = "Utilisation depuis le bureau";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Use iOS call interface" = "Utiliser l'interface d'appel d'iOS"; "Use iOS call interface" = "Utiliser l'interface d'appel d'iOS";
@@ -3608,24 +3491,12 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Using SimpleX Chat servers." = "Utilisation des serveurs SimpleX Chat."; "Using SimpleX Chat servers." = "Utilisation des serveurs SimpleX Chat.";
/* No comment provided by engineer. */
"v%@" = "v%@";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"v%@ (%@)" = "v%@ (%@)"; "v%@ (%@)" = "v%@ (%@)";
/* No comment provided by engineer. */
"Verify code with desktop" = "Vérifier le code avec le bureau";
/* No comment provided by engineer. */
"Verify connection" = "Vérifier la connexion";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Verify connection security" = "Vérifier la sécurité de la connexion"; "Verify connection security" = "Vérifier la sécurité de la connexion";
/* No comment provided by engineer. */
"Verify connections" = "Vérifier les connexions";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Verify security code" = "Vérifier le code de sécurité"; "Verify security code" = "Vérifier le code de sécurité";
@@ -3644,9 +3515,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"via relay" = "via relais"; "via relay" = "via relais";
/* No comment provided by engineer. */
"Via secure quantum resistant protocol." = "Via un protocole sécurisé de cryptographie post-quantique.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Video call" = "Appel vidéo"; "Video call" = "Appel vidéo";
@@ -3686,9 +3554,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"waiting for confirmation…" = "en attente de confirmation…"; "waiting for confirmation…" = "en attente de confirmation…";
/* No comment provided by engineer. */
"Waiting for desktop..." = "En attente du bureau...";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Waiting for file" = "En attente du fichier"; "Waiting for file" = "En attente du fichier";
@@ -7,9 +7,6 @@
/* Privacy - Face ID Usage Description */ /* Privacy - Face ID Usage Description */
"NSFaceIDUsageDescription" = "SimpleGroup not found!X utilise Face ID pour l'authentification locale"; "NSFaceIDUsageDescription" = "SimpleGroup not found!X utilise Face ID pour l'authentification locale";
/* Privacy - Local Network Usage Description */
"NSLocalNetworkUsageDescription" = "SimpleX utilise un accès au réseau local pour permettre l'utilisation du profil de chat de l'utilisateur via l'application de bureau au sein de ce même réseau.";
/* Privacy - Microphone Usage Description */ /* Privacy - Microphone Usage Description */
"NSMicrophoneUsageDescription" = "SimpleX a besoin d'un accès au microphone pour les appels audio et vidéo ainsi que pour enregistrer des messages vocaux."; "NSMicrophoneUsageDescription" = "SimpleX a besoin d'un accès au microphone pour les appels audio et vidéo ainsi que pour enregistrer des messages vocaux.";
-117
View File
@@ -25,9 +25,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"- more stable message delivery.\n- a bit better groups.\n- and more!" = "- recapito dei messaggi più stabile.\n- gruppi un po' migliorati.\n- e altro ancora!"; "- more stable message delivery.\n- a bit better groups.\n- and more!" = "- recapito dei messaggi più stabile.\n- gruppi un po' migliorati.\n- e altro ancora!";
/* No comment provided by engineer. */
"- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!" = "- avvisa facoltativamente i contatti eliminati.\n- nomi del profilo con spazi.\n- e molto altro!";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- messaggi vocali fino a 5 minuti.\n- tempo di scomparsa personalizzato.\n- cronologia delle modifiche."; "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- messaggi vocali fino a 5 minuti.\n- tempo di scomparsa personalizzato.\n- cronologia delle modifiche.";
@@ -46,12 +43,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"(" = "("; "(" = "(";
/* No comment provided by engineer. */
"(new)" = "(nuovo)";
/* No comment provided by engineer. */
"(this device v%@)" = "(questo dispositivo v%@)";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
")" = ")"; ")" = ")";
@@ -557,9 +548,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Back" = "Indietro"; "Back" = "Indietro";
/* No comment provided by engineer. */
"Bad desktop address" = "Indirizzo desktop errato";
/* integrity error chat item */ /* integrity error chat item */
"bad message hash" = "hash del messaggio errato"; "bad message hash" = "hash del messaggio errato";
@@ -572,18 +560,12 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Bad message ID" = "ID del messaggio errato"; "Bad message ID" = "ID del messaggio errato";
/* No comment provided by engineer. */
"Better groups" = "Gruppi migliorati";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Better messages" = "Messaggi migliorati"; "Better messages" = "Messaggi migliorati";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Block" = "Blocca"; "Block" = "Blocca";
/* No comment provided by engineer. */
"Block group members" = "Blocca i membri dei gruppi";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Block member" = "Blocca membro"; "Block member" = "Blocca membro";
@@ -789,9 +771,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connect incognito" = "Connetti in incognito"; "Connect incognito" = "Connetti in incognito";
/* No comment provided by engineer. */
"Connect to desktop" = "Connetti al desktop";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"connect to SimpleX Chat developers." = "connettiti agli sviluppatori di SimpleX Chat."; "connect to SimpleX Chat developers." = "connettiti agli sviluppatori di SimpleX Chat.";
@@ -822,15 +801,9 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"connected" = "connesso/a"; "connected" = "connesso/a";
/* No comment provided by engineer. */
"Connected desktop" = "Desktop connesso";
/* rcv group event chat item */ /* rcv group event chat item */
"connected directly" = "si è connesso/a direttamente"; "connected directly" = "si è connesso/a direttamente";
/* No comment provided by engineer. */
"Connected to desktop" = "Connesso al desktop";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"connecting" = "in connessione"; "connecting" = "in connessione";
@@ -855,9 +828,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connecting server… (error: %@)" = "Connessione al server… (errore: %@)"; "Connecting server… (error: %@)" = "Connessione al server… (errore: %@)";
/* No comment provided by engineer. */
"Connecting to desktop" = "Connessione al desktop";
/* chat list item title */ /* chat list item title */
"connecting…" = "in connessione…"; "connecting…" = "in connessione…";
@@ -876,9 +846,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connection request sent!" = "Richiesta di connessione inviata!"; "Connection request sent!" = "Richiesta di connessione inviata!";
/* No comment provided by engineer. */
"Connection terminated" = "Connessione terminata";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connection timeout" = "Connessione scaduta"; "Connection timeout" = "Connessione scaduta";
@@ -933,9 +900,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Create" = "Crea"; "Create" = "Crea";
/* No comment provided by engineer. */
"Create a group using a random profile." = "Crea un gruppo usando un profilo casuale.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Create an address to let people connect with you." = "Crea un indirizzo per consentire alle persone di connettersi con te."; "Create an address to let people connect with you." = "Crea un indirizzo per consentire alle persone di connettersi con te.";
@@ -1209,15 +1173,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Description" = "Descrizione"; "Description" = "Descrizione";
/* No comment provided by engineer. */
"Desktop address" = "Indirizzo desktop";
/* No comment provided by engineer. */
"Desktop app version %@ is not compatible with this app." = "La versione dell'app desktop %@ non è compatibile con questa app.";
/* No comment provided by engineer. */
"Desktop devices" = "Dispositivi desktop";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Develop" = "Sviluppa"; "Develop" = "Sviluppa";
@@ -1281,9 +1236,6 @@
/* server test step */ /* server test step */
"Disconnect" = "Disconnetti"; "Disconnect" = "Disconnetti";
/* No comment provided by engineer. */
"Disconnect desktop?" = "Disconnettere il desktop?";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Discover and join groups" = "Scopri ed unisciti ai gruppi"; "Discover and join groups" = "Scopri ed unisciti ai gruppi";
@@ -1422,12 +1374,6 @@
/* chat item text */ /* chat item text */
"encryption re-negotiation allowed for %@" = "rinegoziazione della crittografia consentita per %@"; "encryption re-negotiation allowed for %@" = "rinegoziazione della crittografia consentita per %@";
/* message decrypt error item */
"Encryption re-negotiation error" = "Errore di rinegoziazione crittografia";
/* No comment provided by engineer. */
"Encryption re-negotiation failed." = "Rinegoziazione crittografia fallita.";
/* chat item text */ /* chat item text */
"encryption re-negotiation required" = "richiesta rinegoziazione della crittografia"; "encryption re-negotiation required" = "richiesta rinegoziazione della crittografia";
@@ -1458,9 +1404,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Enter server manually" = "Inserisci il server a mano"; "Enter server manually" = "Inserisci il server a mano";
/* No comment provided by engineer. */
"Enter this device name…" = "Inserisci il nome di questo dispositivo…";
/* placeholder */ /* placeholder */
"Enter welcome message…" = "Inserisci il messaggio di benvenuto…"; "Enter welcome message…" = "Inserisci il messaggio di benvenuto…";
@@ -1662,9 +1605,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Fast and no wait until the sender is online!" = "Veloce e senza aspettare che il mittente sia in linea!"; "Fast and no wait until the sender is online!" = "Veloce e senza aspettare che il mittente sia in linea!";
/* No comment provided by engineer. */
"Faster joining and more reliable messages." = "Ingresso più veloce e messaggi più affidabili.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Favorite" = "Preferito"; "Favorite" = "Preferito";
@@ -1926,9 +1866,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Incognito" = "Incognito"; "Incognito" = "Incognito";
/* No comment provided by engineer. */
"Incognito groups" = "Gruppi in incognito";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Incognito mode" = "Modalità incognito"; "Incognito mode" = "Modalità incognito";
@@ -1956,9 +1893,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Incompatible database version" = "Versione del database incompatibile"; "Incompatible database version" = "Versione del database incompatibile";
/* No comment provided by engineer. */
"Incompatible version" = "Versione incompatibile";
/* PIN entry */ /* PIN entry */
"Incorrect passcode" = "Codice di accesso errato"; "Incorrect passcode" = "Codice di accesso errato";
@@ -2094,9 +2028,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Joining group" = "Ingresso nel gruppo"; "Joining group" = "Ingresso nel gruppo";
/* No comment provided by engineer. */
"Keep the app open to use it from desktop" = "Tieni aperta l'app per usarla dal desktop";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Keep your connections" = "Mantieni le tue connessioni"; "Keep your connections" = "Mantieni le tue connessioni";
@@ -2133,15 +2064,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Limitations" = "Limitazioni"; "Limitations" = "Limitazioni";
/* No comment provided by engineer. */
"Link mobile and desktop apps! 🔗" = "Collega le app mobile e desktop! 🔗";
/* No comment provided by engineer. */
"Linked desktop options" = "Opzioni del desktop collegato";
/* No comment provided by engineer. */
"Linked desktops" = "Desktop collegati";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"LIVE" = "IN DIRETTA"; "LIVE" = "IN DIRETTA";
@@ -2540,9 +2462,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Paste" = "Incolla"; "Paste" = "Incolla";
/* No comment provided by engineer. */
"Paste desktop address" = "Incolla l'indirizzo desktop";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Paste image" = "Incolla immagine"; "Paste image" = "Incolla immagine";
@@ -2927,9 +2846,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Scan QR code" = "Scansiona codice QR"; "Scan QR code" = "Scansiona codice QR";
/* No comment provided by engineer. */
"Scan QR code from desktop" = "Scansiona codice QR dal desktop";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Scan security code from your contact's app." = "Scansiona il codice di sicurezza dall'app del tuo contatto."; "Scan security code from your contact's app." = "Scansiona il codice di sicurezza dall'app del tuo contatto.";
@@ -3074,9 +2990,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Servers" = "Server"; "Servers" = "Server";
/* No comment provided by engineer. */
"Session code" = "Codice di sessione";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Set 1 day" = "Imposta 1 giorno"; "Set 1 day" = "Imposta 1 giorno";
@@ -3386,9 +3299,6 @@
/* notification title */ /* notification title */
"this contact" = "questo contatto"; "this contact" = "questo contatto";
/* No comment provided by engineer. */
"This device name" = "Il nome di questo dispositivo";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"This group has over %lld members, delivery receipts are not sent." = "Questo gruppo ha più di %lld membri, le ricevute di consegna non vengono inviate."; "This group has over %lld members, delivery receipts are not sent." = "Questo gruppo ha più di %lld membri, le ricevute di consegna non vengono inviate.";
@@ -3410,9 +3320,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"To connect, your contact can scan QR code or use the link in the app." = "Per connettervi, il tuo contatto può scansionare il codice QR o usare il link nell'app."; "To connect, your contact can scan QR code or use the link in the app." = "Per connettervi, il tuo contatto può scansionare il codice QR o usare il link nell'app.";
/* No comment provided by engineer. */
"To hide unwanted messages." = "Per nascondere messaggi indesiderati.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"To make a new connection" = "Per creare una nuova connessione"; "To make a new connection" = "Per creare una nuova connessione";
@@ -3509,12 +3416,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "A meno che il tuo contatto non abbia eliminato la connessione o che questo link non sia già stato usato, potrebbe essere un errore; per favore segnalalo.\nPer connetterti, chiedi al tuo contatto di creare un altro link di connessione e controlla di avere una connessione di rete stabile."; "Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "A meno che il tuo contatto non abbia eliminato la connessione o che questo link non sia già stato usato, potrebbe essere un errore; per favore segnalalo.\nPer connetterti, chiedi al tuo contatto di creare un altro link di connessione e controlla di avere una connessione di rete stabile.";
/* No comment provided by engineer. */
"Unlink" = "Scollega";
/* No comment provided by engineer. */
"Unlink desktop?" = "Scollegare il desktop?";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Unlock" = "Sblocca"; "Unlock" = "Sblocca";
@@ -3569,9 +3470,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Use for new connections" = "Usa per connessioni nuove"; "Use for new connections" = "Usa per connessioni nuove";
/* No comment provided by engineer. */
"Use from desktop" = "Usa dal desktop";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Use iOS call interface" = "Usa interfaccia di chiamata iOS"; "Use iOS call interface" = "Usa interfaccia di chiamata iOS";
@@ -3593,24 +3491,12 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Using SimpleX Chat servers." = "Utilizzo dei server SimpleX Chat."; "Using SimpleX Chat servers." = "Utilizzo dei server SimpleX Chat.";
/* No comment provided by engineer. */
"v%@" = "v%@";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"v%@ (%@)" = "v%@ (%@)"; "v%@ (%@)" = "v%@ (%@)";
/* No comment provided by engineer. */
"Verify code with desktop" = "Verifica il codice con il desktop";
/* No comment provided by engineer. */
"Verify connection" = "Verifica la connessione";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Verify connection security" = "Verifica la sicurezza della connessione"; "Verify connection security" = "Verifica la sicurezza della connessione";
/* No comment provided by engineer. */
"Verify connections" = "Verifica le connessioni";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Verify security code" = "Verifica codice di sicurezza"; "Verify security code" = "Verifica codice di sicurezza";
@@ -3629,9 +3515,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"via relay" = "via relay"; "via relay" = "via relay";
/* No comment provided by engineer. */
"Via secure quantum resistant protocol." = "Tramite protocollo sicuro resistente alla quantistica.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Video call" = "Videochiamata"; "Video call" = "Videochiamata";
@@ -7,9 +7,6 @@
/* Privacy - Face ID Usage Description */ /* Privacy - Face ID Usage Description */
"NSFaceIDUsageDescription" = "SimpleX usa Face ID per l'autenticazione locale"; "NSFaceIDUsageDescription" = "SimpleX usa Face ID per l'autenticazione locale";
/* Privacy - Local Network Usage Description */
"NSLocalNetworkUsageDescription" = "SimpleX usa l'accesso alla rete locale per consentire di usare il profilo di chat tramite l'app desktop sulla stessa rete.";
/* Privacy - Microphone Usage Description */ /* Privacy - Microphone Usage Description */
"NSMicrophoneUsageDescription" = "SimpleX ha bisogno dell'accesso al microfono per le chiamate audio e video e per registrare messaggi vocali."; "NSMicrophoneUsageDescription" = "SimpleX ha bisogno dell'accesso al microfono per le chiamate audio e video e per registrare messaggi vocali.";
-135
View File
@@ -25,9 +25,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"- more stable message delivery.\n- a bit better groups.\n- and more!" = "- stabielere berichtbezorging.\n- een beetje betere groepen.\n- en meer!"; "- more stable message delivery.\n- a bit better groups.\n- and more!" = "- stabielere berichtbezorging.\n- een beetje betere groepen.\n- en meer!";
/* No comment provided by engineer. */
"- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!" = "- optioneel verwijderde contacten op de hoogte stellen.\n- profielnamen met spaties.\n- en meer!";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- spraakberichten tot 5 minuten.\n- aangepaste tijd om te verdwijnen.\n- bewerkingsgeschiedenis."; "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- spraakberichten tot 5 minuten.\n- aangepaste tijd om te verdwijnen.\n- bewerkingsgeschiedenis.";
@@ -46,12 +43,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"(" = "("; "(" = "(";
/* No comment provided by engineer. */
"(new)" = "(nieuw)";
/* No comment provided by engineer. */
"(this device v%@)" = "(dit apparaat v%@)";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
")" = ")"; ")" = ")";
@@ -545,9 +536,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Authentication unavailable" = "Verificatie niet beschikbaar"; "Authentication unavailable" = "Verificatie niet beschikbaar";
/* member role */
"author" = "auteur";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Auto-accept" = "Automatisch accepteren"; "Auto-accept" = "Automatisch accepteren";
@@ -560,9 +548,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Back" = "Terug"; "Back" = "Terug";
/* No comment provided by engineer. */
"Bad desktop address" = "Onjuist desktopadres";
/* integrity error chat item */ /* integrity error chat item */
"bad message hash" = "Onjuiste bericht hash"; "bad message hash" = "Onjuiste bericht hash";
@@ -575,18 +560,12 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Bad message ID" = "Onjuiste bericht-ID"; "Bad message ID" = "Onjuiste bericht-ID";
/* No comment provided by engineer. */
"Better groups" = "Betere groepen";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Better messages" = "Betere berichten"; "Better messages" = "Betere berichten";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Block" = "Blokkeren"; "Block" = "Blokkeren";
/* No comment provided by engineer. */
"Block group members" = "Groepsleden blokkeren";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Block member" = "Lid blokkeren"; "Block member" = "Lid blokkeren";
@@ -789,15 +768,9 @@
/* server test step */ /* server test step */
"Connect" = "Verbind"; "Connect" = "Verbind";
/* No comment provided by engineer. */
"Connect automatically" = "Automatisch verbinden";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connect incognito" = "Verbind incognito"; "Connect incognito" = "Verbind incognito";
/* No comment provided by engineer. */
"Connect to desktop" = "Verbinden met desktop";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"connect to SimpleX Chat developers." = "maak verbinding met SimpleX Chat-ontwikkelaars."; "connect to SimpleX Chat developers." = "maak verbinding met SimpleX Chat-ontwikkelaars.";
@@ -828,15 +801,9 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"connected" = "verbonden"; "connected" = "verbonden";
/* No comment provided by engineer. */
"Connected desktop" = "Verbonden desktop";
/* rcv group event chat item */ /* rcv group event chat item */
"connected directly" = "direct verbonden"; "connected directly" = "direct verbonden";
/* No comment provided by engineer. */
"Connected to desktop" = "Verbonden met desktop";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"connecting" = "Verbinden"; "connecting" = "Verbinden";
@@ -861,9 +828,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connecting server… (error: %@)" = "Verbinden met server... (fout: %@)"; "Connecting server… (error: %@)" = "Verbinden met server... (fout: %@)";
/* No comment provided by engineer. */
"Connecting to desktop" = "Verbinding maken met desktop";
/* chat list item title */ /* chat list item title */
"connecting…" = "Verbinden…"; "connecting…" = "Verbinden…";
@@ -882,9 +846,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connection request sent!" = "Verbindingsverzoek verzonden!"; "Connection request sent!" = "Verbindingsverzoek verzonden!";
/* No comment provided by engineer. */
"Connection terminated" = "Verbinding beëindigd";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connection timeout" = "Timeout verbinding"; "Connection timeout" = "Timeout verbinding";
@@ -939,9 +900,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Create" = "Maak"; "Create" = "Maak";
/* No comment provided by engineer. */
"Create a group using a random profile." = "Maak een groep met een willekeurig profiel.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Create an address to let people connect with you." = "Maak een adres aan zodat mensen contact met je kunnen opnemen."; "Create an address to let people connect with you." = "Maak een adres aan zodat mensen contact met je kunnen opnemen.";
@@ -1215,15 +1173,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Description" = "Beschrijving"; "Description" = "Beschrijving";
/* No comment provided by engineer. */
"Desktop address" = "Desktop adres";
/* No comment provided by engineer. */
"Desktop app version %@ is not compatible with this app." = "Desktop-app-versie %@ is niet compatibel met deze app.";
/* No comment provided by engineer. */
"Desktop devices" = "Desktop apparaten";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Develop" = "Ontwikkelen"; "Develop" = "Ontwikkelen";
@@ -1287,15 +1236,9 @@
/* server test step */ /* server test step */
"Disconnect" = "verbinding verbreken"; "Disconnect" = "verbinding verbreken";
/* No comment provided by engineer. */
"Disconnect desktop?" = "Desktop loskoppelen?";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Discover and join groups" = "Ontdek en sluit je aan bij groepen"; "Discover and join groups" = "Ontdek en sluit je aan bij groepen";
/* No comment provided by engineer. */
"Discover via local network" = "Ontdek via het lokale netwerk";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Do it later" = "Doe het later"; "Do it later" = "Doe het later";
@@ -1431,12 +1374,6 @@
/* chat item text */ /* chat item text */
"encryption re-negotiation allowed for %@" = "versleuteling heronderhandeling toegestaan voor % @"; "encryption re-negotiation allowed for %@" = "versleuteling heronderhandeling toegestaan voor % @";
/* message decrypt error item */
"Encryption re-negotiation error" = "Fout bij heronderhandeling van codering";
/* No comment provided by engineer. */
"Encryption re-negotiation failed." = "Opnieuw onderhandelen over de codering is mislukt.";
/* chat item text */ /* chat item text */
"encryption re-negotiation required" = "heronderhandeling van versleuteling vereist"; "encryption re-negotiation required" = "heronderhandeling van versleuteling vereist";
@@ -1467,9 +1404,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Enter server manually" = "Voer de server handmatig in"; "Enter server manually" = "Voer de server handmatig in";
/* No comment provided by engineer. */
"Enter this device name…" = "Voer deze apparaatnaam in…";
/* placeholder */ /* placeholder */
"Enter welcome message…" = "Welkomst bericht invoeren…"; "Enter welcome message…" = "Welkomst bericht invoeren…";
@@ -1671,9 +1605,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Fast and no wait until the sender is online!" = "Snel en niet wachten tot de afzender online is!"; "Fast and no wait until the sender is online!" = "Snel en niet wachten tot de afzender online is!";
/* No comment provided by engineer. */
"Faster joining and more reliable messages." = "Snellere deelname en betrouwbaardere berichten.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Favorite" = "Favoriet"; "Favorite" = "Favoriet";
@@ -1731,9 +1662,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"For console" = "Voor console"; "For console" = "Voor console";
/* No comment provided by engineer. */
"Found desktop" = "Desktop gevonden";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"French interface" = "Franse interface"; "French interface" = "Franse interface";
@@ -1938,9 +1866,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Incognito" = "Incognito"; "Incognito" = "Incognito";
/* No comment provided by engineer. */
"Incognito groups" = "Incognitogroepen";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Incognito mode" = "Incognito modus"; "Incognito mode" = "Incognito modus";
@@ -1968,9 +1893,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Incompatible database version" = "Incompatibele database versie"; "Incompatible database version" = "Incompatibele database versie";
/* No comment provided by engineer. */
"Incompatible version" = "Incompatibele versie";
/* PIN entry */ /* PIN entry */
"Incorrect passcode" = "Onjuiste toegangscode"; "Incorrect passcode" = "Onjuiste toegangscode";
@@ -2106,9 +2028,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Joining group" = "Deel nemen aan groep"; "Joining group" = "Deel nemen aan groep";
/* No comment provided by engineer. */
"Keep the app open to use it from desktop" = "Houd de app geopend om deze vanaf de desktop te gebruiken";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Keep your connections" = "Behoud uw verbindingen"; "Keep your connections" = "Behoud uw verbindingen";
@@ -2145,15 +2064,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Limitations" = "Beperkingen"; "Limitations" = "Beperkingen";
/* No comment provided by engineer. */
"Link mobile and desktop apps! 🔗" = "Koppel mobiele en desktop-apps! 🔗";
/* No comment provided by engineer. */
"Linked desktop options" = "Gekoppelde desktop opties";
/* No comment provided by engineer. */
"Linked desktops" = "Gelinkte desktops";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"LIVE" = "LIVE"; "LIVE" = "LIVE";
@@ -2409,9 +2319,6 @@
/* copied message info in history */ /* copied message info in history */
"no text" = "geen tekst"; "no text" = "geen tekst";
/* No comment provided by engineer. */
"Not compatible!" = "Niet compatibel!";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Notifications" = "Meldingen"; "Notifications" = "Meldingen";
@@ -2555,9 +2462,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Paste" = "Plakken"; "Paste" = "Plakken";
/* No comment provided by engineer. */
"Paste desktop address" = "Desktopadres plakken";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Paste image" = "Afbeelding plakken"; "Paste image" = "Afbeelding plakken";
@@ -2942,9 +2846,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Scan QR code" = "Scan QR-code"; "Scan QR code" = "Scan QR-code";
/* No comment provided by engineer. */
"Scan QR code from desktop" = "Scan QR-code vanaf uw desktop";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Scan security code from your contact's app." = "Scan de beveiligingscode van de app van uw contact."; "Scan security code from your contact's app." = "Scan de beveiligingscode van de app van uw contact.";
@@ -3089,9 +2990,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Servers" = "Servers"; "Servers" = "Servers";
/* No comment provided by engineer. */
"Session code" = "Sessie code";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Set 1 day" = "Stel 1 dag in"; "Set 1 day" = "Stel 1 dag in";
@@ -3401,9 +3299,6 @@
/* notification title */ /* notification title */
"this contact" = "dit contact"; "this contact" = "dit contact";
/* No comment provided by engineer. */
"This device name" = "Deze apparaatnaam";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"This group has over %lld members, delivery receipts are not sent." = "Deze groep heeft meer dan %lld -leden, ontvangstbevestigingen worden niet verzonden."; "This group has over %lld members, delivery receipts are not sent." = "Deze groep heeft meer dan %lld -leden, ontvangstbevestigingen worden niet verzonden.";
@@ -3425,9 +3320,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"To connect, your contact can scan QR code or use the link in the app." = "Om verbinding te maken, kan uw contact de QR-code scannen of de link in de app gebruiken."; "To connect, your contact can scan QR code or use the link in the app." = "Om verbinding te maken, kan uw contact de QR-code scannen of de link in de app gebruiken.";
/* No comment provided by engineer. */
"To hide unwanted messages." = "Om ongewenste berichten te verbergen.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"To make a new connection" = "Om een nieuwe verbinding te maken"; "To make a new connection" = "Om een nieuwe verbinding te maken";
@@ -3524,12 +3416,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "Tenzij uw contact de verbinding heeft verwijderd of deze link al is gebruikt, kan het een bug zijn. Meld het alstublieft.\nOm verbinding te maken, vraagt u uw contact om een andere verbinding link te maken en te controleren of u een stabiele netwerkverbinding heeft."; "Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "Tenzij uw contact de verbinding heeft verwijderd of deze link al is gebruikt, kan het een bug zijn. Meld het alstublieft.\nOm verbinding te maken, vraagt u uw contact om een andere verbinding link te maken en te controleren of u een stabiele netwerkverbinding heeft.";
/* No comment provided by engineer. */
"Unlink" = "Ontkoppelen";
/* No comment provided by engineer. */
"Unlink desktop?" = "Desktop ontkoppelen?";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Unlock" = "Ontgrendelen"; "Unlock" = "Ontgrendelen";
@@ -3584,9 +3470,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Use for new connections" = "Gebruik voor nieuwe verbindingen"; "Use for new connections" = "Gebruik voor nieuwe verbindingen";
/* No comment provided by engineer. */
"Use from desktop" = "Gebruik vanaf desktop";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Use iOS call interface" = "De iOS-oproepinterface gebruiken"; "Use iOS call interface" = "De iOS-oproepinterface gebruiken";
@@ -3608,24 +3491,12 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Using SimpleX Chat servers." = "SimpleX Chat servers gebruiken."; "Using SimpleX Chat servers." = "SimpleX Chat servers gebruiken.";
/* No comment provided by engineer. */
"v%@" = "v%@";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"v%@ (%@)" = "v%@ (%@)"; "v%@ (%@)" = "v%@ (%@)";
/* No comment provided by engineer. */
"Verify code with desktop" = "Code verifiëren met desktop";
/* No comment provided by engineer. */
"Verify connection" = "Controleer de verbinding";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Verify connection security" = "Controleer de verbindingsbeveiliging"; "Verify connection security" = "Controleer de verbindingsbeveiliging";
/* No comment provided by engineer. */
"Verify connections" = "Controleer verbindingen";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Verify security code" = "Controleer de beveiligingscode"; "Verify security code" = "Controleer de beveiligingscode";
@@ -3644,9 +3515,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"via relay" = "via relais"; "via relay" = "via relais";
/* No comment provided by engineer. */
"Via secure quantum resistant protocol." = "Via een beveiligd kwantumbestendig protocol.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Video call" = "video oproep"; "Video call" = "video oproep";
@@ -3686,9 +3554,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"waiting for confirmation…" = "Wachten op bevestiging…"; "waiting for confirmation…" = "Wachten op bevestiging…";
/* No comment provided by engineer. */
"Waiting for desktop..." = "Wachten op desktop...";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Waiting for file" = "Wachten op bestand"; "Waiting for file" = "Wachten op bestand";
@@ -7,9 +7,6 @@
/* Privacy - Face ID Usage Description */ /* Privacy - Face ID Usage Description */
"NSFaceIDUsageDescription" = "SimpleX gebruikt Face-ID voor lokale authenticatie"; "NSFaceIDUsageDescription" = "SimpleX gebruikt Face-ID voor lokale authenticatie";
/* Privacy - Local Network Usage Description */
"NSLocalNetworkUsageDescription" = "SimpleX maakt gebruik van lokale netwerktoegang om het gebruik van een gebruikerschatprofiel via de desktop-app op hetzelfde netwerk mogelijk te maken.";
/* Privacy - Microphone Usage Description */ /* Privacy - Microphone Usage Description */
"NSMicrophoneUsageDescription" = "SimpleX heeft microfoon toegang nodig voor audio en video oproepen en om spraak berichten op te nemen."; "NSMicrophoneUsageDescription" = "SimpleX heeft microfoon toegang nodig voor audio en video oproepen en om spraak berichten op te nemen.";
-135
View File
@@ -25,9 +25,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"- more stable message delivery.\n- a bit better groups.\n- and more!" = "- bardziej stabilne dostarczanie wiadomości.\n- nieco lepsze grupy.\n- i więcej!"; "- more stable message delivery.\n- a bit better groups.\n- and more!" = "- bardziej stabilne dostarczanie wiadomości.\n- nieco lepsze grupy.\n- i więcej!";
/* No comment provided by engineer. */
"- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!" = "- opcjonalnie powiadamiaj usunięte kontakty.\n- nazwy profili ze spacją.\n- i wiele więcej!";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- wiadomości głosowe do 5 minut.\n- niestandardowy czas zniknięcia.\n- historia edycji."; "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- wiadomości głosowe do 5 minut.\n- niestandardowy czas zniknięcia.\n- historia edycji.";
@@ -46,12 +43,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"(" = "("; "(" = "(";
/* No comment provided by engineer. */
"(new)" = "(nowy)";
/* No comment provided by engineer. */
"(this device v%@)" = "(to urządzenie v%@)";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
")" = ")"; ")" = ")";
@@ -545,9 +536,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Authentication unavailable" = "Uwierzytelnianie niedostępne"; "Authentication unavailable" = "Uwierzytelnianie niedostępne";
/* member role */
"author" = "autor";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Auto-accept" = "Automatycznie akceptuj"; "Auto-accept" = "Automatycznie akceptuj";
@@ -560,9 +548,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Back" = "Wstecz"; "Back" = "Wstecz";
/* No comment provided by engineer. */
"Bad desktop address" = "Zły adres komputera";
/* integrity error chat item */ /* integrity error chat item */
"bad message hash" = "zły hash wiadomości"; "bad message hash" = "zły hash wiadomości";
@@ -575,18 +560,12 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Bad message ID" = "Zły identyfikator wiadomości"; "Bad message ID" = "Zły identyfikator wiadomości";
/* No comment provided by engineer. */
"Better groups" = "Lepsze grupy";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Better messages" = "Lepsze wiadomości"; "Better messages" = "Lepsze wiadomości";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Block" = "Zablokuj"; "Block" = "Zablokuj";
/* No comment provided by engineer. */
"Block group members" = "Blokuj członków grupy";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Block member" = "Zablokuj członka"; "Block member" = "Zablokuj członka";
@@ -789,15 +768,9 @@
/* server test step */ /* server test step */
"Connect" = "Połącz"; "Connect" = "Połącz";
/* No comment provided by engineer. */
"Connect automatically" = "Łącz automatycznie";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connect incognito" = "Połącz incognito"; "Connect incognito" = "Połącz incognito";
/* No comment provided by engineer. */
"Connect to desktop" = "Połącz do komputera";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"connect to SimpleX Chat developers." = "połącz się z deweloperami SimpleX Chat."; "connect to SimpleX Chat developers." = "połącz się z deweloperami SimpleX Chat.";
@@ -828,15 +801,9 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"connected" = "połączony"; "connected" = "połączony";
/* No comment provided by engineer. */
"Connected desktop" = "Połączony komputer";
/* rcv group event chat item */ /* rcv group event chat item */
"connected directly" = "połącz bezpośrednio"; "connected directly" = "połącz bezpośrednio";
/* No comment provided by engineer. */
"Connected to desktop" = "Połączony do komputera";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"connecting" = "łączenie"; "connecting" = "łączenie";
@@ -861,9 +828,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connecting server… (error: %@)" = "Łączenie z serwerem... (błąd: %@)"; "Connecting server… (error: %@)" = "Łączenie z serwerem... (błąd: %@)";
/* No comment provided by engineer. */
"Connecting to desktop" = "Łączenie z komputerem";
/* chat list item title */ /* chat list item title */
"connecting…" = "łączenie…"; "connecting…" = "łączenie…";
@@ -882,9 +846,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connection request sent!" = "Prośba o połączenie wysłana!"; "Connection request sent!" = "Prośba o połączenie wysłana!";
/* No comment provided by engineer. */
"Connection terminated" = "Połączenie zakończone";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connection timeout" = "Czas połączenia minął"; "Connection timeout" = "Czas połączenia minął";
@@ -939,9 +900,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Create" = "Utwórz"; "Create" = "Utwórz";
/* No comment provided by engineer. */
"Create a group using a random profile." = "Utwórz grupę używając losowego profilu.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Create an address to let people connect with you." = "Utwórz adres, aby ludzie mogli się z Tobą połączyć."; "Create an address to let people connect with you." = "Utwórz adres, aby ludzie mogli się z Tobą połączyć.";
@@ -1215,15 +1173,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Description" = "Opis"; "Description" = "Opis";
/* No comment provided by engineer. */
"Desktop address" = "Adres komputera";
/* No comment provided by engineer. */
"Desktop app version %@ is not compatible with this app." = "Wersja aplikacji komputerowej %@ nie jest kompatybilna z tą aplikacją.";
/* No comment provided by engineer. */
"Desktop devices" = "Urządzenia komputerowe";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Develop" = "Deweloperskie"; "Develop" = "Deweloperskie";
@@ -1287,15 +1236,9 @@
/* server test step */ /* server test step */
"Disconnect" = "Rozłącz"; "Disconnect" = "Rozłącz";
/* No comment provided by engineer. */
"Disconnect desktop?" = "Rozłączyć komputer?";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Discover and join groups" = "Odkrywaj i dołączaj do grup"; "Discover and join groups" = "Odkrywaj i dołączaj do grup";
/* No comment provided by engineer. */
"Discover via local network" = "Odkryj przez sieć lokalną";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Do it later" = "Zrób to później"; "Do it later" = "Zrób to później";
@@ -1431,12 +1374,6 @@
/* chat item text */ /* chat item text */
"encryption re-negotiation allowed for %@" = "renegocjacja szyfrowania dozwolona dla %@"; "encryption re-negotiation allowed for %@" = "renegocjacja szyfrowania dozwolona dla %@";
/* message decrypt error item */
"Encryption re-negotiation error" = "Błąd renegocjacji szyfrowania";
/* No comment provided by engineer. */
"Encryption re-negotiation failed." = "Renegocjacja szyfrowania nie powiodła się.";
/* chat item text */ /* chat item text */
"encryption re-negotiation required" = "renegocjacja szyfrowania wymagana"; "encryption re-negotiation required" = "renegocjacja szyfrowania wymagana";
@@ -1467,9 +1404,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Enter server manually" = "Wprowadź serwer ręcznie"; "Enter server manually" = "Wprowadź serwer ręcznie";
/* No comment provided by engineer. */
"Enter this device name…" = "Podaj nazwę urządzenia…";
/* placeholder */ /* placeholder */
"Enter welcome message…" = "Wpisz wiadomość powitalną…"; "Enter welcome message…" = "Wpisz wiadomość powitalną…";
@@ -1671,9 +1605,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Fast and no wait until the sender is online!" = "Szybko i bez czekania aż nadawca będzie online!"; "Fast and no wait until the sender is online!" = "Szybko i bez czekania aż nadawca będzie online!";
/* No comment provided by engineer. */
"Faster joining and more reliable messages." = "Szybsze dołączenie i bardziej niezawodne wiadomości.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Favorite" = "Ulubione"; "Favorite" = "Ulubione";
@@ -1731,9 +1662,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"For console" = "Dla konsoli"; "For console" = "Dla konsoli";
/* No comment provided by engineer. */
"Found desktop" = "Znaleziono komputer";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"French interface" = "Francuski interfejs"; "French interface" = "Francuski interfejs";
@@ -1938,9 +1866,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Incognito" = "Incognito"; "Incognito" = "Incognito";
/* No comment provided by engineer. */
"Incognito groups" = "Grupy incognito";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Incognito mode" = "Tryb incognito"; "Incognito mode" = "Tryb incognito";
@@ -1968,9 +1893,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Incompatible database version" = "Niekompatybilna wersja bazy danych"; "Incompatible database version" = "Niekompatybilna wersja bazy danych";
/* No comment provided by engineer. */
"Incompatible version" = "Niekompatybilna wersja";
/* PIN entry */ /* PIN entry */
"Incorrect passcode" = "Nieprawidłowy pin"; "Incorrect passcode" = "Nieprawidłowy pin";
@@ -2106,9 +2028,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Joining group" = "Dołączanie do grupy"; "Joining group" = "Dołączanie do grupy";
/* No comment provided by engineer. */
"Keep the app open to use it from desktop" = "Zostaw aplikację otwartą i używaj ją z komputera";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Keep your connections" = "Zachowaj swoje połączenia"; "Keep your connections" = "Zachowaj swoje połączenia";
@@ -2145,15 +2064,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Limitations" = "Ograniczenia"; "Limitations" = "Ograniczenia";
/* No comment provided by engineer. */
"Link mobile and desktop apps! 🔗" = "Połącz mobile i komputerowe aplikacje! 🔗";
/* No comment provided by engineer. */
"Linked desktop options" = "Połączone opcje komputera";
/* No comment provided by engineer. */
"Linked desktops" = "Połączone komputery";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"LIVE" = "NA ŻYWO"; "LIVE" = "NA ŻYWO";
@@ -2409,9 +2319,6 @@
/* copied message info in history */ /* copied message info in history */
"no text" = "brak tekstu"; "no text" = "brak tekstu";
/* No comment provided by engineer. */
"Not compatible!" = "Nie kompatybilny!";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Notifications" = "Powiadomienia"; "Notifications" = "Powiadomienia";
@@ -2555,9 +2462,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Paste" = "Wklej"; "Paste" = "Wklej";
/* No comment provided by engineer. */
"Paste desktop address" = "Wklej adres komputera";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Paste image" = "Wklej obraz"; "Paste image" = "Wklej obraz";
@@ -2942,9 +2846,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Scan QR code" = "Zeskanuj kod QR"; "Scan QR code" = "Zeskanuj kod QR";
/* No comment provided by engineer. */
"Scan QR code from desktop" = "Zeskanuj kod QR z komputera";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Scan security code from your contact's app." = "Zeskanuj kod bezpieczeństwa z aplikacji Twojego kontaktu."; "Scan security code from your contact's app." = "Zeskanuj kod bezpieczeństwa z aplikacji Twojego kontaktu.";
@@ -3089,9 +2990,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Servers" = "Serwery"; "Servers" = "Serwery";
/* No comment provided by engineer. */
"Session code" = "Kod sesji";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Set 1 day" = "Ustaw 1 dzień"; "Set 1 day" = "Ustaw 1 dzień";
@@ -3401,9 +3299,6 @@
/* notification title */ /* notification title */
"this contact" = "ten kontakt"; "this contact" = "ten kontakt";
/* No comment provided by engineer. */
"This device name" = "Nazwa tego urządzenia";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"This group has over %lld members, delivery receipts are not sent." = "Ta grupa ma ponad %lld członków, potwierdzenia dostawy nie są wysyłane."; "This group has over %lld members, delivery receipts are not sent." = "Ta grupa ma ponad %lld członków, potwierdzenia dostawy nie są wysyłane.";
@@ -3425,9 +3320,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"To connect, your contact can scan QR code or use the link in the app." = "Aby się połączyć, Twój kontakt może zeskanować kod QR lub skorzystać z linku w aplikacji."; "To connect, your contact can scan QR code or use the link in the app." = "Aby się połączyć, Twój kontakt może zeskanować kod QR lub skorzystać z linku w aplikacji.";
/* No comment provided by engineer. */
"To hide unwanted messages." = "Aby ukryć niechciane wiadomości.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"To make a new connection" = "Aby nawiązać nowe połączenie"; "To make a new connection" = "Aby nawiązać nowe połączenie";
@@ -3524,12 +3416,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "O ile Twój kontakt nie usunął połączenia lub ten link był już użyty, może to być błąd - zgłoś go.\nAby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połączenia i sprawdź, czy masz stabilne połączenie z siecią."; "Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "O ile Twój kontakt nie usunął połączenia lub ten link był już użyty, może to być błąd - zgłoś go.\nAby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połączenia i sprawdź, czy masz stabilne połączenie z siecią.";
/* No comment provided by engineer. */
"Unlink" = "Odłącz";
/* No comment provided by engineer. */
"Unlink desktop?" = "Odłączyć komputer?";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Unlock" = "Odblokuj"; "Unlock" = "Odblokuj";
@@ -3584,9 +3470,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Use for new connections" = "Użyj dla nowych połączeń"; "Use for new connections" = "Użyj dla nowych połączeń";
/* No comment provided by engineer. */
"Use from desktop" = "Użyj z komputera";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Use iOS call interface" = "Użyj interfejsu połączeń iOS"; "Use iOS call interface" = "Użyj interfejsu połączeń iOS";
@@ -3608,24 +3491,12 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Using SimpleX Chat servers." = "Używanie serwerów SimpleX Chat."; "Using SimpleX Chat servers." = "Używanie serwerów SimpleX Chat.";
/* No comment provided by engineer. */
"v%@" = "v%@";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"v%@ (%@)" = "v%@ (%@)"; "v%@ (%@)" = "v%@ (%@)";
/* No comment provided by engineer. */
"Verify code with desktop" = "Zweryfikuj kod z komputera";
/* No comment provided by engineer. */
"Verify connection" = "Zweryfikuj połączenie";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Verify connection security" = "Weryfikuj bezpieczeństwo połączenia"; "Verify connection security" = "Weryfikuj bezpieczeństwo połączenia";
/* No comment provided by engineer. */
"Verify connections" = "Zweryfikuj połączenia";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Verify security code" = "Weryfikuj kod bezpieczeństwa"; "Verify security code" = "Weryfikuj kod bezpieczeństwa";
@@ -3644,9 +3515,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"via relay" = "przez przekaźnik"; "via relay" = "przez przekaźnik";
/* No comment provided by engineer. */
"Via secure quantum resistant protocol." = "Dzięki bezpiecznemu protokołowi odpornego kwantowo.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Video call" = "Połączenie wideo"; "Video call" = "Połączenie wideo";
@@ -3686,9 +3554,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"waiting for confirmation…" = "oczekiwanie na potwierdzenie…"; "waiting for confirmation…" = "oczekiwanie na potwierdzenie…";
/* No comment provided by engineer. */
"Waiting for desktop..." = "Oczekiwanie na komputer...";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Waiting for file" = "Oczekiwanie na plik"; "Waiting for file" = "Oczekiwanie na plik";
@@ -7,9 +7,6 @@
/* Privacy - Face ID Usage Description */ /* Privacy - Face ID Usage Description */
"NSFaceIDUsageDescription" = "SimpleX używa Face ID do lokalnego uwierzytelniania"; "NSFaceIDUsageDescription" = "SimpleX używa Face ID do lokalnego uwierzytelniania";
/* Privacy - Local Network Usage Description */
"NSLocalNetworkUsageDescription" = "SimpleX używa sieci lokalnej aby pozwolić na dostęp profilom czatu użytkownika przez aplikację komputerową na tej samej sieci.";
/* Privacy - Microphone Usage Description */ /* Privacy - Microphone Usage Description */
"NSMicrophoneUsageDescription" = "SimpleX potrzebuje dostępu do mikrofonu, w celu połączeń audio i wideo oraz nagrywania wiadomości głosowych."; "NSMicrophoneUsageDescription" = "SimpleX potrzebuje dostępu do mikrofonu, w celu połączeń audio i wideo oraz nagrywania wiadomości głosowych.";
-339
View File
@@ -25,9 +25,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"- more stable message delivery.\n- a bit better groups.\n- and more!" = "- более стабильная доставка сообщений.\n- немного улучшенные группы.\n- и прочее!"; "- more stable message delivery.\n- a bit better groups.\n- and more!" = "- более стабильная доставка сообщений.\n- немного улучшенные группы.\n- и прочее!";
/* No comment provided by engineer. */
"- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!" = "- опционально уведомляйте удалённые контакты.\n- имена профилей с пробелами.\n- и прочее!";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- голосовые сообщения до 5 минут.\n- настройка времени исчезающих сообщений.\n- история редактирования."; "- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- голосовые сообщения до 5 минут.\n- настройка времени исчезающих сообщений.\n- история редактирования.";
@@ -46,12 +43,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"(" = "("; "(" = "(";
/* No comment provided by engineer. */
"(new)" = "(новое)";
/* No comment provided by engineer. */
"(this device v%@)" = "(это устройство v%@)";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
")" = ")"; ")" = ")";
@@ -127,18 +118,12 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"%@ %@" = "%@ %@"; "%@ %@" = "%@ %@";
/* No comment provided by engineer. */
"%@ and %@" = "%@ и %@";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"%@ and %@ connected" = "%@ и %@ соединены"; "%@ and %@ connected" = "%@ и %@ соединены";
/* copied message info, <sender> at <time> */ /* copied message info, <sender> at <time> */
"%@ at %@:" = "%1$@ в %2$@:"; "%@ at %@:" = "%1$@ в %2$@:";
/* No comment provided by engineer. */
"%@ connected" = "%@ соединен(а)";
/* notification title */ /* notification title */
"%@ is connected!" = "Установлено соединение с %@!"; "%@ is connected!" = "Установлено соединение с %@!";
@@ -154,9 +139,6 @@
/* notification title */ /* notification title */
"%@ wants to connect!" = "%@ хочет соединиться!"; "%@ wants to connect!" = "%@ хочет соединиться!";
/* No comment provided by engineer. */
"%@, %@ and %lld members" = "%@, %@ и %lld членов группы";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"%@, %@ and %lld other members connected" = "%@, %@ и %lld других членов соединены"; "%@, %@ and %lld other members connected" = "%@, %@ и %lld других членов соединены";
@@ -196,21 +178,9 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"%lld file(s) with total size of %@" = "%lld файл(ов) общим размером %@"; "%lld file(s) with total size of %@" = "%lld файл(ов) общим размером %@";
/* No comment provided by engineer. */
"%lld group events" = "%lld событий";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"%lld members" = "Членов группы: %lld"; "%lld members" = "Членов группы: %lld";
/* No comment provided by engineer. */
"%lld messages blocked" = "%lld сообщений заблокировано";
/* No comment provided by engineer. */
"%lld messages marked deleted" = "%lld сообщений помечено удалёнными";
/* No comment provided by engineer. */
"%lld messages moderated by %@" = "%lld сообщений модерировано членом %@";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"%lld minutes" = "%lld минуты"; "%lld minutes" = "%lld минуты";
@@ -259,9 +229,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"~strike~" = "\\~зачеркнуть~"; "~strike~" = "\\~зачеркнуть~";
/* time to disappear */
"0 sec" = "0 сек";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"0s" = "0с"; "0s" = "0с";
@@ -404,9 +371,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you." = "Все сообщения будут удалены - это действие нельзя отменить! Сообщения будут удалены только для Вас."; "All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you." = "Все сообщения будут удалены - это действие нельзя отменить! Сообщения будут удалены только для Вас.";
/* No comment provided by engineer. */
"All new messages from %@ will be hidden!" = "Все новые сообщения от %@ будут скрыты!";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"All your contacts will remain connected." = "Все контакты, которые соединились через этот адрес, сохранятся."; "All your contacts will remain connected." = "Все контакты, которые соединились через этот адрес, сохранятся.";
@@ -470,12 +434,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Already connected?" = "Соединение уже установлено?"; "Already connected?" = "Соединение уже установлено?";
/* No comment provided by engineer. */
"Already connecting!" = "Уже соединяется!";
/* No comment provided by engineer. */
"Already joining the group!" = "Вступление в группу уже начато!";
/* pref value */ /* pref value */
"always" = "всегда"; "always" = "всегда";
@@ -485,9 +443,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"An empty chat profile with the provided name is created, and the app opens as usual." = "Будет создан пустой профиль чата с указанным именем, и приложение откроется в обычном режиме."; "An empty chat profile with the provided name is created, and the app opens as usual." = "Будет создан пустой профиль чата с указанным именем, и приложение откроется в обычном режиме.";
/* No comment provided by engineer. */
"and %lld other events" = "и %lld других событий";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Answer call" = "Принять звонок"; "Answer call" = "Принять звонок";
@@ -545,9 +500,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Authentication unavailable" = "Аутентификация недоступна"; "Authentication unavailable" = "Аутентификация недоступна";
/* member role */
"author" = "автор";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Auto-accept" = "Автоприем"; "Auto-accept" = "Автоприем";
@@ -560,9 +512,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Back" = "Назад"; "Back" = "Назад";
/* No comment provided by engineer. */
"Bad desktop address" = "Неверный адрес компьютера";
/* integrity error chat item */ /* integrity error chat item */
"bad message hash" = "ошибка хэш сообщения"; "bad message hash" = "ошибка хэш сообщения";
@@ -575,27 +524,9 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Bad message ID" = "Ошибка ID сообщения"; "Bad message ID" = "Ошибка ID сообщения";
/* No comment provided by engineer. */
"Better groups" = "Улучшенные группы";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Better messages" = "Улучшенные сообщения"; "Better messages" = "Улучшенные сообщения";
/* No comment provided by engineer. */
"Block" = "Заблокировать";
/* No comment provided by engineer. */
"Block group members" = "Блокируйте членов группы";
/* No comment provided by engineer. */
"Block member" = "Заблокировать члена группы";
/* No comment provided by engineer. */
"Block member?" = "Заблокировать члена группы?";
/* No comment provided by engineer. */
"blocked" = "заблокировано";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"bold" = "жирный"; "bold" = "жирный";
@@ -789,30 +720,12 @@
/* server test step */ /* server test step */
"Connect" = "Соединиться"; "Connect" = "Соединиться";
/* No comment provided by engineer. */
"Connect automatically" = "Соединяться автоматически";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connect incognito" = "Соединиться Инкогнито"; "Connect incognito" = "Соединиться Инкогнито";
/* No comment provided by engineer. */
"Connect to desktop" = "Подключиться к компьютеру";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"connect to SimpleX Chat developers." = "соединитесь с разработчиками."; "connect to SimpleX Chat developers." = "соединитесь с разработчиками.";
/* No comment provided by engineer. */
"Connect to yourself?" = "Соединиться с самим собой?";
/* No comment provided by engineer. */
"Connect to yourself?\nThis is your own one-time link!" = "Соединиться с самим собой?\nЭто ваша собственная одноразовая ссылка!";
/* No comment provided by engineer. */
"Connect to yourself?\nThis is your own SimpleX address!" = "Соединиться с самим собой?\nЭто ваш собственный адрес SimpleX!";
/* No comment provided by engineer. */
"Connect via contact address" = "Соединиться через адрес";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connect via link" = "Соединиться через ссылку"; "Connect via link" = "Соединиться через ссылку";
@@ -822,21 +735,9 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connect via one-time link" = "Соединиться через одноразовую ссылку"; "Connect via one-time link" = "Соединиться через одноразовую ссылку";
/* No comment provided by engineer. */
"Connect with %@" = "Соединиться с %@";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"connected" = "соединение установлено"; "connected" = "соединение установлено";
/* No comment provided by engineer. */
"Connected desktop" = "Подключенный компьютер";
/* rcv group event chat item */
"connected directly" = "соединен(а) напрямую";
/* No comment provided by engineer. */
"Connected to desktop" = "Компьютер подключен";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"connecting" = "соединяется"; "connecting" = "соединяется";
@@ -861,9 +762,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connecting server… (error: %@)" = "Устанавливается соединение с сервером… (ошибка: %@)"; "Connecting server… (error: %@)" = "Устанавливается соединение с сервером… (ошибка: %@)";
/* No comment provided by engineer. */
"Connecting to desktop" = "Подключение к компьютеру";
/* chat list item title */ /* chat list item title */
"connecting…" = "соединяется…"; "connecting…" = "соединяется…";
@@ -882,9 +780,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connection request sent!" = "Запрос на соединение отправлен!"; "Connection request sent!" = "Запрос на соединение отправлен!";
/* No comment provided by engineer. */
"Connection terminated" = "Подключение прервано";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Connection timeout" = "Превышено время соединения"; "Connection timeout" = "Превышено время соединения";
@@ -933,24 +828,15 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Core version: v%@" = "Версия ядра: v%@"; "Core version: v%@" = "Версия ядра: v%@";
/* No comment provided by engineer. */
"Correct name to %@?" = "Исправить имя на %@?";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Create" = "Создать"; "Create" = "Создать";
/* No comment provided by engineer. */
"Create a group using a random profile." = "Создайте группу, используя случайный профиль.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Create an address to let people connect with you." = "Создайте адрес, чтобы можно было соединиться с вами."; "Create an address to let people connect with you." = "Создайте адрес, чтобы можно было соединиться с вами.";
/* server test step */ /* server test step */
"Create file" = "Создание файла"; "Create file" = "Создание файла";
/* No comment provided by engineer. */
"Create group" = "Создать группу";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Create group link" = "Создать ссылку группы"; "Create group link" = "Создать ссылку группы";
@@ -963,9 +849,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Create one-time invitation link" = "Создать ссылку-приглашение"; "Create one-time invitation link" = "Создать ссылку-приглашение";
/* No comment provided by engineer. */
"Create profile" = "Создать профиль";
/* server test step */ /* server test step */
"Create queue" = "Создание очереди"; "Create queue" = "Создание очереди";
@@ -1080,9 +963,6 @@
/* chat item action */ /* chat item action */
"Delete" = "Удалить"; "Delete" = "Удалить";
/* No comment provided by engineer. */
"Delete %lld messages?" = "Удалить %lld сообщений?";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Delete address" = "Удалить адрес"; "Delete address" = "Удалить адрес";
@@ -1095,9 +975,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Delete all files" = "Удалить все файлы"; "Delete all files" = "Удалить все файлы";
/* No comment provided by engineer. */
"Delete and notify contact" = "Удалить и уведомить контакт";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Delete archive" = "Удалить архив"; "Delete archive" = "Удалить архив";
@@ -1119,9 +996,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Delete Contact" = "Удалить контакт"; "Delete Contact" = "Удалить контакт";
/* No comment provided by engineer. */
"Delete contact?\nThis cannot be undone!" = "Удалить контакт?\nЭто не может быть отменено!";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Delete database" = "Удалить данные чата"; "Delete database" = "Удалить данные чата";
@@ -1197,9 +1071,6 @@
/* copied message info */ /* copied message info */
"Deleted at: %@" = "Удалено: %@"; "Deleted at: %@" = "Удалено: %@";
/* rcv direct event chat item */
"deleted contact" = "удалил(а) контакт";
/* rcv group event chat item */ /* rcv group event chat item */
"deleted group" = "удалил(а) группу"; "deleted group" = "удалил(а) группу";
@@ -1215,15 +1086,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Description" = "Описание"; "Description" = "Описание";
/* No comment provided by engineer. */
"Desktop address" = "Адрес компьютера";
/* No comment provided by engineer. */
"Desktop app version %@ is not compatible with this app." = "Версия настольного приложения %@ несовместима с этим приложением.";
/* No comment provided by engineer. */
"Desktop devices" = "Компьютеры";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Develop" = "Для разработчиков"; "Develop" = "Для разработчиков";
@@ -1287,15 +1149,9 @@
/* server test step */ /* server test step */
"Disconnect" = "Разрыв соединения"; "Disconnect" = "Разрыв соединения";
/* No comment provided by engineer. */
"Disconnect desktop?" = "Отключить компьютер?";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Discover and join groups" = "Найдите и вступите в группы"; "Discover and join groups" = "Найдите и вступите в группы";
/* No comment provided by engineer. */
"Discover via local network" = "Обнаружение по локальной сети";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Do it later" = "Отложить"; "Do it later" = "Отложить";
@@ -1431,12 +1287,6 @@
/* chat item text */ /* chat item text */
"encryption re-negotiation allowed for %@" = "новое соглашение о шифровании разрешено для %@"; "encryption re-negotiation allowed for %@" = "новое соглашение о шифровании разрешено для %@";
/* message decrypt error item */
"Encryption re-negotiation error" = "Ошибка нового соглашения о шифровании";
/* No comment provided by engineer. */
"Encryption re-negotiation failed." = "Ошибка нового соглашения о шифровании.";
/* chat item text */ /* chat item text */
"encryption re-negotiation required" = "требуется новое соглашение о шифровании"; "encryption re-negotiation required" = "требуется новое соглашение о шифровании";
@@ -1452,9 +1302,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Enter correct passphrase." = "Введите правильный пароль."; "Enter correct passphrase." = "Введите правильный пароль.";
/* No comment provided by engineer. */
"Enter group name…" = "Введите имя группы…";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Enter Passcode" = "Введите Код"; "Enter Passcode" = "Введите Код";
@@ -1467,18 +1314,12 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Enter server manually" = "Ввести сервер вручную"; "Enter server manually" = "Ввести сервер вручную";
/* No comment provided by engineer. */
"Enter this device name…" = "Введите имя этого устройства…";
/* placeholder */ /* placeholder */
"Enter welcome message…" = "Введите приветственное сообщение…"; "Enter welcome message…" = "Введите приветственное сообщение…";
/* placeholder */ /* placeholder */
"Enter welcome message… (optional)" = "Введите приветственное сообщение... (опционально)"; "Enter welcome message… (optional)" = "Введите приветственное сообщение... (опционально)";
/* No comment provided by engineer. */
"Enter your name…" = "Введите ваше имя…";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"error" = "ошибка"; "error" = "ошибка";
@@ -1515,9 +1356,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Error creating group link" = "Ошибка при создании ссылки группы"; "Error creating group link" = "Ошибка при создании ссылки группы";
/* No comment provided by engineer. */
"Error creating member contact" = "Ошибка создания контакта с членом группы";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Error creating profile!" = "Ошибка создания профиля!"; "Error creating profile!" = "Ошибка создания профиля!";
@@ -1596,9 +1434,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Error sending email" = "Ошибка отправки email"; "Error sending email" = "Ошибка отправки email";
/* No comment provided by engineer. */
"Error sending member contact invitation" = "Ошибка отправки приглашения члену группы";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Error sending message" = "Ошибка при отправке сообщения"; "Error sending message" = "Ошибка при отправке сообщения";
@@ -1650,9 +1485,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Exit without saving" = "Выйти без сохранения"; "Exit without saving" = "Выйти без сохранения";
/* chat item action */
"Expand" = "Раскрыть";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Export database" = "Экспорт архива чата"; "Export database" = "Экспорт архива чата";
@@ -1671,9 +1503,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Fast and no wait until the sender is online!" = "Быстрые и не нужно ждать, когда отправитель онлайн!"; "Fast and no wait until the sender is online!" = "Быстрые и не нужно ждать, когда отправитель онлайн!";
/* No comment provided by engineer. */
"Faster joining and more reliable messages." = "Быстрое вступление и надежная доставка сообщений.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Favorite" = "Избранный"; "Favorite" = "Избранный";
@@ -1731,9 +1560,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"For console" = "Для консоли"; "For console" = "Для консоли";
/* No comment provided by engineer. */
"Found desktop" = "Компьютер найден";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"French interface" = "Французский интерфейс"; "French interface" = "Французский интерфейс";
@@ -1746,9 +1572,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Full name:" = "Полное имя:"; "Full name:" = "Полное имя:";
/* No comment provided by engineer. */
"Fully decentralized visible only to members." = "Группа полностью децентрализована – она видна только членам.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Fully re-implemented - work in background!" = "Полностью обновлены - работают в фоне!"; "Fully re-implemented - work in background!" = "Полностью обновлены - работают в фоне!";
@@ -1761,12 +1584,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Group" = "Группа"; "Group" = "Группа";
/* No comment provided by engineer. */
"Group already exists" = "Группа уже существует";
/* No comment provided by engineer. */
"Group already exists!" = "Группа уже существует!";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"group deleted" = "группа удалена"; "group deleted" = "группа удалена";
@@ -1938,9 +1755,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Incognito" = "Инкогнито"; "Incognito" = "Инкогнито";
/* No comment provided by engineer. */
"Incognito groups" = "Инкогнито группы";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Incognito mode" = "Режим Инкогнито"; "Incognito mode" = "Режим Инкогнито";
@@ -1968,9 +1782,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Incompatible database version" = "Несовместимая версия базы данных"; "Incompatible database version" = "Несовместимая версия базы данных";
/* No comment provided by engineer. */
"Incompatible version" = "Несовместимая версия";
/* PIN entry */ /* PIN entry */
"Incorrect passcode" = "Неправильный код"; "Incorrect passcode" = "Неправильный код";
@@ -2010,9 +1821,6 @@
/* invalid chat item */ /* invalid chat item */
"invalid data" = "ошибка данных"; "invalid data" = "ошибка данных";
/* No comment provided by engineer. */
"Invalid name!" = "Неверное имя!";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Invalid server address!" = "Ошибка в адресе сервера!"; "Invalid server address!" = "Ошибка в адресе сервера!";
@@ -2091,24 +1899,12 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Join group" = "Вступить в группу"; "Join group" = "Вступить в группу";
/* No comment provided by engineer. */
"Join group?" = "Вступить в группу?";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Join incognito" = "Вступить инкогнито"; "Join incognito" = "Вступить инкогнито";
/* No comment provided by engineer. */
"Join with current profile" = "Вступить с активным профилем";
/* No comment provided by engineer. */
"Join your group?\nThis is your link for group %@!" = "Вступить в вашу группу?\nЭто ваша ссылка на группу %@!";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Joining group" = "Вступление в группу"; "Joining group" = "Вступление в группу";
/* No comment provided by engineer. */
"Keep the app open to use it from desktop" = "Оставьте приложение открытым, чтобы использовать его с компьютера";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Keep your connections" = "Сохраните Ваши соединения"; "Keep your connections" = "Сохраните Ваши соединения";
@@ -2145,15 +1941,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Limitations" = "Ограничения"; "Limitations" = "Ограничения";
/* No comment provided by engineer. */
"Link mobile and desktop apps! 🔗" = "Свяжите мобильное и настольное приложения! 🔗";
/* No comment provided by engineer. */
"Linked desktop options" = "Опции связанных компьютеров";
/* No comment provided by engineer. */
"Linked desktops" = "Связанные компьютеры";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"LIVE" = "LIVE"; "LIVE" = "LIVE";
@@ -2259,9 +2046,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Messages & files" = "Сообщения"; "Messages & files" = "Сообщения";
/* No comment provided by engineer. */
"Messages from %@ will be shown!" = "Сообщения от %@ будут показаны!";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Migrating database archive…" = "Данные чата перемещаются…"; "Migrating database archive…" = "Данные чата перемещаются…";
@@ -2409,9 +2193,6 @@
/* copied message info in history */ /* copied message info in history */
"no text" = "нет текста"; "no text" = "нет текста";
/* No comment provided by engineer. */
"Not compatible!" = "Несовместимая версия!";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Notifications" = "Уведомления"; "Notifications" = "Уведомления";
@@ -2507,18 +2288,12 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Only your contact can send voice messages." = "Только Ваш контакт может отправлять голосовые сообщения."; "Only your contact can send voice messages." = "Только Ваш контакт может отправлять голосовые сообщения.";
/* No comment provided by engineer. */
"Open" = "Открыть";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Open chat" = "Открыть чат"; "Open chat" = "Открыть чат";
/* authentication reason */ /* authentication reason */
"Open chat console" = "Открыть консоль"; "Open chat console" = "Открыть консоль";
/* No comment provided by engineer. */
"Open group" = "Открыть группу";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Open Settings" = "Открыть Настройки"; "Open Settings" = "Открыть Настройки";
@@ -2555,9 +2330,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Paste" = "Вставить"; "Paste" = "Вставить";
/* No comment provided by engineer. */
"Paste desktop address" = "Вставить адрес компьютера";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Paste image" = "Вставить изображение"; "Paste image" = "Вставить изображение";
@@ -2654,12 +2426,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Profile image" = "Аватар"; "Profile image" = "Аватар";
/* No comment provided by engineer. */
"Profile name" = "Имя профиля";
/* No comment provided by engineer. */
"Profile name:" = "Имя профиля:";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Profile password" = "Пароль профиля"; "Profile password" = "Пароль профиля";
@@ -2825,12 +2591,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Renegotiate encryption?" = "Пересогласовать шифрование?"; "Renegotiate encryption?" = "Пересогласовать шифрование?";
/* No comment provided by engineer. */
"Repeat connection request?" = "Повторить запрос на соединение?";
/* No comment provided by engineer. */
"Repeat join request?" = "Повторить запрос на вступление?";
/* chat item action */ /* chat item action */
"Reply" = "Ответить"; "Reply" = "Ответить";
@@ -2942,9 +2702,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Scan QR code" = "Сканировать QR код"; "Scan QR code" = "Сканировать QR код";
/* No comment provided by engineer. */
"Scan QR code from desktop" = "Сканировать QR код с компьютера";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Scan security code from your contact's app." = "Сканируйте код безопасности из приложения контакта."; "Scan security code from your contact's app." = "Сканируйте код безопасности из приложения контакта.";
@@ -2999,15 +2756,9 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Send delivery receipts to" = "Отправка отчётов о доставке"; "Send delivery receipts to" = "Отправка отчётов о доставке";
/* No comment provided by engineer. */
"send direct message" = "отправьте сообщение";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Send direct message" = "Отправить сообщение"; "Send direct message" = "Отправить сообщение";
/* No comment provided by engineer. */
"Send direct message to connect" = "Отправьте сообщение чтобы соединиться";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Send disappearing message" = "Отправить исчезающее сообщение"; "Send disappearing message" = "Отправить исчезающее сообщение";
@@ -3089,9 +2840,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Servers" = "Серверы"; "Servers" = "Серверы";
/* No comment provided by engineer. */
"Session code" = "Код сессии";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Set 1 day" = "Установить 1 день"; "Set 1 day" = "Установить 1 день";
@@ -3278,9 +3026,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Tap to activate profile." = "Нажмите, чтобы сделать профиль активным."; "Tap to activate profile." = "Нажмите, чтобы сделать профиль активным.";
/* No comment provided by engineer. */
"Tap to Connect" = "Нажмите чтобы соединиться";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Tap to join" = "Нажмите, чтобы вступить"; "Tap to join" = "Нажмите, чтобы вступить";
@@ -3401,21 +3146,12 @@
/* notification title */ /* notification title */
"this contact" = "этот контакт"; "this contact" = "этот контакт";
/* No comment provided by engineer. */
"This device name" = "Имя этого устройства";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"This group has over %lld members, delivery receipts are not sent." = "В группе более %lld членов, отчёты о доставке выключены."; "This group has over %lld members, delivery receipts are not sent." = "В группе более %lld членов, отчёты о доставке выключены.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"This group no longer exists." = "Эта группа больше не существует."; "This group no longer exists." = "Эта группа больше не существует.";
/* No comment provided by engineer. */
"This is your own one-time link!" = "Это ваша собственная одноразовая ссылка!";
/* No comment provided by engineer. */
"This is your own SimpleX address!" = "Это ваш собственный адрес SimpleX!";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"This setting applies to messages in your current chat profile **%@**." = "Эта настройка применяется к сообщениям в Вашем текущем профиле чата **%@**."; "This setting applies to messages in your current chat profile **%@**." = "Эта настройка применяется к сообщениям в Вашем текущем профиле чата **%@**.";
@@ -3425,9 +3161,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"To connect, your contact can scan QR code or use the link in the app." = "Чтобы соединиться с Вами, Ваш контакт может отсканировать QR-код или использовать ссылку в приложении."; "To connect, your contact can scan QR code or use the link in the app." = "Чтобы соединиться с Вами, Ваш контакт может отсканировать QR-код или использовать ссылку в приложении.";
/* No comment provided by engineer. */
"To hide unwanted messages." = "Чтобы скрыть нежелательные сообщения.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"To make a new connection" = "Чтобы соединиться"; "To make a new connection" = "Чтобы соединиться";
@@ -3476,15 +3209,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Unable to record voice message" = "Невозможно записать голосовое сообщение"; "Unable to record voice message" = "Невозможно записать голосовое сообщение";
/* No comment provided by engineer. */
"Unblock" = "Разблокировать";
/* No comment provided by engineer. */
"Unblock member" = "Разблокировать члена группы";
/* No comment provided by engineer. */
"Unblock member?" = "Разблокировать члена группы?";
/* item status description */ /* item status description */
"Unexpected error: %@" = "Неожиданная ошибка: %@"; "Unexpected error: %@" = "Неожиданная ошибка: %@";
@@ -3524,12 +3248,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "Возможно, Ваш контакт удалил ссылку, или она уже была использована. Если это не так, то это может быть ошибкой - пожалуйста, сообщите нам об этом.\nЧтобы установить соединение, попросите Ваш контакт создать еще одну ссылку и проверьте Ваше соединение с сетью."; "Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "Возможно, Ваш контакт удалил ссылку, или она уже была использована. Если это не так, то это может быть ошибкой - пожалуйста, сообщите нам об этом.\nЧтобы установить соединение, попросите Ваш контакт создать еще одну ссылку и проверьте Ваше соединение с сетью.";
/* No comment provided by engineer. */
"Unlink" = "Забыть";
/* No comment provided by engineer. */
"Unlink desktop?" = "Забыть компьютер?";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Unlock" = "Разблокировать"; "Unlock" = "Разблокировать";
@@ -3584,9 +3302,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Use for new connections" = "Использовать для новых соединений"; "Use for new connections" = "Использовать для новых соединений";
/* No comment provided by engineer. */
"Use from desktop" = "Использовать с компьютера";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Use iOS call interface" = "Использовать интерфейс iOS для звонков"; "Use iOS call interface" = "Использовать интерфейс iOS для звонков";
@@ -3608,24 +3323,12 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Using SimpleX Chat servers." = "Используются серверы, предоставленные SimpleX Chat."; "Using SimpleX Chat servers." = "Используются серверы, предоставленные SimpleX Chat.";
/* No comment provided by engineer. */
"v%@" = "v%@";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"v%@ (%@)" = "v%@ (%@)"; "v%@ (%@)" = "v%@ (%@)";
/* No comment provided by engineer. */
"Verify code with desktop" = "Сверьте код с компьютером";
/* No comment provided by engineer. */
"Verify connection" = "Проверить соединение";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Verify connection security" = "Проверить безопасность соединения"; "Verify connection security" = "Проверить безопасность соединения";
/* No comment provided by engineer. */
"Verify connections" = "Проверять соединения";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Verify security code" = "Подтвердить код безопасности"; "Verify security code" = "Подтвердить код безопасности";
@@ -3644,9 +3347,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"via relay" = "через relay сервер"; "via relay" = "через relay сервер";
/* No comment provided by engineer. */
"Via secure quantum resistant protocol." = "Через безопасный квантово-устойчивый протокол.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Video call" = "Видеозвонок"; "Video call" = "Видеозвонок";
@@ -3686,9 +3386,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"waiting for confirmation…" = "ожидается подтверждение…"; "waiting for confirmation…" = "ожидается подтверждение…";
/* No comment provided by engineer. */
"Waiting for desktop..." = "Ожидается подключение компьютера...";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Waiting for file" = "Ожидается прием файла"; "Waiting for file" = "Ожидается прием файла";
@@ -3758,27 +3455,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"You are already connected to %@." = "Вы уже соединены с контактом %@."; "You are already connected to %@." = "Вы уже соединены с контактом %@.";
/* No comment provided by engineer. */
"You are already connecting to %@." = "Вы уже соединяетесь с %@.";
/* No comment provided by engineer. */
"You are already connecting via this one-time link!" = "Вы уже соединяетесь по этой одноразовой ссылке!";
/* No comment provided by engineer. */
"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. */
"You are already joining the group via this link." = "Вы уже вступаете в группу по этой ссылке.";
/* No comment provided by engineer. */
"You are already joining the group!\nRepeat join request?" = "Вы уже вступаете в группу!\nПовторить запрос на вступление?";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"You are connected to the server used to receive messages from this contact." = "Установлено соединение с сервером, через который Вы получаете сообщения от этого контакта."; "You are connected to the server used to receive messages from this contact." = "Установлено соединение с сервером, через который Вы получаете сообщения от этого контакта.";
@@ -3854,12 +3530,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"You could not be verified; please try again." = "Верификация не удалась; пожалуйста, попробуйте ещё раз."; "You could not be verified; please try again." = "Верификация не удалась; пожалуйста, попробуйте ещё раз.";
/* No comment provided by engineer. */
"You have already requested connection via this address!" = "Вы уже запросили соединение через этот адрес!";
/* No comment provided by engineer. */
"You have already requested connection!\nRepeat connection request?" = "Вы уже запросили соединение!\nПовторить запрос?";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"You have no chats" = "У Вас нет чатов"; "You have no chats" = "У Вас нет чатов";
@@ -3902,9 +3572,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"You will be connected to group when the group host's device is online, please wait or check later!" = "Соединение с группой будет установлено, когда хост группы будет онлайн. Пожалуйста, подождите или проверьте позже!"; "You will be connected to group when the group host's device is online, please wait or check later!" = "Соединение с группой будет установлено, когда хост группы будет онлайн. Пожалуйста, подождите или проверьте позже!";
/* No comment provided by engineer. */
"You will be connected when group link host's device is online, please wait or check later!" = "Соединение будет установлено, когда владелец ссылки группы будет онлайн. Пожалуйста, подождите или проверьте позже!";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"You will be connected when your connection request is accepted, please wait or check later!" = "Соединение будет установлено, когда Ваш запрос будет принят. Пожалуйста, подождите или проверьте позже!"; "You will be connected when your connection request is accepted, please wait or check later!" = "Соединение будет установлено, когда Ваш запрос будет принят. Пожалуйста, подождите или проверьте позже!";
@@ -3914,9 +3581,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"You will be required to authenticate when you start or resume the app after 30 seconds in background." = "Вы будете аутентифицированы при запуске и возобновлении приложения, которое было 30 секунд в фоновом режиме."; "You will be required to authenticate when you start or resume the app after 30 seconds in background." = "Вы будете аутентифицированы при запуске и возобновлении приложения, которое было 30 секунд в фоновом режиме.";
/* No comment provided by engineer. */
"You will connect to all group members." = "Вы соединитесь со всеми членами группы.";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"You will still receive calls and notifications from muted profiles when they are active." = "Вы все равно получите звонки и уведомления в профилях без звука, когда они активные."; "You will still receive calls and notifications from muted profiles when they are active." = "Вы все равно получите звонки и уведомления в профилях без звука, когда они активные.";
@@ -3980,9 +3644,6 @@
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Your privacy" = "Конфиденциальность"; "Your privacy" = "Конфиденциальность";
/* No comment provided by engineer. */
"Your profile" = "Ваш профиль";
/* No comment provided by engineer. */ /* No comment provided by engineer. */
"Your profile **%@** will be shared." = "Будет отправлен Ваш профиль **%@**."; "Your profile **%@** will be shared." = "Будет отправлен Ваш профиль **%@**.";
@@ -7,9 +7,6 @@
/* Privacy - Face ID Usage Description */ /* Privacy - Face ID Usage Description */
"NSFaceIDUsageDescription" = "SimpleX использует Face ID для аутентификации"; "NSFaceIDUsageDescription" = "SimpleX использует Face ID для аутентификации";
/* Privacy - Local Network Usage Description */
"NSLocalNetworkUsageDescription" = "SimpleX использует доступ к локальной сети, чтобы разрешить использование профиля чата через компьютер в той же сети.";
/* Privacy - Microphone Usage Description */ /* Privacy - Microphone Usage Description */
"NSMicrophoneUsageDescription" = "SimpleX использует микрофон для аудио и видео звонков, и для записи голосовых сообщений."; "NSMicrophoneUsageDescription" = "SimpleX использует микрофон для аудио и видео звонков, и для записи голосовых сообщений.";
+1 -1
View File
@@ -12,7 +12,7 @@ android {
defaultConfig { defaultConfig {
applicationId = "chat.simplex.app" applicationId = "chat.simplex.app"
minSdkVersion(28) minSdkVersion(26)
targetSdkVersion(33) targetSdkVersion(33)
// !!! // !!!
// skip version code after release to F-Droid, as it uses two version codes // skip version code after release to F-Droid, as it uses two version codes
@@ -39,7 +39,6 @@
android:exported="true" android:exported="true"
android:label="${app_name}" android:label="${app_name}"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize"
android:configChanges="uiMode"
android:theme="@style/Theme.SimpleX"> android:theme="@style/Theme.SimpleX">
<intent-filter> <intent-filter>
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
@@ -5,7 +5,6 @@ import android.net.Uri
import android.os.* import android.os.*
import android.view.WindowManager import android.view.WindowManager
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatDelegate
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import chat.simplex.app.model.NtfManager import chat.simplex.app.model.NtfManager
import chat.simplex.app.model.NtfManager.getUserIdFromIntent import chat.simplex.app.model.NtfManager.getUserIdFromIntent
@@ -23,7 +22,6 @@ import java.lang.ref.WeakReference
class MainActivity: FragmentActivity() { class MainActivity: FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
platform.androidSetNightModeIfSupported()
applyAppLocale(ChatModel.controller.appPrefs.appLanguage) applyAppLocale(ChatModel.controller.appPrefs.appLanguage)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
// testJson() // testJson()
@@ -43,7 +41,9 @@ class MainActivity: FragmentActivity() {
) )
} }
setContent { setContent {
AppScreen() SimpleXTheme {
AppScreen()
}
} }
SimplexApp.context.schedulePeriodicServiceRestartWorker() SimplexApp.context.schedulePeriodicServiceRestartWorker()
SimplexApp.context.schedulePeriodicWakeUp() SimplexApp.context.schedulePeriodicWakeUp()
@@ -126,9 +126,7 @@ fun processIntent(intent: Intent?) {
when (intent?.action) { when (intent?.action) {
"android.intent.action.VIEW" -> { "android.intent.action.VIEW" -> {
val uri = intent.data val uri = intent.data
if (uri != null) { if (uri != null) connectIfOpenedViaUri(chatModel.remoteHostId, uri.toURI(), ChatModel)
chatModel.appOpenUrl.value = null to uri.toURI()
}
} }
} }
} }
@@ -1,8 +1,7 @@
package chat.simplex.app package chat.simplex.app
import android.app.Application import android.app.Application
import android.app.UiModeManager import chat.simplex.common.platform.Log
import android.os.*
import androidx.lifecycle.* import androidx.lifecycle.*
import androidx.work.* import androidx.work.*
import chat.simplex.app.model.NtfManager import chat.simplex.app.model.NtfManager
@@ -11,12 +10,10 @@ import chat.simplex.common.helpers.requiresIgnoringBattery
import chat.simplex.common.model.* import chat.simplex.common.model.*
import chat.simplex.common.model.ChatController.appPrefs import chat.simplex.common.model.ChatController.appPrefs
import chat.simplex.common.model.ChatModel.updatingChatsMutex import chat.simplex.common.model.ChatModel.updatingChatsMutex
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.CurrentColors
import chat.simplex.common.ui.theme.DefaultTheme
import chat.simplex.common.views.call.RcvCallInvitation
import chat.simplex.common.views.helpers.* import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.onboarding.OnboardingStage import chat.simplex.common.views.onboarding.OnboardingStage
import chat.simplex.common.platform.*
import chat.simplex.common.views.call.RcvCallInvitation
import com.jakewharton.processphoenix.ProcessPhoenix import com.jakewharton.processphoenix.ProcessPhoenix
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
@@ -35,24 +32,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
if (ProcessPhoenix.isPhoenixProcess(this)) { if (ProcessPhoenix.isPhoenixProcess(this)) {
return return;
} else {
registerGlobalErrorHandler()
Handler(Looper.getMainLooper()).post {
while (true) {
try {
Looper.loop()
} catch (e: Throwable) {
if (e.message != null && e.message!!.startsWith("Unable to start activity")) {
android.os.Process.killProcess(android.os.Process.myPid())
break
} else {
// Send it to our exception handled because it will not get the exception otherwise
Thread.getDefaultUncaughtExceptionHandler()?.uncaughtException(Looper.getMainLooper().thread, e)
}
}
}
}
} }
context = this context = this
initHaskell() initHaskell()
@@ -77,7 +57,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
updatingChatsMutex.withLock { updatingChatsMutex.withLock {
kotlin.runCatching { kotlin.runCatching {
val currentUserId = chatModel.currentUser.value?.userId val currentUserId = chatModel.currentUser.value?.userId
val chats = ArrayList(chatController.apiGetChats(chatModel.remoteHostId())) val chats = ArrayList(chatController.apiGetChats(chatModel.remoteHostId))
/** Active user can be changed in background while [ChatController.apiGetChats] is executing */ /** Active user can be changed in background while [ChatController.apiGetChats] is executing */
if (chatModel.currentUser.value?.userId == currentUserId) { if (chatModel.currentUser.value?.userId == currentUserId) {
val currentChatId = chatModel.chatId.value val currentChatId = chatModel.chatId.value
@@ -95,7 +75,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
} }
Lifecycle.Event.ON_RESUME -> { Lifecycle.Event.ON_RESUME -> {
isAppOnForeground = true isAppOnForeground = true
if (chatModel.controller.appPrefs.onboardingStage.get() == OnboardingStage.OnboardingComplete && chatModel.currentUser.value != null) { if (chatModel.controller.appPrefs.onboardingStage.get() == OnboardingStage.OnboardingComplete) {
SimplexService.showBackgroundServiceNoticeIfNeeded() SimplexService.showBackgroundServiceNoticeIfNeeded()
} }
/** /**
@@ -164,14 +144,13 @@ class SimplexApp: Application(), LifecycleEventObserver {
androidAppContext = this androidAppContext = this
APPLICATION_ID = BuildConfig.APPLICATION_ID APPLICATION_ID = BuildConfig.APPLICATION_ID
ntfManager = object : chat.simplex.common.platform.NtfManager() { ntfManager = object : chat.simplex.common.platform.NtfManager() {
override fun notifyCallInvitation(invitation: RcvCallInvitation): Boolean = NtfManager.notifyCallInvitation(invitation) override fun notifyCallInvitation(invitation: RcvCallInvitation) = NtfManager.notifyCallInvitation(invitation)
override fun hasNotificationsForChat(chatId: String): Boolean = NtfManager.hasNotificationsForChat(chatId) override fun hasNotificationsForChat(chatId: String): Boolean = NtfManager.hasNotificationsForChat(chatId)
override fun cancelNotificationsForChat(chatId: String) = NtfManager.cancelNotificationsForChat(chatId) override fun cancelNotificationsForChat(chatId: String) = NtfManager.cancelNotificationsForChat(chatId)
override fun displayNotification(user: UserLike, chatId: String, displayName: String, msgText: String, image: String?, actions: List<Pair<NotificationAction, () -> Unit>>) = NtfManager.displayNotification(user, chatId, displayName, msgText, image, actions.map { it.first }) override fun displayNotification(user: UserLike, chatId: String, displayName: String, msgText: String, image: String?, actions: List<Pair<NotificationAction, () -> Unit>>) = NtfManager.displayNotification(user, chatId, displayName, msgText, image, actions.map { it.first })
override fun androidCreateNtfChannelsMaybeShowAlert() = NtfManager.createNtfChannelsMaybeShowAlert() override fun androidCreateNtfChannelsMaybeShowAlert() = NtfManager.createNtfChannelsMaybeShowAlert()
override fun cancelCallNotification() = NtfManager.cancelCallNotification() override fun cancelCallNotification() = NtfManager.cancelCallNotification()
override fun cancelAllNotifications() = NtfManager.cancelAllNotifications() override fun cancelAllNotifications() = NtfManager.cancelAllNotifications()
override fun showMessage(title: String, text: String) = NtfManager.showMessage(title, text)
} }
platform = object : PlatformInterface { platform = object : PlatformInterface {
override suspend fun androidServiceStart() { override suspend fun androidServiceStart() {
@@ -227,23 +206,6 @@ class SimplexApp: Application(), LifecycleEventObserver {
override fun androidIsBackgroundCallAllowed(): Boolean = !SimplexService.isBackgroundRestricted() override fun androidIsBackgroundCallAllowed(): Boolean = !SimplexService.isBackgroundRestricted()
override fun androidSetNightModeIfSupported() {
if (Build.VERSION.SDK_INT < 31) return
val light = if (CurrentColors.value.name == DefaultTheme.SYSTEM.name) {
null
} else {
CurrentColors.value.colors.isLight
}
val mode = when (light) {
null -> UiModeManager.MODE_NIGHT_AUTO
true -> UiModeManager.MODE_NIGHT_NO
false -> UiModeManager.MODE_NIGHT_YES
}
val uiModeManager = androidAppContext.getSystemService(UI_MODE_SERVICE) as UiModeManager
uiModeManager.setApplicationNightMode(mode)
}
override suspend fun androidAskToAllowBackgroundCalls(): Boolean { override suspend fun androidAskToAllowBackgroundCalls(): Boolean {
if (SimplexService.isBackgroundRestricted()) { if (SimplexService.isBackgroundRestricted()) {
val userChoice: CompletableDeferred<Boolean> = CompletableDeferred() val userChoice: CompletableDeferred<Boolean> = CompletableDeferred()
@@ -30,7 +30,7 @@ object NtfManager {
const val ShowChatsAction: String = "chat.simplex.app.SHOW_CHATS" const val ShowChatsAction: String = "chat.simplex.app.SHOW_CHATS"
// DO NOT change notification channel settings / names // DO NOT change notification channel settings / names
const val CallChannel: String = "chat.simplex.app.CALL_NOTIFICATION_2" const val CallChannel: String = "chat.simplex.app.CALL_NOTIFICATION_1"
const val AcceptCallAction: String = "chat.simplex.app.ACCEPT_CALL" const val AcceptCallAction: String = "chat.simplex.app.ACCEPT_CALL"
const val RejectCallAction: String = "chat.simplex.app.REJECT_CALL" const val RejectCallAction: String = "chat.simplex.app.REJECT_CALL"
const val CallNotificationId: Int = -1 const val CallNotificationId: Int = -1
@@ -59,7 +59,7 @@ object NtfManager {
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
.build() .build()
val soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.packageName + "/raw/ring_once") val soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.packageName + "/" + R.raw.ring_once)
Log.d(TAG, "callNotificationChannel sound: $soundUri") Log.d(TAG, "callNotificationChannel sound: $soundUri")
callChannel.setSound(soundUri, attrs) callChannel.setSound(soundUri, attrs)
callChannel.enableVibration(true) callChannel.enableVibration(true)
@@ -140,7 +140,7 @@ object NtfManager {
} }
} }
fun notifyCallInvitation(invitation: RcvCallInvitation): Boolean { fun notifyCallInvitation(invitation: RcvCallInvitation) {
val keyguardManager = getKeyguardManager(context) val keyguardManager = getKeyguardManager(context)
Log.d( Log.d(
TAG, TAG,
@@ -149,7 +149,7 @@ object NtfManager {
"callOnLockScreen ${appPreferences.callOnLockScreen.get()}, " + "callOnLockScreen ${appPreferences.callOnLockScreen.get()}, " +
"onForeground ${isAppOnForeground}" "onForeground ${isAppOnForeground}"
) )
if (isAppOnForeground) return false if (isAppOnForeground) return
val contactId = invitation.contact.id val contactId = invitation.contact.id
Log.d(TAG, "notifyCallInvitation $contactId") Log.d(TAG, "notifyCallInvitation $contactId")
val image = invitation.contact.image val image = invitation.contact.image
@@ -163,7 +163,7 @@ object NtfManager {
.setFullScreenIntent(fullScreenPendingIntent, true) .setFullScreenIntent(fullScreenPendingIntent, true)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
} else { } else {
val soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.packageName + "/raw/ring_once") val soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.packageName + "/" + R.raw.ring_once)
val fullScreenPendingIntent = PendingIntent.getActivity(context, 0, Intent(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) val fullScreenPendingIntent = PendingIntent.getActivity(context, 0, Intent(), PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
NotificationCompat.Builder(context, CallChannel) NotificationCompat.Builder(context, CallChannel)
.setContentIntent(chatPendingIntent(OpenChatAction, invitation.user.userId, invitation.contact.id)) .setContentIntent(chatPendingIntent(OpenChatAction, invitation.user.userId, invitation.contact.id))
@@ -206,39 +206,6 @@ object NtfManager {
notify(CallNotificationId, notification) notify(CallNotificationId, notification)
} }
} }
return true
}
fun showMessage(title: String, text: String) {
val builder = NotificationCompat.Builder(context, MessageChannel)
.setContentTitle(title)
.setContentText(text)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setGroup(MessageGroup)
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
.setSmallIcon(R.drawable.ntf_icon)
.setLargeIcon(null)
.setColor(0x88FFFF)
.setAutoCancel(true)
.setVibrate(null)
.setContentIntent(chatPendingIntent(ShowChatsAction, null, null))
.setSilent(false)
val summary = NotificationCompat.Builder(context, MessageChannel)
.setSmallIcon(R.drawable.ntf_icon)
.setColor(0x88FFFF)
.setGroup(MessageGroup)
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
.setGroupSummary(true)
.setContentIntent(chatPendingIntent(ShowChatsAction, null))
.build()
with(NotificationManagerCompat.from(context)) {
if (ActivityCompat.checkSelfPermission(SimplexApp.context, android.Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
notify("MESSAGE".hashCode(), builder.build())
notify(0, summary)
}
}
} }
fun cancelCallNotification() { fun cancelCallNotification() {
@@ -281,7 +248,6 @@ object NtfManager {
manager.createNotificationChannel(callNotificationChannel(CallChannel, generalGetString(MR.strings.ntf_channel_calls))) manager.createNotificationChannel(callNotificationChannel(CallChannel, generalGetString(MR.strings.ntf_channel_calls)))
// Remove old channels since they can't be edited // Remove old channels since they can't be edited
manager.deleteNotificationChannel("chat.simplex.app.CALL_NOTIFICATION") manager.deleteNotificationChannel("chat.simplex.app.CALL_NOTIFICATION")
manager.deleteNotificationChannel("chat.simplex.app.CALL_NOTIFICATION_1")
manager.deleteNotificationChannel("chat.simplex.app.LOCK_SCREEN_CALL_NOTIFICATION") manager.deleteNotificationChannel("chat.simplex.app.LOCK_SCREEN_CALL_NOTIFICATION")
} }
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.SimpleX" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:statusBarColor">@color/black</item>
<item name="android:windowBackground">@color/window_background_dark</item>
</style>
</resources>
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="Theme.SimpleX" parent="Theme.AppCompat.DayNight.NoActionBar"> <style name="Theme.SimpleX" parent="android:Theme.Material.Light.NoActionBar">
<item name="android:statusBarColor">@color/black</item> <item name="android:statusBarColor">@color/black</item>
</style> </style>
</resources> </resources>
+5 -61
View File
@@ -110,7 +110,7 @@ android {
compileSdkVersion(34) compileSdkVersion(34)
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig { defaultConfig {
minSdkVersion(28) minSdkVersion(26)
targetSdkVersion(33) targetSdkVersion(33)
} }
compileOptions { compileOptions {
@@ -155,34 +155,6 @@ afterEvaluate {
val endTagRegex = Regex("</") val endTagRegex = Regex("</")
val anyHtmlRegex = Regex("[^>]*>.*(<|>).*</string>|[^>]*>.*(&lt;|&gt;).*</string>") val anyHtmlRegex = Regex("[^>]*>.*(<|>).*</string>|[^>]*>.*(&lt;|&gt;).*</string>")
val correctHtmlRegex = Regex("[^>]*>.*<b>.*</b>.*</string>|[^>]*>.*<i>.*</i>.*</string>|[^>]*>.*<u>.*</u>.*</string>|[^>]*>.*<font[^>]*>.*</font>.*</string>") val correctHtmlRegex = Regex("[^>]*>.*<b>.*</b>.*</string>|[^>]*>.*<i>.*</i>.*</string>|[^>]*>.*<u>.*</u>.*</string>|[^>]*>.*<font[^>]*>.*</font>.*</string>")
val possibleFormat = listOf("s", "d", "1\$s", "1\$d", "2s", "f")
fun String.id(): String = replace("<string name=\"", "").trim().substringBefore("\"")
fun String.formatting(filepath: String): List<String> {
if (!contains("%")) return emptyList()
val value = substringAfter("\">").substringBeforeLast("</string>")
val formats = ArrayList<String>()
var substring = value.substringAfter("%")
while (true) {
var foundFormat = false
for (format in possibleFormat) {
if (substring.startsWith(format)) {
formats.add(format)
foundFormat = true
break
}
}
if (!foundFormat) {
throw Exception("Unknown formatting in string. Add it to 'possibleFormat' in common/build.gradle.kts if needed: $this \nin $filepath")
}
val was = substring
substring = substring.substringAfter("%")
if (was.length == substring.length) break
}
return formats
}
fun String.removeCDATA(): String = fun String.removeCDATA(): String =
if (contains("<![CDATA")) { if (contains("<![CDATA")) {
@@ -223,44 +195,20 @@ afterEvaluate {
return this return this
} }
val fileRegex = Regex("MR/../strings.xml$|MR/..-.../strings.xml$|MR/..-../strings.xml$|MR/base/strings.xml$") val fileRegex = Regex("MR/../strings.xml$|MR/..-.../strings.xml$|MR/..-../strings.xml$|MR/base/strings.xml$")
val tree = kotlin.sourceSets["commonMain"].resources.filter { fileRegex.containsMatchIn(it.absolutePath.replace("\\", "/")) }.asFileTree kotlin.sourceSets["commonMain"].resources.filter { fileRegex.containsMatchIn(it.absolutePath) }.asFileTree.forEach { file ->
val baseStringsFile = tree.firstOrNull { it.absolutePath.replace("\\", "/").endsWith("base/strings.xml") } ?: throw Exception("No base/strings.xml found")
val treeList = ArrayList(tree.toList())
treeList.remove(baseStringsFile)
treeList.add(0, baseStringsFile)
val baseFormatting = mutableMapOf<String, List<String>>()
treeList.forEachIndexed { index, file ->
val isBase = index == 0
val initialLines = ArrayList<String>() val initialLines = ArrayList<String>()
val finalLines = ArrayList<String>() val finalLines = ArrayList<String>()
val errors = ArrayList<String>()
file.useLines { lines -> file.useLines { lines ->
val multiline = ArrayList<String>() val multiline = ArrayList<String>()
lines.forEach { line -> lines.forEach { line ->
initialLines.add(line) initialLines.add(line)
if (stringRegex.matches(line)) { if (stringRegex.matches(line)) {
val fixedLine = line.removeCDATA().addCDATA(file.absolutePath) finalLines.add(line.removeCDATA().addCDATA(file.absolutePath))
val lineId = fixedLine.id()
if (isBase) {
baseFormatting[lineId] = fixedLine.formatting(file.absolutePath)
} else if (baseFormatting[lineId] != fixedLine.formatting(file.absolutePath)) {
errors.add("Incorrect formatting in string: $fixedLine \nin ${file.absolutePath}")
}
finalLines.add(fixedLine)
} else if (multiline.isEmpty() && startStringRegex.containsMatchIn(line)) { } else if (multiline.isEmpty() && startStringRegex.containsMatchIn(line)) {
multiline.add(line) multiline.add(line)
} else if (multiline.isNotEmpty() && endStringRegex.containsMatchIn(line)) { } else if (multiline.isNotEmpty() && endStringRegex.containsMatchIn(line)) {
multiline.add(line) multiline.add(line)
val fixedLines = multiline.joinToString("\n").removeCDATA().addCDATA(file.absolutePath).split("\n") finalLines.addAll(multiline.joinToString("\n").removeCDATA().addCDATA(file.absolutePath).split("\n"))
val fixedLinesJoined = fixedLines.joinToString("")
val lineId = fixedLinesJoined.id()
if (isBase) {
baseFormatting[lineId] = fixedLinesJoined.formatting(file.absolutePath)
} else if (baseFormatting[lineId] != fixedLinesJoined.formatting(file.absolutePath)) {
errors.add("Incorrect formatting in string: $fixedLinesJoined \nin ${file.absolutePath}")
}
finalLines.addAll(fixedLines)
multiline.clear() multiline.clear()
} else if (multiline.isNotEmpty()) { } else if (multiline.isNotEmpty()) {
multiline.add(line) multiline.add(line)
@@ -269,14 +217,10 @@ afterEvaluate {
} }
} }
if (multiline.isNotEmpty()) { if (multiline.isNotEmpty()) {
errors.add("Unclosed string tag: ${multiline.joinToString("\n")} \nin ${file.absolutePath}") throw Exception("Unclosed string tag: ${multiline.joinToString("\n")} \nin ${file.absolutePath}")
} }
} }
if (errors.isNotEmpty()) {
throw Exception("Found errors: \n\n${errors.joinToString("\n\n")}")
}
if (!debug && finalLines != initialLines) { if (!debug && finalLines != initialLines) {
file.writer().use { file.writer().use {
finalLines.forEachIndexed { index, line -> finalLines.forEachIndexed { index, line ->
@@ -68,5 +68,12 @@ fun initHaskell() {
s.acquire() s.acquire()
pipeStdOutToSocket(socketName) pipeStdOutToSocket(socketName)
initHS() var inited = false
thread(name = "initHs", isDaemon = true) {
initHS()
inited = true
}
while (!inited) {
Thread.sleep(2)
}
} }
@@ -4,7 +4,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.painter.Painter
import com.google.accompanist.insets.navigationBarsWithImePadding import com.google.accompanist.insets.navigationBarsWithImePadding
import java.io.File
actual fun Modifier.navigationBarsWithImePadding(): Modifier = navigationBarsWithImePadding() actual fun Modifier.navigationBarsWithImePadding(): Modifier = navigationBarsWithImePadding()
@@ -20,7 +19,7 @@ actual fun ProvideWindowInsets(
@Composable @Composable
actual fun Modifier.desktopOnExternalDrag( actual fun Modifier.desktopOnExternalDrag(
enabled: Boolean, enabled: Boolean,
onFiles: (List<File>) -> Unit, onFiles: (List<String>) -> Unit,
onImage: (Painter) -> Unit, onImage: (Painter) -> Unit,
onText: (String) -> Unit onText: (String) -> Unit
): Modifier = this ): Modifier = this
@@ -27,6 +27,7 @@ import androidx.core.view.inputmethod.EditorInfoCompat
import androidx.core.view.inputmethod.InputConnectionCompat import androidx.core.view.inputmethod.InputConnectionCompat
import androidx.core.widget.doAfterTextChanged import androidx.core.widget.doAfterTextChanged
import androidx.core.widget.doOnTextChanged import androidx.core.widget.doOnTextChanged
import chat.simplex.common.*
import chat.simplex.common.R import chat.simplex.common.R
import chat.simplex.common.helpers.toURI import chat.simplex.common.helpers.toURI
import chat.simplex.common.model.ChatModel import chat.simplex.common.model.ChatModel
@@ -44,7 +45,6 @@ import java.net.URI
actual fun PlatformTextField( actual fun PlatformTextField(
composeState: MutableState<ComposeState>, composeState: MutableState<ComposeState>,
sendMsgEnabled: Boolean, sendMsgEnabled: Boolean,
sendMsgButtonDisabled: Boolean,
textStyle: MutableState<TextStyle>, textStyle: MutableState<TextStyle>,
showDeleteTextButton: MutableState<Boolean>, showDeleteTextButton: MutableState<Boolean>,
userIsObserver: Boolean, userIsObserver: Boolean,
@@ -8,12 +8,15 @@ import android.provider.MediaStore
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import androidx.compose.ui.platform.ClipboardManager import androidx.compose.ui.platform.ClipboardManager
import androidx.compose.ui.platform.UriHandler import androidx.compose.ui.platform.UriHandler
import androidx.core.content.FileProvider
import androidx.core.net.toUri
import chat.simplex.common.helpers.* import chat.simplex.common.helpers.*
import chat.simplex.common.model.* import chat.simplex.common.model.*
import chat.simplex.common.views.helpers.* import chat.simplex.common.views.helpers.*
import java.io.BufferedOutputStream import java.io.BufferedOutputStream
import java.io.File import java.io.File
import chat.simplex.res.MR import chat.simplex.res.MR
import java.io.ByteArrayOutputStream
actual fun ClipboardManager.shareText(text: String) { actual fun ClipboardManager.shareText(text: String) {
val sendIntent: Intent = Intent().apply { val sendIntent: Intent = Intent().apply {
@@ -4,17 +4,14 @@ import android.app.Activity
import android.content.Context import android.content.Context
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.graphics.Rect import android.graphics.Rect
import android.os.* import android.os.Build
import android.view.* import android.view.*
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.setContent
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import chat.simplex.common.AppScreen import chat.simplex.common.views.helpers.KeyboardState
import chat.simplex.common.views.helpers.*
import androidx.compose.ui.platform.LocalContext as LocalContext1 import androidx.compose.ui.platform.LocalContext as LocalContext1
import chat.simplex.res.MR
actual fun showToast(text: String, timeout: Long) = Toast.makeText(androidAppContext, text, Toast.LENGTH_SHORT).show() actual fun showToast(text: String, timeout: Long) = Toast.makeText(androidAppContext, text, Toast.LENGTH_SHORT).show()
@@ -74,44 +71,3 @@ actual fun hideKeyboard(view: Any?) {
} }
actual fun androidIsFinishingMainActivity(): Boolean = (mainActivity.get()?.isFinishing == true) actual fun androidIsFinishingMainActivity(): Boolean = (mainActivity.get()?.isFinishing == true)
actual class GlobalExceptionsHandler: Thread.UncaughtExceptionHandler {
actual override fun uncaughtException(thread: Thread, e: Throwable) {
Log.e(TAG, "App crashed, thread name: " + thread.name + ", exception: " + e.stackTraceToString())
includeMoreFailedComposables()
if (ModalManager.start.hasModalsOpen()) {
ModalManager.start.closeModal()
} else if (chatModel.chatId.value != null) {
// Since no modals are open, the problem is probably in ChatView
chatModel.chatId.value = null
chatModel.chatItems.clear()
} else {
// ChatList, nothing to do. Maybe to show other view except ChatList
}
chatModel.activeCall.value?.let {
withBGApi {
chatModel.callManager.endCall(it)
}
}
if (thread.name == "main") {
mainActivity.get()?.recreate()
} else {
mainActivity.get()?.apply {
window
?.decorView
?.findViewById<ViewGroup>(android.R.id.content)
?.removeViewAt(0)
setContent {
AppScreen()
}
}
}
// Wait until activity recreates to prevent showing two alerts (in case `main` was crashed)
Handler(Looper.getMainLooper()).post {
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.app_was_crashed),
text = e.stackTraceToString()
)
}
}
}
@@ -71,7 +71,7 @@ actual class VideoPlayer actual constructor(
private fun start(seek: Long? = null, onProgressUpdate: (position: Long?, state: TrackState) -> Unit): Boolean { private fun start(seek: Long? = null, onProgressUpdate: (position: Long?, state: TrackState) -> Unit): Boolean {
val filepath = getAppFilePath(uri) val filepath = getAppFilePath(uri)
if (filepath == null || !File(filepath).exists()) { if (filepath == null || !File(filepath).exists()) {
Log.e(TAG, "No such file: $filepath") Log.e(TAG, "No such file: $uri")
brokenVideo.value = true brokenVideo.value = true
return false return false
} }
@@ -370,6 +370,7 @@ fun CallInfoView(call: Call, alignment: Alignment.Horizontal) {
InfoText(call.callState.text) InfoText(call.callState.text)
val connInfo = call.connectionInfo val connInfo = call.connectionInfo
// val connInfoText = if (connInfo == null) "" else " (${connInfo.text}, ${connInfo.protocolText})"
val connInfoText = if (connInfo == null) "" else " (${connInfo.text})" val connInfoText = if (connInfo == null) "" else " (${connInfo.text})"
InfoText(call.encryptionStatus + connInfoText) InfoText(call.encryptionStatus + connInfoText)
} }
@@ -584,8 +585,8 @@ fun PreviewActiveCallOverlayVideo() {
localMedia = CallMediaType.Video, localMedia = CallMediaType.Video,
peerMedia = CallMediaType.Video, peerMedia = CallMediaType.Video,
connectionInfo = ConnectionInfo( connectionInfo = ConnectionInfo(
RTCIceCandidate(RTCIceCandidateType.Host, "tcp"), RTCIceCandidate(RTCIceCandidateType.Host, "tcp", null),
RTCIceCandidate(RTCIceCandidateType.Host, "tcp") RTCIceCandidate(RTCIceCandidateType.Host, "tcp", null)
) )
), ),
speakerCanBeEnabled = true, speakerCanBeEnabled = true,
@@ -610,8 +611,8 @@ fun PreviewActiveCallOverlayAudio() {
localMedia = CallMediaType.Audio, localMedia = CallMediaType.Audio,
peerMedia = CallMediaType.Audio, peerMedia = CallMediaType.Audio,
connectionInfo = ConnectionInfo( connectionInfo = ConnectionInfo(
RTCIceCandidate(RTCIceCandidateType.Host, "udp"), RTCIceCandidate(RTCIceCandidateType.Host, "udp", null),
RTCIceCandidate(RTCIceCandidateType.Host, "udp") RTCIceCandidate(RTCIceCandidateType.Host, "udp", null)
) )
), ),
speakerCanBeEnabled = true, speakerCanBeEnabled = true,
@@ -1,7 +0,0 @@
package chat.simplex.common.views.database
import chat.simplex.common.views.usersettings.restartApp
actual fun restartChatOrApp() {
restartApp()
}

Some files were not shown because too many files have changed in this diff Show More