Compare commits

..

2 Commits

Author SHA1 Message Date
Evgeny Poberezkin 4947cf2c0c Merge branch 'master' into av/android-lint 2023-08-10 19:15:56 +01:00
Avently 2e205b055d android: lint & gradle issues 2023-08-08 23:33:51 +07:00
53 changed files with 348 additions and 701 deletions
@@ -44,6 +44,7 @@ struct ComposeState {
var contextItem: ComposeContextItem
var voiceMessageRecordingState: VoiceMessageRecordingState
var inProgress = false
var disabled = false
var useLinkPreviews: Bool = UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_LINK_PREVIEWS)
init(
@@ -654,7 +655,10 @@ struct ComposeView: View {
return sent
func sending() async {
await MainActor.run { composeState.inProgress = true }
await MainActor.run { composeState.disabled = true }
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
if composeState.disabled { composeState.inProgress = true }
}
}
func updateMessage(_ ei: ChatItem, live: Bool) async -> ChatItem? {
@@ -848,6 +852,7 @@ struct ComposeView: View {
private func clearState(live: Bool = false) {
if live {
composeState.disabled = false
composeState.inProgress = false
} else {
composeState = ComposeState()
@@ -36,7 +36,6 @@ struct SendMessageView: View {
@State private var showCustomDisappearingMessageDialogue = false
@State private var showCustomTimePicker = false
@State private var selectedDisappearingMessageTime: Int? = customDisappearingMessageTimeDefault.get()
@State private var progressByTimeout = false
var maxHeight: CGFloat = 360
var minHeight: CGFloat = 37
@AppStorage(DEFAULT_LIVE_MESSAGE_ALERT_SHOWN) private var liveMessageAlertShown = false
@@ -82,7 +81,7 @@ struct SendMessageView: View {
}
}
if progressByTimeout {
if composeState.inProgress {
ProgressView()
.scaleEffect(1.4)
.frame(width: 31, height: 31, alignment: .center)
@@ -103,15 +102,6 @@ struct SendMessageView: View {
.strokeBorder(.secondary, lineWidth: 0.3, antialiased: true)
.frame(height: teHeight)
}
.onChange(of: composeState.inProgress) { inProgress in
if inProgress {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
progressByTimeout = composeState.inProgress
}
} else {
progressByTimeout = false
}
}
.padding(.vertical, 8)
}
@@ -129,7 +119,7 @@ struct SendMessageView: View {
startVoiceMessageRecording: startVoiceMessageRecording,
finishVoiceMessageRecording: finishVoiceMessageRecording,
holdingVMR: $holdingVMR,
disabled: composeState.inProgress
disabled: composeState.disabled
)
} else {
voiceMessageNotAllowedButton()
@@ -175,7 +165,7 @@ struct SendMessageView: View {
}
.disabled(
!composeState.sendEnabled ||
composeState.inProgress ||
composeState.disabled ||
(!voiceMessageAllowed && composeState.voicePreview) ||
composeState.endLiveDisabled
)
@@ -303,7 +293,7 @@ struct SendMessageView: View {
Image(systemName: "mic")
.foregroundColor(.secondary)
}
.disabled(composeState.inProgress)
.disabled(composeState.disabled)
.frame(width: 29, height: 29)
.padding([.bottom, .trailing], 4)
}
@@ -388,7 +378,7 @@ struct SendMessageView: View {
Image(systemName: "stop.fill")
.foregroundColor(.accentColor)
}
.disabled(composeState.inProgress)
.disabled(composeState.disabled)
.frame(width: 29, height: 29)
.padding([.bottom, .trailing], 4)
}
@@ -127,7 +127,7 @@ class NotificationService: UNNotificationServiceExtension {
logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfMsgInfo), privacy: .public)")
if let connEntity = ntfMsgInfo.connEntity {
setBestAttemptNtf(
ntfMsgInfo.ntfsEnabled
ntfMsgInfo.user.showNotifications
? .nse(notification: createConnectionEventNtf(ntfMsgInfo.user, connEntity))
: .empty
)
@@ -401,8 +401,4 @@ struct NtfMessages {
var connEntity: ConnectionEntity?
var msgTs: Date?
var ntfMessages: [NtfMsgInfo]
var ntfsEnabled: Bool {
user.showNotifications && (connEntity?.ntfsEnabled ?? false)
}
}
+28 -28
View File
@@ -24,6 +24,11 @@
5C00168128C4FE760094D739 /* KeyChain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C00168028C4FE760094D739 /* KeyChain.swift */; };
5C029EA82837DBB3004A9677 /* CICallItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C029EA72837DBB3004A9677 /* CICallItemView.swift */; };
5C029EAA283942EA004A9677 /* CallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C029EA9283942EA004A9677 /* CallController.swift */; };
5C0403922A7EAA41006ACFE8 /* libHSsimplex-chat-5.3.0.2-57EsBXX08D1H5qwhz1zMA5-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C04038D2A7EAA41006ACFE8 /* libHSsimplex-chat-5.3.0.2-57EsBXX08D1H5qwhz1zMA5-ghc8.10.7.a */; };
5C0403932A7EAA41006ACFE8 /* libHSsimplex-chat-5.3.0.2-57EsBXX08D1H5qwhz1zMA5.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C04038E2A7EAA41006ACFE8 /* libHSsimplex-chat-5.3.0.2-57EsBXX08D1H5qwhz1zMA5.a */; };
5C0403942A7EAA41006ACFE8 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C04038F2A7EAA41006ACFE8 /* libffi.a */; };
5C0403952A7EAA41006ACFE8 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C0403902A7EAA41006ACFE8 /* libgmp.a */; };
5C0403962A7EAA41006ACFE8 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C0403912A7EAA41006ACFE8 /* libgmpxx.a */; };
5C05DF532840AA1D00C683F9 /* CallSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C05DF522840AA1D00C683F9 /* CallSettings.swift */; };
5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C063D2627A4564100AEC577 /* ChatPreviewView.swift */; };
5C10D88828EED12E00E58BF0 /* ContactConnectionInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C10D88728EED12E00E58BF0 /* ContactConnectionInfo.swift */; };
@@ -43,11 +48,6 @@
5C3F1D562842B68D00EC8A82 /* IntegrityErrorItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3F1D552842B68D00EC8A82 /* IntegrityErrorItemView.swift */; };
5C3F1D58284363C400EC8A82 /* PrivacySettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3F1D57284363C400EC8A82 /* PrivacySettings.swift */; };
5C4B3B0A285FB130003915F2 /* DatabaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C4B3B09285FB130003915F2 /* DatabaseView.swift */; };
5C4CC1B02A88383C006BF552 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C4CC1AB2A88383C006BF552 /* libffi.a */; };
5C4CC1B12A88383C006BF552 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C4CC1AC2A88383C006BF552 /* libgmpxx.a */; };
5C4CC1B22A88383C006BF552 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C4CC1AD2A88383C006BF552 /* libgmp.a */; };
5C4CC1B32A88383C006BF552 /* libHSsimplex-chat-5.3.0.4-FE3hjvmcZIPFfEovdlxWAx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C4CC1AE2A88383C006BF552 /* libHSsimplex-chat-5.3.0.4-FE3hjvmcZIPFfEovdlxWAx.a */; };
5C4CC1B42A88383C006BF552 /* libHSsimplex-chat-5.3.0.4-FE3hjvmcZIPFfEovdlxWAx-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C4CC1AF2A88383C006BF552 /* libHSsimplex-chat-5.3.0.4-FE3hjvmcZIPFfEovdlxWAx-ghc8.10.7.a */; };
5C5346A827B59A6A004DF848 /* ChatHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5346A727B59A6A004DF848 /* ChatHelp.swift */; };
5C55A91F283AD0E400C4E99E /* CallManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C55A91E283AD0E400C4E99E /* CallManager.swift */; };
5C55A921283CCCB700C4E99E /* IncomingCallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C55A920283CCCB700C4E99E /* IncomingCallView.swift */; };
@@ -263,6 +263,11 @@
5C00168028C4FE760094D739 /* KeyChain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyChain.swift; sourceTree = "<group>"; };
5C029EA72837DBB3004A9677 /* CICallItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CICallItemView.swift; sourceTree = "<group>"; };
5C029EA9283942EA004A9677 /* CallController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallController.swift; sourceTree = "<group>"; };
5C04038D2A7EAA41006ACFE8 /* libHSsimplex-chat-5.3.0.2-57EsBXX08D1H5qwhz1zMA5-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.3.0.2-57EsBXX08D1H5qwhz1zMA5-ghc8.10.7.a"; sourceTree = "<group>"; };
5C04038E2A7EAA41006ACFE8 /* libHSsimplex-chat-5.3.0.2-57EsBXX08D1H5qwhz1zMA5.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.3.0.2-57EsBXX08D1H5qwhz1zMA5.a"; sourceTree = "<group>"; };
5C04038F2A7EAA41006ACFE8 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5C0403902A7EAA41006ACFE8 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5C0403912A7EAA41006ACFE8 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5C05DF522840AA1D00C683F9 /* CallSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallSettings.swift; sourceTree = "<group>"; };
5C063D2627A4564100AEC577 /* ChatPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatPreviewView.swift; sourceTree = "<group>"; };
5C10D88728EED12E00E58BF0 /* ContactConnectionInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactConnectionInfo.swift; sourceTree = "<group>"; };
@@ -284,11 +289,6 @@
5C3F1D57284363C400EC8A82 /* PrivacySettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacySettings.swift; sourceTree = "<group>"; };
5C422A7C27A9A6FA0097A1E1 /* SimpleX (iOS).entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "SimpleX (iOS).entitlements"; sourceTree = "<group>"; };
5C4B3B09285FB130003915F2 /* DatabaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseView.swift; sourceTree = "<group>"; };
5C4CC1AB2A88383C006BF552 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5C4CC1AC2A88383C006BF552 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5C4CC1AD2A88383C006BF552 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5C4CC1AE2A88383C006BF552 /* libHSsimplex-chat-5.3.0.4-FE3hjvmcZIPFfEovdlxWAx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.3.0.4-FE3hjvmcZIPFfEovdlxWAx.a"; sourceTree = "<group>"; };
5C4CC1AF2A88383C006BF552 /* libHSsimplex-chat-5.3.0.4-FE3hjvmcZIPFfEovdlxWAx-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.3.0.4-FE3hjvmcZIPFfEovdlxWAx-ghc8.10.7.a"; sourceTree = "<group>"; };
5C5346A727B59A6A004DF848 /* ChatHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatHelp.swift; sourceTree = "<group>"; };
5C55A91E283AD0E400C4E99E /* CallManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallManager.swift; sourceTree = "<group>"; };
5C55A920283CCCB700C4E99E /* IncomingCallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncomingCallView.swift; sourceTree = "<group>"; };
@@ -501,13 +501,13 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5C4CC1B12A88383C006BF552 /* libgmpxx.a in Frameworks */,
5C4CC1B32A88383C006BF552 /* libHSsimplex-chat-5.3.0.4-FE3hjvmcZIPFfEovdlxWAx.a in Frameworks */,
5C4CC1B22A88383C006BF552 /* libgmp.a in Frameworks */,
5C0403932A7EAA41006ACFE8 /* libHSsimplex-chat-5.3.0.2-57EsBXX08D1H5qwhz1zMA5.a in Frameworks */,
5C0403922A7EAA41006ACFE8 /* libHSsimplex-chat-5.3.0.2-57EsBXX08D1H5qwhz1zMA5-ghc8.10.7.a in Frameworks */,
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
5C4CC1B02A88383C006BF552 /* libffi.a in Frameworks */,
5C0403942A7EAA41006ACFE8 /* libffi.a in Frameworks */,
5C0403952A7EAA41006ACFE8 /* libgmp.a in Frameworks */,
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
5C4CC1B42A88383C006BF552 /* libHSsimplex-chat-5.3.0.4-FE3hjvmcZIPFfEovdlxWAx-ghc8.10.7.a in Frameworks */,
5C0403962A7EAA41006ACFE8 /* libgmpxx.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -568,11 +568,11 @@
5C764E5C279C70B7000C6508 /* Libraries */ = {
isa = PBXGroup;
children = (
5C4CC1AB2A88383C006BF552 /* libffi.a */,
5C4CC1AD2A88383C006BF552 /* libgmp.a */,
5C4CC1AC2A88383C006BF552 /* libgmpxx.a */,
5C4CC1AF2A88383C006BF552 /* libHSsimplex-chat-5.3.0.4-FE3hjvmcZIPFfEovdlxWAx-ghc8.10.7.a */,
5C4CC1AE2A88383C006BF552 /* libHSsimplex-chat-5.3.0.4-FE3hjvmcZIPFfEovdlxWAx.a */,
5C04038F2A7EAA41006ACFE8 /* libffi.a */,
5C0403902A7EAA41006ACFE8 /* libgmp.a */,
5C0403912A7EAA41006ACFE8 /* libgmpxx.a */,
5C04038D2A7EAA41006ACFE8 /* libHSsimplex-chat-5.3.0.2-57EsBXX08D1H5qwhz1zMA5-ghc8.10.7.a */,
5C04038E2A7EAA41006ACFE8 /* libHSsimplex-chat-5.3.0.2-57EsBXX08D1H5qwhz1zMA5.a */,
);
path = Libraries;
sourceTree = "<group>";
@@ -1478,7 +1478,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 167;
CURRENT_PROJECT_VERSION = 164;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES;
@@ -1520,7 +1520,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 167;
CURRENT_PROJECT_VERSION = 164;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES;
@@ -1600,7 +1600,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 167;
CURRENT_PROJECT_VERSION = 164;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GENERATE_INFOPLIST_FILE = YES;
@@ -1632,7 +1632,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 167;
CURRENT_PROJECT_VERSION = 164;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GENERATE_INFOPLIST_FILE = YES;
@@ -1664,7 +1664,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 167;
CURRENT_PROJECT_VERSION = 164;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1688,7 +1688,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Libraries/sim",
);
MARKETING_VERSION = 5.3;
MARKETING_VERSION = 5.2.2;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos;
@@ -1710,7 +1710,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 167;
CURRENT_PROJECT_VERSION = 164;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1734,7 +1734,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Libraries/sim",
);
MARKETING_VERSION = 5.3;
MARKETING_VERSION = 5.2.2;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos;
-12
View File
@@ -1466,8 +1466,6 @@ public struct SecurityCode: Decodable, Equatable {
public struct UserContact: Decodable {
public var userContactLinkId: Int64
// public var connReqContact: String
public var groupId: Int64?
public init(userContactLinkId: Int64) {
self.userContactLinkId = userContactLinkId
@@ -1929,16 +1927,6 @@ public enum ConnectionEntity: Decodable {
return nil
}
}
public var ntfsEnabled: Bool {
switch self {
case let .rcvDirectMsgConnection(contact): return contact?.chatSettings.enableNtfs ?? false
case let .rcvGroupMsgConnection(groupInfo, _): return groupInfo.chatSettings.enableNtfs
case .sndFileConnection: return false
case .rcvFileConnection: return false
case let .userContactConnection(userContact): return userContact.groupId == nil
}
}
}
public struct NtfMsgInfo: Decodable {
+4 -6
View File
@@ -8,12 +8,13 @@ plugins {
}
android {
compileSdkVersion(33)
namespace = "chat.simplex.app"
compileSdk = 33
defaultConfig {
applicationId = "chat.simplex.app"
minSdkVersion(26)
targetSdkVersion(33)
minSdk = 26
targetSdk = 33
// !!!
// skip version code after release to F-Droid, as it uses two version codes
versionCode = (extra["android.version_code"] as String).toInt()
@@ -56,8 +57,6 @@ android {
freeCompilerArgs += "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi"
freeCompilerArgs += "-opt-in=androidx.compose.ui.text.ExperimentalTextApi"
freeCompilerArgs += "-opt-in=androidx.compose.material.ExperimentalMaterialApi"
freeCompilerArgs += "-opt-in=com.google.accompanist.insets.ExperimentalAnimatedInsets"
freeCompilerArgs += "-opt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi"
freeCompilerArgs += "-opt-in=kotlinx.serialization.InternalSerializationApi"
freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi"
}
@@ -83,7 +82,6 @@ android {
// Comma separated list of languages that will be included in the apk
android.defaultConfig.resConfigs(
"en",
"bg",
"cs",
"de",
"es",
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="chat.simplex.app">
xmlns:tools="http://schemas.android.com/tools">
<uses-feature android:name="android.hardware.camera" />
@@ -24,8 +23,9 @@
<application
android:name="SimplexApp"
android:allowBackup="false"
android:fullBackupOnly="false"
android:allowBackup="true"
android:fullBackupOnly="true"
android:backupAgent="BackupAgent"
android:icon="@mipmap/icon"
android:label="${app_name}"
android:extractNativeLibs="${extract_native_libs}"
@@ -0,0 +1,18 @@
package chat.simplex.app
import android.app.backup.BackupAgentHelper
import android.app.backup.FullBackupDataOutput
import android.content.Context
import chat.simplex.common.model.AppPreferences
import chat.simplex.common.model.AppPreferences.Companion.SHARED_PREFS_PRIVACY_FULL_BACKUP
class BackupAgent: BackupAgentHelper() {
override fun onFullBackup(data: FullBackupDataOutput?) {
if (applicationContext
.getSharedPreferences(AppPreferences.SHARED_PREFS_ID, Context.MODE_PRIVATE)
.getBoolean(SHARED_PREFS_PRIVACY_FULL_BACKUP, true)
) {
super.onFullBackup(data)
}
}
}
-3
View File
@@ -1,6 +1,4 @@
import org.gradle.initialization.Environment.Properties
import java.io.File
import java.io.FileInputStream
buildscript {
val prop = java.util.Properties().apply {
@@ -33,7 +31,6 @@ buildscript {
dependencies {
classpath("com.android.tools.build:gradle:${rootProject.extra["gradle.plugin.version"]}")
classpath(kotlin("gradle-plugin", version = rootProject.extra["kotlin.version"] as String))
classpath("org.jetbrains.kotlin:kotlin-serialization:1.3.2")
classpath("dev.icerock.moko:resources-generator:0.22.3")
// NOTE: Do not place your application dependencies here; they belong
+10 -99
View File
@@ -22,7 +22,6 @@ kotlin {
optIn("androidx.compose.foundation.ExperimentalFoundationApi")
optIn("androidx.compose.ui.text.ExperimentalTextApi")
optIn("androidx.compose.material.ExperimentalMaterialApi")
optIn("com.arkivanov.decompose.ExperimentalDecomposeApi")
optIn("kotlinx.serialization.InternalSerializationApi")
optIn("kotlinx.serialization.ExperimentalSerializationApi")
optIn("androidx.compose.ui.ExperimentalComposeUiApi")
@@ -104,11 +103,11 @@ kotlin {
}
android {
compileSdkVersion(33)
namespace = "chat.simplex.common"
compileSdk = 33
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdkVersion(26)
targetSdkVersion(33)
minSdk = 26
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
@@ -123,11 +122,17 @@ android {
// No other ways to exclude a file work but it's large and should be excluded
kotlin.sourceSets["commonMain"].resources.exclude("/MR/fonts/NotoColorEmoji-Regular.ttf")
}
kotlin {
jvmToolchain(8)
}
lint {
disable += "MissingTranslation"
disable += "ExtraTranslation"
}
}
multiplatformResources {
multiplatformResourcesPackage = "chat.simplex.res"
// multiplatformResourcesClassName = "MR"
}
buildConfig {
@@ -137,97 +142,3 @@ buildConfig {
buildConfigField("String", "DESKTOP_VERSION_NAME", "\"${extra["desktop.version_name"]}\"")
}
}
afterEvaluate {
tasks.named("generateMRcommonMain") {
dependsOn("adjustFormatting")
}
tasks.create("adjustFormatting") {
doLast {
val debug = false
val stringRegex = Regex(".*<string .*</string>.*")
val startStringRegex = Regex("<string [^>]*>")
val endStringRegex = Regex("</string>[ ]*")
val endTagRegex = Regex("</")
val anyHtmlRegex = Regex("[^>]*>.*(<|>).*</string>|[^>]*>.*(&lt;|&gt;).*</string>")
val correctHtmlRegex = Regex("[^>]*>.*<b>.*</b>.*</string>|[^>]*>.*<i>.*</i>.*</string>|[^>]*>.*<u>.*</u>.*</string>|[^>]*>.*<font[^>]*>.*</font>.*</string>")
fun String.removeCDATA(): String =
if (contains("<![CDATA")) {
replace("<![CDATA[", "").replace("]]></string>", "</string>")
} else {
this
}
fun String.addCDATA(filepath: String): String {
//return this
if (anyHtmlRegex.matches(this)) {
val countOfStartTag = count { it == '<' }
val countOfEndTag = count { it == '>' }
if (countOfStartTag != countOfEndTag || countOfStartTag != endTagRegex.findAll(this).count() * 2 || !correctHtmlRegex.matches(this)) {
if (debug) {
println("Wrong string:")
println(this)
println("in $filepath")
println(" ")
} else {
throw Exception("Wrong string: $this \nin $filepath")
}
}
val res = replace(startStringRegex) { it.value + "<![CDATA[" }.replace(endStringRegex) { "]]>" + it.value }
if (debug) {
println("Changed string:")
println(this)
println(res)
println(" ")
}
return res
}
if (debug) {
println("Correct string:")
println(this)
println(" ")
}
return this
}
val fileRegex = Regex("MR/../strings.xml$|MR/..-.../strings.xml$|MR/..-../strings.xml$|MR/base/strings.xml$")
kotlin.sourceSets["commonMain"].resources.filter { fileRegex.containsMatchIn(it.absolutePath) }.asFileTree.forEach { file ->
val initialLines = ArrayList<String>()
val finalLines = ArrayList<String>()
file.useLines { lines ->
val multiline = ArrayList<String>()
lines.forEach { line ->
initialLines.add(line)
if (stringRegex.matches(line)) {
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)
finalLines.addAll(multiline.joinToString("\n").removeCDATA().addCDATA(file.absolutePath).split("\n"))
multiline.clear()
} else if (multiline.isNotEmpty()) {
multiline.add(line)
} else {
finalLines.add(line)
}
}
if (multiline.isNotEmpty()) {
throw Exception("Unclosed string tag: ${multiline.joinToString("\n")} \nin ${file.absolutePath}")
}
}
if (!debug && finalLines != initialLines) {
file.writer().use {
finalLines.forEachIndexed { index, line ->
it.write(line)
if (index != finalLines.lastIndex) {
it.write("\n")
}
}
}
}
}
}
}
}
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="chat.simplex.common">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.VIBRATE" />
</manifest>
@@ -92,6 +92,7 @@ class AppPreferences {
set = fun(mode: SimplexLinkMode) { _simplexLinkMode.set(mode.name) }
)
val privacyDeliveryReceiptsSet = mkBoolPreference(SHARED_PREFS_PRIVACY_DELIVERY_RECEIPTS_SET, false)
val privacyFullBackup = mkBoolPreference(SHARED_PREFS_PRIVACY_FULL_BACKUP, false)
val experimentalCalls = mkBoolPreference(SHARED_PREFS_EXPERIMENTAL_CALLS, false)
val showUnreadAndFavorites = mkBoolPreference(SHARED_PREFS_SHOW_UNREAD_AND_FAVORITES, false)
val chatArchiveName = mkStrPreference(SHARED_PREFS_CHAT_ARCHIVE_NAME, null)
@@ -400,15 +401,19 @@ object ChatController {
return withContext(Dispatchers.IO) {
val c = cmd.cmdString
chatModel.addTerminalItem(TerminalItem.cmd(cmd.obfuscated))
Log.d(TAG, "sendCmd: ${cmd.cmdType}")
if (cmd !is CC.ApiParseMarkdown) {
chatModel.addTerminalItem(TerminalItem.cmd(cmd.obfuscated))
Log.d(TAG, "sendCmd: ${cmd.cmdType}")
}
val json = chatSendCmd(ctrl, c)
val r = APIResponse.decodeStr(json)
Log.d(TAG, "sendCmd response type ${r.resp.responseType}")
if (r.resp is CR.Response || r.resp is CR.Invalid) {
Log.d(TAG, "sendCmd response json $json")
}
chatModel.addTerminalItem(TerminalItem.resp(r.resp))
if (r.resp !is CR.ParsedMarkdown) {
chatModel.addTerminalItem(TerminalItem.resp(r.resp))
}
r.resp
}
}
@@ -1285,6 +1290,13 @@ object ChatController {
}
}
suspend fun apiParseMarkdown(text: String): List<FormattedText>? {
val r = sendCmd(CC.ApiParseMarkdown(text))
if (r is CR.ParsedMarkdown) return r.formattedText
Log.e(TAG, "apiParseMarkdown bad response: ${r.responseType} ${r.details}")
return null
}
private fun networkErrorAlert(r: CR): Boolean {
return when {
r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorAgent
@@ -1845,6 +1857,7 @@ sealed class CC {
class ApiListContacts(val userId: Long): CC()
class ApiUpdateProfile(val userId: Long, val profile: Profile): CC()
class ApiSetContactPrefs(val contactId: Long, val prefs: ChatPreferences): CC()
class ApiParseMarkdown(val text: String): CC()
class ApiSetContactAlias(val contactId: Long, val localAlias: String): CC()
class ApiSetConnectionAlias(val connId: Long, val localAlias: String): CC()
class ApiCreateMyAddress(val userId: Long): CC()
@@ -1950,6 +1963,7 @@ sealed class CC {
is ApiListContacts -> "/_contacts $userId"
is ApiUpdateProfile -> "/_profile $userId ${json.encodeToString(profile)}"
is ApiSetContactPrefs -> "/_set prefs @$contactId ${json.encodeToString(prefs)}"
is ApiParseMarkdown -> "/_parse $text"
is ApiSetContactAlias -> "/_set alias @$contactId ${localAlias.trim()}"
is ApiSetConnectionAlias -> "/_set alias :$connId ${localAlias.trim()}"
is ApiCreateMyAddress -> "/_address $userId"
@@ -2044,6 +2058,7 @@ sealed class CC {
is ApiListContacts -> "apiListContacts"
is ApiUpdateProfile -> "apiUpdateProfile"
is ApiSetContactPrefs -> "apiSetContactPrefs"
is ApiParseMarkdown -> "apiParseMarkdown"
is ApiSetContactAlias -> "apiSetContactAlias"
is ApiSetConnectionAlias -> "apiSetConnectionAlias"
is ApiCreateMyAddress -> "apiCreateMyAddress"
@@ -3325,6 +3340,7 @@ sealed class CR {
@Serializable @SerialName("newContactConnection") class NewContactConnection(val user: User, val connection: PendingContactConnection): CR()
@Serializable @SerialName("contactConnectionDeleted") class ContactConnectionDeleted(val user: User, val connection: PendingContactConnection): CR()
@Serializable @SerialName("versionInfo") class VersionInfo(val versionInfo: CoreVersionInfo, val chatMigrations: List<UpMigration>, val agentMigrations: List<UpMigration>): CR()
@Serializable @SerialName("apiParsedMarkdown") class ParsedMarkdown(val formattedText: List<FormattedText>? = null): CR()
@Serializable @SerialName("cmdOk") class CmdOk(val user: User?): CR()
@Serializable @SerialName("chatCmdError") class ChatCmdError(val user_: User?, val chatError: ChatError): CR()
@Serializable @SerialName("chatError") class ChatRespError(val user_: User?, val chatError: ChatError): CR()
@@ -3449,6 +3465,7 @@ sealed class CR {
is NewContactConnection -> "newContactConnection"
is ContactConnectionDeleted -> "contactConnectionDeleted"
is VersionInfo -> "versionInfo"
is ParsedMarkdown -> "apiParsedMarkdown"
is CmdOk -> "cmdOk"
is ChatCmdError -> "chatCmdError"
is ChatRespError -> "chatError"
@@ -3501,6 +3518,7 @@ sealed class CR {
is ContactAliasUpdated -> withUser(user, json.encodeToString(toContact))
is ConnectionAliasUpdated -> withUser(user, json.encodeToString(toConnection))
is ContactPrefsUpdated -> withUser(user, "fromContact: $fromContact\ntoContact: \n${json.encodeToString(toContact)}")
is ParsedMarkdown -> json.encodeToString(formattedText)
is UserContactLink -> withUser(user, contactLink.responseDetails)
is UserContactLinkUpdated -> withUser(user, contactLink.responseDetails)
is UserContactLinkCreated -> withUser(user, connReqContact)
@@ -400,7 +400,6 @@ fun LocalAliasEditor(
updateValue: (String) -> Unit
) {
var value by rememberSaveable { mutableStateOf(initialValue) }
var updatedValueAtLeastOnce = remember { false }
val modifier = if (center)
Modifier.padding(horizontal = if (!leadingIcon) DEFAULT_PADDING else 0.dp).widthIn(min = 100.dp)
else
@@ -425,23 +424,19 @@ fun LocalAliasEditor(
keyboardActions = KeyboardActions(onDone = { updateValue(value) })
) {
value = it
updatedValueAtLeastOnce = true
}
}
LaunchedEffect(Unit) {
var prevValue = value
snapshotFlow { value }
.distinctUntilChanged()
.onEach { delay(500) } // wait a little after every new character, don't emit until user stops typing
.conflate() // get the latest value
.filter { it == value && it != prevValue } // don't process old ones
.filter { it == value } // don't process old ones
.collect {
updateValue(it)
prevValue = it
updateValue(value)
}
}
DisposableEffect(Unit) {
onDispose { if (updatedValueAtLeastOnce) updateValue(value) } // just in case snapshotFlow will be canceled when user presses Back too fast
onDispose { updateValue(value) } // just in case snapshotFlow will be canceled when user presses Back too fast
}
}
@@ -240,7 +240,7 @@ fun ComposeView(
link.startsWith("https://simplex.chat", true) || link.startsWith("http://simplex.chat", true)
fun parseMessage(msg: String): String? {
val parsedMsg = parseToMarkdown(msg)
val parsedMsg = runBlocking { chatModel.controller.apiParseMarkdown(msg) }
val link = parsedMsg?.firstOrNull { ft -> ft.format is Format.Uri && !cancelledLinks.contains(ft.text) && !isSimplexLink(ft.text) }
return link?.text
}
@@ -64,22 +64,12 @@ fun SendMsgView(
Box(Modifier.padding(vertical = 8.dp)) {
val cs = composeState.value
var progressByTimeout by rememberSaveable { mutableStateOf(false) }
LaunchedEffect(composeState.value.inProgress) {
progressByTimeout = if (composeState.value.inProgress) {
delay(500)
composeState.value.inProgress
} else {
false
}
}
val showProgress = cs.inProgress && (cs.preview is ComposePreview.MediaPreview || cs.preview is ComposePreview.FilePreview)
val showVoiceButton = cs.message.isEmpty() && showVoiceRecordIcon && !composeState.value.editing &&
cs.liveMessage == null && (cs.preview is ComposePreview.NoPreview || recState.value is RecordingState.Started)
val showDeleteTextButton = rememberSaveable { mutableStateOf(false) }
PlatformTextField(composeState, textStyle, showDeleteTextButton, userIsObserver, onMessageChange, editPrevMessage) {
if (!cs.inProgress) {
sendMessage(null)
}
sendMessage(null)
}
// Disable clicks on text field
if (cs.preview is ComposePreview.VoicePreview || !userCanSend || cs.inProgress) {
@@ -108,7 +98,7 @@ fun SendMsgView(
}
}
when {
progressByTimeout -> ProgressIndicator()
showProgress -> ProgressIndicator()
showVoiceButton -> {
Row(verticalAlignment = Alignment.CenterVertically) {
val stopRecOnNextClick = remember { mutableStateOf(false) }
@@ -78,6 +78,7 @@ fun DatabaseView(
chatArchiveName,
chatArchiveTime,
chatLastStart,
m.controller.appPrefs.privacyFullBackup,
appFilesCountAndSize,
chatItemTTL,
m.currentUser.value,
@@ -127,6 +128,7 @@ fun DatabaseLayout(
chatArchiveName: MutableState<String?>,
chatArchiveTime: MutableState<Instant?>,
chatLastStart: MutableState<Instant?>,
privacyFullBackup: SharedPreference<Boolean>,
appFilesCountAndSize: MutableState<Pair<Int, Long>>,
chatItemTTL: MutableState<ChatItemTTL>,
currentUser: User?,
@@ -166,13 +168,6 @@ fun DatabaseLayout(
SectionView(stringResource(MR.strings.run_chat_section)) {
RunChatSetting(runChat, stopped, startChat, stopChatAlert)
}
SectionTextFooter(
if (stopped) {
stringResource(MR.strings.you_must_use_the_most_recent_version_of_database)
} else {
stringResource(MR.strings.stop_chat_to_enable_database_actions)
}
)
SectionDividerSpaced()
SectionView(stringResource(MR.strings.chat_database_section)) {
@@ -185,6 +180,8 @@ fun DatabaseLayout(
iconColor = if (unencrypted) WarningOrange else MaterialTheme.colors.secondary,
disabled = operationsDisabled
)
AppDataBackupPreference(privacyFullBackup, initialRandomDBPassphrase)
SectionDividerSpaced(maxBottomPadding = false)
SettingsActionItem(
painterResource(MR.images.ic_ios_share),
stringResource(MR.strings.export_database),
@@ -228,6 +225,13 @@ fun DatabaseLayout(
disabled = operationsDisabled
)
}
SectionTextFooter(
if (stopped) {
stringResource(MR.strings.you_must_use_the_most_recent_version_of_database)
} else {
stringResource(MR.strings.stop_chat_to_enable_database_actions)
}
)
SectionDividerSpaced(maxTopPadding = true)
SectionView(stringResource(MR.strings.files_and_media_section).uppercase()) {
@@ -254,6 +258,23 @@ fun DatabaseLayout(
}
}
@Composable
private fun AppDataBackupPreference(privacyFullBackup: SharedPreference<Boolean>, initialRandomDBPassphrase: SharedPreference<Boolean>) {
SettingsPreferenceItem(
painterResource(MR.images.ic_backup),
iconColor = MaterialTheme.colors.secondary,
pref = privacyFullBackup,
text = stringResource(MR.strings.full_backup)
) {
if (initialRandomDBPassphrase.get()) {
exportProhibitedAlert()
privacyFullBackup.set(false)
} else {
privacyFullBackup.set(it)
}
}
}
private fun setChatItemTTLAlert(
m: ChatModel, selectedChatItemTTL: MutableState<ChatItemTTL>,
progressIndicator: MutableState<Boolean>,
@@ -662,6 +683,7 @@ fun PreviewDatabaseLayout() {
chatArchiveName = remember { mutableStateOf("dummy_archive") },
chatArchiveTime = remember { mutableStateOf(Clock.System.now()) },
chatLastStart = remember { mutableStateOf(Clock.System.now()) },
privacyFullBackup = SharedPreference({ true }, {}),
appFilesCountAndSize = remember { mutableStateOf(0 to 0L) },
chatItemTTL = remember { mutableStateOf(ChatItemTTL.None) },
currentUser = User.sampleData,
@@ -201,7 +201,6 @@ object AppearanceScope {
val supportedLanguages = mapOf(
"system" to generalGetString(MR.strings.language_system),
"en" to "English",
"bg" to "Български",
"cs" to "Čeština",
"de" to "Deutsch",
"es" to "Español",
@@ -214,7 +213,7 @@ object AppearanceScope {
"ru" to "Русский",
"zh-CN" to "简体中文"
)
val values by remember(ChatController.appPrefs.appLanguage.state.value) { mutableStateOf(supportedLanguages.map { it.key to it.value }) }
val values by remember { mutableStateOf(supportedLanguages.map { it.key to it.value }) }
ExposedDropDownSettingRow(
generalGetString(MR.strings.settings_section_title_language).lowercase().replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.US) else it.toString() },
values,
@@ -228,9 +227,7 @@ object AppearanceScope {
@Composable
private fun ThemeSelector(state: State<String>, onSelected: (String) -> Unit) {
val darkTheme = chat.simplex.common.ui.theme.isSystemInDarkTheme()
val values by remember(ChatController.appPrefs.appLanguage.state.value) {
mutableStateOf(ThemeManager.allThemes(darkTheme).map { it.second.name to it.third })
}
val values by remember { mutableStateOf(ThemeManager.allThemes(darkTheme).map { it.second.name to it.third }) }
ExposedDropDownSettingRow(
generalGetString(MR.strings.theme),
values,
@@ -147,7 +147,7 @@
<string name="rcv_conn_event_switch_queue_phase_completed">تم تغيير العنوان من أجلك</string>
<string name="cant_delete_user_profile">لا يمكن حذف ملف تعريف المستخدم!</string>
<string name="icon_descr_video_asked_to_receive">طلب لاستلام الفيديو</string>
<string name="add_new_contact_to_create_one_time_QR_code"><![CDATA[<b> إضافة جهة اتصال جديدة </b>: لإنشاء رمز الاستجابة السريعة الخاص بك لمرة واحدة لجهة اتصالك.]]></string>
<string name="add_new_contact_to_create_one_time_QR_code"><b> إضافة جهة اتصال جديدة </b>: لإنشاء رمز الاستجابة السريعة الخاص بك لمرة واحدة لجهة اتصالك.</string>
<string name="scan_QR_code_to_connect_to_contact_who_shows_QR_code"><![CDATA[<b> امسح رمز الاستجابة السريعة </b>: للاتصال بجهة الاتصال التي تعرض لك رمز الاستجابة السريعة.]]></string>
<string name="callstatus_in_progress">مكالمتك تحت الإجراء</string>
<string name="callstatus_ended">انتهت المكالمة</string>
@@ -414,7 +414,7 @@
<string name="settings_developer_tools">أدوات المطور</string>
<string name="smp_server_test_delete_queue">حذف قائمة الانتظار</string>
<string name="error_updating_user_privacy">خطأ في تحديث خصوصية المستخدم</string>
<string name="desktop_scan_QR_code_from_app_via_scan_QR_code"><![CDATA[💻 سطح المكتب: امسح رمز الاستجابة السريعة (QR) المعروض من التطبيق، عبر <b>مسح رمز QR</b>.]]></string>
<string name="desktop_scan_QR_code_from_app_via_scan_QR_code">💻 سطح المكتب: امسح رمز الاستجابة السريعة (QR) المعروض من التطبيق، عبر <b>مسح رمز QR</b>.</string>
<string name="delete_profile">حذف ملف التعريف</string>
<string name="smp_servers_delete_server">حذف الخادم</string>
<string name="error_updating_link_for_group">خطأ في تحديث ارتباط المجموعة</string>
@@ -498,7 +498,7 @@
<string name="custom_time_unit_hours">ساعات</string>
<string name="edit_history">السجل</string>
<string name="image_will_be_received_when_contact_completes_uploading">سيتم استلام الصورة عند اكتمال تحميل جهة اتصالك.</string>
<string name="if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel"><![CDATA[إذا لم تتمكن من الالتقاء شخصيًا، <b>اعرض رمز الاستجابة السريعة في مكالمة الفيديو</b>، أو شارك الرابط.]]></string>
<string name="if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel">إذا لم تتمكن من الالتقاء شخصيًا، <b>اعرض رمز الاستجابة السريعة في مكالمة الفيديو</b>، أو شارك الرابط.</string>
<string name="install_simplex_chat_for_terminal">ثبّت SimpleX Chat لطرفية</string>
<string name="network_disable_socks_info">إذا قمت بالتأكيد، فستتمكن خوادم المراسلة من رؤية عنوان IP الخاص بك ومزود الخدمة الخاص بك - أي الخوادم التي تتصل بها.</string>
<string name="hide_dev_options">إخفاء:</string>
@@ -521,7 +521,7 @@
<string name="info_menu">المعلومات</string>
<string name="v4_3_improved_privacy_and_security_desc">إخفاء شاشة التطبيق في التطبيقات الحديثة.</string>
<string name="v4_3_improved_privacy_and_security">تحسن الخصوصية والأمان</string>
<string name="if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link"><![CDATA[إذا لم تتمكن من الالتقاء شخصيًا، فيمكنك <b>مسح رمز QR في مكالمة الفيديو</b>، أو يمكن لجهة الاتصال مشاركة رابط الدعوة.]]></string>
<string name="if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link">إذا لم تتمكن من الالتقاء شخصيًا، فيمكنك <b>مسح رمز QR في مكالمة الفيديو</b>، أو يمكن لجهة الاتصال مشاركة رابط الدعوة.</string>
<string name="immune_to_spam_and_abuse">محصن ضد البريد العشوائي وسوء المعاملة</string>
<string name="description_via_one_time_link_incognito">التخفي عبر رابط لمرة واحدة</string>
<string name="icon_descr_image_snd_complete">أرسلت صورة</string>
@@ -541,7 +541,7 @@
<string name="onboarding_notifications_mode_service">فوري</string>
<string name="host_verb">المضيف</string>
<string name="hide_notification">إخفاء</string>
<string name="turn_off_battery_optimization"><![CDATA[من أجل استخدامها، يرجى <b>تعطيل تحسين البطارية</b> لSimpleX في مربع الحوار التالي. وإلا، سيتم تعطيل الإخطارات.]]></string>
<string name="turn_off_battery_optimization">من أجل استخدامها، يرجى <b>تعطيل تحسين البطارية</b> لSimpleX في مربع الحوار التالي. وإلا، سيتم تعطيل الإخطارات.</string>
<string name="in_reply_to">ردًا على</string>
<string name="icon_descr_instant_notifications">إشعارات فورية</string>
<string name="enter_one_ICE_server_per_line">خوادم ICE (واحد لكل سطر)</string>
@@ -716,7 +716,7 @@
<string name="ensure_ICE_server_address_are_correct_format_and_unique">تأكد من أن عناوين خادم WebRTC ICE بالتنسيق الصحيح، وأن تكون مفصولة بأسطر وليست مكررة.</string>
<string name="mark_code_verified">وضع علامة تَحقق منه</string>
<string name="error_saving_user_password">خطأ في حفظ كلمة مرور المستخدم</string>
<string name="many_people_asked_how_can_it_deliver"><![CDATA[سأل الكثير من الناس: <i>إذا SimpleX ليس لديه معرّفات مستخدم، كيف يمكنه توصيل الرسائل؟</i>]]></string>
<string name="many_people_asked_how_can_it_deliver">سأل الكثير من الناس: <i>إذا SimpleX ليس لديه معرّفات مستخدم، كيف يمكنه توصيل الرسائل؟</i></string>
<string name="error_saving_group_profile">خطأ في حفظ ملف تعريف المجموعة</string>
<string name="notification_preview_mode_message">رسالة نصية</string>
<string name="message_reactions">ردود فعل الرسائل</string>
@@ -725,7 +725,7 @@
<string name="message_delivery_error_title">خطأ في تسليم الرسالة</string>
<string name="network_and_servers">الشبكة والخوادم</string>
<string name="moderate_verb">إشراف</string>
<string name="mobile_tap_open_in_mobile_app_then_tap_connect_in_app"><![CDATA[📱 للجوال: انقر فوق <b>فتح في تطبيق الجوال</b> ، ثم انقر فوق <b>اتصال</b> في التطبيق.]]></string>
<string name="mobile_tap_open_in_mobile_app_then_tap_connect_in_app">📱 للجوال: انقر فوق <b>فتح في تطبيق الجوال</b> ، ثم انقر فوق <b>اتصال</b> في التطبيق.</string>
<string name="share_text_moderated_at">تحت الإشراف في: %s</string>
<string name="message_reactions_prohibited_in_this_chat">ردود الفعل الرسائل ممنوعة في هذه الدردشة.</string>
<string name="moderated_item_description">مُشرف بواسطة %s</string>
@@ -839,7 +839,7 @@
<string name="call_connection_peer_to_peer">ند لند</string>
<string name="people_can_connect_only_via_links_you_share">يمكن للناس التواصل معك فقط عبر الرابط الذي تقوم بمشاركته</string>
<string name="icon_descr_call_pending_sent">مكالمة في الانتظار</string>
<string name="only_client_devices_store_contacts_groups_e2e_encrypted_messages"><![CDATA[تقوم أجهزة العميل فقط بتخزين ملفات تعريف المستخدمين وجهات الاتصال والمجموعات والرسائل المرسلة باستخدام <b>تشفير ثنائي الطبقات من بين الطريفين</b>.]]></string>
<string name="only_client_devices_store_contacts_groups_e2e_encrypted_messages">تقوم أجهزة العميل فقط بتخزين ملفات تعريف المستخدمين وجهات الاتصال والمجموعات والرسائل المرسلة باستخدام <b>تشفير ثنائي الطبقات من بين الطريفين</b>.</string>
<string name="reset_color">إعادة تعيين الألوان</string>
<string name="save_verb">حفظ</string>
<string name="smp_servers_preset_address">عنوان الخادم المحدد مسبقًا</string>
@@ -853,7 +853,7 @@
<string name="onboarding_notifications_mode_title">الإشعارات خاصة</string>
<string name="store_passphrase_securely_without_recover">يرجى تخزين عبارة المرور بشكل آمن، فلن تتمكن من الوصول إلى الدردشة إذا فقدتها.</string>
<string name="contact_developers">يرجى تحديث التطبيق والاتصال بالمطورين.</string>
<string name="read_more_in_user_guide_with_link"><![CDATA[اقرأ المزيد في <font color="#0088ff">دليل المستخدم</font>.]]></string>
<string name="read_more_in_user_guide_with_link">اقرأ المزيد في &lt;font color=#0088ff&gt;دليل المستخدم&lt;/font&gt;.</string>
<string name="auth_open_chat_profiles">افتح ملفات تعريف الدردشة</string>
<string name="revoke_file__confirm">اسحب الوصول</string>
<string name="save_archive">حفظ الأرشيف</string>
@@ -878,7 +878,7 @@
<string name="saved_ICE_servers_will_be_removed">ستتم إزالة خوادم WebRTC ICE المحفوظة.</string>
<string name="prohibit_sending_files">منع إرسال الملفات والوسائط.</string>
<string name="callstate_received_answer">استلمت إجابة…</string>
<string name="read_more_in_github_with_link"><![CDATA[اقرأ المزيد في <font color="#0088ff">مستودع GitHub</font>.]]></string>
<string name="read_more_in_github_with_link">اقرأ المزيد في &lt;font color=#0088ff&gt;مستودع GitHub&lt;/font&gt;.</string>
<string name="reject">رفض</string>
<string name="relay_server_protects_ip">يحمي خادم الترحيل عنوان IP الخاص بك، ولكن يمكنه مراقبة مدة المكالمة.</string>
<string name="restore_database_alert_desc">الرجاء إدخال كلمة المرور السابقة بعد استعادة نسخة احتياطية لقاعدة البيانات. لا يمكن التراجع عن هذا الإجراء.</string>
@@ -1090,7 +1090,7 @@
<string name="stop_file__action">إيقاف الملف</string>
<string name="stop_snd_file__title">التوقف عن استلام الملف؟</string>
<string name="icon_descr_address">عنوان SimpleX</string>
<string name="disable_onion_hosts_when_not_supported"><![CDATA[اضبط <i>استخدم مضيفي .onion</i> إلى \"لا\" إذا كان وكيل SOCKS لا يدعمها.]]></string>
<string name="disable_onion_hosts_when_not_supported">اضبط <i>استخدم مضيفي .onion</i> إلى \"لا\" إذا كان وكيل SOCKS لا يدعمها.</string>
<string name="share_with_contacts">مشاركة مع جهات الاتصال</string>
<string name="shutdown_alert_question">إيقاف التشغيل؟</string>
<string name="network_socks_proxy_settings">إعدادات وكيل SOCKS</string>
@@ -1117,7 +1117,7 @@
<string name="v4_4_french_interface_descr">بفضل المستخدمين - المساهمة عبر Weblate!</string>
<string name="v4_6_audio_video_calls_descr">دعم البلوتوث وتحسينات أخرى.</string>
<string name="v5_0_polish_interface_descr">بفضل المستخدمين - المساهمة عبر Weblate!</string>
<string name="to_preserve_privacy_simplex_has_background_service_instead_of_push_notifications_it_uses_a_few_pc_battery"><![CDATA[للحفاظ على خصوصيتك، بدلاً من دفع الإشعارات، يحتوي التطبيق على <b>خدمة SimpleX تعمل في الخلفية</b> – يستخدم نسبة قليلة من البطارية يوميًا.]]></string>
<string name="to_preserve_privacy_simplex_has_background_service_instead_of_push_notifications_it_uses_a_few_pc_battery">للحفاظ على خصوصيتك، بدلاً من دفع الإشعارات، يحتوي التطبيق على <b>خدمة SimpleX تعمل في الخلفية</b> – يستخدم نسبة قليلة من البطارية يوميًا.</string>
<string name="tap_to_start_new_chat">انقر لبدء محادثة جديدة</string>
<string name="to_share_with_your_contact">(للمشاركة مع جهة اتصالك)</string>
<string name="to_connect_via_link_title">للتواصل عبر الرابط</string>
@@ -1240,7 +1240,7 @@
<string name="snd_group_event_user_left">غادرت</string>
<string name="you_must_use_the_most_recent_version_of_database">يجب عليك استخدام أحدث إصدار من قاعدة بيانات الدردشة الخاصة بك على جهاز واحد فقط، وإلا فقد تتوقف عن تلقي الرسائل من بعض جهات الاتصال.</string>
<string name="video_will_be_received_when_contact_is_online">سيتم استلام الفيديو عندما تكون جهة اتصالك متصلة بالإنترنت، يرجى الانتظار أو التحقق لاحقًا!</string>
<string name="you_control_servers_to_receive_your_contacts_to_send"><![CDATA[يمكنك التحكم من خلال الخادم (الخوادم) <b>لتلقي</b> الرسائل وجهات اتصالك - الخوادم التي تستخدمها لمراسلتهم.]]></string>
<string name="you_control_servers_to_receive_your_contacts_to_send">يمكنك التحكم من خلال الخادم (الخوادم) <b>لتلقي</b> الرسائل وجهات اتصالك - الخوادم التي تستخدمها لمراسلتهم.</string>
<string name="you_can_share_this_address_with_your_contacts">يمكنك مشاركة هذا العنوان مع جهات اتصالك للسماح لهم بالاتصال بـ%s.</string>
<string name="snd_group_event_member_deleted">أُزيلت %1$s</string>
<string name="update_database">تحديث</string>
@@ -1293,7 +1293,7 @@
<string name="unhide_profile">إلغاء إخفاء ملف تعريف</string>
<string name="alert_text_connection_pending_they_need_to_be_online_can_delete_and_retry">يجب أن تكون جهة الاتصال متصلة بالإنترنت حتى يكتمل الاتصال.
\nيمكنك إلغاء هذا الاتصال وإزالة جهة الاتصال (والمحاولة لاحقًا باستخدام رابط جديد).</string>
<string name="you_can_also_connect_by_clicking_the_link"><![CDATA[يمكنك أيضًا الاتصال بالضغط على الرابط. إذا تم فتحه في المتصفح، فانقر فوق الزر <b>فتح في تطبيق الجوال</b>.]]></string>
<string name="you_can_also_connect_by_clicking_the_link">يمكنك أيضًا الاتصال بالضغط على الرابط. إذا تم فتحه في المتصفح، فانقر فوق الزر <b>فتح في تطبيق الجوال</b>.</string>
<string name="smp_servers_use_server_for_new_conn">استخدم للاتصالات الجديدة</string>
<string name="smp_servers_use_server">استخدم الخادم</string>
<string name="smp_servers_your_server_address">عنوان خادمك</string>
@@ -1335,7 +1335,7 @@
<string name="observer_cant_send_message_title">لا يمكنك إرسال رسائل!</string>
<string name="you_need_to_allow_to_send_voice">تحتاج إلى السماح لجهة الاتصال الخاصة بك بإرسال رسائل صوتية لتتمكن من إرسالها.</string>
<string name="contact_sent_large_file">أرسلت جهة اتصالك ملفًا أكبر من الحجم الأقصى المعتمد حاليًا (%1$s).</string>
<string name="you_can_connect_to_simplex_chat_founder"><![CDATA[يمكنك <font color=#0088ff>الاتصال بمطوري SimpleX Chat لطرح أي أسئلة وتلقي التحديثات</font>.]]></string>
<string name="you_can_connect_to_simplex_chat_founder">يمكنك &lt;font color=#0088ff&gt;الاتصال بمطوري SimpleX Chat لطرح أي أسئلة وتلقي التحديثات&lt;/font&gt;.</string>
<string name="smp_servers_your_server">خادمك</string>
<string name="your_profile_is_stored_on_device_and_shared_only_with_contacts_simplex_cannot_see_it">يُخزن ملف تعريفك على جهازك ومشاركته فقط مع جهات اتصالك. لا تستطيع خوادم SimpleX رؤية ملف تعريفك.</string>
<string name="icon_descr_video_off">الفيديو مقفل</string>
@@ -140,11 +140,11 @@
<string name="turn_off_battery_optimization_button">Allow</string>
<string name="turn_off_system_restriction_button">Open app settings</string>
<string name="disable_notifications_button">Disable notifications</string>
<string name="system_restricted_background_desc">SimpleX can\'t run in background. You will receive the notifications only when the app is running.</string>
<string name="system_restricted_background_desc"><![CDATA[SimpleX can\'t run in background. You will receive the notifications only when the app is running.]]></string>
<string name="system_restricted_background_warn"><![CDATA[To enable notifications, please choose <b>App battery usage</b> / <b>Unrestricted</b> in the app settings.]]></string>
<string name="system_restricted_background_in_call_title">No background calls</string>
<string name="system_restricted_background_in_call_desc">The app may be closed after 1 minute in background.</string>
<string name="system_restricted_background_in_call_warn"><![CDATA[To make calls in background, please choose <b>App battery usage</b> / <b>Unrestricted</b> in the app settings.]]></string>
<string name="system_restricted_background_in_call_desc"><![CDATA[The app may be closed after 1 minute in background.]]></string>
<string name="system_restricted_background_in_call_warn"><![CDATA[To make calls in background, please choose <b>App battery usage</b> / <b>Unrestricted</b> in the app settings]]>.</string>
<string name="enter_passphrase_notification_title">Passphrase is needed</string>
<string name="enter_passphrase_notification_desc">To receive notifications, please, enter the database passphrase</string>
<string name="database_initialization_error_title">Can\'t initialize the database</string>
@@ -1556,4 +1556,4 @@
<!-- Under development -->
<string name="in_developing_title">Coming soon!</string>
<string name="in_developing_desc">This feature is not yet supported. Try the next release.</string>
</resources>
</resources>
@@ -48,7 +48,7 @@
<string name="accept_feature">Приеми</string>
<string name="color_primary_variant">Допълнителен акцент</string>
<string name="v4_2_group_links_desc">Админите могат да създадат линкове за присъединяване към групи.</string>
<string name="if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel"><![CDATA[Ако не можете да се срещнете лично, <b>покажете QR кода във видеоразговора</b> или споделете линка.]]></string>
<string name="if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel">Ако не можете да се срещнете лично, <b>покажете QR кода във видеоразговора</b> или споделете линка.</string>
<string name="open_simplex_chat_to_accept_call">Отворете SimpleX Chat, за да приемете повикването</string>
<string name="v4_6_audio_video_calls_descr">Поддръжка на bluetooth и други подобрения.</string>
<string name="relay_server_protects_ip">Relay сървърът защитава вашия IP адрес, но може да наблюдава продължителността на разговора.</string>
@@ -87,9 +87,9 @@
<string name="incognito_random_profile_from_contact_description">Произволен профил ще бъде изпратен до контакта, от който сте получили тази връзка</string>
<string name="la_authenticate">Идентифицирай</string>
<string name="turning_off_service_and_periodic">Оптимизацията на батерията е активна, изключват се фоновата услуга и периодичните заявки за нови съобщения. Можете да ги активирате отново през настройките.</string>
<string name="network_session_mode_user_description"><![CDATA[Ще се използва отделна TCP връзка (и идентификационни данни за SOCKS) <b>за всеки чат профил, който имате в приложението</b>.]]></string>
<string name="network_session_mode_user_description">Ще се използва отделна TCP връзка (и идентификационни данни за SOCKS) <b>за всеки чат профил, който имате в приложението</b>.</string>
<string name="icon_descr_audio_call">аудио разговор</string>
<string name="onboarding_notifications_mode_off_desc"><![CDATA[<b>Най-добро за батерията</b>. Ще получавате известия само когато приложението работи (БЕЗ фонова услуга).]]></string>
<string name="onboarding_notifications_mode_off_desc"><b>Най-добро за батерията</b>. Ще получавате известия само когато приложението работи (БЕЗ фонова услуга).</string>
<string name="network_session_mode_entity_description">Ще се използва отделна TCP връзка (и идентификационни данни за SOCKS) <b>за всеки контакт и член на група</b>.
\n<b>Моля, обърнете внимание</b>: ако имате много връзки, консумацията на батерията и трафика може да бъде значително по-висока и някои връзки може да се провалят.</string>
<string name="icon_descr_asked_to_receive">Помолен да получи изображението</string>
@@ -99,15 +99,15 @@
<string name="both_you_and_your_contacts_can_delete">И вие, и вашият контакт можете да изтриете необратимо изпратените съобщения.</string>
<string name="auth_unavailable">Идентификацията е недостъпна</string>
<string name="both_you_and_your_contact_can_add_message_reactions">И вие, и вашият контакт можете да добавяте реакции към съобщението.</string>
<string name="impossible_to_recover_passphrase"><![CDATA[<b>Моля, обърнете внимание</b>: НЯМА да можете да възстановите или промените паролата, ако я загубите.]]></string>
<string name="onboarding_notifications_mode_service_desc"><![CDATA[<b>Използва повече батерия</b>! Услугата на заден план винаги работи – известията се показват веднага щом съобщенията са налични.]]></string>
<string name="impossible_to_recover_passphrase"><b>Моля, обърнете внимание</b>: НЯМА да можете да възстановите или промените паролата, ако я загубите.</string>
<string name="onboarding_notifications_mode_service_desc"><b>Използва повече батерия</b>! Услугата на заден план винаги работи – известията се показват веднага щом съобщенията са налични.</string>
<string name="integrity_msg_bad_hash">лош хеш на съобщението</string>
<string name="alert_title_msg_bad_hash">Лош хеш на съобщението</string>
<string name="no_call_on_lock_screen">Деактивиране</string>
<string name="callstatus_calling">повикване…</string>
<string name="if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link"><![CDATA[Ако не можете да се срещнете лично, можете <b>да сканирате QR код във видеоразговора</b> или вашият контакт може да сподели линк за покана.]]></string>
<string name="if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link">Ако не можете да се срещнете лично, можете <b>да сканирате QR код във видеоразговора</b> или вашият контакт може да сподели линк за покана.</string>
<string name="notifications_mode_service_desc">Услугата във фонов режим винаги работи – известията ще се показват веднага щом съобщенията са налични.</string>
<string name="it_can_disabled_via_settings_notifications_still_shown"><![CDATA[<b>Може да бъде деактивирано през настройките</b> – известията ще продължат да се показват, докато приложението работи.]]></string>
<string name="it_can_disabled_via_settings_notifications_still_shown"><b>Може да бъде деактивирано през настройките</b> – известията ще продължат да се показват, докато приложението работи.</string>
<string name="ntf_channel_calls">SimpleX Chat разговори</string>
<string name="notifications_mode_periodic">Започва периодично</string>
<string name="database_initialization_error_title">Базата данни не може да се стартира</string>
@@ -120,8 +120,8 @@
<string name="back">Назад</string>
<string name="cancel_verb">Отказ</string>
<string name="icon_descr_cancel_live_message">Спри живото съобщение</string>
<string name="add_new_contact_to_create_one_time_QR_code"><![CDATA[<b>Добави нов контакт</b>: за да създадете своя еднократен QR код за вашия контакт.]]></string>
<string name="scan_QR_code_to_connect_to_contact_who_shows_QR_code"><![CDATA[<b>Сканирай QR код</b>: за да се свържете с вашия контакт, който ви показва QR код.]]></string>
<string name="add_new_contact_to_create_one_time_QR_code"><b>Добави нов контакт</b>: за да създадете своя еднократен QR код за вашия контакт.</string>
<string name="scan_QR_code_to_connect_to_contact_who_shows_QR_code"><b>Сканирай QR код</b>: за да се свържете с вашия контакт, който ви показва QR код.</string>
<string name="use_camera_button">Камера</string>
<string name="if_you_cant_meet_in_person">Ако не можете да се срещнете лично, покажете QR код във видеоразговора или споделете линка.</string>
<string name="icon_descr_cancel_link_preview">спри визуализацията на линка</string>
@@ -140,7 +140,7 @@
<string name="callstatus_missed">пропуснато повикване</string>
<string name="callstatus_rejected">отхвърлено повикване</string>
<string name="callstate_starting">стартиране…</string>
<string name="onboarding_notifications_mode_periodic_desc"><![CDATA[<b>Добър за батерията</b>. Фоновата услуга проверява съобщенията на всеки 10 минути. Може да пропуснете обаждания или спешни съобщения.]]></string>
<string name="onboarding_notifications_mode_periodic_desc"><b>Добър за батерията</b>. Фоновата услуга проверява съобщенията на всеки 10 минути. Може да пропуснете обаждания или спешни съобщения.</string>
<string name="callstate_connected">свързан</string>
<string name="callstate_connecting">свързване…</string>
<string name="encrypted_audio_call">e2e криптиран аудио разговор</string>
@@ -441,7 +441,7 @@
<string name="custom_time_unit_days">дни</string>
<string name="choose_file_title">Избери файл</string>
<string name="if_you_received_simplex_invitation_link_you_can_open_in_browser">Ако сте получили линк за покана за SimpleX Chat, можете да го отворите във вашия браузър:</string>
<string name="desktop_scan_QR_code_from_app_via_scan_QR_code"><![CDATA[💻 настолен компютър: сканирайте показания QR код от приложението чрез <b>Сканирай QR код</b>.]]></string>
<string name="desktop_scan_QR_code_from_app_via_scan_QR_code">💻 настолен компютър: сканирайте показания QR код от приложението чрез <b>Сканирай QR код</b>.</string>
<string name="delete_pending_connection__question">Изтрий предстоящата връзка\?</string>
<string name="icon_descr_email">Електронна поща</string>
<string name="share_invitation_link">Сподели еднократен линк</string>
@@ -452,7 +452,7 @@
<string name="smp_servers_delete_server">Изтрий сървър</string>
<string name="dont_create_address">Не създавай адрес</string>
<string name="display_name__field">Показвано име:</string>
<string name="only_client_devices_store_contacts_groups_e2e_encrypted_messages"><![CDATA[Само потребителските устройства съхраняват потребителски профили, контакти, групи и съобщения, изпратени с <b>двуслойно криптиране от край до край</b>.]]></string>
<string name="only_client_devices_store_contacts_groups_e2e_encrypted_messages">Само потребителските устройства съхраняват потребителски профили, контакти, групи и съобщения, изпратени с <b>двуслойно криптиране от край до край</b>.</string>
<string name="receipts_contacts_title_disable">Деактивирай потвърждениeто\?</string>
<string name="receipts_contacts_enable_keep_overrides">Активиране (запазване на промените)</string>
<string name="receipts_contacts_title_enable">Активирай потвърждениeто\?</string>
@@ -766,7 +766,7 @@
<string name="invalid_message_format">невалиден формат на съобщението</string>
<string name="invalid_connection_link">Невалиден линк за връзка</string>
<string name="notification_display_mode_hidden_desc">Скриване на контакт и съобщение</string>
<string name="turn_off_battery_optimization"><![CDATA[За да го използвате, моля, <b>деактивирайте оптимизирането на батерията</b> за SimpleX в следващия диалогов прозорец. В противен случай известията ще бъдат деактивирани.]]></string>
<string name="turn_off_battery_optimization">За да го използвате, моля, <b>деактивирайте оптимизирането на батерията</b> за SimpleX в следващия диалогов прозорец. В противен случай известията ще бъдат деактивирани.</string>
<string name="icon_descr_instant_notifications">Незабавни известия</string>
<string name="service_notifications">Незабавни известия!</string>
<string name="service_notifications_disabled">Незабавните известия са деактивирани!</string>
@@ -791,14 +791,14 @@
<string name="custom_time_unit_seconds">секунди</string>
<string name="custom_time_unit_weeks">седмици</string>
<string name="v5_1_japanese_portuguese_interface">Японски и португалски потребителски интерфейс</string>
<string name="mobile_tap_open_in_mobile_app_then_tap_connect_in_app"><![CDATA[📱 мобилно: докоснете <b>Отваряне в мобилно приложение</b>, след което докоснете <b>Свързване</b> в приложението.]]></string>
<string name="mobile_tap_open_in_mobile_app_then_tap_connect_in_app">📱 мобилно: докоснете <b>Отваряне в мобилно приложение</b>, след което докоснете <b>Свързване</b> в приложението.</string>
<string name="reject_contact_button">Отхвърляне</string>
<string name="mark_unread">Маркирай като непрочетено</string>
<string name="mark_read">Маркирай като прочетено</string>
<string name="mute_chat">Без звук</string>
<string name="image_descr_qr_code">QR код</string>
<string name="icon_descr_more_button">Повече</string>
<string name="read_more_in_user_guide_with_link"><![CDATA[Прочетете повече в <font color=#0088ff>Ръководство за потребителя</font>.]]></string>
<string name="read_more_in_user_guide_with_link">Прочетете повече в &lt;font color=#0088ff&gt;Ръководство за потребителя&lt;/font&gt;.</string>
<string name="mark_code_verified">Маркирай като проверено</string>
<string name="is_not_verified">%s не е потвърдено</string>
<string name="is_verified">%s е потвърдено</string>
@@ -818,7 +818,7 @@
<string name="email_invite_subject">Нека да поговорим в SimpleX Chat</string>
<string name="password_to_show">Парола за показване</string>
<string name="no_spaces">Без интервали!</string>
<string name="read_more_in_github_with_link"><![CDATA[Прочетете повече в нашето <font color=#0088ff>GitHub хранилище</font>.]]></string>
<string name="read_more_in_github_with_link">Прочетете повече в нашето &lt;font color=#0088ff&gt;GitHub хранилище&lt;/font&gt;.</string>
<string name="onboarding_notifications_mode_off">Когато приложението работи</string>
<string name="onboarding_notifications_mode_periodic">Периодично</string>
<string name="paste_the_link_you_received">Постави получения линк</string>
@@ -973,7 +973,7 @@
<string name="simplex_service_notification_text">Получаване на съобщения…</string>
<string name="notifications_mode_off">Работи, когато приложението е отворено</string>
<string name="simplex_service_notification_title">Simplex Chat услуга</string>
<string name="to_preserve_privacy_simplex_has_background_service_instead_of_push_notifications_it_uses_a_few_pc_battery"><![CDATA[За да запази вашата поверителност, вместо да изпозлва push известия, приложението има <b> SimpleX фонова услуга </b> – използва няколко процента от батерията на ден.]]></string>
<string name="to_preserve_privacy_simplex_has_background_service_instead_of_push_notifications_it_uses_a_few_pc_battery">За да запази вашата поверителност, вместо да изпозлва push известия, приложението има <b> SimpleX фонова услуга </b> – използва няколко процента от батерията на ден.</string>
<string name="enter_passphrase_notification_desc">За да получавате известия, моля, въведете паролата на базата данни</string>
<string name="auth_log_in_using_credential">Влезте с вашите идентификационни данни</string>
<string name="message_delivery_error_title">Грешка при доставката на съобщението</string>
@@ -1001,7 +1001,7 @@
<string name="privacy_redefined">Поверителността преосмислена</string>
<string name="read_more_in_github">Прочетете повече в нашето хранилище в GitHub.</string>
<string name="make_private_connection">Добави поверителна връзка</string>
<string name="many_people_asked_how_can_it_deliver"><![CDATA[Много хора попитаха: <i>ако SimpleX няма потребителски идентификатори, как може да доставя съобщения\?</i>]]></string>
<string name="many_people_asked_how_can_it_deliver">Много хора попитаха: <i>ако SimpleX няма потребителски идентификатори, как може да доставя съобщения\?</i></string>
<string name="open_verb">Отвори</string>
<string name="relay_server_if_necessary">Реле сървър се използва само ако е необходимо. Друга страна може да наблюдава вашия IP адрес.</string>
<string name="lock_after">Заключване след</string>
@@ -1157,7 +1157,7 @@
<string name="stop_file__action">Спри файл</string>
<string name="icon_descr_settings">Настройки</string>
<string name="star_on_github">Звезда в GitHub</string>
<string name="disable_onion_hosts_when_not_supported"><![CDATA[Задайте <i>Използване на .onion хостове</i> на Не, ако SOCKS проксито не ги поддържа.]]></string>
<string name="disable_onion_hosts_when_not_supported">Задайте <i>Използване на .onion хостове</i> на Не, ако SOCKS проксито не ги поддържа.</string>
<string name="show_dev_options">Покажи:</string>
<string name="core_simplexmq_version">simplexmq: v%s (%2s)</string>
<string name="save_and_notify_contact">Запази и уведоми контакта</string>
@@ -1257,7 +1257,7 @@
\nМожете да го промените в Настройки.</string>
<string name="your_profile_is_stored_on_your_device">Вашият профил, контакти и доставени съобщения се съхраняват на вашето устройство.</string>
<string name="you_can_use_markdown_to_format_messages__prompt">Можете да използвате markdown за форматиране на съобщенията:</string>
<string name="you_control_servers_to_receive_your_contacts_to_send"><![CDATA[Вие контролирате през кой сървър(и) <b>да получавате</b> съобщенията, вашите контакти – сървърите, които използвате, за да им изпращате съобщения.]]></string>
<string name="you_control_servers_to_receive_your_contacts_to_send">Вие контролирате през кой сървър(и) <b>да получавате</b> съобщенията, вашите контакти – сървърите, които използвате, за да им изпращате съобщения.</string>
<string name="use_chat">Използвай чата</string>
<string name="update_database">Актуализация</string>
<string name="you_have_to_enter_passphrase_every_time">Трябва да въвеждате парола при всяко стартиране на приложението - тя не се съхранява на устройството.</string>
@@ -1351,9 +1351,9 @@
<string name="auth_you_will_be_required_to_authenticate_when_you_start_or_resume">Ще трябва да се идентифицирате, когато стартирате или възобновите приложението след 30 секунди във фонов режим.</string>
<string name="you_are_observer">вие сте наблюдател</string>
<string name="gallery_video_button">Видео</string>
<string name="you_can_connect_to_simplex_chat_founder"><![CDATA[Можете да <font color=#0088ff>се свържете с разработчиците на SimpleX Chat, за да задавате въпроси и да получавате актуализации</font>;.]]></string>
<string name="you_can_connect_to_simplex_chat_founder">Можете да &lt;font color=#0088ff&gt;се свържете с разработчиците на SimpleX Chat, за да задавате въпроси и да получавате актуализации&lt;/font&gt;.</string>
<string name="contact_wants_to_connect_with_you">иска да се свърже с вас!</string>
<string name="you_can_also_connect_by_clicking_the_link"><![CDATA[Можете също да се свържете, като натиснете върху линка. Ако се отвори в браузъра, натиснете върху бутона <b>Отваряне в мобилно приложение</b>.]]></string>
<string name="you_can_also_connect_by_clicking_the_link">Можете също да се свържете, като натиснете върху линка. Ако се отвори в браузъра, натиснете върху бутона <b>Отваряне в мобилно приложение</b>.</string>
<string name="xftp_servers">XFTP сървъри</string>
<string name="update_network_session_mode_question">Актуализиране на режима на изолация на транспорта\?</string>
<string name="your_current_profile">Вашият текущ профил</string>
@@ -762,7 +762,7 @@
<string name="enable_automatic_deletion_message">Esta acción no se puede deshacer. Se eliminarán los mensajes enviados y recibidos anteriores a la selección. Puede tardar varios minutos.</string>
<string name="messages_section_description">Esta configuración se aplica a los mensajes del perfil actual</string>
<string name="this_string_is_not_a_connection_link">¡Esta cadena no es un enlace de conexión!</string>
<string name="to_preserve_privacy_simplex_has_background_service_instead_of_push_notifications_it_uses_a_few_pc_battery"><![CDATA[Para preservar tu privacidad, en lugar de notificaciones automáticas la aplicación cuenta con un <b>servicio en segundo planoSimpleX</b>, usa un pequeño porcentaje de la batería al día.]]></string>
<string name="to_preserve_privacy_simplex_has_background_service_instead_of_push_notifications_it_uses_a_few_pc_battery">Para preservar tu privacidad, en lugar de notificaciones automáticas la aplicación cuenta con un <b>servicio en segundo planoSimpleX</b>, usa un pequeño porcentaje de la batería al día.</string>
<string name="icon_descr_settings">Configuración</string>
<string name="icon_descr_speaker_off">Altavoz desactivado</string>
<string name="add_contact_or_create_group">Inciar chat nuevo</string>
@@ -763,7 +763,7 @@
<string name="wrong_passphrase">Password del database sbagliata</string>
<string name="wrong_passphrase_title">Password sbagliata!</string>
<string name="you_are_invited_to_group_join_to_connect_with_group_members">Sei stato/a invitato/a al gruppo. Entra per connetterti con i suoi membri.</string>
<string name="you_can_start_chat_via_setting_or_by_restarting_the_app">Puoi avviare la chat tramite Impostazioni / Database o riavviando l\'app.</string>
<string name="you_can_start_chat_via_setting_or_by_restarting_the_app">Puoi avviare la chat tramite Impostazioni -&gt; Database o riavviando l\'app.</string>
<string name="youve_accepted_group_invitation_connecting_to_inviting_group_member">Sei entrato/a in questo gruppo. Connessione al membro del gruppo invitante.</string>
<string name="you_will_stop_receiving_messages_from_this_group_chat_history_will_be_preserved">Non riceverai più messaggi da questo gruppo. La cronologia della chat verrà conservata.</string>
<string name="group_member_status_invited">ha invitato</string>
@@ -530,7 +530,7 @@
<string name="v5_0_large_files_support">Vaizdo įrašai ir failai iki 1GB</string>
<string name="search_verb">Ieškoti</string>
<string name="la_mode_off">Išjungta</string>
<string name="network_session_mode_user_description"><![CDATA[Atskiras TCP ryšys (ir SOCKS kredencialas) bus naudojamas <b>kiekvienam pokalbių profiliui, kurį turite programoje</b>.]]></string>
<string name="network_session_mode_user_description">Atskiras TCP ryšys (ir SOCKS kredencialas) bus naudojamas <b>kiekvienam pokalbių profiliui, kurį turite programoje</b>.</string>
<string name="alert_text_decryption_error_n_messages_failed_to_decrypt">Nepavyko iššifruoti %1$d pranešimų.</string>
<string name="button_add_welcome_message">Pridėti sveikinimo pranešimą</string>
<string name="v5_2_more_things">Dar keletas dalykų</string>
@@ -539,7 +539,7 @@
<string name="always_use_relay">Visada naudoti relę</string>
<string name="icon_descr_asked_to_receive">Paprašė leidimo gauti nuotrauką</string>
<string name="send_disappearing_message_5_minutes">5 minučių</string>
<string name="onboarding_notifications_mode_off_desc"><![CDATA[<b>Mažiausiai bateriją eikvojantis variantas</b>. Jūs gausite sistemos pranešimus tik kai bus atidaryta programėlė (NEBUS fone veikiančios paslaugos).]]></string>
<string name="onboarding_notifications_mode_off_desc"><b>Mažiausiai bateriją eikvojantis variantas</b>. Jūs gausite sistemos pranešimus tik kai bus atidaryta programėlė (NEBUS fone veikiančios paslaugos).</string>
<string name="abort_switch_receiving_address">Atšaukti adreso keitimą</string>
<string name="v4_2_auto_accept_contact_requests">Automatiškai priimti susisiekimo užklausas</string>
<string name="v5_1_self_destruct_passcode_descr">Kai jį suvedate visi duomenys yra pašalinami.</string>
@@ -603,19 +603,19 @@
<string name="one_time_link_short">vienkartinė nuoroda</string>
<string name="accept_call_on_lock_screen">Priimti</string>
<string name="auto_accept_contact">Automatiškai priimti</string>
<string name="onboarding_notifications_mode_periodic_desc"><![CDATA[<b>Geriau baterijai</b>. Fono paslauga tikrina pranešimus kas 10 minučių. Galite praleisti skambučius arba skubius pranešimus.]]></string>
<string name="onboarding_notifications_mode_periodic_desc"><b>Geriau baterijai</b>. Fono paslauga tikrina pranešimus kas 10 minučių. Galite praleisti skambučius arba skubius pranešimus.</string>
<string name="callstatus_in_progress">vyksta skambutis</string>
<string name="icon_descr_cancel_live_message">Atšaukti tiesioginę žinutę</string>
<string name="alert_title_cant_invite_contacts">Nepavyko pakviesti kontaktų!</string>
<string name="icon_descr_call_progress">Vyksta skambutis</string>
<string name="feature_cancelled_item">atšaukta %s</string>
<string name="it_can_disabled_via_settings_notifications_still_shown"><![CDATA[<b>Galima išjungti nustatymuose</b> - pranešimai vis tiek bus rodomi kol programėlė veikia.]]></string>
<string name="it_can_disabled_via_settings_notifications_still_shown"><b>Galima išjungti nustatymuose</b> - pranešimai vis tiek bus rodomi kol programėlė veikia.</string>
<string name="icon_descr_cancel_link_preview">atšaukti nuorodos peržiūrą</string>
<string name="both_you_and_your_contact_can_add_message_reactions">Jūs ir jūsų kontaktas galite pridėti reakcijas į žinutę.</string>
<string name="call_on_lock_screen">Skambučiai užrakinimo ekrane:</string>
<string name="invite_prohibited">Nepavyko pakviesti kontakto!</string>
<string name="v4_5_transport_isolation_descr">Pagal pokalbių profilį (numatytieji nustatymai) arba pagal ryšį (BETA).</string>
<string name="onboarding_notifications_mode_service_desc"><![CDATA[<b>Naudoja daugiau baterijos</b>! Fono paslauga veikia visada - pranešimai rodomi, kai tik atsiranda žinučių.]]></string>
<string name="onboarding_notifications_mode_service_desc"><b>Naudoja daugiau baterijos</b>! Fono paslauga veikia visada - pranešimai rodomi, kai tik atsiranda žinučių.</string>
<string name="cannot_access_keychain">Negalima pasiekti \"Keystore\", kad išsaugotumėte duomenų bazės slaptažodį</string>
<string name="icon_descr_cancel_file_preview">Atšaukti failo peržiūrą</string>
<string name="icon_descr_cancel_image_preview">Atšaukti vaizdo peržiūrą</string>
@@ -34,7 +34,7 @@
<string name="incognito_random_profile_from_contact_description">โปรไฟล์แบบสุ่มจะถูกส่งไปยังผู้ติดต่อที่คุณได้รับลิงก์นี้จาก</string>
<string name="incognito_random_profile_description">โปรไฟล์แบบสุ่มจะถูกส่งไปยังผู้ติดต่อของคุณ</string>
<string name="network_session_mode_user_description"><![CDATA[การเชื่อมต่อ TCP (และข้อมูลรับรอง SOCKS) แบบแยกต่างหาก จะถูกใช้ <b> สําหรับแต่ละโปรไฟล์แชทที่คุณมีในแอป </b>]]></string>
<string name="network_session_mode_entity_description"><![CDATA[การเชื่อมต่อ TCP (และข้อมูลรับรอง SOCKS) แบบแยกต่างหาก จะถูกใช้ <b> สําหรับผู้ติดต่อแต่ละคนและสมาชิกกลุ่มแต่ละคน </b>\n<b>โปรดทราบ </b>: หากคุณมีการเชื่อมต่อจํานวนมากแบตเตอรี่และปริมาณการใช้การจราจรของคุณอาจสูงขึ้นอย่างมากและการเชื่อมต่อบางอย่างอาจล้มเหลว]]></string>
<string name="network_session_mode_entity_description">การเชื่อมต่อ TCP (และข้อมูลรับรอง SOCKS) แบบแยกต่างหาก จะถูกใช้ <b> สําหรับผู้ติดต่อแต่ละคนและสมาชิกกลุ่มแต่ละคน </b>\n<b>โปรดทราบ </b>: หากคุณมีการเชื่อมต่อจํานวนมากแบตเตอรี่และปริมาณการใช้การจราจรของคุณอาจสูงขึ้นอย่างมากและการเชื่อมต่อบางอย่างอาจล้มเหลว</string>
<string name="attach">แนบ</string>
<string name="v4_6_audio_video_calls">การโทรด้วยเสียงและวิดีโอ</string>
<string name="auto_accept_contact">ยอมรับอัตโนมัติ</string>
@@ -530,7 +530,7 @@
<string name="error_receiving_file">เกิดข้อผิดพลาดในการรับไฟล์</string>
<string name="if_you_enter_passcode_data_removed">หากคุณใส่รหัสผ่านนี้เมื่อเปิดแอป ข้อมูลแอปทั้งหมดจะถูกลบอย่างถาวร!</string>
<string name="if_you_choose_to_reject_the_sender_will_not_be_notified">หากคุณเลือกที่จะปฏิเสธ ผู้ส่งจะไม่ได้รับแจ้ง</string>
<string name="if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link">หากคุณไม่สามารถพบกันในชีวิตจริงได้ คุณสามารถสแกนคิวอาร์โค้ดในวิดีโอคอล หรือผู้ติดต่อของคุณสามารถแชร์ลิงก์เชิญได้</string>
<string name="if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link"><![CDATA[หากคุณไม่สามารถพบกันในชีวิตจริงได้ คุณสามารถสแกนคิวอาร์โค้ดในวิดีโอคอล </b> หรือผู้ติดต่อของคุณสามารถแชร์ลิงก์เชิญได้]]></string>
<string name="if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel"><![CDATA[หากคุณไม่สามารถพบกันในชีวิตจริงได้ <b>ให้แสดงคิวอาร์โค้ดในวิดีโอคอล</b> หรือแชร์ลิงก์]]></string>
<string name="if_you_cant_meet_in_person">หากคุณไม่สามารถพบกันในชีวิตจริงได้ ให้แสดงคิวอาร์โค้ดในวิดีโอคอล หรือแชร์ลิงก์</string>
<string name="network_disable_socks_info">หากคุณยืนยัน เซิร์ฟเวอร์การส่งข้อความจะสามารถเห็นที่อยู่ IP ของคุณและผู้ให้บริการของคุณ - ซึ่งคือเซิร์ฟเวอร์ใดที่คุณกำลังเชื่อมต่ออยู่</string>
@@ -927,7 +927,7 @@
<string name="smp_servers_scan_qr">สแกนคิวอาร์โค้ดของเซิร์ฟเวอร์</string>
<string name="smp_save_servers_question">บันทึกเซิร์ฟเวอร์\?</string>
<string name="saved_ICE_servers_will_be_removed">เซิร์ฟเวอร์ WebRTC ICE ที่บันทึกไว้จะถูกลบออก</string>
<string name="disable_onion_hosts_when_not_supported"><![CDATA[ตั้ง <i>ใช้โฮสต์ .onion</i> เป็น ไม่ หากพร็อกซี SOCKS ไม่รองรับ]]></string>
<string name="disable_onion_hosts_when_not_supported">ตั้ง <i>ใช้โฮสต์ .onion</i> เป็น ไม่ หากพร็อกซี SOCKS ไม่รองรับ</string>
<string name="show_dev_options">แสดง:</string>
<string name="show_developer_options">แสดงตัวเลือกสําหรับนักพัฒนาซอฟต์แวร์</string>
<string name="share_link">แชร์ลิงก์</string>
@@ -73,7 +73,7 @@
<string name="all_group_members_will_remain_connected">Konuşma üyelerinin tümü bağlı kalacaktır.</string>
<string name="allow_verb">İzin ver</string>
<string name="v5_0_app_passcode">Uygulama erişim kodu</string>
<string name="network_session_mode_user_description"><![CDATA[<b>Uygulamadaki her konuşma profliniz için</b> ayrı bir TCP bağlantısı (ve SOCKS kimliği) kullanılacaktır.]]></string>
<string name="network_session_mode_user_description"><b>Uygulamadaki her konuşma profliniz için</b> ayrı bir TCP bağlantısı (ve SOCKS kimliği) kullanılacaktır.</string>
<string name="network_session_mode_entity_description"><b>Konuştuğun kişilerin ve grup üyelerinin tamamı için</b> ayrı bir TCP bağlantısı (ve SOCKS kimliği) kullanılacaktır.
\n<b>Bilgin olsun</b>: Çok sayıda bağlantın varsa pilin ve veri kullanımın önemli ölçüde artabilir ve bazı bağlantılar başarısız olabilir.</string>
<string name="save_and_notify_group_members">Kaydet ve grup üyelerini bilgilendir</string>
@@ -391,7 +391,7 @@
<string name="send_disappearing_message_custom_time">Kişiselleştirilmiş süre</string>
<string name="copied">Panoya kopyalandı</string>
<string name="share_one_time_link">Tek seferlik davet bağlantısı oluştur</string>
<string name="desktop_scan_QR_code_from_app_via_scan_QR_code"><![CDATA[💻 masaüstü: uygulamadaki <b>Karekodu okut</b> ile karekodu okut]]></string>
<string name="desktop_scan_QR_code_from_app_via_scan_QR_code">💻 masaüstü: uygulamadaki <b>Karekodu okut</b> ile karekodu okut</string>
<string name="delete_pending_connection__question">Bekleyen bağlantıları sil\?</string>
<string name="delete_contact_menu_action">Sil</string>
<string name="delete_group_menu_action">Sil</string>
@@ -625,8 +625,8 @@
<string name="notification_display_mode_hidden_desc">Konuşulan kişileri ve mesajları gizle</string>
<string name="v4_3_improved_privacy_and_security_desc">Uygulamayı, son kullanılanlar kısmından gizle.</string>
<string name="how_simplex_works">SimpleX nasıl çalışıyor</string>
<string name="if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel"><![CDATA[Eğer yüz yüze görüşemiyorsanız <b>bir görüntülü aramada karşıdakine karekodunu gösterebilir</b> ya da konuştuğun kişiye bir katılım bağlantısı paylaşabilirsin.]]></string>
<string name="if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link"><![CDATA[Eğer yüz yüze görüşemiyorsanız <b>bir görüntülü aramada karşıdakinin karekodunu okutabilirsin</b> ya da konuştuğun kişi seninle bir katılım bağlantısı paylaşabilir.]]></string>
<string name="if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel">Eğer yüz yüze görüşemiyorsanız <b>bir görüntülü aramada karşıdakine karekodunu gösterebilir</b> ya da konuştuğun kişiye bir katılım bağlantısı paylaşabilirsin.</string>
<string name="if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link">Eğer yüz yüze görüşemiyorsanız <b>bir görüntülü aramada karşıdakinin karekodunu okutabilirsin</b> ya da konuştuğun kişi seninle bir katılım bağlantısı paylaşabilir.</string>
<string name="if_you_cant_meet_in_person">Eğer yüz yüze görüşemiyorsanız bir görüntülü aramada karşıdakine karekodunu gösterebilir ya da konuştuğun kişiye bir katılım bağlantısı paylaşabilirsin.</string>
<string name="if_you_choose_to_reject_the_sender_will_not_be_notified">Eğer geri çevirmeyi seçersen göndericiye bildirilmeyecek.</string>
<string name="if_you_received_simplex_invitation_link_you_can_open_in_browser">Eğer SimplexX Chat katılım bağlantısı alırsan bu bağlantıyı tarayıcında açabilirsin:</string>
@@ -717,7 +717,7 @@
<string name="icon_descr_instant_notifications">Anlık bildirimler</string>
<string name="service_notifications">Anlık bildirimler</string>
<string name="service_notifications_disabled">Anlık bildirimler devre dışı!</string>
<string name="turn_off_battery_optimization"><![CDATA[Bunu kullanmak için lütfen bir sonraki iletişim kutusunda SimpleX için <b>pil optimizasyonunu devre dışı bırakın</b>. Aksi takdirde, bildirimler devre dışı bırakılacaktır.]]></string>
<string name="turn_off_battery_optimization">Bunu kullanmak için lütfen bir sonraki iletişim kutusunda SimpleX için <b>pil optimizasyonunu devre dışı bırakın</b>. Aksi takdirde, bildirimler devre dışı bırakılacaktır.</string>
<string name="notification_preview_mode_contact">Kişi ismi</string>
<string name="auth_device_authentication_is_disabled_turning_off">Cihaz doğrulaması devre dışı. SimpleX Kilidi Kapatılıyor.</string>
<string name="auth_device_authentication_is_not_enabled_you_can_turn_on_in_settings_once_enabled">Cihaz doğrulaması etkin değil. Cihaz doğrulamasını etkinleştirdikten sonra SimpleX Kilidini Ayarlar üzerinden açabilirsiniz.</string>
@@ -740,7 +740,7 @@
<string name="v4_3_irreversible_message_deletion">Geri alınamaz mesaj silme</string>
<string name="v4_5_italian_interface">İtalyanca arayüz</string>
<string name="choose_file_title">Dosya seç</string>
<string name="scan_QR_code_to_connect_to_contact_who_shows_QR_code"><![CDATA[<b>QR kodunu tara</b>: size QR kodunu gösteren kişiyle bağlantı kurmak için.]]></string>
<string name="scan_QR_code_to_connect_to_contact_who_shows_QR_code"><b>QR kodunu tara</b>: size QR kodunu gösteren kişiyle bağlantı kurmak için.</string>
<string name="invite_friends">Arkadaşlarınızı davet edin</string>
<string name="bold_text">kalın</string>
<string name="italic_text">İtalik</string>
@@ -748,7 +748,7 @@
<string name="status_contact_has_e2e_encryption">Kişi uçtan uca şifrelemeye sahiptir</string>
<string name="status_contact_has_no_e2e_encryption">Kişi uçtan uca şifrelemeye sahip değildir</string>
<string name="chat_is_stopped">Sohbet durduruldu</string>
<string name="impossible_to_recover_passphrase"><![CDATA[<b>Aklınızda bulunsun</b>: kaybederseniz, parolayı kurtaramaz veya değiştiremezsiniz.]]></string>
<string name="impossible_to_recover_passphrase"><b>Aklınızda bulunsun</b>: kaybederseniz, parolayı kurtaramaz veya değiştiremezsiniz.</string>
<string name="chat_archive_header">Sohbet arşivi</string>
<string name="chat_archive_section">SOHBET ARŞİVİ</string>
<string name="group_invitation_item_description">1$s grubuna davet</string>
@@ -767,7 +767,7 @@
<string name="callstatus_connecting">Aramaya bağlanılıyor…</string>
<string name="delivery_receipts_are_disabled">Gönderildi bilgisi kapalı!</string>
<string name="v5_2_more_things">Birkaç şey daha</string>
<string name="onboarding_notifications_mode_service_desc"><![CDATA[<b>Daha fazla pil kullanır</b>! Arka plan hizmeti her zaman çalışır - mesajlar gelir gelmez bildirim gönderilir.]]></string>
<string name="onboarding_notifications_mode_service_desc"><b>Daha fazla pil kullanır</b>! Arka plan hizmeti her zaman çalışır - mesajlar gelir gelmez bildirim gönderilir.</string>
<string name="in_developing_title">Çok yakında!</string>
<string name="delete_contact_all_messages_deleted_cannot_undo_warning">Kişi ve tüm mesajlar silinecektir - bu geri alınamaz!</string>
<string name="alert_title_contact_connection_pending">Kişi henüz bağlanmadı!</string>
@@ -783,7 +783,7 @@
<string name="description_via_contact_address_link_incognito">Bağlantı linki ile gizli</string>
<string name="description_via_one_time_link_incognito">Tek seferlik bağlantı ile gizli</string>
<string name="smp_server_test_compare_file">Dosyaları karşılaştır</string>
<string name="it_can_disabled_via_settings_notifications_still_shown"><![CDATA[<b>ayarlardan devre dışı bırakılabilir</b> - uygulama çalışıyorken bildirimler gösterilmeye devam edilecektir.]]></string>
<string name="it_can_disabled_via_settings_notifications_still_shown"><b>ayarlardan devre dışı bırakılabilir</b> - uygulama çalışıyorken bildirimler gösterilmeye devam edilecektir.</string>
<string name="turning_off_service_and_periodic">Pil optimizasyonu etkin, arka plan hizmeti kapatılacak ve düzenli olarak yeni mesajlar kontrol edilmeyecek . Bunları ayarlardan yeniden etkinleştirebilirsiniz.</string>
<string name="enter_passphrase_notification_title">Parola gerekli</string>
<string name="notification_preview_mode_message">Mesaj metni</string>
@@ -794,7 +794,7 @@
<string name="delete_message_cannot_be_undone_warning">Mesajlar silinecek - bu geri alınamaz!</string>
<string name="switch_receiving_address_question">Alıcı adresini değiştir\?</string>
<string name="back">Geri</string>
<string name="add_new_contact_to_create_one_time_QR_code"><![CDATA[<b>Yeni kişi ekle</b>: Kişiniz için tek seferlik QR Kodunuzu oluşturmak için.]]></string>
<string name="add_new_contact_to_create_one_time_QR_code"><b>Yeni kişi ekle</b>: Kişiniz için tek seferlik QR Kodunuzu oluşturmak için.</string>
<string name="you_will_be_connected_when_your_connection_request_is_accepted">Bağlantı isteğiniz kabul edildiğinde bağlanacaksınız, lütfen bekleyin veya daha sonra kontrol edin!</string>
<string name="you_will_be_connected_when_your_contacts_device_is_online">Kişinizin cihazı çevrimiçi olduğunda bağlanacaksınız, lütfen bekleyin veya daha sonra kontrol edin!</string>
<string name="learn_more">Daha fazla bilgi edinin</string>
@@ -836,10 +836,10 @@
<string name="mark_read">Okundu olarak işaretle</string>
<string name="mark_unread">Okunmadı olarak işaretle</string>
<string name="chat_console">Sohbet konsolu</string>
<string name="onboarding_notifications_mode_periodic_desc"><![CDATA[<b>Pil için iyi</b>. Arka plan hizmeti mesajları 10 dakikada bir kontrol eder. Aramaları veya acil mesajları kaçırabilirsiniz.]]></string>
<string name="onboarding_notifications_mode_periodic_desc"><b>Pil için iyi</b>. Arka plan hizmeti mesajları 10 dakikada bir kontrol eder. Aramaları veya acil mesajları kaçırabilirsiniz.</string>
<string name="v4_4_verify_connection_security_desc">Güvenlik kodlarını kişilerinizle karşılaştırın.</string>
<string name="notifications_mode_service_desc">Arka plan hizmeti her zaman çalışır - mesajlar gelir gelmez bildirim gönderilir.</string>
<string name="onboarding_notifications_mode_off_desc"><![CDATA[<b>Pil için en iyisi</b>. Sadece uygulama çalışırken bildirim alırsınız (arka plan hizmeti YOK).]]></string>
<string name="onboarding_notifications_mode_off_desc"><b>Pil için en iyisi</b>. Sadece uygulama çalışırken bildirim alırsınız (arka plan hizmeti YOK).</string>
<string name="snd_conn_event_switch_queue_phase_changing_for_member">%s için adres değiştiriliyor.</string>
<string name="large_file">Büyük dosya!</string>
<string name="you_will_be_connected_when_group_host_device_is_online">Grup sahibinin cihazı çevrimiçi olduğunda gruba bağlanacaksınız, lütfen bekleyin veya daha sonra kontrol edin!</string>
@@ -869,7 +869,7 @@
<string name="your_ICE_servers">ICE sunucularınız</string>
<string name="how_to">Nasıl</string>
<string name="your_current_profile">Mevcut profiliniz</string>
<string name="you_control_servers_to_receive_your_contacts_to_send"><![CDATA[Mesajların hangi sunucu(lar)dan <b>alınacağını</b> siz kontrol edersiniz, kişileriniz - onlara mesaj göndermek için kullandığınız sunucular.]]></string>
<string name="you_control_servers_to_receive_your_contacts_to_send">Mesajların hangi sunucu(lar)dan <b>alınacağını</b> siz kontrol edersiniz, kişileriniz - onlara mesaj göndermek için kullandığınız sunucular.</string>
<string name="video_call_no_encryption">video arama (uçtan uca şifreli değil)</string>
<string name="your_ice_servers">ICE sunucularınız</string>
<string name="icon_descr_video_off">Video kapalı</string>
@@ -908,8 +908,8 @@
\nBu bağlantıyı iptal edebilir ve kişiyi kaldırabilirsiniz (ve daha sonra yeni bir bağlantıyla deneyebilirsiniz).</string>
<string name="contact_sent_large_file">Kişiniz desteklenen maksimum boyuttan (%1$s) daha büyük bir dosya gönderdi.</string>
<string name="video_will_be_received_when_contact_completes_uploading">Kişiniz yüklemeyi tamamladığında video alınacaktır.</string>
<string name="you_can_also_connect_by_clicking_the_link"><![CDATA[Ayrıca bağlantıya tıklayarak da bağlanabilirsiniz. Eğer bağlantı tarayıcda açılırsa, <b>mobil uygulamada aç</b> seçeneğine tıklayın.]]></string>
<string name="you_can_connect_to_simplex_chat_founder"><![CDATA[Soru sormak ve güncellemeleri almak için <font color=#0088ff>SimpleX Chat geliştiricilerine bağlanabilirsiniz</font>.]]></string>
<string name="you_can_also_connect_by_clicking_the_link">Ayrıca bağlantıya tıklayarak da bağlanabilirsiniz. Eğer bağlantı tarayıcda açılırsa, <b>mobil uygulamada aç</b> seçeneğine tıklayın.</string>
<string name="you_can_connect_to_simplex_chat_founder">Soru sormak ve güncellemeleri almak için &lt;font color=#0088ff&gt;SimpleX Chat geliştiricilerine bağlanabilirsiniz&lt;/font&gt;.</string>
<string name="you_can_hide_or_mute_user_profile">Bir kullanıcının profilini gizleyebilir veya sessize alabilirsiniz - menü için basılı tutun.</string>
<string name="you_invited_your_contact">Kişinizi davet ettiniz</string>
<string name="you_must_use_the_most_recent_version_of_database">Sohbet veritabanınızın en son sürümünü SADECE bir cihazda kullanmalısınız, aksi takdirde bazı kişilerden daha fazla mesaj alamayabilirsiniz.</string>
@@ -957,7 +957,7 @@
\n- ve daha fazlası!</string>
<string name="item_status_rcv_new_desc">Bu göndericiden yeni bir mesaj var.</string>
<string name="item_status_snd_error_auth_desc">Mesaj teslim hatası. Büyük olasılıkla bu alıcı sizinle olan bağlantısını silmiştir.</string>
<string name="only_client_devices_store_contacts_groups_e2e_encrypted_messages"><![CDATA[Sadece istemci cihazlar <b>2 katmanlı uçtan uca şifreleme</b> ile kullanıcı profillerini, kişileri, grupları ve gönderilen mesajları depolar.]]></string>
<string name="only_client_devices_store_contacts_groups_e2e_encrypted_messages">Sadece istemci cihazlar <b>2 katmanlı uçtan uca şifreleme</b> ile kullanıcı profillerini, kişileri, grupları ve gönderilen mesajları depolar.</string>
<string name="only_group_owners_can_change_prefs">Grup tercihlerini sadece grup sahipleri değiştirebilir.</string>
<string name="item_info_no_text">metin yok</string>
<string name="network_status">Ağ durumu</string>
@@ -966,7 +966,7 @@
<string name="change_lock_mode">Kilit modunu değiştir</string>
<string name="trying_to_connect_to_server_to_receive_messages">Bu kişiden mesaj almak için kullanılan sunucuya bağlanılmaya çalışılıyor.</string>
<string name="please_check_correct_link_and_maybe_ask_for_a_new_one">Lütfen doğru bağlantıyı kullandığınızı kontrol edin veya irtibat kişinizden size başka bir bağlantı göndermesini isteyin.</string>
<string name="to_preserve_privacy_simplex_has_background_service_instead_of_push_notifications_it_uses_a_few_pc_battery"><![CDATA[Gizliliğinizi korumak için, anlık bildirimler yerine <b>SimpleX arka plan hizmeti</b> kullanılır - günde pilin yüzde birkaçını kullanır.]]></string>
<string name="to_preserve_privacy_simplex_has_background_service_instead_of_push_notifications_it_uses_a_few_pc_battery">Gizliliğinizi korumak için, anlık bildirimler yerine <b>SimpleX arka plan hizmeti</b> kullanılır - günde pilin yüzde birkaçını kullanır.</string>
<string name="periodic_notifications">Periyodik bildirimler</string>
<string name="periodic_notifications_disabled">Periyodik bildirimler devre dışı</string>
<string name="enter_passphrase_notification_desc">Bildirimleri almak için lütfen veri tabanı parolasını girin</string>
@@ -988,7 +988,7 @@
<string name="this_link_is_not_a_valid_connection_link">Bu geçerli bir bağlantı linki değil</string>
<string name="this_QR_code_is_not_a_link">Bu QR kodu bir bağlantı değil!</string>
<string name="paste_connection_link_below_to_connect">Kişinizle bağlantı kurmak için aldığınız bağlantıyı aşağıdaki kutuya yapıştırın.</string>
<string name="read_more_in_user_guide_with_link"><![CDATA[Daha fazla bilgi için <font color=#0088ff>Kullanıcı Kılavuzu</font>.]]></string>
<string name="read_more_in_user_guide_with_link">Daha fazla bilgi için &lt;font color=#0088ff&gt;Kullanıcı Kılavuzu&lt;/font&gt;.</string>
<string name="paste_button">Yapıştır</string>
<string name="this_string_is_not_a_connection_link">Bu dize bir bağlantı linki değil!</string>
<string name="rate_the_app">Uygulamaya puan verin</string>
@@ -1033,7 +1033,7 @@
<string name="unfavorite_chat">Favorilerden çıkar</string>
<string name="make_profile_private">Sohbeti gizli yap!</string>
<string name="profile_update_will_be_sent_to_contacts">Profil güncellemesi kişilerinize gönderilecektir.</string>
<string name="read_more_in_github_with_link"><![CDATA[<font color=#0088ff>GitHub repomuzda</font> daha fazlasını okuyun.]]></string>
<string name="read_more_in_github_with_link">&lt;font color=#0088ff&gt;GitHub repomuzda&lt;/font&gt; daha fazlasını okuyun.</string>
<string name="alert_text_fragment_please_report_to_developers">Lütfen geliştiricilere bildirin.</string>
<string name="users_delete_with_connections">Profil ve sunucu bağlantıları</string>
<string name="user_unhide">gizlemeyi kaldır</string>
@@ -74,7 +74,7 @@
<string name="notifications_mode_off_desc">Додаток може отримувати сповіщення лише під час роботи, жодні фонові служби не запускаються</string>
<string name="chat_preferences_always">завжди</string>
<string name="allow_your_contacts_to_call">Дозвольте вашим контактам телефонувати вам.</string>
<string name="network_session_mode_user_description"><![CDATA[<b>Для кожного профілю чату, який ви маєте в додатку</b>, буде використовуватися окреме TCP-з\'єднання (і SOCKS-обліковий запис).]]></string>
<string name="network_session_mode_user_description"><![CDATA[Для кожного профілю чату, який ви маєте в додатку</b>, буде використовуватися окреме TCP-з\'єднання (і SOCKS-обліковий запис) <b>.]]></string>
<string name="appearance_settings">Зовнішній вигляд</string>
<string name="app_version_name">Версія програми: v%s</string>
<string name="network_session_mode_entity_description"><b>Для кожного контакту і члена групи</b> буде використовуватися окреме TCP-з\'єднання (і SOCKS-обліковий запис).
@@ -14,7 +14,6 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.*
import chat.simplex.common.model.ChatController
import chat.simplex.common.model.ChatModel
import chat.simplex.common.platform.defaultLocale
import chat.simplex.common.platform.desktopPlatform
import chat.simplex.common.ui.theme.SimpleXTheme
import chat.simplex.common.views.helpers.FileDialogChooser
@@ -26,96 +25,89 @@ import java.io.File
val simplexWindowState = SimplexWindowState()
fun showApp() = application {
// TODO: remove after update to compose 1.5.0+
// See: https://github.com/JetBrains/compose-multiplatform/issues/3366#issuecomment-1643799976
System.setProperty("compose.scrolling.smooth.enabled", "false")
// For some reason on Linux actual width will be 10.dp less after specifying it here. If we specify 1366,
// it will show 1356. But after that we can still update it to 1366 by changing window state. Just making it +10 now here
val width = if (desktopPlatform.isLinux()) 1376.dp else 1366.dp
val windowState = rememberWindowState(placement = WindowPlacement.Floating, width = width, height = 768.dp)
simplexWindowState.windowState = windowState
// Reload all strings in all @Composable's after language change at runtime
if (remember { ChatController.appPrefs.appLanguage.state }.value != "") {
Window(state = windowState, onCloseRequest = ::exitApplication, onKeyEvent = {
if (it.key == Key.Escape && it.type == KeyEventType.KeyUp) {
simplexWindowState.backstack.lastOrNull()?.invoke() != null
} else {
false
Window(state = windowState, onCloseRequest = ::exitApplication, onKeyEvent = {
if (it.key == Key.Escape && it.type == KeyEventType.KeyUp) {
simplexWindowState.backstack.lastOrNull()?.invoke() != null
} else {
false
}
}, title = "SimpleX") {
SimpleXTheme {
AppScreen()
if (simplexWindowState.openDialog.isAwaiting) {
FileDialogChooser(
title = "SimpleX",
isLoad = true,
params = simplexWindowState.openDialog.params,
onResult = {
simplexWindowState.openDialog.onResult(it.firstOrNull())
}
)
}
}, title = "SimpleX") {
SimpleXTheme {
AppScreen()
if (simplexWindowState.openDialog.isAwaiting) {
FileDialogChooser(
title = "SimpleX",
isLoad = true,
params = simplexWindowState.openDialog.params,
onResult = {
simplexWindowState.openDialog.onResult(it.firstOrNull())
}
)
}
if (simplexWindowState.openMultipleDialog.isAwaiting) {
FileDialogChooser(
title = "SimpleX",
isLoad = true,
params = simplexWindowState.openMultipleDialog.params,
onResult = {
simplexWindowState.openMultipleDialog.onResult(it)
}
)
}
if (simplexWindowState.openMultipleDialog.isAwaiting) {
FileDialogChooser(
title = "SimpleX",
isLoad = true,
params = simplexWindowState.openMultipleDialog.params,
onResult = {
simplexWindowState.openMultipleDialog.onResult(it)
}
)
}
if (simplexWindowState.saveDialog.isAwaiting) {
FileDialogChooser(
title = "SimpleX",
isLoad = false,
params = simplexWindowState.saveDialog.params,
onResult = { simplexWindowState.saveDialog.onResult(it.firstOrNull()) }
if (simplexWindowState.saveDialog.isAwaiting) {
FileDialogChooser(
title = "SimpleX",
isLoad = false,
params = simplexWindowState.saveDialog.params,
onResult = { simplexWindowState.saveDialog.onResult(it.firstOrNull()) }
)
}
val toasts = remember { simplexWindowState.toasts }
val toast = toasts.firstOrNull()
if (toast != null) {
Box(Modifier.fillMaxSize().padding(bottom = 20.dp), contentAlignment = Alignment.BottomCenter) {
Text(
toast.first,
Modifier.background(MaterialTheme.colors.primary, RoundedCornerShape(100)).padding(vertical = 5.dp, horizontal = 10.dp),
color = MaterialTheme.colors.onPrimary,
style = MaterialTheme.typography.body1
)
}
val toasts = remember { simplexWindowState.toasts }
val toast = toasts.firstOrNull()
if (toast != null) {
Box(Modifier.fillMaxSize().padding(bottom = 20.dp), contentAlignment = Alignment.BottomCenter) {
Text(
toast.first,
Modifier.background(MaterialTheme.colors.primary, RoundedCornerShape(100)).padding(vertical = 5.dp, horizontal = 10.dp),
color = MaterialTheme.colors.onPrimary,
style = MaterialTheme.typography.body1
)
}
// Shows toast in insertion order with preferred delay per toast. New one will be shown once previous one expires
LaunchedEffect(toast, toasts.size) {
delay(toast.second)
simplexWindowState.toasts.removeFirst()
}
// Shows toast in insertion order with preferred delay per toast. New one will be shown once previous one expires
LaunchedEffect(toast, toasts.size) {
delay(toast.second)
simplexWindowState.toasts.removeFirst()
}
}
var windowFocused by remember { simplexWindowState.windowFocused }
LaunchedEffect(windowFocused) {
val delay = ChatController.appPrefs.laLockDelay.get()
if (!windowFocused && ChatModel.performLA.value && delay > 0) {
delay(delay * 1000L)
// Trigger auth state check when delay ends (and if it ends)
}
var windowFocused by remember { simplexWindowState.windowFocused }
LaunchedEffect(windowFocused) {
val delay = ChatController.appPrefs.laLockDelay.get()
if (!windowFocused && ChatModel.performLA.value && delay > 0) {
delay(delay * 1000L)
// Trigger auth state check when delay ends (and if it ends)
AppLock.recheckAuthState()
}
}
LaunchedEffect(Unit) {
window.addWindowFocusListener(object : WindowFocusListener {
override fun windowGainedFocus(p0: WindowEvent?) {
windowFocused = true
AppLock.recheckAuthState()
}
}
LaunchedEffect(Unit) {
window.addWindowFocusListener(object: WindowFocusListener {
override fun windowGainedFocus(p0: WindowEvent?) {
windowFocused = true
AppLock.recheckAuthState()
}
override fun windowLostFocus(p0: WindowEvent?) {
windowFocused = false
AppLock.appWasHidden()
}
})
}
override fun windowLostFocus(p0: WindowEvent?) {
windowFocused = false
AppLock.appWasHidden()
}
})
}
}
}
+4 -4
View File
@@ -25,11 +25,11 @@ android.nonTransitiveRClass=true
android.enableJetifier=true
kotlin.mpp.androidSourceSetLayoutVersion=2
android.version_name=5.3-beta.4
android.version_code=146
android.version_name=5.3-beta.3
android.version_code=141
desktop.version_name=1.2.0
desktop.version_code=4
desktop.version_name=1.1.0
desktop.version_code=3
kotlin.version=1.8.20
gradle.plugin.version=7.4.2
@@ -19,7 +19,7 @@ import Control.Monad.Reader
import qualified Data.ByteString.Char8 as B
import Data.Time.Clock (getCurrentTime)
import Data.Time.LocalTime (getCurrentTimeZone)
import Data.Maybe (fromMaybe, maybeToList)
import Data.Maybe (fromMaybe, isJust, maybeToList)
import qualified Data.Set as S
import Data.Text (Text)
import qualified Data.Text as T
@@ -134,7 +134,7 @@ directoryService st DirectoryOpts {superUsers, serviceName, testing} user@User {
_ -> "Error joining group " <> displayName <> ", please re-send the invitation!"
deContactConnected :: Contact -> IO ()
deContactConnected ct = when (contactDirect ct) $ do
deContactConnected ct = unless (isJust $ viaGroup ct) $ do
unless testing $ putStrLn $ T.unpack (localDisplayName' ct) <> " connected"
sendMessage cc ct $
"Welcome to " <> serviceName <> " service!\n\
+1 -1
View File
@@ -7,7 +7,7 @@ constraints: zip +disable-bzip2 +disable-zstd
source-repository-package
type: git
location: https://github.com/simplex-chat/simplexmq.git
tag: e2065ab3525ca67e39700ee8040839d667f75ea2
tag: 82aec2cd8f7b4033dbf08d5de33ced216f574bbb
source-repository-package
type: git
+12 -102
View File
@@ -1,6 +1,6 @@
---
title: Hosting your own SMP Server
revision: 31.07.2023
revision: 05.06.2023
---
| Updated 05.06.2023 | Languages: EN, [FR](/docs/lang/fr/SERVER.md), [CZ](/docs/lang/cs/SERVER.md) |
@@ -19,27 +19,17 @@ _Please note_: when you change the servers in the app configuration, it only aff
0. First, install `smp-server`:
- Manual deployment (see below)
- Manual deployment:
- Semi-automatic deployment:
- [Offical installation script](https://github.com/simplex-chat/simplexmq#using-installation-script)
- [Docker container](https://github.com/simplex-chat/simplexmq#using-docker)
- [Compiling from source](https://github.com/simplex-chat/simplexmq#using-your-distribution)
- [Using pre-compiled binaries](https://github.com/simplex-chat/simplexmq#install-binaries)
- Alternatively, you can deploy `smp-server` using:
- [Docker container](https://github.com/simplex-chat/simplexmq#using-docker-1)
- [Linode StackScript](https://github.com/simplex-chat/simplexmq#deploy-smp-server-on-linode)
Manual installation requires some preliminary actions:
0. Install binary:
- Using offical binaries:
```sh
curl -L https://github.com/simplex-chat/simplexmq/releases/latest/download/smp-server-ubuntu-20_04-x86-64 -o /usr/local/bin/smp-server
```
- Compiling from source:
Please refer to [Build from source: Using your distribution](https://github.com/simplex-chat/simplexmq#using-your-distribution)
1. Create user and group for `smp-server`:
```sh
@@ -67,104 +57,24 @@ Manual installation requires some preliminary actions:
```sh
[Unit]
Description=SMP server systemd service
Description=SMP server
[Service]
User=smp
Group=smp
Type=simple
ExecStart=/usr/local/bin/smp-server start +RTS -N -RTS
ExecStart=smp-server start
ExecStopPost=/usr/bin/env sh -c '[ -e "/var/opt/simplex/smp-server-store.log" ] && cp "/var/opt/simplex/smp-server-store.log" "/var/opt/simplex/smp-server-store.log.bak"'
LimitNOFILE=65535
KillSignal=SIGINT
TimeoutStopSec=infinity
Restart=always
RestartSec=10
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
```
And execute `sudo systemctl daemon-reload`.
## Tor installation
smp-server can also be deployed to serve from [tor](https://www.torproject.org) network. Run the following commands as `root` user.
1. Install tor:
We're assuming you're using Ubuntu/Debian based distributions. If not, please refer to [offical tor documentation](https://community.torproject.org/onion-services/setup/install/) or your distribution guide.
- Configure offical Tor PPA repository:
```sh
CODENAME="$(lsb_release -c | awk '{print $2}')"
echo "deb [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] https://deb.torproject.org/torproject.org ${CODENAME} main
deb-src [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] https://deb.torproject.org/torproject.org ${CODENAME} main" > /etc/apt/sources.list.d/tor.list
```
- Import repository key:
```sh
curl --proto '=https' --tlsv1.2 -sSf https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | gpg --dearmor | tee /usr/share/keyrings/tor-archive-keyring.gpg >/dev/null
```
- Update repository index:
```sh
apt update
```
- Install `tor` package:
```sh
apt install -y tor deb.torproject.org-keyring
```
2. Configure tor:
- File configuration:
Open tor configuration with your editor of choice (`nano`,`vim`,`emacs`,etc.):
```sh
vim /etc/tor/torrc
```
And insert the following lines to the bottom of configuration. Please note lines starting with `#`: this is comments about each individual options.
```sh
# Enable log (otherwise, tor doesn't seemd to deploy onion address)
Log notice file /var/log/tor/notices.log
# Enable single hop routing (2 options below are dependencies of third). Will reduce latency in exchange of anonimity (since tor runs alongside smp-server and onion address will be displayed in clients, this is totally fine)
SOCKSPort 0
HiddenServiceNonAnonymousMode 1
HiddenServiceSingleHopMode 1
# smp-server hidden service host directory and port mappings
HiddenServiceDir /var/lib/tor/simplex-smp/
HiddenServicePort 5223 localhost:5223
```
- Create directories:
```sh
mkdir /var/lib/tor/simplex-smp/ && chown debian-tor:debian-tor /var/lib/tor/simplex-smp/ && chmod 700 /var/lib/tor/simplex-smp/
```
3. Start tor:
Enable `systemd` service and start tor. Offical `tor` is a bit flunky on the first start and may not create onion host address, so we're restarting it just in case.
```sh
systemctl enable tor && systemctl start tor && systemctl restart tor
```
4. Display onion host:
Execute the following command to display your onion host address:
```sh
cat /var/lib/tor/simplex-smp/hostname
```
## Configuration
To see which options are available, execute `smp-server` without flags:
+9 -106
View File
@@ -1,6 +1,6 @@
---
title: Hosting your own XFTP Server
revision: 31.07.2023
revision: 21.04.2023
---
# Hosting your own XFTP Server
@@ -17,43 +17,26 @@ XFTP is a new file transfer protocol focussed on meta-data protection - it is ba
## Installation
0. First, install `xftp-server`:
1. Download `xftp-server` binary:
- Manual deployment (see below)
```sh
sudo curl -L https://github.com/simplex-chat/simplexmq/releases/latest/download/xftp-server-ubuntu-20_04-x86-64 -o /usr/local/bin/xftp-server && sudo chmod +x /usr/local/bin/xftp-server
```
- Semi-automatic deployment:
- [Offical installation script](https://github.com/simplex-chat/simplexmq#using-installation-script)
- [Docker container](https://github.com/simplex-chat/simplexmq#using-docker)
Manual installation requires some preliminary actions:
0. Install binary:
- Using offical binaries:
```sh
curl -L https://github.com/simplex-chat/simplexmq/releases/latest/download/xftp-server-ubuntu-20_04-x86-64 -o /usr/local/bin/xftp-server
```
- Compiling from source:
Please refer to [Build from source: Using your distribution](https://github.com/simplex-chat/simplexmq#using-your-distribution)
1. Create user and group for `xftp-server`:
2. Create user and group for `xftp-server`:
```sh
sudo useradd -m xftp
```
2. Create necessary directories and assign permissions:
3. Create necessary directories and assign permissions:
```sh
sudo mkdir -p /var/opt/simplex-xftp /etc/opt/simplex-xftp /srv/xftp
sudo chown xftp:xftp /var/opt/simplex-xftp /etc/opt/simplex-xftp /srv/xftp
```
3. Allow xftp-server port in firewall:
4. Allow xftp-server port in firewall:
```sh
# For Ubuntu
@@ -63,7 +46,7 @@ Manual installation requires some preliminary actions:
sudo firewall-cmd --reload
```
4. **Optional** — If you're using distribution with `systemd`, create `/etc/systemd/system/xftp-server.service` file with the following content:
5. **Optional** — If you're using distribution with `systemd`, create `/etc/systemd/system/xftp-server.service` file with the following content:
```sh
[Unit]
@@ -86,86 +69,6 @@ Manual installation requires some preliminary actions:
And execute `sudo systemctl daemon-reload`.
## Tor installation
xftp-server can also be deployed to serve from [tor](https://www.torproject.org) network. Run the following commands as `root` user.
1. Install tor:
We're assuming you're using Ubuntu/Debian based distributions. If not, please refer to [offical tor documentation](https://community.torproject.org/onion-services/setup/install/) or your distribution guide.
- Configure offical Tor PPA repository:
```sh
CODENAME="$(lsb_release -c | awk '{print $2}')"
echo "deb [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] https://deb.torproject.org/torproject.org ${CODENAME} main
deb-src [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] https://deb.torproject.org/torproject.org ${CODENAME} main" > /etc/apt/sources.list.d/tor.list
```
- Import repository key:
```sh
curl --proto '=https' --tlsv1.2 -sSf https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | gpg --dearmor | tee /usr/share/keyrings/tor-archive-keyring.gpg >/dev/null
```
- Update repository index:
```sh
apt update
```
- Install `tor` package:
```sh
apt install -y tor deb.torproject.org-keyring
```
2. Configure tor:
- File configuration:
Open tor configuration with your editor of choice (`nano`,`vim`,`emacs`,etc.):
```sh
vim /etc/tor/torrc
```
And insert the following lines to the bottom of configuration. Please note lines starting with `#`: this is comments about each individual options.
```sh
# Enable log (otherwise, tor doesn't seemd to deploy onion address)
Log notice file /var/log/tor/notices.log
# Enable single hop routing (2 options below are dependencies of third). Will reduce latency in exchange of anonimity (since tor runs alongside xftp-server and onion address will be displayed in clients, this is totally fine)
SOCKSPort 0
HiddenServiceNonAnonymousMode 1
HiddenServiceSingleHopMode 1
# xftp-server hidden service host directory and port mappings
HiddenServiceDir /var/lib/tor/simplex-xftp/
HiddenServicePort 443 localhost:443
```
- Create directories:
```sh
mkdir /var/lib/tor/simplex-xftp/ && chown debian-tor:debian-tor /var/lib/tor/simplex-xftp/ && chmod 700 /var/lib/tor/simplex-xftp/
```
3. Start tor:
Enable `systemd` service and start tor. Offical `tor` is a bit flunky on the first start and may not create onion host address, so we're restarting it just in case.
```sh
systemctl enable tor && systemctl start tor && systemctl restart tor
```
4. Display onion host:
Execute the following command to display your onion host address:
```sh
cat /var/lib/tor/simplex-xftp/hostname
```
## Configuration
To see which options are available, execute `xftp-server` without flags:
+1 -1
View File
@@ -1,5 +1,5 @@
name: simplex-chat
version: 5.3.0.4
version: 5.3.0.2
#synopsis:
#description:
homepage: https://github.com/simplex-chat/simplex-chat#readme
+1 -1
View File
@@ -1,5 +1,5 @@
{
"https://github.com/simplex-chat/simplexmq.git"."e2065ab3525ca67e39700ee8040839d667f75ea2" = "13rz58bdpkhfrp1d58ylpbazhzs26q5fjg0sclxgvdqnnmac5cgz";
"https://github.com/simplex-chat/simplexmq.git"."82aec2cd8f7b4033dbf08d5de33ced216f574bbb" = "1x3rjq10d3c8qb6wf66a2j127xi9xdg21pyw5r4n124f8yvlb0nc";
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
"https://github.com/kazu-yamamoto/http2.git"."b5a1b7200cf5bc7044af34ba325284271f6dff25" = "0dqb50j57an64nf4qcf5vcz4xkd1vzvghvf8bk529c1k30r9nfzb";
"https://github.com/simplex-chat/direct-sqlcipher.git"."34309410eb2069b029b8fc1872deb1e0db123294" = "0kwkmhyfsn2lixdlgl15smgr1h5gjk7fky6abzh8rng2h5ymnffd";
+2 -2
View File
@@ -5,12 +5,12 @@ cabal-version: 1.12
-- see: https://github.com/sol/hpack
name: simplex-chat
version: 5.3.0.4
version: 5.3.0.2
category: Web, System, Services, Cryptography
homepage: https://github.com/simplex-chat/simplex-chat#readme
author: simplex.chat
maintainer: chat@simplex.chat
copyright: 2020-23 simplex.chat
copyright: 2020-22 simplex.chat
license: AGPL-3
license-file: LICENSE
build-type: Simple
+6 -15
View File
@@ -48,7 +48,7 @@ import Data.Time (NominalDiffTime, addUTCTime, defaultTimeLocale, formatTime)
import Data.Time.Clock (UTCTime, diffUTCTime, getCurrentTime, nominalDay, nominalDiffTimeToSeconds)
import Data.Time.Clock.System (SystemTime, systemToUTCTime)
import Data.Word (Word32)
import qualified Database.SQLite.Simple as SQL
import qualified Database.SQLite.Simple as DB
import Simplex.Chat.Archive
import Simplex.Chat.Call
import Simplex.Chat.Controller
@@ -74,12 +74,11 @@ import Simplex.FileTransfer.Client.Presets (defaultXFTPServers)
import Simplex.FileTransfer.Description (ValidFileDescription, gb, kb, mb)
import Simplex.FileTransfer.Protocol (FileParty (..), FilePartyI)
import Simplex.Messaging.Agent as Agent
import Simplex.Messaging.Agent.Client (AgentStatsKey (..), agentClientStore, temporaryAgentError)
import Simplex.Messaging.Agent.Client (AgentStatsKey (..), temporaryAgentError)
import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), InitialAgentServers (..), createAgentStore, defaultAgentConfig)
import Simplex.Messaging.Agent.Lock
import Simplex.Messaging.Agent.Protocol
import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), MigrationError, SQLiteStore (dbNew), execSQL, upMigration, withConnection)
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), MigrationError, SQLiteStore (dbNew), execSQL, upMigration)
import qualified Simplex.Messaging.Agent.Store.SQLite.Migrations as Migrations
import Simplex.Messaging.Client (defaultNetworkConfig)
import qualified Simplex.Messaging.Crypto as C
@@ -398,7 +397,7 @@ processChatCommand = \case
asks currentUser >>= atomically . (`writeTVar` Just user'')
pure $ CRActiveUser user''
SetActiveUser uName viewPwd_ -> do
tryChatError (withStore (`getUserIdByName` uName)) >>= \case
tryError (withStore (`getUserIdByName` uName)) >>= \case
Left _ -> throwChatError CEUserUnknown
Right userId -> processChatCommand $ APISetActiveUser userId viewPwd_
SetAllContactReceipts onOff -> withUser $ \_ -> withStore' (`updateAllContactReceipts` onOff) >> ok_
@@ -492,13 +491,6 @@ processChatCommand = \case
APIStorageEncryption cfg -> withStoreChanged $ sqlCipherExport cfg
ExecChatStoreSQL query -> CRSQLResult <$> withStore' (`execSQL` query)
ExecAgentStoreSQL query -> CRSQLResult <$> withAgent (`execAgentStoreSQL` query)
SlowSQLQueries -> do
ChatController {chatStore, smpAgent} <- ask
chatQueries <- slowQueries chatStore
agentQueries <- slowQueries $ agentClientStore smpAgent
pure CRSlowSQLQueries {chatQueries, agentQueries}
where
slowQueries st = liftIO $ map (uncurry SlowSQLQuery . first SQL.fromQuery) . sortOn snd . M.assocs <$> withConnection st (readTVarIO . DB.slow)
APIGetChats userId withPCC -> withUserId userId $ \user ->
CRApiChats user <$> withStoreCtx' (Just "APIGetChats, getChatPreviews") (\db -> getChatPreviews db user withPCC)
APIGetChat (ChatRef cType cId) pagination search -> withUser $ \user -> case cType of
@@ -1714,7 +1706,7 @@ processChatCommand = \case
QuitChat -> liftIO exitSuccess
ShowVersion -> do
let versionInfo = coreVersionInfo $(simplexmqCommitQ)
chatMigrations <- map upMigration <$> withStore' (Migrations.getCurrent . DB.conn)
chatMigrations <- map upMigration <$> withStore' Migrations.getCurrent
agentMigrations <- withAgent getAgentMigrations
pure $ CRVersionInfo {versionInfo, chatMigrations, agentMigrations}
DebugLocks -> do
@@ -1967,7 +1959,7 @@ processChatCommand = \case
drgRandomBytes n = asks idsDrg >>= liftIO . (`randomBytes` n)
privateGetUser :: UserId -> m User
privateGetUser userId =
tryChatError (withStore (`getUser` userId)) >>= \case
tryError (withStore (`getUser` userId)) >>= \case
Left _ -> throwChatError CEUserUnknown
Right user -> pure user
validateUserPassword :: User -> User -> Maybe UserPwd -> m ()
@@ -5048,7 +5040,6 @@ chatCommandP =
"/db decrypt " *> (APIStorageEncryption . (`DBEncryptionConfig` "") <$> dbKeyP),
"/sql chat " *> (ExecChatStoreSQL <$> textP),
"/sql agent " *> (ExecAgentStoreSQL <$> textP),
"/sql slow" $> SlowSQLQueries,
"/_get chats " *> (APIGetChats <$> A.decimal <*> (" pcc=on" $> True <|> " pcc=off" $> False <|> pure False)),
"/_get chat " *> (APIGetChat <$> chatRefP <* A.space <*> chatPaginationP <*> optional (" search=" *> stringP)),
"/_get items " *> (APIGetChatItems <$> chatPaginationP <*> optional (" search=" *> stringP)),
-10
View File
@@ -229,7 +229,6 @@ data ChatCommand
| APIStorageEncryption DBEncryptionConfig
| ExecChatStoreSQL Text
| ExecAgentStoreSQL Text
| SlowSQLQueries
| APIGetChats {userId :: UserId, pendingConnections :: Bool}
| APIGetChat ChatRef ChatPagination (Maybe String)
| APIGetChatItems ChatPagination (Maybe String)
@@ -564,7 +563,6 @@ data ChatResponse
| CRNewContactConnection {user :: User, connection :: PendingContactConnection}
| CRContactConnectionDeleted {user :: User, connection :: PendingContactConnection}
| CRSQLResult {rows :: [Text]}
| CRSlowSQLQueries {chatQueries :: [SlowSQLQuery], agentQueries :: [SlowSQLQuery]}
| CRDebugLocks {chatLockName :: Maybe String, agentLocks :: AgentLocks}
| CRAgentStats {agentStats :: [[String]]}
| CRConnectionDisabled {connectionEntity :: ConnectionEntity}
@@ -803,14 +801,6 @@ data SendFileMode
| SendFileXFTP
deriving (Show, Generic)
data SlowSQLQuery = SlowSQLQuery
{ query :: Text,
duration :: Int64
}
deriving (Show, Generic)
instance ToJSON SlowSQLQuery where toEncoding = J.genericToEncoding J.defaultOptions
data ChatError
= ChatError {errorType :: ChatErrorType}
| ChatErrorAgent {agentError :: AgentErrorType, connectionEntity_ :: Maybe ConnectionEntity}
-8
View File
@@ -30,7 +30,6 @@ import Foreign.C.Types (CInt (..))
import Foreign.Ptr
import Foreign.StablePtr
import Foreign.Storable (poke)
import GHC.IO.Encoding (setLocaleEncoding, setFileSystemEncoding, setForeignEncoding)
import GHC.Generics (Generic)
import Simplex.Chat
import Simplex.Chat.Controller
@@ -48,7 +47,6 @@ import Simplex.Messaging.Encoding.String
import Simplex.Messaging.Parsers (dropPrefix, sumTypeJSON)
import Simplex.Messaging.Protocol (AProtoServerWithAuth (..), AProtocolType (..), BasicAuth (..), CorrId (..), ProtoServerWithAuth (..), ProtocolServer (..))
import Simplex.Messaging.Util (catchAll, liftEitherWith, safeDecodeUtf8)
import System.IO (utf8)
import System.Timeout (timeout)
foreign export ccall "chat_migrate_init" cChatMigrateInit :: CString -> CString -> CString -> Ptr (StablePtr ChatController) -> IO CJSONString
@@ -72,12 +70,6 @@ foreign export ccall "chat_decrypt_media" cChatDecryptMedia :: CString -> Ptr Wo
-- | check / migrate database and initialize chat controller on success
cChatMigrateInit :: CString -> CString -> CString -> Ptr (StablePtr ChatController) -> IO CJSONString
cChatMigrateInit fp key conf ctrl = do
-- ensure we are set to UTF-8; iOS does not have locale, and will default to
-- US-ASCII all the time.
setLocaleEncoding utf8
setFileSystemEncoding utf8
setForeignEncoding utf8
dbPath <- peekCAString fp
dbKey <- peekCAString key
confirm <- peekCAString conf
+1 -1
View File
@@ -16,6 +16,7 @@ import Data.Maybe (fromMaybe)
import Data.Text (Text)
import Data.Time.Clock (UTCTime (..))
import Database.SQLite.Simple ((:.) (..))
import qualified Database.SQLite.Simple as DB
import Database.SQLite.Simple.QQ (sql)
import Simplex.Chat.Store.Files
import Simplex.Chat.Store.Groups
@@ -24,7 +25,6 @@ import Simplex.Chat.Protocol
import Simplex.Chat.Types
import Simplex.Chat.Types.Preferences
import Simplex.Messaging.Agent.Store.SQLite (firstRow, firstRow')
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
getConnectionEntity :: DB.Connection -> User -> AgentConnId -> ExceptT StoreError IO ConnectionEntity
getConnectionEntity db user@User {userId, userContactId} agentConnId = do
+1 -1
View File
@@ -68,13 +68,13 @@ import Data.Maybe (fromMaybe, isJust, isNothing)
import Data.Text (Text)
import Data.Time.Clock (UTCTime (..), getCurrentTime)
import Database.SQLite.Simple (NamedParam (..), Only (..), (:.) (..))
import qualified Database.SQLite.Simple as DB
import Database.SQLite.Simple.QQ (sql)
import Simplex.Chat.Store.Shared
import Simplex.Chat.Types
import Simplex.Chat.Types.Preferences
import Simplex.Messaging.Agent.Protocol (ConnId, InvitationId, UserId)
import Simplex.Messaging.Agent.Store.SQLite (firstRow, maybeFirstRow)
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
getPendingContactConnection :: DB.Connection -> UserId -> Int64 -> ExceptT StoreError IO PendingContactConnection
getPendingContactConnection db userId connId = do
+1 -1
View File
@@ -83,6 +83,7 @@ import Data.Time (addUTCTime)
import Data.Time.Clock (UTCTime (..), getCurrentTime, nominalDay)
import Data.Type.Equality
import Database.SQLite.Simple (Only (..), (:.) (..))
import qualified Database.SQLite.Simple as DB
import Database.SQLite.Simple.QQ (sql)
import Simplex.Chat.Store.Direct
import Simplex.Chat.Store.Messages
@@ -95,7 +96,6 @@ import Simplex.Chat.Types
import Simplex.Chat.Util (week)
import Simplex.Messaging.Agent.Protocol (AgentMsgId, ConnId, UserId)
import Simplex.Messaging.Agent.Store.SQLite (firstRow, maybeFirstRow)
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
getLiveSndFileTransfers :: DB.Connection -> User -> IO [SndFileTransfer]
getLiveSndFileTransfers db User {userId} = do
+14 -14
View File
@@ -94,6 +94,7 @@ import Data.Maybe (fromMaybe, isNothing)
import Data.Text (Text)
import Data.Time.Clock (UTCTime (..), getCurrentTime)
import Database.SQLite.Simple (NamedParam (..), Only (..), Query (..), (:.) (..))
import qualified Database.SQLite.Simple as DB
import Database.SQLite.Simple.QQ (sql)
import Simplex.Chat.Messages
import Simplex.Chat.Store.Direct
@@ -102,7 +103,6 @@ import Simplex.Chat.Types
import Simplex.Chat.Types.Preferences
import Simplex.Messaging.Agent.Protocol (ConnId, UserId)
import Simplex.Messaging.Agent.Store.SQLite (firstRow, maybeFirstRow)
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
import qualified Simplex.Messaging.Crypto as C
import Simplex.Messaging.Util (eitherToMaybe)
import UnliftIO.STM
@@ -242,11 +242,11 @@ getGroupAndMember db User {userId, userContactId} groupMemberId =
LEFT JOIN connections c ON c.connection_id = (
SELECT max(cc.connection_id)
FROM connections cc
where cc.user_id = ? AND cc.group_member_id = m.group_member_id
where cc.group_member_id = m.group_member_id
)
WHERE m.group_member_id = ? AND g.user_id = ? AND mu.contact_id = ?
|]
(userId, groupMemberId, userId, userContactId)
(groupMemberId, userId, userContactId)
where
toGroupAndMember :: (GroupInfoRow :. GroupMemberRow :. MaybeConnectionRow) -> (GroupInfo, GroupMember)
toGroupAndMember (groupInfoRow :. memberRow :. connRow) =
@@ -530,7 +530,7 @@ groupMemberQuery =
LEFT JOIN connections c ON c.connection_id = (
SELECT max(cc.connection_id)
FROM connections cc
where cc.user_id = ? AND cc.group_member_id = m.group_member_id
where cc.group_member_id = m.group_member_id
)
|]
@@ -540,7 +540,7 @@ getGroupMember db user@User {userId} groupId groupMemberId =
DB.query
db
(groupMemberQuery <> " WHERE m.group_id = ? AND m.group_member_id = ? AND m.user_id = ?")
(userId, groupId, groupMemberId, userId)
(groupId, groupMemberId, userId)
getGroupMemberById :: DB.Connection -> User -> GroupMemberId -> ExceptT StoreError IO GroupMember
getGroupMemberById db user@User {userId} groupMemberId =
@@ -548,7 +548,7 @@ getGroupMemberById db user@User {userId} groupMemberId =
DB.query
db
(groupMemberQuery <> " WHERE m.group_member_id = ? AND m.user_id = ?")
(userId, groupMemberId, userId)
(groupMemberId, userId)
getGroupMembers :: DB.Connection -> User -> GroupInfo -> IO [GroupMember]
getGroupMembers db user@User {userId, userContactId} GroupInfo {groupId} = do
@@ -556,7 +556,7 @@ getGroupMembers db user@User {userId, userContactId} GroupInfo {groupId} = do
<$> DB.query
db
(groupMemberQuery <> " WHERE m.group_id = ? AND m.user_id = ? AND (m.contact_id IS NULL OR m.contact_id != ?)")
(userId, groupId, userId, userContactId)
(groupId, userId, userContactId)
getGroupMembersForExpiration :: DB.Connection -> User -> GroupInfo -> IO [GroupMember]
getGroupMembersForExpiration db user@User {userId, userContactId} GroupInfo {groupId} = do
@@ -572,7 +572,7 @@ getGroupMembersForExpiration db user@User {userId, userContactId} GroupInfo {gro
)
|]
)
(userId, groupId, userId, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted)
(groupId, userId, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted)
toContactMember :: User -> (GroupMemberRow :. MaybeConnectionRow) -> GroupMember
toContactMember User {userContactId} (memberRow :. connRow) =
@@ -998,11 +998,11 @@ getViaGroupMember db User {userId, userContactId} Contact {contactId} =
LEFT JOIN connections c ON c.connection_id = (
SELECT max(cc.connection_id)
FROM connections cc
where cc.user_id = ? AND cc.group_member_id = m.group_member_id
where cc.group_member_id = m.group_member_id
)
WHERE ct.user_id = ? AND ct.contact_id = ? AND mu.contact_id = ? AND ct.deleted = 0
|]
(userId, userId, contactId, userContactId)
(userId, contactId, userContactId)
where
toGroupAndMember :: (GroupInfoRow :. GroupMemberRow :. MaybeConnectionRow) -> (GroupInfo, GroupMember)
toGroupAndMember (groupInfoRow :. memberRow :. connRow) =
@@ -1296,11 +1296,11 @@ getXGrpMemIntroContDirect db User {userId} Contact {contactId} = do
LEFT JOIN connections ch ON ch.connection_id = (
SELECT max(cc.connection_id)
FROM connections cc
where cc.user_id = ? AND cc.group_member_id = mh.group_member_id
where cc.group_member_id = mh.group_member_id
)
WHERE ct.user_id = ? AND ct.contact_id = ? AND ct.deleted = 0 AND mh.member_category = ?
|]
(userId, userId, contactId, GCHostMember)
(userId, contactId, GCHostMember)
where
toCont :: (Int64, GroupId, GroupMemberId, MemberId, Maybe ConnReqInvitation) -> Maybe (Int64, XGrpMemIntroCont)
toCont (hostConnId, groupId, groupMemberId, memberId, connReq_) = case connReq_ of
@@ -1326,11 +1326,11 @@ getXGrpMemIntroContGroup db User {userId} GroupMember {groupMemberId} = do
LEFT JOIN connections ch ON ch.connection_id = (
SELECT max(cc.connection_id)
FROM connections cc
where cc.user_id = ? AND cc.group_member_id = mh.group_member_id
where cc.group_member_id = mh.group_member_id
)
WHERE m.user_id = ? AND m.group_member_id = ? AND mh.member_category = ? AND ct.deleted = 0
|]
(userId, userId, groupMemberId, GCHostMember)
(userId, groupMemberId, GCHostMember)
where
toCont :: (Int64, Maybe ConnReqInvitation) -> Maybe (Int64, ConnReqInvitation)
toCont (hostConnId, connReq_) = case connReq_ of
+1 -1
View File
@@ -110,6 +110,7 @@ import Data.Text (Text)
import Data.Time (addUTCTime)
import Data.Time.Clock (UTCTime (..), getCurrentTime)
import Database.SQLite.Simple (NamedParam (..), Only (..), (:.) (..))
import qualified Database.SQLite.Simple as DB
import Database.SQLite.Simple.QQ (sql)
import Simplex.Chat.Markdown
import Simplex.Chat.Messages
@@ -121,7 +122,6 @@ import Simplex.Chat.Store.Shared
import Simplex.Chat.Types
import Simplex.Messaging.Agent.Protocol (AgentMsgId, ConnId, MsgMeta (..), UserId)
import Simplex.Messaging.Agent.Store.SQLite (firstRow, firstRow', maybeFirstRow)
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
import Simplex.Messaging.Util (eitherToMaybe)
import UnliftIO.STM
+1 -1
View File
@@ -66,6 +66,7 @@ import Data.Text (Text)
import Data.Text.Encoding (decodeLatin1, encodeUtf8)
import Data.Time.Clock (UTCTime (..), getCurrentTime)
import Database.SQLite.Simple (NamedParam (..), Only (..), (:.) (..))
import qualified Database.SQLite.Simple as DB
import Database.SQLite.Simple.QQ (sql)
import GHC.Generics (Generic)
import Simplex.Chat.Call
@@ -77,7 +78,6 @@ import Simplex.Chat.Types
import Simplex.Chat.Types.Preferences
import Simplex.Messaging.Agent.Protocol (ACorrId, ConnId, UserId)
import Simplex.Messaging.Agent.Store.SQLite (firstRow, maybeFirstRow)
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
import qualified Simplex.Messaging.Crypto as C
import Simplex.Messaging.Encoding.String
import Simplex.Messaging.Protocol (BasicAuth (..), ProtoServerWithAuth (..), ProtocolServer (..), ProtocolTypeI (..))
+4 -5
View File
@@ -25,7 +25,7 @@ import Data.Text (Text)
import qualified Data.Text as T
import Data.Time.Clock (UTCTime (..), getCurrentTime)
import Database.SQLite.Simple (NamedParam (..), Only (..), Query, SQLError, (:.) (..))
import qualified Database.SQLite.Simple as SQL
import qualified Database.SQLite.Simple as DB
import Database.SQLite.Simple.QQ (sql)
import GHC.Generics (Generic)
import Simplex.Chat.Messages
@@ -34,7 +34,6 @@ import Simplex.Chat.Types
import Simplex.Chat.Types.Preferences
import Simplex.Messaging.Agent.Protocol (AgentMsgId, ConnId, UserId)
import Simplex.Messaging.Agent.Store.SQLite (firstRow, maybeFirstRow)
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
import Simplex.Messaging.Parsers (dropPrefix, sumTypeJSON)
import Simplex.Messaging.Util (allFinally)
import UnliftIO.STM
@@ -108,7 +107,7 @@ checkConstraint err action = ExceptT $ runExceptT action `E.catch` (pure . Left
handleSQLError :: StoreError -> SQLError -> StoreError
handleSQLError err e
| SQL.sqlError e == SQL.ErrorConstraint = err
| DB.sqlError e == DB.ErrorConstraint = err
| otherwise = SEInternalError $ show e
storeFinally :: ExceptT StoreError IO a -> ExceptT StoreError IO b -> ExceptT StoreError IO a
@@ -310,7 +309,7 @@ withLocalDisplayName db userId displayName action = getLdnSuffix >>= (`tryCreate
E.try (insertName ldn currentTs) >>= \case
Right () -> action ldn
Left e
| SQL.sqlError e == SQL.ErrorConstraint -> tryCreateName (ldnSuffix + 1) (attempts - 1)
| DB.sqlError e == DB.ErrorConstraint -> tryCreateName (ldnSuffix + 1) (attempts - 1)
| otherwise -> E.throwIO e
where
insertName ldn ts =
@@ -336,7 +335,7 @@ createWithRandomBytes size gVar create = tryCreate 3
liftIO (E.try $ create id') >>= \case
Right x -> pure x
Left e
| SQL.sqlError e == SQL.ErrorConstraint -> tryCreate (n - 1)
| DB.sqlError e == DB.ErrorConstraint -> tryCreate (n - 1)
| otherwise -> throwError . SEInternalError $ show e
encodedRandomBytes :: TVar ChaChaDRG -> Int -> IO ByteString
+2 -3
View File
@@ -25,7 +25,7 @@ import Data.Text (Text)
import qualified Data.Text as T
import Data.Text.Encoding (encodeUtf8)
import Database.SQLite.Simple (Only (..))
import qualified Database.SQLite.Simple as SQL
import qualified Database.SQLite.Simple as DB
import Database.SQLite.Simple.QQ (sql)
import GHC.Weak (deRefWeak)
import Simplex.Chat
@@ -36,7 +36,6 @@ import Simplex.Chat.Styled
import Simplex.Chat.Terminal.Output
import Simplex.Chat.Types (User (..))
import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore, withTransaction)
import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB
import Simplex.Messaging.Util (catchAll_, safeDecodeUtf8, whenM)
import System.Exit (exitSuccess)
import System.Terminal hiding (insertChars)
@@ -300,7 +299,7 @@ updateTermState user_ st ac live tw (key, ms) ts@TerminalState {inputString = s,
getNameSfxs table pfx =
getNameSfxs_ pfx (userId, pfx <> "%") $
"SELECT local_display_name FROM " <> table <> " WHERE user_id = ? AND local_display_name LIKE ?"
getNameSfxs_ :: SQL.ToRow p => Text -> p -> SQL.Query -> IO [String]
getNameSfxs_ :: DB.ToRow p => Text -> p -> DB.Query -> IO [String]
getNameSfxs_ pfx ps q =
withTransaction st (\db -> hasPfx pfx . map fromOnly <$> DB.query db q ps) `catchAll_` pure []
commands =
-3
View File
@@ -247,9 +247,6 @@ responseToView user_ ChatConfig {logLevel, showReactions, showReceipts, testView
CRNtfToken _ status mode -> ["device token status: " <> plain (smpEncode status) <> ", notifications mode: " <> plain (strEncode mode)]
CRNtfMessages {} -> []
CRSQLResult rows -> map plain rows
CRSlowSQLQueries {chatQueries, agentQueries} ->
let viewQuery SlowSQLQuery {query, duration} = sShow duration <> " ms: " <> plain (T.unwords $ T.lines query)
in ("Chat queries" : map viewQuery chatQueries) <> [""] <> ("Agent queries" : map viewQuery agentQueries)
CRDebugLocks {chatLockName, agentLocks} ->
[ maybe "no chat lock" (("chat lock: " <>) . plain) chatLockName,
plain $ "agent locks: " <> LB.unpack (J.encode agentLocks)
+1 -1
View File
@@ -49,7 +49,7 @@ extra-deps:
# - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561
# - ../simplexmq
- github: simplex-chat/simplexmq
commit: e2065ab3525ca67e39700ee8040839d667f75ea2
commit: 82aec2cd8f7b4033dbf08d5de33ced216f574bbb
- github: kazu-yamamoto/http2
commit: b5a1b7200cf5bc7044af34ba325284271f6dff25
# - ../direct-sqlcipher
-51
View File
@@ -25,7 +25,6 @@ directoryServiceTests :: SpecWith FilePath
directoryServiceTests = do
it "should register group" testDirectoryService
it "should suspend and resume group" testSuspendResume
it "should join found group via link" testJoinGroup
describe "de-listing the group" $ do
it "should de-list if owner leaves the group" testDelistedOwnerLeaves
it "should de-list if owner is removed from the group" testDelistedOwnerRemoved
@@ -191,56 +190,6 @@ testSuspendResume tmp =
bob <# "SimpleX-Directory> The group ID 1 (privacy) is listed in the directory again!"
groupFound bob "privacy"
testJoinGroup :: HasCallStack => FilePath -> IO ()
testJoinGroup tmp =
withDirectoryService tmp $ \superUser dsLink ->
withNewTestChat tmp "bob" bobProfile $ \bob -> do
withNewTestChat tmp "cath" cathProfile $ \cath ->
withNewTestChat tmp "dan" danProfile $ \dan -> do
bob `connectVia` dsLink
registerGroup superUser bob "privacy" "Privacy"
cath `connectVia` dsLink
cath #> "@SimpleX-Directory privacy"
cath <# "SimpleX-Directory> > privacy"
cath <## " Found 1 group(s)"
cath <# "SimpleX-Directory> privacy (Privacy)"
cath <## "Welcome message:"
welcomeMsg <- getTermLine cath
let groupLink = dropStrPrefix "Link to join the group privacy: " welcomeMsg
cath <## "2 members"
cath ##> ("/c " <> groupLink)
cath <## "connection request sent!"
cath <## "SimpleX-Directory_1: contact is connected"
cath <## "contact SimpleX-Directory_1 is merged into SimpleX-Directory"
cath <## "use @SimpleX-Directory <message> to send messages"
cath <## "#privacy: you joined the group"
cath <# ("#privacy SimpleX-Directory> " <> welcomeMsg)
cath <## "#privacy: member bob (Bob) is connected"
bob <## "#privacy: SimpleX-Directory added cath (Catherine) to the group (connecting...)"
bob <## "#privacy: new member cath is connected"
bob ##> "/create link #privacy"
bobLink <- getGroupLink bob "privacy" GRMember True
dan ##> ("/c " <> bobLink)
dan <## "connection request sent!"
concurrentlyN_
[ do
bob <## "dan (Daniel): accepting request to join group #privacy..."
bob <## "dan (Daniel): contact is connected"
bob <## "dan invited to group #privacy via your group link"
bob <## "#privacy: dan joined the group",
do
dan <## "bob (Bob): contact is connected"
dan <## "#privacy: you joined the group"
dan <# ("#privacy bob> " <> welcomeMsg)
dan <###
[ "#privacy: member SimpleX-Directory is connected",
"#privacy: member cath (Catherine) is connected"
],
do
cath <## "#privacy: bob added dan (Daniel) to the group (connecting...)"
cath <## "#privacy: new member dan is connected"
]
testDelistedOwnerLeaves :: HasCallStack => FilePath -> IO ()
testDelistedOwnerLeaves tmp =
withDirectoryService tmp $ \superUser dsLink ->
+4 -4
View File
@@ -10,7 +10,7 @@ import Data.List (dropWhileEnd)
import Data.Maybe (fromJust, isJust)
import Simplex.Chat.Store (createChatStore)
import qualified Simplex.Chat.Store as Store
import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), closeSQLiteStore, createSQLiteStore, withConnection')
import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), closeSQLiteStore, createSQLiteStore, withConnection)
import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..), MigrationsToRun (..), toDownMigration)
import qualified Simplex.Messaging.Agent.Store.SQLite.Migrations as Migrations
import Simplex.Messaging.Util (ifM, whenM)
@@ -53,14 +53,14 @@ testSchemaMigrations = withTmpFiles $ do
putStrLn $ "down migration " <> name m
let downMigr = fromJust $ toDownMigration m
schema <- getSchema testDB testSchema
withConnection' st (`Migrations.run` MTRUp [m])
withConnection st (`Migrations.run` MTRUp [m])
schema' <- getSchema testDB testSchema
schema' `shouldNotBe` schema
withConnection' st (`Migrations.run` MTRDown [downMigr])
withConnection st (`Migrations.run` MTRDown [downMigr])
unless (name m `elem` skipComparisonForDownMigrations) $ do
schema'' <- getSchema testDB testSchema
schema'' `shouldBe` schema
withConnection' st (`Migrations.run` MTRUp [m])
withConnection st (`Migrations.run` MTRUp [m])
schema''' <- getSchema testDB testSchema
schema''' `shouldBe` schema'
+1 -1
View File
@@ -120,7 +120,7 @@
"simplex-unique-card-1-p-1": "يحمي SimpleX خصوصية ملف التعريف الخاص بك، جهات الاتصال والبيانات الوصفية، ويخفيه عن خوادم منصة SimpleX وأي مراقبين.",
"privacy-matters-overlay-card-2-p-1": "منذ وقت ليس ببعيد، لاحظنا أن الانتخابات الرئيسية يتم التلاعب بها بواسطة <a href='https://en.wikipedia.org/wiki/FacebookCambridge_Analytica_data_scandal' target='_blank'> شركة استشارية ذات سمعة طيبة </a> التي استخدمت الرسوم البيانية الاجتماعية لتشويه نظرتنا للعالم الحقيقي والتلاعب بأصواتنا.",
"privacy-matters-overlay-card-2-p-2": "لكي تكون موضوعيًا وتتخذ قرارات مستقلة، يجب أن تكون متحكمًا في مساحة المعلومات الخاصة بك. هذا ممكن فقط إذا كنت تستخدم منصة اتصالات خاصة لا يمكنها الوصول إلى الرسم البياني الاجتماعي الخاص بك.",
"privacy-matters-overlay-card-2-p-3": "SimpleX هو النظام الأساسي الأول الذي لا يحتوي على أي معرفات مستخدم صمّم ليكون خاصًا، وبهذه الطريقة تحمي مخطط اتصالاتك بشكل أفضل من أي بديل معروف.",
"privacy-matters-overlay-card-2-p-3": "SimpleX هو النظام الأساسي الأول الذي لا يحتوي على أي معرفات مستخدم حسب التصميم، وبهذه الطريقة تحمي مخطط اتصالاتك بشكل أفضل من أي بديل معروف.",
"privacy-matters-overlay-card-3-p-2": "واحدة من أكثر القصص إثارة للصدمة هي تجربة <a href='https://en.wikipedia.org/wiki/Mohamedou_Ould_Slahi' target='_blank'> محمدو ولد صلاحي </a> الموصوفة في مذكراته والموضحة في فيلم موريتاني. تم وضعه في معتقل غوانتانامو بدون محاكمة، وتعرض للتعذيب هناك لمدة 15 عامًا بعد مكالمة هاتفية مع قريبه في أفغانستان، للاشتباه في تورطه في هجمات 11 سبتمبر، على الرغم من أنه عاش في ألمانيا طوال السنوات العشر الماضية.",
"privacy-matters-overlay-card-3-p-3": "يتم القبض على الأشخاص العاديين بسبب ما يشاركونه عبر الإنترنت، حتى عبر حساباتهم \"المجهولة\"، <a href='https://www.dailymail.co.uk/news/article-11282263/Moment-police-swoop-house-devout-catholic-mother-malicious-online-posts.html' target='_blank'>وحتى في البلدان الديمقراطية</a>.",
"simplex-unique-overlay-card-1-p-1": "على عكس أنظمة المراسلة الأخرى، <strong>لا يحتوي SimpleX على معرفات مخصصة للمستخدمين</strong>. لا يعتمد على أرقام الهواتف أو العناوين المستندة إلى النطاقات (مثل البريد الإلكتروني أو XMPP)، أسماء المستخدمين، المفاتيح العامة أو حتى الأرقام العشوائية لتحديد مستخدميها &mdash; لا نعرف عدد الأشخاص الذين يستخدمون خوادم SimpleX الخاصة بنا.",
+3 -3
View File
@@ -1,6 +1,6 @@
{
"simplex-privacy": "Privatsphäre mit SimpleX",
"simplex-network": "SimpleX-Netzwerk",
"simplex-privacy": "SimpleX Privatsphäre",
"simplex-network": "SimpleX Netzwerk",
"home": "Startseite",
"developers": "Entwickler",
"reference": "Referenz",
@@ -9,7 +9,7 @@
"features": "Funktionen",
"simplex-private-card-6-point-2": "Um das zu verhindern, werden von SimpleX Einmal-Schlüssel Out-of-Band weitergeleitet, wenn Sie eine Adresse als Link oder QR-Code teilen.",
"simplex-explained": "SimpleX erklärt",
"simplex-explained-tab-2-text": "2. Wie es funktioniert",
"simplex-explained-tab-2-text": "2. Wie funktioniert es",
"simplex-explained-tab-3-text": "3. Was die Server sehen",
"simplex-explained-tab-1-text": "1. Wie es die Nutzer erleben",
"simplex-explained-tab-1-p-1": "Sie können Kontakte und Gruppen erstellen und haben Zwei-Wege-Kommunikation wie in jedem anderen Messenger.",
+1 -1
View File
@@ -95,7 +95,7 @@
"simplex-unique-2-overlay-1-title": "La mejor protección contra el spam y el abuso",
"simplex-unique-3-overlay-1-title": "Titularidad, control y seguridad de tus datos",
"simplex-unique-4-overlay-1-title": "Totalmente descentralizado &mdash; los usuarios son dueños de la red SimpleX",
"hero-overlay-card-1-p-2": "Para entregar los mensajes, en lugar de los identificadores de usuario utilizados por todas las demás plataformas, SimpleX emplea identificadores por pares temporales y anónimos de colas de mensajes, independientes para cada una de sus conexiones &mdash; no hay identificadores a largo plazo.",
"hero-overlay-card-1-p-2": "Para entregar los mensajes, en lugar de los identificadores de usuario utilizados por todas las demás plataformas, SimpleX usa identificadores por pares anónimos y temporales de colas de mensajes, independientes para cada una de sus conexiones &mdash; no hay identificadores a largo plazo.",
"hero-overlay-card-1-p-5": "Los perfiles de usuario, contactos y grupos sólo se almacenan en los dispositivos cliente; los mensajes se envían con cifrado de doble capa de extremo a extremo .",
"hero-overlay-card-1-p-3": "Usted define qué servidor(es) se usan para recibir los mensajes; sus contactos &mdash; los servidores que se usan para enviar los mensajes. Es probable que cada conversación use dos servidores distintos.",
"hero-overlay-card-2-p-4": "SimpleX protege frente estos ataques al no disponer de ID de usuario por diseño. Y si usa el modo incógnito, tendrá un nombre mostrado diferente por cada contacto, evitando cualquier dato compartido entre ellos.",