Compare commits
1 Commits
v5.4.4-arm
...
av/multipl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29857e33e2 |
15
.github/workflows/build.yml
vendored
15
.github/workflows/build.yml
vendored
@@ -9,7 +9,6 @@ on:
|
||||
tags:
|
||||
- "v*"
|
||||
- "!*-fdroid"
|
||||
- "!*-armv7a"
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
@@ -80,10 +79,10 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Haskell
|
||||
uses: haskell/actions/setup@v2
|
||||
uses: haskell-actions/setup@v2
|
||||
with:
|
||||
ghc-version: "8.10.7"
|
||||
cabal-version: "latest"
|
||||
ghc-version: "9.6.3"
|
||||
cabal-version: "3.10.1.0"
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v3
|
||||
@@ -189,7 +188,7 @@ jobs:
|
||||
APPLE_SIMPLEX_NOTARIZATION_APPLE_ID: ${{ secrets.APPLE_SIMPLEX_NOTARIZATION_APPLE_ID }}
|
||||
APPLE_SIMPLEX_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_SIMPLEX_NOTARIZATION_PASSWORD }}
|
||||
run: |
|
||||
scripts/build-desktop-mac.sh
|
||||
scripts/ci/build-desktop-mac.sh
|
||||
path=$(echo $PWD/apps/multiplatform/release/main/dmg/SimpleX-*.dmg)
|
||||
echo "package_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "package_hash=$(echo SHA2-512\(${{ matrix.desktop_asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
@@ -260,12 +259,10 @@ jobs:
|
||||
# Unix /
|
||||
|
||||
# / Windows
|
||||
|
||||
# * In powershell multiline commands do not fail if individual commands fail - https://github.community/t/multiline-commands-on-windows-do-not-fail-if-individual-commands-fail/16753
|
||||
# * And GitHub Actions does not support parameterizing shell in a matrix job - https://github.community/t/using-matrix-to-specify-shell-is-it-possible/17065
|
||||
# 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'
|
||||
if: matrix.os == 'windows-latest'
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'windows-latest'
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ucrt64
|
||||
|
||||
10
README.md
10
README.md
@@ -232,8 +232,6 @@ You can use SimpleX with your own servers and still communicate with people usin
|
||||
|
||||
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).
|
||||
|
||||
[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).
|
||||
- ✅ Desktop client.
|
||||
- ✅ Encryption of local files stored in the 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.
|
||||
- 🏗 Using mobile profiles from the desktop app.
|
||||
- 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.
|
||||
- Improve sending videos (including encryption of locally stored videos).
|
||||
- Improve experience for the new users.
|
||||
- SMP queue redundancy and rotation (manual is supported).
|
||||
- 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.).
|
||||
|
||||
@@ -42,7 +42,6 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
let m = ChatModel.shared
|
||||
let deviceToken = DeviceToken(pushProvider: PushProvider(env: pushEnvironment), token: token)
|
||||
m.deviceToken = deviceToken
|
||||
// savedToken is set in startChat, when it is started before this method is called
|
||||
if m.savedToken != nil {
|
||||
registerToken(token: deviceToken)
|
||||
}
|
||||
@@ -81,7 +80,7 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
}
|
||||
} else if let checkMessages = ntfData["checkMessages"] as? Bool, checkMessages {
|
||||
logger.debug("AppDelegate: didReceiveRemoteNotification: checkMessages")
|
||||
if m.ntfEnablePeriodic && allowBackgroundRefresh() && BGManager.shared.lastRanLongAgo {
|
||||
if appStateGroupDefault.get().inactive && m.ntfEnablePeriodic {
|
||||
receiveMessages(completionHandler)
|
||||
} else {
|
||||
completionHandler(.noData)
|
||||
|
||||
@@ -14,14 +14,11 @@ struct ContentView: View {
|
||||
@ObservedObject var alertManager = AlertManager.shared
|
||||
@ObservedObject var callController = CallController.shared
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
var contentAccessAuthenticationExtended: Bool
|
||||
|
||||
@Environment(\.scenePhase) var scenePhase
|
||||
@State private var automaticAuthenticationAttempted = false
|
||||
@State private var canConnectViewCall = false
|
||||
@State private var lastSuccessfulUnlock: TimeInterval? = nil
|
||||
|
||||
@Binding var doAuthenticate: Bool
|
||||
@Binding var userAuthorized: Bool?
|
||||
@Binding var canConnectCall: Bool
|
||||
@Binding var lastSuccessfulUnlock: TimeInterval?
|
||||
@Binding var showInitializationView: Bool
|
||||
@AppStorage(DEFAULT_SHOW_LA_NOTICE) private var prefShowLANotice = false
|
||||
@AppStorage(DEFAULT_LA_NOTICE_SHOWN) private var prefLANoticeShown = 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 {
|
||||
ZStack {
|
||||
// contentView() has to be in a single branch, so that enabling authentication doesn't trigger re-rendering and close settings.
|
||||
// i.e. with separate branches like this settings are closed: `if prefPerformLA { ... contentView() ... } else { contentView() }
|
||||
if !prefPerformLA || accessAuthenticated {
|
||||
contentView()
|
||||
} else {
|
||||
lockButton()
|
||||
}
|
||||
contentView()
|
||||
if chatModel.showCallView, let call = chatModel.activeCall {
|
||||
callView(call)
|
||||
}
|
||||
@@ -63,7 +50,6 @@ struct ContentView: View {
|
||||
LocalAuthView(authRequest: la)
|
||||
} else if showSetPasscode {
|
||||
SetAppPasscodeView {
|
||||
chatModel.contentViewAccessAuthenticated = true
|
||||
prefPerformLA = true
|
||||
showSetPasscode = false
|
||||
privacyLocalAuthModeDefault.set(.passcode)
|
||||
@@ -74,9 +60,13 @@ struct ContentView: View {
|
||||
alertManager.showAlert(laPasscodeNotSetAlert())
|
||||
}
|
||||
}
|
||||
if chatModel.chatDbStatus == nil {
|
||||
initializationView()
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
if prefPerformLA { requestNtfAuthorization() }
|
||||
initAuthenticate()
|
||||
}
|
||||
.onChange(of: doAuthenticate) { _ in
|
||||
initAuthenticate()
|
||||
}
|
||||
.alert(isPresented: $alertManager.presentAlert) { alertManager.alertView! }
|
||||
.sheet(isPresented: $showSettings) {
|
||||
@@ -86,44 +76,14 @@ struct ContentView: View {
|
||||
Button("System authentication") { initialEnableLA() }
|
||||
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 {
|
||||
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)
|
||||
} else if !chatModel.v3DBMigration.startChat {
|
||||
MigrateToAppGroupView()
|
||||
@@ -146,11 +106,11 @@ struct ContentView: View {
|
||||
if CallController.useCallKit() {
|
||||
ActiveCallView(call: call, canConnectCall: Binding.constant(true))
|
||||
.onDisappear {
|
||||
if prefPerformLA && !accessAuthenticated { authenticateContentViewAccess() }
|
||||
if userAuthorized == false && doAuthenticate { runAuthenticate() }
|
||||
}
|
||||
} else {
|
||||
ActiveCallView(call: call, canConnectCall: $canConnectViewCall)
|
||||
if prefPerformLA && !accessAuthenticated {
|
||||
ActiveCallView(call: call, canConnectCall: $canConnectCall)
|
||||
if prefPerformLA && userAuthorized != true {
|
||||
Rectangle()
|
||||
.fill(colorScheme == .dark ? .black : .white)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
@@ -160,27 +120,22 @@ struct ContentView: 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 {
|
||||
VStack {
|
||||
ProgressView().scaleEffect(2)
|
||||
Text("Opening app…")
|
||||
Text("Opening database…")
|
||||
.padding()
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity )
|
||||
.background(
|
||||
Rectangle()
|
||||
.fill(.background)
|
||||
)
|
||||
}
|
||||
|
||||
private func mainView() -> some View {
|
||||
ZStack(alignment: .top) {
|
||||
ChatListView(showSettings: $showSettings).privacySensitive(protectScreen)
|
||||
.onAppear {
|
||||
requestNtfAuthorization()
|
||||
if !prefPerformLA { requestNtfAuthorization() }
|
||||
// Local Authentication notice is to be shown on next start after onboarding is complete
|
||||
if (!prefLANoticeShown && prefShowLANotice && !chatModel.chats.isEmpty) {
|
||||
prefLANoticeShown = true
|
||||
@@ -232,37 +187,48 @@ struct ContentView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func unlockedRecently() -> Bool {
|
||||
if let lastSuccessfulUnlock = lastSuccessfulUnlock {
|
||||
return ProcessInfo.processInfo.systemUptime - lastSuccessfulUnlock < 2
|
||||
} else {
|
||||
return false
|
||||
private func initAuthenticate() {
|
||||
logger.debug("initAuthenticate")
|
||||
if CallController.useCallKit() && chatModel.showCallView && chatModel.activeCall != nil {
|
||||
userAuthorized = false
|
||||
} else if doAuthenticate {
|
||||
runAuthenticate()
|
||||
}
|
||||
}
|
||||
|
||||
private func authenticateContentViewAccess() {
|
||||
logger.debug("DEBUGGING: authenticateContentViewAccess")
|
||||
dismissAllSheets(animated: false) {
|
||||
logger.debug("DEBUGGING: authenticateContentViewAccess, in dismissAllSheets callback")
|
||||
chatModel.chatId = nil
|
||||
private func runAuthenticate() {
|
||||
logger.debug("DEBUGGING: runAuthenticate")
|
||||
if !prefPerformLA {
|
||||
userAuthorized = true
|
||||
} 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
|
||||
logger.debug("DEBUGGING: authenticate callback: \(String(describing: laResult))")
|
||||
switch (laResult) {
|
||||
case .success:
|
||||
chatModel.contentViewAccessAuthenticated = true
|
||||
canConnectViewCall = true
|
||||
lastSuccessfulUnlock = ProcessInfo.processInfo.systemUptime
|
||||
case .failed:
|
||||
chatModel.contentViewAccessAuthenticated = false
|
||||
if privacyLocalAuthModeDefault.get() == .passcode {
|
||||
AlertManager.shared.showAlert(laFailedAlert())
|
||||
}
|
||||
case .unavailable:
|
||||
prefPerformLA = false
|
||||
canConnectViewCall = true
|
||||
AlertManager.shared.showAlert(laUnavailableTurningOffAlert())
|
||||
private func justAuthenticate() {
|
||||
userAuthorized = false
|
||||
let laMode = privacyLocalAuthModeDefault.get()
|
||||
authenticate(reason: NSLocalizedString("Unlock app", comment: "authentication reason"), selfDestruct: true) { laResult in
|
||||
logger.debug("DEBUGGING: authenticate callback: \(String(describing: laResult))")
|
||||
switch (laResult) {
|
||||
case .success:
|
||||
userAuthorized = true
|
||||
canConnectCall = true
|
||||
lastSuccessfulUnlock = ProcessInfo.processInfo.systemUptime
|
||||
case .failed:
|
||||
if laMode == .passcode {
|
||||
AlertManager.shared.showAlert(laFailedAlert())
|
||||
}
|
||||
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
|
||||
switch laResult {
|
||||
case .success:
|
||||
chatModel.contentViewAccessAuthenticated = true
|
||||
prefPerformLA = true
|
||||
alertManager.showAlert(laTurnedOnAlert())
|
||||
case .failed:
|
||||
|
||||
@@ -15,13 +15,7 @@ private let receiveTaskId = "chat.simplex.app.receive"
|
||||
// TCP timeout + 2 sec
|
||||
private let waitForMessages: TimeInterval = 6
|
||||
|
||||
// This is the smallest interval between refreshes, and also target interval in "off" mode
|
||||
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 bgRefreshInterval: TimeInterval = 450
|
||||
|
||||
private let maxTimerCount = 9
|
||||
|
||||
@@ -39,14 +33,14 @@ class BGManager {
|
||||
}
|
||||
}
|
||||
|
||||
func schedule(interval: TimeInterval? = nil) {
|
||||
func schedule() {
|
||||
if !ChatModel.shared.ntfEnableLocal {
|
||||
logger.debug("BGManager.schedule: disabled")
|
||||
return
|
||||
}
|
||||
logger.debug("BGManager.schedule")
|
||||
let request = BGAppRefreshTaskRequest(identifier: receiveTaskId)
|
||||
request.earliestBeginDate = Date(timeIntervalSinceNow: interval ?? runInterval)
|
||||
request.earliestBeginDate = Date(timeIntervalSinceNow: bgRefreshInterval)
|
||||
do {
|
||||
try BGTaskScheduler.shared.submit(request)
|
||||
} 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) {
|
||||
if !ChatModel.shared.ntfEnableLocal {
|
||||
logger.debug("BGManager.handleRefresh: disabled")
|
||||
return
|
||||
}
|
||||
logger.debug("BGManager.handleRefresh")
|
||||
let shouldRun_ = lastRanLongAgo
|
||||
if allowBackgroundRefresh() && shouldRun_ {
|
||||
schedule()
|
||||
schedule()
|
||||
if appStateGroupDefault.get().inactive {
|
||||
let completeRefresh = completionHandler {
|
||||
task.setTaskCompleted(success: true)
|
||||
}
|
||||
task.expirationHandler = { completeRefresh("expirationHandler") }
|
||||
receiveMessages(completeRefresh)
|
||||
} else {
|
||||
schedule(interval: shouldRun_ ? bgRefreshInterval : runInterval)
|
||||
logger.debug("BGManager.completionHandler: already active, not started")
|
||||
task.setTaskCompleted(success: true)
|
||||
}
|
||||
@@ -110,22 +90,20 @@ class BGManager {
|
||||
}
|
||||
self.completed = false
|
||||
DispatchQueue.main.async {
|
||||
chatLastBackgroundRunGroupDefault.set(Date.now)
|
||||
let m = ChatModel.shared
|
||||
if (!m.chatInitialized) {
|
||||
setAppState(.bgRefresh)
|
||||
do {
|
||||
try initializeChat(start: true)
|
||||
} catch let error {
|
||||
fatalError("Failed to start or load chats: \(responseError(error))")
|
||||
}
|
||||
}
|
||||
activateChat(appState: .bgRefresh)
|
||||
if m.currentUser == nil {
|
||||
completeReceiving("no current user")
|
||||
return
|
||||
}
|
||||
logger.debug("BGManager.receiveMessages: starting chat")
|
||||
activateChat(appState: .bgRefresh)
|
||||
let cr = ChatReceiver()
|
||||
self.chatReceiver = cr
|
||||
cr.start()
|
||||
|
||||
@@ -54,8 +54,6 @@ final class ChatModel: ObservableObject {
|
||||
@Published var chatDbChanged = false
|
||||
@Published var chatDbEncrypted: Bool?
|
||||
@Published var chatDbStatus: DBMigrationResult?
|
||||
// local authentication
|
||||
@Published var contentViewAccessAuthenticated: Bool = false
|
||||
@Published var laRequest: LocalAuthRequest?
|
||||
// list of chat "previews"
|
||||
@Published var chats: [Chat] = []
|
||||
@@ -85,7 +83,7 @@ final class ChatModel: ObservableObject {
|
||||
// current WebRTC call
|
||||
@Published var callInvitations: Dictionary<ChatId, RcvCallInvitation> = [:]
|
||||
@Published var activeCall: Call?
|
||||
let callCommand: WebRTCCommandProcessor = WebRTCCommandProcessor()
|
||||
@Published var callCommand: WCallCommand?
|
||||
@Published var showCallView = false
|
||||
// remote desktop
|
||||
@Published var remoteCtrlSession: RemoteCtrlSession?
|
||||
@@ -106,10 +104,12 @@ final class ChatModel: ObservableObject {
|
||||
|
||||
static var ok: Bool { ChatModel.shared.chatDbStatus == .ok }
|
||||
|
||||
let ntfEnableLocal = true
|
||||
var ntfEnableLocal: Bool {
|
||||
notificationMode == .off || ntfEnableLocalGroupDefault.get()
|
||||
}
|
||||
|
||||
var ntfEnablePeriodic: Bool {
|
||||
notificationMode != .off
|
||||
notificationMode == .periodic || ntfEnablePeriodicGroupDefault.get()
|
||||
}
|
||||
|
||||
var activeRemoteCtrl: Bool {
|
||||
@@ -267,20 +267,7 @@ final class ChatModel: ObservableObject {
|
||||
func addChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) {
|
||||
// update previews
|
||||
if let i = getChatIndex(cInfo.id) {
|
||||
chats[i].chatItems = switch cInfo {
|
||||
case .group:
|
||||
if let currentPreviewItem = chats[i].chatItems.first {
|
||||
if cItem.meta.itemTs >= currentPreviewItem.meta.itemTs {
|
||||
[cItem]
|
||||
} else {
|
||||
[currentPreviewItem]
|
||||
}
|
||||
} else {
|
||||
[cItem]
|
||||
}
|
||||
default:
|
||||
[cItem]
|
||||
}
|
||||
chats[i].chatItems = [cItem]
|
||||
if case .rcvNew = cItem.meta.itemStatus {
|
||||
chats[i].chatStats.unreadCount = chats[i].chatStats.unreadCount + 1
|
||||
increaseUnreadCounter(user: currentUser!)
|
||||
@@ -783,7 +770,7 @@ final class GMember: ObservableObject, Identifiable {
|
||||
}
|
||||
|
||||
struct RemoteCtrlSession {
|
||||
var ctrlAppInfo: CtrlAppInfo?
|
||||
var ctrlAppInfo: CtrlAppInfo
|
||||
var appVersion: String
|
||||
var sessionState: UIRemoteCtrlSessionState
|
||||
|
||||
@@ -795,10 +782,6 @@ struct RemoteCtrlSession {
|
||||
if case .connected = sessionState { true } else { false }
|
||||
}
|
||||
|
||||
var discovery: Bool {
|
||||
if case .searching = sessionState { true } else { false }
|
||||
}
|
||||
|
||||
var sessionCode: String? {
|
||||
switch sessionState {
|
||||
case let .pendingConfirmation(_, sessionCode): sessionCode
|
||||
@@ -810,8 +793,6 @@ struct RemoteCtrlSession {
|
||||
|
||||
enum UIRemoteCtrlSessionState {
|
||||
case starting
|
||||
case searching
|
||||
case found(remoteCtrl: RemoteCtrlInfo, compatible: Bool)
|
||||
case connecting(remoteCtrl_: RemoteCtrlInfo?)
|
||||
case pendingConfirmation(remoteCtrl_: RemoteCtrlInfo?, sessionCode: String)
|
||||
case connected(remoteCtrl: RemoteCtrlInfo, sessionCode: String)
|
||||
|
||||
@@ -195,18 +195,18 @@ func moveTempFileFromURL(_ url: URL) -> CryptoFile? {
|
||||
}
|
||||
}
|
||||
|
||||
func generateNewFileName(_ prefix: String, _ ext: String, fullPath: Bool = false) -> String {
|
||||
uniqueCombine("\(prefix)_\(getTimestamp()).\(ext)", fullPath: fullPath)
|
||||
func generateNewFileName(_ prefix: String, _ ext: String) -> String {
|
||||
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 {
|
||||
let ns = fileName as NSString
|
||||
let name = ns.deletingPathExtension
|
||||
let ext = ns.pathExtension
|
||||
let suffix = (n == 0) ? "" : "_\(n)"
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,7 +211,7 @@ func apiDeleteUser(_ userId: Int64, _ delSMPQueues: Bool, viewPwd: String?) asyn
|
||||
}
|
||||
|
||||
func apiStartChat() throws -> Bool {
|
||||
let r = chatSendCmdSync(.startChat(mainApp: true))
|
||||
let r = chatSendCmdSync(.startChat(subscribe: true, expire: true, xftp: true))
|
||||
switch r {
|
||||
case .chatStarted: return true
|
||||
case .chatRunning: return false
|
||||
@@ -228,8 +228,7 @@ func apiStopChat() async throws {
|
||||
}
|
||||
|
||||
func apiActivateChat() {
|
||||
chatReopenStore()
|
||||
let r = chatSendCmdSync(.apiActivateChat(restoreChat: true))
|
||||
let r = chatSendCmdSync(.apiActivateChat)
|
||||
if case .cmdOk = r { return }
|
||||
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 .chatCmdError(_, .errorAgent(.CMD(.PROHIBITED))): return (nil, nil, .off)
|
||||
default:
|
||||
logger.debug("apiGetNtfToken response: \(String(describing: r))")
|
||||
logger.debug("apiGetNtfToken response: \(String(describing: r), privacy: .public)")
|
||||
return (nil, nil, .off)
|
||||
}
|
||||
}
|
||||
@@ -606,29 +605,27 @@ func apiConnectPlan(connReq: String) async throws -> ConnectionPlan {
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiConnect(incognito: Bool, connReq: String) async -> (ConnReqType, PendingContactConnection)? {
|
||||
let (r, alert) = await apiConnect_(incognito: incognito, connReq: connReq)
|
||||
func apiConnect(incognito: Bool, connReq: String) async -> ConnReqType? {
|
||||
let (connReqType, alert) = await apiConnect_(incognito: incognito, connReq: connReq)
|
||||
if let alert = alert {
|
||||
AlertManager.shared.showAlert(alert)
|
||||
return nil
|
||||
} 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 {
|
||||
logger.error("apiConnect: no current user")
|
||||
return (nil, nil)
|
||||
}
|
||||
let r = await chatSendCmd(.apiConnect(userId: userId, incognito: incognito, connReq: connReq))
|
||||
let m = ChatModel.shared
|
||||
switch r {
|
||||
case let .sentConfirmation(_, connection):
|
||||
return ((.invitation, connection), nil)
|
||||
case let .sentInvitation(_, connection):
|
||||
return ((.contact, connection), nil)
|
||||
case .sentConfirmation: return (.invitation, nil)
|
||||
case .sentInvitation: return (.contact, nil)
|
||||
case let .contactAlreadyExists(_, contact):
|
||||
let m = ChatModel.shared
|
||||
if let c = m.getContactChat(contact.contactId) {
|
||||
await MainActor.run { m.chatId = c.id }
|
||||
}
|
||||
@@ -922,10 +919,8 @@ func findKnownRemoteCtrl() async throws {
|
||||
try await sendCommandOkResp(.findKnownRemoteCtrl)
|
||||
}
|
||||
|
||||
func confirmRemoteCtrl(_ rcId: Int64) async throws -> (RemoteCtrlInfo?, CtrlAppInfo, String) {
|
||||
let r = await chatSendCmd(.confirmRemoteCtrl(remoteCtrlId: rcId))
|
||||
if case let .remoteCtrlConnecting(rc_, ctrlAppInfo, v) = r { return (rc_, ctrlAppInfo, v) }
|
||||
throw r
|
||||
func confirmRemoteCtrl(_ rcId: Int64) async throws {
|
||||
try await sendCommandOkResp(.confirmRemoteCtrl(remoteCtrlId: rcId))
|
||||
}
|
||||
|
||||
func verifyRemoteCtrlSession(_ sessCode: String) async throws -> RemoteCtrlInfo {
|
||||
@@ -1235,9 +1230,6 @@ func initializeChat(start: Bool, dbKey: String? = nil, refreshInvitations: Bool
|
||||
try startChat(refreshInvitations: refreshInvitations)
|
||||
} else {
|
||||
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()
|
||||
}
|
||||
(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 {
|
||||
registerToken(token: token)
|
||||
}
|
||||
@@ -1370,6 +1360,18 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
let m = ChatModel.shared
|
||||
logger.debug("processReceivedMsg: \(res.responseType)")
|
||||
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):
|
||||
if active(user) && contact.directOrUsed {
|
||||
await MainActor.run {
|
||||
@@ -1662,40 +1664,36 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
activateCall(invitation)
|
||||
case let .callOffer(_, contact, callType, offer, sharedKey, _):
|
||||
await withCall(contact) { call in
|
||||
await MainActor.run {
|
||||
call.callState = .offerReceived
|
||||
call.peerMedia = callType.media
|
||||
call.sharedKey = sharedKey
|
||||
}
|
||||
call.callState = .offerReceived
|
||||
call.peerMedia = callType.media
|
||||
call.sharedKey = sharedKey
|
||||
let useRelay = UserDefaults.standard.bool(forKey: DEFAULT_WEBRTC_POLICY_RELAY)
|
||||
let iceServers = getIceServers()
|
||||
logger.debug(".callOffer useRelay \(useRelay)")
|
||||
logger.debug(".callOffer iceServers \(String(describing: iceServers))")
|
||||
await m.callCommand.processCommand(.offer(
|
||||
m.callCommand = .offer(
|
||||
offer: offer.rtcSession,
|
||||
iceCandidates: offer.rtcIceCandidates,
|
||||
media: callType.media, aesKey: sharedKey,
|
||||
iceServers: iceServers,
|
||||
relay: useRelay
|
||||
))
|
||||
)
|
||||
}
|
||||
case let .callAnswer(_, contact, answer):
|
||||
await withCall(contact) { call in
|
||||
await MainActor.run {
|
||||
call.callState = .answerReceived
|
||||
}
|
||||
await m.callCommand.processCommand(.answer(answer: answer.rtcSession, iceCandidates: answer.rtcIceCandidates))
|
||||
call.callState = .answerReceived
|
||||
m.callCommand = .answer(answer: answer.rtcSession, iceCandidates: answer.rtcIceCandidates)
|
||||
}
|
||||
case let .callExtraInfo(_, contact, extraInfo):
|
||||
await withCall(contact) { _ in
|
||||
await m.callCommand.processCommand(.ice(iceCandidates: extraInfo.rtcIceCandidates))
|
||||
m.callCommand = .ice(iceCandidates: extraInfo.rtcIceCandidates)
|
||||
}
|
||||
case let .callEnded(_, contact):
|
||||
if let invitation = await MainActor.run(body: { m.callInvitations.removeValue(forKey: contact.id) }) {
|
||||
CallController.shared.reportCallRemoteEnded(invitation: invitation)
|
||||
}
|
||||
await withCall(contact) { call in
|
||||
await m.callCommand.processCommand(.end)
|
||||
m.callCommand = .end
|
||||
CallController.shared.reportCallRemoteEnded(call: call)
|
||||
}
|
||||
case .chatSuspended:
|
||||
@@ -1716,17 +1714,9 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
await MainActor.run {
|
||||
m.updateGroupMemberConnectionStats(groupInfo, member, ratchetSyncProgress.connectionStats)
|
||||
}
|
||||
case let .remoteCtrlFound(remoteCtrl, ctrlAppInfo_, appVersion, compatible):
|
||||
await MainActor.run {
|
||||
if let sess = m.remoteCtrlSession, case .searching = sess.sessionState {
|
||||
let state = UIRemoteCtrlSessionState.found(remoteCtrl: remoteCtrl, compatible: compatible)
|
||||
m.remoteCtrlSession = RemoteCtrlSession(
|
||||
ctrlAppInfo: ctrlAppInfo_,
|
||||
appVersion: appVersion,
|
||||
sessionState: state
|
||||
)
|
||||
}
|
||||
}
|
||||
case let .remoteCtrlFound(remoteCtrl):
|
||||
// TODO multicast
|
||||
logger.debug("\(String(describing: remoteCtrl))")
|
||||
case let .remoteCtrlSessionCode(remoteCtrl_, sessionCode):
|
||||
await MainActor.run {
|
||||
let state = UIRemoteCtrlSessionState.pendingConfirmation(remoteCtrl_: remoteCtrl_, sessionCode: sessionCode)
|
||||
@@ -1741,23 +1731,16 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
case .remoteCtrlStopped:
|
||||
// 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.
|
||||
if let sess = m.remoteCtrlSession {
|
||||
await MainActor.run {
|
||||
m.remoteCtrlSession = nil
|
||||
}
|
||||
if case .connected = sess.sessionState {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
switchToLocalSession()
|
||||
}
|
||||
}
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
switchToLocalSession()
|
||||
}
|
||||
default:
|
||||
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 {
|
||||
await perform(call)
|
||||
await MainActor.run { perform(call) }
|
||||
} else {
|
||||
logger.debug("processReceivedMsg: ignoring \(res.responseType), not in call with the contact \(contact.id)")
|
||||
}
|
||||
|
||||
@@ -9,30 +9,27 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import SimpleXChat
|
||||
import SwiftUI
|
||||
|
||||
private let suspendLockQueue = DispatchQueue(label: "chat.simplex.app.suspend.lock")
|
||||
|
||||
let appSuspendTimeout: Int = 15 // seconds
|
||||
|
||||
let bgSuspendTimeout: Int = 5 // seconds
|
||||
|
||||
let terminationTimeout: Int = 3 // seconds
|
||||
|
||||
let activationDelay: TimeInterval = 1.5
|
||||
|
||||
let nseSuspendTimeout: TimeInterval = 5
|
||||
|
||||
private func _suspendChat(timeout: Int) {
|
||||
// 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 {
|
||||
logger.error("_suspendChat called, current state: \(state.rawValue)")
|
||||
logger.error("_suspendChat called, current state: \(state.rawValue, privacy: .public)")
|
||||
} else if ChatModel.ok {
|
||||
AppChatState.shared.set(.suspending)
|
||||
appStateGroupDefault.set(.suspending)
|
||||
apiSuspendChat(timeoutMicroseconds: timeout * 1000000)
|
||||
let endTask = beginBGTask(chatSuspended)
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + Double(timeout) + 1, execute: endTask)
|
||||
} else {
|
||||
AppChatState.shared.set(.suspended)
|
||||
appStateGroupDefault.set(.suspended)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,16 +41,18 @@ func suspendChat() {
|
||||
|
||||
func suspendBgRefresh() {
|
||||
suspendLockQueue.sync {
|
||||
if case .bgRefresh = AppChatState.shared.value {
|
||||
if case .bgRefresh = appStateGroupDefault.get() {
|
||||
_suspendChat(timeout: bgSuspendTimeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var terminating = false
|
||||
|
||||
func terminateChat() {
|
||||
logger.debug("terminateChat")
|
||||
suspendLockQueue.sync {
|
||||
switch AppChatState.shared.value {
|
||||
switch appStateGroupDefault.get() {
|
||||
case .suspending:
|
||||
// suspend instantly if already suspending
|
||||
_chatSuspended()
|
||||
@@ -65,6 +64,7 @@ func terminateChat() {
|
||||
case .stopped:
|
||||
chatCloseStore()
|
||||
default:
|
||||
terminating = true
|
||||
// the store will be closed in _chatSuspended when event is received
|
||||
_suspendChat(timeout: terminationTimeout)
|
||||
}
|
||||
@@ -73,7 +73,7 @@ func terminateChat() {
|
||||
|
||||
func chatSuspended() {
|
||||
suspendLockQueue.sync {
|
||||
if case .suspending = AppChatState.shared.value {
|
||||
if case .suspending = appStateGroupDefault.get() {
|
||||
_chatSuspended()
|
||||
}
|
||||
}
|
||||
@@ -81,121 +81,48 @@ func chatSuspended() {
|
||||
|
||||
private func _chatSuspended() {
|
||||
logger.debug("_chatSuspended")
|
||||
AppChatState.shared.set(.suspended)
|
||||
appStateGroupDefault.set(.suspended)
|
||||
if ChatModel.shared.chatRunning == true {
|
||||
ChatReceiver.shared.stop()
|
||||
}
|
||||
chatCloseStore()
|
||||
}
|
||||
|
||||
func setAppState(_ appState: AppState) {
|
||||
suspendLockQueue.sync {
|
||||
AppChatState.shared.set(appState)
|
||||
if terminating {
|
||||
chatCloseStore()
|
||||
}
|
||||
}
|
||||
|
||||
func activateChat(appState: AppState = .active) {
|
||||
logger.debug("DEBUGGING: activateChat")
|
||||
terminating = false
|
||||
suspendLockQueue.sync {
|
||||
AppChatState.shared.set(appState)
|
||||
appStateGroupDefault.set(appState)
|
||||
if ChatModel.ok { apiActivateChat() }
|
||||
logger.debug("DEBUGGING: activateChat: after apiActivateChat")
|
||||
}
|
||||
}
|
||||
|
||||
func initChatAndMigrate(refreshInvitations: Bool = true) {
|
||||
terminating = false
|
||||
let m = ChatModel.shared
|
||||
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 {
|
||||
try initializeChat(start: m.v3DBMigration.startChat && start, refreshInvitations: refreshInvitations)
|
||||
m.v3DBMigration = v3DBMigrationDefault.get()
|
||||
try initializeChat(start: m.v3DBMigration.startChat, refreshInvitations: refreshInvitations)
|
||||
} catch let error {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title: start ? "Error starting chat" : "Error opening chat",
|
||||
message: "Please contact developers.\nError: \(responseError(error))"
|
||||
)
|
||||
fatalError("Failed to start or load chats: \(responseError(error))")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func startChatForCall() {
|
||||
logger.debug("DEBUGGING: startChatForCall")
|
||||
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) {
|
||||
func startChatAndActivate() {
|
||||
terminating = false
|
||||
logger.debug("DEBUGGING: startChatAndActivate")
|
||||
if ChatModel.shared.chatRunning == true {
|
||||
ChatReceiver.shared.start()
|
||||
logger.debug("DEBUGGING: startChatAndActivate: after ChatReceiver.shared.start")
|
||||
}
|
||||
if case .active = AppChatState.shared.value {
|
||||
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() {
|
||||
if .active != appStateGroupDefault.get() {
|
||||
logger.debug("DEBUGGING: startChatAndActivate: before activateChat")
|
||||
activateChat()
|
||||
completion()
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,17 @@ struct SimpleXApp: App {
|
||||
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
||||
@StateObject private var chatModel = ChatModel.shared
|
||||
@ObservedObject var alertManager = AlertManager.shared
|
||||
|
||||
@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() {
|
||||
DispatchQueue.global(qos: .background).sync {
|
||||
haskell_init()
|
||||
// hs_init(0, nil)
|
||||
}
|
||||
hs_init(0, nil)
|
||||
UserDefaults.standard.register(defaults: appDefaults)
|
||||
setGroupDefaults()
|
||||
registerGroupDefaults()
|
||||
@@ -34,17 +36,22 @@ struct SimpleXApp: App {
|
||||
}
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
// contentAccessAuthenticationExtended has to be passed to ContentView on view initialization,
|
||||
// so that it's computed by the time view renders, and not on event after rendering
|
||||
ContentView(contentAccessAuthenticationExtended: !authenticationExpired())
|
||||
return WindowGroup {
|
||||
ContentView(
|
||||
doAuthenticate: $doAuthenticate,
|
||||
userAuthorized: $userAuthorized,
|
||||
canConnectCall: $canConnectCall,
|
||||
lastSuccessfulUnlock: $lastSuccessfulUnlock,
|
||||
showInitializationView: $showInitializationView
|
||||
)
|
||||
.environmentObject(chatModel)
|
||||
.onOpenURL { url in
|
||||
logger.debug("ContentView.onOpenURL: \(url)")
|
||||
chatModel.appOpenUrl = url
|
||||
}
|
||||
.onAppear() {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
|
||||
showInitializationView = true
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
initChatAndMigrate()
|
||||
}
|
||||
}
|
||||
@@ -52,35 +59,30 @@ struct SimpleXApp: App {
|
||||
logger.debug("scenePhase was \(String(describing: scenePhase)), now \(String(describing: phase))")
|
||||
switch (phase) {
|
||||
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 {
|
||||
CallController.shared.shouldSuspendChat = true
|
||||
} else {
|
||||
suspendChat()
|
||||
BGManager.shared.schedule()
|
||||
}
|
||||
if userAuthorized == true {
|
||||
enteredBackground = ProcessInfo.processInfo.systemUptime
|
||||
}
|
||||
doAuthenticate = false
|
||||
canConnectCall = false
|
||||
NtfManager.shared.setNtfBadgeCount(chatModel.totalUnreadCountForAllUsers())
|
||||
case .active:
|
||||
CallController.shared.shouldSuspendChat = false
|
||||
let appState = AppChatState.shared.value
|
||||
|
||||
if appState != .stopped {
|
||||
startChatAndActivate {
|
||||
if appState.inactive && chatModel.chatRunning == true {
|
||||
updateChats()
|
||||
if !chatModel.showCallView && !CallController.shared.hasActiveCalls() {
|
||||
updateCallInvitations()
|
||||
}
|
||||
}
|
||||
let appState = appStateGroupDefault.get()
|
||||
startChatAndActivate()
|
||||
if appState.inactive && chatModel.chatRunning == true {
|
||||
updateChats()
|
||||
if !chatModel.showCallView && !CallController.shared.hasActiveCalls() {
|
||||
updateCallInvitations()
|
||||
}
|
||||
}
|
||||
doAuthenticate = authenticationExpired()
|
||||
canConnectCall = !(doAuthenticate && prefPerformLA) || unlockedRecently()
|
||||
default:
|
||||
break
|
||||
}
|
||||
@@ -98,12 +100,12 @@ struct SimpleXApp: App {
|
||||
if legacyDatabase, case .documents = dbContainerGroupDefault.get() {
|
||||
dbContainerGroupDefault.set(.documents)
|
||||
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 {
|
||||
dbContainerGroupDefault.set(.group)
|
||||
setMigrationState(.ready)
|
||||
logger.debug("SimpleXApp init: using DB in app group container: \(getAppDatabasePath())*.db")
|
||||
logger.debug("SimpleXApp init: legacy DB\(legacyDatabase ? "" : " not") present")
|
||||
logger.debug("SimpleXApp init: using DB in app group container: \(getAppDatabasePath(), privacy: .public)*.db")
|
||||
logger.debug("SimpleXApp init: legacy DB\(legacyDatabase ? "" : " not", privacy: .public) present")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,14 +115,22 @@ struct SimpleXApp: App {
|
||||
}
|
||||
|
||||
private func authenticationExpired() -> Bool {
|
||||
if let enteredBackgroundAuthenticated = enteredBackgroundAuthenticated {
|
||||
if let enteredBackground = enteredBackground {
|
||||
let delay = Double(UserDefaults.standard.integer(forKey: DEFAULT_LA_LOCK_DELAY))
|
||||
return ProcessInfo.processInfo.systemUptime - enteredBackgroundAuthenticated >= delay
|
||||
return ProcessInfo.processInfo.systemUptime - enteredBackground >= delay
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private func unlockedRecently() -> Bool {
|
||||
if let lastSuccessfulUnlock = lastSuccessfulUnlock {
|
||||
return ProcessInfo.processInfo.systemUptime - lastSuccessfulUnlock < 2
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private func updateChats() {
|
||||
do {
|
||||
let chats = try apiGetChats()
|
||||
|
||||
@@ -38,21 +38,21 @@ struct ActiveCallView: View {
|
||||
}
|
||||
}
|
||||
.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)
|
||||
createWebRTCClient()
|
||||
dismissAllSheets()
|
||||
}
|
||||
.onChange(of: canConnectCall) { _ in
|
||||
logger.debug("ActiveCallView: canConnectCall changed to \(canConnectCall)")
|
||||
logger.debug("ActiveCallView: canConnectCall changed to \(canConnectCall, privacy: .public)")
|
||||
createWebRTCClient()
|
||||
}
|
||||
.onDisappear {
|
||||
logger.debug("ActiveCallView: disappear")
|
||||
Task { await m.callCommand.setClient(nil) }
|
||||
AppDelegate.keepScreenOn(false)
|
||||
client?.endCall()
|
||||
}
|
||||
.onChange(of: m.callCommand) { _ in sendCommandToClient()}
|
||||
.background(.black)
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
@@ -60,8 +60,19 @@ struct ActiveCallView: View {
|
||||
private func createWebRTCClient() {
|
||||
if client == nil && canConnectCall {
|
||||
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 {
|
||||
await m.callCommand.setClient(client)
|
||||
await client.sendCallCommand(command: cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,10 +168,8 @@ struct ActiveCallView: View {
|
||||
}
|
||||
case let .error(message):
|
||||
logger.debug("ActiveCallView: command error: \(message)")
|
||||
AlertManager.shared.showAlert(Alert(title: Text("Error"), message: Text(message)))
|
||||
case let .invalid(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 {
|
||||
Text(call.encryptionStatus)
|
||||
if let connInfo = call.connectionInfo {
|
||||
// Text("(") + Text(connInfo.text) + Text(", \(connInfo.protocolText))")
|
||||
Text("(") + Text(connInfo.text) + Text(")")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
||||
// The delay allows to accept the second call before suspending a chat
|
||||
// see `.onChange(of: scenePhase)` in SimpleXApp
|
||||
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 {
|
||||
self?.shouldSuspendChat = false
|
||||
suspendChat()
|
||||
@@ -142,46 +142,33 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
||||
|
||||
@objc(pushRegistry:didUpdatePushCredentials:forType:)
|
||||
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) {
|
||||
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 {
|
||||
completion()
|
||||
return
|
||||
}
|
||||
if AppChatState.shared.value == .stopped {
|
||||
self.reportExpiredCall(payload: payload, completion)
|
||||
return
|
||||
}
|
||||
logger.debug("CallController: initializing chat")
|
||||
if (!ChatModel.shared.chatInitialized) {
|
||||
logger.debug("CallController: initializing chat")
|
||||
do {
|
||||
try initializeChat(start: true, refreshInvitations: false)
|
||||
} catch let error {
|
||||
logger.error("CallController: initializing chat error: \(error)")
|
||||
self.reportExpiredCall(payload: payload, completion)
|
||||
return
|
||||
}
|
||||
initChatAndMigrate(refreshInvitations: false)
|
||||
}
|
||||
logger.debug("CallController: initialized chat")
|
||||
startChatForCall()
|
||||
logger.debug("CallController: started chat")
|
||||
self.shouldSuspendChat = true
|
||||
startChatAndActivate()
|
||||
shouldSuspendChat = true
|
||||
// There are no invitations in the model, as it was processed by NSE
|
||||
_ = try? justRefreshCallInvitations()
|
||||
logger.debug("CallController: updated call invitations chat")
|
||||
// logger.debug("CallController justRefreshCallInvitations: \(String(describing: m.callInvitations))")
|
||||
// Extract the call information from the push notification payload
|
||||
let m = ChatModel.shared
|
||||
if let contactId = payload.dictionaryPayload["contactId"] as? String,
|
||||
let invitation = m.callInvitations[contactId] {
|
||||
let update = self.cxCallUpdate(invitation: invitation)
|
||||
let update = cxCallUpdate(invitation: invitation)
|
||||
if let uuid = invitation.callkitUUID {
|
||||
logger.debug("CallController: report pushkit call via CallKit")
|
||||
let update = self.cxCallUpdate(invitation: invitation)
|
||||
self.provider.reportNewIncomingCall(with: uuid, update: update) { error in
|
||||
let update = cxCallUpdate(invitation: invitation)
|
||||
provider.reportNewIncomingCall(with: uuid, update: update) { error in
|
||||
if error != nil {
|
||||
m.callInvitations.removeValue(forKey: contactId)
|
||||
}
|
||||
@@ -189,10 +176,10 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
||||
completion()
|
||||
}
|
||||
} else {
|
||||
self.reportExpiredCall(update: update, completion)
|
||||
reportExpiredCall(update: update, completion)
|
||||
}
|
||||
} 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) {
|
||||
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 invitation.callTs.timeIntervalSinceNow >= -180 {
|
||||
let update = cxCallUpdate(invitation: invitation)
|
||||
@@ -363,7 +350,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
||||
private func requestTransaction(with action: CXAction, onSuccess: @escaping () -> Void = {}) {
|
||||
controller.request(CXTransaction(action: action)) { error in
|
||||
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 {
|
||||
logger.debug("CallController.requestTransaction requested transaction successfully")
|
||||
onSuccess()
|
||||
|
||||
@@ -22,7 +22,7 @@ class CallManager {
|
||||
let m = ChatModel.shared
|
||||
if let call = m.activeCall, call.callkitUUID == callUUID {
|
||||
m.showCallView = true
|
||||
Task { await m.callCommand.processCommand(.capabilities(media: call.localMedia)) }
|
||||
m.callCommand = .capabilities(media: call.localMedia)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -57,21 +57,19 @@ class CallManager {
|
||||
m.activeCall = call
|
||||
m.showCallView = true
|
||||
|
||||
Task {
|
||||
await m.callCommand.processCommand(.start(
|
||||
m.callCommand = .start(
|
||||
media: invitation.callType.media,
|
||||
aesKey: invitation.sharedKey,
|
||||
iceServers: iceServers,
|
||||
relay: useRelay
|
||||
))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func enableMedia(media: CallMediaType, enable: Bool, callUUID: UUID) -> Bool {
|
||||
if let call = ChatModel.shared.activeCall, call.callkitUUID == callUUID {
|
||||
let m = ChatModel.shared
|
||||
Task { await m.callCommand.processCommand(.media(media: media, enable: enable)) }
|
||||
m.callCommand = .media(media: media, enable: enable)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -96,13 +94,11 @@ class CallManager {
|
||||
completed()
|
||||
} else {
|
||||
logger.debug("CallManager.endCall: ending call...")
|
||||
m.callCommand = .end
|
||||
m.activeCall = nil
|
||||
m.showCallView = false
|
||||
completed()
|
||||
Task {
|
||||
await m.callCommand.processCommand(.end)
|
||||
await MainActor.run {
|
||||
m.activeCall = nil
|
||||
m.showCallView = false
|
||||
completed()
|
||||
}
|
||||
do {
|
||||
try await apiEndCall(call.contact)
|
||||
} catch {
|
||||
|
||||
@@ -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 {
|
||||
var connectionState: String
|
||||
var iceConnectionState: String
|
||||
@@ -402,12 +358,26 @@ struct ConnectionInfo: Codable, Equatable {
|
||||
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
|
||||
struct RTCIceCandidate: Codable, Equatable {
|
||||
var candidateType: RTCIceCandidateType?
|
||||
var `protocol`: String?
|
||||
var relayProtocol: String?
|
||||
var sdpMid: String?
|
||||
var sdpMLineIndex: Int?
|
||||
var candidate: String
|
||||
|
||||
@@ -18,11 +18,10 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
}()
|
||||
private static let ivTagBytes: Int = 28
|
||||
private static let enableEncryption: Bool = true
|
||||
private var chat_ctrl = getChatCtrl()
|
||||
|
||||
struct Call {
|
||||
var connection: RTCPeerConnection
|
||||
var iceCandidates: IceCandidates
|
||||
var iceCandidates: [RTCIceCandidate]
|
||||
var localMedia: CallMediaType
|
||||
var localCamera: RTCVideoCapturer?
|
||||
var localVideoSource: RTCVideoSource?
|
||||
@@ -34,24 +33,10 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
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 audioQueue = DispatchQueue(label: "audio")
|
||||
private var sendCallResponse: (WVAPIMessage) async -> Void
|
||||
var activeCall: Binding<Call?>
|
||||
private var activeCall: Binding<Call?>
|
||||
private var localRendererAspectRatio: Binding<CGFloat?>
|
||||
|
||||
@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"),
|
||||
]
|
||||
|
||||
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)
|
||||
connection.delegate = self
|
||||
createAudioSender(connection)
|
||||
@@ -102,7 +87,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
}
|
||||
return Call(
|
||||
connection: connection,
|
||||
iceCandidates: IceCandidates(),
|
||||
iceCandidates: remoteIceCandidates,
|
||||
localMedia: mediaType,
|
||||
localCamera: localCamera,
|
||||
localVideoSource: localVideoSource,
|
||||
@@ -159,18 +144,26 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
logger.debug("starting incoming call - create webrtc session")
|
||||
if activeCall.wrappedValue != nil { endCall() }
|
||||
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
|
||||
let (offer, error) = await call.connection.offer()
|
||||
if let offer = offer {
|
||||
resp = .offer(
|
||||
offer: compressToBase64(input: encodeJSON(CustomRTCSessionDescription(type: offer.type.toSdpType(), sdp: offer.sdp))),
|
||||
iceCandidates: compressToBase64(input: encodeJSON(await self.getInitialIceCandidates())),
|
||||
capabilities: CallCapabilities(encryption: encryption)
|
||||
)
|
||||
self.waitForMoreIceCandidates()
|
||||
} else {
|
||||
resp = .error(message: "offer error: \(error?.localizedDescription ?? "unknown error")")
|
||||
call.connection.offer { answer in
|
||||
Task {
|
||||
let gotCandidates = await self.waitWithTimeout(10_000, stepMs: 1000, until: { self.activeCall.wrappedValue?.iceCandidates.count ?? 0 > 0 })
|
||||
if gotCandidates {
|
||||
await self.sendCallResponse(.init(
|
||||
corrId: nil,
|
||||
resp: .offer(
|
||||
offer: compressToBase64(input: encodeJSON(CustomRTCSessionDescription(type: answer.type.toSdpType(), sdp: answer.sdp))),
|
||||
iceCandidates: compressToBase64(input: encodeJSON(self.activeCall.wrappedValue?.iceCandidates ?? [])),
|
||||
capabilities: CallCapabilities(encryption: encryption)
|
||||
),
|
||||
command: command)
|
||||
)
|
||||
} else {
|
||||
self.endCall()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
case let .offer(offer, iceCandidates, media, aesKey, iceServers, relay):
|
||||
if activeCall.wrappedValue != nil {
|
||||
@@ -179,21 +172,26 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
resp = .error(message: "accept: encryption is not supported")
|
||||
} else if let offer: CustomRTCSessionDescription = decodeJSON(decompressFromBase64(input: offer)),
|
||||
let remoteIceCandidates: [RTCIceCandidate] = decodeJSON(decompressFromBase64(input: iceCandidates)) {
|
||||
let call = initializeCall(iceServers?.toWebRTCIceServers(), media, WebRTCClient.enableEncryption ? aesKey : nil, relay)
|
||||
let call = initializeCall(iceServers?.toWebRTCIceServers(), remoteIceCandidates, media, WebRTCClient.enableEncryption ? aesKey : nil, relay)
|
||||
activeCall.wrappedValue = call
|
||||
let pc = call.connection
|
||||
if let type = offer.type, let sdp = offer.sdp {
|
||||
if (try? await pc.setRemoteDescription(RTCSessionDescription(type: type.toWebRTCSdpType(), sdp: sdp))) != nil {
|
||||
let (answer, error) = await pc.answer()
|
||||
if let answer = answer {
|
||||
pc.answer { answer in
|
||||
self.addIceCandidates(pc, remoteIceCandidates)
|
||||
resp = .answer(
|
||||
answer: compressToBase64(input: encodeJSON(CustomRTCSessionDescription(type: answer.type.toSdpType(), sdp: answer.sdp))),
|
||||
iceCandidates: compressToBase64(input: encodeJSON(await self.getInitialIceCandidates()))
|
||||
)
|
||||
self.waitForMoreIceCandidates()
|
||||
} else {
|
||||
resp = .error(message: "answer error: \(error?.localizedDescription ?? "unknown error")")
|
||||
// Task {
|
||||
// try? await Task.sleep(nanoseconds: 32_000 * 1000000)
|
||||
Task {
|
||||
await self.sendCallResponse(.init(
|
||||
corrId: nil,
|
||||
resp: .answer(
|
||||
answer: compressToBase64(input: encodeJSON(CustomRTCSessionDescription(type: answer.type.toSdpType(), sdp: answer.sdp))),
|
||||
iceCandidates: compressToBase64(input: encodeJSON(call.iceCandidates))
|
||||
),
|
||||
command: command)
|
||||
)
|
||||
}
|
||||
// }
|
||||
}
|
||||
} else {
|
||||
resp = .error(message: "accept: remote description is not set")
|
||||
@@ -236,7 +234,6 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
resp = .ok
|
||||
}
|
||||
case .end:
|
||||
// TODO possibly, endCall should be called before returning .ok
|
||||
await sendCallResponse(.init(corrId: nil, resp: .ok, command: command))
|
||||
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) {
|
||||
logger.debug("WebRTCClient: enabling media \(media.rawValue) \(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)
|
||||
let isKeyFrame = unencrypted[0] & 1 == 0
|
||||
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)
|
||||
} else {
|
||||
return nil
|
||||
@@ -417,13 +387,12 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
audioSessionToDefaults()
|
||||
}
|
||||
|
||||
func untilIceComplete(timeoutMs: UInt64, stepMs: UInt64, action: @escaping () async -> Void) async {
|
||||
var t: UInt64 = 0
|
||||
repeat {
|
||||
_ = try? await Task.sleep(nanoseconds: stepMs * 1000000)
|
||||
t += stepMs
|
||||
await action()
|
||||
} while t < timeoutMs && activeCall.wrappedValue?.connection.iceGatheringState != .complete
|
||||
func waitWithTimeout(_ timeoutMs: UInt64, stepMs: UInt64, until success: () -> Bool) async -> Bool {
|
||||
let startedAt = DispatchTime.now()
|
||||
while !success() && startedAt.uptimeNanoseconds + timeoutMs * 1000000 > DispatchTime.now().uptimeNanoseconds {
|
||||
guard let _ = try? await Task.sleep(nanoseconds: stepMs * 1000000) else { break }
|
||||
}
|
||||
return success()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,33 +405,25 @@ extension WebRTC.RTCPeerConnection {
|
||||
optionalConstraints: nil)
|
||||
}
|
||||
|
||||
func offer() async -> (RTCSessionDescription?, Error?) {
|
||||
await withCheckedContinuation { cont in
|
||||
offer(for: mediaConstraints()) { (sdp, error) in
|
||||
self.processSDP(cont, sdp, error)
|
||||
func offer(_ completion: @escaping (_ sdp: RTCSessionDescription) -> Void) {
|
||||
offer(for: mediaConstraints()) { (sdp, error) in
|
||||
guard let sdp = sdp else {
|
||||
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
|
||||
if let error = error {
|
||||
cont.resume(returning: (nil, error))
|
||||
} else {
|
||||
cont.resume(returning: (sdp, nil))
|
||||
}
|
||||
completion(sdp)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
setSpeakerEnabledAndConfigureSession(enableSpeaker)
|
||||
case .connected: sendConnectedEvent(connection)
|
||||
case .disconnected, .failed: endCall()
|
||||
default: do {}
|
||||
}
|
||||
@@ -531,9 +491,7 @@ extension WebRTCClient: RTCPeerConnectionDelegate {
|
||||
|
||||
func peerConnection(_ connection: RTCPeerConnection, didGenerate candidate: WebRTC.RTCIceCandidate) {
|
||||
// logger.debug("Connection generated candidate \(candidate.debugDescription)")
|
||||
Task {
|
||||
await self.activeCall.wrappedValue?.iceCandidates.append(candidate.toCandidate(nil, nil))
|
||||
}
|
||||
activeCall.wrappedValue?.iceCandidates.append(candidate.toCandidate(nil, nil, nil))
|
||||
}
|
||||
|
||||
func peerConnection(_ connection: RTCPeerConnection, didRemove candidates: [WebRTC.RTCIceCandidate]) {
|
||||
@@ -548,9 +506,10 @@ extension WebRTCClient: RTCPeerConnectionDelegate {
|
||||
lastReceivedMs lastDataReceivedMs: Int32,
|
||||
changeReason reason: String) {
|
||||
// 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
|
||||
stats.statistics.values.forEach { stat in
|
||||
// logger.debug("Stat \(stat.debugDescription)")
|
||||
@@ -558,25 +517,24 @@ extension WebRTCClient: RTCPeerConnectionDelegate {
|
||||
let localId = stat.values["localCandidateId"] as? String,
|
||||
let remoteId = stat.values["remoteCandidateId"] as? String,
|
||||
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 {
|
||||
await self.sendCallResponse(.init(
|
||||
corrId: nil,
|
||||
resp: .connected(connectionInfo: ConnectionInfo(
|
||||
localCandidate: RTCIceCandidate(
|
||||
candidateType: RTCIceCandidateType.init(rawValue: localStats.values["candidateType"] as! String),
|
||||
protocol: localStats.values["protocol"] as? String,
|
||||
sdpMid: nil,
|
||||
sdpMLineIndex: nil,
|
||||
candidate: ""
|
||||
localCandidate: local.toCandidate(
|
||||
RTCIceCandidateType.init(rawValue: localStats.values["candidateType"] as! String),
|
||||
localStats.values["protocol"] as? String,
|
||||
localStats.values["relayProtocol"] as? String
|
||||
),
|
||||
remoteCandidate: RTCIceCandidate(
|
||||
candidateType: RTCIceCandidateType.init(rawValue: remoteStats.values["candidateType"] as! String),
|
||||
protocol: remoteStats.values["protocol"] as? String,
|
||||
sdpMid: nil,
|
||||
sdpMLineIndex: nil,
|
||||
candidate: ""))),
|
||||
remoteCandidate: remote.toCandidate(
|
||||
RTCIceCandidateType.init(rawValue: remoteStats.values["candidateType"] as! String),
|
||||
remoteStats.values["protocol"] as? String,
|
||||
remoteStats.values["relayProtocol"] as? String
|
||||
))),
|
||||
command: nil)
|
||||
)
|
||||
}
|
||||
@@ -676,10 +634,11 @@ extension RTCIceCandidate {
|
||||
}
|
||||
|
||||
extension WebRTC.RTCIceCandidate {
|
||||
func toCandidate(_ candidateType: RTCIceCandidateType?, _ protocol: String?) -> RTCIceCandidate {
|
||||
func toCandidate(_ candidateType: RTCIceCandidateType?, _ protocol: String?, _ relayProtocol: String?) -> RTCIceCandidate {
|
||||
RTCIceCandidate(
|
||||
candidateType: candidateType,
|
||||
protocol: `protocol`,
|
||||
relayProtocol: relayProtocol,
|
||||
sdpMid: sdpMid,
|
||||
sdpMLineIndex: Int(sdpMLineIndex),
|
||||
candidate: sdp
|
||||
|
||||
@@ -723,14 +723,9 @@ struct ChatView: View {
|
||||
if ci.meta.itemDeleted == nil && !ci.isLiveDummy && !live {
|
||||
menu.append(replyUIAction(ci))
|
||||
}
|
||||
let fileSource = getLoadedFileSource(ci.file)
|
||||
let fileExists = if let fs = fileSource, FileManager.default.fileExists(atPath: getAppFilePath(fs.filePath).path) { true } else { false }
|
||||
let copyAndShareAllowed = !ci.content.text.isEmpty || (ci.content.msgContent?.isImage == true && fileExists)
|
||||
if copyAndShareAllowed {
|
||||
menu.append(shareUIAction(ci))
|
||||
menu.append(copyUIAction(ci))
|
||||
}
|
||||
if let fileSource = fileSource, fileExists {
|
||||
menu.append(shareUIAction(ci))
|
||||
menu.append(copyUIAction(ci))
|
||||
if let fileSource = getLoadedFileSource(ci.file) {
|
||||
if case .image = ci.content.msgContent, let image = getLoadedImage(ci.file) {
|
||||
if image.imageData != nil {
|
||||
menu.append(saveFileAction(fileSource))
|
||||
|
||||
@@ -104,7 +104,7 @@ struct ComposeState {
|
||||
|
||||
var sendEnabled: Bool {
|
||||
switch preview {
|
||||
case let .mediaPreviews(media): return !media.isEmpty
|
||||
case .mediaPreviews: return true
|
||||
case .voicePreview: return voiceMessageRecordingState == .finished
|
||||
case .filePreview: return true
|
||||
default: return !message.isEmpty || liveMessage != nil
|
||||
@@ -384,10 +384,10 @@ struct ComposeView: View {
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showMediaPicker) {
|
||||
LibraryMediaListPicker(addMedia: addMediaContent, selectionLimit: 10, finishedPreprocessing: finishedPreprocessingMediaContent) { itemsSelected in
|
||||
await MainActor.run {
|
||||
showMediaPicker = false
|
||||
if itemsSelected {
|
||||
LibraryMediaListPicker(media: $chosenMedia, selectionLimit: 10) { itemsSelected in
|
||||
showMediaPicker = false
|
||||
if itemsSelected {
|
||||
DispatchQueue.main.async {
|
||||
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 {
|
||||
getMaxFileSize(.xftp)
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ struct AddGroupMembersViewCommon: View {
|
||||
private func rolePicker() -> some View {
|
||||
Picker("New member role", selection: $selectedRole) {
|
||||
ForEach(GroupMemberRole.allCases) { role in
|
||||
if role <= groupInfo.membership.memberRole && role != .author {
|
||||
if role <= groupInfo.membership.memberRole {
|
||||
Text(role.text)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ struct GroupChatInfoView: View {
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@ObservedObject var chat: Chat
|
||||
@Binding var groupInfo: GroupInfo
|
||||
@ObservedObject private var alertManager = AlertManager.shared
|
||||
@State private var alert: GroupChatInfoViewAlert? = nil
|
||||
@State private var groupLink: String?
|
||||
@State private var groupLinkMemberRole: GroupMemberRole = .member
|
||||
|
||||
@@ -188,19 +188,17 @@ struct GroupMemberInfoView: View {
|
||||
// this condition prevents re-setting picker
|
||||
if !justOpened { return }
|
||||
}
|
||||
justOpened = false
|
||||
DispatchQueue.main.async {
|
||||
newRole = member.memberRole
|
||||
do {
|
||||
let (_, stats) = try apiGroupMemberInfo(groupInfo.apiId, member.groupMemberId)
|
||||
let (mem, code) = member.memberActive ? try apiGetGroupMemberCode(groupInfo.apiId, member.groupMemberId) : (member, nil)
|
||||
_ = chatModel.upsertGroupMember(groupInfo, mem)
|
||||
connectionStats = stats
|
||||
connectionCode = code
|
||||
} catch let error {
|
||||
logger.error("apiGroupMemberInfo or apiGetGroupMemberCode error: \(responseError(error))")
|
||||
}
|
||||
newRole = member.memberRole
|
||||
do {
|
||||
let (_, stats) = try apiGroupMemberInfo(groupInfo.apiId, member.groupMemberId)
|
||||
let (mem, code) = member.memberActive ? try apiGetGroupMemberCode(groupInfo.apiId, member.groupMemberId) : (member, nil)
|
||||
_ = chatModel.upsertGroupMember(groupInfo, mem)
|
||||
connectionStats = stats
|
||||
connectionCode = code
|
||||
} catch let error {
|
||||
logger.error("apiGroupMemberInfo or apiGetGroupMemberCode error: \(responseError(error))")
|
||||
}
|
||||
justOpened = false
|
||||
}
|
||||
.onChange(of: newRole) { newRole in
|
||||
if newRole != member.memberRole {
|
||||
|
||||
@@ -103,10 +103,8 @@ struct GroupProfileView: View {
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showImagePicker) {
|
||||
LibraryImagePicker(image: $chosenImage) { _ in
|
||||
await MainActor.run {
|
||||
showImagePicker = false
|
||||
}
|
||||
LibraryImagePicker(image: $chosenImage) {
|
||||
didSelectItem in showImagePicker = false
|
||||
}
|
||||
}
|
||||
.onChange(of: chosenImage) { image in
|
||||
|
||||
@@ -17,7 +17,7 @@ struct ScanCodeView: View {
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
CodeScannerView(codeTypes: [.qr], scanMode: .oncePerCode, completion: processQRCode)
|
||||
CodeScannerView(codeTypes: [.qr], completion: processQRCode)
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.cornerRadius(12)
|
||||
Text("Scan security code from your contact's app.")
|
||||
|
||||
@@ -415,7 +415,7 @@ struct DatabaseView: View {
|
||||
do {
|
||||
try initializeChat(start: true)
|
||||
m.chatDbChanged = false
|
||||
AppChatState.shared.set(.active)
|
||||
appStateGroupDefault.set(.active)
|
||||
} catch let error {
|
||||
fatalError("Error starting chat \(responseError(error))")
|
||||
}
|
||||
@@ -427,7 +427,7 @@ struct DatabaseView: View {
|
||||
m.chatRunning = true
|
||||
ChatReceiver.shared.start()
|
||||
chatLastStartGroupDefault.set(Date.now)
|
||||
AppChatState.shared.set(.active)
|
||||
appStateGroupDefault.set(.active)
|
||||
} catch let error {
|
||||
runChat = false
|
||||
alert = .error(title: "Error starting chat", error: responseError(error))
|
||||
@@ -477,7 +477,7 @@ func stopChatAsync() async throws {
|
||||
try await apiStopChat()
|
||||
ChatReceiver.shared.stop()
|
||||
await MainActor.run { ChatModel.shared.chatRunning = false }
|
||||
AppChatState.shared.set(.stopped)
|
||||
appStateGroupDefault.set(.stopped)
|
||||
}
|
||||
|
||||
func deleteChatAsync() async throws {
|
||||
|
||||
@@ -13,130 +13,112 @@ import SimpleXChat
|
||||
|
||||
struct LibraryImagePicker: View {
|
||||
@Binding var image: UIImage?
|
||||
var didFinishPicking: (_ didSelectImage: Bool) async -> Void
|
||||
@State var mediaAdded = false
|
||||
var didFinishPicking: (_ didSelectItems: Bool) -> Void
|
||||
@State var images: [UploadContent] = []
|
||||
|
||||
var body: some View {
|
||||
LibraryMediaListPicker(addMedia: addMedia, selectionLimit: 1, didFinishPicking: didFinishPicking)
|
||||
}
|
||||
|
||||
private func addMedia(_ content: UploadContent) async {
|
||||
if mediaAdded { return }
|
||||
await MainActor.run {
|
||||
mediaAdded = true
|
||||
image = content.uiImage
|
||||
}
|
||||
LibraryMediaListPicker(media: $images, selectionLimit: 1, didFinishPicking: didFinishPicking)
|
||||
.onChange(of: images) { _ in
|
||||
if let img = images.first {
|
||||
image = img.uiImage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LibraryMediaListPicker: UIViewControllerRepresentable {
|
||||
typealias UIViewControllerType = PHPickerViewController
|
||||
var addMedia: (_ content: UploadContent) async -> Void
|
||||
@Binding var media: [UploadContent]
|
||||
var selectionLimit: Int
|
||||
var finishedPreprocessing: () -> Void = {}
|
||||
var didFinishPicking: (_ didSelectItems: Bool) async -> Void
|
||||
var didFinishPicking: (_ didSelectItems: Bool) -> Void
|
||||
|
||||
class Coordinator: PHPickerViewControllerDelegate {
|
||||
let parent: LibraryMediaListPicker
|
||||
let dispatchQueue = DispatchQueue(label: "chat.simplex.app.LibraryMediaListPicker")
|
||||
var media: [UploadContent] = []
|
||||
var mediaCount: Int = 0
|
||||
|
||||
init(_ parent: LibraryMediaListPicker) {
|
||||
self.parent = parent
|
||||
}
|
||||
|
||||
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
|
||||
Task {
|
||||
await parent.didFinishPicking(!results.isEmpty)
|
||||
if results.isEmpty { return }
|
||||
for r in results {
|
||||
await loadItem(r.itemProvider)
|
||||
}
|
||||
parent.finishedPreprocessing()
|
||||
parent.didFinishPicking(!results.isEmpty)
|
||||
guard !results.isEmpty else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
private func loadItem(_ p: NSItemProvider) async {
|
||||
logger.debug("LibraryMediaListPicker result")
|
||||
if p.hasItemConformingToTypeIdentifier(UTType.movie.identifier) {
|
||||
if let video = await loadVideo(p) {
|
||||
await self.parent.addMedia(video)
|
||||
logger.debug("LibraryMediaListPicker: added video")
|
||||
}
|
||||
} else if p.hasItemConformingToTypeIdentifier(UTType.data.identifier) {
|
||||
if let img = await loadImageData(p) {
|
||||
await self.parent.addMedia(img)
|
||||
logger.debug("LibraryMediaListPicker: added image")
|
||||
}
|
||||
} else if p.canLoadObject(ofClass: UIImage.self) {
|
||||
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)
|
||||
parent.media = []
|
||||
media = []
|
||||
mediaCount = results.count
|
||||
for result in results {
|
||||
logger.log("LibraryMediaListPicker result")
|
||||
let p = result.itemProvider
|
||||
if p.hasItemConformingToTypeIdentifier(UTType.movie.identifier) {
|
||||
p.loadFileRepresentation(forTypeIdentifier: UTType.movie.identifier) { url, error in
|
||||
if let url = url {
|
||||
let tempUrl = URL(fileURLWithPath: getTempFilesDirectory().path + "/" + generateNewFileName("video", url.pathExtension))
|
||||
if ((try? FileManager.default.copyItem(at: url, to: tempUrl)) != nil) {
|
||||
ChatModel.shared.filesToDelete.insert(tempUrl)
|
||||
self.loadVideo(url: tempUrl, error: error)
|
||||
}
|
||||
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) {
|
||||
p.loadFileRepresentation(forTypeIdentifier: type.identifier) { url, err in
|
||||
if let err = err {
|
||||
logger.error("LibraryMediaListPicker loadFileURL error: \(err.localizedDescription)")
|
||||
completion(nil)
|
||||
} else {
|
||||
completion(url)
|
||||
func loadImage(object: Any?, error: Error? = nil) {
|
||||
if let error = error {
|
||||
logger.error("LibraryMediaListPicker: couldn't load image with error: \(error.localizedDescription)")
|
||||
} else if let image = object as? UIImage {
|
||||
media.append(.simpleImage(image: image))
|
||||
logger.log("LibraryMediaListPicker: added image")
|
||||
} 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()
|
||||
try initializeChat(start: true)
|
||||
m.chatDbChanged = false
|
||||
AppChatState.shared.set(.active)
|
||||
appStateGroupDefault.set(.active)
|
||||
if m.currentUser != nil { return }
|
||||
var profile: Profile? = nil
|
||||
if let displayName = displayName, displayName != "" {
|
||||
|
||||
@@ -130,10 +130,8 @@ struct AddGroupView: View {
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showImagePicker) {
|
||||
LibraryImagePicker(image: $chosenImage) { _ in
|
||||
await MainActor.run {
|
||||
showImagePicker = false
|
||||
}
|
||||
LibraryImagePicker(image: $chosenImage) {
|
||||
didSelectItem in showImagePicker = false
|
||||
}
|
||||
}
|
||||
.alert(isPresented: $showInvalidNameAlert) {
|
||||
|
||||
@@ -73,7 +73,6 @@ struct CreateLinkView: View {
|
||||
Task {
|
||||
if let (connReq, pcc) = await apiAddContact(incognito: incognitoGroupDefault.get()) {
|
||||
await MainActor.run {
|
||||
m.updateContactConnection(pcc)
|
||||
connReqInvitation = connReq
|
||||
contactConnection = pcc
|
||||
m.connReqInv = connReq
|
||||
|
||||
@@ -52,9 +52,6 @@ struct NewChatButton: View {
|
||||
func addContactAction() {
|
||||
Task {
|
||||
if let (connReq, pcc) = await apiAddContact(incognito: incognitoGroupDefault.get()) {
|
||||
await MainActor.run {
|
||||
ChatModel.shared.updateContactConnection(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) {
|
||||
Task {
|
||||
if let (connReqType, pcc) = await apiConnect(incognito: incognito, connReq: connectionLink) {
|
||||
await MainActor.run {
|
||||
ChatModel.shared.updateContactConnection(pcc)
|
||||
}
|
||||
if let connReqType = await apiConnect(incognito: incognito, connReq: connectionLink) {
|
||||
let crt: ConnReqType
|
||||
if let plan = connectionPlan {
|
||||
crt = planToConnReqType(plan)
|
||||
|
||||
@@ -11,12 +11,20 @@ import CoreImage.CIFilterBuiltins
|
||||
|
||||
struct MutableQRCode: View {
|
||||
@Binding var uri: String
|
||||
var withLogo: Bool = true
|
||||
var tintColor = UIColor(red: 0.023, green: 0.176, blue: 0.337, alpha: 1)
|
||||
@State private var image: UIImage?
|
||||
|
||||
var body: some View {
|
||||
QRCode(uri: uri, withLogo: withLogo, tintColor: tintColor)
|
||||
.id("simplex-qrcode-view-for-\(uri)")
|
||||
ZStack {
|
||||
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 tintColor = UIColor(red: 0.023, green: 0.176, blue: 0.337, alpha: 1)
|
||||
@State private var image: UIImage? = nil
|
||||
@State private var makeScreenshotFunc: () -> Void = {}
|
||||
@State private var makeScreenshotBinding: () -> Void = {}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
@@ -62,19 +70,18 @@ struct QRCode: View {
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
makeScreenshotFunc = {
|
||||
makeScreenshotBinding = {
|
||||
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)
|
||||
}
|
||||
}
|
||||
.onTapGesture(perform: makeScreenshotFunc)
|
||||
.onTapGesture(perform: makeScreenshotBinding)
|
||||
.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)
|
||||
}
|
||||
|
||||
private func generateImage(_ uri: String, tintColor: UIColor) -> UIImage? {
|
||||
private func generateImage(_ uri: String) -> UIImage? {
|
||||
let context = CIContext()
|
||||
let filter = CIFilter.qrCodeGenerator()
|
||||
filter.message = Data(uri.utf8)
|
||||
if let outputImage = filter.outputImage,
|
||||
let cgImage = context.createCGImage(outputImage, from: outputImage.extent) {
|
||||
return UIImage(cgImage: cgImage).replaceColor(UIColor.black, tintColor)
|
||||
return UIImage(cgImage: cgImage)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ struct ScanToConnectView: View {
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.padding(.vertical)
|
||||
|
||||
CodeScannerView(codeTypes: [.qr], scanMode: .continuous, completion: processQRCode)
|
||||
CodeScannerView(codeTypes: [.qr], completion: processQRCode)
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.cornerRadius(12)
|
||||
|
||||
|
||||
@@ -81,6 +81,11 @@ struct CreateSimpleXAddress: View {
|
||||
DispatchQueue.main.async {
|
||||
m.userAddress = UserContactLink(connReqContact: connReqContact)
|
||||
}
|
||||
if let u = try await apiSetProfileAddress(on: true) {
|
||||
DispatchQueue.main.async {
|
||||
m.updateUser(u)
|
||||
}
|
||||
}
|
||||
await MainActor.run { progressIndicator = false }
|
||||
} catch let error {
|
||||
logger.error("CreateSimpleXAddress create address: \(responseError(error))")
|
||||
@@ -95,7 +100,7 @@ struct CreateSimpleXAddress: View {
|
||||
} label: {
|
||||
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)
|
||||
.font(.footnote)
|
||||
.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
|
||||
|
||||
@@ -16,19 +16,12 @@ struct ConnectDesktopView: View {
|
||||
var viaSettings = false
|
||||
@AppStorage(DEFAULT_DEVICE_NAME_FOR_REMOTE_ACCESS) private var deviceName = UIDevice.current.name
|
||||
@AppStorage(DEFAULT_CONFIRM_REMOTE_SESSIONS) private var confirmRemoteSessions = false
|
||||
@AppStorage(DEFAULT_CONNECT_REMOTE_VIA_MULTICAST) private var connectRemoteViaMulticast = true
|
||||
@AppStorage(DEFAULT_CONNECT_REMOTE_VIA_MULTICAST_AUTO) private var connectRemoteViaMulticastAuto = true
|
||||
@AppStorage(DEFAULT_CONNECT_REMOTE_VIA_MULTICAST) private var connectRemoteViaMulticast = false
|
||||
@AppStorage(DEFAULT_OFFER_REMOTE_MULTICAST) private var offerRemoteMulticast = true
|
||||
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
|
||||
@State private var sessionAddress: String = ""
|
||||
@State private var remoteCtrls: [RemoteCtrlInfo] = []
|
||||
@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 {
|
||||
case unlinkDesktop(rc: RemoteCtrlInfo)
|
||||
@@ -74,14 +67,9 @@ struct ConnectDesktopView: View {
|
||||
|
||||
var viewBody: some View {
|
||||
Group {
|
||||
let discovery = m.remoteCtrlSession?.discovery
|
||||
if discovery == true || (discovery == nil && !showConnectScreen) {
|
||||
searchingDesktopView()
|
||||
} else if let session = m.remoteCtrlSession {
|
||||
if let session = m.remoteCtrlSession {
|
||||
switch session.sessionState {
|
||||
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 .pendingConfirmation(rc_, sessCode):
|
||||
if confirmRemoteSessions || rc_ == nil {
|
||||
@@ -93,35 +81,16 @@ struct ConnectDesktopView: View {
|
||||
}
|
||||
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 {
|
||||
connectDesktopView(showScanner: false)
|
||||
connectDesktopView()
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
setDeviceName(deviceName)
|
||||
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 {
|
||||
if m.remoteCtrlSession != nil {
|
||||
showConnectScreen = false
|
||||
disconnectDesktop()
|
||||
}
|
||||
}
|
||||
@@ -165,14 +134,12 @@ struct ConnectDesktopView: View {
|
||||
.interactiveDismissDisabled(m.activeRemoteCtrl)
|
||||
}
|
||||
|
||||
private func connectDesktopView(showScanner: Bool = true) -> some View {
|
||||
private func connectDesktopView() -> some View {
|
||||
List {
|
||||
Section("This device name") {
|
||||
devicesView()
|
||||
}
|
||||
if showScanner {
|
||||
scanDesctopAddressView()
|
||||
}
|
||||
scanDesctopAddressView()
|
||||
if developerTools {
|
||||
desktopAddressView()
|
||||
}
|
||||
@@ -200,56 +167,6 @@ struct ConnectDesktopView: View {
|
||||
.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 {
|
||||
List {
|
||||
Section("Connected to desktop") {
|
||||
@@ -274,7 +191,7 @@ struct ConnectDesktopView: View {
|
||||
}
|
||||
|
||||
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) {
|
||||
t = t + Text(" ") + Text("(new)").italic()
|
||||
}
|
||||
@@ -282,8 +199,8 @@ struct ConnectDesktopView: View {
|
||||
}
|
||||
|
||||
private func ctrlDeviceVersionText(_ session: RemoteCtrlSession) -> Text {
|
||||
let v = session.ctrlAppInfo?.appVersionRange.maxVersion
|
||||
var t = Text("v\(v ?? "")")
|
||||
let v = session.ctrlAppInfo.appVersionRange.maxVersion
|
||||
var t = Text("v\(v)")
|
||||
if v != session.appVersion {
|
||||
t = t + Text(" ") + Text("(this device v\(session.appVersion))").italic()
|
||||
}
|
||||
@@ -332,7 +249,7 @@ struct ConnectDesktopView: View {
|
||||
|
||||
private func scanDesctopAddressView() -> some View {
|
||||
Section("Scan QR code from desktop") {
|
||||
CodeScannerView(codeTypes: [.qr], scanMode: .oncePerCode, completion: processDesktopQRCode)
|
||||
CodeScannerView(codeTypes: [.qr], completion: processDesktopQRCode)
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.cornerRadius(12)
|
||||
.listRowBackground(Color.clear)
|
||||
@@ -384,10 +301,7 @@ struct ConnectDesktopView: View {
|
||||
|
||||
Section("Linked desktop options") {
|
||||
Toggle("Verify connections", isOn: $confirmRemoteSessions)
|
||||
Toggle("Discover via local network", isOn: $connectRemoteViaMulticast)
|
||||
if connectRemoteViaMulticast {
|
||||
Toggle("Connect automatically", isOn: $connectRemoteViaMulticastAuto)
|
||||
}
|
||||
Toggle("Discover on network", isOn: $connectRemoteViaMulticast).disabled(true)
|
||||
}
|
||||
}
|
||||
.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) {
|
||||
connectDesktop_ {
|
||||
try await connectRemoteCtrl(desktopAddress: addr)
|
||||
}
|
||||
}
|
||||
|
||||
private func connectDesktop_(_ connect: @escaping () async throws -> (RemoteCtrlInfo?, CtrlAppInfo, String)) {
|
||||
Task {
|
||||
do {
|
||||
let (rc_, ctrlAppInfo, v) = try await connect()
|
||||
let (rc_, ctrlAppInfo, v) = try await connectRemoteCtrl(desktopAddress: addr)
|
||||
await MainActor.run {
|
||||
sessionAddress = ""
|
||||
m.remoteCtrlSession = RemoteCtrlSession(
|
||||
@@ -498,11 +380,11 @@ struct ConnectDesktopView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func disconnectButton(_ label: LocalizedStringKey = "Disconnect") -> some View {
|
||||
private func disconnectButton() -> some View {
|
||||
Button {
|
||||
disconnectDesktop(.dismiss)
|
||||
disconnectDesktop()
|
||||
} label: {
|
||||
Label(label, systemImage: "multiply")
|
||||
Label("Disconnect", systemImage: "multiply")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,11 +393,7 @@ struct ConnectDesktopView: View {
|
||||
do {
|
||||
try await stopRemoteCtrl()
|
||||
await MainActor.run {
|
||||
if case .connected = m.remoteCtrlSession?.sessionState {
|
||||
switchToLocalSession()
|
||||
} else {
|
||||
m.remoteCtrlSession = nil
|
||||
}
|
||||
switchToLocalSession()
|
||||
switch action {
|
||||
case .back: dismiss()
|
||||
case .dismiss: dismiss()
|
||||
|
||||
@@ -51,9 +51,9 @@ struct AdvancedNetworkSettings: View {
|
||||
}
|
||||
.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("Protocol timeout", selection: $netCfg.tcpTimeout, values: [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("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: [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, 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)
|
||||
intSettingPicker("PING count", selection: $netCfg.smpPingCount, values: [1, 2, 3, 5, 8], label: "")
|
||||
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 showAlert: NotificationAlert?
|
||||
@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 {
|
||||
List {
|
||||
@@ -85,6 +88,13 @@ struct NotificationsView: View {
|
||||
.padding(.top, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// if developerTools {
|
||||
// Section(String("Experimental")) {
|
||||
// Toggle(String("Always enable local"), isOn: $ntfEnableLocal)
|
||||
// Toggle(String("Always enable periodic"), isOn: $ntfEnablePeriodic)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
.disabled(legacyDatabase)
|
||||
}
|
||||
@@ -109,7 +119,7 @@ struct NotificationsView: View {
|
||||
|
||||
private func ntfModeAlertTitle(_ mode: NotificationsMode) -> LocalizedStringKey {
|
||||
switch mode {
|
||||
case .off: return "Use only local notifications?"
|
||||
case .off: return "Turn off notifications?"
|
||||
case .periodic: return "Enable periodic notifications?"
|
||||
case .instant: return "Enable instant notifications?"
|
||||
}
|
||||
|
||||
@@ -467,7 +467,6 @@ struct SimplexLockView: View {
|
||||
switch a {
|
||||
case .enableAuth:
|
||||
SetAppPasscodeView {
|
||||
m.contentViewAccessAuthenticated = true
|
||||
laLockDelay = 30
|
||||
prefPerformLA = true
|
||||
showChangePassword = true
|
||||
@@ -620,7 +619,6 @@ struct SimplexLockView: View {
|
||||
authenticate(reason: NSLocalizedString("Enable SimpleX Lock", comment: "authentication reason")) { laResult in
|
||||
switch laResult {
|
||||
case .success:
|
||||
m.contentViewAccessAuthenticated = true
|
||||
prefPerformLA = true
|
||||
laAlert = .laTurnedOnAlert
|
||||
case .failed:
|
||||
|
||||
@@ -21,7 +21,7 @@ struct ScanProtocolServer: View {
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
.padding(.vertical)
|
||||
CodeScannerView(codeTypes: [.qr], scanMode: .oncePerCode, completion: processQRCode)
|
||||
CodeScannerView(codeTypes: [.qr], completion: processQRCode)
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.cornerRadius(12)
|
||||
.padding(.top)
|
||||
|
||||
@@ -56,7 +56,7 @@ let DEFAULT_SHOW_UNREAD_AND_FAVORITES = "showUnreadAndFavorites"
|
||||
let DEFAULT_DEVICE_NAME_FOR_REMOTE_ACCESS = "deviceNameForRemoteAccess"
|
||||
let DEFAULT_CONFIRM_REMOTE_SESSIONS = "confirmRemoteSessions"
|
||||
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] = [
|
||||
DEFAULT_SHOW_LA_NOTICE: false,
|
||||
@@ -91,8 +91,8 @@ let appDefaults: [String: Any] = [
|
||||
DEFAULT_CUSTOM_DISAPPEARING_MESSAGE_TIME: 300,
|
||||
DEFAULT_SHOW_UNREAD_AND_FAVORITES: false,
|
||||
DEFAULT_CONFIRM_REMOTE_SESSIONS: false,
|
||||
DEFAULT_CONNECT_REMOTE_VIA_MULTICAST: true,
|
||||
DEFAULT_CONNECT_REMOTE_VIA_MULTICAST_AUTO: true,
|
||||
DEFAULT_CONNECT_REMOTE_VIA_MULTICAST: false,
|
||||
DEFAULT_OFFER_REMOTE_MULTICAST: true
|
||||
]
|
||||
|
||||
enum SimpleXLinkMode: String, Identifiable {
|
||||
|
||||
@@ -190,8 +190,7 @@ struct UserAddressView: View {
|
||||
|
||||
@ViewBuilder private func existingAddressView(_ userAddress: UserContactLink) -> some View {
|
||||
Section {
|
||||
SimpleXLinkQRCode(uri: userAddress.connReqContact)
|
||||
.id("simplex-contact-address-qrcode-\(userAddress.connReqContact)")
|
||||
MutableQRCode(uri: Binding.constant(simplexChatLink(userAddress.connReqContact)))
|
||||
shareQRCodeButton(userAddress)
|
||||
if MFMailComposeViewController.canSendMail() {
|
||||
shareViaEmailButton(userAddress)
|
||||
|
||||
@@ -120,10 +120,8 @@ struct UserProfile: View {
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showImagePicker) {
|
||||
LibraryImagePicker(image: $chosenImage) { _ in
|
||||
await MainActor.run {
|
||||
showImagePicker = false
|
||||
}
|
||||
LibraryImagePicker(image: $chosenImage) {
|
||||
didSelectItem in showImagePicker = false
|
||||
}
|
||||
}
|
||||
.onChange(of: chosenImage) { image in
|
||||
|
||||
@@ -386,12 +386,6 @@
|
||||
- и още!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="- optionally notify deleted contacts. - profile names with spaces. - 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. - custom time to disappear. - editing history." xml:space="preserve">
|
||||
<source>- voice messages up to 5 minutes.
|
||||
- custom time to disappear.
|
||||
@@ -886,10 +880,6 @@
|
||||
<target>Лош хеш на съобщението</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Better messages</source>
|
||||
<target>По-добри съобщения</target>
|
||||
@@ -899,10 +889,6 @@
|
||||
<source>Block</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Block member</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -1172,10 +1158,6 @@
|
||||
<target>Свързване</target>
|
||||
<note>server test step</note>
|
||||
</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">
|
||||
<source>Connect incognito</source>
|
||||
<target>Свързване инкогнито</target>
|
||||
@@ -1347,10 +1329,6 @@ This is your own one-time link!</source>
|
||||
<target>Създай SimpleX адрес</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Create an address to let people connect with you.</source>
|
||||
<target>Създайте адрес, за да позволите на хората да се свързват с вас.</target>
|
||||
@@ -1866,8 +1844,8 @@ This cannot be undone!</source>
|
||||
<target>Открийте и се присъединете към групи</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Discover via local network" xml:space="preserve">
|
||||
<source>Discover via local network</source>
|
||||
<trans-unit id="Discover on network" xml:space="preserve">
|
||||
<source>Discover on network</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
|
||||
@@ -2404,10 +2382,6 @@ This cannot be undone!</source>
|
||||
<target>Бързо и без чакане, докато подателят е онлайн!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Favorite</source>
|
||||
<target>Любим</target>
|
||||
@@ -2503,10 +2477,6 @@ This cannot be undone!</source>
|
||||
<target>За конзолата</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>French interface</source>
|
||||
<target>Френски интерфейс</target>
|
||||
@@ -2829,10 +2799,6 @@ This cannot be undone!</source>
|
||||
<target>Инкогнито</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Incognito mode</source>
|
||||
<target>Режим инкогнито</target>
|
||||
@@ -3096,10 +3062,6 @@ This is your link for group %@!</source>
|
||||
<target>Ограничения</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Linked desktop options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -3457,10 +3419,6 @@ This is your link for group %@!</source>
|
||||
<target>Няма получени или изпратени файлове</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Notifications</source>
|
||||
<target>Известия</target>
|
||||
@@ -5035,10 +4993,6 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
<target>За да се свърже, вашият контакт може да сканира QR код или да използва линка в приложението.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>To make a new connection</source>
|
||||
<target>За да направите нова връзка</target>
|
||||
@@ -5354,10 +5308,6 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Чрез браузър</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Video call</source>
|
||||
<target>Видео разговор</target>
|
||||
@@ -5408,10 +5358,6 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Гласово съобщение…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Waiting for file</source>
|
||||
<target>Изчаква се получаването на файла</target>
|
||||
@@ -5942,10 +5888,6 @@ SimpleX сървърите не могат да видят вашия профи
|
||||
<target>аудио разговор (не е e2e криптиран)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>bad message ID</source>
|
||||
<target>лошо ID на съобщението</target>
|
||||
|
||||
@@ -386,12 +386,6 @@
|
||||
- a více!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="- optionally notify deleted contacts. - profile names with spaces. - 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. - custom time to disappear. - editing history." xml:space="preserve">
|
||||
<source>- voice messages up to 5 minutes.
|
||||
- custom time to disappear.
|
||||
@@ -886,10 +880,6 @@
|
||||
<target>Špatný hash zprávy</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Better messages</source>
|
||||
<target>Lepší zprávy</target>
|
||||
@@ -899,10 +889,6 @@
|
||||
<source>Block</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Block member</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -1172,10 +1158,6 @@
|
||||
<target>Připojit</target>
|
||||
<note>server test step</note>
|
||||
</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">
|
||||
<source>Connect incognito</source>
|
||||
<target>Spojit se inkognito</target>
|
||||
@@ -1347,10 +1329,6 @@ This is your own one-time link!</source>
|
||||
<target>Vytvořit SimpleX adresu</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Create an address to let people connect with you.</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Discover via local network" xml:space="preserve">
|
||||
<source>Discover via local network</source>
|
||||
<trans-unit id="Discover on network" xml:space="preserve">
|
||||
<source>Discover on network</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Favorite</source>
|
||||
<target>Oblíbené</target>
|
||||
@@ -2503,10 +2477,6 @@ This cannot be undone!</source>
|
||||
<target>Pro konzoli</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>French interface</source>
|
||||
<target>Francouzské rozhraní</target>
|
||||
@@ -2829,10 +2799,6 @@ This cannot be undone!</source>
|
||||
<target>Inkognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Incognito mode</source>
|
||||
<target>Režim inkognito</target>
|
||||
@@ -3096,10 +3062,6 @@ This is your link for group %@!</source>
|
||||
<target>Omezení</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Linked desktop options</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Notifications</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>To make a new connection</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Video call</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Waiting for file</source>
|
||||
<target>Čekání na soubor</target>
|
||||
@@ -5942,10 +5888,6 @@ Servery SimpleX nevidí váš profil.</target>
|
||||
<target>zvukový hovor (nešifrovaný e2e)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>bad message ID</source>
|
||||
<target>špatné ID zprávy</target>
|
||||
|
||||
@@ -299,12 +299,10 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="(new)" xml:space="preserve">
|
||||
<source>(new)</source>
|
||||
<target>(Neu)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(this device v%@)" xml:space="preserve">
|
||||
<source>(this device v%@)</source>
|
||||
<target>(Dieses Gerät hat v%@)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=")" xml:space="preserve">
|
||||
@@ -395,15 +393,6 @@
|
||||
- und mehr!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="- optionally notify deleted contacts. - profile names with spaces. - 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. - custom time to disappear. - editing history." xml:space="preserve">
|
||||
<source>- voice messages up to 5 minutes.
|
||||
- custom time to disappear.
|
||||
@@ -890,7 +879,6 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Bad desktop address" xml:space="preserve">
|
||||
<source>Bad desktop address</source>
|
||||
<target>Falsche Desktop-Adresse</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Bad message ID" xml:space="preserve">
|
||||
@@ -903,11 +891,6 @@
|
||||
<target>Ungültiger Nachrichten-Hash</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Better messages</source>
|
||||
<target>Verbesserungen bei Nachrichten</target>
|
||||
@@ -918,11 +901,6 @@
|
||||
<target>Blockieren</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>Gruppenmitglieder blockieren</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Block member" xml:space="preserve">
|
||||
<source>Block member</source>
|
||||
<target>Mitglied blockieren</target>
|
||||
@@ -1194,11 +1172,6 @@
|
||||
<target>Verbinden</target>
|
||||
<note>server test step</note>
|
||||
</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">
|
||||
<source>Connect incognito</source>
|
||||
<target>Inkognito verbinden</target>
|
||||
@@ -1206,7 +1179,6 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect to desktop" xml:space="preserve">
|
||||
<source>Connect to desktop</source>
|
||||
<target>Mit dem Desktop verbinden</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect to yourself?" xml:space="preserve">
|
||||
@@ -1255,12 +1227,10 @@ Das ist Ihr eigener Einmal-Link!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connected desktop" xml:space="preserve">
|
||||
<source>Connected desktop</source>
|
||||
<target>Verbundener Desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connected to desktop" xml:space="preserve">
|
||||
<source>Connected to desktop</source>
|
||||
<target>Mit dem Desktop verbunden</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connecting server…" xml:space="preserve">
|
||||
@@ -1275,7 +1245,6 @@ Das ist Ihr eigener Einmal-Link!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connecting to desktop" xml:space="preserve">
|
||||
<source>Connecting to desktop</source>
|
||||
<target>Mit dem Desktop verbinden</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection" xml:space="preserve">
|
||||
@@ -1300,7 +1269,6 @@ Das ist Ihr eigener Einmal-Link!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection terminated" xml:space="preserve">
|
||||
<source>Connection terminated</source>
|
||||
<target>Verbindung beendet</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection timeout" xml:space="preserve">
|
||||
@@ -1383,11 +1351,6 @@ Das ist Ihr eigener Einmal-Link!</target>
|
||||
<target>SimpleX-Adresse erstellen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<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>
|
||||
@@ -1800,17 +1763,14 @@ Das kann nicht rückgängig gemacht werden!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Desktop address" xml:space="preserve">
|
||||
<source>Desktop address</source>
|
||||
<target>Desktop-Adresse</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target>Desktop App-Version %@ ist mit dieser App nicht kompatibel.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Desktop devices" xml:space="preserve">
|
||||
<source>Desktop devices</source>
|
||||
<target>Desktop-Geräte</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Develop" xml:space="preserve">
|
||||
@@ -1905,7 +1865,6 @@ Das kann nicht rückgängig gemacht werden!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Disconnect desktop?" xml:space="preserve">
|
||||
<source>Disconnect desktop?</source>
|
||||
<target>Desktop-Verbindung trennen?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Discover via local network" xml:space="preserve">
|
||||
<source>Discover via local network</source>
|
||||
<target>Lokales Netzwerk durchsuchen</target>
|
||||
<trans-unit id="Discover on network" xml:space="preserve">
|
||||
<source>Discover on network</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Encryption re-negotiation error" xml:space="preserve">
|
||||
<source>Encryption re-negotiation error</source>
|
||||
<target>Fehler bei der Neuverhandlung der Verschlüsselung</target>
|
||||
<note>message decrypt error item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encryption re-negotiation failed." xml:space="preserve">
|
||||
<source>Encryption re-negotiation failed.</source>
|
||||
<target>Neuverhandlung der Verschlüsselung fehlgeschlagen.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Enter this device name…" xml:space="preserve">
|
||||
<source>Enter this device name…</source>
|
||||
<target>Geben Sie diesen Gerätenamen ein…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Favorite</source>
|
||||
<target>Favorit</target>
|
||||
@@ -2558,11 +2508,6 @@ Das kann nicht rückgängig gemacht werden!</target>
|
||||
<target>Für Konsole</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>French interface</source>
|
||||
<target>Französische Bedienoberfläche</target>
|
||||
@@ -2888,11 +2833,6 @@ Das kann nicht rückgängig gemacht werden!</target>
|
||||
<target>Inkognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Incognito mode</source>
|
||||
<target>Inkognito-Modus</target>
|
||||
@@ -2925,7 +2865,6 @@ Das kann nicht rückgängig gemacht werden!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Incompatible version" xml:space="preserve">
|
||||
<source>Incompatible version</source>
|
||||
<target>Inkompatible Version</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Keep the app open to use it from desktop" xml:space="preserve">
|
||||
<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>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Linked desktop options</source>
|
||||
<target>Verknüpfte Desktop-Optionen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Linked desktops" xml:space="preserve">
|
||||
<source>Linked desktops</source>
|
||||
<target>Verknüpfte Desktops</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Notifications</source>
|
||||
<target>Benachrichtigungen</target>
|
||||
@@ -3754,7 +3680,6 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<target>Desktop-Adresse einfügen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Scan QR code from desktop" xml:space="preserve">
|
||||
<source>Scan QR code from desktop</source>
|
||||
<target>Den QR-Code vom Desktop scannen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Session code" xml:space="preserve">
|
||||
<source>Session code</source>
|
||||
<target>Sitzungscode</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="This device name" xml:space="preserve">
|
||||
<source>This device name</source>
|
||||
<target>Dieser Gerätename</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>To make a new connection</source>
|
||||
<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 id="Unlink" xml:space="preserve">
|
||||
<source>Unlink</source>
|
||||
<target>Entkoppeln</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unlink desktop?" xml:space="preserve">
|
||||
<source>Unlink desktop?</source>
|
||||
<target>Desktop entkoppeln?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Use from desktop" xml:space="preserve">
|
||||
<source>Use from desktop</source>
|
||||
<target>Vom Desktop aus nutzen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Verify code with desktop" xml:space="preserve">
|
||||
<source>Verify code with desktop</source>
|
||||
<target>Code mit dem Desktop überprüfen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Verify connection" xml:space="preserve">
|
||||
<source>Verify connection</source>
|
||||
<target>Verbindung überprüfen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Verify connections" xml:space="preserve">
|
||||
<source>Verify connections</source>
|
||||
<target>Verbindungen überprüfen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Video call</source>
|
||||
<target>Videoanruf</target>
|
||||
@@ -5503,11 +5409,6 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
|
||||
<target>Sprachnachrichten…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Waiting for file</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>bad message ID</source>
|
||||
<target>Ungültige Nachrichten-ID</target>
|
||||
@@ -6642,7 +6538,6 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="v%@" xml:space="preserve">
|
||||
<source>v%@</source>
|
||||
<target>v%@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="v%@ (%@)" xml:space="preserve">
|
||||
@@ -6784,7 +6679,6 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<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>
|
||||
</trans-unit>
|
||||
<trans-unit id="NSMicrophoneUsageDescription" xml:space="preserve">
|
||||
|
||||
@@ -392,15 +392,6 @@
|
||||
- and more!</source>
|
||||
<target>- more stable message delivery.
|
||||
- a bit better groups.
|
||||
- and more!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="- optionally notify deleted contacts. - profile names with spaces. - 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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
@@ -903,11 +894,6 @@
|
||||
<target>Bad message hash</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Better messages</source>
|
||||
<target>Better messages</target>
|
||||
@@ -918,11 +904,6 @@
|
||||
<target>Block</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>Block group members</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Block member" xml:space="preserve">
|
||||
<source>Block member</source>
|
||||
<target>Block member</target>
|
||||
@@ -1194,11 +1175,6 @@
|
||||
<target>Connect</target>
|
||||
<note>server test step</note>
|
||||
</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">
|
||||
<source>Connect incognito</source>
|
||||
<target>Connect incognito</target>
|
||||
@@ -1383,11 +1359,6 @@ This is your own one-time link!</target>
|
||||
<target>Create SimpleX address</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Create an address to let people connect with you.</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Discover via local network" xml:space="preserve">
|
||||
<source>Discover via local network</source>
|
||||
<target>Discover via local network</target>
|
||||
<trans-unit id="Discover on network" xml:space="preserve">
|
||||
<source>Discover on network</source>
|
||||
<target>Discover on network</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Favorite</source>
|
||||
<target>Favorite</target>
|
||||
@@ -2558,11 +2524,6 @@ This cannot be undone!</target>
|
||||
<target>For console</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>French interface</source>
|
||||
<target>French interface</target>
|
||||
@@ -2888,11 +2849,6 @@ This cannot be undone!</target>
|
||||
<target>Incognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Incognito mode</source>
|
||||
<target>Incognito mode</target>
|
||||
@@ -3163,11 +3119,6 @@ This is your link for group %@!</target>
|
||||
<target>Limitations</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Linked desktop options</source>
|
||||
<target>Linked desktop options</target>
|
||||
@@ -3528,11 +3479,6 @@ This is your link for group %@!</target>
|
||||
<target>No received or sent files</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Notifications</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>To make a new connection</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Video call</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Waiting for file</source>
|
||||
<target>Waiting for file</target>
|
||||
@@ -6053,11 +5984,6 @@ SimpleX servers cannot see your profile.</target>
|
||||
<target>audio call (not e2e encrypted)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>bad message ID</source>
|
||||
<target>bad message ID</target>
|
||||
|
||||
@@ -386,12 +386,6 @@
|
||||
- ¡y más!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="- optionally notify deleted contacts. - profile names with spaces. - 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. - custom time to disappear. - editing history." xml:space="preserve">
|
||||
<source>- voice messages up to 5 minutes.
|
||||
- custom time to disappear.
|
||||
@@ -886,10 +880,6 @@
|
||||
<target>Hash de mensaje incorrecto</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Better messages</source>
|
||||
<target>Mensajes mejorados</target>
|
||||
@@ -899,10 +889,6 @@
|
||||
<source>Block</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Block member</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -1172,10 +1158,6 @@
|
||||
<target>Conectar</target>
|
||||
<note>server test step</note>
|
||||
</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">
|
||||
<source>Connect incognito</source>
|
||||
<target>Conectar incognito</target>
|
||||
@@ -1347,10 +1329,6 @@ This is your own one-time link!</source>
|
||||
<target>Crear tu dirección SimpleX</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Create an address to let people connect with you.</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Discover via local network" xml:space="preserve">
|
||||
<source>Discover via local network</source>
|
||||
<trans-unit id="Discover on network" xml:space="preserve">
|
||||
<source>Discover on network</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Favorite</source>
|
||||
<target>Favoritos</target>
|
||||
@@ -2503,10 +2477,6 @@ This cannot be undone!</source>
|
||||
<target>Para consola</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>French interface</source>
|
||||
<target>Interfaz en francés</target>
|
||||
@@ -2829,10 +2799,6 @@ This cannot be undone!</source>
|
||||
<target>Incógnito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Incognito mode</source>
|
||||
<target>Modo incógnito</target>
|
||||
@@ -3096,10 +3062,6 @@ This is your link for group %@!</source>
|
||||
<target>Limitaciones</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Linked desktop options</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Notifications</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>To make a new connection</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Video call</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Waiting for file</source>
|
||||
<target>Esperando archivo</target>
|
||||
@@ -5943,10 +5889,6 @@ Los servidores de SimpleX no pueden ver tu perfil.</target>
|
||||
<target>llamada (sin cifrar)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>bad message ID</source>
|
||||
<target>ID de mensaje erróneo</target>
|
||||
|
||||
@@ -383,12 +383,6 @@
|
||||
- ja paljon muuta!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="- optionally notify deleted contacts. - profile names with spaces. - 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. - custom time to disappear. - editing history." xml:space="preserve">
|
||||
<source>- voice messages up to 5 minutes.
|
||||
- custom time to disappear.
|
||||
@@ -882,10 +876,6 @@
|
||||
<target>Virheellinen viestin tarkiste</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Better messages</source>
|
||||
<target>Parempia viestejä</target>
|
||||
@@ -895,10 +885,6 @@
|
||||
<source>Block</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Block member</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -1167,10 +1153,6 @@
|
||||
<target>Yhdistä</target>
|
||||
<note>server test step</note>
|
||||
</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">
|
||||
<source>Connect incognito</source>
|
||||
<target>Yhdistä Incognito</target>
|
||||
@@ -1342,10 +1324,6 @@ This is your own one-time link!</source>
|
||||
<target>Luo SimpleX-osoite</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Create an address to let people connect with you.</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Discover via local network" xml:space="preserve">
|
||||
<source>Discover via local network</source>
|
||||
<trans-unit id="Discover on network" xml:space="preserve">
|
||||
<source>Discover on network</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Favorite</source>
|
||||
<target>Suosikki</target>
|
||||
@@ -2495,10 +2469,6 @@ This cannot be undone!</source>
|
||||
<target>Konsoliin</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>French interface</source>
|
||||
<target>Ranskalainen käyttöliittymä</target>
|
||||
@@ -2821,10 +2791,6 @@ This cannot be undone!</source>
|
||||
<target>Incognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Incognito mode</source>
|
||||
<target>Incognito-tila</target>
|
||||
@@ -3088,10 +3054,6 @@ This is your link for group %@!</source>
|
||||
<target>Rajoitukset</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Linked desktop options</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Notifications</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>To make a new connection</source>
|
||||
<target>Uuden yhteyden luominen</target>
|
||||
@@ -5341,10 +5295,6 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
|
||||
<target>Selaimella</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Video call</source>
|
||||
<target>Videopuhelu</target>
|
||||
@@ -5395,10 +5345,6 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
|
||||
<target>Ääniviesti…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Waiting for file</source>
|
||||
<target>Odottaa tiedostoa</target>
|
||||
@@ -5929,10 +5875,6 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
|
||||
<target>äänipuhelu (ei e2e-salattu)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>bad message ID</source>
|
||||
<target>virheellinen viestin tunniste</target>
|
||||
|
||||
@@ -299,12 +299,10 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="(new)" xml:space="preserve">
|
||||
<source>(new)</source>
|
||||
<target>(nouveau)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(this device v%@)" xml:space="preserve">
|
||||
<source>(this device v%@)</source>
|
||||
<target>(cet appareil v%@)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=")" xml:space="preserve">
|
||||
@@ -395,15 +393,6 @@
|
||||
- et bien d'autres choses encore !</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="- optionally notify deleted contacts. - profile names with spaces. - 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. - custom time to disappear. - editing history." xml:space="preserve">
|
||||
<source>- voice messages up to 5 minutes.
|
||||
- custom time to disappear.
|
||||
@@ -890,7 +879,6 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Bad desktop address" xml:space="preserve">
|
||||
<source>Bad desktop address</source>
|
||||
<target>Mauvaise adresse de bureau</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Bad message ID" xml:space="preserve">
|
||||
@@ -903,11 +891,6 @@
|
||||
<target>Mauvais hash de message</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Better messages</source>
|
||||
<target>Meilleurs messages</target>
|
||||
@@ -918,11 +901,6 @@
|
||||
<target>Bloquer</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>Bloquer des membres d'un groupe</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Block member" xml:space="preserve">
|
||||
<source>Block member</source>
|
||||
<target>Bloquer ce membre</target>
|
||||
@@ -1194,11 +1172,6 @@
|
||||
<target>Se connecter</target>
|
||||
<note>server test step</note>
|
||||
</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">
|
||||
<source>Connect incognito</source>
|
||||
<target>Se connecter incognito</target>
|
||||
@@ -1206,7 +1179,6 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect to desktop" xml:space="preserve">
|
||||
<source>Connect to desktop</source>
|
||||
<target>Se connecter au bureau</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Connected desktop" xml:space="preserve">
|
||||
<source>Connected desktop</source>
|
||||
<target>Bureau connecté</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connected to desktop" xml:space="preserve">
|
||||
<source>Connected to desktop</source>
|
||||
<target>Connecté au bureau</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Connecting to desktop" xml:space="preserve">
|
||||
<source>Connecting to desktop</source>
|
||||
<target>Connexion au bureau</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection" xml:space="preserve">
|
||||
@@ -1300,7 +1269,6 @@ Il s'agit de votre propre lien unique !</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection terminated" xml:space="preserve">
|
||||
<source>Connection terminated</source>
|
||||
<target>Connexion terminée</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Create an address to let people connect with you.</source>
|
||||
<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 id="Desktop address" xml:space="preserve">
|
||||
<source>Desktop address</source>
|
||||
<target>Adresse de bureau</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target>La version de l'application de bureau %@ n'est pas compatible avec cette application.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Desktop devices" xml:space="preserve">
|
||||
<source>Desktop devices</source>
|
||||
<target>Appareils de bureau</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Develop" xml:space="preserve">
|
||||
@@ -1905,7 +1865,6 @@ Cette opération ne peut être annulée !</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Disconnect desktop?" xml:space="preserve">
|
||||
<source>Disconnect desktop?</source>
|
||||
<target>Déconnecter le bureau ?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Discover via local network" xml:space="preserve">
|
||||
<source>Discover via local network</source>
|
||||
<target>Rechercher sur le réseau</target>
|
||||
<trans-unit id="Discover on network" xml:space="preserve">
|
||||
<source>Discover on network</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Encryption re-negotiation error" xml:space="preserve">
|
||||
<source>Encryption re-negotiation error</source>
|
||||
<target>Erreur lors de la renégociation du chiffrement</target>
|
||||
<note>message decrypt error item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encryption re-negotiation failed." xml:space="preserve">
|
||||
<source>Encryption re-negotiation failed.</source>
|
||||
<target>La renégociation du chiffrement a échoué.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Enter this device name…" xml:space="preserve">
|
||||
<source>Enter this device name…</source>
|
||||
<target>Entrez le nom de l'appareil…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Favorite</source>
|
||||
<target>Favoris</target>
|
||||
@@ -2558,11 +2508,6 @@ Cette opération ne peut être annulée !</target>
|
||||
<target>Pour la console</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>French interface</source>
|
||||
<target>Interface en français</target>
|
||||
@@ -2888,11 +2833,6 @@ Cette opération ne peut être annulée !</target>
|
||||
<target>Incognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Incognito mode</source>
|
||||
<target>Mode Incognito</target>
|
||||
@@ -2925,7 +2865,6 @@ Cette opération ne peut être annulée !</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Incompatible version" xml:space="preserve">
|
||||
<source>Incompatible version</source>
|
||||
<target>Version incompatible</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Incorrect passcode" xml:space="preserve">
|
||||
@@ -3100,7 +3039,6 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target>Garder l'application ouverte pour l'utiliser depuis le bureau</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
@@ -3163,19 +3101,12 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
<target>Limitations</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Linked desktop options</source>
|
||||
<target>Options de bureau lié</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Linked desktops" xml:space="preserve">
|
||||
<source>Linked desktops</source>
|
||||
<target>Bureaux liés</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Notifications</source>
|
||||
<target>Notifications</target>
|
||||
@@ -3754,7 +3680,6 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<target>Coller l'adresse du bureau</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste image" xml:space="preserve">
|
||||
@@ -4354,7 +4279,6 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Scan QR code from desktop" xml:space="preserve">
|
||||
<source>Scan QR code from desktop</source>
|
||||
<target>Scanner le code QR du bureau</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Scan code" xml:space="preserve">
|
||||
@@ -4579,7 +4503,6 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Session code" xml:space="preserve">
|
||||
<source>Session code</source>
|
||||
<target>Code de session</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="This device name" xml:space="preserve">
|
||||
<source>This device name</source>
|
||||
<target>Ce nom d'appareil</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>To make a new connection</source>
|
||||
<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 id="Unlink" xml:space="preserve">
|
||||
<source>Unlink</source>
|
||||
<target>Délier</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unlink desktop?" xml:space="preserve">
|
||||
<source>Unlink desktop?</source>
|
||||
<target>Délier le bureau ?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Use from desktop" xml:space="preserve">
|
||||
<source>Use from desktop</source>
|
||||
<target>Utilisation depuis le bureau</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Verify code with desktop" xml:space="preserve">
|
||||
<source>Verify code with desktop</source>
|
||||
<target>Vérifier le code avec le bureau</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Verify connection" xml:space="preserve">
|
||||
<source>Verify connection</source>
|
||||
<target>Vérifier la connexion</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Verify connections" xml:space="preserve">
|
||||
<source>Verify connections</source>
|
||||
<target>Vérifier les connexions</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Video call</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Waiting for file</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>bad message ID</source>
|
||||
<target>ID de message incorrecte</target>
|
||||
@@ -6642,7 +6538,6 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="v%@" xml:space="preserve">
|
||||
<source>v%@</source>
|
||||
<target>v%@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 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>
|
||||
<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>
|
||||
</trans-unit>
|
||||
<trans-unit id="NSMicrophoneUsageDescription" xml:space="preserve">
|
||||
|
||||
@@ -299,12 +299,10 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="(new)" xml:space="preserve">
|
||||
<source>(new)</source>
|
||||
<target>(nuovo)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(this device v%@)" xml:space="preserve">
|
||||
<source>(this device v%@)</source>
|
||||
<target>(questo dispositivo v%@)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=")" xml:space="preserve">
|
||||
@@ -395,15 +393,6 @@
|
||||
- e altro ancora!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="- optionally notify deleted contacts. - profile names with spaces. - 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. - custom time to disappear. - editing history." xml:space="preserve">
|
||||
<source>- voice messages up to 5 minutes.
|
||||
- custom time to disappear.
|
||||
@@ -890,7 +879,6 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Bad desktop address" xml:space="preserve">
|
||||
<source>Bad desktop address</source>
|
||||
<target>Indirizzo desktop errato</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Bad message ID" xml:space="preserve">
|
||||
@@ -903,11 +891,6 @@
|
||||
<target>Hash del messaggio errato</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Better messages</source>
|
||||
<target>Messaggi migliorati</target>
|
||||
@@ -918,11 +901,6 @@
|
||||
<target>Blocca</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>Blocca i membri dei gruppi</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Block member" xml:space="preserve">
|
||||
<source>Block member</source>
|
||||
<target>Blocca membro</target>
|
||||
@@ -1194,10 +1172,6 @@
|
||||
<target>Connetti</target>
|
||||
<note>server test step</note>
|
||||
</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">
|
||||
<source>Connect incognito</source>
|
||||
<target>Connetti in incognito</target>
|
||||
@@ -1205,7 +1179,6 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect to desktop" xml:space="preserve">
|
||||
<source>Connect to desktop</source>
|
||||
<target>Connetti al desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect to yourself?" xml:space="preserve">
|
||||
@@ -1254,12 +1227,10 @@ Questo è il tuo link una tantum!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connected desktop" xml:space="preserve">
|
||||
<source>Connected desktop</source>
|
||||
<target>Desktop connesso</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connected to desktop" xml:space="preserve">
|
||||
<source>Connected to desktop</source>
|
||||
<target>Connesso al desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connecting server…" xml:space="preserve">
|
||||
@@ -1274,7 +1245,6 @@ Questo è il tuo link una tantum!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connecting to desktop" xml:space="preserve">
|
||||
<source>Connecting to desktop</source>
|
||||
<target>Connessione al desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection" xml:space="preserve">
|
||||
@@ -1299,7 +1269,6 @@ Questo è il tuo link una tantum!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection terminated" xml:space="preserve">
|
||||
<source>Connection terminated</source>
|
||||
<target>Connessione terminata</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection timeout" xml:space="preserve">
|
||||
@@ -1382,11 +1351,6 @@ Questo è il tuo link una tantum!</target>
|
||||
<target>Crea indirizzo SimpleX</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Create an address to let people connect with you.</source>
|
||||
<target>Crea un indirizzo per consentire alle persone di connettersi con te.</target>
|
||||
@@ -1799,17 +1763,14 @@ Non è reversibile!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Desktop address" xml:space="preserve">
|
||||
<source>Desktop address</source>
|
||||
<target>Indirizzo desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target>La versione dell'app desktop %@ non è compatibile con questa app.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Desktop devices" xml:space="preserve">
|
||||
<source>Desktop devices</source>
|
||||
<target>Dispositivi desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Develop" xml:space="preserve">
|
||||
@@ -1904,7 +1865,6 @@ Non è reversibile!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Disconnect desktop?" xml:space="preserve">
|
||||
<source>Disconnect desktop?</source>
|
||||
<target>Disconnettere il desktop?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Discover and join groups" xml:space="preserve">
|
||||
@@ -1912,8 +1872,8 @@ Non è reversibile!</target>
|
||||
<target>Scopri ed unisciti ai gruppi</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Discover via local network" xml:space="preserve">
|
||||
<source>Discover via local network</source>
|
||||
<trans-unit id="Discover on network" xml:space="preserve">
|
||||
<source>Discover on network</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
|
||||
@@ -2088,12 +2048,10 @@ Non è reversibile!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encryption re-negotiation error" xml:space="preserve">
|
||||
<source>Encryption re-negotiation error</source>
|
||||
<target>Errore di rinegoziazione crittografia</target>
|
||||
<note>message decrypt error item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encryption re-negotiation failed." xml:space="preserve">
|
||||
<source>Encryption re-negotiation failed.</source>
|
||||
<target>Rinegoziazione crittografia fallita.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enter Passcode" xml:space="preserve">
|
||||
@@ -2128,7 +2086,6 @@ Non è reversibile!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enter this device name…" xml:space="preserve">
|
||||
<source>Enter this device name…</source>
|
||||
<target>Inserisci il nome di questo dispositivo…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Favorite</source>
|
||||
<target>Preferito</target>
|
||||
@@ -2556,10 +2508,6 @@ Non è reversibile!</target>
|
||||
<target>Per console</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>French interface</source>
|
||||
<target>Interfaccia francese</target>
|
||||
@@ -2885,11 +2833,6 @@ Non è reversibile!</target>
|
||||
<target>Incognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Incognito mode</source>
|
||||
<target>Modalità incognito</target>
|
||||
@@ -2922,7 +2865,6 @@ Non è reversibile!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Incompatible version" xml:space="preserve">
|
||||
<source>Incompatible version</source>
|
||||
<target>Versione incompatibile</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Incorrect passcode" xml:space="preserve">
|
||||
@@ -3097,7 +3039,6 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target>Tieni aperta l'app per usarla dal desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
@@ -3160,19 +3101,12 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
<target>Limitazioni</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Linked desktop options</source>
|
||||
<target>Opzioni del desktop collegato</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Linked desktops" xml:space="preserve">
|
||||
<source>Linked desktops</source>
|
||||
<target>Desktop collegati</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Notifications</source>
|
||||
<target>Notifiche</target>
|
||||
@@ -3750,7 +3680,6 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<target>Incolla l'indirizzo desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste image" xml:space="preserve">
|
||||
@@ -4350,7 +4279,6 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Scan QR code from desktop" xml:space="preserve">
|
||||
<source>Scan QR code from desktop</source>
|
||||
<target>Scansiona codice QR dal desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Scan code" xml:space="preserve">
|
||||
@@ -4575,7 +4503,6 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Session code" xml:space="preserve">
|
||||
<source>Session code</source>
|
||||
<target>Codice di sessione</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="This device name" xml:space="preserve">
|
||||
<source>This device name</source>
|
||||
<target>Il nome di questo dispositivo</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>To make a new connection</source>
|
||||
<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 id="Unlink" xml:space="preserve">
|
||||
<source>Unlink</source>
|
||||
<target>Scollega</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unlink desktop?" xml:space="preserve">
|
||||
<source>Unlink desktop?</source>
|
||||
<target>Scollegare il desktop?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Use from desktop" xml:space="preserve">
|
||||
<source>Use from desktop</source>
|
||||
<target>Usa dal desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Verify code with desktop" xml:space="preserve">
|
||||
<source>Verify code with desktop</source>
|
||||
<target>Verifica il codice con il desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Verify connection" xml:space="preserve">
|
||||
<source>Verify connection</source>
|
||||
<target>Verifica la connessione</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Verify connections" xml:space="preserve">
|
||||
<source>Verify connections</source>
|
||||
<target>Verifica le connessioni</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Video call</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Waiting for file</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>bad message ID</source>
|
||||
<target>ID messaggio errato</target>
|
||||
@@ -6636,7 +6538,6 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="v%@" xml:space="preserve">
|
||||
<source>v%@</source>
|
||||
<target>v%@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 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>
|
||||
<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>
|
||||
</trans-unit>
|
||||
<trans-unit id="NSMicrophoneUsageDescription" xml:space="preserve">
|
||||
|
||||
@@ -383,12 +383,6 @@
|
||||
- などなど!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="- optionally notify deleted contacts. - profile names with spaces. - 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. - custom time to disappear. - editing history." xml:space="preserve">
|
||||
<source>- voice messages up to 5 minutes.
|
||||
- custom time to disappear.
|
||||
@@ -883,10 +877,6 @@
|
||||
<target>メッセージのハッシュ値問題</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Better messages</source>
|
||||
<target>より良いメッセージ</target>
|
||||
@@ -896,10 +886,6 @@
|
||||
<source>Block</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Block member</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -1169,10 +1155,6 @@
|
||||
<target>接続</target>
|
||||
<note>server test step</note>
|
||||
</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">
|
||||
<source>Connect incognito</source>
|
||||
<target>シークレットモードで接続</target>
|
||||
@@ -1344,10 +1326,6 @@ This is your own one-time link!</source>
|
||||
<target>SimpleXアドレスの作成</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Create an address to let people connect with you.</source>
|
||||
<target>人とつながるためのアドレスを作成する。</target>
|
||||
@@ -1863,8 +1841,8 @@ This cannot be undone!</source>
|
||||
<target>グループを見つけて参加する</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Discover via local network" xml:space="preserve">
|
||||
<source>Discover via local network</source>
|
||||
<trans-unit id="Discover on network" xml:space="preserve">
|
||||
<source>Discover on network</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
|
||||
@@ -2399,10 +2377,6 @@ This cannot be undone!</source>
|
||||
<target>送信者がオンラインになるまでの待ち時間がなく、速い!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Favorite</source>
|
||||
<target>お気に入り</target>
|
||||
@@ -2498,10 +2472,6 @@ This cannot be undone!</source>
|
||||
<target>コンソール</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>French interface</source>
|
||||
<target>フランス語UI</target>
|
||||
@@ -2824,10 +2794,6 @@ This cannot be undone!</source>
|
||||
<target>シークレットモード</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Incognito mode</source>
|
||||
<target>シークレットモード</target>
|
||||
@@ -3091,10 +3057,6 @@ This is your link for group %@!</source>
|
||||
<target>制限事項</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Linked desktop options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -3451,10 +3413,6 @@ This is your link for group %@!</source>
|
||||
<target>送受信済みのファイルがありません</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Notifications</source>
|
||||
<target>通知</target>
|
||||
@@ -5019,10 +4977,6 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
<target>接続するにはQRコードを読み込むか、アプリ内のリンクを使用する必要があります。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>To make a new connection</source>
|
||||
<target>新規に接続する場合</target>
|
||||
@@ -5337,10 +5291,6 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>ブラウザ経由</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Video call</source>
|
||||
<target>ビデオ通話</target>
|
||||
@@ -5391,10 +5341,6 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>音声メッセージ…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Waiting for file</source>
|
||||
<target>ファイル待ち</target>
|
||||
@@ -5925,10 +5871,6 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
|
||||
<target>音声通話 (エンドツーエンド暗号化なし)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>bad message ID</source>
|
||||
<target>メッセージ ID が正しくありません</target>
|
||||
|
||||
@@ -299,12 +299,10 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="(new)" xml:space="preserve">
|
||||
<source>(new)</source>
|
||||
<target>(nieuw)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(this device v%@)" xml:space="preserve">
|
||||
<source>(this device v%@)</source>
|
||||
<target>(dit apparaat v%@)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=")" xml:space="preserve">
|
||||
@@ -392,15 +390,6 @@
|
||||
- and more!</source>
|
||||
<target>- stabielere berichtbezorging.
|
||||
- een beetje betere groepen.
|
||||
- en meer!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="- optionally notify deleted contacts. - profile names with spaces. - 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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
@@ -890,7 +879,6 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Bad desktop address" xml:space="preserve">
|
||||
<source>Bad desktop address</source>
|
||||
<target>Onjuist desktopadres</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Bad message ID" xml:space="preserve">
|
||||
@@ -903,11 +891,6 @@
|
||||
<target>Onjuiste bericht hash</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Better messages</source>
|
||||
<target>Betere berichten</target>
|
||||
@@ -918,11 +901,6 @@
|
||||
<target>Blokkeren</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>Groepsleden blokkeren</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Block member" xml:space="preserve">
|
||||
<source>Block member</source>
|
||||
<target>Lid blokkeren</target>
|
||||
@@ -1194,11 +1172,6 @@
|
||||
<target>Verbind</target>
|
||||
<note>server test step</note>
|
||||
</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">
|
||||
<source>Connect incognito</source>
|
||||
<target>Verbind incognito</target>
|
||||
@@ -1206,7 +1179,6 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect to desktop" xml:space="preserve">
|
||||
<source>Connect to desktop</source>
|
||||
<target>Verbinden met desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect to yourself?" xml:space="preserve">
|
||||
@@ -1255,12 +1227,10 @@ Dit is uw eigen eenmalige link!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connected desktop" xml:space="preserve">
|
||||
<source>Connected desktop</source>
|
||||
<target>Verbonden desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connected to desktop" xml:space="preserve">
|
||||
<source>Connected to desktop</source>
|
||||
<target>Verbonden met desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connecting server…" xml:space="preserve">
|
||||
@@ -1275,7 +1245,6 @@ Dit is uw eigen eenmalige link!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connecting to desktop" xml:space="preserve">
|
||||
<source>Connecting to desktop</source>
|
||||
<target>Verbinding maken met desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection" xml:space="preserve">
|
||||
@@ -1300,7 +1269,6 @@ Dit is uw eigen eenmalige link!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection terminated" xml:space="preserve">
|
||||
<source>Connection terminated</source>
|
||||
<target>Verbinding beëindigd</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Create an address to let people connect with you.</source>
|
||||
<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 id="Desktop address" xml:space="preserve">
|
||||
<source>Desktop address</source>
|
||||
<target>Desktop adres</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target>Desktop-app-versie %@ is niet compatibel met deze app.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Desktop devices" xml:space="preserve">
|
||||
<source>Desktop devices</source>
|
||||
<target>Desktop apparaten</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Develop" xml:space="preserve">
|
||||
@@ -1905,7 +1865,6 @@ Dit kan niet ongedaan gemaakt worden!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Disconnect desktop?" xml:space="preserve">
|
||||
<source>Disconnect desktop?</source>
|
||||
<target>Desktop loskoppelen?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Discover via local network" xml:space="preserve">
|
||||
<source>Discover via local network</source>
|
||||
<target>Ontdek via het lokale netwerk</target>
|
||||
<trans-unit id="Discover on network" xml:space="preserve">
|
||||
<source>Discover on network</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Encryption re-negotiation error" xml:space="preserve">
|
||||
<source>Encryption re-negotiation error</source>
|
||||
<target>Fout bij heronderhandeling van codering</target>
|
||||
<note>message decrypt error item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encryption re-negotiation failed." xml:space="preserve">
|
||||
<source>Encryption re-negotiation failed.</source>
|
||||
<target>Opnieuw onderhandelen over de codering is mislukt.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enter Passcode" xml:space="preserve">
|
||||
@@ -2130,7 +2086,6 @@ Dit kan niet ongedaan gemaakt worden!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enter this device name…" xml:space="preserve">
|
||||
<source>Enter this device name…</source>
|
||||
<target>Voer deze apparaatnaam in…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Favorite</source>
|
||||
<target>Favoriet</target>
|
||||
@@ -2558,11 +2508,6 @@ Dit kan niet ongedaan gemaakt worden!</target>
|
||||
<target>Voor console</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>French interface</source>
|
||||
<target>Franse interface</target>
|
||||
@@ -2888,11 +2833,6 @@ Dit kan niet ongedaan gemaakt worden!</target>
|
||||
<target>Incognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Incognito mode</source>
|
||||
<target>Incognito modus</target>
|
||||
@@ -2925,7 +2865,6 @@ Dit kan niet ongedaan gemaakt worden!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Incompatible version" xml:space="preserve">
|
||||
<source>Incompatible version</source>
|
||||
<target>Incompatibele versie</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Incorrect passcode" xml:space="preserve">
|
||||
@@ -3100,7 +3039,6 @@ Dit is jouw link voor groep %@!</target>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target>Houd de app geopend om deze vanaf de desktop te gebruiken</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
@@ -3163,19 +3101,12 @@ Dit is jouw link voor groep %@!</target>
|
||||
<target>Beperkingen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Linked desktop options</source>
|
||||
<target>Gekoppelde desktop opties</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Linked desktops" xml:space="preserve">
|
||||
<source>Linked desktops</source>
|
||||
<target>Gelinkte desktops</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Notifications</source>
|
||||
<target>Meldingen</target>
|
||||
@@ -3754,7 +3680,6 @@ Dit is jouw link voor groep %@!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<target>Desktopadres plakken</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste image" xml:space="preserve">
|
||||
@@ -4354,7 +4279,6 @@ Dit is jouw link voor groep %@!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Scan QR code from desktop" xml:space="preserve">
|
||||
<source>Scan QR code from desktop</source>
|
||||
<target>Scan QR-code vanaf uw desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Scan code" xml:space="preserve">
|
||||
@@ -4579,7 +4503,6 @@ Dit is jouw link voor groep %@!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Session code" xml:space="preserve">
|
||||
<source>Session code</source>
|
||||
<target>Sessie code</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="This device name" xml:space="preserve">
|
||||
<source>This device name</source>
|
||||
<target>Deze apparaatnaam</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>To make a new connection</source>
|
||||
<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 id="Unlink" xml:space="preserve">
|
||||
<source>Unlink</source>
|
||||
<target>Ontkoppelen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unlink desktop?" xml:space="preserve">
|
||||
<source>Unlink desktop?</source>
|
||||
<target>Desktop ontkoppelen?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Use from desktop" xml:space="preserve">
|
||||
<source>Use from desktop</source>
|
||||
<target>Gebruik vanaf desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Verify code with desktop" xml:space="preserve">
|
||||
<source>Verify code with desktop</source>
|
||||
<target>Code verifiëren met desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Verify connection" xml:space="preserve">
|
||||
<source>Verify connection</source>
|
||||
<target>Controleer de verbinding</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Verify connections" xml:space="preserve">
|
||||
<source>Verify connections</source>
|
||||
<target>Controleer verbindingen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Video call</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Waiting for file</source>
|
||||
<target>Wachten op bestand</target>
|
||||
@@ -6053,11 +5954,6 @@ SimpleX servers kunnen uw profiel niet zien.</target>
|
||||
<target>audio oproep (niet e2e versleuteld)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>bad message ID</source>
|
||||
<target>Onjuiste bericht-ID</target>
|
||||
@@ -6642,7 +6538,6 @@ SimpleX servers kunnen uw profiel niet zien.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="v%@" xml:space="preserve">
|
||||
<source>v%@</source>
|
||||
<target>v%@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="v%@ (%@)" xml:space="preserve">
|
||||
@@ -6784,7 +6679,6 @@ SimpleX servers kunnen uw profiel niet zien.</target>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<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>
|
||||
</trans-unit>
|
||||
<trans-unit id="NSMicrophoneUsageDescription" xml:space="preserve">
|
||||
|
||||
@@ -299,12 +299,10 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="(new)" xml:space="preserve">
|
||||
<source>(new)</source>
|
||||
<target>(nowy)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(this device v%@)" xml:space="preserve">
|
||||
<source>(this device v%@)</source>
|
||||
<target>(to urządzenie v%@)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=")" xml:space="preserve">
|
||||
@@ -395,15 +393,6 @@
|
||||
- i więcej!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="- optionally notify deleted contacts. - profile names with spaces. - 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. - custom time to disappear. - editing history." xml:space="preserve">
|
||||
<source>- voice messages up to 5 minutes.
|
||||
- custom time to disappear.
|
||||
@@ -890,7 +879,6 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Bad desktop address" xml:space="preserve">
|
||||
<source>Bad desktop address</source>
|
||||
<target>Zły adres komputera</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Bad message ID" xml:space="preserve">
|
||||
@@ -903,11 +891,6 @@
|
||||
<target>Zły hash wiadomości</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Better messages</source>
|
||||
<target>Lepsze wiadomości</target>
|
||||
@@ -918,11 +901,6 @@
|
||||
<target>Zablokuj</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>Blokuj członków grupy</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Block member" xml:space="preserve">
|
||||
<source>Block member</source>
|
||||
<target>Zablokuj członka</target>
|
||||
@@ -1194,11 +1172,6 @@
|
||||
<target>Połącz</target>
|
||||
<note>server test step</note>
|
||||
</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">
|
||||
<source>Connect incognito</source>
|
||||
<target>Połącz incognito</target>
|
||||
@@ -1206,7 +1179,6 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect to desktop" xml:space="preserve">
|
||||
<source>Connect to desktop</source>
|
||||
<target>Połącz do komputera</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect to yourself?" xml:space="preserve">
|
||||
@@ -1255,12 +1227,10 @@ To jest twój jednorazowy link!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connected desktop" xml:space="preserve">
|
||||
<source>Connected desktop</source>
|
||||
<target>Połączony komputer</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connected to desktop" xml:space="preserve">
|
||||
<source>Connected to desktop</source>
|
||||
<target>Połączony do komputera</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connecting server…" xml:space="preserve">
|
||||
@@ -1275,7 +1245,6 @@ To jest twój jednorazowy link!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connecting to desktop" xml:space="preserve">
|
||||
<source>Connecting to desktop</source>
|
||||
<target>Łączenie z komputerem</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection" xml:space="preserve">
|
||||
@@ -1300,7 +1269,6 @@ To jest twój jednorazowy link!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection terminated" xml:space="preserve">
|
||||
<source>Connection terminated</source>
|
||||
<target>Połączenie zakończone</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Create an address to let people connect with you.</source>
|
||||
<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 id="Desktop address" xml:space="preserve">
|
||||
<source>Desktop address</source>
|
||||
<target>Adres komputera</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target>Wersja aplikacji komputerowej %@ nie jest kompatybilna z tą aplikacją.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Desktop devices" xml:space="preserve">
|
||||
<source>Desktop devices</source>
|
||||
<target>Urządzenia komputerowe</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Develop" xml:space="preserve">
|
||||
@@ -1905,7 +1865,6 @@ To nie może być cofnięte!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Disconnect desktop?" xml:space="preserve">
|
||||
<source>Disconnect desktop?</source>
|
||||
<target>Rozłączyć komputer?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Discover via local network" xml:space="preserve">
|
||||
<source>Discover via local network</source>
|
||||
<target>Odkryj przez sieć lokalną</target>
|
||||
<trans-unit id="Discover on network" xml:space="preserve">
|
||||
<source>Discover on network</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Encryption re-negotiation error" xml:space="preserve">
|
||||
<source>Encryption re-negotiation error</source>
|
||||
<target>Błąd renegocjacji szyfrowania</target>
|
||||
<note>message decrypt error item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encryption re-negotiation failed." xml:space="preserve">
|
||||
<source>Encryption re-negotiation failed.</source>
|
||||
<target>Renegocjacja szyfrowania nie powiodła się.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enter Passcode" xml:space="preserve">
|
||||
@@ -2130,7 +2086,6 @@ To nie może być cofnięte!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enter this device name…" xml:space="preserve">
|
||||
<source>Enter this device name…</source>
|
||||
<target>Podaj nazwę urządzenia…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Favorite</source>
|
||||
<target>Ulubione</target>
|
||||
@@ -2558,11 +2508,6 @@ To nie może być cofnięte!</target>
|
||||
<target>Dla konsoli</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>French interface</source>
|
||||
<target>Francuski interfejs</target>
|
||||
@@ -2888,11 +2833,6 @@ To nie może być cofnięte!</target>
|
||||
<target>Incognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Incognito mode</source>
|
||||
<target>Tryb incognito</target>
|
||||
@@ -2925,7 +2865,6 @@ To nie może być cofnięte!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Incompatible version" xml:space="preserve">
|
||||
<source>Incompatible version</source>
|
||||
<target>Niekompatybilna wersja</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Incorrect passcode" xml:space="preserve">
|
||||
@@ -3100,7 +3039,6 @@ To jest twój link do grupy %@!</target>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target>Zostaw aplikację otwartą i używaj ją z komputera</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
@@ -3163,19 +3101,12 @@ To jest twój link do grupy %@!</target>
|
||||
<target>Ograniczenia</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Linked desktop options</source>
|
||||
<target>Połączone opcje komputera</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Linked desktops" xml:space="preserve">
|
||||
<source>Linked desktops</source>
|
||||
<target>Połączone komputery</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Notifications</source>
|
||||
<target>Powiadomienia</target>
|
||||
@@ -3754,7 +3680,6 @@ To jest twój link do grupy %@!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<target>Wklej adres komputera</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste image" xml:space="preserve">
|
||||
@@ -4354,7 +4279,6 @@ To jest twój link do grupy %@!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Scan QR code from desktop" xml:space="preserve">
|
||||
<source>Scan QR code from desktop</source>
|
||||
<target>Zeskanuj kod QR z komputera</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Scan code" xml:space="preserve">
|
||||
@@ -4579,7 +4503,6 @@ To jest twój link do grupy %@!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Session code" xml:space="preserve">
|
||||
<source>Session code</source>
|
||||
<target>Kod sesji</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="This device name" xml:space="preserve">
|
||||
<source>This device name</source>
|
||||
<target>Nazwa tego urządzenia</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>To make a new connection</source>
|
||||
<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 id="Unlink" xml:space="preserve">
|
||||
<source>Unlink</source>
|
||||
<target>Odłącz</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unlink desktop?" xml:space="preserve">
|
||||
<source>Unlink desktop?</source>
|
||||
<target>Odłączyć komputer?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Use from desktop" xml:space="preserve">
|
||||
<source>Use from desktop</source>
|
||||
<target>Użyj z komputera</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Verify code with desktop" xml:space="preserve">
|
||||
<source>Verify code with desktop</source>
|
||||
<target>Zweryfikuj kod z komputera</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Verify connection" xml:space="preserve">
|
||||
<source>Verify connection</source>
|
||||
<target>Zweryfikuj połączenie</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Verify connections" xml:space="preserve">
|
||||
<source>Verify connections</source>
|
||||
<target>Zweryfikuj połączenia</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Video call</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Waiting for file</source>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>bad message ID</source>
|
||||
<target>zły identyfikator wiadomości</target>
|
||||
@@ -6642,7 +6538,6 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="v%@" xml:space="preserve">
|
||||
<source>v%@</source>
|
||||
<target>v%@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="v%@ (%@)" xml:space="preserve">
|
||||
@@ -6784,7 +6679,6 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<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>
|
||||
</trans-unit>
|
||||
<trans-unit id="NSMicrophoneUsageDescription" xml:space="preserve">
|
||||
|
||||
@@ -89,7 +89,6 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ and %@" xml:space="preserve">
|
||||
<source>%@ and %@</source>
|
||||
<target>%@ и %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ and %@ connected" xml:space="preserve">
|
||||
@@ -104,7 +103,6 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ connected" xml:space="preserve">
|
||||
<source>%@ connected</source>
|
||||
<target>%@ соединен(а)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ is connected!" xml:space="preserve">
|
||||
@@ -134,7 +132,6 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
||||
<source>%@, %@ and %lld members</source>
|
||||
<target>%@, %@ и %lld членов группы</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@, %@ and %lld other members connected" xml:space="preserve">
|
||||
@@ -204,7 +201,6 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld group events" xml:space="preserve">
|
||||
<source>%lld group events</source>
|
||||
<target>%lld событий</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld members" xml:space="preserve">
|
||||
@@ -214,17 +210,14 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld messages blocked" xml:space="preserve">
|
||||
<source>%lld messages blocked</source>
|
||||
<target>%lld сообщений заблокировано</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld messages marked deleted" xml:space="preserve">
|
||||
<source>%lld messages marked deleted</source>
|
||||
<target>%lld сообщений помечено удалёнными</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld messages moderated by %@" xml:space="preserve">
|
||||
<source>%lld messages moderated by %@</source>
|
||||
<target>%lld сообщений модерировано членом %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld minutes" xml:space="preserve">
|
||||
@@ -299,12 +292,10 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="(new)" xml:space="preserve">
|
||||
<source>(new)</source>
|
||||
<target>(новое)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(this device v%@)" xml:space="preserve">
|
||||
<source>(this device v%@)</source>
|
||||
<target>(это устройство v%@)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=")" xml:space="preserve">
|
||||
@@ -392,15 +383,6 @@
|
||||
- and more!</source>
|
||||
<target>- более стабильная доставка сообщений.
|
||||
- немного улучшенные группы.
|
||||
- и прочее!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="- optionally notify deleted contacts. - profile names with spaces. - and more!" xml:space="preserve">
|
||||
<source>- optionally notify deleted contacts.
|
||||
- profile names with spaces.
|
||||
- and more!</source>
|
||||
<target>- опционально уведомляйте удалённые контакты.
|
||||
- имена профилей с пробелами.
|
||||
- и прочее!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
@@ -420,7 +402,6 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="0 sec" xml:space="preserve">
|
||||
<source>0 sec</source>
|
||||
<target>0 сек</target>
|
||||
<note>time to disappear</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="0s" xml:space="preserve">
|
||||
@@ -650,7 +631,6 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="All new messages from %@ will be hidden!" xml:space="preserve">
|
||||
<source>All new messages from %@ will be hidden!</source>
|
||||
<target>Все новые сообщения от %@ будут скрыты!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
|
||||
@@ -760,12 +740,10 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Already connecting!" xml:space="preserve">
|
||||
<source>Already connecting!</source>
|
||||
<target>Уже соединяется!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Already joining the group!" xml:space="preserve">
|
||||
<source>Already joining the group!</source>
|
||||
<target>Вступление в группу уже начато!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Always use relay" xml:space="preserve">
|
||||
@@ -890,7 +868,6 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Bad desktop address" xml:space="preserve">
|
||||
<source>Bad desktop address</source>
|
||||
<target>Неверный адрес компьютера</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Bad message ID" xml:space="preserve">
|
||||
@@ -903,11 +880,6 @@
|
||||
<target>Ошибка хэш сообщения</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Better messages</source>
|
||||
<target>Улучшенные сообщения</target>
|
||||
@@ -915,22 +887,14 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Block" xml:space="preserve">
|
||||
<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>
|
||||
</trans-unit>
|
||||
<trans-unit id="Block member" xml:space="preserve">
|
||||
<source>Block member</source>
|
||||
<target>Заблокировать члена группы</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Block member?" xml:space="preserve">
|
||||
<source>Block member?</source>
|
||||
<target>Заблокировать члена группы?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Both you and your contact can add message reactions." xml:space="preserve">
|
||||
@@ -1194,11 +1158,6 @@
|
||||
<target>Соединиться</target>
|
||||
<note>server test step</note>
|
||||
</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">
|
||||
<source>Connect incognito</source>
|
||||
<target>Соединиться Инкогнито</target>
|
||||
@@ -1206,31 +1165,24 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect to desktop" xml:space="preserve">
|
||||
<source>Connect to desktop</source>
|
||||
<target>Подключиться к компьютеру</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect to yourself?" xml:space="preserve">
|
||||
<source>Connect to yourself?</source>
|
||||
<target>Соединиться с самим собой?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect to yourself? This is your own SimpleX address!" xml:space="preserve">
|
||||
<source>Connect to yourself?
|
||||
This is your own SimpleX address!</source>
|
||||
<target>Соединиться с самим собой?
|
||||
Это ваш собственный адрес SimpleX!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect to yourself? This is your own one-time link!" xml:space="preserve">
|
||||
<source>Connect to yourself?
|
||||
This is your own one-time link!</source>
|
||||
<target>Соединиться с самим собой?
|
||||
Это ваша собственная одноразовая ссылка!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via contact address" xml:space="preserve">
|
||||
<source>Connect via contact address</source>
|
||||
<target>Соединиться через адрес</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Connect with %@" xml:space="preserve">
|
||||
<source>Connect with %@</source>
|
||||
<target>Соединиться с %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connected desktop" xml:space="preserve">
|
||||
<source>Connected desktop</source>
|
||||
<target>Подключенный компьютер</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connected to desktop" xml:space="preserve">
|
||||
<source>Connected to desktop</source>
|
||||
<target>Компьютер подключен</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connecting server…" xml:space="preserve">
|
||||
@@ -1275,7 +1224,6 @@ This is your own one-time link!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connecting to desktop" xml:space="preserve">
|
||||
<source>Connecting to desktop</source>
|
||||
<target>Подключение к компьютеру</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection" xml:space="preserve">
|
||||
@@ -1300,7 +1248,6 @@ This is your own one-time link!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection terminated" xml:space="preserve">
|
||||
<source>Connection terminated</source>
|
||||
<target>Подключение прервано</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection timeout" xml:space="preserve">
|
||||
@@ -1370,7 +1317,6 @@ This is your own one-time link!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
||||
<source>Correct name to %@?</source>
|
||||
<target>Исправить имя на %@?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create" xml:space="preserve">
|
||||
@@ -1383,11 +1329,6 @@ This is your own one-time link!</source>
|
||||
<target>Создать адрес SimpleX</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Create an address to let people connect with you.</source>
|
||||
<target>Создайте адрес, чтобы можно было соединиться с вами.</target>
|
||||
@@ -1400,7 +1341,6 @@ This is your own one-time link!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create group" xml:space="preserve">
|
||||
<source>Create group</source>
|
||||
<target>Создать группу</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Create profile" xml:space="preserve">
|
||||
<source>Create profile</source>
|
||||
<target>Создать профиль</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create queue" xml:space="preserve">
|
||||
@@ -1588,7 +1527,6 @@ This is your own one-time link!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete %lld messages?" xml:space="preserve">
|
||||
<source>Delete %lld messages?</source>
|
||||
<target>Удалить %lld сообщений?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete Contact" xml:space="preserve">
|
||||
@@ -1618,7 +1556,6 @@ This is your own one-time link!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete and notify contact" xml:space="preserve">
|
||||
<source>Delete and notify contact</source>
|
||||
<target>Удалить и уведомить контакт</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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? This cannot be undone!" xml:space="preserve">
|
||||
<source>Delete contact?
|
||||
This cannot be undone!</source>
|
||||
<target>Удалить контакт?
|
||||
Это не может быть отменено!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete database" xml:space="preserve">
|
||||
@@ -1800,17 +1735,14 @@ This cannot be undone!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Desktop address" xml:space="preserve">
|
||||
<source>Desktop address</source>
|
||||
<target>Адрес компьютера</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target>Версия настольного приложения %@ несовместима с этим приложением.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Desktop devices" xml:space="preserve">
|
||||
<source>Desktop devices</source>
|
||||
<target>Компьютеры</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Develop" xml:space="preserve">
|
||||
@@ -1905,7 +1837,6 @@ This cannot be undone!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Disconnect desktop?" xml:space="preserve">
|
||||
<source>Disconnect desktop?</source>
|
||||
<target>Отключить компьютер?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Discover and join groups" xml:space="preserve">
|
||||
@@ -1913,9 +1844,8 @@ This cannot be undone!</source>
|
||||
<target>Найдите и вступите в группы</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Discover via local network" xml:space="preserve">
|
||||
<source>Discover via local network</source>
|
||||
<target>Обнаружение по локальной сети</target>
|
||||
<trans-unit id="Discover on network" xml:space="preserve">
|
||||
<source>Discover on network</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Encryption re-negotiation error" xml:space="preserve">
|
||||
<source>Encryption re-negotiation error</source>
|
||||
<target>Ошибка нового соглашения о шифровании</target>
|
||||
<note>message decrypt error item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encryption re-negotiation failed." xml:space="preserve">
|
||||
<source>Encryption re-negotiation failed.</source>
|
||||
<target>Ошибка нового соглашения о шифровании.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enter Passcode" xml:space="preserve">
|
||||
@@ -2110,7 +2038,6 @@ This cannot be undone!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enter group name…" xml:space="preserve">
|
||||
<source>Enter group name…</source>
|
||||
<target>Введите имя группы…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enter passphrase…" xml:space="preserve">
|
||||
@@ -2130,7 +2057,6 @@ This cannot be undone!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enter this device name…" xml:space="preserve">
|
||||
<source>Enter this device name…</source>
|
||||
<target>Введите имя этого устройства…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enter welcome message…" xml:space="preserve">
|
||||
@@ -2145,7 +2071,6 @@ This cannot be undone!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enter your name…" xml:space="preserve">
|
||||
<source>Enter your name…</source>
|
||||
<target>Введите ваше имя…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error" xml:space="preserve">
|
||||
@@ -2205,7 +2130,6 @@ This cannot be undone!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error creating member contact" xml:space="preserve">
|
||||
<source>Error creating member contact</source>
|
||||
<target>Ошибка создания контакта с членом группы</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error creating profile!" xml:space="preserve">
|
||||
@@ -2340,7 +2264,6 @@ This cannot be undone!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error sending member contact invitation" xml:space="preserve">
|
||||
<source>Error sending member contact invitation</source>
|
||||
<target>Ошибка отправки приглашения члену группы</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error sending message" xml:space="preserve">
|
||||
@@ -2425,7 +2348,6 @@ This cannot be undone!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Expand" xml:space="preserve">
|
||||
<source>Expand</source>
|
||||
<target>Раскрыть</target>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Export database" xml:space="preserve">
|
||||
@@ -2458,11 +2380,6 @@ This cannot be undone!</source>
|
||||
<target>Быстрые и не нужно ждать, когда отправитель онлайн!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Favorite</source>
|
||||
<target>Избранный</target>
|
||||
@@ -2558,11 +2475,6 @@ This cannot be undone!</source>
|
||||
<target>Для консоли</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>French interface</source>
|
||||
<target>Французский интерфейс</target>
|
||||
@@ -2585,7 +2497,6 @@ This cannot be undone!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Fully decentralized – visible only to members." xml:space="preserve">
|
||||
<source>Fully decentralized – visible only to members.</source>
|
||||
<target>Группа полностью децентрализована – она видна только членам.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Group already exists" xml:space="preserve">
|
||||
<source>Group already exists</source>
|
||||
<target>Группа уже существует</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group already exists!" xml:space="preserve">
|
||||
<source>Group already exists!</source>
|
||||
<target>Группа уже существует!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group display name" xml:space="preserve">
|
||||
@@ -2888,11 +2797,6 @@ This cannot be undone!</source>
|
||||
<target>Инкогнито</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Incognito mode</source>
|
||||
<target>Режим Инкогнито</target>
|
||||
@@ -2925,7 +2829,6 @@ This cannot be undone!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Incompatible version" xml:space="preserve">
|
||||
<source>Incompatible version</source>
|
||||
<target>Несовместимая версия</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Incorrect passcode" xml:space="preserve">
|
||||
@@ -2977,7 +2880,6 @@ This cannot be undone!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid name!" xml:space="preserve">
|
||||
<source>Invalid name!</source>
|
||||
<target>Неверное имя!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid server address!" xml:space="preserve">
|
||||
@@ -3073,7 +2975,6 @@ This cannot be undone!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Join group?" xml:space="preserve">
|
||||
<source>Join group?</source>
|
||||
<target>Вступить в группу?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Join incognito" xml:space="preserve">
|
||||
@@ -3083,14 +2984,11 @@ This cannot be undone!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Join with current profile" xml:space="preserve">
|
||||
<source>Join with current profile</source>
|
||||
<target>Вступить с активным профилем</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Join your group? This is your link for group %@!" xml:space="preserve">
|
||||
<source>Join your group?
|
||||
This is your link for group %@!</source>
|
||||
<target>Вступить в вашу группу?
|
||||
Это ваша ссылка на группу %@!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Joining group" xml:space="preserve">
|
||||
@@ -3100,7 +2998,6 @@ This is your link for group %@!</source>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target>Оставьте приложение открытым, чтобы использовать его с компьютера</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
@@ -3163,19 +3060,12 @@ This is your link for group %@!</source>
|
||||
<target>Ограничения</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Linked desktop options</source>
|
||||
<target>Опции связанных компьютеров</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Linked desktops" xml:space="preserve">
|
||||
<source>Linked desktops</source>
|
||||
<target>Связанные компьютеры</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Live message!" xml:space="preserve">
|
||||
@@ -3330,7 +3220,6 @@ This is your link for group %@!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Messages from %@ will be shown!" xml:space="preserve">
|
||||
<source>Messages from %@ will be shown!</source>
|
||||
<target>Сообщения от %@ будут показаны!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Migrating database archive…" xml:space="preserve">
|
||||
@@ -3528,11 +3417,6 @@ This is your link for group %@!</source>
|
||||
<target>Нет полученных или отправленных файлов</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Notifications</source>
|
||||
<target>Уведомления</target>
|
||||
@@ -3669,7 +3553,6 @@ This is your link for group %@!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open" xml:space="preserve">
|
||||
<source>Open</source>
|
||||
<target>Открыть</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open Settings" xml:space="preserve">
|
||||
@@ -3689,7 +3572,6 @@ This is your link for group %@!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open group" xml:space="preserve">
|
||||
<source>Open group</source>
|
||||
<target>Открыть группу</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open user profiles" xml:space="preserve">
|
||||
@@ -3754,7 +3636,6 @@ This is your link for group %@!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<target>Вставить адрес компьютера</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste image" xml:space="preserve">
|
||||
@@ -3904,12 +3785,10 @@ This is your link for group %@!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Profile name" xml:space="preserve">
|
||||
<source>Profile name</source>
|
||||
<target>Имя профиля</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Profile name:" xml:space="preserve">
|
||||
<source>Profile name:</source>
|
||||
<target>Имя профиля:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Profile password" xml:space="preserve">
|
||||
@@ -4159,12 +4038,10 @@ This is your link for group %@!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Repeat connection request?" xml:space="preserve">
|
||||
<source>Repeat connection request?</source>
|
||||
<target>Повторить запрос на соединение?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Repeat join request?" xml:space="preserve">
|
||||
<source>Repeat join request?</source>
|
||||
<target>Повторить запрос на вступление?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reply" xml:space="preserve">
|
||||
@@ -4354,7 +4231,6 @@ This is your link for group %@!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Scan QR code from desktop" xml:space="preserve">
|
||||
<source>Scan QR code from desktop</source>
|
||||
<target>Сканировать QR код с компьютера</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Scan code" xml:space="preserve">
|
||||
@@ -4439,7 +4315,6 @@ This is your link for group %@!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Send direct message to connect" xml:space="preserve">
|
||||
<source>Send direct message to connect</source>
|
||||
<target>Отправьте сообщение чтобы соединиться</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Send disappearing message" xml:space="preserve">
|
||||
@@ -4579,7 +4454,6 @@ This is your link for group %@!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Session code" xml:space="preserve">
|
||||
<source>Session code</source>
|
||||
<target>Код сессии</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Set 1 day" xml:space="preserve">
|
||||
@@ -4894,7 +4768,6 @@ This is your link for group %@!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to Connect" xml:space="preserve">
|
||||
<source>Tap to Connect</source>
|
||||
<target>Нажмите чтобы соединиться</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="This device name" xml:space="preserve">
|
||||
<source>This device name</source>
|
||||
<target>Имя этого устройства</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="This is your own SimpleX address!" xml:space="preserve">
|
||||
<source>This is your own SimpleX address!</source>
|
||||
<target>Это ваш собственный адрес SimpleX!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="This is your own one-time link!" xml:space="preserve">
|
||||
<source>This is your own one-time link!</source>
|
||||
<target>Это ваша собственная одноразовая ссылка!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>To make a new connection</source>
|
||||
<target>Чтобы соединиться</target>
|
||||
@@ -5208,17 +5073,14 @@ You will be prompted to complete authentication before this feature is enabled.<
|
||||
</trans-unit>
|
||||
<trans-unit id="Unblock" xml:space="preserve">
|
||||
<source>Unblock</source>
|
||||
<target>Разблокировать</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unblock member" xml:space="preserve">
|
||||
<source>Unblock member</source>
|
||||
<target>Разблокировать члена группы</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unblock member?" xml:space="preserve">
|
||||
<source>Unblock member?</source>
|
||||
<target>Разблокировать члена группы?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Unlink" xml:space="preserve">
|
||||
<source>Unlink</source>
|
||||
<target>Забыть</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unlink desktop?" xml:space="preserve">
|
||||
<source>Unlink desktop?</source>
|
||||
<target>Забыть компьютер?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Use from desktop" xml:space="preserve">
|
||||
<source>Use from desktop</source>
|
||||
<target>Использовать с компьютера</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Verify code with desktop" xml:space="preserve">
|
||||
<source>Verify code with desktop</source>
|
||||
<target>Сверьте код с компьютером</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Verify connection" xml:space="preserve">
|
||||
<source>Verify connection</source>
|
||||
<target>Проверить соединение</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Verify connections" xml:space="preserve">
|
||||
<source>Verify connections</source>
|
||||
<target>Проверять соединения</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Video call</source>
|
||||
<target>Видеозвонок</target>
|
||||
@@ -5503,11 +5354,6 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Голосовое сообщение…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Waiting for file</source>
|
||||
<target>Ожидается прием файла</target>
|
||||
@@ -5610,39 +5456,31 @@ To connect, please ask your contact to create another connection link and check
|
||||
</trans-unit>
|
||||
<trans-unit id="You are already connecting to %@." xml:space="preserve">
|
||||
<source>You are already connecting to %@.</source>
|
||||
<target>Вы уже соединяетесь с %@.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target>Вы уже соединяетесь по этой одноразовой ссылке!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You are already in group %@." xml:space="preserve">
|
||||
<source>You are already in group %@.</source>
|
||||
<target>Вы уже состоите в группе %@.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You are already joining the group %@." xml:space="preserve">
|
||||
<source>You are already joining the group %@.</source>
|
||||
<target>Вы уже вступаете в группу %@.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target>Вы уже вступаете в группу по этой ссылке!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target>Вы уже вступаете в группу по этой ссылке.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You are already joining the group! Repeat join request?" xml:space="preserve">
|
||||
<source>You are already joining the group!
|
||||
Repeat join request?</source>
|
||||
<target>Вы уже вступаете в группу!
|
||||
Повторить запрос на вступление?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="You have already requested connection via this address!" xml:space="preserve">
|
||||
<source>You have already requested connection via this address!</source>
|
||||
<target>Вы уже запросили соединение через этот адрес!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You have already requested connection! Repeat connection request?" xml:space="preserve">
|
||||
<source>You have already requested connection!
|
||||
Repeat connection request?</source>
|
||||
<target>Вы уже запросили соединение!
|
||||
Повторить запрос?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You have no chats" xml:space="preserve">
|
||||
@@ -5804,7 +5639,6 @@ Repeat connection request?</source>
|
||||
</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">
|
||||
<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>
|
||||
</trans-unit>
|
||||
<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 id="You will connect to all group members." xml:space="preserve">
|
||||
<source>You will connect to all group members.</source>
|
||||
<target>Вы соединитесь со всеми членами группы.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<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 id="Your profile" xml:space="preserve">
|
||||
<source>Your profile</source>
|
||||
<target>Ваш профиль</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your profile **%@** will be shared." xml:space="preserve">
|
||||
@@ -6045,7 +5877,6 @@ SimpleX серверы не могут получить доступ к Ваше
|
||||
</trans-unit>
|
||||
<trans-unit id="and %lld other events" xml:space="preserve">
|
||||
<source>and %lld other events</source>
|
||||
<target>и %lld других событий</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="audio call (not e2e encrypted)" xml:space="preserve">
|
||||
@@ -6053,11 +5884,6 @@ SimpleX серверы не могут получить доступ к Ваше
|
||||
<target>аудиозвонок (не e2e зашифрованный)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>bad message ID</source>
|
||||
<target>ошибка ID сообщения</target>
|
||||
@@ -6070,7 +5896,6 @@ SimpleX серверы не могут получить доступ к Ваше
|
||||
</trans-unit>
|
||||
<trans-unit id="blocked" xml:space="preserve">
|
||||
<source>blocked</source>
|
||||
<target>заблокировано</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="bold" xml:space="preserve">
|
||||
@@ -6145,7 +5970,6 @@ SimpleX серверы не могут получить доступ к Ваше
|
||||
</trans-unit>
|
||||
<trans-unit id="connected directly" xml:space="preserve">
|
||||
<source>connected directly</source>
|
||||
<target>соединен(а) напрямую</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="connecting" xml:space="preserve">
|
||||
@@ -6245,7 +6069,6 @@ SimpleX серверы не могут получить доступ к Ваше
|
||||
</trans-unit>
|
||||
<trans-unit id="deleted contact" xml:space="preserve">
|
||||
<source>deleted contact</source>
|
||||
<target>удалил(а) контакт</target>
|
||||
<note>rcv direct event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="deleted group" xml:space="preserve">
|
||||
@@ -6612,7 +6435,6 @@ SimpleX серверы не могут получить доступ к Ваше
|
||||
</trans-unit>
|
||||
<trans-unit id="send direct message" xml:space="preserve">
|
||||
<source>send direct message</source>
|
||||
<target>отправьте сообщение</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="starting…" xml:space="preserve">
|
||||
@@ -6642,7 +6464,6 @@ SimpleX серверы не могут получить доступ к Ваше
|
||||
</trans-unit>
|
||||
<trans-unit id="v%@" xml:space="preserve">
|
||||
<source>v%@</source>
|
||||
<target>v%@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="v%@ (%@)" xml:space="preserve">
|
||||
@@ -6784,7 +6605,6 @@ SimpleX серверы не могут получить доступ к Ваше
|
||||
</trans-unit>
|
||||
<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>
|
||||
<target>SimpleX использует доступ к локальной сети, чтобы разрешить использование профиля чата через компьютер в той же сети.</target>
|
||||
<note>Privacy - Local Network Usage Description</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="NSMicrophoneUsageDescription" xml:space="preserve">
|
||||
|
||||
@@ -377,12 +377,6 @@
|
||||
- และอื่น ๆ!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="- optionally notify deleted contacts. - profile names with spaces. - 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. - custom time to disappear. - editing history." xml:space="preserve">
|
||||
<source>- voice messages up to 5 minutes.
|
||||
- custom time to disappear.
|
||||
@@ -874,10 +868,6 @@
|
||||
<target>แฮชข้อความไม่ดี</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Better messages</source>
|
||||
<target>ข้อความที่ดีขึ้น</target>
|
||||
@@ -887,10 +877,6 @@
|
||||
<source>Block</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Block member</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -1159,10 +1145,6 @@
|
||||
<target>เชื่อมต่อ</target>
|
||||
<note>server test step</note>
|
||||
</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">
|
||||
<source>Connect incognito</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -1332,10 +1314,6 @@ This is your own one-time link!</source>
|
||||
<target>สร้างที่อยู่ SimpleX</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Create an address to let people connect with you.</source>
|
||||
<target>สร้างที่อยู่เพื่อให้ผู้อื่นเชื่อมต่อกับคุณ</target>
|
||||
@@ -1848,8 +1826,8 @@ This cannot be undone!</source>
|
||||
<source>Discover and join groups</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Discover via local network" xml:space="preserve">
|
||||
<source>Discover via local network</source>
|
||||
<trans-unit id="Discover on network" xml:space="preserve">
|
||||
<source>Discover on network</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
|
||||
@@ -2381,10 +2359,6 @@ This cannot be undone!</source>
|
||||
<target>รวดเร็วและไม่ต้องรอจนกว่าผู้ส่งจะออนไลน์!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Favorite</source>
|
||||
<target>ที่ชอบ</target>
|
||||
@@ -2480,10 +2454,6 @@ This cannot be undone!</source>
|
||||
<target>สำหรับคอนโซล</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>French interface</source>
|
||||
<target>อินเทอร์เฟซภาษาฝรั่งเศส</target>
|
||||
@@ -2806,10 +2776,6 @@ This cannot be undone!</source>
|
||||
<target>ไม่ระบุตัวตน</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Incognito mode</source>
|
||||
<target>โหมดไม่ระบุตัวตน</target>
|
||||
@@ -3071,10 +3037,6 @@ This is your link for group %@!</source>
|
||||
<target>ข้อจำกัด</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Linked desktop options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -3429,10 +3391,6 @@ This is your link for group %@!</source>
|
||||
<target>ไม่มีไฟล์ที่ได้รับหรือส่ง</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Notifications</source>
|
||||
<target>การแจ้งเตือน</target>
|
||||
@@ -4996,10 +4954,6 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
<target>เพื่อการเชื่อมต่อ ผู้ติดต่อของคุณสามารถสแกนคิวอาร์โค้ดหรือใช้ลิงก์ในแอป</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>To make a new connection</source>
|
||||
<target>เพื่อสร้างการเชื่อมต่อใหม่</target>
|
||||
@@ -5312,10 +5266,6 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>ผ่านเบราว์เซอร์</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Video call</source>
|
||||
<target>การสนทนาทางวิดีโอ</target>
|
||||
@@ -5366,10 +5316,6 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>ข้อความเสียง…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Waiting for file</source>
|
||||
<target>กำลังรอไฟล์</target>
|
||||
@@ -5898,10 +5844,6 @@ SimpleX servers cannot see your profile.</source>
|
||||
<target>การโทรด้วยเสียง (ไม่ได้ encrypt จากต้นจนจบ)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>bad message ID</source>
|
||||
<target>ID ข้อความที่ไม่ดี</target>
|
||||
|
||||
@@ -382,12 +382,6 @@
|
||||
- і багато іншого!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="- optionally notify deleted contacts. - profile names with spaces. - 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. - custom time to disappear. - editing history." xml:space="preserve">
|
||||
<source>- voice messages up to 5 minutes.
|
||||
- custom time to disappear.
|
||||
@@ -881,10 +875,6 @@
|
||||
<target>Поганий хеш повідомлення</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Better messages</source>
|
||||
<target>Кращі повідомлення</target>
|
||||
@@ -894,10 +884,6 @@
|
||||
<source>Block</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Block member</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -1166,10 +1152,6 @@
|
||||
<target>Підключіться</target>
|
||||
<note>server test step</note>
|
||||
</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">
|
||||
<source>Connect incognito</source>
|
||||
<target>Підключайтеся інкогніто</target>
|
||||
@@ -1341,10 +1323,6 @@ This is your own one-time link!</source>
|
||||
<target>Створіть адресу SimpleX</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Create an address to let people connect with you.</source>
|
||||
<target>Створіть адресу, щоб люди могли з вами зв'язатися.</target>
|
||||
@@ -1858,8 +1836,8 @@ This cannot be undone!</source>
|
||||
<source>Discover and join groups</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Discover via local network" xml:space="preserve">
|
||||
<source>Discover via local network</source>
|
||||
<trans-unit id="Discover on network" xml:space="preserve">
|
||||
<source>Discover on network</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
|
||||
@@ -2391,10 +2369,6 @@ This cannot be undone!</source>
|
||||
<target>Швидко і без очікування, поки відправник буде онлайн!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Favorite</source>
|
||||
<target>Улюблений</target>
|
||||
@@ -2490,10 +2464,6 @@ This cannot be undone!</source>
|
||||
<target>Для консолі</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>French interface</source>
|
||||
<target>Французький інтерфейс</target>
|
||||
@@ -2816,10 +2786,6 @@ This cannot be undone!</source>
|
||||
<target>Інкогніто</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Incognito mode</source>
|
||||
<target>Режим інкогніто</target>
|
||||
@@ -3083,10 +3049,6 @@ This is your link for group %@!</source>
|
||||
<target>Обмеження</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Linked desktop options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -3443,10 +3405,6 @@ This is your link for group %@!</source>
|
||||
<target>Немає отриманих або відправлених файлів</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Notifications</source>
|
||||
<target>Сповіщення</target>
|
||||
@@ -5018,10 +4976,6 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
<target>Щоб підключитися, ваш контакт може відсканувати QR-код або скористатися посиланням у додатку.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>To make a new connection</source>
|
||||
<target>Щоб створити нове з'єднання</target>
|
||||
@@ -5336,10 +5290,6 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Через браузер</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Video call</source>
|
||||
<target>Відеодзвінок</target>
|
||||
@@ -5390,10 +5340,6 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Голосове повідомлення…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Waiting for file</source>
|
||||
<target>Очікування файлу</target>
|
||||
@@ -5924,10 +5870,6 @@ SimpleX servers cannot see your profile.</source>
|
||||
<target>аудіовиклик (без шифрування e2e)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>bad message ID</source>
|
||||
<target>невірний ідентифікатор повідомлення</target>
|
||||
|
||||
@@ -386,12 +386,6 @@
|
||||
- 以及更多!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="- optionally notify deleted contacts. - profile names with spaces. - 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. - custom time to disappear. - editing history." xml:space="preserve">
|
||||
<source>- voice messages up to 5 minutes.
|
||||
- custom time to disappear.
|
||||
@@ -886,10 +880,6 @@
|
||||
<target>错误消息散列</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Better messages</source>
|
||||
<target>更好的消息</target>
|
||||
@@ -899,10 +889,6 @@
|
||||
<source>Block</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Block member</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -1172,10 +1158,6 @@
|
||||
<target>连接</target>
|
||||
<note>server test step</note>
|
||||
</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">
|
||||
<source>Connect incognito</source>
|
||||
<target>在隐身状态下连接</target>
|
||||
@@ -1347,10 +1329,6 @@ This is your own one-time link!</source>
|
||||
<target>创建 SimpleX 地址</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Create an address to let people connect with you.</source>
|
||||
<target>创建一个地址,让人们与您联系。</target>
|
||||
@@ -1866,8 +1844,8 @@ This cannot be undone!</source>
|
||||
<target>发现和加入群组</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Discover via local network" xml:space="preserve">
|
||||
<source>Discover via local network</source>
|
||||
<trans-unit id="Discover on network" xml:space="preserve">
|
||||
<source>Discover on network</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
|
||||
@@ -2404,10 +2382,6 @@ This cannot be undone!</source>
|
||||
<target>快速且无需等待发件人在线!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Favorite</source>
|
||||
<target>最喜欢</target>
|
||||
@@ -2503,10 +2477,6 @@ This cannot be undone!</source>
|
||||
<target>用于控制台</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>French interface</source>
|
||||
<target>法语界面</target>
|
||||
@@ -2829,10 +2799,6 @@ This cannot be undone!</source>
|
||||
<target>隐身聊天</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Incognito mode</source>
|
||||
<target>隐身模式</target>
|
||||
@@ -3096,10 +3062,6 @@ This is your link for group %@!</source>
|
||||
<target>限制</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Linked desktop options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -3457,10 +3419,6 @@ This is your link for group %@!</source>
|
||||
<target>未收到或发送文件</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Notifications</source>
|
||||
<target>通知</target>
|
||||
@@ -5035,10 +4993,6 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
<target>您的联系人可以扫描二维码或使用应用程序中的链接来建立连接。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>To make a new connection</source>
|
||||
<target>建立新连接</target>
|
||||
@@ -5354,10 +5308,6 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>通过浏览器</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Video call</source>
|
||||
<target>视频通话</target>
|
||||
@@ -5408,10 +5358,6 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>语音消息……</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>Waiting for file</source>
|
||||
<target>等待文件中</target>
|
||||
@@ -5942,10 +5888,6 @@ SimpleX 服务器无法看到您的资料。</target>
|
||||
<target>语音通话(非端到端加密)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</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">
|
||||
<source>bad message ID</source>
|
||||
<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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,233 +14,91 @@ import SimpleXChat
|
||||
|
||||
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 {
|
||||
static let shared = PendingNtfs()
|
||||
private var ntfStreams: [String: NtfStream] = [:]
|
||||
private var ntfConts: [String: NtfStream.Continuation] = [:]
|
||||
|
||||
func createStream(_ id: String) async {
|
||||
logger.debug("NotificationService PendingNtfs.createStream: \(id)")
|
||||
if ntfStreams[id] == nil {
|
||||
ntfStreams[id] = ConcurrentQueue()
|
||||
logger.debug("NotificationService PendingNtfs.createStream: created ConcurrentQueue")
|
||||
func createStream(_ id: String) {
|
||||
logger.debug("PendingNtfs.createStream: \(id, privacy: .public)")
|
||||
if ntfStreams.index(forKey: id) == nil {
|
||||
ntfStreams[id] = AsyncStream { cont in
|
||||
ntfConts[id] = cont
|
||||
logger.debug("PendingNtfs.createStream: store continuation")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func readStream(_ id: String, for nse: NotificationService, ntfInfo: NtfMessages) async {
|
||||
logger.debug("NotificationService PendingNtfs.readStream: \(id) \(ntfInfo.ntfMessages.count)")
|
||||
if !ntfInfo.user.showNotifications {
|
||||
nse.setBestAttemptNtf(.empty)
|
||||
}
|
||||
func readStream(_ id: String, for nse: NotificationService, msgCount: Int = 1, showNotifications: Bool) async {
|
||||
logger.debug("PendingNtfs.readStream: \(id, privacy: .public) \(msgCount, privacy: .public)")
|
||||
if let s = ntfStreams[id] {
|
||||
logger.debug("NotificationService PendingNtfs.readStream: has stream")
|
||||
var expected = Set(ntfInfo.ntfMessages.map { $0.msgId })
|
||||
logger.debug("NotificationService PendingNtfs.readStream: expecting: \(expected)")
|
||||
var readCancelled = false
|
||||
var dequeued: DequeueElement<NSENotification>?
|
||||
nse.cancelRead = {
|
||||
readCancelled = true
|
||||
if let elementId = dequeued?.elementId {
|
||||
s.cancelDequeue(elementId)
|
||||
}
|
||||
logger.debug("PendingNtfs.readStream: has stream")
|
||||
var rcvCount = max(1, msgCount)
|
||||
for await ntf in s {
|
||||
nse.setBestAttemptNtf(showNotifications ? ntf : .empty)
|
||||
rcvCount -= 1
|
||||
if rcvCount == 0 || ntf.categoryIdentifier == ntfCategoryCallInvitation { break }
|
||||
}
|
||||
while !readCancelled {
|
||||
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")
|
||||
logger.debug("PendingNtfs.readStream: exiting")
|
||||
}
|
||||
}
|
||||
|
||||
func writeStream(_ id: String, _ ntf: NSENotification) async {
|
||||
logger.debug("NotificationService PendingNtfs.writeStream: \(id)")
|
||||
if let s = ntfStreams[id] {
|
||||
logger.debug("NotificationService PendingNtfs.writeStream: writing ntf")
|
||||
s.enqueue(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
|
||||
}
|
||||
func writeStream(_ id: String, _ ntf: NSENotification) {
|
||||
logger.debug("PendingNtfs.writeStream: \(id, privacy: .public)")
|
||||
if let cont = ntfConts[id] {
|
||||
logger.debug("PendingNtfs.writeStream: writing ntf")
|
||||
cont.yield(ntf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum NSENotification {
|
||||
case nse(UNMutableNotificationContent)
|
||||
case callkit(RcvCallInvitation)
|
||||
case nse(notification: UNMutableNotificationContent)
|
||||
case callkit(invitation: RcvCallInvitation)
|
||||
case empty
|
||||
case msgInfo(NtfMsgInfo)
|
||||
|
||||
var isCallInvitation: Bool {
|
||||
var categoryIdentifier: String? {
|
||||
switch self {
|
||||
case let .nse(ntf): ntf.categoryIdentifier == ntfCategoryCallInvitation
|
||||
case .callkit: true
|
||||
case .empty: false
|
||||
case .msgInfo: false
|
||||
case let .nse(ntf): return ntf.categoryIdentifier
|
||||
case .callkit: return ntfCategoryCallInvitation
|
||||
case .empty: return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
var contentHandler: ((UNNotificationContent) -> Void)?
|
||||
var bestAttemptNtf: NSENotification?
|
||||
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) {
|
||||
logger.debug("DEBUGGING: NotificationService.didReceive")
|
||||
let ntf = if let ntf_ = request.content.mutableCopy() as? UNMutableNotificationContent { ntf_ } else { UNMutableNotificationContent() }
|
||||
setBestAttemptNtf(ntf)
|
||||
if let ntf = request.content.mutableCopy() as? UNMutableNotificationContent {
|
||||
setBestAttemptNtf(ntf)
|
||||
}
|
||||
self.contentHandler = contentHandler
|
||||
registerGroupDefaults()
|
||||
let appState = appStateGroupDefault.get()
|
||||
logger.debug("NotificationService: app is \(appState.rawValue)")
|
||||
switch appState {
|
||||
case .stopped:
|
||||
setBadgeCount()
|
||||
setBestAttemptNtf(createAppStoppedNtf())
|
||||
deliverBestAttemptNtf()
|
||||
case .suspended:
|
||||
logger.debug("NotificationService: app is suspended")
|
||||
setBadgeCount()
|
||||
receiveNtfMessages(request, contentHandler)
|
||||
case .suspending:
|
||||
logger.debug("NotificationService: app is suspending")
|
||||
setBadgeCount()
|
||||
Task {
|
||||
let state: AppState = await withCheckedContinuation { cont in
|
||||
appSubscriber = appStateSubscriber { s in
|
||||
if s == .suspended { appSuspension(s) }
|
||||
}
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + Double(appSuspendTimeout) + 1) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
var state = appState
|
||||
for _ in 1...5 {
|
||||
_ = try await Task.sleep(nanoseconds: suspendingDelay)
|
||||
state = appStateGroupDefault.get()
|
||||
if state == .suspended || state != .suspending { break }
|
||||
}
|
||||
logger.debug("NotificationService: app state is now \(state.rawValue)")
|
||||
logger.debug("NotificationService: app state is \(state.rawValue, privacy: .public)")
|
||||
if state.inactive {
|
||||
receiveNtfMessages(request, contentHandler)
|
||||
} else {
|
||||
@@ -248,6 +106,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
}
|
||||
}
|
||||
default:
|
||||
logger.debug("NotificationService: app state is \(appState.rawValue, privacy: .public)")
|
||||
deliverBestAttemptNtf()
|
||||
}
|
||||
}
|
||||
@@ -262,35 +121,27 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
if let ntfData = userInfo["notificationData"] as? [AnyHashable : Any],
|
||||
let nonce = ntfData["nonce"] as? String,
|
||||
let encNtfInfo = ntfData["message"] as? String,
|
||||
// check it here again
|
||||
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()
|
||||
let dbStatus = startChat() {
|
||||
if case .ok = dbStatus,
|
||||
let ntfInfo = apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo) {
|
||||
logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfInfo.ntfMessages.count))")
|
||||
if let connEntity = ntfInfo.connEntity_ {
|
||||
let ntfMsgInfo = apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo) {
|
||||
logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfMsgInfo), privacy: .public)")
|
||||
if let connEntity = ntfMsgInfo.connEntity {
|
||||
setBestAttemptNtf(
|
||||
ntfInfo.ntfsEnabled
|
||||
? .nse(createConnectionEventNtf(ntfInfo.user, connEntity))
|
||||
ntfMsgInfo.ntfsEnabled
|
||||
? .nse(notification: createConnectionEventNtf(ntfMsgInfo.user, connEntity))
|
||||
: .empty
|
||||
)
|
||||
if let id = connEntity.id {
|
||||
receiveEntityId = id
|
||||
NtfStreamSemaphores.shared.waitForStream(id)
|
||||
if receiveEntityId != nil {
|
||||
Task {
|
||||
logger.debug("NotificationService: receiveNtfMessages: in Task, connEntity id \(id)")
|
||||
await PendingNtfs.shared.createStream(id)
|
||||
await PendingNtfs.shared.readStream(id, for: self, ntfInfo: ntfInfo)
|
||||
deliverBestAttemptNtf()
|
||||
}
|
||||
Task {
|
||||
logger.debug("NotificationService: receiveNtfMessages: in Task, connEntity id \(id, privacy: .public)")
|
||||
await PendingNtfs.shared.createStream(id)
|
||||
await PendingNtfs.shared.readStream(id, for: self, msgCount: ntfMsgInfo.ntfMessages.count, showNotifications: ntfMsgInfo.user.showNotifications)
|
||||
deliverBestAttemptNtf()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
} else if let dbStatus = dbStatus {
|
||||
return
|
||||
} else {
|
||||
setBestAttemptNtf(createErrorNtf(dbStatus))
|
||||
}
|
||||
}
|
||||
@@ -299,7 +150,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
|
||||
override func serviceExtensionTimeWillExpire() {
|
||||
logger.debug("DEBUGGING: NotificationService.serviceExtensionTimeWillExpire")
|
||||
deliverBestAttemptNtf(urgent: true)
|
||||
deliverBestAttemptNtf()
|
||||
}
|
||||
|
||||
func setBadgeCount() {
|
||||
@@ -308,301 +159,91 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
}
|
||||
|
||||
func setBestAttemptNtf(_ ntf: UNMutableNotificationContent) {
|
||||
setBestAttemptNtf(.nse(ntf))
|
||||
setBestAttemptNtf(.nse(notification: ntf))
|
||||
}
|
||||
|
||||
func setBestAttemptNtf(_ ntf: NSENotification) {
|
||||
logger.debug("NotificationService.setBestAttemptNtf")
|
||||
if case let .nse(notification) = ntf {
|
||||
notification.badge = badgeCount as NSNumber
|
||||
bestAttemptNtf = .nse(notification)
|
||||
bestAttemptNtf = .nse(notification: notification)
|
||||
} else {
|
||||
bestAttemptNtf = ntf
|
||||
}
|
||||
}
|
||||
|
||||
private func deliverBestAttemptNtf(urgent: Bool = false) {
|
||||
private func 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 {
|
||||
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 {
|
||||
case let .nse(content): deliver(content)
|
||||
case let .nse(content): handler(content)
|
||||
case let .callkit(invitation):
|
||||
logger.debug("NotificationService reportNewIncomingVoIPPushPayload for \(invitation.contact.id)")
|
||||
CXProvider.reportNewIncomingVoIPPushPayload([
|
||||
"displayName": invitation.contact.displayName,
|
||||
"contactId": invitation.contact.id,
|
||||
"media": invitation.callType.media.rawValue
|
||||
]) { error in
|
||||
logger.debug("reportNewIncomingVoIPPushPayload result: \(error)")
|
||||
deliver(error == nil ? nil : createCallInvitationNtf(invitation))
|
||||
if error == nil {
|
||||
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 .msgInfo: deliver(nil) // unreachable, the best attempt is never set to msgInfo
|
||||
case .empty: handler(UNMutableNotificationContent())
|
||||
}
|
||||
bestAttemptNtf = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nseStateGroupDefault must not be used in NSE directly, only via this singleton
|
||||
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 chatStarted = false
|
||||
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? {
|
||||
logger.debug("NotificationService: startChat")
|
||||
// only skip creating if there is chat controller
|
||||
if case .active = NSEChatState.shared.value, hasChatCtrl() { return .ok }
|
||||
|
||||
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))")
|
||||
hs_init(0, nil)
|
||||
if chatStarted { return .ok }
|
||||
let (_, dbStatus) = chatMigrateInit(confirmMigrations: defaultMigrationConfirmation())
|
||||
if dbStatus != .ok {
|
||||
resetChatCtrl()
|
||||
NSEChatState.shared.set(.created)
|
||||
return dbStatus
|
||||
}
|
||||
let state = NSEChatState.shared.value
|
||||
NSEChatState.shared.set(.starting)
|
||||
if let user = apiGetActiveUser() {
|
||||
logger.debug("NotificationService active user \(String(describing: user))")
|
||||
logger.debug("active user \(String(describing: user))")
|
||||
do {
|
||||
try setNetworkConfig(networkConfig)
|
||||
try apiSetTempFolder(tempFolder: getTempFilesDirectory().path)
|
||||
try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path)
|
||||
try setXFTPConfig(xftpConfig)
|
||||
try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get())
|
||||
// prevent suspension while starting chat
|
||||
suspendLock.wait()
|
||||
defer { suspendLock.signal() }
|
||||
if NSEChatState.shared.value == .starting {
|
||||
updateNetCfg()
|
||||
let justStarted = try apiStartChat()
|
||||
NSEChatState.shared.set(.active)
|
||||
if justStarted {
|
||||
chatLastStartGroupDefault.set(Date.now)
|
||||
Task {
|
||||
if !receiverStarted {
|
||||
receiverStarted = true
|
||||
await receiveMessages()
|
||||
}
|
||||
}
|
||||
}
|
||||
return .ok
|
||||
let justStarted = try apiStartChat()
|
||||
chatStarted = true
|
||||
if justStarted {
|
||||
chatLastStartGroupDefault.set(Date.now)
|
||||
Task { await receiveMessages() }
|
||||
}
|
||||
return .ok
|
||||
} catch {
|
||||
logger.error("NotificationService startChat error: \(responseError(error))")
|
||||
logger.error("NotificationService startChat error: \(responseError(error), privacy: .public)")
|
||||
}
|
||||
} else {
|
||||
logger.debug("NotificationService: no active user")
|
||||
logger.debug("no active user")
|
||||
}
|
||||
if NSEChatState.shared.value == .starting { NSEChatState.shared.set(state) }
|
||||
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 {
|
||||
logger.debug("NotificationService receiveMessages")
|
||||
while true {
|
||||
switch NSEChatState.shared.value {
|
||||
// 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 {
|
||||
updateNetCfg()
|
||||
if let msg = await chatRecvMsg() {
|
||||
logger.debug("NotificationService receiveMsg: message")
|
||||
if let (id, ntf) = await receivedMsgNtf(msg) {
|
||||
logger.debug("NotificationService receiveMsg: notification")
|
||||
await PendingNtfs.shared.createStream(id)
|
||||
await PendingNtfs.shared.writeStream(id, ntf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func delayWhenInactive() async {
|
||||
logger.debug("NotificationService delayWhenInactive")
|
||||
_ = try? await Task.sleep(nanoseconds: 1000_000000)
|
||||
}
|
||||
}
|
||||
|
||||
func chatRecvMsg() async -> ChatResponse? {
|
||||
@@ -616,14 +257,14 @@ private let isInChina = SKStorefront().countryCode == "CHN"
|
||||
private func useCallKit() -> Bool { !isInChina && callKitEnabledGroupDefault.get() }
|
||||
|
||||
func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? {
|
||||
logger.debug("NotificationService receivedMsgNtf: \(res.responseType)")
|
||||
logger.debug("NotificationService processReceivedMsg: \(res.responseType)")
|
||||
switch res {
|
||||
case let .contactConnected(user, contact, _):
|
||||
return (contact.id, .nse(createContactConnectedNtf(user, contact)))
|
||||
return (contact.id, .nse(notification: createContactConnectedNtf(user, contact)))
|
||||
// case let .contactConnecting(contact):
|
||||
// TODO profile update
|
||||
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):
|
||||
let cInfo = aChatItem.chatInfo
|
||||
var cItem = aChatItem.chatItem
|
||||
@@ -633,7 +274,7 @@ func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? {
|
||||
if let file = cItem.autoReceiveFile() {
|
||||
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
|
||||
case let .rcvFileSndCancelled(_, 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
|
||||
return (
|
||||
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:
|
||||
logger.debug("NotificationService receivedMsgNtf ignored event: \(res.responseType)")
|
||||
logger.debug("NotificationService processReceivedMsg ignored event: \(res.responseType)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -675,22 +308,17 @@ func updateNetCfg() {
|
||||
try setNetworkConfig(networkConfig)
|
||||
networkConfig = newNetConfig
|
||||
} 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? {
|
||||
let r = sendSimpleXCmd(.showActiveUser)
|
||||
logger.debug("apiGetActiveUser sendSimpleXCmd response: \(r.responseType)")
|
||||
logger.debug("apiGetActiveUser sendSimpleXCmd response: \(String(describing: r))")
|
||||
switch r {
|
||||
case let .activeUser(user): return user
|
||||
case .chatCmdError(_, .error(.noActiveUser)):
|
||||
logger.debug("apiGetActiveUser sendSimpleXCmd no active user")
|
||||
return nil
|
||||
case let .chatCmdError(_, err):
|
||||
logger.debug("apiGetActiveUser sendSimpleXCmd error: \(String(describing: err))")
|
||||
return nil
|
||||
case .chatCmdError(_, .error(.noActiveUser)): return nil
|
||||
default:
|
||||
logger.error("NotificationService apiGetActiveUser unexpected response: \(String(describing: r))")
|
||||
return nil
|
||||
@@ -698,7 +326,7 @@ func apiGetActiveUser() -> User? {
|
||||
}
|
||||
|
||||
func apiStartChat() throws -> Bool {
|
||||
let r = sendSimpleXCmd(.startChat(mainApp: false))
|
||||
let r = sendSimpleXCmd(.startChat(subscribe: false, expire: false, xftp: false))
|
||||
switch r {
|
||||
case .chatStarted: return true
|
||||
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 {
|
||||
let r = sendSimpleXCmd(.setTempFolder(tempFolder: tempFolder))
|
||||
if case .cmdOk = r { return }
|
||||
@@ -751,13 +364,12 @@ func apiGetNtfMessage(nonce: String, encNtfInfo: String) -> NtfMessages? {
|
||||
return nil
|
||||
}
|
||||
let r = sendSimpleXCmd(.apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo))
|
||||
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)
|
||||
if case let .ntfMessages(user, connEntity, msgTs, ntfMessages) = r, let user = user {
|
||||
return NtfMessages(user: user, connEntity: connEntity, msgTs: msgTs, ntfMessages: ntfMessages)
|
||||
} else if case let .chatCmdError(_, error) = r {
|
||||
logger.debug("apiGetNtfMessage error response: \(String.init(describing: error))")
|
||||
} 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
|
||||
}
|
||||
@@ -793,11 +405,11 @@ func setNetworkConfig(_ cfg: NetCfg) throws {
|
||||
|
||||
struct NtfMessages {
|
||||
var user: User
|
||||
var connEntity_: ConnectionEntity?
|
||||
var connEntity: ConnectionEntity?
|
||||
var msgTs: Date?
|
||||
var ntfMessages: [NtfMsgInfo]
|
||||
|
||||
var ntfsEnabled: Bool {
|
||||
user.showNotifications && (connEntity_?.ntfsEnabled ?? false)
|
||||
user.showNotifications && (connEntity?.ntfsEnabled ?? false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,6 @@
|
||||
5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; };
|
||||
5C13730B28156D2700F43030 /* ContactConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C13730A28156D2700F43030 /* ContactConnectionView.swift */; };
|
||||
5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; };
|
||||
5C245F232B4EAA5E001CC39F /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C245F1E2B4EAA5E001CC39F /* libgmpxx.a */; };
|
||||
5C245F242B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C245F1F2B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH-ghc9.6.3.a */; };
|
||||
5C245F252B4EAA5E001CC39F /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C245F202B4EAA5E001CC39F /* libgmp.a */; };
|
||||
5C245F262B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C245F212B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH.a */; };
|
||||
5C245F272B4EAA5E001CC39F /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C245F222B4EAA5E001CC39F /* libffi.a */; };
|
||||
5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; };
|
||||
5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260A27A30CFA00F70299 /* ChatListView.swift */; };
|
||||
5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260E27A30FDC00F70299 /* ChatView.swift */; };
|
||||
@@ -123,8 +118,11 @@
|
||||
5CCB939C297EFCB100399E78 /* NavStackCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCB939B297EFCB100399E78 /* NavStackCompat.swift */; };
|
||||
5CCD403427A5F6DF00368C90 /* AddContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* AddContactView.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, ); }; };
|
||||
5CD67B902B0E858A00C510B1 /* hs_init.c in Sources */ = {isa = PBXBuildFile; fileRef = 5CD67B8E2B0E858A00C510B1 /* hs_init.c */; };
|
||||
5CDA5A2D2B04FE2D00A71D61 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CDA5A282B04FE2D00A71D61 /* libgmp.a */; };
|
||||
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 */; };
|
||||
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, ); }; };
|
||||
@@ -150,9 +148,6 @@
|
||||
5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; };
|
||||
5CEBD7462A5C0A8F00665FE2 /* KeyboardPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.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 */; };
|
||||
5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59CF286477B400863A68 /* ChatArchiveView.swift */; };
|
||||
5CFE0921282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; };
|
||||
@@ -168,11 +163,6 @@
|
||||
64466DC829FC2B3B00E3D48D /* CreateSimpleXAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64466DC729FC2B3B00E3D48D /* CreateSimpleXAddress.swift */; };
|
||||
64466DCC29FFE3E800E3D48D /* MailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64466DCB29FFE3E800E3D48D /* MailView.swift */; };
|
||||
6448BBB628FA9D56000D2AB9 /* GroupLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6448BBB528FA9D56000D2AB9 /* GroupLinkView.swift */; };
|
||||
6449333A2AF8E51000AC506E /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933352AF8E51000AC506E /* libgmpxx.a */; };
|
||||
6449333B2AF8E51000AC506E /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933362AF8E51000AC506E /* libgmp.a */; };
|
||||
6449333C2AF8E51000AC506E /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933372AF8E51000AC506E /* libffi.a */; };
|
||||
6449333D2AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933382AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8-ghc9.6.3.a */; };
|
||||
6449333E2AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933392AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8.a */; };
|
||||
644EFFDE292BCD9D00525D5B /* ComposeVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFDD292BCD9D00525D5B /* ComposeVoiceView.swift */; };
|
||||
644EFFE0292CFD7F00525D5B /* CIVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFDF292CFD7F00525D5B /* CIVoiceView.swift */; };
|
||||
644EFFE2292D089800525D5B /* FramedCIVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFE1292D089800525D5B /* FramedCIVoiceView.swift */; };
|
||||
@@ -193,7 +183,6 @@
|
||||
64D0C2C629FAC1EC00B38D5F /* AddContactLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C529FAC1EC00B38D5F /* AddContactLearnMore.swift */; };
|
||||
64E972072881BB22008DBC02 /* CIGroupInvitationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64E972062881BB22008DBC02 /* CIGroupInvitationView.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 */; };
|
||||
D72A9088294BD7A70047C86D /* NativeTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72A9087294BD7A70047C86D /* NativeTextEditor.swift */; };
|
||||
D741547829AF89AF0022400A /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D741547729AF89AF0022400A /* StoreKit.framework */; };
|
||||
@@ -285,11 +274,6 @@
|
||||
5C13730A28156D2700F43030 /* ContactConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactConnectionView.swift; sourceTree = "<group>"; };
|
||||
5C13730C2815740A00F43030 /* DebugJSON.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = DebugJSON.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||
5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemView.swift; sourceTree = "<group>"; };
|
||||
5C245F1E2B4EAA5E001CC39F /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5C245F1F2B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
5C245F202B4EAA5E001CC39F /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5C245F212B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH.a"; sourceTree = "<group>"; };
|
||||
5C245F222B4EAA5E001CC39F /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5C2E260627A2941F00F70299 /* SimpleXAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleXAPI.swift; sourceTree = "<group>"; };
|
||||
5C2E260A27A30CFA00F70299 /* ChatListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListView.swift; sourceTree = "<group>"; };
|
||||
5C2E260E27A30FDC00F70299 /* ChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatView.swift; sourceTree = "<group>"; };
|
||||
@@ -415,8 +399,11 @@
|
||||
5CCB939B297EFCB100399E78 /* NavStackCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavStackCompat.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>"; };
|
||||
5CD67B8D2B0E858A00C510B1 /* hs_init.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hs_init.h; sourceTree = "<group>"; };
|
||||
5CD67B8E2B0E858A00C510B1 /* hs_init.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hs_init.c; sourceTree = "<group>"; };
|
||||
5CDA5A282B04FE2D00A71D61 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; 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; };
|
||||
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>"; };
|
||||
@@ -443,9 +430,6 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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; };
|
||||
@@ -460,11 +444,6 @@
|
||||
64466DC729FC2B3B00E3D48D /* CreateSimpleXAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateSimpleXAddress.swift; sourceTree = "<group>"; };
|
||||
64466DCB29FFE3E800E3D48D /* MailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailView.swift; sourceTree = "<group>"; };
|
||||
6448BBB528FA9D56000D2AB9 /* GroupLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupLinkView.swift; sourceTree = "<group>"; };
|
||||
644933352AF8E51000AC506E /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
644933362AF8E51000AC506E /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
644933372AF8E51000AC506E /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
644933382AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
644933392AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8.a"; sourceTree = "<group>"; };
|
||||
644EFFDD292BCD9D00525D5B /* ComposeVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeVoiceView.swift; sourceTree = "<group>"; };
|
||||
644EFFDF292CFD7F00525D5B /* CIVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIVoiceView.swift; sourceTree = "<group>"; };
|
||||
644EFFE1292D089800525D5B /* FramedCIVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FramedCIVoiceView.swift; sourceTree = "<group>"; };
|
||||
@@ -487,7 +466,6 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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; };
|
||||
@@ -529,13 +507,13 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5C245F232B4EAA5E001CC39F /* libgmpxx.a in Frameworks */,
|
||||
5C245F262B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH.a in Frameworks */,
|
||||
5C245F252B4EAA5E001CC39F /* libgmp.a in Frameworks */,
|
||||
5CDA5A302B04FE2D00A71D61 /* libgmpxx.a in Frameworks */,
|
||||
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
|
||||
5C245F272B4EAA5E001CC39F /* libffi.a in Frameworks */,
|
||||
5CDA5A2D2B04FE2D00A71D61 /* libgmp.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 */,
|
||||
5C245F242B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH-ghc9.6.3.a in Frameworks */,
|
||||
5CDA5A312B04FE2D00A71D61 /* libHSsimplex-chat-5.4.0.3-rODxCBVsb2BkD1fnTAqXL.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -597,11 +575,11 @@
|
||||
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5C245F222B4EAA5E001CC39F /* libffi.a */,
|
||||
5C245F202B4EAA5E001CC39F /* libgmp.a */,
|
||||
5C245F1E2B4EAA5E001CC39F /* libgmpxx.a */,
|
||||
5C245F1F2B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH-ghc9.6.3.a */,
|
||||
5C245F212B4EAA5E001CC39F /* libHSsimplex-chat-5.4.3.0-BrNBQIZf2Ju9TtaZoeJyIH.a */,
|
||||
5CDA5A2A2B04FE2D00A71D61 /* libffi.a */,
|
||||
5CDA5A282B04FE2D00A71D61 /* libgmp.a */,
|
||||
5CDA5A2B2B04FE2D00A71D61 /* libgmpxx.a */,
|
||||
5CDA5A292B04FE2D00A71D61 /* libHSsimplex-chat-5.4.0.3-rODxCBVsb2BkD1fnTAqXL-ghc9.6.3.a */,
|
||||
5CDA5A2C2B04FE2D00A71D61 /* libHSsimplex-chat-5.4.0.3-rODxCBVsb2BkD1fnTAqXL.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
@@ -626,7 +604,6 @@
|
||||
5C35CFC727B2782E00FB6C6D /* BGManager.swift */,
|
||||
5C35CFCA27B2E91D00FB6C6D /* NtfManager.swift */,
|
||||
5CB346E42868AA7F001FD2EF /* SuspendChat.swift */,
|
||||
5CF937212B25034A00E1D781 /* NSESubscriber.swift */,
|
||||
5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */,
|
||||
5C93293E2928E0FD0090FFF9 /* AudioRecPlay.swift */,
|
||||
5CBD2859295711D700EC2CF4 /* ImageUtils.swift */,
|
||||
@@ -654,7 +631,6 @@
|
||||
64466DCB29FFE3E800E3D48D /* MailView.swift */,
|
||||
64C3B0202A0D359700E19930 /* CustomTimePicker.swift */,
|
||||
5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */,
|
||||
8C05382D2B39887E006436DC /* VideoUtils.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
@@ -808,7 +784,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5CDCAD5128186DE400503DA2 /* SimpleX NSE.entitlements */,
|
||||
5CF9371D2B23429500E1D781 /* ConcurrentQueue.swift */,
|
||||
5CDCAD472818589900503DA2 /* NotificationService.swift */,
|
||||
5CDCAD492818589900503DA2 /* Info.plist */,
|
||||
5CB0BA862826CB3A00B3292C /* InfoPlist.strings */,
|
||||
@@ -829,13 +804,10 @@
|
||||
64DAE1502809D9F5000DA960 /* FileUtils.swift */,
|
||||
5C9D81182AA7A4F1001D49FD /* CryptoFile.swift */,
|
||||
5C00168028C4FE760094D739 /* KeyChain.swift */,
|
||||
5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */,
|
||||
5CE2BA76284530BF00EC33A6 /* SimpleXChat.h */,
|
||||
5CE2BA8A2845332200EC33A6 /* SimpleX.h */,
|
||||
5CE2BA78284530CC00EC33A6 /* SimpleXChat.docc */,
|
||||
5CE2BA96284537A800EC33A6 /* dummy.m */,
|
||||
5CD67B8D2B0E858A00C510B1 /* hs_init.h */,
|
||||
5CD67B8E2B0E858A00C510B1 /* hs_init.c */,
|
||||
);
|
||||
path = SimpleXChat;
|
||||
sourceTree = "<group>";
|
||||
@@ -920,7 +892,6 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5CE2BA77284530BF00EC33A6 /* SimpleXChat.h in Headers */,
|
||||
5CD67B8F2B0E858A00C510B1 /* hs_init.h in Headers */,
|
||||
5CE2BA952845354B00EC33A6 /* SimpleX.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -1202,7 +1173,6 @@
|
||||
5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */,
|
||||
5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */,
|
||||
6442E0BA287F169300CEC0F9 /* AddGroupView.swift in Sources */,
|
||||
5CF937232B2503D000E1D781 /* NSESubscriber.swift in Sources */,
|
||||
6419EC582AB97507004A607A /* CIMemberCreatedContactView.swift in Sources */,
|
||||
64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */,
|
||||
64466DCC29FFE3E800E3D48D /* MailView.swift in Sources */,
|
||||
@@ -1225,7 +1195,6 @@
|
||||
5CCD403727A5F9A200368C90 /* ScanToConnectView.swift in Sources */,
|
||||
5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */,
|
||||
649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */,
|
||||
8C05382E2B39887E006436DC /* VideoUtils.swift in Sources */,
|
||||
5CADE79C292131E900072E13 /* ContactPreferencesView.swift in Sources */,
|
||||
5CB346E52868AA7F001FD2EF /* SuspendChat.swift in Sources */,
|
||||
5C9C2DA52894777E00CC63B1 /* GroupProfileView.swift in Sources */,
|
||||
@@ -1283,7 +1252,6 @@
|
||||
files = (
|
||||
5CDCAD482818589900503DA2 /* NotificationService.swift in Sources */,
|
||||
5CFE0922282EEAF60002594B /* ZoomableScrollView.swift in Sources */,
|
||||
5CF9371E2B23429500E1D781 /* ConcurrentQueue.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -1291,11 +1259,9 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5CF937202B24DE8C00E1D781 /* SharedFileSubscriber.swift in Sources */,
|
||||
5C00168128C4FE760094D739 /* KeyChain.swift in Sources */,
|
||||
5CE2BA97284537A800EC33A6 /* dummy.m in Sources */,
|
||||
5CE2BA922845340900EC33A6 /* FileUtils.swift in Sources */,
|
||||
5CD67B902B0E858A00C510B1 /* hs_init.c in Sources */,
|
||||
5CE2BA91284533A300EC33A6 /* Notifications.swift in Sources */,
|
||||
5CE2BA79284530CC00EC33A6 /* SimpleXChat.docc in Sources */,
|
||||
5CE2BA90284533A300EC33A6 /* JSON.swift in Sources */,
|
||||
@@ -1528,7 +1494,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 188;
|
||||
CURRENT_PROJECT_VERSION = 181;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1550,7 +1516,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.4.3;
|
||||
MARKETING_VERSION = 5.4;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||
PRODUCT_NAME = SimpleX;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -1571,7 +1537,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 188;
|
||||
CURRENT_PROJECT_VERSION = 181;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1593,7 +1559,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.4.3;
|
||||
MARKETING_VERSION = 5.4;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||
PRODUCT_NAME = SimpleX;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -1652,7 +1618,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 188;
|
||||
CURRENT_PROJECT_VERSION = 181;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1665,7 +1631,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.4.3;
|
||||
MARKETING_VERSION = 5.4;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -1684,7 +1650,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 188;
|
||||
CURRENT_PROJECT_VERSION = 181;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1697,7 +1663,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.4.3;
|
||||
MARKETING_VERSION = 5.4;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -1716,7 +1682,7 @@
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 188;
|
||||
CURRENT_PROJECT_VERSION = 181;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
@@ -1740,7 +1706,7 @@
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Libraries/sim",
|
||||
);
|
||||
MARKETING_VERSION = 5.4.3;
|
||||
MARKETING_VERSION = 5.4;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SDKROOT = iphoneos;
|
||||
@@ -1762,7 +1728,7 @@
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 188;
|
||||
CURRENT_PROJECT_VERSION = 181;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
@@ -1786,7 +1752,7 @@
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Libraries/sim",
|
||||
);
|
||||
MARKETING_VERSION = 5.4.3;
|
||||
MARKETING_VERSION = 5.4;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SDKROOT = iphoneos;
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
buildConfiguration = "Release"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1400"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "1.3">
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@@ -47,14 +47,16 @@
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
|
||||
@@ -12,16 +12,12 @@ private var chatController: chat_ctrl?
|
||||
|
||||
private var migrationResult: (Bool, DBMigrationResult)?
|
||||
|
||||
public func hasChatCtrl() -> Bool {
|
||||
chatController != nil
|
||||
}
|
||||
|
||||
public func getChatCtrl() -> chat_ctrl {
|
||||
public func getChatCtrl(_ useKey: String? = nil) -> chat_ctrl {
|
||||
if let controller = chatController { return controller }
|
||||
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 }
|
||||
let dbPath = getAppDatabasePath().path
|
||||
var dbKey = ""
|
||||
@@ -45,7 +41,7 @@ public func chatMigrateInit(_ useKey: String? = nil, confirmMigrations: Migratio
|
||||
var cKey = dbKey.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
|
||||
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 encrypted = 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() {
|
||||
chatController = nil
|
||||
migrationResult = nil
|
||||
|
||||
@@ -25,9 +25,9 @@ public enum ChatCommand {
|
||||
case apiMuteUser(userId: Int64)
|
||||
case apiUnmuteUser(userId: Int64)
|
||||
case apiDeleteUser(userId: Int64, delSMPQueues: Bool, viewPwd: String?)
|
||||
case startChat(mainApp: Bool)
|
||||
case startChat(subscribe: Bool, expire: Bool, xftp: Bool)
|
||||
case apiStopChat
|
||||
case apiActivateChat(restoreChat: Bool)
|
||||
case apiActivateChat
|
||||
case apiSuspendChat(timeoutMicroseconds: Int)
|
||||
case setTempFolder(tempFolder: String)
|
||||
case setFilesFolder(filesFolder: String)
|
||||
@@ -154,9 +154,9 @@ public enum ChatCommand {
|
||||
case let .apiMuteUser(userId): return "/_mute 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 .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 let .apiActivateChat(restore): return "/_app activate restore=\(onOff(restore))"
|
||||
case .apiActivateChat: return "/_app activate"
|
||||
case let .apiSuspendChat(timeoutMicroseconds): return "/_app suspend \(timeoutMicroseconds)"
|
||||
case let .setTempFolder(tempFolder): return "/_temp_folder \(tempFolder)"
|
||||
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 connectionIncognitoUpdated(user: UserRef, toConnection: PendingContactConnection)
|
||||
case connectionPlan(user: UserRef, connectionPlan: ConnectionPlan)
|
||||
case sentConfirmation(user: UserRef, connection: PendingContactConnection)
|
||||
case sentInvitation(user: UserRef, connection: PendingContactConnection)
|
||||
case sentConfirmation(user: UserRef)
|
||||
case sentInvitation(user: UserRef)
|
||||
case sentInvitationToContact(user: UserRef, contact: Contact, customUserProfile: Profile?)
|
||||
case contactAlreadyExists(user: UserRef, contact: Contact)
|
||||
case contactRequestAlreadyAccepted(user: UserRef, contact: Contact)
|
||||
@@ -604,16 +604,16 @@ public enum ChatResponse: Decodable, Error {
|
||||
case callInvitations(callInvitations: [RcvCallInvitation])
|
||||
case ntfTokenStatus(status: NtfTknStatus)
|
||||
case ntfToken(token: DeviceToken, status: NtfTknStatus, ntfMode: NotificationsMode)
|
||||
case ntfMessages(user_: User?, connEntity_: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo])
|
||||
case ntfMessage(user: UserRef, connEntity: ConnectionEntity, ntfMessage: NtfMsgInfo)
|
||||
case ntfMessages(user_: User?, connEntity: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo])
|
||||
case newContactConnection(user: UserRef, connection: PendingContactConnection)
|
||||
case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection)
|
||||
// remote desktop responses/events
|
||||
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 remoteCtrlSessionCode(remoteCtrl_: RemoteCtrlInfo?, sessionCode: String)
|
||||
case remoteCtrlConnected(remoteCtrl: RemoteCtrlInfo)
|
||||
case remoteCtrlStopped(rcsState: RemoteCtrlSessionState, rcStopReason: RemoteCtrlStopReason)
|
||||
case remoteCtrlStopped
|
||||
// misc
|
||||
case versionInfo(versionInfo: CoreVersionInfo, chatMigrations: [UpMigration], agentMigrations: [UpMigration])
|
||||
case cmdOk(user: UserRef?)
|
||||
@@ -752,7 +752,7 @@ public enum ChatResponse: Decodable, Error {
|
||||
case .ntfTokenStatus: return "ntfTokenStatus"
|
||||
case .ntfToken: return "ntfToken"
|
||||
case .ntfMessages: return "ntfMessages"
|
||||
case .ntfMessage: return "ntfMessage"
|
||||
case .newContactConnection: return "newContactConnection"
|
||||
case .contactConnectionDeleted: return "contactConnectionDeleted"
|
||||
case .remoteCtrlList: return "remoteCtrlList"
|
||||
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 .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 .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 .connectionPlan(u, connectionPlan): return withUser(u, String(describing: connectionPlan))
|
||||
case let .sentConfirmation(u, connection): return withUser(u, String(describing: connection))
|
||||
case let .sentInvitation(u, connection): return withUser(u, String(describing: connection))
|
||||
case .sentConfirmation: return noDetails
|
||||
case .sentInvitation: return noDetails
|
||||
case let .sentInvitationToContact(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))
|
||||
@@ -900,10 +900,10 @@ public enum ChatResponse: Decodable, Error {
|
||||
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 .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 .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 .remoteCtrlSessionCode(remoteCtrl_, sessionCode): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nsessionCode: \(sessionCode)"
|
||||
case let .remoteCtrlConnected(remoteCtrl): return String(describing: remoteCtrl)
|
||||
@@ -1207,9 +1207,9 @@ public struct NetCfg: Codable, Equatable {
|
||||
public static let defaults: NetCfg = NetCfg(
|
||||
socksProxy: nil,
|
||||
sessionMode: TransportSessionMode.user,
|
||||
tcpConnectTimeout: 20_000_000,
|
||||
tcpTimeout: 15_000_000,
|
||||
tcpTimeoutPerKb: 45_000,
|
||||
tcpConnectTimeout: 15_000_000,
|
||||
tcpTimeout: 10_000_000,
|
||||
tcpTimeoutPerKb: 30_000,
|
||||
tcpKeepAlive: KeepAliveOpts.defaults,
|
||||
smpPingInterval: 1200_000_000,
|
||||
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 {
|
||||
case off = "OFF"
|
||||
case periodic = "PERIODIC"
|
||||
@@ -1507,9 +1505,9 @@ public enum NotificationsMode: String, Decodable, SelectableItem {
|
||||
|
||||
public var label: LocalizedStringKey {
|
||||
switch self {
|
||||
case .off: "Local"
|
||||
case .periodic: "Periodically"
|
||||
case .instant: "Instantly"
|
||||
case .off: return "Off (Local)"
|
||||
case .periodic: return "Periodically"
|
||||
case .instant: return "Instantly"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1548,19 +1546,11 @@ public struct RemoteCtrlInfo: Decodable {
|
||||
|
||||
public enum RemoteCtrlSessionState: Decodable {
|
||||
case starting
|
||||
case searching
|
||||
case connecting
|
||||
case pendingConfirmation(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 var appVersionRange: AppVersionRange
|
||||
public var deviceName: String
|
||||
|
||||
@@ -9,16 +9,12 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
public let appSuspendTimeout: Int = 15 // seconds
|
||||
|
||||
let GROUP_DEFAULT_APP_STATE = "appState"
|
||||
let GROUP_DEFAULT_NSE_STATE = "nseState"
|
||||
let GROUP_DEFAULT_DB_CONTAINER = "dbContainer"
|
||||
public let GROUP_DEFAULT_CHAT_LAST_START = "chatLastStart"
|
||||
public let GROUP_DEFAULT_CHAT_LAST_BACKGROUND_RUN = "chatLastBackgroundRun"
|
||||
let GROUP_DEFAULT_NTF_PREVIEW_MODE = "ntfPreviewMode"
|
||||
public let GROUP_DEFAULT_NTF_ENABLE_LOCAL = "ntfEnableLocal" // no longer used
|
||||
public let GROUP_DEFAULT_NTF_ENABLE_PERIODIC = "ntfEnablePeriodic" // no longer used
|
||||
public let GROUP_DEFAULT_NTF_ENABLE_LOCAL = "ntfEnableLocal"
|
||||
public let GROUP_DEFAULT_NTF_ENABLE_PERIODIC = "ntfEnablePeriodic"
|
||||
let GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES = "privacyAcceptImages"
|
||||
public let GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE = "privacyTransferImagesInline" // no longer used
|
||||
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 activating
|
||||
case bgRefresh
|
||||
case suspending
|
||||
case suspended
|
||||
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 {
|
||||
switch self {
|
||||
case .suspending: return true
|
||||
@@ -98,57 +84,23 @@ public enum AppState: String, Codable {
|
||||
public var canSuspend: Bool {
|
||||
switch self {
|
||||
case .active: return true
|
||||
case .activating: return true
|
||||
case .bgRefresh: return true
|
||||
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 {
|
||||
case documents
|
||||
case group
|
||||
}
|
||||
|
||||
// appStateGroupDefault must not be used in the app directly, only via AppChatState singleton
|
||||
public let appStateGroupDefault = EnumDefault<AppState>(
|
||||
defaults: groupDefaults,
|
||||
forKey: GROUP_DEFAULT_APP_STATE,
|
||||
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>(
|
||||
defaults: groupDefaults,
|
||||
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 chatLastBackgroundRunGroupDefault = DateDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_CHAT_LAST_BACKGROUND_RUN)
|
||||
|
||||
public let ntfPreviewModeGroupDefault = EnumDefault<NotificationPreviewMode>(
|
||||
defaults: groupDefaults,
|
||||
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 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 privacyEncryptLocalFilesGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES)
|
||||
|
||||
@@ -1847,7 +1847,7 @@ public struct GroupMember: Identifiable, Decodable {
|
||||
public func canChangeRoleTo(groupInfo: GroupInfo) -> [GroupMemberRole]? {
|
||||
if !canBeRemoved(groupInfo: groupInfo) { return nil }
|
||||
let userRole = groupInfo.membership.memberRole
|
||||
return GroupMemberRole.allCases.filter { $0 <= userRole && $0 != .author }
|
||||
return GroupMemberRole.allCases.filter { $0 <= userRole }
|
||||
}
|
||||
|
||||
public var memberIncognito: Bool {
|
||||
@@ -1887,7 +1887,6 @@ public struct GroupMemberIds: Decodable {
|
||||
|
||||
public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Decodable {
|
||||
case observer = "observer"
|
||||
case author = "author"
|
||||
case member = "member"
|
||||
case admin = "admin"
|
||||
case owner = "owner"
|
||||
@@ -1897,7 +1896,6 @@ public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Dec
|
||||
public var text: String {
|
||||
switch self {
|
||||
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 .admin: return NSLocalizedString("admin", 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 {
|
||||
switch self {
|
||||
case .observer: return 0
|
||||
case .author: return 1
|
||||
case .member: return 2
|
||||
case .admin: return 3
|
||||
case .owner: return 4
|
||||
case .member: return 1
|
||||
case .admin: return 2
|
||||
case .owner: return 3
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2016,8 +2013,7 @@ public enum ConnectionEntity: Decodable {
|
||||
}
|
||||
|
||||
public struct NtfMsgInfo: Decodable {
|
||||
public var msgId: String
|
||||
public var msgTs: Date
|
||||
|
||||
}
|
||||
|
||||
public struct AChatItem: Decodable {
|
||||
|
||||
@@ -17,7 +17,7 @@ public func writeCryptoFile(path: String, data: Data) throws -> CryptoFileArgs {
|
||||
let ptr: UnsafeMutableRawPointer = malloc(data.count)
|
||||
memcpy(ptr, (data as NSData).bytes, data.count)
|
||||
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)!
|
||||
switch try jsonDecoder.decode(WriteFileResult.self, from: d) {
|
||||
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 {
|
||||
var cFromPath = fromPath.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)!
|
||||
switch try jsonDecoder.decode(WriteFileResult.self, from: d) {
|
||||
case let .result(cfArgs): return cfArgs
|
||||
|
||||
@@ -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 {
|
||||
hideContent
|
||||
? 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))
|
||||
}
|
||||
@@ -9,27 +9,27 @@
|
||||
#ifndef SimpleX_h
|
||||
#define SimpleX_h
|
||||
|
||||
#include "hs_init.h"
|
||||
#endif /* SimpleX_h */
|
||||
|
||||
extern void hs_init(int argc, char **argv[]);
|
||||
|
||||
typedef void* chat_ctrl;
|
||||
|
||||
// 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_reopen_store(chat_ctrl ctl);
|
||||
extern char *chat_send_cmd(chat_ctrl ctl, char *cmd);
|
||||
extern char *chat_recv_msg(chat_ctrl ctl);
|
||||
extern char *chat_recv_msg_wait(chat_ctrl ctl, int wait);
|
||||
extern char *chat_parse_markdown(char *str);
|
||||
extern char *chat_parse_server(char *str);
|
||||
extern char *chat_password_hash(char *pwd, char *salt);
|
||||
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);
|
||||
|
||||
// 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:
|
||||
// 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);
|
||||
|
||||
// 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
|
||||
extern char *chat_decrypt_file(char *fromPath, char *key, char *nonce, char *toPath);
|
||||
|
||||
#endif /* SimpleX_h */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -25,9 +25,6 @@
|
||||
/* 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!";
|
||||
|
||||
/* 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. */
|
||||
"- 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. */
|
||||
"(new)" = "(Neu)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"(this device v%@)" = "(Dieses Gerät hat v%@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
")" = ")";
|
||||
|
||||
@@ -545,9 +536,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Authentication unavailable" = "Authentifizierung nicht verfügbar";
|
||||
|
||||
/* member role */
|
||||
"author" = "Autor";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Auto-accept" = "Automatisch akzeptieren";
|
||||
|
||||
@@ -560,9 +548,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Back" = "Zurück";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Bad desktop address" = "Falsche Desktop-Adresse";
|
||||
|
||||
/* integrity error chat item */
|
||||
"bad message hash" = "Ungültiger Nachrichten-Hash";
|
||||
|
||||
@@ -575,18 +560,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Bad message ID" = "Falsche Nachrichten-ID";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Better groups" = "Bessere Gruppen";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Better messages" = "Verbesserungen bei Nachrichten";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block" = "Blockieren";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block group members" = "Gruppenmitglieder blockieren";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block member" = "Mitglied blockieren";
|
||||
|
||||
@@ -789,15 +768,9 @@
|
||||
/* server test step */
|
||||
"Connect" = "Verbinden";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect automatically" = "Automatisch verbinden";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect incognito" = "Inkognito verbinden";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect to desktop" = "Mit dem Desktop verbinden";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"connect to SimpleX Chat developers." = "Mit den SimpleX Chat-Entwicklern verbinden.";
|
||||
|
||||
@@ -828,15 +801,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"connected" = "Verbunden";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connected desktop" = "Verbundener Desktop";
|
||||
|
||||
/* rcv group event chat item */
|
||||
"connected directly" = "Direkt miteinander verbunden";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connected to desktop" = "Mit dem Desktop verbunden";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"connecting" = "verbinde";
|
||||
|
||||
@@ -861,9 +828,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting server… (error: %@)" = "Mit dem Server verbinden… (Fehler: %@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting to desktop" = "Mit dem Desktop verbinden";
|
||||
|
||||
/* chat list item title */
|
||||
"connecting…" = "Verbinde…";
|
||||
|
||||
@@ -882,9 +846,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Connection request sent!" = "Verbindungsanfrage wurde gesendet!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connection terminated" = "Verbindung beendet";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connection timeout" = "Verbindungszeitüberschreitung";
|
||||
|
||||
@@ -939,9 +900,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"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. */
|
||||
"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. */
|
||||
"Develop" = "Entwicklung";
|
||||
|
||||
@@ -1287,15 +1236,9 @@
|
||||
/* server test step */
|
||||
"Disconnect" = "Trennen";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Disconnect desktop?" = "Desktop-Verbindung trennen?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Do it later" = "Später wiederholen";
|
||||
|
||||
@@ -1431,12 +1374,6 @@
|
||||
/* chat item text */
|
||||
"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 */
|
||||
"encryption re-negotiation required" = "Neuaushandlung der Verschlüsselung notwendig";
|
||||
|
||||
@@ -1467,9 +1404,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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 */
|
||||
"Enter welcome message…" = "Geben Sie eine Begrüßungsmeldung ein …";
|
||||
|
||||
@@ -1671,9 +1605,6 @@
|
||||
/* 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!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Faster joining and more reliable messages." = "Schnellerer Gruppenbeitritt und zuverlässigere Nachrichtenzustellung.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Favorite" = "Favorit";
|
||||
|
||||
@@ -1731,9 +1662,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"For console" = "Für Konsole";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Found desktop" = "Gefundener Desktop";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"French interface" = "Französische Bedienoberfläche";
|
||||
|
||||
@@ -1938,9 +1866,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito" = "Inkognito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito groups" = "Inkognito-Gruppen";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito mode" = "Inkognito-Modus";
|
||||
|
||||
@@ -1968,9 +1893,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Incompatible database version" = "Inkompatible Datenbank-Version";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incompatible version" = "Inkompatible Version";
|
||||
|
||||
/* PIN entry */
|
||||
"Incorrect passcode" = "Zugangscode ist falsch";
|
||||
|
||||
@@ -2106,9 +2028,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Keep your connections" = "Ihre Verbindungen beibehalten";
|
||||
|
||||
@@ -2145,15 +2064,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"LIVE" = "LIVE";
|
||||
|
||||
@@ -2409,9 +2319,6 @@
|
||||
/* copied message info in history */
|
||||
"no text" = "Kein Text";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Not compatible!" = "Nicht kompatibel!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Notifications" = "Benachrichtigungen";
|
||||
|
||||
@@ -2555,9 +2462,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Paste" = "Einfügen";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Paste desktop address" = "Desktop-Adresse einfügen";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Paste image" = "Bild einfügen";
|
||||
|
||||
@@ -2942,9 +2846,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"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. */
|
||||
"Servers" = "Server";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Session code" = "Sitzungscode";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Set 1 day" = "Einen Tag festlegen";
|
||||
|
||||
@@ -3401,9 +3299,6 @@
|
||||
/* notification title */
|
||||
"this contact" = "Dieser Kontakt";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This device name" = "Dieser Gerätename";
|
||||
|
||||
/* 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.";
|
||||
|
||||
@@ -3425,9 +3320,6 @@
|
||||
/* 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.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To hide unwanted messages." = "Um unerwünschte Nachrichten zu verbergen.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To make a new connection" = "Um eine Verbindung mit einem neuen Kontakt zu erstellen";
|
||||
|
||||
@@ -3524,12 +3416,6 @@
|
||||
/* 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.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlink" = "Entkoppeln";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlink desktop?" = "Desktop entkoppeln?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlock" = "Entsperren";
|
||||
|
||||
@@ -3584,9 +3470,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Use iOS call interface" = "iOS Anrufschnittstelle nutzen";
|
||||
|
||||
@@ -3608,24 +3491,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Using SimpleX Chat servers." = "Verwendung von SimpleX-Chat-Servern.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"v%@" = "v%@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Verify connection security" = "Sicherheit der Verbindung überprüfen";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Verify connections" = "Verbindungen überprüfen";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Verify security code" = "Sicherheitscode überprüfen";
|
||||
|
||||
@@ -3644,9 +3515,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Video call" = "Videoanruf";
|
||||
|
||||
@@ -3686,9 +3554,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Waiting for file" = "Warte auf Datei";
|
||||
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
/* Privacy - Face ID Usage Description */
|
||||
"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 */
|
||||
"NSMicrophoneUsageDescription" = "SimpleX benötigt Zugriff auf das Mikrofon, um Audio- und Videoanrufe und die Aufnahme von Sprachnachrichten zu ermöglichen.";
|
||||
|
||||
|
||||
@@ -25,9 +25,6 @@
|
||||
/* 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 !";
|
||||
|
||||
/* 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. */
|
||||
"- 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. */
|
||||
"(new)" = "(nouveau)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"(this device v%@)" = "(cet appareil v%@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
")" = ")";
|
||||
|
||||
@@ -545,9 +536,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Authentication unavailable" = "Authentification indisponible";
|
||||
|
||||
/* member role */
|
||||
"author" = "auteur";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Auto-accept" = "Auto-accepter";
|
||||
|
||||
@@ -560,9 +548,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Back" = "Retour";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Bad desktop address" = "Mauvaise adresse de bureau";
|
||||
|
||||
/* integrity error chat item */
|
||||
"bad message hash" = "hash de message incorrect";
|
||||
|
||||
@@ -575,18 +560,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Bad message ID" = "Mauvais ID de message";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Better groups" = "Des groupes plus performants";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Better messages" = "Meilleurs messages";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block" = "Bloquer";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block group members" = "Bloquer des membres d'un groupe";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block member" = "Bloquer ce membre";
|
||||
|
||||
@@ -789,15 +768,9 @@
|
||||
/* server test step */
|
||||
"Connect" = "Se connecter";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect automatically" = "Connexion automatique";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect incognito" = "Se connecter incognito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect to desktop" = "Se connecter au bureau";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"connect to SimpleX Chat developers." = "se connecter aux developpeurs de SimpleX Chat.";
|
||||
|
||||
@@ -828,15 +801,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"connected" = "connecté";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connected desktop" = "Bureau connecté";
|
||||
|
||||
/* rcv group event chat item */
|
||||
"connected directly" = "s'est connecté.e de manière directe";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connected to desktop" = "Connecté au bureau";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"connecting" = "connexion";
|
||||
|
||||
@@ -861,9 +828,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting server… (error: %@)" = "Connexion au serveur… (erreur : %@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting to desktop" = "Connexion au bureau";
|
||||
|
||||
/* chat list item title */
|
||||
"connecting…" = "connexion…";
|
||||
|
||||
@@ -882,9 +846,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Connection request sent!" = "Demande de connexion envoyée !";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connection terminated" = "Connexion terminée";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connection timeout" = "Délai de connexion";
|
||||
|
||||
@@ -939,9 +900,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"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. */
|
||||
"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. */
|
||||
"Develop" = "Développer";
|
||||
|
||||
@@ -1287,15 +1236,9 @@
|
||||
/* server test step */
|
||||
"Disconnect" = "Se déconnecter";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Disconnect desktop?" = "Déconnecter le bureau ?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Do it later" = "Faites-le plus tard";
|
||||
|
||||
@@ -1431,12 +1374,6 @@
|
||||
/* chat item text */
|
||||
"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 */
|
||||
"encryption re-negotiation required" = "renégociation de chiffrement requise";
|
||||
|
||||
@@ -1467,9 +1404,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Enter server manually" = "Entrer un serveur manuellement";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enter this device name…" = "Entrez le nom de l'appareil…";
|
||||
|
||||
/* placeholder */
|
||||
"Enter welcome message…" = "Entrez un message de bienvenue…";
|
||||
|
||||
@@ -1671,9 +1605,6 @@
|
||||
/* 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 !";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Faster joining and more reliable messages." = "Connexion plus rapide et messages plus fiables.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Favorite" = "Favoris";
|
||||
|
||||
@@ -1731,9 +1662,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"For console" = "Pour la console";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Found desktop" = "Bureau trouvé";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"French interface" = "Interface en français";
|
||||
|
||||
@@ -1938,9 +1866,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito" = "Incognito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito groups" = "Groupes incognito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito mode" = "Mode Incognito";
|
||||
|
||||
@@ -1968,9 +1893,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Incompatible database version" = "Version de la base de données incompatible";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incompatible version" = "Version incompatible";
|
||||
|
||||
/* PIN entry */
|
||||
"Incorrect passcode" = "Code d'accès erroné";
|
||||
|
||||
@@ -2106,9 +2028,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Keep your connections" = "Conserver vos connexions";
|
||||
|
||||
@@ -2145,15 +2064,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"LIVE" = "LIVE";
|
||||
|
||||
@@ -2409,9 +2319,6 @@
|
||||
/* copied message info in history */
|
||||
"no text" = "aucun texte";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Not compatible!" = "Non compatible !";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Notifications" = "Notifications";
|
||||
|
||||
@@ -2555,9 +2462,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Paste" = "Coller";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Paste desktop address" = "Coller l'adresse du bureau";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Paste image" = "Coller l'image";
|
||||
|
||||
@@ -2942,9 +2846,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"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. */
|
||||
"Servers" = "Serveurs";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Session code" = "Code de session";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Set 1 day" = "Définir 1 jour";
|
||||
|
||||
@@ -3401,9 +3299,6 @@
|
||||
/* notification title */
|
||||
"this contact" = "ce contact";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This device name" = "Ce nom d'appareil";
|
||||
|
||||
/* 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.";
|
||||
|
||||
@@ -3425,9 +3320,6 @@
|
||||
/* 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.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To hide unwanted messages." = "Pour cacher les messages indésirables.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To make a new connection" = "Pour établir une nouvelle connexion";
|
||||
|
||||
@@ -3524,12 +3416,6 @@
|
||||
/* 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.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlink" = "Délier";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlink desktop?" = "Délier le bureau ?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlock" = "Déverrouiller";
|
||||
|
||||
@@ -3584,9 +3470,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Use iOS call interface" = "Utiliser l'interface d'appel d'iOS";
|
||||
|
||||
@@ -3608,24 +3491,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Using SimpleX Chat servers." = "Utilisation des serveurs SimpleX Chat.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"v%@" = "v%@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"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. */
|
||||
"Verify security code" = "Vérifier le code de sécurité";
|
||||
|
||||
@@ -3644,9 +3515,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Video call" = "Appel vidéo";
|
||||
|
||||
@@ -3686,9 +3554,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"waiting for confirmation…" = "en attente de confirmation…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Waiting for desktop..." = "En attente du bureau...";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Waiting for file" = "En attente du fichier";
|
||||
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
/* Privacy - Face ID Usage Description */
|
||||
"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 */
|
||||
"NSMicrophoneUsageDescription" = "SimpleX a besoin d'un accès au microphone pour les appels audio et vidéo ainsi que pour enregistrer des messages vocaux.";
|
||||
|
||||
|
||||
@@ -25,9 +25,6 @@
|
||||
/* 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!";
|
||||
|
||||
/* 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. */
|
||||
"- 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. */
|
||||
"(new)" = "(nuovo)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"(this device v%@)" = "(questo dispositivo v%@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
")" = ")";
|
||||
|
||||
@@ -557,9 +548,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Back" = "Indietro";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Bad desktop address" = "Indirizzo desktop errato";
|
||||
|
||||
/* integrity error chat item */
|
||||
"bad message hash" = "hash del messaggio errato";
|
||||
|
||||
@@ -572,18 +560,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Bad message ID" = "ID del messaggio errato";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Better groups" = "Gruppi migliorati";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Better messages" = "Messaggi migliorati";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block" = "Blocca";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block group members" = "Blocca i membri dei gruppi";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block member" = "Blocca membro";
|
||||
|
||||
@@ -789,9 +771,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Connect incognito" = "Connetti in incognito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect to desktop" = "Connetti al desktop";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"connect to SimpleX Chat developers." = "connettiti agli sviluppatori di SimpleX Chat.";
|
||||
|
||||
@@ -822,15 +801,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"connected" = "connesso/a";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connected desktop" = "Desktop connesso";
|
||||
|
||||
/* rcv group event chat item */
|
||||
"connected directly" = "si è connesso/a direttamente";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connected to desktop" = "Connesso al desktop";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"connecting" = "in connessione";
|
||||
|
||||
@@ -855,9 +828,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting server… (error: %@)" = "Connessione al server… (errore: %@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting to desktop" = "Connessione al desktop";
|
||||
|
||||
/* chat list item title */
|
||||
"connecting…" = "in connessione…";
|
||||
|
||||
@@ -876,9 +846,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Connection request sent!" = "Richiesta di connessione inviata!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connection terminated" = "Connessione terminata";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connection timeout" = "Connessione scaduta";
|
||||
|
||||
@@ -933,9 +900,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"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. */
|
||||
"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. */
|
||||
"Develop" = "Sviluppa";
|
||||
|
||||
@@ -1281,9 +1236,6 @@
|
||||
/* server test step */
|
||||
"Disconnect" = "Disconnetti";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Disconnect desktop?" = "Disconnettere il desktop?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Discover and join groups" = "Scopri ed unisciti ai gruppi";
|
||||
|
||||
@@ -1422,12 +1374,6 @@
|
||||
/* chat item text */
|
||||
"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 */
|
||||
"encryption re-negotiation required" = "richiesta rinegoziazione della crittografia";
|
||||
|
||||
@@ -1458,9 +1404,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Enter server manually" = "Inserisci il server a mano";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enter this device name…" = "Inserisci il nome di questo dispositivo…";
|
||||
|
||||
/* placeholder */
|
||||
"Enter welcome message…" = "Inserisci il messaggio di benvenuto…";
|
||||
|
||||
@@ -1662,9 +1605,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Favorite" = "Preferito";
|
||||
|
||||
@@ -1926,9 +1866,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito" = "Incognito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito groups" = "Gruppi in incognito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito mode" = "Modalità incognito";
|
||||
|
||||
@@ -1956,9 +1893,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Incompatible database version" = "Versione del database incompatibile";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incompatible version" = "Versione incompatibile";
|
||||
|
||||
/* PIN entry */
|
||||
"Incorrect passcode" = "Codice di accesso errato";
|
||||
|
||||
@@ -2094,9 +2028,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Keep your connections" = "Mantieni le tue connessioni";
|
||||
|
||||
@@ -2133,15 +2064,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"LIVE" = "IN DIRETTA";
|
||||
|
||||
@@ -2540,9 +2462,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Paste" = "Incolla";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Paste desktop address" = "Incolla l'indirizzo desktop";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Paste image" = "Incolla immagine";
|
||||
|
||||
@@ -2927,9 +2846,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"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. */
|
||||
"Servers" = "Server";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Session code" = "Codice di sessione";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Set 1 day" = "Imposta 1 giorno";
|
||||
|
||||
@@ -3386,9 +3299,6 @@
|
||||
/* notification title */
|
||||
"this contact" = "questo contatto";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This device name" = "Il nome di questo dispositivo";
|
||||
|
||||
/* 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.";
|
||||
|
||||
@@ -3410,9 +3320,6 @@
|
||||
/* 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.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To hide unwanted messages." = "Per nascondere messaggi indesiderati.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To make a new connection" = "Per creare una nuova connessione";
|
||||
|
||||
@@ -3509,12 +3416,6 @@
|
||||
/* 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.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlink" = "Scollega";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlink desktop?" = "Scollegare il desktop?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlock" = "Sblocca";
|
||||
|
||||
@@ -3569,9 +3470,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Use for new connections" = "Usa per connessioni nuove";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Use from desktop" = "Usa dal desktop";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Use iOS call interface" = "Usa interfaccia di chiamata iOS";
|
||||
|
||||
@@ -3593,24 +3491,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Using SimpleX Chat servers." = "Utilizzo dei server SimpleX Chat.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"v%@" = "v%@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Verify connection security" = "Verifica la sicurezza della connessione";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Verify connections" = "Verifica le connessioni";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Verify security code" = "Verifica codice di sicurezza";
|
||||
|
||||
@@ -3629,9 +3515,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Video call" = "Videochiamata";
|
||||
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
/* Privacy - Face ID Usage Description */
|
||||
"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 */
|
||||
"NSMicrophoneUsageDescription" = "SimpleX ha bisogno dell'accesso al microfono per le chiamate audio e video e per registrare messaggi vocali.";
|
||||
|
||||
|
||||
@@ -25,9 +25,6 @@
|
||||
/* 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!";
|
||||
|
||||
/* 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. */
|
||||
"- 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. */
|
||||
"(new)" = "(nieuw)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"(this device v%@)" = "(dit apparaat v%@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
")" = ")";
|
||||
|
||||
@@ -545,9 +536,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Authentication unavailable" = "Verificatie niet beschikbaar";
|
||||
|
||||
/* member role */
|
||||
"author" = "auteur";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Auto-accept" = "Automatisch accepteren";
|
||||
|
||||
@@ -560,9 +548,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Back" = "Terug";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Bad desktop address" = "Onjuist desktopadres";
|
||||
|
||||
/* integrity error chat item */
|
||||
"bad message hash" = "Onjuiste bericht hash";
|
||||
|
||||
@@ -575,18 +560,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Bad message ID" = "Onjuiste bericht-ID";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Better groups" = "Betere groepen";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Better messages" = "Betere berichten";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block" = "Blokkeren";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block group members" = "Groepsleden blokkeren";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block member" = "Lid blokkeren";
|
||||
|
||||
@@ -789,15 +768,9 @@
|
||||
/* server test step */
|
||||
"Connect" = "Verbind";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect automatically" = "Automatisch verbinden";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect incognito" = "Verbind incognito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect to desktop" = "Verbinden met desktop";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"connect to SimpleX Chat developers." = "maak verbinding met SimpleX Chat-ontwikkelaars.";
|
||||
|
||||
@@ -828,15 +801,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"connected" = "verbonden";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connected desktop" = "Verbonden desktop";
|
||||
|
||||
/* rcv group event chat item */
|
||||
"connected directly" = "direct verbonden";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connected to desktop" = "Verbonden met desktop";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"connecting" = "Verbinden";
|
||||
|
||||
@@ -861,9 +828,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting server… (error: %@)" = "Verbinden met server... (fout: %@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting to desktop" = "Verbinding maken met desktop";
|
||||
|
||||
/* chat list item title */
|
||||
"connecting…" = "Verbinden…";
|
||||
|
||||
@@ -882,9 +846,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Connection request sent!" = "Verbindingsverzoek verzonden!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connection terminated" = "Verbinding beëindigd";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connection timeout" = "Timeout verbinding";
|
||||
|
||||
@@ -939,9 +900,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"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. */
|
||||
"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. */
|
||||
"Develop" = "Ontwikkelen";
|
||||
|
||||
@@ -1287,15 +1236,9 @@
|
||||
/* server test step */
|
||||
"Disconnect" = "verbinding verbreken";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Disconnect desktop?" = "Desktop loskoppelen?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Do it later" = "Doe het later";
|
||||
|
||||
@@ -1431,12 +1374,6 @@
|
||||
/* chat item text */
|
||||
"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 */
|
||||
"encryption re-negotiation required" = "heronderhandeling van versleuteling vereist";
|
||||
|
||||
@@ -1467,9 +1404,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Enter server manually" = "Voer de server handmatig in";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enter this device name…" = "Voer deze apparaatnaam in…";
|
||||
|
||||
/* placeholder */
|
||||
"Enter welcome message…" = "Welkomst bericht invoeren…";
|
||||
|
||||
@@ -1671,9 +1605,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Favorite" = "Favoriet";
|
||||
|
||||
@@ -1731,9 +1662,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"For console" = "Voor console";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Found desktop" = "Desktop gevonden";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"French interface" = "Franse interface";
|
||||
|
||||
@@ -1938,9 +1866,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito" = "Incognito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito groups" = "Incognitogroepen";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito mode" = "Incognito modus";
|
||||
|
||||
@@ -1968,9 +1893,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Incompatible database version" = "Incompatibele database versie";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incompatible version" = "Incompatibele versie";
|
||||
|
||||
/* PIN entry */
|
||||
"Incorrect passcode" = "Onjuiste toegangscode";
|
||||
|
||||
@@ -2106,9 +2028,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Keep your connections" = "Behoud uw verbindingen";
|
||||
|
||||
@@ -2145,15 +2064,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"LIVE" = "LIVE";
|
||||
|
||||
@@ -2409,9 +2319,6 @@
|
||||
/* copied message info in history */
|
||||
"no text" = "geen tekst";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Not compatible!" = "Niet compatibel!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Notifications" = "Meldingen";
|
||||
|
||||
@@ -2555,9 +2462,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Paste" = "Plakken";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Paste desktop address" = "Desktopadres plakken";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Paste image" = "Afbeelding plakken";
|
||||
|
||||
@@ -2942,9 +2846,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"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. */
|
||||
"Servers" = "Servers";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Session code" = "Sessie code";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Set 1 day" = "Stel 1 dag in";
|
||||
|
||||
@@ -3401,9 +3299,6 @@
|
||||
/* notification title */
|
||||
"this contact" = "dit contact";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This device name" = "Deze apparaatnaam";
|
||||
|
||||
/* 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.";
|
||||
|
||||
@@ -3425,9 +3320,6 @@
|
||||
/* 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.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To hide unwanted messages." = "Om ongewenste berichten te verbergen.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To make a new connection" = "Om een nieuwe verbinding te maken";
|
||||
|
||||
@@ -3524,12 +3416,6 @@
|
||||
/* 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.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlink" = "Ontkoppelen";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlink desktop?" = "Desktop ontkoppelen?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlock" = "Ontgrendelen";
|
||||
|
||||
@@ -3584,9 +3470,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Use for new connections" = "Gebruik voor nieuwe verbindingen";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Use from desktop" = "Gebruik vanaf desktop";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Use iOS call interface" = "De iOS-oproepinterface gebruiken";
|
||||
|
||||
@@ -3608,24 +3491,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Using SimpleX Chat servers." = "SimpleX Chat servers gebruiken.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"v%@" = "v%@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Verify connection security" = "Controleer de verbindingsbeveiliging";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Verify connections" = "Controleer verbindingen";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Verify security code" = "Controleer de beveiligingscode";
|
||||
|
||||
@@ -3644,9 +3515,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"via relay" = "via relais";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Via secure quantum resistant protocol." = "Via een beveiligd kwantumbestendig protocol.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Video call" = "video oproep";
|
||||
|
||||
@@ -3686,9 +3554,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"waiting for confirmation…" = "Wachten op bevestiging…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Waiting for desktop..." = "Wachten op desktop...";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Waiting for file" = "Wachten op bestand";
|
||||
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
/* Privacy - Face ID Usage Description */
|
||||
"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 */
|
||||
"NSMicrophoneUsageDescription" = "SimpleX heeft microfoon toegang nodig voor audio en video oproepen en om spraak berichten op te nemen.";
|
||||
|
||||
|
||||
@@ -25,9 +25,6 @@
|
||||
/* 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!";
|
||||
|
||||
/* 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. */
|
||||
"- 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. */
|
||||
"(new)" = "(nowy)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"(this device v%@)" = "(to urządzenie v%@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
")" = ")";
|
||||
|
||||
@@ -545,9 +536,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Authentication unavailable" = "Uwierzytelnianie niedostępne";
|
||||
|
||||
/* member role */
|
||||
"author" = "autor";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Auto-accept" = "Automatycznie akceptuj";
|
||||
|
||||
@@ -560,9 +548,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Back" = "Wstecz";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Bad desktop address" = "Zły adres komputera";
|
||||
|
||||
/* integrity error chat item */
|
||||
"bad message hash" = "zły hash wiadomości";
|
||||
|
||||
@@ -575,18 +560,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Bad message ID" = "Zły identyfikator wiadomości";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Better groups" = "Lepsze grupy";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Better messages" = "Lepsze wiadomości";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block" = "Zablokuj";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block group members" = "Blokuj członków grupy";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block member" = "Zablokuj członka";
|
||||
|
||||
@@ -789,15 +768,9 @@
|
||||
/* server test step */
|
||||
"Connect" = "Połącz";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect automatically" = "Łącz automatycznie";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect incognito" = "Połącz incognito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect to desktop" = "Połącz do komputera";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"connect to SimpleX Chat developers." = "połącz się z deweloperami SimpleX Chat.";
|
||||
|
||||
@@ -828,15 +801,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"connected" = "połączony";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connected desktop" = "Połączony komputer";
|
||||
|
||||
/* rcv group event chat item */
|
||||
"connected directly" = "połącz bezpośrednio";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connected to desktop" = "Połączony do komputera";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"connecting" = "łączenie";
|
||||
|
||||
@@ -861,9 +828,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting server… (error: %@)" = "Łączenie z serwerem... (błąd: %@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting to desktop" = "Łączenie z komputerem";
|
||||
|
||||
/* chat list item title */
|
||||
"connecting…" = "łączenie…";
|
||||
|
||||
@@ -882,9 +846,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Connection timeout" = "Czas połączenia minął";
|
||||
|
||||
@@ -939,9 +900,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"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. */
|
||||
"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. */
|
||||
"Develop" = "Deweloperskie";
|
||||
|
||||
@@ -1287,15 +1236,9 @@
|
||||
/* server test step */
|
||||
"Disconnect" = "Rozłącz";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Disconnect desktop?" = "Rozłączyć komputer?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Do it later" = "Zrób to później";
|
||||
|
||||
@@ -1431,12 +1374,6 @@
|
||||
/* chat item text */
|
||||
"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 */
|
||||
"encryption re-negotiation required" = "renegocjacja szyfrowania wymagana";
|
||||
|
||||
@@ -1467,9 +1404,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Enter server manually" = "Wprowadź serwer ręcznie";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enter this device name…" = "Podaj nazwę urządzenia…";
|
||||
|
||||
/* placeholder */
|
||||
"Enter welcome message…" = "Wpisz wiadomość powitalną…";
|
||||
|
||||
@@ -1671,9 +1605,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Favorite" = "Ulubione";
|
||||
|
||||
@@ -1731,9 +1662,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"For console" = "Dla konsoli";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Found desktop" = "Znaleziono komputer";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"French interface" = "Francuski interfejs";
|
||||
|
||||
@@ -1938,9 +1866,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito" = "Incognito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito groups" = "Grupy incognito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito mode" = "Tryb incognito";
|
||||
|
||||
@@ -1968,9 +1893,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Incompatible database version" = "Niekompatybilna wersja bazy danych";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incompatible version" = "Niekompatybilna wersja";
|
||||
|
||||
/* PIN entry */
|
||||
"Incorrect passcode" = "Nieprawidłowy pin";
|
||||
|
||||
@@ -2106,9 +2028,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Keep your connections" = "Zachowaj swoje połączenia";
|
||||
|
||||
@@ -2145,15 +2064,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"LIVE" = "NA ŻYWO";
|
||||
|
||||
@@ -2409,9 +2319,6 @@
|
||||
/* copied message info in history */
|
||||
"no text" = "brak tekstu";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Not compatible!" = "Nie kompatybilny!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Notifications" = "Powiadomienia";
|
||||
|
||||
@@ -2555,9 +2462,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Paste" = "Wklej";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Paste desktop address" = "Wklej adres komputera";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Paste image" = "Wklej obraz";
|
||||
|
||||
@@ -2942,9 +2846,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"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. */
|
||||
"Servers" = "Serwery";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Session code" = "Kod sesji";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Set 1 day" = "Ustaw 1 dzień";
|
||||
|
||||
@@ -3401,9 +3299,6 @@
|
||||
/* notification title */
|
||||
"this contact" = "ten kontakt";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This device name" = "Nazwa tego urządzenia";
|
||||
|
||||
/* 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.";
|
||||
|
||||
@@ -3425,9 +3320,6 @@
|
||||
/* 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.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To hide unwanted messages." = "Aby ukryć niechciane wiadomości.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To make a new connection" = "Aby nawiązać nowe połączenie";
|
||||
|
||||
@@ -3524,12 +3416,6 @@
|
||||
/* 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ą.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlink" = "Odłącz";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlink desktop?" = "Odłączyć komputer?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlock" = "Odblokuj";
|
||||
|
||||
@@ -3584,9 +3470,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Use iOS call interface" = "Użyj interfejsu połączeń iOS";
|
||||
|
||||
@@ -3608,24 +3491,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Using SimpleX Chat servers." = "Używanie serwerów SimpleX Chat.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"v%@" = "v%@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Verify connection security" = "Weryfikuj bezpieczeństwo połączenia";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Verify connections" = "Zweryfikuj połączenia";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Verify security code" = "Weryfikuj kod bezpieczeństwa";
|
||||
|
||||
@@ -3644,9 +3515,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Video call" = "Połączenie wideo";
|
||||
|
||||
@@ -3686,9 +3554,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"waiting for confirmation…" = "oczekiwanie na potwierdzenie…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Waiting for desktop..." = "Oczekiwanie na komputer...";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Waiting for file" = "Oczekiwanie na plik";
|
||||
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
/* Privacy - Face ID Usage Description */
|
||||
"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 */
|
||||
"NSMicrophoneUsageDescription" = "SimpleX potrzebuje dostępu do mikrofonu, w celu połączeń audio i wideo oraz nagrywania wiadomości głosowych.";
|
||||
|
||||
|
||||
@@ -25,9 +25,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"- 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. */
|
||||
"- 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. */
|
||||
"(new)" = "(новое)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"(this device v%@)" = "(это устройство v%@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
")" = ")";
|
||||
|
||||
@@ -127,18 +118,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"%@ %@" = "%@ %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@ and %@" = "%@ и %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@ and %@ connected" = "%@ и %@ соединены";
|
||||
|
||||
/* copied message info, <sender> at <time> */
|
||||
"%@ at %@:" = "%1$@ в %2$@:";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@ connected" = "%@ соединен(а)";
|
||||
|
||||
/* notification title */
|
||||
"%@ is connected!" = "Установлено соединение с %@!";
|
||||
|
||||
@@ -154,9 +139,6 @@
|
||||
/* notification title */
|
||||
"%@ wants to connect!" = "%@ хочет соединиться!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@, %@ and %lld members" = "%@, %@ и %lld членов группы";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@, %@ and %lld other members connected" = "%@, %@ и %lld других членов соединены";
|
||||
|
||||
@@ -196,21 +178,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"%lld file(s) with total size of %@" = "%lld файл(ов) общим размером %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%lld group events" = "%lld событий";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%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. */
|
||||
"%lld minutes" = "%lld минуты";
|
||||
|
||||
@@ -259,9 +229,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"~strike~" = "\\~зачеркнуть~";
|
||||
|
||||
/* time to disappear */
|
||||
"0 sec" = "0 сек";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"0s" = "0с";
|
||||
|
||||
@@ -404,9 +371,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"All your contacts will remain connected." = "Все контакты, которые соединились через этот адрес, сохранятся.";
|
||||
|
||||
@@ -470,12 +434,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Already connected?" = "Соединение уже установлено?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Already connecting!" = "Уже соединяется!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Already joining the group!" = "Вступление в группу уже начато!";
|
||||
|
||||
/* pref value */
|
||||
"always" = "всегда";
|
||||
|
||||
@@ -485,9 +443,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Answer call" = "Принять звонок";
|
||||
|
||||
@@ -545,9 +500,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Authentication unavailable" = "Аутентификация недоступна";
|
||||
|
||||
/* member role */
|
||||
"author" = "автор";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Auto-accept" = "Автоприем";
|
||||
|
||||
@@ -560,9 +512,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Back" = "Назад";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Bad desktop address" = "Неверный адрес компьютера";
|
||||
|
||||
/* integrity error chat item */
|
||||
"bad message hash" = "ошибка хэш сообщения";
|
||||
|
||||
@@ -575,27 +524,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Bad message ID" = "Ошибка ID сообщения";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Better groups" = "Улучшенные группы";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"bold" = "жирный";
|
||||
|
||||
@@ -789,30 +720,12 @@
|
||||
/* server test step */
|
||||
"Connect" = "Соединиться";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect automatically" = "Соединяться автоматически";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect incognito" = "Соединиться Инкогнито";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect to desktop" = "Подключиться к компьютеру";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Connect via link" = "Соединиться через ссылку";
|
||||
|
||||
@@ -822,21 +735,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Connect via one-time link" = "Соединиться через одноразовую ссылку";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect with %@" = "Соединиться с %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"connecting" = "соединяется";
|
||||
|
||||
@@ -861,9 +762,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting server… (error: %@)" = "Устанавливается соединение с сервером… (ошибка: %@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting to desktop" = "Подключение к компьютеру";
|
||||
|
||||
/* chat list item title */
|
||||
"connecting…" = "соединяется…";
|
||||
|
||||
@@ -882,9 +780,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Connection request sent!" = "Запрос на соединение отправлен!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connection terminated" = "Подключение прервано";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connection timeout" = "Превышено время соединения";
|
||||
|
||||
@@ -933,24 +828,15 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Core version: v%@" = "Версия ядра: v%@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Correct name to %@?" = "Исправить имя на %@?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create" = "Создать";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create a group using a random profile." = "Создайте группу, используя случайный профиль.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create an address to let people connect with you." = "Создайте адрес, чтобы можно было соединиться с вами.";
|
||||
|
||||
/* server test step */
|
||||
"Create file" = "Создание файла";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create group" = "Создать группу";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create group link" = "Создать ссылку группы";
|
||||
|
||||
@@ -963,9 +849,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Create one-time invitation link" = "Создать ссылку-приглашение";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create profile" = "Создать профиль";
|
||||
|
||||
/* server test step */
|
||||
"Create queue" = "Создание очереди";
|
||||
|
||||
@@ -1080,9 +963,6 @@
|
||||
/* chat item action */
|
||||
"Delete" = "Удалить";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete %lld messages?" = "Удалить %lld сообщений?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete address" = "Удалить адрес";
|
||||
|
||||
@@ -1095,9 +975,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Delete all files" = "Удалить все файлы";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete and notify contact" = "Удалить и уведомить контакт";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete archive" = "Удалить архив";
|
||||
|
||||
@@ -1119,9 +996,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Delete Contact" = "Удалить контакт";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete contact?\nThis cannot be undone!" = "Удалить контакт?\nЭто не может быть отменено!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete database" = "Удалить данные чата";
|
||||
|
||||
@@ -1197,9 +1071,6 @@
|
||||
/* copied message info */
|
||||
"Deleted at: %@" = "Удалено: %@";
|
||||
|
||||
/* rcv direct event chat item */
|
||||
"deleted contact" = "удалил(а) контакт";
|
||||
|
||||
/* rcv group event chat item */
|
||||
"deleted group" = "удалил(а) группу";
|
||||
|
||||
@@ -1215,15 +1086,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Develop" = "Для разработчиков";
|
||||
|
||||
@@ -1287,15 +1149,9 @@
|
||||
/* server test step */
|
||||
"Disconnect" = "Разрыв соединения";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Disconnect desktop?" = "Отключить компьютер?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Discover and join groups" = "Найдите и вступите в группы";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Discover via local network" = "Обнаружение по локальной сети";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Do it later" = "Отложить";
|
||||
|
||||
@@ -1431,12 +1287,6 @@
|
||||
/* chat item text */
|
||||
"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 */
|
||||
"encryption re-negotiation required" = "требуется новое соглашение о шифровании";
|
||||
|
||||
@@ -1452,9 +1302,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Enter correct passphrase." = "Введите правильный пароль.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enter group name…" = "Введите имя группы…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enter Passcode" = "Введите Код";
|
||||
|
||||
@@ -1467,18 +1314,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Enter server manually" = "Ввести сервер вручную";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enter this device name…" = "Введите имя этого устройства…";
|
||||
|
||||
/* placeholder */
|
||||
"Enter welcome message…" = "Введите приветственное сообщение…";
|
||||
|
||||
/* placeholder */
|
||||
"Enter welcome message… (optional)" = "Введите приветственное сообщение... (опционально)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enter your name…" = "Введите ваше имя…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"error" = "ошибка";
|
||||
|
||||
@@ -1515,9 +1356,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Error creating group link" = "Ошибка при создании ссылки группы";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Error creating member contact" = "Ошибка создания контакта с членом группы";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Error creating profile!" = "Ошибка создания профиля!";
|
||||
|
||||
@@ -1596,9 +1434,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Error sending email" = "Ошибка отправки email";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Error sending member contact invitation" = "Ошибка отправки приглашения члену группы";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Error sending message" = "Ошибка при отправке сообщения";
|
||||
|
||||
@@ -1650,9 +1485,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Exit without saving" = "Выйти без сохранения";
|
||||
|
||||
/* chat item action */
|
||||
"Expand" = "Раскрыть";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Export database" = "Экспорт архива чата";
|
||||
|
||||
@@ -1671,9 +1503,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Favorite" = "Избранный";
|
||||
|
||||
@@ -1731,9 +1560,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"For console" = "Для консоли";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Found desktop" = "Компьютер найден";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"French interface" = "Французский интерфейс";
|
||||
|
||||
@@ -1746,9 +1572,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Full name:" = "Полное имя:";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Fully decentralized – visible only to members." = "Группа полностью децентрализована – она видна только членам.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Fully re-implemented - work in background!" = "Полностью обновлены - работают в фоне!";
|
||||
|
||||
@@ -1761,12 +1584,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Group" = "Группа";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Group already exists" = "Группа уже существует";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Group already exists!" = "Группа уже существует!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"group deleted" = "группа удалена";
|
||||
|
||||
@@ -1938,9 +1755,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito" = "Инкогнито";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito groups" = "Инкогнито группы";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito mode" = "Режим Инкогнито";
|
||||
|
||||
@@ -1968,9 +1782,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Incompatible database version" = "Несовместимая версия базы данных";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incompatible version" = "Несовместимая версия";
|
||||
|
||||
/* PIN entry */
|
||||
"Incorrect passcode" = "Неправильный код";
|
||||
|
||||
@@ -2010,9 +1821,6 @@
|
||||
/* invalid chat item */
|
||||
"invalid data" = "ошибка данных";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Invalid name!" = "Неверное имя!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Invalid server address!" = "Ошибка в адресе сервера!";
|
||||
|
||||
@@ -2091,24 +1899,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Join group" = "Вступить в группу";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Join group?" = "Вступить в группу?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"Joining group" = "Вступление в группу";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Keep the app open to use it from desktop" = "Оставьте приложение открытым, чтобы использовать его с компьютера";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Keep your connections" = "Сохраните Ваши соединения";
|
||||
|
||||
@@ -2145,15 +1941,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"LIVE" = "LIVE";
|
||||
|
||||
@@ -2259,9 +2046,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Messages & files" = "Сообщения";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Messages from %@ will be shown!" = "Сообщения от %@ будут показаны!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Migrating database archive…" = "Данные чата перемещаются…";
|
||||
|
||||
@@ -2409,9 +2193,6 @@
|
||||
/* copied message info in history */
|
||||
"no text" = "нет текста";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Not compatible!" = "Несовместимая версия!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Notifications" = "Уведомления";
|
||||
|
||||
@@ -2507,18 +2288,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Only your contact can send voice messages." = "Только Ваш контакт может отправлять голосовые сообщения.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Open" = "Открыть";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Open chat" = "Открыть чат";
|
||||
|
||||
/* authentication reason */
|
||||
"Open chat console" = "Открыть консоль";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Open group" = "Открыть группу";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Open Settings" = "Открыть Настройки";
|
||||
|
||||
@@ -2555,9 +2330,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Paste" = "Вставить";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Paste desktop address" = "Вставить адрес компьютера";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Paste image" = "Вставить изображение";
|
||||
|
||||
@@ -2654,12 +2426,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Profile image" = "Аватар";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Profile name" = "Имя профиля";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Profile name:" = "Имя профиля:";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Profile password" = "Пароль профиля";
|
||||
|
||||
@@ -2825,12 +2591,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Renegotiate encryption?" = "Пересогласовать шифрование?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Repeat connection request?" = "Повторить запрос на соединение?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Repeat join request?" = "Повторить запрос на вступление?";
|
||||
|
||||
/* chat item action */
|
||||
"Reply" = "Ответить";
|
||||
|
||||
@@ -2942,9 +2702,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Scan QR code" = "Сканировать QR код";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Scan QR code from desktop" = "Сканировать QR код с компьютера";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Scan security code from your contact's app." = "Сканируйте код безопасности из приложения контакта.";
|
||||
|
||||
@@ -2999,15 +2756,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Send delivery receipts to" = "Отправка отчётов о доставке";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"send direct message" = "отправьте сообщение";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Send direct message" = "Отправить сообщение";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Send direct message to connect" = "Отправьте сообщение чтобы соединиться";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Send disappearing message" = "Отправить исчезающее сообщение";
|
||||
|
||||
@@ -3089,9 +2840,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Servers" = "Серверы";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Session code" = "Код сессии";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Set 1 day" = "Установить 1 день";
|
||||
|
||||
@@ -3278,9 +3026,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Tap to activate profile." = "Нажмите, чтобы сделать профиль активным.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Tap to Connect" = "Нажмите чтобы соединиться";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Tap to join" = "Нажмите, чтобы вступить";
|
||||
|
||||
@@ -3401,21 +3146,12 @@
|
||||
/* notification title */
|
||||
"this contact" = "этот контакт";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This device name" = "Имя этого устройства";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This group has over %lld members, delivery receipts are not sent." = "В группе более %lld членов, отчёты о доставке выключены.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"This setting applies to messages in your current chat profile **%@**." = "Эта настройка применяется к сообщениям в Вашем текущем профиле чата **%@**.";
|
||||
|
||||
@@ -3425,9 +3161,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"To make a new connection" = "Чтобы соединиться";
|
||||
|
||||
@@ -3476,15 +3209,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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 */
|
||||
"Unexpected error: %@" = "Неожиданная ошибка: %@";
|
||||
|
||||
@@ -3524,12 +3248,6 @@
|
||||
/* 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Чтобы установить соединение, попросите Ваш контакт создать еще одну ссылку и проверьте Ваше соединение с сетью.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlink" = "Забыть";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlink desktop?" = "Забыть компьютер?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlock" = "Разблокировать";
|
||||
|
||||
@@ -3584,9 +3302,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Use for new connections" = "Использовать для новых соединений";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Use from desktop" = "Использовать с компьютера";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Use iOS call interface" = "Использовать интерфейс iOS для звонков";
|
||||
|
||||
@@ -3608,24 +3323,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Using SimpleX Chat servers." = "Используются серверы, предоставленные SimpleX Chat.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"v%@" = "v%@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"v%@ (%@)" = "v%@ (%@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Verify code with desktop" = "Сверьте код с компьютером";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Verify connection" = "Проверить соединение";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Verify connection security" = "Проверить безопасность соединения";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Verify connections" = "Проверять соединения";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Verify security code" = "Подтвердить код безопасности";
|
||||
|
||||
@@ -3644,9 +3347,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"via relay" = "через relay сервер";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Via secure quantum resistant protocol." = "Через безопасный квантово-устойчивый протокол.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Video call" = "Видеозвонок";
|
||||
|
||||
@@ -3686,9 +3386,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"waiting for confirmation…" = "ожидается подтверждение…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Waiting for desktop..." = "Ожидается подключение компьютера...";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Waiting for file" = "Ожидается прием файла";
|
||||
|
||||
@@ -3758,27 +3455,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"You are connected to the server used to receive messages from this contact." = "Установлено соединение с сервером, через который Вы получаете сообщения от этого контакта.";
|
||||
|
||||
@@ -3854,12 +3530,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"You have no chats" = "У Вас нет чатов";
|
||||
|
||||
@@ -3902,9 +3572,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"You will be connected when your connection request is accepted, please wait or check later!" = "Соединение будет установлено, когда Ваш запрос будет принят. Пожалуйста, подождите или проверьте позже!";
|
||||
|
||||
@@ -3914,9 +3581,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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. */
|
||||
"You will still receive calls and notifications from muted profiles when they are active." = "Вы все равно получите звонки и уведомления в профилях без звука, когда они активные.";
|
||||
|
||||
@@ -3980,9 +3644,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Your privacy" = "Конфиденциальность";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your profile" = "Ваш профиль";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your profile **%@** will be shared." = "Будет отправлен Ваш профиль **%@**.";
|
||||
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
/* Privacy - Face ID Usage Description */
|
||||
"NSFaceIDUsageDescription" = "SimpleX использует Face ID для аутентификации";
|
||||
|
||||
/* Privacy - Local Network Usage Description */
|
||||
"NSLocalNetworkUsageDescription" = "SimpleX использует доступ к локальной сети, чтобы разрешить использование профиля чата через компьютер в той же сети.";
|
||||
|
||||
/* Privacy - Microphone Usage Description */
|
||||
"NSMicrophoneUsageDescription" = "SimpleX использует микрофон для аудио и видео звонков, и для записи голосовых сообщений.";
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "chat.simplex.app"
|
||||
minSdkVersion(28)
|
||||
minSdkVersion(26)
|
||||
targetSdkVersion(33)
|
||||
// !!!
|
||||
// skip version code after release to F-Droid, as it uses two version codes
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
android:exported="true"
|
||||
android:label="${app_name}"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:configChanges="uiMode"
|
||||
android:theme="@style/Theme.SimpleX">
|
||||
<intent-filter>
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
||||
@@ -5,7 +5,6 @@ import android.net.Uri
|
||||
import android.os.*
|
||||
import android.view.WindowManager
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import chat.simplex.app.model.NtfManager
|
||||
import chat.simplex.app.model.NtfManager.getUserIdFromIntent
|
||||
@@ -23,7 +22,6 @@ import java.lang.ref.WeakReference
|
||||
class MainActivity: FragmentActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
platform.androidSetNightModeIfSupported()
|
||||
applyAppLocale(ChatModel.controller.appPrefs.appLanguage)
|
||||
super.onCreate(savedInstanceState)
|
||||
// testJson()
|
||||
@@ -43,7 +41,9 @@ class MainActivity: FragmentActivity() {
|
||||
)
|
||||
}
|
||||
setContent {
|
||||
AppScreen()
|
||||
SimpleXTheme {
|
||||
AppScreen()
|
||||
}
|
||||
}
|
||||
SimplexApp.context.schedulePeriodicServiceRestartWorker()
|
||||
SimplexApp.context.schedulePeriodicWakeUp()
|
||||
@@ -126,9 +126,7 @@ fun processIntent(intent: Intent?) {
|
||||
when (intent?.action) {
|
||||
"android.intent.action.VIEW" -> {
|
||||
val uri = intent.data
|
||||
if (uri != null) {
|
||||
chatModel.appOpenUrl.value = null to uri.toURI()
|
||||
}
|
||||
if (uri != null) connectIfOpenedViaUri(chatModel.remoteHostId, uri.toURI(), ChatModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package chat.simplex.app
|
||||
|
||||
import android.app.Application
|
||||
import android.app.UiModeManager
|
||||
import android.os.*
|
||||
import chat.simplex.common.platform.Log
|
||||
import androidx.lifecycle.*
|
||||
import androidx.work.*
|
||||
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.ChatController.appPrefs
|
||||
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.onboarding.OnboardingStage
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.call.RcvCallInvitation
|
||||
import com.jakewharton.processphoenix.ProcessPhoenix
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
@@ -35,24 +32,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
if (ProcessPhoenix.isPhoenixProcess(this)) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
context = this
|
||||
initHaskell()
|
||||
@@ -77,7 +57,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
updatingChatsMutex.withLock {
|
||||
kotlin.runCatching {
|
||||
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 */
|
||||
if (chatModel.currentUser.value?.userId == currentUserId) {
|
||||
val currentChatId = chatModel.chatId.value
|
||||
@@ -95,7 +75,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
}
|
||||
Lifecycle.Event.ON_RESUME -> {
|
||||
isAppOnForeground = true
|
||||
if (chatModel.controller.appPrefs.onboardingStage.get() == OnboardingStage.OnboardingComplete && chatModel.currentUser.value != null) {
|
||||
if (chatModel.controller.appPrefs.onboardingStage.get() == OnboardingStage.OnboardingComplete) {
|
||||
SimplexService.showBackgroundServiceNoticeIfNeeded()
|
||||
}
|
||||
/**
|
||||
@@ -164,14 +144,13 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
androidAppContext = this
|
||||
APPLICATION_ID = BuildConfig.APPLICATION_ID
|
||||
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 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 androidCreateNtfChannelsMaybeShowAlert() = NtfManager.createNtfChannelsMaybeShowAlert()
|
||||
override fun cancelCallNotification() = NtfManager.cancelCallNotification()
|
||||
override fun cancelAllNotifications() = NtfManager.cancelAllNotifications()
|
||||
override fun showMessage(title: String, text: String) = NtfManager.showMessage(title, text)
|
||||
}
|
||||
platform = object : PlatformInterface {
|
||||
override suspend fun androidServiceStart() {
|
||||
@@ -227,23 +206,6 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
|
||||
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 {
|
||||
if (SimplexService.isBackgroundRestricted()) {
|
||||
val userChoice: CompletableDeferred<Boolean> = CompletableDeferred()
|
||||
|
||||
@@ -30,7 +30,7 @@ object NtfManager {
|
||||
const val ShowChatsAction: String = "chat.simplex.app.SHOW_CHATS"
|
||||
|
||||
// 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 RejectCallAction: String = "chat.simplex.app.REJECT_CALL"
|
||||
const val CallNotificationId: Int = -1
|
||||
@@ -59,7 +59,7 @@ object NtfManager {
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
|
||||
.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")
|
||||
callChannel.setSound(soundUri, attrs)
|
||||
callChannel.enableVibration(true)
|
||||
@@ -140,7 +140,7 @@ object NtfManager {
|
||||
}
|
||||
}
|
||||
|
||||
fun notifyCallInvitation(invitation: RcvCallInvitation): Boolean {
|
||||
fun notifyCallInvitation(invitation: RcvCallInvitation) {
|
||||
val keyguardManager = getKeyguardManager(context)
|
||||
Log.d(
|
||||
TAG,
|
||||
@@ -149,7 +149,7 @@ object NtfManager {
|
||||
"callOnLockScreen ${appPreferences.callOnLockScreen.get()}, " +
|
||||
"onForeground ${isAppOnForeground}"
|
||||
)
|
||||
if (isAppOnForeground) return false
|
||||
if (isAppOnForeground) return
|
||||
val contactId = invitation.contact.id
|
||||
Log.d(TAG, "notifyCallInvitation $contactId")
|
||||
val image = invitation.contact.image
|
||||
@@ -163,7 +163,7 @@ object NtfManager {
|
||||
.setFullScreenIntent(fullScreenPendingIntent, true)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
} 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)
|
||||
NotificationCompat.Builder(context, CallChannel)
|
||||
.setContentIntent(chatPendingIntent(OpenChatAction, invitation.user.userId, invitation.contact.id))
|
||||
@@ -206,39 +206,6 @@ object NtfManager {
|
||||
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() {
|
||||
@@ -281,7 +248,6 @@ object NtfManager {
|
||||
manager.createNotificationChannel(callNotificationChannel(CallChannel, generalGetString(MR.strings.ntf_channel_calls)))
|
||||
// Remove old channels since they can't be edited
|
||||
manager.deleteNotificationChannel("chat.simplex.app.CALL_NOTIFICATION")
|
||||
manager.deleteNotificationChannel("chat.simplex.app.CALL_NOTIFICATION_1")
|
||||
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"?>
|
||||
<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>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
@@ -110,7 +110,7 @@ android {
|
||||
compileSdkVersion(34)
|
||||
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
|
||||
defaultConfig {
|
||||
minSdkVersion(28)
|
||||
minSdkVersion(26)
|
||||
targetSdkVersion(33)
|
||||
}
|
||||
compileOptions {
|
||||
@@ -155,34 +155,6 @@ afterEvaluate {
|
||||
val endTagRegex = Regex("</")
|
||||
val anyHtmlRegex = Regex("[^>]*>.*(<|>).*</string>|[^>]*>.*(<|>).*</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 =
|
||||
if (contains("<![CDATA")) {
|
||||
@@ -223,44 +195,20 @@ afterEvaluate {
|
||||
return this
|
||||
}
|
||||
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
|
||||
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
|
||||
kotlin.sourceSets["commonMain"].resources.filter { fileRegex.containsMatchIn(it.absolutePath) }.asFileTree.forEach { file ->
|
||||
val initialLines = ArrayList<String>()
|
||||
val finalLines = ArrayList<String>()
|
||||
val errors = ArrayList<String>()
|
||||
|
||||
file.useLines { lines ->
|
||||
val multiline = ArrayList<String>()
|
||||
lines.forEach { line ->
|
||||
initialLines.add(line)
|
||||
if (stringRegex.matches(line)) {
|
||||
val fixedLine = 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)
|
||||
finalLines.add(line.removeCDATA().addCDATA(file.absolutePath))
|
||||
} else if (multiline.isEmpty() && startStringRegex.containsMatchIn(line)) {
|
||||
multiline.add(line)
|
||||
} else if (multiline.isNotEmpty() && endStringRegex.containsMatchIn(line)) {
|
||||
multiline.add(line)
|
||||
val fixedLines = 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)
|
||||
finalLines.addAll(multiline.joinToString("\n").removeCDATA().addCDATA(file.absolutePath).split("\n"))
|
||||
multiline.clear()
|
||||
} else if (multiline.isNotEmpty()) {
|
||||
multiline.add(line)
|
||||
@@ -269,14 +217,10 @@ afterEvaluate {
|
||||
}
|
||||
}
|
||||
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) {
|
||||
file.writer().use {
|
||||
finalLines.forEachIndexed { index, line ->
|
||||
|
||||
@@ -68,5 +68,12 @@ fun initHaskell() {
|
||||
s.acquire()
|
||||
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.graphics.painter.Painter
|
||||
import com.google.accompanist.insets.navigationBarsWithImePadding
|
||||
import java.io.File
|
||||
|
||||
actual fun Modifier.navigationBarsWithImePadding(): Modifier = navigationBarsWithImePadding()
|
||||
|
||||
@@ -20,7 +19,7 @@ actual fun ProvideWindowInsets(
|
||||
@Composable
|
||||
actual fun Modifier.desktopOnExternalDrag(
|
||||
enabled: Boolean,
|
||||
onFiles: (List<File>) -> Unit,
|
||||
onFiles: (List<String>) -> Unit,
|
||||
onImage: (Painter) -> Unit,
|
||||
onText: (String) -> Unit
|
||||
): Modifier = this
|
||||
|
||||
@@ -27,6 +27,7 @@ import androidx.core.view.inputmethod.EditorInfoCompat
|
||||
import androidx.core.view.inputmethod.InputConnectionCompat
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import chat.simplex.common.*
|
||||
import chat.simplex.common.R
|
||||
import chat.simplex.common.helpers.toURI
|
||||
import chat.simplex.common.model.ChatModel
|
||||
@@ -44,7 +45,6 @@ import java.net.URI
|
||||
actual fun PlatformTextField(
|
||||
composeState: MutableState<ComposeState>,
|
||||
sendMsgEnabled: Boolean,
|
||||
sendMsgButtonDisabled: Boolean,
|
||||
textStyle: MutableState<TextStyle>,
|
||||
showDeleteTextButton: MutableState<Boolean>,
|
||||
userIsObserver: Boolean,
|
||||
|
||||
@@ -8,12 +8,15 @@ import android.provider.MediaStore
|
||||
import android.webkit.MimeTypeMap
|
||||
import androidx.compose.ui.platform.ClipboardManager
|
||||
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.model.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import chat.simplex.res.MR
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
actual fun ClipboardManager.shareText(text: String) {
|
||||
val sendIntent: Intent = Intent().apply {
|
||||
|
||||
@@ -4,17 +4,14 @@ import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.graphics.Rect
|
||||
import android.os.*
|
||||
import android.os.Build
|
||||
import android.view.*
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import chat.simplex.common.AppScreen
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.helpers.KeyboardState
|
||||
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()
|
||||
|
||||
@@ -74,44 +71,3 @@ actual fun hideKeyboard(view: Any?) {
|
||||
}
|
||||
|
||||
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 {
|
||||
val filepath = getAppFilePath(uri)
|
||||
if (filepath == null || !File(filepath).exists()) {
|
||||
Log.e(TAG, "No such file: $filepath")
|
||||
Log.e(TAG, "No such file: $uri")
|
||||
brokenVideo.value = true
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -370,6 +370,7 @@ fun CallInfoView(call: Call, alignment: Alignment.Horizontal) {
|
||||
InfoText(call.callState.text)
|
||||
|
||||
val connInfo = call.connectionInfo
|
||||
// val connInfoText = if (connInfo == null) "" else " (${connInfo.text}, ${connInfo.protocolText})"
|
||||
val connInfoText = if (connInfo == null) "" else " (${connInfo.text})"
|
||||
InfoText(call.encryptionStatus + connInfoText)
|
||||
}
|
||||
@@ -584,8 +585,8 @@ fun PreviewActiveCallOverlayVideo() {
|
||||
localMedia = CallMediaType.Video,
|
||||
peerMedia = CallMediaType.Video,
|
||||
connectionInfo = ConnectionInfo(
|
||||
RTCIceCandidate(RTCIceCandidateType.Host, "tcp"),
|
||||
RTCIceCandidate(RTCIceCandidateType.Host, "tcp")
|
||||
RTCIceCandidate(RTCIceCandidateType.Host, "tcp", null),
|
||||
RTCIceCandidate(RTCIceCandidateType.Host, "tcp", null)
|
||||
)
|
||||
),
|
||||
speakerCanBeEnabled = true,
|
||||
@@ -610,8 +611,8 @@ fun PreviewActiveCallOverlayAudio() {
|
||||
localMedia = CallMediaType.Audio,
|
||||
peerMedia = CallMediaType.Audio,
|
||||
connectionInfo = ConnectionInfo(
|
||||
RTCIceCandidate(RTCIceCandidateType.Host, "udp"),
|
||||
RTCIceCandidate(RTCIceCandidateType.Host, "udp")
|
||||
RTCIceCandidate(RTCIceCandidateType.Host, "udp", null),
|
||||
RTCIceCandidate(RTCIceCandidateType.Host, "udp", null)
|
||||
)
|
||||
),
|
||||
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
Reference in New Issue
Block a user