Merge branch 'master' into ios-one-hand-ui

This commit is contained in:
Levitating Pineapple
2024-08-06 19:07:45 +03:00
50 changed files with 565 additions and 708 deletions
+2 -2
View File
@@ -137,8 +137,8 @@ func apiGetActiveUser(ctrl: chat_ctrl? = nil) throws -> User? {
}
}
func apiCreateActiveUser(_ p: Profile?, sameServers: Bool = false, pastTimestamp: Bool = false, ctrl: chat_ctrl? = nil) throws -> User {
let r = chatSendCmdSync(.createActiveUser(profile: p, sameServers: sameServers, pastTimestamp: pastTimestamp), ctrl)
func apiCreateActiveUser(_ p: Profile?, pastTimestamp: Bool = false, ctrl: chat_ctrl? = nil) throws -> User {
let r = chatSendCmdSync(.createActiveUser(profile: p, pastTimestamp: pastTimestamp), ctrl)
if case let .activeUser(user) = r { return user }
throw r
}
+3 -4
View File
@@ -15,7 +15,7 @@ public let jsonEncoder = getJSONEncoder()
public enum ChatCommand {
case showActiveUser
case createActiveUser(profile: Profile?, sameServers: Bool, pastTimestamp: Bool)
case createActiveUser(profile: Profile?, pastTimestamp: Bool)
case listUsers
case apiSetActiveUser(userId: Int64, viewPwd: String?)
case setAllContactReceipts(enable: Bool)
@@ -156,8 +156,8 @@ public enum ChatCommand {
get {
switch self {
case .showActiveUser: return "/u"
case let .createActiveUser(profile, sameServers, pastTimestamp):
let user = NewUser(profile: profile, sameServers: sameServers, pastTimestamp: pastTimestamp)
case let .createActiveUser(profile, pastTimestamp):
let user = NewUser(profile: profile, pastTimestamp: pastTimestamp)
return "/_create user \(encodeJSON(user))"
case .listUsers: return "/users"
case let .apiSetActiveUser(userId, viewPwd): return "/_user \(userId)\(maybePwd(viewPwd))"
@@ -1097,7 +1097,6 @@ public enum GroupLinkPlan: Decodable, Hashable {
struct NewUser: Encodable, Hashable {
var profile: Profile?
var sameServers: Bool
var pastTimestamp: Bool
}
@@ -276,7 +276,9 @@ fun AndroidScreen(settingsState: SettingsViewState) {
snapshotFlow { ModalManager.center.modalCount.value > 0 }
.filter { chatModel.chatId.value == null }
.collect { modalBackground ->
if (modalBackground && !chatModel.newChatSheetVisible.value) {
if (chatModel.newChatSheetVisible.value) {
platform.androidSetStatusAndNavBarColors(CurrentColors.value.colors.isLight, CurrentColors.value.colors.background, false, appPrefs.oneHandUI.get())
} else if (modalBackground) {
platform.androidSetStatusAndNavBarColors(CurrentColors.value.colors.isLight, CurrentColors.value.colors.background, false, false)
} else {
platform.androidSetStatusAndNavBarColors(CurrentColors.value.colors.isLight, CurrentColors.value.colors.background, !appPrefs.oneHandUI.get(), appPrefs.oneHandUI.get())
@@ -142,8 +142,8 @@ class AppPreferences {
},
set = fun(mode: TransportSessionMode) { _networkSessionMode.set(mode.name) }
)
val networkSMPProxyMode = mkStrPreference(SHARED_PREFS_NETWORK_SMP_PROXY_MODE, SMPProxyMode.Never.name)
val networkSMPProxyFallback = mkStrPreference(SHARED_PREFS_NETWORK_SMP_PROXY_FALLBACK, SMPProxyFallback.Allow.name)
val networkSMPProxyMode = mkStrPreference(SHARED_PREFS_NETWORK_SMP_PROXY_MODE, NetCfg.defaults.smpProxyMode.name)
val networkSMPProxyFallback = mkStrPreference(SHARED_PREFS_NETWORK_SMP_PROXY_FALLBACK, NetCfg.defaults.smpProxyFallback.name)
val networkHostMode = mkStrPreference(SHARED_PREFS_NETWORK_HOST_MODE, HostMode.OnionViaSocks.name)
val networkRequiredHostMode = mkBoolPreference(SHARED_PREFS_NETWORK_REQUIRED_HOST_MODE, false)
val networkTCPConnectTimeout = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_CONNECT_TIMEOUT, NetCfg.defaults.tcpConnectTimeout, NetCfg.proxyDefaults.tcpConnectTimeout)
@@ -657,8 +657,8 @@ object ChatController {
return null
}
suspend fun apiCreateActiveUser(rh: Long?, p: Profile?, sameServers: Boolean = false, pastTimestamp: Boolean = false, ctrl: ChatCtrl? = null): User? {
val r = sendCmd(rh, CC.CreateActiveUser(p, sameServers = sameServers, pastTimestamp = pastTimestamp), ctrl)
suspend fun apiCreateActiveUser(rh: Long?, p: Profile?, pastTimestamp: Boolean = false, ctrl: ChatCtrl? = null): User? {
val r = sendCmd(rh, CC.CreateActiveUser(p, pastTimestamp = pastTimestamp), ctrl)
if (r is CR.ActiveUser) return r.user.updateRemoteHostId(rh)
else if (
r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorStore && r.chatError.storeError is StoreError.DuplicateName ||
@@ -2824,7 +2824,7 @@ class SharedPreference<T>(val get: () -> T, set: (T) -> Unit) {
sealed class CC {
class Console(val cmd: String): CC()
class ShowActiveUser: CC()
class CreateActiveUser(val profile: Profile?, val sameServers: Boolean, val pastTimestamp: Boolean): CC()
class CreateActiveUser(val profile: Profile?, val pastTimestamp: Boolean): CC()
class ListUsers: CC()
class ApiSetActiveUser(val userId: Long, val viewPwd: String?): CC()
class SetAllContactReceipts(val enable: Boolean): CC()
@@ -2962,7 +2962,7 @@ sealed class CC {
is Console -> cmd
is ShowActiveUser -> "/u"
is CreateActiveUser -> {
val user = NewUser(profile, sameServers = sameServers, pastTimestamp = pastTimestamp)
val user = NewUser(profile, pastTimestamp = pastTimestamp)
"/_create user ${json.encodeToString(user)}"
}
is ListUsers -> "/users"
@@ -3293,7 +3293,6 @@ fun onOff(b: Boolean): String = if (b) "on" else "off"
@Serializable
data class NewUser(
val profile: Profile?,
val sameServers: Boolean,
val pastTimestamp: Boolean
)
@@ -3589,10 +3588,6 @@ enum class SMPProxyMode {
@SerialName("unknown") Unknown,
@SerialName("unprotected") Unprotected,
@SerialName("never") Never;
companion object {
val default = Never
}
}
@Serializable
@@ -3600,10 +3595,6 @@ enum class SMPProxyFallback {
@SerialName("allow") Allow,
@SerialName("allowProtected") AllowProtected,
@SerialName("prohibit") Prohibit;
companion object {
val default = Allow
}
}
@Serializable
@@ -47,13 +47,24 @@ private fun showNewChatSheet(oneHandUI: State<Boolean>) {
ModalManager.start.closeModals()
ModalManager.end.closeModals()
chatModel.newChatSheetVisible.value = true
ModalManager.start.showModalCloseable(
closeOnTop = !oneHandUI.value,
) { close ->
NewChatSheet(rh = chatModel.currentRemoteHost.value, close)
DisposableEffect(Unit) {
onDispose {
chatModel.newChatSheetVisible.value = false
ModalManager.start.showCustomModal { close ->
val close = {
// It will set it faster than in onDispose. It's important to catch the actual state before
// closing modal for reacting with status bar changes in [App]
chatModel.newChatSheetVisible.value = false
close()
}
ModalView(close, closeOnTop = !oneHandUI.value) {
if (appPlatform.isAndroid) {
BackHandler {
close()
}
}
NewChatSheet(rh = chatModel.currentRemoteHost.value, close)
DisposableEffect(Unit) {
onDispose {
chatModel.newChatSheetVisible.value = false
}
}
}
}
@@ -522,9 +522,17 @@ private fun exportArchive(
progressIndicator.value = true
withLongRunningApi {
try {
val archiveFile = exportChatArchive(m, null, chatArchiveName, chatArchiveTime, chatArchiveFile)
val (archiveFile, archiveErrors) = exportChatArchive(m, null, chatArchiveName, chatArchiveTime, chatArchiveFile)
chatArchiveFile.value = archiveFile
saveArchiveLauncher.launch(archiveFile.substringAfterLast(File.separator))
if (archiveErrors.isEmpty()) {
saveArchiveLauncher.launch(archiveFile.substringAfterLast(File.separator))
} else {
showArchiveExportedWithErrorsAlert(generalGetString(MR.strings.chat_database_exported_save), archiveErrors) {
withLongRunningApi {
saveArchiveLauncher.launch(archiveFile.substringAfterLast(File.separator))
}
}
}
progressIndicator.value = false
} catch (e: Error) {
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.error_exporting_chat_database), e.toString())
@@ -539,7 +547,7 @@ suspend fun exportChatArchive(
chatArchiveName: MutableState<String?>,
chatArchiveTime: MutableState<Instant?>,
chatArchiveFile: MutableState<String?>
): String {
): Pair<String, List<ArchiveError>> {
val archiveTime = Clock.System.now()
val ts = SimpleDateFormat("yyyy-MM-dd'T'HHmmss", Locale.US).format(Date.from(archiveTime.toJavaInstant()))
val archiveName = "simplex-chat.$ts.zip"
@@ -550,7 +558,7 @@ suspend fun exportChatArchive(
controller.apiSaveAppSettings(AppSettings.current.prepareForExport())
}
wallpapersDir.mkdirs()
m.controller.apiExportArchive(config)
val archiveErrors = m.controller.apiExportArchive(config)
if (storagePath == null) {
deleteOldArchive(m)
m.controller.appPrefs.chatArchiveName.set(archiveName)
@@ -559,7 +567,7 @@ suspend fun exportChatArchive(
chatArchiveName.value = archiveName
chatArchiveTime.value = archiveTime
chatArchiveFile.value = archivePath
return archivePath
return archivePath to archiveErrors
}
private fun deleteOldArchive(m: ChatModel) {
@@ -592,6 +600,28 @@ private fun importArchiveAlert(
)
}
fun showArchiveImportedWithErrorsAlert(archiveErrors: List<ArchiveError>) {
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.chat_database_imported),
text = generalGetString(MR.strings.restart_the_app_to_use_imported_chat_database) + "\n\n" + generalGetString(MR.strings.non_fatal_errors_occured_during_import) + archiveErrorsText(archiveErrors))
}
fun showArchiveExportedWithErrorsAlert(description: String, archiveErrors: List<ArchiveError>, onConfirm: () -> Unit) {
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.chat_database_exported_title),
text = description + "\n\n" + generalGetString(MR.strings.chat_database_exported_not_all_files) + archiveErrorsText(archiveErrors),
confirmText = generalGetString(MR.strings.chat_database_exported_continue),
onConfirm = onConfirm
)
}
private fun archiveErrorsText(errs: List<ArchiveError>): String = "\n" + errs.map {
when (it) {
is ArchiveError.ArchiveErrorImport -> it.importError
is ArchiveError.ArchiveErrorFile -> "${it.file}: ${it.fileError}"
}
}.joinToString(separator = "\n")
private fun importArchive(
m: ChatModel,
importedArchiveURI: URI,
@@ -621,7 +651,7 @@ private fun importArchive(
}
} else {
operationEnded(m, progressIndicator) {
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.chat_database_imported), text = generalGetString(MR.strings.restart_the_app_to_use_imported_chat_database) + "\n" + generalGetString(MR.strings.non_fatal_errors_occured_during_import))
showArchiveImportedWithErrorsAlert(archiveErrors)
}
}
} catch (e: Error) {
@@ -70,7 +70,7 @@ fun CloseSheetBar(close: (() -> Unit)?, showClose: Boolean = true, tintColor: Co
}
@Composable
fun AppBarTitle(title: String, hostDevice: Pair<Long?, String>? = null, withPadding: Boolean = true, bottomPadding: Dp = DEFAULT_PADDING * 1.5f) {
fun AppBarTitle(title: String, hostDevice: Pair<Long?, String>? = null, withPadding: Boolean = true, bottomPadding: Dp = DEFAULT_PADDING * 1.5f + 8.dp) {
val theme = CurrentColors.collectAsState()
val titleColor = MaterialTheme.appColors.title
val brush = if (theme.value.base == DefaultTheme.SIMPLEX)
@@ -480,12 +480,13 @@ private fun MutableState<MigrationFromState>.exportArchive() {
withLongRunningApi {
try {
getMigrationTempFilesDirectory().mkdir()
val archivePath = exportChatArchive(chatModel, getMigrationTempFilesDirectory(), mutableStateOf(""), mutableStateOf(Instant.DISTANT_PAST), mutableStateOf(""))
val totalBytes = File(archivePath).length()
if (totalBytes > 0L) {
state = MigrationFromState.DatabaseInit(totalBytes, archivePath)
val (archivePath, archiveErrors) = exportChatArchive(chatModel, getMigrationTempFilesDirectory(), mutableStateOf(""), mutableStateOf(Instant.DISTANT_PAST), mutableStateOf(""))
if (archiveErrors.isEmpty()) {
uploadArchive(archivePath)
} else {
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.migrate_from_device_exported_file_doesnt_exist))
showArchiveExportedWithErrorsAlert(generalGetString(MR.strings.chat_database_exported_migrate), archiveErrors) {
uploadArchive(archivePath)
}
state = MigrationFromState.UploadConfirmation
}
} catch (e: Exception) {
@@ -498,6 +499,17 @@ private fun MutableState<MigrationFromState>.exportArchive() {
}
}
private fun MutableState<MigrationFromState>.uploadArchive(archivePath: String) {
val totalBytes = File(archivePath).length()
if (totalBytes > 0L) {
state = MigrationFromState.DatabaseInit(totalBytes, archivePath)
} else {
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.migrate_from_device_exported_file_doesnt_exist))
state = MigrationFromState.UploadConfirmation
}
}
suspend fun initTemporaryDatabase(tempDatabaseFile: File, netCfg: NetCfg): Pair<ChatCtrl, User>? {
val (status, ctrl) = chatInitTemporaryDatabase(tempDatabaseFile.absolutePath)
showErrorOnMigrationIfNeeded(status)
@@ -237,7 +237,6 @@ private fun ModalData.OnionView(link: String, socksProxy: String?, hostMode: Hos
proxy
}
}
val proxyPort = remember { derivedStateOf { networkProxyHostPort.value?.split(":")?.lastOrNull()?.toIntOrNull() ?: 9050 } }
val netCfg = rememberSaveable(stateSaver = serializableSaver()) {
mutableStateOf(getNetCfg().withOnionHosts(onionHosts.value).copy(socksProxy = socksProxy, sessionMode = sessionMode.value))
@@ -275,7 +274,6 @@ private fun ModalData.OnionView(link: String, socksProxy: String?, hostMode: Hos
onionHosts,
sessionMode,
networkProxyHostPortPref,
proxyPort,
toggleSocksProxy = { enable ->
networkUseSocksProxy.value = enable
},
@@ -599,10 +597,7 @@ private fun MutableState<MigrationToState?>.importArchive(archivePath: String, n
val config = ArchiveConfig(archivePath, parentTempDirectory = databaseExportDir.toString())
val archiveErrors = controller.apiImportArchive(config)
if (archiveErrors.isNotEmpty()) {
AlertManager.shared.showAlertMsg(
generalGetString(MR.strings.chat_database_imported),
generalGetString(MR.strings.non_fatal_errors_occured_during_import)
)
showArchiveImportedWithErrorsAlert(archiveErrors)
}
state = MigrationToState.Passphrase("", netCfg)
MigrationToDeviceState.save(MigrationToDeviceState.Passphrase(netCfg))
@@ -89,10 +89,7 @@ fun ConnectMobileLayout(
connectDesktop: () -> Unit,
deleteHost: (RemoteHostInfo) -> Unit,
) {
ColumnWithScrollBar(
Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
ColumnWithScrollBar(Modifier.fillMaxWidth()) {
AppBarTitle(stringResource(if (remember { chatModel.remoteHosts }.isEmpty()) MR.strings.link_a_mobile else MR.strings.linked_mobiles))
SectionView(generalGetString(MR.strings.this_device_name).uppercase()) {
DeviceNameField(deviceName.value ?: "") { updateDeviceName(it) }
@@ -100,7 +97,7 @@ fun ConnectMobileLayout(
PreferenceToggle(stringResource(MR.strings.multicast_discoverable_via_local_network), checked = remember { controller.appPrefs.offerRemoteMulticast.state }.value) {
controller.appPrefs.offerRemoteMulticast.set(it)
}
SectionDividerSpaced(maxBottomPadding = false)
SectionDividerSpaced()
}
SectionView(stringResource(MR.strings.devices).uppercase()) {
if (chatModel.localUserCreated.value == true) {
@@ -179,10 +176,7 @@ private fun ConnectMobileViewLayout(
refreshQrCode: () -> Unit = {},
UnderQrLayout: @Composable () -> Unit = {},
) {
ColumnWithScrollBar(
Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
ColumnWithScrollBar(Modifier.fillMaxWidth()) {
if (title != null) {
AppBarTitle(title)
}
@@ -2,39 +2,54 @@ package chat.simplex.common.views.usersettings
import SectionBottomSpacer
import SectionCustomFooter
import SectionDividerSpaced
import SectionItemView
import SectionItemWithValue
import SectionView
import SectionViewSelectableCards
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalDensity
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import chat.simplex.common.model.*
import chat.simplex.common.model.ChatController.appPrefs
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.helpers.*
import chat.simplex.common.model.ChatModel
import chat.simplex.common.model.ChatModel.controller
import chat.simplex.common.platform.ColumnWithScrollBar
import chat.simplex.common.platform.chatModel
import chat.simplex.res.MR
import java.text.DecimalFormat
@Composable
fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
val currentCfg = remember { mutableStateOf(chatModel.controller.getNetCfg()) }
fun ModalData.AdvancedNetworkSettingsView(showModal: (ModalData.() -> Unit) -> Unit, close: () -> Unit) {
val currentRemoteHost by remember { chatModel.currentRemoteHost }
val developerTools = remember { appPrefs.developerTools.get() }
// Will be actual once the screen is re-opened
val savedCfg = remember { mutableStateOf(controller.getNetCfg()) }
// Will have an edited state when the screen is re-opened
val currentCfg = remember { stateGetOrPut("currentCfg") { controller.getNetCfg() } }
val currentCfgVal = currentCfg.value // used only on initialization
val onionHosts = remember { mutableStateOf(currentCfgVal.onionHosts) }
val sessionMode = remember { mutableStateOf(currentCfgVal.sessionMode) }
val smpProxyMode = remember { mutableStateOf(currentCfgVal.smpProxyMode) }
val smpProxyFallback = remember { mutableStateOf(currentCfgVal.smpProxyFallback) }
val networkUseSocksProxy: MutableState<Boolean> = remember { mutableStateOf(currentCfgVal.useSocksProxy) }
val networkTCPConnectTimeout = remember { mutableStateOf(currentCfgVal.tcpConnectTimeout) }
val networkTCPTimeout = remember { mutableStateOf(currentCfgVal.tcpTimeout) }
val networkTCPTimeoutPerKb = remember { mutableStateOf(currentCfgVal.tcpTimeoutPerKb) }
var networkRcvConcurrency = remember { mutableStateOf(currentCfgVal.rcvConcurrency) }
val networkRcvConcurrency = remember { mutableStateOf(currentCfgVal.rcvConcurrency) }
val networkSMPPingInterval = remember { mutableStateOf(currentCfgVal.smpPingInterval) }
val networkSMPPingCount = remember { mutableStateOf(currentCfgVal.smpPingCount) }
val networkEnableKeepAlive = remember { mutableStateOf(currentCfgVal.enableKeepAlive) }
@@ -63,11 +78,11 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
}
return NetCfg(
socksProxy = currentCfg.value.socksProxy,
hostMode = currentCfg.value.hostMode,
requiredHostMode = currentCfg.value.requiredHostMode,
sessionMode = currentCfg.value.sessionMode,
smpProxyMode = currentCfg.value.smpProxyMode,
smpProxyFallback = currentCfg.value.smpProxyFallback,
// hostMode = currentCfg.value.hostMode,
// requiredHostMode = currentCfg.value.requiredHostMode,
sessionMode = sessionMode.value,
smpProxyMode = smpProxyMode.value,
smpProxyFallback = smpProxyFallback.value,
tcpConnectTimeout = networkTCPConnectTimeout.value,
tcpTimeout = networkTCPTimeout.value,
tcpTimeoutPerKb = networkTCPTimeoutPerKb.value,
@@ -75,10 +90,14 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
tcpKeepAlive = tcpKeepAlive,
smpPingInterval = networkSMPPingInterval.value,
smpPingCount = networkSMPPingCount.value
)
).withOnionHosts(onionHosts.value)
}
fun updateView(cfg: NetCfg) {
onionHosts.value = cfg.onionHosts
sessionMode.value = cfg.sessionMode
smpProxyMode.value = cfg.smpProxyMode
smpProxyFallback.value = cfg.smpProxyFallback
networkTCPConnectTimeout.value = cfg.tcpConnectTimeout
networkTCPTimeout.value = cfg.tcpTimeout
networkTCPTimeoutPerKb.value = cfg.tcpTimeoutPerKb
@@ -97,40 +116,80 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
}
}
fun saveCfg(cfg: NetCfg) {
fun saveCfg(cfg: NetCfg, close: (() -> Unit)? = null) {
withBGApi {
chatModel.controller.apiSetNetworkConfig(cfg)
currentCfg.value = cfg
chatModel.controller.setNetCfg(cfg)
if (chatModel.controller.apiSetNetworkConfig(cfg)) {
currentCfg.value = cfg
savedCfg.value = cfg
chatModel.controller.setNetCfg(cfg)
close?.invoke()
}
}
}
fun reset() {
val newCfg = if (currentCfg.value.useSocksProxy) NetCfg.proxyDefaults else NetCfg.defaults
updateView(newCfg)
saveCfg(newCfg)
currentCfg.value = newCfg
}
AdvancedNetworkSettingsLayout(
networkTCPConnectTimeout,
networkTCPTimeout,
networkTCPTimeoutPerKb,
networkRcvConcurrency,
networkSMPPingInterval,
networkSMPPingCount,
networkEnableKeepAlive,
networkTCPKeepIdle,
networkTCPKeepIntvl,
networkTCPKeepCnt,
resetDisabled = if (currentCfg.value.useSocksProxy) currentCfg.value == NetCfg.proxyDefaults else currentCfg.value == NetCfg.defaults,
reset = { showUpdateNetworkSettingsDialog(::reset) },
footerDisabled = buildCfg() == currentCfg.value,
revert = { updateView(currentCfg.value) },
save = { showUpdateNetworkSettingsDialog { saveCfg(buildCfg()) } }
)
val saveDisabled = buildCfg() == savedCfg.value
ModalView(
close = {
if (saveDisabled) {
close()
} else {
showUnsavedChangesAlert({
saveCfg(buildCfg(), close)
}, close)
}
},
) {
AdvancedNetworkSettingsLayout(
currentRemoteHost = currentRemoteHost,
networkUseSocksProxy = networkUseSocksProxy,
developerTools = developerTools,
onionHosts = onionHosts,
useOnion = { onionHosts.value = it; currentCfg.value = currentCfg.value.withOnionHosts(it) },
sessionMode = sessionMode,
smpProxyMode = smpProxyMode,
smpProxyFallback = smpProxyFallback,
networkTCPConnectTimeout,
networkTCPTimeout,
networkTCPTimeoutPerKb,
networkRcvConcurrency,
networkSMPPingInterval,
networkSMPPingCount,
networkEnableKeepAlive,
networkTCPKeepIdle,
networkTCPKeepIntvl,
networkTCPKeepCnt,
updateSessionMode = { sessionMode.value = it; currentCfg.value = currentCfg.value.copy(sessionMode = it) },
updateSMPProxyMode = { smpProxyMode.value = it; currentCfg.value = currentCfg.value.copy(smpProxyMode = it) },
updateSMPProxyFallback = { smpProxyFallback.value = it; currentCfg.value = currentCfg.value.copy(smpProxyFallback = it) },
showModal = showModal,
resetDisabled = if (currentCfg.value.useSocksProxy) buildCfg() == NetCfg.proxyDefaults else buildCfg() == NetCfg.defaults,
reset = ::reset,
saveDisabled = saveDisabled,
save = {
showUpdateNetworkSettingsDialog {
saveCfg(buildCfg())
}
}
)
}
}
@Composable fun AdvancedNetworkSettingsLayout(
currentRemoteHost: RemoteHostInfo?,
networkUseSocksProxy: State<Boolean>,
developerTools: Boolean,
onionHosts: MutableState<OnionHosts>,
useOnion: (OnionHosts) -> Unit,
sessionMode: MutableState<TransportSessionMode>,
smpProxyMode: MutableState<SMPProxyMode>,
smpProxyFallback: MutableState<SMPProxyFallback>,
networkTCPConnectTimeout: MutableState<Long>,
networkTCPTimeout: MutableState<Long>,
networkTCPTimeoutPerKb: MutableState<Long>,
@@ -141,10 +200,13 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
networkTCPKeepIdle: MutableState<Int>,
networkTCPKeepIntvl: MutableState<Int>,
networkTCPKeepCnt: MutableState<Int>,
updateSessionMode: (TransportSessionMode) -> Unit,
updateSMPProxyMode: (SMPProxyMode) -> Unit,
updateSMPProxyFallback: (SMPProxyFallback) -> Unit,
showModal: (ModalData.() -> Unit) -> Unit,
resetDisabled: Boolean,
reset: () -> Unit,
footerDisabled: Boolean,
revert: () -> Unit,
saveDisabled: Boolean,
save: () -> Unit
) {
val secondsLabel = stringResource(MR.strings.network_option_seconds_label)
@@ -154,10 +216,39 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
.fillMaxWidth(),
) {
AppBarTitle(stringResource(MR.strings.network_settings_title))
SectionView {
SectionItemView {
ResetToDefaultsButton(reset, disabled = resetDisabled)
if (currentRemoteHost == null) {
SectionView(generalGetString(MR.strings.settings_section_title_private_message_routing)) {
SMPProxyModePicker(smpProxyMode, showModal, updateSMPProxyMode)
SMPProxyFallbackPicker(smpProxyFallback, showModal, updateSMPProxyFallback, enabled = remember { derivedStateOf { smpProxyMode.value != SMPProxyMode.Never } })
SettingsPreferenceItem(painterResource(MR.images.ic_arrow_forward), stringResource(MR.strings.private_routing_show_message_status), chatModel.controller.appPrefs.showSentViaProxy)
}
SectionCustomFooter {
Text(stringResource(MR.strings.private_routing_explanation))
}
SectionDividerSpaced(maxTopPadding = true)
}
if (currentRemoteHost == null && networkUseSocksProxy.value) {
SectionView(stringResource(MR.strings.network_socks_proxy).uppercase()) {
UseOnionHosts(onionHosts, networkUseSocksProxy, showModal, useOnion)
SectionCustomFooter {
Column {
Text(annotatedStringResource(MR.strings.disable_onion_hosts_when_not_supported))
}
}
}
SectionDividerSpaced(maxTopPadding = true)
}
if (currentRemoteHost == null && developerTools) {
SectionView(stringResource(MR.strings.network_session_mode_transport_isolation).uppercase()) {
SessionModePicker(sessionMode, showModal, updateSessionMode)
}
SectionDividerSpaced()
}
SectionView(stringResource(MR.strings.network_option_tcp_connection).uppercase()) {
SectionItemView {
TimeoutSettingRow(
stringResource(MR.strings.network_option_tcp_connection_timeout), networkTCPConnectTimeout,
@@ -220,23 +311,92 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
}
}
}
SectionCustomFooter {
SettingsSectionFooter(revert, save, footerDisabled)
SectionDividerSpaced(maxBottomPadding = false)
SectionView {
SectionItemView(reset, disabled = resetDisabled) {
Text(stringResource(MR.strings.network_options_reset_to_defaults), color = if (resetDisabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary)
}
SectionItemView(save, disabled = saveDisabled) {
Text(stringResource(MR.strings.network_options_save_and_reconnect), color = if (saveDisabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary)
}
}
SectionBottomSpacer()
}
}
@Composable
fun ResetToDefaultsButton(reset: () -> Unit, disabled: Boolean) {
val modifier = if (disabled) Modifier else Modifier.clickable { reset() }
Row(
modifier.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically
) {
val color = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
Text(stringResource(MR.strings.network_options_reset_to_defaults), color = color)
private fun SMPProxyModePicker(
smpProxyMode: MutableState<SMPProxyMode>,
showModal: (@Composable ModalData.() -> Unit) -> Unit,
updateSMPProxyMode: (SMPProxyMode) -> Unit,
) {
val density = LocalDensity.current
val values = remember {
SMPProxyMode.values().map {
when (it) {
SMPProxyMode.Always -> ValueTitleDesc(SMPProxyMode.Always, generalGetString(MR.strings.network_smp_proxy_mode_always), escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_smp_proxy_mode_always_description), density))
SMPProxyMode.Unknown -> ValueTitleDesc(SMPProxyMode.Unknown, generalGetString(MR.strings.network_smp_proxy_mode_unknown), escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_smp_proxy_mode_unknown_description), density))
SMPProxyMode.Unprotected -> ValueTitleDesc(SMPProxyMode.Unprotected, generalGetString(MR.strings.network_smp_proxy_mode_unprotected), escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_smp_proxy_mode_unprotected_description), density))
SMPProxyMode.Never -> ValueTitleDesc(SMPProxyMode.Never, generalGetString(MR.strings.network_smp_proxy_mode_never), escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_smp_proxy_mode_never_description), density))
}
}
}
SectionItemWithValue(
generalGetString(MR.strings.network_smp_proxy_mode_private_routing),
smpProxyMode,
values,
icon = painterResource(MR.images.ic_settings_ethernet),
onSelected = {
showModal {
ColumnWithScrollBar(
Modifier.fillMaxWidth(),
) {
AppBarTitle(stringResource(MR.strings.network_smp_proxy_mode_private_routing))
SectionViewSelectableCards(null, smpProxyMode, values, updateSMPProxyMode)
}
}
}
)
}
@Composable
private fun SMPProxyFallbackPicker(
smpProxyFallback: MutableState<SMPProxyFallback>,
showModal: (@Composable ModalData.() -> Unit) -> Unit,
updateSMPProxyFallback: (SMPProxyFallback) -> Unit,
enabled: State<Boolean>,
) {
val density = LocalDensity.current
val values = remember {
SMPProxyFallback.values().map {
when (it) {
SMPProxyFallback.Allow -> ValueTitleDesc(SMPProxyFallback.Allow, generalGetString(MR.strings.network_smp_proxy_fallback_allow), escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_smp_proxy_fallback_allow_description), density))
SMPProxyFallback.AllowProtected -> ValueTitleDesc(SMPProxyFallback.AllowProtected, generalGetString(MR.strings.network_smp_proxy_fallback_allow_protected), escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_smp_proxy_fallback_allow_protected_description), density))
SMPProxyFallback.Prohibit -> ValueTitleDesc(SMPProxyFallback.Prohibit, generalGetString(MR.strings.network_smp_proxy_fallback_prohibit), escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_smp_proxy_fallback_prohibit_description), density))
}
}
}
SectionItemWithValue(
generalGetString(MR.strings.network_smp_proxy_fallback_allow_downgrade),
smpProxyFallback,
values,
icon = painterResource(MR.images.ic_arrows_left_right),
enabled = enabled,
onSelected = {
showModal {
ColumnWithScrollBar(
Modifier.fillMaxWidth(),
) {
AppBarTitle(stringResource(MR.strings.network_smp_proxy_fallback_allow_downgrade))
SectionViewSelectableCards(null, smpProxyFallback, values, updateSMPProxyFallback)
}
}
}
)
}
@Composable
@@ -377,43 +537,6 @@ fun TimeoutSettingRow(title: String, selection: MutableState<Long>, values: List
}
}
@Composable
fun SettingsSectionFooter(revert: () -> Unit, save: () -> Unit, disabled: Boolean) {
Row(
Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
FooterButton(painterResource(MR.images.ic_replay), stringResource(MR.strings.network_options_revert), revert, disabled)
FooterButton(painterResource(MR.images.ic_check), stringResource(MR.strings.network_options_save), save, disabled)
}
}
@Composable
fun FooterButton(icon: Painter, title: String, action: () -> Unit, disabled: Boolean) {
Surface(
shape = RoundedCornerShape(20.dp),
color = Color.Black.copy(alpha = 0f),
contentColor = LocalContentColor.current
) {
val modifier = if (disabled) Modifier else Modifier.clickable { action() }
Row(
modifier.padding(8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Icon(
icon,
title,
tint = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
)
Text(
title,
color = if (disabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
)
}
}
}
fun showUpdateNetworkSettingsDialog(action: () -> Unit) {
AlertManager.shared.showAlertDialog(
title = generalGetString(MR.strings.update_network_settings_question),
@@ -423,11 +546,27 @@ fun showUpdateNetworkSettingsDialog(action: () -> Unit) {
)
}
private fun showUnsavedChangesAlert(save: () -> Unit, revert: () -> Unit) {
AlertManager.shared.showAlertDialogStacked(
title = generalGetString(MR.strings.update_network_settings_question),
confirmText = generalGetString(MR.strings.network_options_save_and_reconnect),
dismissText = generalGetString(MR.strings.exit_without_saving),
onConfirm = save,
onDismiss = revert,
)
}
@Preview
@Composable
fun PreviewAdvancedNetworkSettingsLayout() {
SimpleXTheme {
AdvancedNetworkSettingsLayout(
currentRemoteHost = null,
networkUseSocksProxy = remember { mutableStateOf(false) },
developerTools = false,
sessionMode = remember { mutableStateOf(TransportSessionMode.User) },
smpProxyMode = remember { mutableStateOf(SMPProxyMode.Never) },
smpProxyFallback = remember { mutableStateOf(SMPProxyFallback.Allow) },
networkTCPConnectTimeout = remember { mutableStateOf(10_000000) },
networkTCPTimeout = remember { mutableStateOf(10_000000) },
networkTCPTimeoutPerKb = remember { mutableStateOf(10_000) },
@@ -438,10 +577,15 @@ fun PreviewAdvancedNetworkSettingsLayout() {
networkTCPKeepIdle = remember { mutableStateOf(10) },
networkTCPKeepIntvl = remember { mutableStateOf(10) },
networkTCPKeepCnt = remember { mutableStateOf(10) },
onionHosts = remember { mutableStateOf(OnionHosts.PREFER) },
useOnion = {},
updateSessionMode = {},
updateSMPProxyMode = {},
updateSMPProxyFallback = {},
showModal = {},
resetDisabled = false,
reset = {},
footerDisabled = false,
revert = {},
saveDisabled = false,
save = {}
)
}
@@ -36,10 +36,7 @@ fun CallSettingsLayout(
callOnLockScreen: SharedPreference<CallOnLockScreen>,
editIceServers: () -> Unit,
) {
ColumnWithScrollBar(
Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
ColumnWithScrollBar(Modifier.fillMaxWidth()) {
AppBarTitle(stringResource(MR.strings.your_calls))
val lockCallState = remember { mutableStateOf(callOnLockScreen.get()) }
SectionView(stringResource(MR.strings.settings_section_title_settings)) {
@@ -7,9 +7,7 @@ import SectionItemView
import SectionItemWithValue
import SectionView
import SectionViewSelectable
import SectionViewSelectableCards
import TextIconSpaced
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.*
@@ -20,20 +18,17 @@ import androidx.compose.ui.Modifier
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.*
import androidx.compose.ui.text.input.*
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.common.model.*
import chat.simplex.common.model.ChatController.appPrefs
import chat.simplex.common.model.ChatModel.controller
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.chat.item.ClickableText
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.helpers.annotatedStringResource
import chat.simplex.res.MR
@Composable
@@ -42,22 +37,11 @@ fun NetworkAndServersView() {
// It's not a state, just a one-time value. Shouldn't be used in any state-related situations
val netCfg = remember { chatModel.controller.getNetCfg() }
val networkUseSocksProxy: MutableState<Boolean> = remember { mutableStateOf(netCfg.useSocksProxy) }
val developerTools = chatModel.controller.appPrefs.developerTools.get()
val onionHosts = remember { mutableStateOf(netCfg.onionHosts) }
val sessionMode = remember { mutableStateOf(netCfg.sessionMode) }
val smpProxyMode = remember { mutableStateOf(netCfg.smpProxyMode) }
val smpProxyFallback = remember { mutableStateOf(netCfg.smpProxyFallback) }
val proxyPort = remember { derivedStateOf { chatModel.controller.appPrefs.networkProxyHostPort.state.value?.split(":")?.lastOrNull()?.toIntOrNull() ?: 9050 } }
NetworkAndServersLayout(
currentRemoteHost = currentRemoteHost,
developerTools = developerTools,
networkUseSocksProxy = networkUseSocksProxy,
onionHosts = onionHosts,
sessionMode = sessionMode,
smpProxyMode = smpProxyMode,
smpProxyFallback = smpProxyFallback,
proxyPort = proxyPort,
toggleSocksProxy = { enable ->
val def = NetCfg.defaults
val proxyDef = NetCfg.proxyDefaults
@@ -84,7 +68,6 @@ fun NetworkAndServersView() {
chatModel.controller.apiSetNetworkConfig(conf)
chatModel.controller.setNetCfg(conf)
networkUseSocksProxy.value = true
onionHosts.value = conf.onionHosts
}
}
)
@@ -111,184 +94,48 @@ fun NetworkAndServersView() {
chatModel.controller.apiSetNetworkConfig(conf)
chatModel.controller.setNetCfg(conf)
networkUseSocksProxy.value = false
onionHosts.value = conf.onionHosts
}
}
)
}
},
useOnion = {
if (onionHosts.value == it) return@NetworkAndServersLayout
val prevValue = onionHosts.value
onionHosts.value = it
val startsWith = when (it) {
OnionHosts.NEVER -> generalGetString(MR.strings.network_use_onion_hosts_no_desc_in_alert)
OnionHosts.PREFER -> generalGetString(MR.strings.network_use_onion_hosts_prefer_desc_in_alert)
OnionHosts.REQUIRED -> generalGetString(MR.strings.network_use_onion_hosts_required_desc_in_alert)
}
showUpdateNetworkSettingsDialog(
title = generalGetString(MR.strings.update_onion_hosts_settings_question),
startsWith,
onDismiss = {
onionHosts.value = prevValue
}
) {
withBGApi {
val newCfg = chatModel.controller.getNetCfg().withOnionHosts(it)
val res = chatModel.controller.apiSetNetworkConfig(newCfg)
if (res) {
chatModel.controller.setNetCfg(newCfg)
onionHosts.value = it
} else {
onionHosts.value = prevValue
}
}
}
},
updateSessionMode = {
if (sessionMode.value == it) return@NetworkAndServersLayout
val prevValue = sessionMode.value
sessionMode.value = it
val startsWith = when (it) {
TransportSessionMode.User -> generalGetString(MR.strings.network_session_mode_user_description)
TransportSessionMode.Entity -> generalGetString(MR.strings.network_session_mode_entity_description)
}
showUpdateNetworkSettingsDialog(
title = generalGetString(MR.strings.update_network_session_mode_question),
startsWith,
onDismiss = { sessionMode.value = prevValue }
) {
withBGApi {
val newCfg = chatModel.controller.getNetCfg().copy(sessionMode = it)
val res = chatModel.controller.apiSetNetworkConfig(newCfg)
if (res) {
chatModel.controller.setNetCfg(newCfg)
sessionMode.value = it
} else {
sessionMode.value = prevValue
}
}
}
},
updateSMPProxyMode = {
if (smpProxyMode.value == it) return@NetworkAndServersLayout
val prevValue = smpProxyMode.value
smpProxyMode.value = it
val startsWith = when (it) {
SMPProxyMode.Always -> generalGetString(MR.strings.network_smp_proxy_mode_always_description)
SMPProxyMode.Unknown -> generalGetString(MR.strings.network_smp_proxy_mode_unknown_description)
SMPProxyMode.Unprotected -> generalGetString(MR.strings.network_smp_proxy_mode_unprotected_description)
SMPProxyMode.Never -> generalGetString(MR.strings.network_smp_proxy_mode_never_description)
}
showUpdateNetworkSettingsDialog(
title = generalGetString(MR.strings.update_network_smp_proxy_mode_question),
startsWith,
onDismiss = { smpProxyMode.value = prevValue }
) {
withBGApi {
val newCfg = chatModel.controller.getNetCfg().copy(smpProxyMode = it)
val res = chatModel.controller.apiSetNetworkConfig(newCfg)
if (res) {
chatModel.controller.setNetCfg(newCfg)
smpProxyMode.value = it
} else {
smpProxyMode.value = prevValue
}
}
}
},
updateSMPProxyFallback = {
if (smpProxyFallback.value == it) return@NetworkAndServersLayout
val prevValue = smpProxyFallback.value
smpProxyFallback.value = it
val startsWith = when (it) {
SMPProxyFallback.Allow -> generalGetString(MR.strings.network_smp_proxy_fallback_allow_description)
SMPProxyFallback.AllowProtected -> generalGetString(MR.strings.network_smp_proxy_fallback_allow_protected_description)
SMPProxyFallback.Prohibit -> generalGetString(MR.strings.network_smp_proxy_fallback_prohibit_description)
}
showUpdateNetworkSettingsDialog(
title = generalGetString(MR.strings.update_network_smp_proxy_fallback_question),
startsWith,
onDismiss = { smpProxyFallback.value = prevValue }
) {
withBGApi {
val newCfg = chatModel.controller.getNetCfg().copy(smpProxyFallback = it)
val res = chatModel.controller.apiSetNetworkConfig(newCfg)
if (res) {
chatModel.controller.setNetCfg(newCfg)
smpProxyFallback.value = it
} else {
smpProxyFallback.value = prevValue
}
}
}
}
)
}
@Composable fun NetworkAndServersLayout(
currentRemoteHost: RemoteHostInfo?,
developerTools: Boolean,
networkUseSocksProxy: MutableState<Boolean>,
onionHosts: MutableState<OnionHosts>,
sessionMode: MutableState<TransportSessionMode>,
smpProxyMode: MutableState<SMPProxyMode>,
smpProxyFallback: MutableState<SMPProxyFallback>,
proxyPort: State<Int>,
toggleSocksProxy: (Boolean) -> Unit,
useOnion: (OnionHosts) -> Unit,
updateSessionMode: (TransportSessionMode) -> Unit,
updateSMPProxyMode: (SMPProxyMode) -> Unit,
updateSMPProxyFallback: (SMPProxyFallback) -> Unit,
) {
val m = chatModel
ColumnWithScrollBar(
Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
ColumnWithScrollBar(Modifier.fillMaxWidth()) {
val showModal = { it: @Composable ModalData.() -> Unit -> ModalManager.start.showModal(content = it) }
val showCustomModal = { it: @Composable (close: () -> Unit) -> Unit -> ModalManager.start.showCustomModal { close -> it(close) }}
AppBarTitle(stringResource(MR.strings.network_and_servers))
if (!chatModel.desktopNoUserNoRemote) {
SectionView(generalGetString(MR.strings.settings_section_title_messages)) {
SettingsActionItem(painterResource(MR.images.ic_dns), stringResource(MR.strings.smp_servers), { ModalManager.start.showCustomModal { close -> ProtocolServersView(m, m.remoteHostId, ServerProtocol.SMP, close) } })
SettingsActionItem(painterResource(MR.images.ic_dns), stringResource(MR.strings.message_servers), { ModalManager.start.showCustomModal { close -> ProtocolServersView(m, m.remoteHostId, ServerProtocol.SMP, close) } })
SettingsActionItem(painterResource(MR.images.ic_dns), stringResource(MR.strings.xftp_servers), { ModalManager.start.showCustomModal { close -> ProtocolServersView(m, m.remoteHostId, ServerProtocol.XFTP, close) } })
SettingsActionItem(painterResource(MR.images.ic_dns), stringResource(MR.strings.media_and_file_servers), { ModalManager.start.showCustomModal { close -> ProtocolServersView(m, m.remoteHostId, ServerProtocol.XFTP, close) } })
if (currentRemoteHost == null) {
UseSocksProxySwitch(networkUseSocksProxy, proxyPort, toggleSocksProxy, showModal, chatModel.controller.appPrefs.networkProxyHostPort, false)
UseOnionHosts(onionHosts, networkUseSocksProxy, showModal, useOnion)
if (developerTools) {
SessionModePicker(sessionMode, showModal, updateSessionMode)
UseSocksProxySwitch(networkUseSocksProxy, toggleSocksProxy)
SettingsActionItem(painterResource(MR.images.ic_settings_ethernet), stringResource(MR.strings.network_socks_proxy_settings), { showCustomModal { SocksProxySettings(networkUseSocksProxy.value, appPrefs.networkProxyHostPort, false, it) }})
SettingsActionItem(painterResource(MR.images.ic_cable), stringResource(MR.strings.network_settings), { ModalManager.start.showCustomModal { AdvancedNetworkSettingsView(showModal, it) } })
if (networkUseSocksProxy.value) {
SectionCustomFooter {
Column {
Text(annotatedStringResource(MR.strings.socks_proxy_setting_limitations))
}
}
SectionDividerSpaced(maxTopPadding = true)
} else {
SectionDividerSpaced()
}
SettingsActionItem(painterResource(MR.images.ic_cable), stringResource(MR.strings.network_settings), { ModalManager.start.showModal { AdvancedNetworkSettingsView(m) } })
}
}
}
if (currentRemoteHost == null && networkUseSocksProxy.value) {
SectionCustomFooter {
Column {
Text(annotatedStringResource(MR.strings.disable_onion_hosts_when_not_supported))
Spacer(Modifier.height(DEFAULT_PADDING_HALF))
Text(annotatedStringResource(MR.strings.socks_proxy_setting_limitations))
}
}
Divider(Modifier.padding(start = DEFAULT_PADDING_HALF, top = 32.dp, end = DEFAULT_PADDING_HALF, bottom = 30.dp))
} else if (!chatModel.desktopNoUserNoRemote) {
Divider(Modifier.padding(start = DEFAULT_PADDING_HALF, top = 24.dp, end = DEFAULT_PADDING_HALF, bottom = 30.dp))
}
if (currentRemoteHost == null) {
SectionView(generalGetString(MR.strings.settings_section_title_private_message_routing)) {
SMPProxyModePicker(smpProxyMode, showModal, updateSMPProxyMode)
SMPProxyFallbackPicker(smpProxyFallback, showModal, updateSMPProxyFallback, enabled = remember { mutableStateOf(smpProxyMode.value != SMPProxyMode.Never) })
SettingsPreferenceItem(painterResource(MR.images.ic_arrow_forward), stringResource(MR.strings.private_routing_show_message_status), chatModel.controller.appPrefs.showSentViaProxy)
}
SectionCustomFooter {
Text(stringResource(MR.strings.private_routing_explanation))
}
Divider(Modifier.padding(start = DEFAULT_PADDING_HALF, top = 32.dp, end = DEFAULT_PADDING_HALF, bottom = 30.dp))
}
SectionView(generalGetString(MR.strings.settings_section_title_calls)) {
SettingsActionItem(painterResource(MR.images.ic_electrical_services), stringResource(MR.strings.webrtc_ice_servers), { ModalManager.start.showModal { RTCServersView(m) } })
@@ -313,13 +160,14 @@ fun NetworkAndServersView() {
onionHosts: MutableState<OnionHosts>,
sessionMode: MutableState<TransportSessionMode>,
networkProxyHostPort: SharedPreference<String?>,
proxyPort: State<Int>,
toggleSocksProxy: (Boolean) -> Unit,
useOnion: (OnionHosts) -> Unit,
updateSessionMode: (TransportSessionMode) -> Unit,
) {
val showModal = { it: @Composable ModalData.() -> Unit -> ModalManager.fullscreen.showModal(content = it) }
UseSocksProxySwitch(networkUseSocksProxy, proxyPort, toggleSocksProxy, showModal, networkProxyHostPort, true)
val showCustomModal = { it: @Composable (close: () -> Unit) -> Unit -> ModalManager.fullscreen.showCustomModal { close -> it(close) }}
UseSocksProxySwitch(networkUseSocksProxy, toggleSocksProxy)
SettingsActionItem(painterResource(MR.images.ic_settings_ethernet), stringResource(MR.strings.network_socks_proxy_settings), { showCustomModal { SocksProxySettings(networkUseSocksProxy.value, networkProxyHostPort, true, it) } })
UseOnionHosts(onionHosts, networkUseSocksProxy, showModal, useOnion)
if (developerTools) {
SessionModePicker(sessionMode, showModal, updateSessionMode)
@@ -329,11 +177,7 @@ fun NetworkAndServersView() {
@Composable
fun UseSocksProxySwitch(
networkUseSocksProxy: MutableState<Boolean>,
proxyPort: State<Int>,
toggleSocksProxy: (Boolean) -> Unit,
showModal: (@Composable ModalData.() -> Unit) -> Unit,
networkProxyHostPort: SharedPreference<String?> = chatModel.controller.appPrefs.networkProxyHostPort,
migration: Boolean = false,
) {
Row(
Modifier.fillMaxWidth().padding(end = DEFAULT_PADDING),
@@ -350,32 +194,7 @@ fun UseSocksProxySwitch(
tint = MaterialTheme.colors.secondary
)
TextIconSpaced(false)
val text = buildAnnotatedString {
append(generalGetString(MR.strings.network_socks_toggle_use_socks_proxy) + " (")
val style = SpanStyle(color = MaterialTheme.colors.primary)
val disabledStyle = SpanStyle(color = MaterialTheme.colors.onBackground)
withAnnotation(tag = "PORT", annotation = generalGetString(MR.strings.network_proxy_port).format(proxyPort.value)) {
withStyle(if (networkUseSocksProxy.value || !migration) style else disabledStyle) {
append(generalGetString(MR.strings.network_proxy_port).format(proxyPort.value))
}
}
append(")")
}
ClickableText(
text,
style = TextStyle(color = MaterialTheme.colors.onBackground, fontSize = 16.sp, fontFamily = Inter, fontWeight = FontWeight.Normal),
onClick = { offset ->
text.getStringAnnotations(tag = "PORT", start = offset, end = offset)
.firstOrNull()?.let { _ ->
if (networkUseSocksProxy.value || !migration) {
showModal { SockProxySettings(chatModel, networkProxyHostPort, migration) }
}
}
},
shouldConsumeEvent = { offset ->
text.getStringAnnotations(tag = "PORT", start = offset, end = offset).any()
}
)
Text(generalGetString(MR.strings.network_socks_toggle_use_socks_proxy))
}
DefaultSwitch(
checked = networkUseSocksProxy.value,
@@ -385,94 +204,124 @@ fun UseSocksProxySwitch(
}
@Composable
fun SockProxySettings(
m: ChatModel,
networkProxyHostPort: SharedPreference<String?> = m.controller.appPrefs.networkProxyHostPort,
fun SocksProxySettings(
networkUseSocksProxy: Boolean,
networkProxyHostPort: SharedPreference<String?> = appPrefs.networkProxyHostPort,
migration: Boolean,
close: () -> Unit
) {
ColumnWithScrollBar(
Modifier
.fillMaxWidth()
) {
val defaultHostPort = remember { "localhost:9050" }
AppBarTitle(generalGetString(MR.strings.network_socks_proxy_settings))
val hostPortSaved by remember { networkProxyHostPort.state }
val hostUnsaved = rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(TextFieldValue(hostPortSaved?.split(":")?.firstOrNull() ?: "localhost"))
}
val portUnsaved = rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(TextFieldValue(hostPortSaved?.split(":")?.lastOrNull() ?: "9050"))
}
val save = {
val defaultHostPort = remember { "localhost:9050" }
val hostPortSaved by remember { networkProxyHostPort.state }
val hostUnsaved = rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(TextFieldValue(hostPortSaved?.split(":")?.firstOrNull() ?: "localhost"))
}
val portUnsaved = rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(TextFieldValue(hostPortSaved?.split(":")?.lastOrNull() ?: "9050"))
}
val save = {
val oldValue = networkProxyHostPort.get()
networkProxyHostPort.set(hostUnsaved.value.text + ":" + portUnsaved.value.text)
if (networkUseSocksProxy && !migration) {
withBGApi {
networkProxyHostPort.set(hostUnsaved.value.text + ":" + portUnsaved.value.text)
if (m.controller.appPrefs.networkUseSocksProxy.get() && !migration) {
m.controller.apiSetNetworkConfig(m.controller.getNetCfg())
if (!controller.apiSetNetworkConfig(controller.getNetCfg())) {
networkProxyHostPort.set(oldValue)
}
}
}
SectionView {
SectionItemView {
ResetToDefaultsButton({
val reset = {
networkProxyHostPort.set(defaultHostPort)
val newHost = defaultHostPort.split(":").first()
val newPort = defaultHostPort.split(":").last()
hostUnsaved.value = hostUnsaved.value.copy(newHost, TextRange(newHost.length))
portUnsaved.value = portUnsaved.value.copy(newPort, TextRange(newPort.length))
save()
}
if (m.controller.appPrefs.networkUseSocksProxy.get() && !migration) {
showUpdateNetworkSettingsDialog {
reset()
}
} else {
reset()
}
}, disabled = hostPortSaved == defaultHostPort)
}
SectionItemView {
DefaultConfigurableTextField(
hostUnsaved,
stringResource(MR.strings.host_verb),
modifier = Modifier.fillMaxWidth(),
isValid = ::validHost,
keyboardActions = KeyboardActions(onNext = { defaultKeyboardAction(ImeAction.Next) }),
keyboardType = KeyboardType.Text,
)
}
SectionItemView {
DefaultConfigurableTextField(
portUnsaved,
stringResource(MR.strings.port_verb),
modifier = Modifier.fillMaxWidth(),
isValid = ::validPort,
keyboardActions = KeyboardActions(onDone = { defaultKeyboardAction(ImeAction.Done); save() }),
keyboardType = KeyboardType.Number,
)
}
val saveAndClose = {
val oldValue = networkProxyHostPort.get()
networkProxyHostPort.set(hostUnsaved.value.text + ":" + portUnsaved.value.text)
if (networkUseSocksProxy && !migration) {
withBGApi {
if (controller.apiSetNetworkConfig(controller.getNetCfg())) {
close()
} else {
networkProxyHostPort.set(oldValue)
}
}
}
SectionCustomFooter {
NetworkSectionFooter(
revert = {
val prevHost = hostPortSaved?.split(":")?.firstOrNull() ?: "localhost"
val prevPort = hostPortSaved?.split(":")?.lastOrNull() ?: "9050"
hostUnsaved.value = hostUnsaved.value.copy(prevHost, TextRange(prevHost.length))
portUnsaved.value = portUnsaved.value.copy(prevPort, TextRange(prevPort.length))
},
save = { if (m.controller.appPrefs.networkUseSocksProxy.get() && !migration) showUpdateNetworkSettingsDialog { save() } else save() },
revertDisabled = hostPortSaved == (hostUnsaved.value.text + ":" + portUnsaved.value.text),
saveDisabled = hostPortSaved == (hostUnsaved.value.text + ":" + portUnsaved.value.text) ||
remember { derivedStateOf { !validHost(hostUnsaved.value.text) } }.value ||
remember { derivedStateOf { !validPort(portUnsaved.value.text) } }.value
)
}
val saveDisabled = hostPortSaved == (hostUnsaved.value.text + ":" + portUnsaved.value.text) ||
remember { derivedStateOf { !validHost(hostUnsaved.value.text) } }.value ||
remember { derivedStateOf { !validPort(portUnsaved.value.text) } }.value
val resetDisabled = hostUnsaved.value.text + ":" + portUnsaved.value.text == defaultHostPort
ModalView(
close = {
if (saveDisabled) {
close()
} else {
showUnsavedSocksHostPortAlert(
confirmText = generalGetString(if (networkUseSocksProxy && !migration) MR.strings.network_options_save_and_reconnect else MR.strings.network_options_save),
save = saveAndClose,
close = close
)
}
},
) {
ColumnWithScrollBar(
Modifier
.fillMaxWidth()
) {
AppBarTitle(generalGetString(MR.strings.network_socks_proxy_settings))
SectionView {
SectionItemView {
DefaultConfigurableTextField(
hostUnsaved,
stringResource(MR.strings.host_verb),
modifier = Modifier.fillMaxWidth(),
isValid = ::validHost,
keyboardActions = KeyboardActions(onNext = { defaultKeyboardAction(ImeAction.Next) }),
keyboardType = KeyboardType.Text,
)
}
SectionItemView {
DefaultConfigurableTextField(
portUnsaved,
stringResource(MR.strings.port_verb),
modifier = Modifier.fillMaxWidth(),
isValid = ::validPort,
keyboardActions = KeyboardActions(onDone = { defaultKeyboardAction(ImeAction.Done); save() }),
keyboardType = KeyboardType.Number,
)
}
}
Divider(Modifier.padding(start = DEFAULT_PADDING_HALF, top = 27.dp, end = DEFAULT_PADDING_HALF, bottom = 30.dp))
SectionView {
SectionItemView({
val newHost = defaultHostPort.split(":").first()
val newPort = defaultHostPort.split(":").last()
hostUnsaved.value = hostUnsaved.value.copy(newHost, TextRange(newHost.length))
portUnsaved.value = portUnsaved.value.copy(newPort, TextRange(newPort.length))
}, disabled = resetDisabled) {
Text(stringResource(MR.strings.network_options_reset_to_defaults), color = if (resetDisabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary)
}
SectionItemView(
click = { if (networkUseSocksProxy && !migration) showUpdateNetworkSettingsDialog { save() } else save() },
disabled = saveDisabled
) {
Text(stringResource(if (networkUseSocksProxy && !migration) MR.strings.network_options_save_and_reconnect else MR.strings.network_options_save), color = if (saveDisabled) MaterialTheme.colors.secondary else MaterialTheme.colors.primary)
}
}
SectionBottomSpacer()
}
SectionBottomSpacer()
}
}
private fun showUnsavedSocksHostPortAlert(confirmText: String, save: () -> Unit, close: () -> Unit) {
AlertManager.shared.showAlertDialogStacked(
title = generalGetString(MR.strings.update_network_settings_question),
confirmText = confirmText,
dismissText = generalGetString(MR.strings.exit_without_saving),
onConfirm = save,
onDismiss = close,
)
}
@Composable
private fun UseOnionHosts(
fun UseOnionHosts(
onionHosts: MutableState<OnionHosts>,
enabled: State<Boolean>,
showModal: (@Composable ModalData.() -> Unit) -> Unit,
@@ -521,7 +370,7 @@ private fun UseOnionHosts(
}
@Composable
private fun SessionModePicker(
fun SessionModePicker(
sessionMode: MutableState<TransportSessionMode>,
showModal: (@Composable ModalData.() -> Unit) -> Unit,
updateSessionMode: (TransportSessionMode) -> Unit,
@@ -554,91 +403,6 @@ private fun SessionModePicker(
)
}
@Composable
private fun SMPProxyModePicker(
smpProxyMode: MutableState<SMPProxyMode>,
showModal: (@Composable ModalData.() -> Unit) -> Unit,
updateSMPProxyMode: (SMPProxyMode) -> Unit,
) {
val density = LocalDensity.current
val values = remember {
SMPProxyMode.values().map {
when (it) {
SMPProxyMode.Always -> ValueTitleDesc(SMPProxyMode.Always, generalGetString(MR.strings.network_smp_proxy_mode_always), escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_smp_proxy_mode_always_description), density))
SMPProxyMode.Unknown -> ValueTitleDesc(SMPProxyMode.Unknown, generalGetString(MR.strings.network_smp_proxy_mode_unknown), escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_smp_proxy_mode_unknown_description), density))
SMPProxyMode.Unprotected -> ValueTitleDesc(SMPProxyMode.Unprotected, generalGetString(MR.strings.network_smp_proxy_mode_unprotected), escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_smp_proxy_mode_unprotected_description), density))
SMPProxyMode.Never -> ValueTitleDesc(SMPProxyMode.Never, generalGetString(MR.strings.network_smp_proxy_mode_never), escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_smp_proxy_mode_never_description), density))
}
}
}
SectionItemWithValue(
generalGetString(MR.strings.network_smp_proxy_mode_private_routing),
smpProxyMode,
values,
icon = painterResource(MR.images.ic_settings_ethernet),
onSelected = {
showModal {
ColumnWithScrollBar(
Modifier.fillMaxWidth(),
) {
AppBarTitle(stringResource(MR.strings.network_smp_proxy_mode_private_routing))
SectionViewSelectableCards(null, smpProxyMode, values, updateSMPProxyMode)
}
}
}
)
}
@Composable
private fun SMPProxyFallbackPicker(
smpProxyFallback: MutableState<SMPProxyFallback>,
showModal: (@Composable ModalData.() -> Unit) -> Unit,
updateSMPProxyFallback: (SMPProxyFallback) -> Unit,
enabled: State<Boolean>,
) {
val density = LocalDensity.current
val values = remember {
SMPProxyFallback.values().map {
when (it) {
SMPProxyFallback.Allow -> ValueTitleDesc(SMPProxyFallback.Allow, generalGetString(MR.strings.network_smp_proxy_fallback_allow), escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_smp_proxy_fallback_allow_description), density))
SMPProxyFallback.AllowProtected -> ValueTitleDesc(SMPProxyFallback.AllowProtected, generalGetString(MR.strings.network_smp_proxy_fallback_allow_protected), escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_smp_proxy_fallback_allow_protected_description), density))
SMPProxyFallback.Prohibit -> ValueTitleDesc(SMPProxyFallback.Prohibit, generalGetString(MR.strings.network_smp_proxy_fallback_prohibit), escapedHtmlToAnnotatedString(generalGetString(MR.strings.network_smp_proxy_fallback_prohibit_description), density))
}
}
}
SectionItemWithValue(
generalGetString(MR.strings.network_smp_proxy_fallback_allow_downgrade),
smpProxyFallback,
values,
icon = painterResource(MR.images.ic_arrows_left_right),
enabled = enabled,
onSelected = {
showModal {
ColumnWithScrollBar(
Modifier.fillMaxWidth(),
) {
AppBarTitle(stringResource(MR.strings.network_smp_proxy_fallback_allow_downgrade))
SectionViewSelectableCards(null, smpProxyFallback, values, updateSMPProxyFallback)
}
}
}
)
}
@Composable
private fun NetworkSectionFooter(revert: () -> Unit, save: () -> Unit, revertDisabled: Boolean, saveDisabled: Boolean) {
Row(
Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
FooterButton(painterResource(MR.images.ic_replay), stringResource(MR.strings.network_options_revert), revert, revertDisabled)
FooterButton(painterResource(MR.images.ic_check), stringResource(MR.strings.network_options_save), save, saveDisabled)
}
}
// https://stackoverflow.com/a/106223
private fun validHost(s: String): Boolean {
val validIp = Regex("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])[.]){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$")
@@ -652,7 +416,7 @@ fun validPort(s: String): Boolean {
return s.isNotBlank() && s.matches(validPort)
}
private fun showUpdateNetworkSettingsDialog(
fun showUpdateNetworkSettingsDialog(
title: String,
startsWith: String = "",
message: String = generalGetString(MR.strings.updating_settings_will_reconnect_client_to_all_servers),
@@ -675,18 +439,8 @@ fun PreviewNetworkAndServersLayout() {
SimpleXTheme {
NetworkAndServersLayout(
currentRemoteHost = null,
developerTools = true,
networkUseSocksProxy = remember { mutableStateOf(true) },
proxyPort = remember { mutableStateOf(9050) },
toggleSocksProxy = {},
onionHosts = remember { mutableStateOf(OnionHosts.PREFER) },
sessionMode = remember { mutableStateOf(TransportSessionMode.User) },
smpProxyMode = remember { mutableStateOf(SMPProxyMode.Never) },
smpProxyFallback = remember { mutableStateOf(SMPProxyFallback.Allow) },
useOnion = {},
updateSessionMode = {},
updateSMPProxyMode = {},
updateSMPProxyFallback = {},
)
}
}
@@ -112,7 +112,7 @@ fun SettingsLayout(
Modifier
.fillMaxSize()
.themedBackground(theme.value.base)
.padding(top = if (appPlatform.isAndroid) DEFAULT_PADDING else DEFAULT_PADDING * 3)
.padding(top = if (appPlatform.isAndroid) DEFAULT_PADDING else DEFAULT_PADDING * 2.8f)
) {
AppBarTitle(stringResource(MR.strings.your_settings))
@@ -773,7 +773,6 @@
<string name="old_database_archive">أرشيف قاعدة البيانات القديمة</string>
<string name="feature_off">غير مفعّل</string>
<string name="one_time_link">رابط دعوة لمرة واحدة</string>
<string name="network_use_onion_hosts_required_desc_in_alert">سوف تكون مضيفات البصل مطلوبة للاتصال.</string>
<string name="group_member_role_observer">المراقب</string>
<string name="item_info_no_text">لا يوجد نص</string>
<string name="new_member_role">دور عضو جديد</string>
@@ -789,8 +788,6 @@
<string name="network_use_onion_hosts_no">لا</string>
<string name="network_use_onion_hosts_required_desc">سوف تكون مضيفات البصل مطلوبة للاتصال.
\nيُرجى ملاحظة: أنك لن تتمكن من الاتصال بالخوادم بدون عنوان onion.</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">سيتم استخدام مضيفات البصل عند توفرها.</string>
<string name="network_use_onion_hosts_no_desc_in_alert">لن يتم استخدام مضيفات البصل.</string>
<string name="self_destruct_new_display_name">اسم عرض جديد:</string>
<string name="new_passphrase">عبارة مرور جديدة…</string>
<string name="icon_descr_server_status_pending">قيد الانتظار</string>
@@ -892,7 +889,6 @@
<string name="info_row_updated_at">حٌديثت السجل في</string>
<string name="share_text_updated_at">حٌديثت السجل في: %s</string>
<string name="restore_database_alert_confirm">استعادة</string>
<string name="network_options_revert">إرجاع</string>
<string name="v4_4_live_messages_desc">يرى المستلمون التحديثات أثناء كتابتها.</string>
<string name="feature_received_prohibited">استلمت، ممنوع</string>
<string name="save_servers_button">حفظ</string>
@@ -1165,7 +1161,6 @@
<string name="your_chat_profiles">ملفات تعريف الدردشة الخاصة بك</string>
<string name="your_simplex_contact_address">عنوان SimpleX الخاص بك</string>
<string name="your_SMP_servers">خوادم SMP الخاصة بك</string>
<string name="update_onion_hosts_settings_question">هل تريد تحديث إعداد مضيفي onion.؟</string>
<string name="onboarding_notifications_mode_off">عندما يكون التطبيق قيد التشغيل</string>
<string name="call_connection_via_relay">عبر المُرحل</string>
<string name="you_joined_this_group">لقد انضممت إلى هذه المجموعة</string>
@@ -707,6 +707,7 @@
<string name="send_us_an_email">Send us email</string>
<string name="chat_lock">SimpleX Lock</string>
<string name="chat_console">Chat console</string>
<string name="message_servers">Message servers</string>
<string name="smp_servers">SMP servers</string>
<string name="smp_servers_configured">Configured SMP servers</string>
<string name="smp_servers_other">Other SMP servers</string>
@@ -731,6 +732,8 @@
<string name="smp_servers_delete_server">Delete server</string>
<string name="smp_servers_per_user">The servers for new connections of your current chat profile</string>
<string name="smp_save_servers_question">Save servers?</string>
<string name="update_network_settings_question">Update network settings?</string>
<string name="media_and_file_servers">Media &amp; file servers</string>
<string name="xftp_servers">XFTP servers</string>
<string name="xftp_servers_configured">Configured XFTP servers</string>
<string name="xftp_servers_other">Other XFTP servers</string>
@@ -754,7 +757,8 @@
<string name="save_servers_button">Save</string>
<string name="network_and_servers">Network &amp; servers</string>
<string name="network_settings">Advanced network settings</string>
<string name="network_settings_title">Network settings</string>
<string name="network_settings_title">Advanced settings</string>
<string name="network_socks_proxy">SOCKS proxy</string>
<string name="network_socks_proxy_settings">SOCKS proxy settings</string>
<string name="network_socks_toggle_use_socks_proxy">Use SOCKS proxy</string>
<string name="network_proxy_port">port %d</string>
@@ -764,7 +768,6 @@
<string name="network_enable_socks_info">Access the servers via SOCKS proxy on port %d? Proxy must be started before enabling this option.</string>
<string name="network_disable_socks">Use direct Internet connection?</string>
<string name="network_disable_socks_info">If you confirm, the messaging servers will be able to see your IP address, and your provider - which servers you are connecting to.</string>
<string name="update_onion_hosts_settings_question">Update .onion hosts setting?</string>
<string name="network_use_onion_hosts">Use .onion hosts</string>
<string name="network_use_onion_hosts_prefer">When available</string>
<string name="network_use_onion_hosts_no">No</string>
@@ -772,9 +775,6 @@
<string name="network_use_onion_hosts_prefer_desc">Onion hosts will be used when available.</string>
<string name="network_use_onion_hosts_no_desc">Onion hosts will not be used.</string>
<string name="network_use_onion_hosts_required_desc">Onion hosts will be required for connection.\nPlease note: you will not be able to connect to the servers without .onion address.</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Onion hosts will be used when available.</string>
<string name="network_use_onion_hosts_no_desc_in_alert">Onion hosts will not be used.</string>
<string name="network_use_onion_hosts_required_desc_in_alert">Onion hosts will be required for connection.</string>
<string name="network_session_mode_transport_isolation">Transport isolation</string>
<string name="network_session_mode_user">Chat profile</string>
<string name="network_session_mode_entity">Connection</string>
@@ -785,7 +785,7 @@
<string name="socks_proxy_setting_limitations"><![CDATA[<b>Please note</b>: message and file relays are connected via SOCKS proxy. Calls and sending link previews use direct connection.]]></string>
<string name="network_smp_proxy_mode_private_routing">Private routing</string>
<string name="network_smp_proxy_mode_always">Always</string>
<string name="network_smp_proxy_mode_unknown">Unknown relays</string>
<string name="network_smp_proxy_mode_unknown">Unknown servers</string>
<string name="network_smp_proxy_mode_unprotected">Unprotected</string>
<string name="network_smp_proxy_mode_never">Never</string>
<string name="network_smp_proxy_mode_always_description">Always use private routing.</string>
@@ -1196,7 +1196,7 @@
<string name="error_importing_database">Error importing chat database</string>
<string name="chat_database_imported">Chat database imported</string>
<string name="restart_the_app_to_use_imported_chat_database">Restart the app to use imported chat database.</string>
<string name="non_fatal_errors_occured_during_import">Some non-fatal errors occurred during import - you may see Chat console for more details.</string>
<string name="non_fatal_errors_occured_during_import">Some non-fatal errors occurred during import:</string>
<string name="delete_chat_profile_question">Delete chat profile?</string>
<string name="delete_chat_profile_action_cannot_be_undone_warning">This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost.</string>
<string name="chat_database_deleted">Chat database deleted</string>
@@ -1222,6 +1222,11 @@
<string name="enable_automatic_deletion_message">This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes.</string>
<string name="delete_messages">Delete messages</string>
<string name="error_changing_message_deletion">Error changing setting</string>
<string name="chat_database_exported_title">Chat database exported</string>
<string name="chat_database_exported_save">You may save the exported archive.</string>
<string name="chat_database_exported_migrate">You may migrate the exported database.</string>
<string name="chat_database_exported_not_all_files">Some file(s) were not exported</string>
<string name="chat_database_exported_continue">Continue</string>
<!-- DatabaseEncryptionView.kt -->
<string name="save_passphrase_in_keychain">Save passphrase in Keystore</string>
@@ -1607,6 +1612,7 @@
<string name="error_saving_group_profile">Error saving group profile</string>
<!-- AdvancedNetworkSettings.kt -->
<string name="network_option_tcp_connection">TCP connection</string>
<string name="network_options_reset_to_defaults">Reset to defaults</string>
<string name="network_option_seconds_label">sec</string>
<string name="network_option_tcp_connection_timeout">TCP connection timeout</string>
@@ -1616,8 +1622,8 @@
<string name="network_option_ping_interval">PING interval</string>
<string name="network_option_ping_count">PING count</string>
<string name="network_option_enable_tcp_keep_alive">Enable TCP keep-alive</string>
<string name="network_options_revert">Revert</string>
<string name="network_options_save">Save</string>
<string name="network_options_save_and_reconnect">Save and reconnect</string>
<string name="update_network_settings_question">Update network settings?</string>
<string name="updating_settings_will_reconnect_client_to_all_servers">Updating settings will re-connect the client to all servers.</string>
<string name="update_network_settings_confirmation">Update</string>
@@ -800,13 +800,11 @@
<string name="network_settings_title">Мрежови настройки</string>
<string name="port_verb">Порт</string>
<string name="network_proxy_port">порт %d</string>
<string name="network_use_onion_hosts_no_desc_in_alert">Няма се използват Onion хостове.</string>
<string name="network_use_onion_hosts_required">Задължително</string>
<string name="network_use_onion_hosts_no">Не</string>
<string name="network_use_onion_hosts_required_desc">За свързване ще са необходими Onion хостове.
\nМоля, обърнете внимание: няма да можете да се свържете със сървърите без .onion адрес.</string>
<string name="network_use_onion_hosts_prefer_desc">Ще се използват Onion хостове, когато са налични.</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Ще се използват Onion хостове, когато са налични.</string>
<string name="network_use_onion_hosts_no_desc">Няма се използват Onion хостове.</string>
<string name="email_invite_subject">Нека да поговорим в SimpleX Chat</string>
<string name="password_to_show">Парола за показване</string>
@@ -878,7 +876,6 @@
<string name="no_selected_chat">Няма избран чат</string>
<string name="notifications">Известия</string>
<string name="shutdown_alert_desc">Известията ще спрат да работят, докато не стартирате отново приложението</string>
<string name="network_use_onion_hosts_required_desc_in_alert">За свързване ще са необходими Onion хостове.</string>
<string name="images_limit_desc">Само 10 изображения могат да бъдат изпратени едновременно</string>
<string name="only_owners_can_enable_files_and_media">Само собствениците на групата могат да активират файлове и медията.</string>
<string name="only_group_owners_can_enable_voice">Само собствениците на групата могат да активират гласови съобщения.</string>
@@ -1147,7 +1144,6 @@
<string name="icon_descr_speaker_off">Високоговорителят е изключен</string>
<string name="stop_chat_to_enable_database_actions">Спрете чата, за да активирате действията с базата данни.</string>
<string name="role_in_group">Роля</string>
<string name="network_options_revert">Отмени промените</string>
<string name="network_options_save">Запази</string>
<string name="reset_color">Нулирай цветовете</string>
<string name="color_secondary">Вторичен</string>
@@ -1225,7 +1221,6 @@
<string name="your_SMP_servers">Вашите SMP сървъри</string>
<string name="network_socks_toggle_use_socks_proxy">Използвай SOCKS прокси</string>
<string name="network_enable_socks">Използвай SOCKS прокси\?</string>
<string name="update_onion_hosts_settings_question">Актуализиране на настройката за .onion хостове\?</string>
<string name="network_disable_socks">Използване на директна интернет връзка\?</string>
<string name="network_use_onion_hosts">Използвай .onion хостове</string>
<string name="network_use_onion_hosts_prefer">Когато са налични</string>
@@ -228,7 +228,6 @@
<string name="network_enable_socks">Použít proxy server SOCKS\?</string>
<string name="network_disable_socks">Použít přímé připojení k internetu\?</string>
<string name="network_use_onion_hosts_no">Ne</string>
<string name="network_use_onion_hosts_no_desc_in_alert">Onion hostitelé nebudou použiti.</string>
<string name="network_session_mode_user">Chat profil</string>
<string name="network_session_mode_entity">Připojení</string>
<string name="core_simplexmq_version">simplexmq: v%s (%2s)</string>
@@ -357,7 +356,6 @@
<string name="no_contacts_selected">Nebyl vybrán žádný kontakt</string>
<string name="invite_prohibited_description">Snažíte se pozvat kontakt, se kterým jste sdíleli inkognito profil, do skupiny, ve které používáte svůj hlavní profil</string>
<string name="info_row_group">Skupina</string>
<string name="network_options_revert">Vrátit</string>
<string name="updating_settings_will_reconnect_client_to_all_servers">Aktualizací nastavení se klient znovu připojí ke všem serverům.</string>
<string name="accept_feature_set_1_day">Nastavit 1 den</string>
<string name="connection_error_auth">Chyba spojení (AUTH)</string>
@@ -629,14 +627,11 @@
<string name="ensure_ICE_server_address_are_correct_format_and_unique">Ujistěte se, že adresy serverů WebRTC ICE jsou ve správném formátu, oddělené na řádcích a nejsou duplicitní.</string>
<string name="save_servers_button">Uložit</string>
<string name="network_and_servers">Síť a servery</string>
<string name="update_onion_hosts_settings_question">Aktualizovat nastavení hostitelů .onion\?</string>
<string name="network_use_onion_hosts">Použít hostitele .onion</string>
<string name="network_use_onion_hosts_prefer">Když bude dostupný</string>
<string name="network_use_onion_hosts_required">Povinné</string>
<string name="network_use_onion_hosts_prefer_desc">Onion hostitelé budou použiti, pokud jsou k dispozici.</string>
<string name="network_use_onion_hosts_no_desc">Onion hostitelé nebudou použiti.</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Onion hostitelé budou použiti, pokud jsou k dispozici.</string>
<string name="network_use_onion_hosts_required_desc_in_alert">Pro připojení budou vyžadováni Onion hostitelé.</string>
<string name="network_session_mode_transport_isolation">Izolace přenosu</string>
<string name="network_session_mode_user_description"><![CDATA[A separate TCP connection (and SOCKS credential) will be used <b>for each chat profile you have in the app</b>.]]></string>
<string name="network_session_mode_entity_description">Oddělit TCP připojení (a SOCKS pověření) bude použito <b>pro všechny kontakty a členy skupin</b>.
@@ -396,7 +396,6 @@
<string name="network_enable_socks_info">Zugriff auf die Server über SOCKS-Proxy auf Port %d? Der Proxy muss gestartet werden, bevor diese Option aktiviert wird.</string>
<string name="network_disable_socks">Direkte Internetverbindung verwenden?</string>
<string name="network_disable_socks_info">Wenn Sie dies bestätigen, können die Messaging-Server Ihre IP-Adresse sowie Ihren Provider sehen und mit welchen Servern Sie sich verbinden.</string>
<string name="update_onion_hosts_settings_question">Einstellung für .onion-Hosts aktualisieren?</string>
<string name="network_use_onion_hosts">Verwende .onion-Hosts</string>
<string name="network_use_onion_hosts_prefer">Wenn verfügbar</string>
<string name="network_use_onion_hosts_no">Nein</string>
@@ -405,9 +404,6 @@
<string name="network_use_onion_hosts_no_desc">Onion-Hosts werden nicht verwendet.</string>
<string name="network_use_onion_hosts_required_desc">Für die Verbindung werden Onion-Hosts benötigt.
\nBitte beachten Sie: Ohne .onion-Adresse können Sie keine Verbindung mit den Servern herstellen.</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Wenn Onion-Hosts verfügbar sind, werden sie verwendet.</string>
<string name="network_use_onion_hosts_no_desc_in_alert">Onion-Hosts werden nicht verwendet.</string>
<string name="network_use_onion_hosts_required_desc_in_alert">Für die Verbindung werden Onion-Hosts benötigt.</string>
<string name="appearance_settings">Erscheinungsbild</string>
<!-- Address Items - UserAddressView.kt -->
<string name="create_address">Adresse erstellen</string>
@@ -826,7 +822,6 @@
<string name="network_option_protocol_timeout">Protokollzeitüberschreitung</string>
<string name="network_option_ping_interval">PING-Intervall</string>
<string name="network_option_enable_tcp_keep_alive">TCP-Keep-Alive aktivieren</string>
<string name="network_options_revert">Zurücksetzen</string>
<string name="network_options_save">Speichern</string>
<string name="update_network_settings_question">Netzwerkeinstellungen aktualisieren?</string>
<string name="updating_settings_will_reconnect_client_to_all_servers">Die Aktualisierung der Einstellungen wird den Client wieder mit allen Servern verbinden.</string>
@@ -447,7 +447,6 @@
<string name="ensure_smp_server_address_are_correct_format_and_unique">Asegúrate de que las direcciones del servidor SMP tienen el formato correcto, están separadas por líneas y no están duplicadas.</string>
<string name="icon_descr_instant_notifications">Notificación instantánea</string>
<string name="network_settings_title">Configuración de red</string>
<string name="network_use_onion_hosts_no_desc_in_alert">No se usarán hosts .onion</string>
<string name="only_client_devices_store_contacts_groups_e2e_encrypted_messages"><![CDATA[Sólo los dispositivos cliente almacenan perfiles de usuario, contactos, grupos y mensajes enviados con <b>cifrado de extremo a extremo de 2 capas</b> .]]></string>
<string name="onboarding_notifications_mode_subtitle">Puedes cambiar estos ajustes más tarde en Configuración.</string>
<string name="onboarding_notifications_mode_service">Instantánea</string>
@@ -466,7 +465,6 @@
<string name="notification_preview_mode_message">Contacto y texto</string>
<string name="member_info_section_title_member">MIEMBRO</string>
<string name="chat_item_ttl_none">nunca</string>
<string name="network_use_onion_hosts_required_desc_in_alert">Se requieren hosts .onion para la conexión</string>
<string name="network_use_onion_hosts_no_desc">No se usarán hosts .onion</string>
<string name="settings_notification_preview_title">Vista previa de notificaciones</string>
<string name="alert_title_group_invitation_expired">¡Invitación caducada!</string>
@@ -511,7 +509,6 @@
<string name="ensure_ICE_server_address_are_correct_format_and_unique">Asegúrate de que las direcciones del servidor WebRTC ICE tienen el formato correcto, están separadas por líneas y no duplicadas.</string>
<string name="network_use_onion_hosts_required_desc">Se requieren hosts .onion para la conexión
\nRecuerda: no podrás conectarte a servidores que no tengan dirección .onion.</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Se usarán hosts .onion si están disponibles.</string>
<string name="immune_to_spam_and_abuse">Inmune a spam y abuso</string>
<string name="many_people_asked_how_can_it_deliver"><![CDATA[Muchos se preguntarán: <i>si SimpleX no tiene identificadores de usuario, ¿cómo puede entregar los mensajes\?</i>]]></string>
<string name="incoming_video_call">Videollamada entrante</string>
@@ -636,7 +633,6 @@
<string name="save_and_notify_contacts">Guardar y notificar contactos</string>
<string name="opensource_protocol_and_code_anybody_can_run_servers">Protocolo y código abiertos: cualquiera puede usar los servidores.</string>
<string name="role_in_group">Rol</string>
<string name="network_options_revert">Revertir</string>
<string name="network_option_ping_interval">Intervalo PING</string>
<string name="network_option_ping_count">Contador PING</string>
<string name="only_your_contact_can_delete">Sólo tu contacto puede eliminar mensajes de forma irreversible (tu puedes marcarlos para eliminar). (24 horas)</string>
@@ -800,7 +796,6 @@
<string name="star_on_github">Estrella en GitHub</string>
<string name="smp_servers_per_user">Lista de servidores para las conexiones nuevas de tu perfil actual</string>
<string name="network_disable_socks">¿Usar conexión directa a Internet\?</string>
<string name="update_onion_hosts_settings_question">¿Actualizar la configuración de los hosts .onion\?</string>
<string name="profile_is_only_shared_with_your_contacts">El perfil sólo se comparte con tus contactos.</string>
<string name="callstate_starting">inicializando…</string>
<string name="alert_title_skipped_messages">Mensajes omitidos</string>
@@ -588,7 +588,6 @@
<string name="network_use_onion_hosts_required_desc">میزبان‌های Onion برای اتصال الزامی خواهد بود.
\nلطفا توجه داشته باشید: شما بدون نشانی‌ onion. قادر نخواهید بود به سرورها متصل شوید.</string>
<string name="save_servers_button">ذخیره</string>
<string name="update_onion_hosts_settings_question">تنظیمات میزبان‌های onion. به روز شود؟</string>
<string name="port_verb">پورت</string>
<string name="network_enable_socks">از پروکسی SOCKS استفاده شود؟</string>
<string name="network_disable_socks">از اتصال مستقیم اینترنت استفاده شود؟</string>
@@ -597,7 +596,6 @@
<string name="host_verb">هاست</string>
<string name="network_use_onion_hosts_required">الزامی</string>
<string name="network_use_onion_hosts_prefer_desc">از میزبان‌های Onion وقتی موجود باشند استفاده خواهد شد.</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">از میزبان‌های Onion وقتی موجود باشند استفاده خواهد شد.</string>
<string name="appearance_settings">ظاهر</string>
<string name="app_version_name">نسخه برنامه: v%s</string>
<string name="show_developer_options">نمایش گزینه‌های توسعه‌دهنده</string>
@@ -608,7 +606,6 @@
<string name="network_session_mode_user_description"><![CDATA[یک اتصال جدای TCP (و اطلاعات ورود SOCKS) <b>برای هر نمایه گپی که در برنامه دارید</b> استفاده خواهد شد.]]></string>
<string name="show_internal_errors">نمایش خطاهای داخلی</string>
<string name="show_slow_api_calls">نمایش تماس‌های کند API</string>
<string name="network_use_onion_hosts_no_desc_in_alert">از میزبان‌های Onion استفاده نخواهد شد.</string>
<string name="network_use_onion_hosts_no">خیر</string>
<string name="disable_onion_hosts_when_not_supported"><![CDATA[<i>استفاده از میزبان‌های onion.</i> را روی «خیر» تنظیم کنید اگر پروکسی SOCKS از آنها پشتیبانی نمی‌کند.]]></string>
<string name="customize_theme_title">سفارشی کردن تم</string>
@@ -630,7 +627,6 @@
<string name="network_session_mode_entity">اتصال</string>
<string name="app_version_code">ساختار برنامه: %s</string>
<string name="network_settings_title">تنظیمات شبکه</string>
<string name="network_use_onion_hosts_required_desc_in_alert">میزبان‌های Onion برای اتصال الزامی خواهد بود.</string>
<string name="show_dev_options">نمایش:</string>
<string name="socks_proxy_setting_limitations"><![CDATA[<b>لطفا توجه داشته باشید</b>: واسطه‌های پیام و پرونده از طریق پروکسی SOCKS متصل می‌شوند. تماس‌ها و ارسال پیش‌نمایش‌های لینک از اتصال مستقیم استفاده می‌کنند.]]></string>
<string name="developer_options_section">گزینه‌های توسعه‌دهنده</string>
@@ -1313,7 +1309,6 @@
<string name="network_option_ping_interval">وقفه پینگ</string>
<string name="network_option_ping_count">شمار پینگ</string>
<string name="network_option_enable_tcp_keep_alive">فعال کردن زنده نگه‌داشتن TCP</string>
<string name="network_options_revert">برگشت</string>
<string name="network_options_save">ذخیره</string>
<string name="update_network_settings_question">تنظیمات شبکه به‌روزرسانی شود؟</string>
<string name="users_add">افزودن نمایه</string>
@@ -623,7 +623,6 @@
<string name="text_field_set_contact_placeholder">Aseta kontaktin nimi…</string>
<string name="save_passphrase_in_keychain">Tallenna salasana Keystoreen</string>
<string name="only_your_contact_can_send_disappearing">Vain kontaktisi voi lähettää katoavia viestejä.</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Onion-isäntiä käytetään, kun niitä on saatavilla.</string>
<string name="port_verb">Portti</string>
<string name="network_proxy_port">portti %d</string>
<string name="disable_onion_hosts_when_not_supported"><![CDATA[Aseta <i>Use .onion hosts</i> arvoon Ei, jos SOCKS-välityspalvelin ei tue niitä.]]></string>
@@ -661,7 +660,6 @@
<string name="network_option_tcp_connection_timeout">TCP-yhteyden aikakatkaisu</string>
<string name="network_option_ping_count">PING-määrä</string>
<string name="network_option_ping_interval">PING-väli</string>
<string name="network_options_revert">Palauta</string>
<string name="users_delete_with_connections">Profiili- ja palvelinyhteydet</string>
<string name="set_group_preferences">Aseta ryhmän asetukset</string>
<string name="many_people_asked_how_can_it_deliver"><![CDATA[Monet ovat kysyneet: <i>jos SimpleX ei sisällä käyttäjätunnuksia, kuinka se voi toimittaa viestejä\?</i>]]></string>
@@ -916,8 +914,6 @@
<string name="thank_you_for_installing_simplex">Kiitos SimpleX Chatin asentamisesta!</string>
<string name="icon_descr_settings">Asetukset</string>
<string name="icon_descr_more_button">Lisää</string>
<string name="network_use_onion_hosts_no_desc_in_alert">Onion-isäntiä ei käytetä.</string>
<string name="network_use_onion_hosts_required_desc_in_alert">Yhteyden muodostamiseen tarvitaan Onion-isäntiä.</string>
<string name="onboarding_notifications_mode_title">Yksityiset ilmoitukset</string>
<string name="icon_descr_speaker_off">Kaiutin pois päältä</string>
<string name="icon_descr_speaker_on">Kaiutin päällä</string>
@@ -1156,7 +1152,6 @@
\nkontakteillesi</string>
<string name="your_ICE_servers">ICE-palvelimesi</string>
<string name="network_use_onion_hosts_prefer">Kun saatavilla</string>
<string name="update_onion_hosts_settings_question">Päivitä .onion-isäntien asetus\?</string>
<string name="network_use_onion_hosts">Käytä .onion-isäntiä</string>
<string name="your_calls">Puhelusi</string>
<string name="alert_message_no_group">Tätä ryhmää ei enää ole olemassa.</string>
@@ -321,8 +321,6 @@
<string name="ensure_ICE_server_address_are_correct_format_and_unique">Assurez-vous que les adresses des serveurs WebRTC ICE sont au bon format et ne sont pas dupliquées, un par ligne.</string>
<string name="network_enable_socks_info">Accéder aux serveurs via un proxy SOCKS sur le port %d \? Le proxy doit être démarré avant d\'activer cette option.</string>
<string name="network_use_onion_hosts">Utiliser les hôtes .onions</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Les hôtes .onion seront utilisés lorsqu\'ils sont disponibles.</string>
<string name="network_use_onion_hosts_required_desc_in_alert">Les hôtes .onion seront nécessaires pour la connexion.</string>
<string name="you_control_servers_to_receive_your_contacts_to_send"><![CDATA[Vous contrôlez par quel·s serveur·s vous pouvez <b>transmettre</b> ainsi que par quel·s serveur·s vous pouvez <b>recevoir</b> les messages de vos contacts.]]></string>
<string name="your_settings">Vos paramètres</string>
<string name="chat_lock">SimpleX Lock</string>
@@ -414,12 +412,10 @@
<string name="how_to">Comment faire</string>
<string name="enter_one_ICE_server_per_line">Serveurs ICE (un par ligne)</string>
<string name="error_saving_ICE_servers">Erreur lors de la sauvegarde des serveurs ICE</string>
<string name="update_onion_hosts_settings_question">Mettre à jour le paramètre des hôtes .onion \?</string>
<string name="network_use_onion_hosts_prefer">Quand disponible</string>
<string name="network_use_onion_hosts_no_desc">Les hôtes .onion ne seront pas utilisés.</string>
<string name="network_use_onion_hosts_required_desc">Les hôtes .onion seront nécessaires pour la connexion.
\nAttention : vous ne pourrez pas vous connecter aux serveurs sans adresse .onion.</string>
<string name="network_use_onion_hosts_no_desc_in_alert">Les hôtes .onion ne seront pas utilisés.</string>
<string name="delete_address__question">Supprimer l\'adresse \?</string>
<string name="all_your_contacts_will_remain_connected">Tous vos contacts resteront connectés.</string>
<string name="share_link">Partager le lien</string>
@@ -833,7 +829,6 @@
<string name="conn_level_desc_direct">directe</string>
<string name="group_is_decentralized">Entièrement décentralisé visible que par ses membres.</string>
<string name="group_members_can_send_disappearing">Les membres du groupes peuvent envoyer des messages éphémères.</string>
<string name="network_options_revert">Revenir en arrière</string>
<string name="prohibit_sending_disappearing_messages">Interdire lenvoi de messages éphémères.</string>
<string name="incognito_info_protects">Le mode incognito protège votre vie privée en utilisant un nouveau profil aléatoire pour chaque contact.</string>
<string name="updating_settings_will_reconnect_client_to_all_servers">La mise à jour des ces paramètres reconnectera le client à tous les serveurs.</string>
@@ -740,9 +740,7 @@
<string name="new_member_role">Új tag szerepköre</string>
<string name="la_mode_off">Kikapcsolva</string>
<string name="invalid_contact_link">Érvénytelen hivatkozás!</string>
<string name="network_use_onion_hosts_required_desc_in_alert">A kapcsolódáshoz Onion kiszolgálókra lesz szükség.</string>
<string name="new_in_version">Változások a %s verzióban</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Onion kiszolgálók használata, ha azok rendelkezésre állnak.</string>
<string name="smp_servers_invalid_address">Érvénytelen kiszolgálócím!</string>
<string name="thousand_abbreviation">k</string>
<string name="chat_item_ttl_none">soha</string>
@@ -765,7 +763,6 @@
<string name="chat_preferences_on">bekapcsolva</string>
<string name="v5_1_japanese_portuguese_interface">Japán és portugál kezelőfelület</string>
<string name="message_deletion_prohibited_in_chat">Az üzenetek végleges törlése le van tiltva ebben a csoportban.</string>
<string name="network_use_onion_hosts_no_desc_in_alert">Onion kiszolgálók nem lesznek használva.</string>
<string name="remote_host_was_disconnected_toast"><![CDATA[A(z) <b>%s</b> eszközzel megszakadt a kapcsolat]]></string>
<string name="custom_time_unit_months">hónap</string>
<string name="privacy_message_draft">Üzenetvázlat</string>
@@ -1098,7 +1095,6 @@
<string name="your_calls">Hívások</string>
<string name="icon_descr_sent_msg_status_send_failed">nem sikerült elküldeni</string>
<string name="theme_colors_section_title">KEZELŐFELÜLET SZÍNEI</string>
<string name="network_options_revert">Visszaállítás</string>
<string name="restore_database_alert_desc">Előző jelszó megadása az adatbázis biztonsági mentésének visszaállítása után. Ez a művelet nem vonható vissza.</string>
<string name="color_secondary">Másodlagos</string>
<string name="settings_section_title_socks">SOCKS PROXY</string>
@@ -1432,7 +1428,6 @@
<string name="unknown_database_error_with_info">Ismeretlen adatbázis hiba: %s</string>
<string name="you_can_hide_or_mute_user_profile">Elrejtheti vagy lenémíthatja a felhasználó profiljait - koppintson (vagy asztali alkalmazásban kattintson) hosszan a profilra a felugró menühöz.</string>
<string name="v5_3_simpler_incognito_mode_descr">Inkognító mód kapcsolódáskor.</string>
<string name="update_onion_hosts_settings_question">Tor .onion kiszolgálók beállításainak frissítése?</string>
<string name="you_can_share_group_link_anybody_will_be_able_to_connect">Megoszthat egy hivatkozást vagy QR-kódot - így bárki csatlakozhat a csoporthoz. Ha a csoport később törlésre kerül, akkor nem fogja elveszíteni annak tagjait.</string>
<string name="you_joined_this_group">Csatlakozott ehhez a csoporthoz</string>
<string name="connect_plan_this_is_your_link_for_group_vName"><![CDATA[Ez a hivatkozása a(z) <b>%1$s</b> csoporthoz!]]></string>
@@ -610,18 +610,14 @@
<string name="network_use_onion_hosts_no">No</string>
<string name="network_use_onion_hosts_required_desc">Gli host Onion saranno necessari per la connessione.
\nNota bene: non potrai connetterti ai server senza indirizzo .onion .</string>
<string name="network_use_onion_hosts_required_desc_in_alert">Gli host Onion saranno necessari per la connessione.</string>
<string name="network_use_onion_hosts_prefer_desc">Gli host Onion verranno usati quando disponibili.</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Gli host Onion verranno usati quando disponibili.</string>
<string name="network_use_onion_hosts_no_desc">Gli host Onion non verranno usati.</string>
<string name="network_use_onion_hosts_no_desc_in_alert">Gli host Onion non verranno usati.</string>
<string name="rate_the_app">Valuta l\'app</string>
<string name="network_use_onion_hosts_required">Obbligatorio</string>
<string name="save_servers_button">Salva</string>
<string name="saved_ICE_servers_will_be_removed">I server WebRTC ICE salvati verranno rimossi.</string>
<string name="share_link">Condividi link</string>
<string name="star_on_github">Dai una stella su GitHub</string>
<string name="update_onion_hosts_settings_question">Aggiornare l\'impostazione degli host .onion\?</string>
<string name="network_disable_socks">Usare una connessione internet diretta\?</string>
<string name="network_use_onion_hosts">Usa gli host .onion</string>
<string name="network_enable_socks">Usare il proxy SOCKS\?</string>
@@ -816,7 +812,6 @@
<string name="network_option_protocol_timeout">Scadenza del protocollo</string>
<string name="receiving_via">Ricezione via</string>
<string name="network_options_reset_to_defaults">Ripristina i predefiniti</string>
<string name="network_options_revert">Ripristina</string>
<string name="network_options_save">Salva</string>
<string name="save_group_profile">Salva il profilo del gruppo</string>
<string name="network_option_seconds_label">sec</string>
@@ -604,8 +604,6 @@
<string name="theme_light">בהיר</string>
<string name="import_theme_error_desc">ודאו שלקובץ יש תחביר YAML תקין. ייצאו ערכת נושא כדי לקבל דוגמה למבנה תקין של קובץ ערכת נושא.</string>
<string name="message_delivery_error_desc">ככל הנראה איש קשר זה מחק את החיבור איתך.</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">ייעשה שימוש במארחי Onion כאשר יהיו זמינים.</string>
<string name="network_use_onion_hosts_required_desc_in_alert">מארחי Onion יידרשו לחיבור.</string>
<string name="moderated_item_description">נחסם על ידי %s</string>
<string name="la_no_app_password">אין קוד גישה לאפליקציה</string>
<string name="videos_limit_desc">ניתן לשלוח רק 10 סרטונים בו־זמנית</string>
@@ -674,7 +672,6 @@
<string name="network_use_onion_hosts_required_desc">יידרשו מארחי onion לחיבור.
\nשימו לב: לא תוכלו להתחבר לשרתים ללא כתובת .onion.</string>
<string name="network_use_onion_hosts_no_desc">לא ייעשה שימוש במארחי Onion.</string>
<string name="network_use_onion_hosts_no_desc_in_alert">לא ייעשה שימוש במארחי Onion.</string>
<string name="callstatus_missed">שיחה שלא נענתה</string>
<string name="only_client_devices_store_contacts_groups_e2e_encrypted_messages"><![CDATA[רק מכשירי לקוח מאחסנים פרופילי משתמש, אנשי קשר, קבוצות, והודעות שנשלחו עם <b>הצפנה מקצה־לקצה דו־שכבתית</b>.]]></string>
<string name="status_no_e2e_encryption">ללא הצפנה מקצה־לקצה</string>
@@ -902,7 +899,6 @@
<string name="custom_time_unit_seconds">שניות</string>
<string name="custom_time_picker_select">אישור</string>
<string name="role_in_group">תפקיד</string>
<string name="network_options_revert">ביטול</string>
<string name="save_and_update_group_profile">שמור ועדכן את פרופיל הקבוצה</string>
<string name="save_welcome_message_question">לשמור הודעת פתיחה\?</string>
<string name="current_version_timestamp">%s (נוכחי)</string>
@@ -1077,7 +1073,6 @@
<string name="to_share_with_your_contact">(כדי לשתף עם איש הקשר שלך)</string>
<string name="to_start_a_new_chat_help_header">כדי להתחיל צ׳אט חדש</string>
<string name="smp_servers_use_server_for_new_conn">השתמש עבור חיבורים חדשים</string>
<string name="update_onion_hosts_settings_question">לעדכן הגדרות מארחי ‪.onion‬\?‬</string>
<string name="to_verify_compare">כדי לאמת הצפנה מקצה־לקצה עם איש הקשר שלכם, יש להשוות (או לסרוק) את הקוד במכשירים שלכם.</string>
<string name="your_chat_profiles">פרופילי צ׳אט</string>
<string name="update_network_session_mode_question">לעדכן מצב בידוד תעבורה\?</string>
@@ -204,7 +204,6 @@
<string name="display_name_connection_established">接続済み</string>
<string name="connect_via_link">リンク経由で繋がる。</string>
<string name="connection_error">接続エラー</string>
<string name="network_use_onion_hosts_required_desc_in_alert">接続にオニオンのホストが必要となります。</string>
<string name="group_member_status_introduced">接続待ち (紹介済み)</string>
<string name="connection_error_auth">接続エラー (AUTH)</string>
<string name="connection_timeout">接続タイムアウト</string>
@@ -222,7 +221,6 @@
<string name="icon_descr_context">追加情報アイコン</string>
<string name="network_use_onion_hosts_prefer_desc">オニオンのホストが利用可能時に使われます。</string>
<string name="network_use_onion_hosts_no_desc">オニオンのホストが使われません。</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">オニオンのホストが利用可能時に使われます。</string>
<string name="images_limit_desc">画像を1回で最大10枚を送信できます。</string>
<string name="only_client_devices_store_contacts_groups_e2e_encrypted_messages"><![CDATA[<b>2層エンドツーエンド暗号化</b>で送信されたプロフィール、連絡先、グループ、メッセージは、クライント端末にしか保存されません。]]></string>
<string name="only_group_owners_can_change_prefs">グループ設定を変えられるのはグループのオーナーだけです。</string>
@@ -315,7 +313,6 @@
<string name="enter_one_ICE_server_per_line">ICEサーバ (1行に1サーバ)</string>
<string name="network_and_servers">ネットワークとサーバ</string>
<string name="network_settings_title">ネットワーク設定</string>
<string name="network_use_onion_hosts_no_desc_in_alert">オニオンのホストが使われません。</string>
<string name="delete_address">アドレスを削除</string>
<string name="exit_without_saving">保存せずに閉じる</string>
<string name="display_name_cannot_contain_whitespace">表示の名前には空白が使用できません。</string>
@@ -735,7 +732,6 @@
<string name="your_ICE_servers">あなたのICEサーバ</string>
<string name="network_disable_socks">直接にインタネットに繋がりますか?</string>
<string name="network_enable_socks">SOCKSプロキシを使いますか?</string>
<string name="update_onion_hosts_settings_question">.onionのホスト設定を更新しますか?</string>
<string name="network_use_onion_hosts">.onionホストを使う</string>
<string name="network_use_onion_hosts_prefer">利用可能時に</string>
<string name="network_session_mode_transport_isolation">トランスポート隔離</string>
@@ -883,7 +879,6 @@
<string name="you_have_to_enter_passphrase_every_time">アプリ起動時にパスフレーズを入力しなければなりません。端末に保存されてません。</string>
<string name="database_backup_can_be_restored">データベースのパスフレーズ変更が完了してません。</string>
<string name="you_can_share_group_link_anybody_will_be_able_to_connect">リンク、またはQRコードを共有できます。誰でもグループに参加できます。後で削除しても、グループのメンバーがそのままのこります。</string>
<string name="network_options_revert">元に戻す</string>
<string name="update_network_settings_confirmation">更新</string>
<string name="accept_feature_set_1_day">1日に設定</string>
<string name="v4_4_disappearing_messages_desc">一定時間が経ったら送信されたメッセージが削除されます。</string>
@@ -635,7 +635,6 @@
<string name="messages_section_description">이 설정은 현재 내 프로필의 메시지에 적용되어요.</string>
<string name="member_info_section_title_member">멤버</string>
<string name="member_role_will_be_changed_with_invitation">역할이 \"%s\"(으)로 변경되고, 회원은 새로운 초대를 받게 될 거예요.</string>
<string name="network_options_revert">되돌리기</string>
<string name="message_deletion_prohibited">이 채팅에서는 메시지 영구 삭제가 허용되지 않았어요.</string>
<string name="leave_group_button">나가기</string>
<string name="large_file">큰 파일!</string>
@@ -667,9 +666,6 @@
<string name="network_use_onion_hosts_prefer_desc">사용 가능한 경우 Onion 호스트가 사용될 거예요.</string>
<string name="network_use_onion_hosts_no_desc">Onion 호스트가 사용되지 않을 거예요.</string>
<string name="network_session_mode_transport_isolation">전송 격리</string>
<string name="network_use_onion_hosts_no_desc_in_alert">Onion 호스트가 사용되지 않을 거예요.</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">사용 가능한 경우 Onion 호스트가 사용될 거예요.</string>
<string name="network_use_onion_hosts_required_desc_in_alert">연결하려면 Onion 호스트가 필요해요.</string>
<string name="next_generation_of_private_messaging">차세대 사생활 보호 메시징</string>
<string name="new_passphrase">새 비밀번호…</string>
<string name="network_option_enable_tcp_keep_alive">TCP 연결 유지 활성화</string>
@@ -130,7 +130,6 @@
<string name="snd_group_event_group_profile_updated">grupės profilis atnaujintas</string>
<string name="info_row_group">Grupė</string>
<string name="users_delete_question">Ištrinti pokalbio profilį\?</string>
<string name="network_options_revert">Sugrąžinti</string>
<string name="network_options_save">Įrašyti</string>
<string name="feature_enabled">įjungta</string>
<string name="delete_after">Ištrinti po</string>
@@ -1373,7 +1372,6 @@
<string name="connect_via_link_or_qr_from_clipboard_or_in_person">(nuskanuokite ar įklijuokite iš iškarpinės)</string>
<string name="you_accepted_connection">Priėmėte prisijungimą</string>
<string name="you_invited_a_contact">Jūs pakvietėte kontaktą</string>
<string name="network_use_onion_hosts_required_desc_in_alert">Onion serveriai bus reikalingi ryšiui.</string>
<string name="network_use_onion_hosts_prefer_desc">Onion serveriai bus naudojami, kai tik bus.</string>
<string name="exit_without_saving">Išeiti neišsaugant</string>
<string name="you_control_your_chat">Jūs kontroliuojate savo pokalbį!</string>
@@ -1565,7 +1563,6 @@
<string name="your_simplex_contact_address">Jūsų SimpleX adresas</string>
<string name="smp_servers_scan_qr">Nuskanuoti serverio QR kodą</string>
<string name="network_use_onion_hosts_required">Reikalingi</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Onion serveriai bus naudojami, kai tik bus.</string>
<string name="network_use_onion_hosts_no_desc">Onion serveriai nebus naudojami.</string>
<string name="self_destruct">Savaiminis susinaikinimas</string>
<string name="old_database_archive">Senas duomenų bazės archyvas</string>
@@ -1628,8 +1625,6 @@
<string name="moderate_verb">Moderuoti</string>
<string name="contact_wants_to_connect_with_you">nori prisijungti prie jūsų!</string>
<string name="image_descr_link_preview">nuorodos peržiūros nuotrauka</string>
<string name="network_use_onion_hosts_no_desc_in_alert">Onion serveriai nebus naudojami.</string>
<string name="update_onion_hosts_settings_question">Atnaujinti .onion serverių nustatymą?</string>
<string name="onboarding_notifications_mode_off">Kai programėlė yra paleista</string>
<string name="lock_mode">Užrakto režimas</string>
<string name="you_can_start_chat_via_setting_or_by_restarting_the_app">Galite paleisti pokalbius per programėlės nustatymus/ duomenų bazę arba paleisdami programėlę iš naujo.</string>
@@ -320,7 +320,6 @@
<string name="switch_receiving_address">സ്വീകരിക്കുന്ന വിലാസം മാറുക</string>
<string name="conn_stats_section_title_servers">സെർവറുകൾ</string>
<string name="network_options_save">സംരക്ഷിക്കുക</string>
<string name="network_options_revert">പഴയപടിയാക്കുക</string>
<string name="theme_system">സംവിധാനം</string>
<string name="language_system">സംവിധാനം</string>
<string name="color_title">ശീർഷകം</string>
@@ -546,7 +546,6 @@
<string name="one_time_link">Eenmalige uitnodiging link</string>
<string name="paste_button">Plakken</string>
<string name="smp_servers_preset_address">Vooraf ingesteld server adres</string>
<string name="network_use_onion_hosts_no_desc_in_alert">Onion hosts worden niet gebruikt.</string>
<string name="onboarding_notifications_mode_title">Privé meldingen</string>
<string name="paste_the_link_you_received">Plak de link die je hebt ontvangen</string>
<string name="onboarding_notifications_mode_periodic">Periodiek</string>
@@ -573,7 +572,6 @@
<string name="enter_passphrase_notification_title">Wachtwoord is nodig</string>
<string name="feature_off">uit</string>
<string name="add_contact">Eenmalige uitnodiging link</string>
<string name="network_use_onion_hosts_required_desc_in_alert">Onion hosts zijn vereist voor verbinding.</string>
<string name="only_group_owners_can_change_prefs">Alleen groep eigenaren kunnen groep voorkeuren wijzigen.</string>
<string name="only_stored_on_members_devices">(alleen opgeslagen door groepsleden)</string>
<string name="smp_servers_preset_server">Vooraf ingestelde server</string>
@@ -584,7 +582,6 @@
<string name="ok">OK</string>
<string name="network_use_onion_hosts_required_desc">Onion hosts zijn vereist voor verbinding.</string>
<string name="network_use_onion_hosts_prefer_desc">Onion hosts worden gebruikt indien beschikbaar.</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Onion hosts worden gebruikt indien beschikbaar.</string>
<string name="network_use_onion_hosts_no_desc">Onion hosts worden niet gebruikt.</string>
<string name="opensource_protocol_and_code_anybody_can_run_servers">Open-source protocol en code. Iedereen kan de servers draaien.</string>
<string name="people_can_connect_only_via_links_you_share">Mensen kunnen alleen verbinding met u maken via de links die u deelt.</string>
@@ -808,7 +805,6 @@
<string name="network_options_reset_to_defaults">Resetten naar standaardwaarden</string>
<string name="switch_receiving_address">Ontvangst adres wijzigen</string>
<string name="network_option_protocol_timeout">Protocol timeout</string>
<string name="network_options_revert">Terugdraaien</string>
<string name="network_options_save">Opslaan</string>
<string name="network_option_seconds_label">sec</string>
<string name="incognito_info_share">Wanneer je een incognito profiel met iemand deelt, wordt dit profiel gebruikt voor de groepen waarvoor ze je uitnodigen.</string>
@@ -904,7 +900,6 @@
<string name="you_can_connect_to_simplex_chat_founder"><![CDATA[U kunt <font color="#0088ff">verbinding maken met SimpleX Chat ontwikkelaars om vragen te stellen en updates te ontvangen</font>.]]></string>
<string name="connection_error_auth_desc">Tenzij uw contact de verbinding heeft verwijderd of deze link al is gebruikt, kan het een bug zijn. Meld het alstublieft.
\nOm verbinding te maken, vraagt u uw contact om een andere verbinding link te maken en te controleren of u een stabiele netwerkverbinding heeft.</string>
<string name="update_onion_hosts_settings_question">.onion hosts-instelling updaten\?</string>
<string name="use_simplex_chat_servers__question">SimpleX Chat servers gebruiken\?</string>
<string name="voice_messages_are_prohibited">Spraak berichten zijn verboden in deze groep.</string>
<string name="personal_welcome">Welkom %1$s!</string>
@@ -348,14 +348,11 @@
<string name="network_use_onion_hosts_no">Nie</string>
<string name="network_use_onion_hosts_required_desc">Hosty onion będą wymagane do połączenia.
\nUwaga: nie będziesz mógł połączyć się z serwerami bez adresu .onion.</string>
<string name="network_use_onion_hosts_required_desc_in_alert">Hosty onion będą wymagane do połączenia.</string>
<string name="network_use_onion_hosts_prefer_desc">Hosty onion będą używane, gdy będą dostępne.</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Hosty onion będą używane, gdy będą dostępne.</string>
<string name="network_use_onion_hosts_no_desc">Hosty onion nie będą używane.</string>
<string name="network_use_onion_hosts_required">Wymagane</string>
<string name="save_servers_button">Zapisz</string>
<string name="network_session_mode_transport_isolation">Izolacja transportu</string>
<string name="update_onion_hosts_settings_question">Zaktualizować ustawienie hostów .onion\?</string>
<string name="update_network_session_mode_question">Zaktualizować tryb izolacji transportu\?</string>
<string name="network_disable_socks">Użyć bezpośredniego połączenia z Internetem\?</string>
<string name="network_use_onion_hosts">Użyj hostów .onion</string>
@@ -758,7 +755,6 @@
<string name="users_delete_with_connections">Profil i połączenia z serwerem</string>
<string name="network_option_protocol_timeout">Limit czasu protokołu</string>
<string name="network_options_reset_to_defaults">Przywróć wartości domyślne</string>
<string name="network_options_revert">Przywrócić</string>
<string name="network_options_save">Zapisz</string>
<string name="network_option_seconds_label">sek</string>
<string name="tap_to_activate_profile">Dotknij, aby aktywować profil.</string>
@@ -953,7 +949,6 @@
<string name="gallery_video_button">Wideo</string>
<string name="feature_offered_item_with_param">zaproponował %s: %2s</string>
<string name="only_group_owners_can_enable_voice">Tylko właściciele grup mogą włączyć wiadomości głosowe.</string>
<string name="network_use_onion_hosts_no_desc_in_alert">Hosty onion nie będą używane.</string>
<string name="only_your_contact_can_delete">Tylko Twój kontakt może nieodwracalnie usunąć wiadomości (możesz oznaczyć je do usunięcia). (24 godziny)</string>
<string name="restore_passphrase_not_found_desc">Hasło nie zostało znalezione w Keystore, wprowadź je ręcznie. Może się tak zdarzyć, gdy przywrócisz dane aplikacji za pomocą narzędzia do kopii zapasowych. Jeśli tak nie jest, skontaktuj się z programistami.</string>
<string name="group_members_can_send_disappearing">Członkowie grupy mogą wysyłać znikające wiadomości.</string>
@@ -482,7 +482,6 @@
<string name="old_database_archive">Arquivo de banco de dados antigo</string>
<string name="button_add_members">Convidar membros</string>
<string name="no_contacts_selected">Nenhum contato selecionado</string>
<string name="network_options_revert">Reverter</string>
<string name="network_options_save">Salvar</string>
<string name="reset_color">Redefinir cores</string>
<string name="v4_5_italian_interface">interface italiana</string>
@@ -647,7 +646,6 @@
<string name="network_use_onion_hosts_no_desc">Onion hosts não serão usados.</string>
<string name="network_use_onion_hosts_required_desc">Os hosts Onion serão necessários para a conexão.
\nAtenção: você não será capaz de se conectar aos servidores sem um endereço .onion</string>
<string name="network_use_onion_hosts_required_desc_in_alert">Os hosts Onion serão necessários para a conexão.</string>
<string name="core_version">Versão principal: v%s</string>
<string name="read_more_in_github_with_link"><![CDATA[Leia mais no nosso <font color="#0088ff">repositório do GitHub</font>.]]></string>
<string name="onboarding_notifications_mode_subtitle">Pode ser mudado mais tarde via configurações.</string>
@@ -761,9 +759,7 @@
<string name="v4_6_hidden_chat_profiles_descr">Proteja seus perfis de bate-papo com uma senha!</string>
<string name="this_text_is_available_in_settings">Este texto está disponível nas configurações</string>
<string name="scan_code">Escanear código</string>
<string name="network_use_onion_hosts_no_desc_in_alert">Hosts Onion não serão usados.</string>
<string name="network_use_onion_hosts_prefer_desc">Os hosts Onion serão usados quando disponíveis.</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Os hosts Onion serão usados quando disponíveis.</string>
<string name="your_current_profile">Seu perfil atual</string>
<string name="privacy_redefined">Privacidade redefinida</string>
<string name="onboarding_notifications_mode_title">Notificações privadas</string>
@@ -963,7 +959,6 @@
<string name="share_message">Compartilhar mensagem…</string>
<string name="personal_welcome">Bem-vindo(a) %1$s!</string>
<string name="group_preview_you_are_invited">você está convidado para o grupo</string>
<string name="update_onion_hosts_settings_question">Atualizar configuração de hosts .onion\?</string>
<string name="use_chat">Usar bate-papo</string>
<string name="voice_prohibited_in_this_chat">Mensagens de voz são proibidas neste chat.</string>
<string name="video_descr">Vídeo</string>
@@ -578,7 +578,6 @@
<string name="settings_notification_preview_title">Pré-visualização de notificação</string>
<string name="message_delivery_error_desc">Muito provavelmente este contato eliminou a conexão consigo.</string>
<string name="this_text_is_available_in_settings">Este texto está disponível nas definições</string>
<string name="update_onion_hosts_settings_question">Atualizar definições de servidores .onion\?</string>
<string name="onboarding_notifications_mode_subtitle">Pode ser alterado mais tarde através das definições.</string>
<string name="settings_section_title_help">AJUDA</string>
<string name="settings_section_title_support">SUPORTE SIMPLEX CHAT</string>
@@ -613,7 +612,6 @@
<string name="enter_passphrase_notification_desc">Para receber notificações, por favor, digite a senha da base de dados</string>
<string name="network_use_onion_hosts_no_desc">Hosts Onion não serão usados.</string>
<string name="network_use_onion_hosts_prefer_desc">Hosts Onion serão usados quando disponíveis.</string>
<string name="network_use_onion_hosts_required_desc_in_alert">Hosts Onion serão necessários para a conexão.</string>
<string name="video_call_no_encryption">chamada de vídeo (sem encriptação ponta a ponta)</string>
<string name="audio_call_no_encryption">chamada de áudio (não encriptada ponta a ponta)</string>
<string name="encrypted_audio_call">chamada de áudio encriptada ponta a ponta</string>
@@ -623,7 +621,6 @@
<string name="feature_offered_item">oferecido %s</string>
<string name="old_database_archive">Arquivo de base de dados antigo</string>
<string name="network_use_onion_hosts_required_desc">Hosts Onion serão necessários para a conexão.</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Hosts Onion serão usados quando disponíveis.</string>
<string name="alert_text_fragment_encryption_out_of_sync_old_database">Pode acontecer quando você ou sua conexão usaram o backup de base de dados antigo.</string>
<string name="status_e2e_encrypted">encriptado ponta a ponta</string>
<string name="feature_off">desligado</string>
@@ -644,7 +641,6 @@
<string name="to_verify_compare">Para verificar a encriptação de ponta a ponta com o seu contato, compare (ou leia) o código nos seus dispositivos.</string>
<string name="scan_code_from_contacts_app">Ler o código de segurança a partir da aplicação do seu contacto.</string>
<string name="smp_servers_scan_qr">Ler o código QR do servidor</string>
<string name="network_use_onion_hosts_no_desc_in_alert">Hosts Onion não serão usados.</string>
<string name="only_client_devices_store_contacts_groups_e2e_encrypted_messages"><![CDATA[Apenas dispositivos cliente armazenam perfis de utilizador, contatos, grupos e mensagens enviadas com <b>encriptação de ponta a ponta de 2 camadas</b>.]]></string>
<string name="status_contact_has_e2e_encryption">o contacto tem encriptação ponta a ponta</string>
<string name="status_no_e2e_encryption">sem encriptação ponta a ponta</string>
@@ -193,7 +193,6 @@
<string name="reject">Respinge</string>
<string name="save_passphrase_in_settings">Salvează fraza de acces în setări</string>
<string name="save_and_update_group_profile">Salvează și actualizează profilul grupului</string>
<string name="network_options_revert">Revenire</string>
<string name="connect_plan_repeat_join_request">Repetă cererea de alăturare?</string>
<string name="restart_chat_button">Repornește conversația</string>
<string name="saved_description">salvat</string>
@@ -394,7 +394,6 @@
<string name="network_enable_socks_info">Соединяться с серверами через SOCKS прокси через порт %d? Прокси должен быть запущен до включения этой опции.</string>
<string name="network_disable_socks">Использовать прямое соединение с Интернет?</string>
<string name="network_disable_socks_info">Если Вы подтвердите, серверы смогут видеть Ваш IP адрес, а провайдер - с какими серверами Вы соединяетесь.</string>
<string name="update_onion_hosts_settings_question">Обновить настройки .onion хостов?</string>
<string name="network_use_onion_hosts">Использовать .onion хосты</string>
<string name="network_use_onion_hosts_prefer">Когда возможно</string>
<string name="network_use_onion_hosts_no">Нет</string>
@@ -403,9 +402,6 @@
<string name="network_use_onion_hosts_no_desc">Onion хосты не используются.</string>
<string name="network_use_onion_hosts_required_desc">Подключаться только к onion хостам.
\nОбратите внимание: Вы не сможете соединиться с серверами, у которых нет .onion адреса.</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Onion хосты используются, если возможно.</string>
<string name="network_use_onion_hosts_no_desc_in_alert">Onion хосты не используются.</string>
<string name="network_use_onion_hosts_required_desc_in_alert">Подключаться только к onion хостам.</string>
<string name="appearance_settings">Интерфейс</string>
<!-- Address Items - UserAddressView.kt -->
<string name="create_address">Создать адрес</string>
@@ -831,7 +827,6 @@
<string name="network_option_protocol_timeout">Таймаут протокола</string>
<string name="network_option_ping_interval">Интервал PING</string>
<string name="network_option_enable_tcp_keep_alive">Включить TCP keep-alive</string>
<string name="network_options_revert">Отменить изменения</string>
<string name="network_options_save">Сохранить</string>
<string name="update_network_settings_question">Обновить настройки сети?</string>
<string name="updating_settings_will_reconnect_client_to_all_servers">Обновление настроек приведет к переподключению клиента ко всем серверам.</string>
@@ -665,10 +665,7 @@
<string name="network_settings_title">การตั้งค่าเครือข่าย</string>
<string name="network_use_onion_hosts_required_desc">จำเป็นต้องมีโฮสต์หัวหอมสำหรับการเชื่อมต่อ</string>
<string name="network_use_onion_hosts_no">ไม่</string>
<string name="network_use_onion_hosts_required_desc_in_alert">จำเป็นต้องมีโฮสต์หัวหอมสำหรับการเชื่อมต่อ</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">โฮสต์หัวหอมจะถูกใช้เมื่อมี</string>
<string name="network_use_onion_hosts_no_desc">โฮสต์หัวหอมจะไม่ถูกใช้</string>
<string name="network_use_onion_hosts_no_desc_in_alert">โฮสต์หัวหอมจะไม่ถูกใช้</string>
<string name="password_to_show">รหัสผ่านที่จะแสดง</string>
<string name="callstatus_missed">สายที่ไม่ได้รับ</string>
<string name="people_can_connect_only_via_links_you_share">ผู้คนสามารถเชื่อมต่อกับคุณผ่านลิงก์ที่คุณแบ่งปันเท่านั้น</string>
@@ -852,7 +849,6 @@
<string name="save_and_update_group_profile">บันทึกและอัปเดตโปรไฟล์กลุ่ม</string>
<string name="receiving_via">กำลังรับผ่าน</string>
<string name="network_options_reset_to_defaults">รีเซ็ตเป็นค่าเริ่มต้น</string>
<string name="network_options_revert">เปลี่ยนกลับ</string>
<string name="network_options_save">บันทึก</string>
<string name="reset_color">รีเซ็ตสี</string>
<string name="feature_received_prohibited">ได้รับ, ห้าม</string>
@@ -1147,7 +1143,6 @@
<string name="network_socks_toggle_use_socks_proxy">ใช้พร็อกซี SOCKS</string>
<string name="network_disable_socks">ใช้การเชื่อมต่ออินเทอร์เน็ตโดยตรงหรือไม่\?</string>
<string name="network_enable_socks">ใช้พร็อกซี SOCKS หรือไม่\?</string>
<string name="update_onion_hosts_settings_question">อัปเดตการตั้งค่าโฮสต์ .onion ไหม\?</string>
<string name="network_use_onion_hosts">ใช้โฮสต์ .onion</string>
<string name="network_use_onion_hosts_prefer">เมื่อพร้อมใช้งาน</string>
<string name="update_network_session_mode_question">อัปเดตโหมดการแยกการขนส่งไหม\?</string>
@@ -1145,7 +1145,6 @@
<string name="share_link">Bağlantı paylaş</string>
<string name="icon_descr_simplex_team">SimpleX Ekibi</string>
<string name="rcv_group_event_3_members_connected">%s, %s ve %s bağlandı</string>
<string name="network_options_revert">Geri al</string>
<string name="settings_section_title_socks">SOCKS VEKİLİ</string>
<string name="desktop_devices">Masaüstür cihazlar</string>
<string name="smp_servers">SMP sunucuları</string>
@@ -1318,14 +1317,12 @@
<string name="share_text_updated_at">Kayıt %s te güncellendi</string>
<string name="v5_3_encrypt_local_files_descr">Uygulama yeni yerel dosyaları şifreler (videolar dışında).</string>
<string name="sender_at_ts">%s , %s de</string>
<string name="network_use_onion_hosts_required_desc_in_alert">Bağlantı için Onion ana bilgisayarları gerekli olacaktır.</string>
<string name="receipts_contacts_title_disable">Alıcılar devre dışı bırakılsın mı?</string>
<string name="sync_connection_force_question">Bağlantı yeniden senkronizasyonunu şifrele?</string>
<string name="receipts_section_description_1">Grup ayarlarından ve kişilerden geçersiz kılınmış olabilirler</string>
<string name="recent_history_is_not_sent_to_new_members">Yeni üyelere geçmiş gönderilmedi.</string>
<string name="v4_2_security_assessment">Güvenlik değerlendirmesi</string>
<string name="retry_verb">Yeniden dene</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Onion ana bilgisayarları mümkün olduğunda kullanılacaktır.</string>
<string name="network_enable_socks_info">Sunuculara %d bağlantı noktasındaki vekil SOCKS aracılığıyla erişilsin mi? Vekil bu seçeneği etkinleştirmeden önce başlatılmak zorundadır.</string>
<string name="v5_4_block_group_members_descr">İstenmeyen mesajları gizlemek için.</string>
<string name="error_smp_test_failed_at_step">Test %s adımında hata yaşandı.</string>
@@ -1362,7 +1359,6 @@
<string name="remove_passphrase_from_settings">Ayarlardaki parola silinsin mi?</string>
<string name="receipts_contacts_title_enable">Alıcılar etkinleştirilsin mi?</string>
<string name="tap_to_start_new_chat">Yeni bir sohbet başlatmak için tıkla</string>
<string name="network_use_onion_hosts_no_desc_in_alert">Onion ana bilgisayarları kullanılmayacaktır.</string>
<string name="unblock_member_button">Kişinin engelini kaldır</string>
<string name="relay_server_if_necessary">Yönlendirici sunucusu sadece lazım ise kullanılacak. Diğer taraf IP adresini görebilir.</string>
<string name="remote_host_was_disconnected_toast"><![CDATA[Telefon bağlantılı <b>%s</b> ın bağlantısı kesildi]]></string>
@@ -1510,7 +1506,6 @@
<string name="connected_to_mobile">Telefona bağlandı</string>
<string name="v5_3_simpler_incognito_mode_descr">Bağlanırken takma ada geçiş yap.</string>
<string name="v5_2_message_delivery_receipts">Mesaj gönderildi!</string>
<string name="update_onion_hosts_settings_question">.onion ana bilgisayarları ayarı güncellensin mi?</string>
<string name="smp_servers_use_server_for_new_conn">Yeni bağlantılar için kullan</string>
<string name="rcv_conn_event_switch_queue_phase_completed">senin için adres değiştirildi</string>
<string name="network_enable_socks">SOCKS vekilini kullan?</string>
@@ -251,7 +251,6 @@
<string name="network_settings_title">Налаштування мережі</string>
<string name="network_enable_socks">Використовувати SOCKS-проксі?</string>
<string name="network_disable_socks">Використовувати прямий підключення до Інтернету?</string>
<string name="update_onion_hosts_settings_question">Оновити налаштування .onion-хостів?</string>
<string name="network_use_onion_hosts">Використовувати .onion-хости</string>
<string name="network_use_onion_hosts_prefer">Якщо доступно</string>
<string name="network_use_onion_hosts_no">Ні</string>
@@ -640,8 +639,6 @@
<string name="error_saving_ICE_servers">Помилка збереження серверів ICE</string>
<string name="network_use_onion_hosts_required_desc">.Onion-хости будуть обов\'язковими для підключення.
\nЗверніть увагу: ви не зможете підключитися до серверів без адреси .onion.</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Хости .onion будуть використовуватися, якщо доступні.</string>
<string name="network_use_onion_hosts_required_desc_in_alert">Хости .onion будуть обов\'язковими для підключення.</string>
<string name="show_developer_options">Показати параметри розробника</string>
<string name="developer_options">Ідентифікатори бази даних та опція ізоляції транспорту.</string>
<string name="shutdown_alert_desc">Сповіщення перестануть працювати, поки ви не перезапустите додаток</string>
@@ -953,7 +950,6 @@
<string name="image_descr_link_preview">зображення попереднього перегляду посилання</string>
<string name="icon_descr_cancel_link_preview">скасувати попередній перегляд посилання</string>
<string name="icon_descr_settings">Налаштування</string>
<string name="network_use_onion_hosts_no_desc_in_alert">Хости .onion не будуть використовуватися.</string>
<string name="icon_descr_call_progress">Виклик у процесі</string>
<string name="database_error">Помилка бази даних</string>
<string name="restore_database_alert_confirm">Відновити</string>
@@ -997,7 +993,6 @@
<string name="section_title_for_console">ДЛЯ КОНСОЛІ</string>
<string name="member_will_be_removed_from_group_cannot_be_undone">Учасника буде вилучено з групи - цю дію неможливо скасувати!</string>
<string name="change_role">Змінити роль</string>
<string name="network_options_revert">Відновити</string>
<string name="you_will_still_receive_calls_and_ntfs">Ви все ще отримуватимете дзвінки та сповіщення від приглушених профілів, коли вони активні.</string>
<string name="ttl_months">%d місяці</string>
<string name="ttl_mth">%dmth</string>
@@ -667,7 +667,6 @@
<string name="feature_off">关闭</string>
<string name="network_use_onion_hosts_required_desc">连接需要 Onion 主机。
\n请注意:如果没有 .onion 地址,您将无法连接到服务器。</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Onion 主机将在可用时使用。</string>
<string name="chat_item_ttl_none">从不</string>
<string name="feature_offered_item">已提供 %s</string>
<string name="feature_offered_item_with_param">已提供 %s%2s</string>
@@ -686,8 +685,6 @@
<string name="no_contacts_to_add">没有联系人可添加</string>
<string name="network_status">网络状态</string>
<string name="chat_preferences_off">关闭</string>
<string name="network_use_onion_hosts_no_desc_in_alert">将不会使用 Onion 主机。</string>
<string name="network_use_onion_hosts_required_desc_in_alert">连接需要 Onion 主机。</string>
<string name="no_received_app_files">没有收到或发送的文件</string>
<string name="sender_cancelled_file_transfer">发送人已取消文件传输。</string>
<string name="share_verb">分享</string>
@@ -744,7 +741,6 @@
<string name="to_start_a_new_chat_help_header">开始新的聊天</string>
<string name="to_verify_compare">要与您的联系人验证端到端加密,请比较(或扫描)您设备上的代码。</string>
<string name="unmute_chat">取消静音</string>
<string name="update_onion_hosts_settings_question">更新 .onion 主机设置?</string>
<string name="update_network_session_mode_question">更新传输隔离模式?</string>
<string name="connect_via_link_or_qr_from_clipboard_or_in_person">(从剪贴板扫描或粘贴)</string>
<string name="smp_server_test_secure_queue">保护队列</string>
@@ -829,7 +825,6 @@
<string name="group_member_status_removed">已删除</string>
<string name="role_in_group">角色</string>
<string name="network_option_seconds_label"></string>
<string name="network_options_revert">恢复</string>
<string name="reset_color">重置颜色</string>
<string name="v4_5_reduced_battery_usage">减少电池使用量</string>
<string name="v4_5_private_filenames_descr">为了保护时区,图像/语音文件使用 UTC。</string>
@@ -412,8 +412,6 @@
<string name="enter_one_ICE_server_per_line">ICE 伺服器(每行一個)</string>
<string name="network_use_onion_hosts">使用 .onion 主機</string>
<string name="network_use_onion_hosts_no_desc">Onion 主機不會啟用。</string>
<string name="network_use_onion_hosts_prefer_desc_in_alert">Onion 主機會在可用時啟用。</string>
<string name="network_use_onion_hosts_no_desc_in_alert">Onion 主機不會啟用。</string>
<string name="network_session_mode_entity">連接</string>
<string name="delete_address">刪除聯絡地址</string>
<string name="colored_text">顏色</string>
@@ -437,7 +435,6 @@
<string name="ensure_ICE_server_address_are_correct_format_and_unique">請確保你的 WebRTC ICE 伺服器地址是正確的格式,每行也有分隔和沒有重複。</string>
<string name="network_disable_socks">使用直接互聯網連接?</string>
<string name="network_disable_socks_info">如果你確定,你的訊息伺服器能夠看到你的 IP 位置,和你的網路供應商 - 你正在連接到哪些伺服器。</string>
<string name="update_onion_hosts_settings_question">更新 .onion 主機設定?</string>
<string name="delete_image">刪除圖片</string>
<string name="callstate_starting">開始中 …</string>
<string name="callstate_waiting_for_answer">等待對方回應…</string>
@@ -496,7 +493,6 @@
<string name="network_use_onion_hosts_no"></string>
<string name="network_use_onion_hosts_required">需要</string>
<string name="network_use_onion_hosts_prefer_desc">Onion 主機會在可用時啟用。</string>
<string name="network_use_onion_hosts_required_desc_in_alert">連接時將需要使用 Onion 主機。</string>
<string name="delete_address__question">刪除聯絡地址?</string>
<string name="your_profile_is_stored_on_device_and_shared_only_with_contacts_simplex_cannot_see_it">你的個人檔案只會儲存於你的裝置和只會分享給你的聯絡人。 SimpleX 伺服器並不會看到你的個人檔案。</string>
<string name="save_and_notify_contact">儲存並通知你的聯絡人</string>
@@ -825,7 +821,6 @@
<string name="disappearing_messages_are_prohibited">自動銷毀訊息於這個群組內是禁用的。</string>
<string name="feature_offered_item">已提供 %s</string>
<string name="error_saving_group_profile">儲存群組檔案時出錯</string>
<string name="network_options_revert">恢復</string>
<string name="theme">主題</string>
<string name="chat_preferences_you_allow">你允許</string>
<string name="set_group_preferences">修改群組內的設定</string>
+1
View File
@@ -593,6 +593,7 @@ test-suite simplex-chat-test
MessageBatching
MobileTests
ProtocolTests
RandomServers
RemoteTests
SchemaDump
ValidNames
+37 -18
View File
@@ -148,8 +148,10 @@ defaultChatConfig =
defaultServers =
DefaultAgentServers
{ smp = _defaultSMPServers,
useSMP = 4,
ntf = _defaultNtfServers,
xftp = L.map (presetServerCfg True) defaultXFTPServers,
useXFTP = L.length defaultXFTPServers,
netCfg = defaultNetworkConfig
},
tbqSize = 1024,
@@ -178,7 +180,13 @@ _defaultSMPServers =
L.fromList $
map
(presetServerCfg True)
[ "smp://h--vW7ZSkXPeOUpfxlFGgauQmXNFOzGoizak7Ult7cw=@smp15.simplex.im,oauu4bgijybyhczbnxtlggo6hiubahmeutaqineuyy23aojpih3dajad.onion",
[ "smp://0YuTwO05YJWS8rkjn9eLJDjQhFKvIYd8d4xG8X1blIU=@smp8.simplex.im,beccx4yfxxbvyhqypaavemqurytl6hozr47wfc7uuecacjqdvwpw2xid.onion",
"smp://SkIkI6EPd2D63F4xFKfHk7I1UGZVNn6k1QWZ5rcyr6w=@smp9.simplex.im,jssqzccmrcws6bhmn77vgmhfjmhwlyr3u7puw4erkyoosywgl67slqqd.onion",
"smp://6iIcWT_dF2zN_w5xzZEY7HI2Prbh3ldP07YTyDexPjE=@smp10.simplex.im,rb2pbttocvnbrngnwziclp2f4ckjq65kebafws6g4hy22cdaiv5dwjqd.onion",
"smp://1OwYGt-yqOfe2IyVHhxz3ohqo3aCCMjtB-8wn4X_aoY=@smp11.simplex.im,6ioorbm6i3yxmuoezrhjk6f6qgkc4syabh7m3so74xunb5nzr4pwgfqd.onion",
"smp://UkMFNAXLXeAAe0beCa4w6X_zp18PwxSaSjY17BKUGXQ=@smp12.simplex.im,ie42b5weq7zdkghocs3mgxdjeuycheeqqmksntj57rmejagmg4eor5yd.onion",
"smp://enEkec4hlR3UtKx2NMpOUK_K4ZuDxjWBO1d9Y4YXVaA=@smp14.simplex.im,aspkyu2sopsnizbyfabtsicikr2s4r3ti35jogbcekhm3fsoeyjvgrid.onion",
"smp://h--vW7ZSkXPeOUpfxlFGgauQmXNFOzGoizak7Ult7cw=@smp15.simplex.im,oauu4bgijybyhczbnxtlggo6hiubahmeutaqineuyy23aojpih3dajad.onion",
"smp://hejn2gVIqNU6xjtGM3OwQeuk8ZEbDXVJXAlnSBJBWUA=@smp16.simplex.im,p3ktngodzi6qrf7w64mmde3syuzrv57y55hxabqcq3l5p6oi7yzze6qd.onion",
"smp://ZKe4uxF4Z_aLJJOEsC-Y6hSkXgQS5-oc442JQGkyP8M=@smp17.simplex.im,ogtwfxyi3h2h5weftjjpjmxclhb5ugufa5rcyrmg7j4xlch7qsr5nuqd.onion",
"smp://PtsqghzQKU83kYTlQ1VKg996dW4Cw4x_bvpKmiv8uns=@smp18.simplex.im,lyqpnwbs2zqfr45jqkncwpywpbtq7jrhxnib5qddtr6npjyezuwd3nqd.onion",
@@ -188,13 +196,7 @@ _defaultSMPServers =
(presetServerCfg False)
[ "smp://u2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU=@smp4.simplex.im,o5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion",
"smp://hpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg=@smp5.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion",
"smp://PQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo=@smp6.simplex.im,bylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion",
"smp://0YuTwO05YJWS8rkjn9eLJDjQhFKvIYd8d4xG8X1blIU=@smp8.simplex.im,beccx4yfxxbvyhqypaavemqurytl6hozr47wfc7uuecacjqdvwpw2xid.onion",
"smp://SkIkI6EPd2D63F4xFKfHk7I1UGZVNn6k1QWZ5rcyr6w=@smp9.simplex.im,jssqzccmrcws6bhmn77vgmhfjmhwlyr3u7puw4erkyoosywgl67slqqd.onion",
"smp://6iIcWT_dF2zN_w5xzZEY7HI2Prbh3ldP07YTyDexPjE=@smp10.simplex.im,rb2pbttocvnbrngnwziclp2f4ckjq65kebafws6g4hy22cdaiv5dwjqd.onion",
"smp://1OwYGt-yqOfe2IyVHhxz3ohqo3aCCMjtB-8wn4X_aoY=@smp11.simplex.im,6ioorbm6i3yxmuoezrhjk6f6qgkc4syabh7m3so74xunb5nzr4pwgfqd.onion",
"smp://UkMFNAXLXeAAe0beCa4w6X_zp18PwxSaSjY17BKUGXQ=@smp12.simplex.im,ie42b5weq7zdkghocs3mgxdjeuycheeqqmksntj57rmejagmg4eor5yd.onion",
"smp://enEkec4hlR3UtKx2NMpOUK_K4ZuDxjWBO1d9Y4YXVaA=@smp14.simplex.im,aspkyu2sopsnizbyfabtsicikr2s4r3ti35jogbcekhm3fsoeyjvgrid.onion"
"smp://PQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo=@smp6.simplex.im,bylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion"
]
_defaultNtfServers :: [NtfServer]
@@ -380,11 +382,31 @@ withFileLock name = withEntityLock name . CLFile
useServers :: UserProtocol p => ChatConfig -> SProtocolType p -> [ServerCfg p] -> NonEmpty (ServerCfg p)
useServers ChatConfig {defaultServers} p = fromMaybe (cfgServers p defaultServers) . nonEmpty
cfgServers :: UserProtocol p => SProtocolType p -> (DefaultAgentServers -> NonEmpty (ServerCfg p))
randomServers :: forall p. UserProtocol p => SProtocolType p -> ChatConfig -> IO (NonEmpty (ServerCfg p), [ServerCfg p])
randomServers p ChatConfig {defaultServers} = do
let srvs = cfgServers p defaultServers
(enbldSrvs, dsbldSrvs) = L.partition (\ServerCfg {enabled} -> enabled) srvs
toUse = cfgServersToUse p defaultServers
if length enbldSrvs <= toUse
then pure (srvs, [])
else do
(enbldSrvs', srvsToDisable) <- splitAt toUse <$> shuffle enbldSrvs
let dsbldSrvs' = map (\srv -> (srv :: ServerCfg p) {enabled = False}) srvsToDisable
srvs' = sortOn server' $ enbldSrvs' <> dsbldSrvs' <> dsbldSrvs
pure (fromMaybe srvs $ L.nonEmpty srvs', srvs')
where
server' ServerCfg {server = ProtoServerWithAuth srv _} = srv
cfgServers :: UserProtocol p => SProtocolType p -> DefaultAgentServers -> NonEmpty (ServerCfg p)
cfgServers p DefaultAgentServers {smp, xftp} = case p of
SPSMP -> smp
SPXFTP -> xftp
cfgServersToUse :: UserProtocol p => SProtocolType p -> DefaultAgentServers -> Int
cfgServersToUse p DefaultAgentServers {useSMP, useXFTP} = case p of
SPSMP -> useSMP
SPXFTP -> useXFTP
-- enableSndFiles has no effect when mainApp is True
startChatController :: Bool -> Bool -> CM' (Async ())
startChatController mainApp enableSndFiles = do
@@ -523,7 +545,7 @@ processChatCommand cmd =
processChatCommand' :: VersionRangeChat -> ChatCommand -> CM ChatResponse
processChatCommand' vr = \case
ShowActiveUser -> withUser' $ pure . CRActiveUser
CreateActiveUser NewUser {profile, sameServers, pastTimestamp} -> do
CreateActiveUser NewUser {profile, pastTimestamp} -> do
forM_ profile $ \Profile {displayName} -> checkValidName displayName
p@Profile {displayName} <- liftIO $ maybe generateRandomProfile pure profile
u <- asks currentUser
@@ -549,12 +571,10 @@ processChatCommand' vr = \case
createContact db user simplexStatusContactProfile
createContact db user simplexTeamContactProfile
chooseServers :: (ProtocolTypeI p, UserProtocol p) => SProtocolType p -> CM (NonEmpty (ServerCfg p), [ServerCfg p])
chooseServers protocol
| sameServers =
asks currentUser >>= readTVarIO >>= \case
Nothing -> throwChatError CENoActiveUser
Just user -> chosenServers =<< withFastStore' (`getProtocolServers` user)
| otherwise = chosenServers []
chooseServers protocol =
asks currentUser >>= readTVarIO >>= \case
Nothing -> asks config >>= liftIO . randomServers protocol
Just user -> chosenServers =<< withFastStore' (`getProtocolServers` user)
where
chosenServers servers = do
cfg <- asks config
@@ -7914,10 +7934,9 @@ chatCommandP =
onOffP = ("on" $> True) <|> ("off" $> False)
profileNames = (,) <$> displayName <*> fullNameP
newUserP = do
sameServers <- "same_servers=" *> onOffP <* A.space <|> pure False
(cName, fullName) <- profileNames
let profile = Just Profile {displayName = cName, fullName, image = Nothing, contactLink = Nothing, preferences = Nothing}
pure NewUser {profile, sameServers, pastTimestamp = False}
pure NewUser {profile, pastTimestamp = False}
jsonP :: J.FromJSON a => Parser a
jsonP = J.eitherDecodeStrict' <$?> A.takeByteString
groupProfile = do
+2
View File
@@ -174,8 +174,10 @@ defaultChatHooks =
data DefaultAgentServers = DefaultAgentServers
{ smp :: NonEmpty (ServerCfg 'PSMP),
useSMP :: Int,
ntf :: [NtfServer],
xftp :: NonEmpty (ServerCfg 'PXFTP),
useXFTP :: Int,
netCfg :: NetworkConfig
}
+1 -1
View File
@@ -105,7 +105,7 @@ createActiveUser cc = do
loop = do
displayName <- T.pack <$> getWithPrompt "display name"
let profile = Just Profile {displayName, fullName = "", image = Nothing, contactLink = Nothing, preferences = Nothing}
execChatCommand' (CreateActiveUser NewUser {profile, sameServers = False, pastTimestamp = False}) `runReaderT` cc >>= \case
execChatCommand' (CreateActiveUser NewUser {profile, pastTimestamp = False}) `runReaderT` cc >>= \case
CRActiveUser user -> pure user
r -> do
ts <- getCurrentTime
+2
View File
@@ -39,8 +39,10 @@ terminalChatConfig =
"smp://hpq7_4gGJiilmz5Rf-CswuU5kZGkm_zOIooSw6yALRg=@smp5.simplex.im,jjbyvoemxysm7qxap7m5d5m35jzv5qq6gnlv7s4rsn7tdwwmuqciwpid.onion",
"smp://PQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo=@smp6.simplex.im,bylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion"
],
useSMP = 3,
ntf = ["ntf://FB-Uop7RTaZZEG0ZLD2CIaTjsPh-Fw0zFAnb7QyA8Ks=@ntf2.simplex.im,ntg7jdjy2i3qbib3sykiho3enekwiaqg3icctliqhtqcg6jmoh6cxiad.onion"],
xftp = L.map (presetServerCfg True) defaultXFTPServers,
useXFTP = L.length defaultXFTPServers,
netCfg =
defaultNetworkConfig
{ smpProxyMode = SPMUnknown,
-1
View File
@@ -124,7 +124,6 @@ data User = User
data NewUser = NewUser
{ profile :: Maybe Profile,
sameServers :: Bool,
pastTimestamp :: Bool
}
deriving (Show)
+1 -35
View File
@@ -97,7 +97,6 @@ chatDirectTests = do
it "create second user" testCreateSecondUser
it "multiple users subscribe and receive messages after restart" testUsersSubscribeAfterRestart
it "both users have contact link" testMultipleUserAddresses
it "create user with default servers" testCreateUserDefaultServers
it "create user with same servers" testCreateUserSameServers
it "delete user" testDeleteUser
it "users have different chat item TTL configuration, chat items expire" testUsersDifferentCIExpirationTTL
@@ -1488,39 +1487,6 @@ testMultipleUserAddresses =
showActiveUser alice "alice (Alice)"
alice @@@ [("@bob", "hey alice")]
testCreateUserDefaultServers :: HasCallStack => FilePath -> IO ()
testCreateUserDefaultServers =
testChat2 aliceProfile bobProfile $
\alice _ -> do
alice #$> ("/smp smp://2345-w==@smp2.example.im smp://3456-w==@smp3.example.im:5224", id, "ok")
alice #$> ("/xftp xftp://2345-w==@xftp2.example.im xftp://3456-w==@xftp3.example.im:5224", id, "ok")
checkCustomServers alice
alice ##> "/create user alisa"
showActiveUser alice "alisa"
alice #$> ("/smp", id, "smp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=:server_password@localhost:7001")
alice #$> ("/xftp", id, "xftp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=:server_password@localhost:7002")
-- with same_servers=off
alice ##> "/user alice"
showActiveUser alice "alice (Alice)"
checkCustomServers alice
alice ##> "/create user same_servers=off alisa2"
showActiveUser alice "alisa2"
alice #$> ("/smp", id, "smp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=:server_password@localhost:7001")
alice #$> ("/xftp", id, "xftp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=:server_password@localhost:7002")
where
checkCustomServers alice = do
alice ##> "/smp"
alice <## "smp://2345-w==@smp2.example.im"
alice <## "smp://3456-w==@smp3.example.im:5224"
alice ##> "/xftp"
alice <## "xftp://2345-w==@xftp2.example.im"
alice <## "xftp://3456-w==@xftp3.example.im:5224"
testCreateUserSameServers :: HasCallStack => FilePath -> IO ()
testCreateUserSameServers =
testChat2 aliceProfile bobProfile $
@@ -1529,7 +1495,7 @@ testCreateUserSameServers =
alice #$> ("/xftp xftp://2345-w==@xftp2.example.im xftp://3456-w==@xftp3.example.im:5224", id, "ok")
checkCustomServers alice
alice ##> "/create user same_servers=on alisa"
alice ##> "/create user alisa"
showActiveUser alice "alisa"
checkCustomServers alice
+51
View File
@@ -0,0 +1,51 @@
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module RandomServers where
import Control.Monad (replicateM)
import qualified Data.List.NonEmpty as L
import Simplex.Chat (cfgServers, cfgServersToUse, defaultChatConfig, randomServers)
import Simplex.Chat.Controller (ChatConfig (..))
import Simplex.Messaging.Agent.Env.SQLite (ServerCfg (..))
import Simplex.Messaging.Protocol (ProtoServerWithAuth (..), SProtocolType (..), UserProtocol)
import Test.Hspec
randomServersTests :: Spec
randomServersTests = describe "choosig random servers" $ do
it "should choose 4 random SMP servers and keep the rest disabled" testRandomSMPServers
it "should keep all 6 XFTP servers" testRandomXFTPServers
deriving instance Eq (ServerCfg p)
testRandomSMPServers :: IO ()
testRandomSMPServers = do
[srvs1, srvs2, srvs3] <-
replicateM 3 $
checkEnabled SPSMP 4 False =<< randomServers SPSMP defaultChatConfig
(srvs1 == srvs2 && srvs2 == srvs3) `shouldBe` False -- && to avoid rare failures
testRandomXFTPServers :: IO ()
testRandomXFTPServers = do
[srvs1, srvs2, srvs3] <-
replicateM 3 $
checkEnabled SPXFTP 6 True =<< randomServers SPXFTP defaultChatConfig
(srvs1 == srvs2 && srvs2 == srvs3) `shouldBe` True
checkEnabled :: UserProtocol p => SProtocolType p -> Int -> Bool -> (L.NonEmpty (ServerCfg p), [ServerCfg p]) -> IO [ServerCfg p]
checkEnabled p n allUsed (srvs, _) = do
let def = defaultServers defaultChatConfig
cfgSrvs = L.sortWith server' $ cfgServers p def
toUse = cfgServersToUse p def
srvs == cfgSrvs `shouldBe` allUsed
L.map enable srvs `shouldBe` L.map enable cfgSrvs
let enbldSrvs = L.filter (\ServerCfg {enabled} -> enabled) srvs
toUse `shouldBe` n
length enbldSrvs `shouldBe` n
pure enbldSrvs
where
server' ServerCfg {server = ProtoServerWithAuth srv _} = srv
enable :: forall p. ServerCfg p -> ServerCfg p
enable srv = (srv :: ServerCfg p) {enabled = False}
+2
View File
@@ -10,6 +10,7 @@ import MarkdownTests
import MessageBatching
import MobileTests
import ProtocolTests
import RandomServers
import RemoteTests
import SchemaDump
import Test.Hspec hiding (it)
@@ -30,6 +31,7 @@ main = do
around tmpBracket $ describe "WebRTC encryption" webRTCTests
describe "Valid names" validNameTests
describe "Message batching" batchingTests
describe "Random servers" randomServersTests
around testBracket $ do
describe "Mobile API Tests" mobileTests
describe "SimpleX chat client" chatTests