mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-06-04 01:41:43 +00:00
Merge branch 'master' into master-ghc8107
This commit is contained in:
@@ -65,7 +65,7 @@ fun MainScreen() {
|
||||
!chatModel.controller.appPrefs.laNoticeShown.get()
|
||||
&& showAdvertiseLAAlert
|
||||
&& chatModel.controller.appPrefs.onboardingStage.get() == OnboardingStage.OnboardingComplete
|
||||
&& chatModel.chats.isNotEmpty()
|
||||
&& chatModel.chats.count() > 1
|
||||
&& chatModel.activeCallInvitation.value == null
|
||||
) {
|
||||
AppLock.showLANotice(ChatModel.controller.appPrefs.laNoticeShown) }
|
||||
|
||||
+17
-11
@@ -3,6 +3,7 @@ package chat.simplex.common.model
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.font.*
|
||||
@@ -27,7 +28,6 @@ import kotlinx.serialization.encoding.Encoder
|
||||
import kotlinx.serialization.json.*
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
import java.net.URLDecoder
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
import java.util.*
|
||||
@@ -113,7 +113,7 @@ object ChatModel {
|
||||
val currentRemoteHost = mutableStateOf<RemoteHostInfo?>(null)
|
||||
val remoteHostId: Long? @Composable get() = remember { currentRemoteHost }.value?.remoteHostId
|
||||
fun remoteHostId(): Long? = currentRemoteHost.value?.remoteHostId
|
||||
val newRemoteHostPairing = mutableStateOf<Pair<RemoteHostInfo?, RemoteHostSessionState>?>(null)
|
||||
val remoteHostPairing = mutableStateOf<Pair<RemoteHostInfo?, RemoteHostSessionState>?>(null)
|
||||
val remoteCtrlSession = mutableStateOf<RemoteCtrlSession?>(null)
|
||||
|
||||
fun getUser(userId: Long): User? = if (currentUser.value?.userId == userId) {
|
||||
@@ -638,6 +638,9 @@ data class User(
|
||||
|
||||
val addressShared: Boolean = profile.contactLink != null
|
||||
|
||||
fun updateRemoteHostId(rh: Long?): User =
|
||||
if (rh == null) this else this.copy(remoteHostId = rh)
|
||||
|
||||
companion object {
|
||||
val sampleData = User(
|
||||
remoteHostId = null,
|
||||
@@ -2301,7 +2304,7 @@ data class CIFile(
|
||||
sent = fileStatus.sent,
|
||||
fileSource = fileSource
|
||||
)
|
||||
cachedRemoteFileRequests.add(fileSource)
|
||||
cachedRemoteFileRequests[fileSource] = false
|
||||
val showAlert = fileSize > 5_000_000 && allowToShowAlert
|
||||
if (showAlert) {
|
||||
AlertManager.shared.showAlertMsgWithProgress(
|
||||
@@ -2310,7 +2313,7 @@ data class CIFile(
|
||||
)
|
||||
}
|
||||
val res = chatModel.controller.getRemoteFile(rh.remoteHostId, rf)
|
||||
cachedRemoteFileRequests.remove(fileSource)
|
||||
cachedRemoteFileRequests[fileSource] = res
|
||||
if (showAlert) {
|
||||
AlertManager.shared.hideAlert()
|
||||
}
|
||||
@@ -2327,7 +2330,7 @@ data class CIFile(
|
||||
): CIFile =
|
||||
CIFile(fileId = fileId, fileName = fileName, fileSize = fileSize, fileSource = if (filePath == null) null else CryptoFile.plain(filePath), fileStatus = fileStatus, fileProtocol = FileProtocol.XFTP)
|
||||
|
||||
val cachedRemoteFileRequests = SnapshotStateList<CryptoFile>()
|
||||
val cachedRemoteFileRequests = SnapshotStateMap<CryptoFile, Boolean>()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2360,7 +2363,7 @@ data class CryptoFile(
|
||||
companion object {
|
||||
fun plain(f: String): CryptoFile = CryptoFile(f, null)
|
||||
|
||||
fun desktopPlain(f: URI): CryptoFile = CryptoFile(URLDecoder.decode(f.rawPath, "UTF-8"), null)
|
||||
fun desktopPlain(f: URI): CryptoFile = CryptoFile(f.toFile().absolutePath, null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2918,7 +2921,7 @@ enum class NotificationPreviewMode {
|
||||
}
|
||||
|
||||
data class RemoteCtrlSession(
|
||||
val ctrlAppInfo: CtrlAppInfo,
|
||||
val ctrlAppInfo: CtrlAppInfo?,
|
||||
val appVersion: String,
|
||||
val sessionState: UIRemoteCtrlSessionState
|
||||
) {
|
||||
@@ -2936,14 +2939,17 @@ data class RemoteCtrlSession(
|
||||
@Serializable
|
||||
sealed class RemoteCtrlSessionState {
|
||||
@Serializable @SerialName("starting") object Starting: RemoteCtrlSessionState()
|
||||
@Serializable @SerialName("searching") object Searching: RemoteCtrlSessionState()
|
||||
@Serializable @SerialName("connecting") object Connecting: RemoteCtrlSessionState()
|
||||
@Serializable @SerialName("pendingConfirmation") data class PendingConfirmation(val sessionCode: String): RemoteCtrlSessionState()
|
||||
@Serializable @SerialName("connected") data class Connected(val sessionCode: String): RemoteCtrlSessionState()
|
||||
}
|
||||
|
||||
sealed class UIRemoteCtrlSessionState {
|
||||
@Serializable @SerialName("starting") object Starting: UIRemoteCtrlSessionState()
|
||||
@Serializable @SerialName("connecting") data class Connecting(val remoteCtrl_: RemoteCtrlInfo? = null): UIRemoteCtrlSessionState()
|
||||
@Serializable @SerialName("pendingConfirmation") data class PendingConfirmation(val remoteCtrl_: RemoteCtrlInfo? = null, val sessionCode: String): UIRemoteCtrlSessionState()
|
||||
@Serializable @SerialName("connected") data class Connected(val remoteCtrl: RemoteCtrlInfo, val sessionCode: String): UIRemoteCtrlSessionState()
|
||||
object Starting: UIRemoteCtrlSessionState()
|
||||
object Searching: UIRemoteCtrlSessionState()
|
||||
data class Found(val remoteCtrl: RemoteCtrlInfo, val compatible: Boolean): UIRemoteCtrlSessionState()
|
||||
data class Connecting(val remoteCtrl_: RemoteCtrlInfo? = null): UIRemoteCtrlSessionState()
|
||||
data class PendingConfirmation(val remoteCtrl_: RemoteCtrlInfo? = null, val sessionCode: String): UIRemoteCtrlSessionState()
|
||||
data class Connected(val remoteCtrl: RemoteCtrlInfo, val sessionCode: String): UIRemoteCtrlSessionState()
|
||||
}
|
||||
|
||||
+50
-20
@@ -170,6 +170,7 @@ class AppPreferences {
|
||||
|
||||
val confirmRemoteSessions = mkBoolPreference(SHARED_PREFS_CONFIRM_REMOTE_SESSIONS, false)
|
||||
val connectRemoteViaMulticast = mkBoolPreference(SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST, false)
|
||||
val connectRemoteViaMulticastAuto = mkBoolPreference(SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST_AUTO, true)
|
||||
val offerRemoteMulticast = mkBoolPreference(SHARED_PREFS_OFFER_REMOTE_MULTICAST, true)
|
||||
|
||||
private fun mkIntPreference(prefName: String, default: Int) =
|
||||
@@ -314,6 +315,7 @@ class AppPreferences {
|
||||
private const val SHARED_PREFS_DEVICE_NAME_FOR_REMOTE_ACCESS = "DeviceNameForRemoteAccess"
|
||||
private const val SHARED_PREFS_CONFIRM_REMOTE_SESSIONS = "ConfirmRemoteSessions"
|
||||
private const val SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST = "ConnectRemoteViaMulticast"
|
||||
private const val SHARED_PREFS_CONNECT_REMOTE_VIA_MULTICAST_AUTO = "ConnectRemoteViaMulticastAuto"
|
||||
private const val SHARED_PREFS_OFFER_REMOTE_MULTICAST = "OfferRemoteMulticast"
|
||||
}
|
||||
}
|
||||
@@ -468,7 +470,7 @@ object ChatController {
|
||||
|
||||
suspend fun apiGetActiveUser(rh: Long?): User? {
|
||||
val r = sendCmd(rh, CC.ShowActiveUser())
|
||||
if (r is CR.ActiveUser) return r.user
|
||||
if (r is CR.ActiveUser) return r.user.updateRemoteHostId(rh)
|
||||
Log.d(TAG, "apiGetActiveUser: ${r.responseType} ${r.details}")
|
||||
chatModel.userCreated.value = false
|
||||
return null
|
||||
@@ -476,7 +478,7 @@ object ChatController {
|
||||
|
||||
suspend fun apiCreateActiveUser(rh: Long?, p: Profile?, sameServers: Boolean = false, pastTimestamp: Boolean = false): User? {
|
||||
val r = sendCmd(rh, CC.CreateActiveUser(p, sameServers = sameServers, pastTimestamp = pastTimestamp))
|
||||
if (r is CR.ActiveUser) return r.user
|
||||
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 ||
|
||||
r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorChat && r.chatError.errorType is ChatErrorType.UserExists
|
||||
@@ -501,7 +503,7 @@ object ChatController {
|
||||
|
||||
suspend fun apiSetActiveUser(rh: Long?, userId: Long, viewPwd: String?): User {
|
||||
val r = sendCmd(rh, CC.ApiSetActiveUser(userId, viewPwd))
|
||||
if (r is CR.ActiveUser) return if (rh == null) r.user else r.user.copy(remoteHostId = rh)
|
||||
if (r is CR.ActiveUser) return r.user.updateRemoteHostId(rh)
|
||||
Log.d(TAG, "apiSetActiveUser: ${r.responseType} ${r.details}")
|
||||
throw Exception("failed to set the user as active ${r.responseType} ${r.details}")
|
||||
}
|
||||
@@ -538,7 +540,7 @@ object ChatController {
|
||||
|
||||
private suspend fun setUserPrivacy(rh: Long?, cmd: CC): User {
|
||||
val r = sendCmd(rh, cmd)
|
||||
if (r is CR.UserPrivacy) return if (rh == null) r.updatedUser else r.updatedUser.copy(remoteHostId = rh)
|
||||
if (r is CR.UserPrivacy) return r.updatedUser.updateRemoteHostId(rh)
|
||||
else throw Exception("Failed to change user privacy: ${r.responseType} ${r.details}")
|
||||
}
|
||||
|
||||
@@ -989,7 +991,7 @@ object ChatController {
|
||||
val userId = try { currentUserId("apiSetProfileAddress") } catch (e: Exception) { return null }
|
||||
return when (val r = sendCmd(rh, CC.ApiSetProfileAddress(userId, on))) {
|
||||
is CR.UserProfileNoChange -> null
|
||||
is CR.UserProfileUpdated -> r.user
|
||||
is CR.UserProfileUpdated -> r.user.updateRemoteHostId(rh)
|
||||
else -> throw Exception("failed to set profile address: ${r.responseType} ${r.details}")
|
||||
}
|
||||
}
|
||||
@@ -1032,7 +1034,7 @@ object ChatController {
|
||||
suspend fun apiDeleteUserAddress(rh: Long?): User? {
|
||||
val userId = try { currentUserId("apiDeleteUserAddress") } catch (e: Exception) { return null }
|
||||
val r = sendCmd(rh, CC.ApiDeleteMyAddress(userId))
|
||||
if (r is CR.UserContactLinkDeleted) return r.user
|
||||
if (r is CR.UserContactLinkDeleted) return r.user.updateRemoteHostId(rh)
|
||||
Log.e(TAG, "apiDeleteUserAddress bad response: ${r.responseType} ${r.details}")
|
||||
return null
|
||||
}
|
||||
@@ -1392,7 +1394,7 @@ object ChatController {
|
||||
chatModel.remoteHosts.addAll(hosts)
|
||||
}
|
||||
|
||||
suspend fun startRemoteHost(rhId: Long?, multicast: Boolean = false): Triple<RemoteHostInfo?, String, String>? {
|
||||
suspend fun startRemoteHost(rhId: Long?, multicast: Boolean = true): Triple<RemoteHostInfo?, String, String>? {
|
||||
val r = sendCmd(null, CC.StartRemoteHost(rhId, multicast))
|
||||
if (r is CR.RemoteHostStarted) return Triple(r.remoteHost_, r.invitation, r.ctrlPort)
|
||||
apiErrorAlert("startRemoteHost", generalGetString(MR.strings.error_alert_title), r)
|
||||
@@ -1428,18 +1430,29 @@ object ChatController {
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun getRemoteFile(rhId: Long, file: RemoteFile): Boolean = sendCommandOkResp(null, CC.GetRemoteFile(rhId, file))
|
||||
suspend fun getRemoteFile(rhId: Long, file: RemoteFile): Boolean = sendCmd(null, CC.GetRemoteFile(rhId, file)) is CR.CmdOk
|
||||
|
||||
suspend fun connectRemoteCtrl(desktopAddress: String): Pair<SomeRemoteCtrl?, CR.ChatCmdError?> {
|
||||
val r = sendCmd(null, CC.ConnectRemoteCtrl(desktopAddress))
|
||||
if (r is CR.RemoteCtrlConnecting) return SomeRemoteCtrl(r.remoteCtrl_, r.ctrlAppInfo, r.appVersion) to null
|
||||
else if (r is CR.ChatCmdError) return null to r
|
||||
else throw Exception("connectRemoteCtrl error: ${r.responseType} ${r.details}")
|
||||
return if (r is CR.RemoteCtrlConnecting) SomeRemoteCtrl(r.remoteCtrl_, r.ctrlAppInfo, r.appVersion) to null
|
||||
else if (r is CR.ChatCmdError) null to r
|
||||
else {
|
||||
apiErrorAlert("connectRemoteCtrl", generalGetString(MR.strings.error_alert_title), r)
|
||||
null to null
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun findKnownRemoteCtrl(): Boolean = sendCommandOkResp(null, CC.FindKnownRemoteCtrl())
|
||||
|
||||
suspend fun confirmRemoteCtrl(rcId: Long): Boolean = sendCommandOkResp(null, CC.ConfirmRemoteCtrl(rcId))
|
||||
suspend fun confirmRemoteCtrl(rcId: Long): Pair<SomeRemoteCtrl?, CR.ChatCmdError?> {
|
||||
val r = sendCmd(null, CC.ConfirmRemoteCtrl(remoteCtrlId = rcId))
|
||||
return if (r is CR.RemoteCtrlConnecting) SomeRemoteCtrl(r.remoteCtrl_, r.ctrlAppInfo, r.appVersion) to null
|
||||
else if (r is CR.ChatCmdError) null to r
|
||||
else {
|
||||
apiErrorAlert("confirmRemoteCtrl", generalGetString(MR.strings.error_alert_title), r)
|
||||
null to null
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun verifyRemoteCtrlSession(sessionCode: String): RemoteCtrlInfo? {
|
||||
val r = sendCmd(null, CC.VerifyRemoteCtrlSession(sessionCode))
|
||||
@@ -1836,7 +1849,7 @@ object ChatController {
|
||||
is CR.GroupMemberRatchetSync ->
|
||||
chatModel.updateGroupMemberConnectionStats(rhId, r.groupInfo, r.member, r.ratchetSyncProgress.connectionStats)
|
||||
is CR.RemoteHostSessionCode -> {
|
||||
chatModel.newRemoteHostPairing.value = r.remoteHost_ to RemoteHostSessionState.PendingConfirmation(r.sessionCode)
|
||||
chatModel.remoteHostPairing.value = r.remoteHost_ to RemoteHostSessionState.PendingConfirmation(r.sessionCode)
|
||||
}
|
||||
is CR.RemoteHostConnected -> {
|
||||
// TODO needs to update it instead in sessions
|
||||
@@ -1845,7 +1858,7 @@ object ChatController {
|
||||
}
|
||||
is CR.RemoteHostStopped -> {
|
||||
val disconnectedHost = chatModel.remoteHosts.firstOrNull { it.remoteHostId == r.remoteHostId_ }
|
||||
chatModel.newRemoteHostPairing.value = null
|
||||
chatModel.remoteHostPairing.value = null
|
||||
if (disconnectedHost != null) {
|
||||
showToast(
|
||||
generalGetString(MR.strings.remote_host_was_disconnected_toast).format(disconnectedHost.hostDeviceName.ifEmpty { disconnectedHost.remoteHostId.toString() })
|
||||
@@ -1857,8 +1870,15 @@ object ChatController {
|
||||
}
|
||||
}
|
||||
is CR.RemoteCtrlFound -> {
|
||||
// TODO multicast
|
||||
Log.d(TAG, "RemoteCtrlFound: ${r.remoteCtrl}")
|
||||
val sess = chatModel.remoteCtrlSession.value
|
||||
if (sess != null && sess.sessionState is UIRemoteCtrlSessionState.Searching) {
|
||||
val state = UIRemoteCtrlSessionState.Found(remoteCtrl = r.remoteCtrl, compatible = r.compatible)
|
||||
chatModel.remoteCtrlSession.value = RemoteCtrlSession(
|
||||
ctrlAppInfo = r.ctrlAppInfo_,
|
||||
appVersion = r.appVersion,
|
||||
sessionState = state
|
||||
)
|
||||
}
|
||||
}
|
||||
is CR.RemoteCtrlSessionCode -> {
|
||||
val state = UIRemoteCtrlSessionState.PendingConfirmation(remoteCtrl_ = r.remoteCtrl_, sessionCode = r.sessionCode)
|
||||
@@ -1870,7 +1890,13 @@ object ChatController {
|
||||
chatModel.remoteCtrlSession.value = chatModel.remoteCtrlSession.value?.copy(sessionState = state)
|
||||
}
|
||||
is CR.RemoteCtrlStopped -> {
|
||||
switchToLocalSession()
|
||||
val sess = chatModel.remoteCtrlSession.value
|
||||
if (sess != null) {
|
||||
chatModel.remoteCtrlSession.value = null
|
||||
if (sess.sessionState is UIRemoteCtrlSessionState.Connected) {
|
||||
switchToLocalSession()
|
||||
}
|
||||
}
|
||||
}
|
||||
else ->
|
||||
Log.d(TAG , "unsupported event: ${r.responseType}")
|
||||
@@ -2322,7 +2348,7 @@ sealed class CC {
|
||||
is DeleteRemoteHost -> "/delete remote host $remoteHostId"
|
||||
is StoreRemoteFile ->
|
||||
"/store remote file $remoteHostId " +
|
||||
(if (storeEncrypted == null) "" else " encrypt=${onOff(storeEncrypted)} ") +
|
||||
(if (storeEncrypted == null) "" else "encrypt=${onOff(storeEncrypted)} ") +
|
||||
localPath
|
||||
is GetRemoteFile -> "/get remote file $remoteHostId ${json.encodeToString(file)}"
|
||||
is ConnectRemoteCtrl -> "/connect remote ctrl $xrcpInvitation"
|
||||
@@ -3782,7 +3808,7 @@ sealed class CR {
|
||||
@Serializable @SerialName("remoteFileStored") class RemoteFileStored(val remoteHostId: Long, val remoteFileSource: CryptoFile): CR()
|
||||
// remote events (mobile)
|
||||
@Serializable @SerialName("remoteCtrlList") class RemoteCtrlList(val remoteCtrls: List<RemoteCtrlInfo>): CR()
|
||||
@Serializable @SerialName("remoteCtrlFound") class RemoteCtrlFound(val remoteCtrl: RemoteCtrlInfo): CR()
|
||||
@Serializable @SerialName("remoteCtrlFound") class RemoteCtrlFound(val remoteCtrl: RemoteCtrlInfo, val ctrlAppInfo_: CtrlAppInfo?, val appVersion: String, val compatible: Boolean): CR()
|
||||
@Serializable @SerialName("remoteCtrlConnecting") class RemoteCtrlConnecting(val remoteCtrl_: RemoteCtrlInfo?, val ctrlAppInfo: CtrlAppInfo, val appVersion: String): CR()
|
||||
@Serializable @SerialName("remoteCtrlSessionCode") class RemoteCtrlSessionCode(val remoteCtrl_: RemoteCtrlInfo?, val sessionCode: String): CR()
|
||||
@Serializable @SerialName("remoteCtrlConnected") class RemoteCtrlConnected(val remoteCtrl: RemoteCtrlInfo): CR()
|
||||
@@ -4080,7 +4106,11 @@ sealed class CR {
|
||||
is RemoteHostStopped -> "remote host ID: $remoteHostId_"
|
||||
is RemoteFileStored -> "remote host ID: $remoteHostId\nremoteFileSource:\n" + json.encodeToString(remoteFileSource)
|
||||
is RemoteCtrlList -> json.encodeToString(remoteCtrls)
|
||||
is RemoteCtrlFound -> json.encodeToString(remoteCtrl)
|
||||
is RemoteCtrlFound -> "remote ctrl: " + json.encodeToString(remoteCtrl) +
|
||||
"\nctrlAppInfo: " +
|
||||
(if (ctrlAppInfo_ == null) "null" else json.encodeToString(ctrlAppInfo_)) +
|
||||
"\nappVersion: $appVersion" +
|
||||
"\ncompatible: $compatible"
|
||||
is RemoteCtrlConnecting ->
|
||||
"remote ctrl: " +
|
||||
(if (remoteCtrl_ == null) "null" else json.encodeToString(remoteCtrl_)) +
|
||||
|
||||
+7
-1
@@ -7,6 +7,8 @@ import chat.simplex.common.views.helpers.generalGetString
|
||||
import chat.simplex.res.MR
|
||||
import java.io.*
|
||||
import java.net.URI
|
||||
import java.net.URLDecoder
|
||||
import java.net.URLEncoder
|
||||
|
||||
expect val dataDir: File
|
||||
expect val tmpDir: File
|
||||
@@ -28,6 +30,10 @@ expect val remoteHostsDir: File
|
||||
|
||||
expect fun desktopOpenDatabaseDir()
|
||||
|
||||
fun createURIFromPath(absolutePath: String): URI = URI.create(URLEncoder.encode(absolutePath, "UTF-8"))
|
||||
|
||||
fun URI.toFile(): File = File(URLDecoder.decode(rawPath, "UTF-8").removePrefix("file:"))
|
||||
|
||||
fun copyFileToFile(from: File, to: URI, finally: () -> Unit) {
|
||||
try {
|
||||
to.outputStream().use { stream ->
|
||||
@@ -92,7 +98,7 @@ fun getLoadedFileSource(file: CIFile?): CryptoFile? {
|
||||
|
||||
private fun fileReady(file: CIFile, filePath: String) =
|
||||
File(filePath).exists() &&
|
||||
!CIFile.cachedRemoteFileRequests.contains(file.fileSource)
|
||||
CIFile.cachedRemoteFileRequests[file.fileSource] != false
|
||||
&& File(filePath).length() >= file.fileSize
|
||||
|
||||
/**
|
||||
|
||||
+2
-1
@@ -3,6 +3,7 @@ package chat.simplex.common.platform
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import java.io.File
|
||||
|
||||
expect fun Modifier.navigationBarsWithImePadding(): Modifier
|
||||
|
||||
@@ -16,7 +17,7 @@ expect fun ProvideWindowInsets(
|
||||
@Composable
|
||||
expect fun Modifier.desktopOnExternalDrag(
|
||||
enabled: Boolean = true,
|
||||
onFiles: (List<String>) -> Unit = {},
|
||||
onFiles: (List<File>) -> Unit = {},
|
||||
onImage: (Painter) -> Unit = {},
|
||||
onText: (String) -> Unit = {}
|
||||
): Modifier
|
||||
|
||||
+4
-7
@@ -43,16 +43,13 @@ object VideoPlayerHolder {
|
||||
): VideoPlayer =
|
||||
players.getOrPut(uri to gallery) { VideoPlayer(uri, gallery, defaultPreview, defaultDuration, soundEnabled) }
|
||||
|
||||
fun enableSound(enable: Boolean, fileName: String?, gallery: Boolean): Boolean =
|
||||
player(fileName, gallery)?.enableSound(enable) == true
|
||||
|
||||
private fun player(fileName: String?, gallery: Boolean): VideoPlayer? {
|
||||
fileName ?: return null
|
||||
return players.values.firstOrNull { player -> player.uri.path?.endsWith(fileName) == true && player.gallery == gallery }
|
||||
private fun player(uri: URI?, gallery: Boolean): VideoPlayer? {
|
||||
uri ?: return null
|
||||
return players.values.firstOrNull { player -> player.uri == uri && player.gallery == gallery }
|
||||
}
|
||||
|
||||
fun release(uri: URI, gallery: Boolean, remove: Boolean) =
|
||||
player(uri.path, gallery)?.release(remove).run { }
|
||||
player(uri, gallery)?.release(remove).run { }
|
||||
|
||||
fun stopAll() {
|
||||
players.values.forEach { it.stop() }
|
||||
|
||||
+1
-1
@@ -501,7 +501,7 @@ fun ChatLayout(
|
||||
.fillMaxWidth()
|
||||
.desktopOnExternalDrag(
|
||||
enabled = !attachmentDisabled.value && rememberUpdatedState(chat.userCanSend).value,
|
||||
onFiles = { paths -> composeState.onFilesAttached(paths.map { URI.create(it) }) },
|
||||
onFiles = { paths -> composeState.onFilesAttached(paths.map { it.toURI() }) },
|
||||
onImage = {
|
||||
// TODO: file is not saved anywhere?!
|
||||
val tmpFile = File.createTempFile("image", ".bmp", tmpDir)
|
||||
|
||||
+1
@@ -850,6 +850,7 @@ fun ComposeView(
|
||||
deleteUnusedFiles()
|
||||
}
|
||||
chatModel.removeLiveDummy()
|
||||
CIFile.cachedRemoteFileRequests.clear()
|
||||
}
|
||||
|
||||
val timedMessageAllowed = remember(chat.chatInfo) { chat.chatInfo.featureEnabled(ChatFeature.TimedMessages) }
|
||||
|
||||
+3
-3
@@ -133,7 +133,7 @@ fun ChatItemView(
|
||||
}
|
||||
|
||||
fun deleteMessageQuestionText(): String {
|
||||
return if (fullDeleteAllowed) {
|
||||
return if (!sent || fullDeleteAllowed) {
|
||||
generalGetString(MR.strings.delete_message_cannot_be_undone_warning)
|
||||
} else {
|
||||
generalGetString(MR.strings.delete_message_mark_deleted_warning)
|
||||
@@ -195,7 +195,7 @@ fun ChatItemView(
|
||||
}
|
||||
val clipboard = LocalClipboardManager.current
|
||||
val cachedRemoteReqs = remember { CIFile.cachedRemoteFileRequests }
|
||||
val copyAndShareAllowed = cItem.file == null || !chatModel.connectedToRemote() || getLoadedFilePath(cItem.file) != null || !cachedRemoteReqs.contains(cItem.file.fileSource)
|
||||
val copyAndShareAllowed = cItem.file == null || !chatModel.connectedToRemote() || getLoadedFilePath(cItem.file) != null || cachedRemoteReqs[cItem.file.fileSource] != false
|
||||
if (copyAndShareAllowed) {
|
||||
ItemAction(stringResource(MR.strings.share_verb), painterResource(MR.images.ic_share), onClick = {
|
||||
var fileSource = getLoadedFileSource(cItem.file)
|
||||
@@ -221,7 +221,7 @@ fun ChatItemView(
|
||||
showMenu.value = false
|
||||
})
|
||||
}
|
||||
if ((cItem.content.msgContent is MsgContent.MCImage || cItem.content.msgContent is MsgContent.MCVideo || cItem.content.msgContent is MsgContent.MCFile || cItem.content.msgContent is MsgContent.MCVoice) && (getLoadedFilePath(cItem.file) != null || (chatModel.connectedToRemote() && !cachedRemoteReqs.contains(cItem.file?.fileSource)))) {
|
||||
if ((cItem.content.msgContent is MsgContent.MCImage || cItem.content.msgContent is MsgContent.MCVideo || cItem.content.msgContent is MsgContent.MCFile || cItem.content.msgContent is MsgContent.MCVoice) && (getLoadedFilePath(cItem.file) != null || (chatModel.connectedToRemote() && cachedRemoteReqs[cItem.file?.fileSource] != false))) {
|
||||
SaveContentItemAction(cItem, saveFileLauncher, showMenu)
|
||||
}
|
||||
if (cItem.meta.editable && cItem.content.msgContent !is MsgContent.MCVoice && !live) {
|
||||
|
||||
+95
-87
@@ -40,6 +40,7 @@ fun DatabaseView(
|
||||
m: ChatModel,
|
||||
showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit)
|
||||
) {
|
||||
val currentRemoteHost by remember { chatModel.currentRemoteHost }
|
||||
val progressIndicator = remember { mutableStateOf(false) }
|
||||
val prefs = m.controller.appPrefs
|
||||
val useKeychain = remember { mutableStateOf(prefs.storeDBPassphrase.get()) }
|
||||
@@ -68,6 +69,7 @@ fun DatabaseView(
|
||||
val user = m.currentUser.value
|
||||
val rhId = user?.remoteHostId
|
||||
DatabaseLayout(
|
||||
currentRemoteHost = currentRemoteHost,
|
||||
progressIndicator.value,
|
||||
remember { m.chatRunning }.value != false,
|
||||
m.chatDbChanged.value,
|
||||
@@ -119,6 +121,7 @@ fun DatabaseView(
|
||||
|
||||
@Composable
|
||||
fun DatabaseLayout(
|
||||
currentRemoteHost: RemoteHostInfo?,
|
||||
progressIndicator: Boolean,
|
||||
runChat: Boolean,
|
||||
chatDbChanged: Boolean,
|
||||
@@ -165,103 +168,107 @@ fun DatabaseLayout(
|
||||
}
|
||||
}
|
||||
)
|
||||
SectionDividerSpaced(maxTopPadding = true)
|
||||
|
||||
SectionView(stringResource(MR.strings.run_chat_section)) {
|
||||
RunChatSetting(runChat, stopped, startChat, stopChatAlert)
|
||||
}
|
||||
SectionTextFooter(
|
||||
if (stopped) {
|
||||
stringResource(MR.strings.you_must_use_the_most_recent_version_of_database)
|
||||
} else {
|
||||
stringResource(MR.strings.stop_chat_to_enable_database_actions)
|
||||
if (currentRemoteHost == null) {
|
||||
SectionDividerSpaced(maxTopPadding = true)
|
||||
|
||||
SectionView(stringResource(MR.strings.run_chat_section)) {
|
||||
RunChatSetting(runChat, stopped, startChat, stopChatAlert)
|
||||
}
|
||||
)
|
||||
SectionDividerSpaced()
|
||||
|
||||
SectionView(stringResource(MR.strings.chat_database_section)) {
|
||||
val unencrypted = chatDbEncrypted == false
|
||||
SettingsActionItem(
|
||||
if (unencrypted) painterResource(MR.images.ic_lock_open_right) else if (useKeyChain) painterResource(MR.images.ic_vpn_key_filled)
|
||||
else painterResource(MR.images.ic_lock),
|
||||
stringResource(MR.strings.database_passphrase),
|
||||
click = showSettingsModal() { DatabaseEncryptionView(it) },
|
||||
iconColor = if (unencrypted || (appPlatform.isDesktop && passphraseSaved)) WarningOrange else MaterialTheme.colors.secondary,
|
||||
disabled = operationsDisabled
|
||||
SectionTextFooter(
|
||||
if (stopped) {
|
||||
stringResource(MR.strings.you_must_use_the_most_recent_version_of_database)
|
||||
} else {
|
||||
stringResource(MR.strings.stop_chat_to_enable_database_actions)
|
||||
}
|
||||
)
|
||||
if (appPlatform.isDesktop && developerTools) {
|
||||
SectionDividerSpaced()
|
||||
|
||||
SectionView(stringResource(MR.strings.chat_database_section)) {
|
||||
val unencrypted = chatDbEncrypted == false
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_folder_open),
|
||||
stringResource(MR.strings.open_database_folder),
|
||||
::desktopOpenDatabaseDir,
|
||||
if (unencrypted) painterResource(MR.images.ic_lock_open_right) else if (useKeyChain) painterResource(MR.images.ic_vpn_key_filled)
|
||||
else painterResource(MR.images.ic_lock),
|
||||
stringResource(MR.strings.database_passphrase),
|
||||
click = showSettingsModal() { DatabaseEncryptionView(it) },
|
||||
iconColor = if (unencrypted || (appPlatform.isDesktop && passphraseSaved)) WarningOrange else MaterialTheme.colors.secondary,
|
||||
disabled = operationsDisabled
|
||||
)
|
||||
if (appPlatform.isDesktop && developerTools) {
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_folder_open),
|
||||
stringResource(MR.strings.open_database_folder),
|
||||
::desktopOpenDatabaseDir,
|
||||
disabled = operationsDisabled
|
||||
)
|
||||
}
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_ios_share),
|
||||
stringResource(MR.strings.export_database),
|
||||
click = {
|
||||
if (initialRandomDBPassphrase.get()) {
|
||||
exportProhibitedAlert()
|
||||
} else {
|
||||
exportArchive()
|
||||
}
|
||||
},
|
||||
textColor = MaterialTheme.colors.primary,
|
||||
iconColor = MaterialTheme.colors.primary,
|
||||
disabled = operationsDisabled
|
||||
)
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_download),
|
||||
stringResource(MR.strings.import_database),
|
||||
{ withApi { importArchiveLauncher.launch("application/zip") } },
|
||||
textColor = Color.Red,
|
||||
iconColor = Color.Red,
|
||||
disabled = operationsDisabled
|
||||
)
|
||||
val chatArchiveNameVal = chatArchiveName.value
|
||||
val chatArchiveTimeVal = chatArchiveTime.value
|
||||
val chatLastStartVal = chatLastStart.value
|
||||
if (chatArchiveNameVal != null && chatArchiveTimeVal != null && chatLastStartVal != null) {
|
||||
val title = chatArchiveTitle(chatArchiveTimeVal, chatLastStartVal)
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_inventory_2),
|
||||
title,
|
||||
click = showSettingsModal { ChatArchiveView(it, title, chatArchiveNameVal, chatArchiveTimeVal) },
|
||||
disabled = operationsDisabled
|
||||
)
|
||||
}
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_delete_forever),
|
||||
stringResource(MR.strings.delete_database),
|
||||
deleteChatAlert,
|
||||
textColor = Color.Red,
|
||||
iconColor = Color.Red,
|
||||
disabled = operationsDisabled
|
||||
)
|
||||
}
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_ios_share),
|
||||
stringResource(MR.strings.export_database),
|
||||
click = {
|
||||
if (initialRandomDBPassphrase.get()) {
|
||||
exportProhibitedAlert()
|
||||
} else {
|
||||
exportArchive()
|
||||
}
|
||||
},
|
||||
textColor = MaterialTheme.colors.primary,
|
||||
iconColor = MaterialTheme.colors.primary,
|
||||
disabled = operationsDisabled
|
||||
)
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_download),
|
||||
stringResource(MR.strings.import_database),
|
||||
{ withApi { importArchiveLauncher.launch("application/zip") }},
|
||||
textColor = Color.Red,
|
||||
iconColor = Color.Red,
|
||||
disabled = operationsDisabled
|
||||
)
|
||||
val chatArchiveNameVal = chatArchiveName.value
|
||||
val chatArchiveTimeVal = chatArchiveTime.value
|
||||
val chatLastStartVal = chatLastStart.value
|
||||
if (chatArchiveNameVal != null && chatArchiveTimeVal != null && chatLastStartVal != null) {
|
||||
val title = chatArchiveTitle(chatArchiveTimeVal, chatLastStartVal)
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_inventory_2),
|
||||
title,
|
||||
click = showSettingsModal { ChatArchiveView(it, title, chatArchiveNameVal, chatArchiveTimeVal) },
|
||||
disabled = operationsDisabled
|
||||
)
|
||||
}
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_delete_forever),
|
||||
stringResource(MR.strings.delete_database),
|
||||
deleteChatAlert,
|
||||
textColor = Color.Red,
|
||||
iconColor = Color.Red,
|
||||
disabled = operationsDisabled
|
||||
)
|
||||
}
|
||||
SectionDividerSpaced(maxTopPadding = true)
|
||||
SectionDividerSpaced(maxTopPadding = true)
|
||||
|
||||
SectionView(stringResource(MR.strings.files_and_media_section).uppercase()) {
|
||||
val deleteFilesDisabled = operationsDisabled || appFilesCountAndSize.value.first == 0
|
||||
SectionItemView(
|
||||
deleteAppFilesAndMedia,
|
||||
disabled = deleteFilesDisabled
|
||||
) {
|
||||
Text(
|
||||
stringResource(if (users.size > 1) MR.strings.delete_files_and_media_for_all_users else MR.strings.delete_files_and_media_all),
|
||||
color = if (deleteFilesDisabled) MaterialTheme.colors.secondary else Color.Red
|
||||
)
|
||||
SectionView(stringResource(MR.strings.files_and_media_section).uppercase()) {
|
||||
val deleteFilesDisabled = operationsDisabled || appFilesCountAndSize.value.first == 0
|
||||
SectionItemView(
|
||||
deleteAppFilesAndMedia,
|
||||
disabled = deleteFilesDisabled
|
||||
) {
|
||||
Text(
|
||||
stringResource(if (users.size > 1) MR.strings.delete_files_and_media_for_all_users else MR.strings.delete_files_and_media_all),
|
||||
color = if (deleteFilesDisabled) MaterialTheme.colors.secondary else Color.Red
|
||||
)
|
||||
}
|
||||
}
|
||||
val (count, size) = appFilesCountAndSize.value
|
||||
SectionTextFooter(
|
||||
if (count == 0) {
|
||||
stringResource(MR.strings.no_received_app_files)
|
||||
} else {
|
||||
String.format(stringResource(MR.strings.total_files_count_and_size), count, formatBytes(size))
|
||||
}
|
||||
)
|
||||
}
|
||||
val (count, size) = appFilesCountAndSize.value
|
||||
SectionTextFooter(
|
||||
if (count == 0) {
|
||||
stringResource(MR.strings.no_received_app_files)
|
||||
} else {
|
||||
String.format(stringResource(MR.strings.total_files_count_and_size), count, formatBytes(size))
|
||||
}
|
||||
)
|
||||
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
@@ -666,6 +673,7 @@ private fun operationEnded(m: ChatModel, progressIndicator: MutableState<Boolean
|
||||
fun PreviewDatabaseLayout() {
|
||||
SimpleXTheme {
|
||||
DatabaseLayout(
|
||||
currentRemoteHost = null,
|
||||
progressIndicator = false,
|
||||
runChat = true,
|
||||
chatDbChanged = false,
|
||||
|
||||
+1
-1
@@ -125,7 +125,7 @@ fun DefaultConfigurableTextField(
|
||||
keyboardType: KeyboardType = KeyboardType.Text,
|
||||
dependsOn: State<Any?>? = null,
|
||||
) {
|
||||
var valid by remember { mutableStateOf(validKey(state.value.text)) }
|
||||
var valid by remember { mutableStateOf(isValid(state.value.text)) }
|
||||
var showKey by remember { mutableStateOf(false) }
|
||||
val icon = if (valid) {
|
||||
if (showKey) painterResource(MR.images.ic_visibility_off_filled) else painterResource(MR.images.ic_visibility_filled)
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ fun CreateLinkView(m: ChatModel, rh: RemoteHostInfo?, initialSelection: CreateLi
|
||||
AddContactView(m, rh,connReqInvitation.value ?: "", contactConnection)
|
||||
}
|
||||
CreateLinkTab.LONG_TERM -> {
|
||||
UserAddressView(m, rh?.remoteHostId, viaCreateLinkView = true, close = {})
|
||||
UserAddressView(m, viaCreateLinkView = true, close = {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+144
-22
@@ -21,6 +21,7 @@ import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
@@ -37,6 +38,7 @@ import chat.simplex.common.views.newchat.QRCodeScanner
|
||||
import chat.simplex.common.views.usersettings.PreferenceToggle
|
||||
import chat.simplex.common.views.usersettings.SettingsActionItem
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.ImageResource
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
|
||||
@@ -69,15 +71,21 @@ fun ConnectDesktopView(close: () -> Unit) {
|
||||
|
||||
@Composable
|
||||
private fun ConnectDesktopLayout(deviceName: String, close: () -> Unit) {
|
||||
val showConnectScreen = remember { mutableStateOf(true) }
|
||||
val sessionAddress = remember { mutableStateOf("") }
|
||||
val remoteCtrls = remember { mutableStateListOf<RemoteCtrlInfo>() }
|
||||
val session = remember { chatModel.remoteCtrlSession }.value
|
||||
Column(
|
||||
Modifier.fillMaxWidth().verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
if (session != null) {
|
||||
val discovery = if (session == null) null else session.sessionState is UIRemoteCtrlSessionState.Searching
|
||||
if (discovery == true || (discovery == null && !showConnectScreen.value)) {
|
||||
SearchingDesktop(deviceName, remoteCtrls)
|
||||
} else if (session != null) {
|
||||
when (session.sessionState) {
|
||||
is UIRemoteCtrlSessionState.Starting -> ConnectingDesktop(session, null)
|
||||
is UIRemoteCtrlSessionState.Searching -> SearchingDesktop(deviceName, remoteCtrls)
|
||||
is UIRemoteCtrlSessionState.Found -> FoundDesktop(session, session.sessionState.remoteCtrl, session.sessionState.compatible, remember { controller.appPrefs.connectRemoteViaMulticastAuto.state }, deviceName, remoteCtrls, sessionAddress)
|
||||
is UIRemoteCtrlSessionState.Connecting -> ConnectingDesktop(session, session.sessionState.remoteCtrl_)
|
||||
is UIRemoteCtrlSessionState.PendingConfirmation -> {
|
||||
if (controller.appPrefs.confirmRemoteSessions.get() || session.sessionState.remoteCtrl_ == null) {
|
||||
@@ -97,11 +105,21 @@ private fun ConnectDesktopLayout(deviceName: String, close: () -> Unit) {
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
DisposableEffect(Unit) {
|
||||
LaunchedEffect(Unit) {
|
||||
setDeviceName(deviceName)
|
||||
updateRemoteCtrls(remoteCtrls)
|
||||
val useMulticast = useMulticast(remoteCtrls)
|
||||
showConnectScreen.value = !useMulticast
|
||||
if (chatModel.remoteCtrlSession.value != null) {
|
||||
disconnectDesktop()
|
||||
} else if (useMulticast) {
|
||||
findKnownDesktop(showConnectScreen)
|
||||
}
|
||||
}
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
if (chatModel.remoteCtrlSession.value != null) {
|
||||
showConnectScreen.value = false
|
||||
disconnectDesktop()
|
||||
}
|
||||
}
|
||||
@@ -146,7 +164,75 @@ private fun ConnectingDesktop(session: RemoteCtrlSession, rc: RemoteCtrlInfo?) {
|
||||
SectionSpacer()
|
||||
|
||||
SectionView {
|
||||
DisconnectButton(::disconnectDesktop)
|
||||
DisconnectButton(onClick = ::disconnectDesktop)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SearchingDesktop(deviceName: String, remoteCtrls: SnapshotStateList<RemoteCtrlInfo>) {
|
||||
AppBarTitle(stringResource(MR.strings.connecting_to_desktop))
|
||||
SectionView(stringResource(MR.strings.this_device_name).uppercase()) {
|
||||
DevicesView(deviceName, remoteCtrls) {
|
||||
if (it != "") {
|
||||
setDeviceName(it)
|
||||
controller.appPrefs.deviceNameForRemoteAccess.set(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
SectionDividerSpaced()
|
||||
SectionView(stringResource(MR.strings.found_desktop).uppercase(), padding = PaddingValues(horizontal = DEFAULT_PADDING)) {
|
||||
Text(stringResource(MR.strings.waiting_for_desktop), fontStyle = FontStyle.Italic)
|
||||
}
|
||||
SectionSpacer()
|
||||
DisconnectButton(stringResource(MR.strings.scan_QR_code).replace('\n', ' '), MR.images.ic_qr_code, ::disconnectDesktop)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FoundDesktop(
|
||||
session: RemoteCtrlSession,
|
||||
rc: RemoteCtrlInfo,
|
||||
compatible: Boolean,
|
||||
connectRemoteViaMulticastAuto: State<Boolean>,
|
||||
deviceName: String,
|
||||
remoteCtrls: SnapshotStateList<RemoteCtrlInfo>,
|
||||
sessionAddress: MutableState<String>,
|
||||
) {
|
||||
AppBarTitle(stringResource(MR.strings.found_desktop))
|
||||
SectionView(stringResource(MR.strings.this_device_name).uppercase()) {
|
||||
DevicesView(deviceName, remoteCtrls) {
|
||||
if (it != "") {
|
||||
setDeviceName(it)
|
||||
controller.appPrefs.deviceNameForRemoteAccess.set(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
SectionDividerSpaced()
|
||||
SectionView(stringResource(MR.strings.found_desktop).uppercase(), padding = PaddingValues(horizontal = DEFAULT_PADDING)) {
|
||||
CtrlDeviceNameText(session, rc)
|
||||
CtrlDeviceVersionText(session)
|
||||
if (!compatible) {
|
||||
Text(stringResource(MR.strings.not_compatible), color = MaterialTheme.colors.error)
|
||||
}
|
||||
}
|
||||
|
||||
SectionSpacer()
|
||||
|
||||
if (compatible) {
|
||||
SectionItemView({ confirmKnownDesktop(sessionAddress, rc) }) {
|
||||
Icon(painterResource(MR.images.ic_check), generalGetString(MR.strings.connect_button), tint = MaterialTheme.colors.secondary)
|
||||
TextIconSpaced(false)
|
||||
Text(generalGetString(MR.strings.connect_button))
|
||||
}
|
||||
}
|
||||
|
||||
if (!compatible || !connectRemoteViaMulticastAuto.value) {
|
||||
DisconnectButton(stringResource(MR.strings.cancel_verb), onClick = ::disconnectDesktop)
|
||||
}
|
||||
|
||||
if (compatible && connectRemoteViaMulticastAuto.value) {
|
||||
LaunchedEffect(Unit) {
|
||||
confirmKnownDesktop(sessionAddress, rc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,7 +260,7 @@ private fun VerifySession(session: RemoteCtrlSession, rc: RemoteCtrlInfo?, sessC
|
||||
}
|
||||
|
||||
SectionView {
|
||||
DisconnectButton(::disconnectDesktop)
|
||||
DisconnectButton(onClick = ::disconnectDesktop)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +268,7 @@ private fun VerifySession(session: RemoteCtrlSession, rc: RemoteCtrlInfo?, sessC
|
||||
private fun CtrlDeviceNameText(session: RemoteCtrlSession, rc: RemoteCtrlInfo?) {
|
||||
val newDesktop = annotatedStringResource(MR.strings.new_desktop)
|
||||
val text = remember(rc) {
|
||||
var t = AnnotatedString(rc?.deviceViewName ?: session.ctrlAppInfo.deviceName)
|
||||
var t = AnnotatedString(rc?.deviceViewName ?: session.ctrlAppInfo?.deviceName ?: "")
|
||||
if (rc == null) {
|
||||
t = t + AnnotatedString(" ") + newDesktop
|
||||
}
|
||||
@@ -195,7 +281,7 @@ private fun CtrlDeviceNameText(session: RemoteCtrlSession, rc: RemoteCtrlInfo?)
|
||||
private fun CtrlDeviceVersionText(session: RemoteCtrlSession) {
|
||||
val thisDeviceVersion = annotatedStringResource(MR.strings.this_device_version, session.appVersion)
|
||||
val text = remember(session) {
|
||||
val v = AnnotatedString(session.ctrlAppInfo.appVersionRange.maxVersion)
|
||||
val v = AnnotatedString(session.ctrlAppInfo?.appVersionRange?.maxVersion ?: "")
|
||||
var t = AnnotatedString("v$v")
|
||||
if (v.text != session.appVersion) {
|
||||
t = t + AnnotatedString(" ") + thisDeviceVersion
|
||||
@@ -243,7 +329,8 @@ private fun SessionCodeText(code: String) {
|
||||
private fun DevicesView(deviceName: String, remoteCtrls: SnapshotStateList<RemoteCtrlInfo>, updateDeviceName: (String) -> Unit) {
|
||||
DeviceNameField(deviceName) { updateDeviceName(it) }
|
||||
if (remoteCtrls.isNotEmpty()) {
|
||||
SectionItemView({ ModalManager.start.showModal { LinkedDesktopsView(remoteCtrls) } }) {
|
||||
SectionItemView({ ModalManager.start.showModal { LinkedDesktopsView(remoteCtrls) }
|
||||
}) {
|
||||
Text(generalGetString(MR.strings.linked_desktops))
|
||||
}
|
||||
}
|
||||
@@ -336,8 +423,13 @@ private fun LinkedDesktopsView(remoteCtrls: SnapshotStateList<RemoteCtrlInfo>) {
|
||||
PreferenceToggle(stringResource(MR.strings.verify_connections), remember { controller.appPrefs.confirmRemoteSessions.state }.value) {
|
||||
controller.appPrefs.confirmRemoteSessions.set(it)
|
||||
}
|
||||
PreferenceToggle(stringResource(MR.strings.discover_on_network), remember { controller.appPrefs.connectRemoteViaMulticast.state }.value && false) {
|
||||
controller.appPrefs.confirmRemoteSessions.set(it)
|
||||
PreferenceToggle(stringResource(MR.strings.discover_on_network), remember { controller.appPrefs.connectRemoteViaMulticast.state }.value) {
|
||||
controller.appPrefs.connectRemoteViaMulticast.set(it)
|
||||
}
|
||||
if (remember { controller.appPrefs.connectRemoteViaMulticast.state }.value) {
|
||||
PreferenceToggle(stringResource(MR.strings.multicast_connect_automatically), remember { controller.appPrefs.connectRemoteViaMulticastAuto.state }.value) {
|
||||
controller.appPrefs.connectRemoteViaMulticastAuto.set(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
@@ -355,13 +447,11 @@ private fun setDeviceName(name: String) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateRemoteCtrls(remoteCtrls: SnapshotStateList<RemoteCtrlInfo>) {
|
||||
withBGApi {
|
||||
val res = controller.listRemoteCtrls()
|
||||
if (res != null) {
|
||||
remoteCtrls.clear()
|
||||
remoteCtrls.addAll(res)
|
||||
}
|
||||
private suspend fun updateRemoteCtrls(remoteCtrls: SnapshotStateList<RemoteCtrlInfo>) {
|
||||
val res = controller.listRemoteCtrls()
|
||||
if (res != null) {
|
||||
remoteCtrls.clear()
|
||||
remoteCtrls.addAll(res)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,9 +459,34 @@ private fun processDesktopQRCode(sessionAddress: MutableState<String>, resp: Str
|
||||
connectDesktopAddress(sessionAddress, resp)
|
||||
}
|
||||
|
||||
private fun connectDesktopAddress(sessionAddress: MutableState<String>, addr: String) {
|
||||
private fun findKnownDesktop(showConnectScreen: MutableState<Boolean>) {
|
||||
withBGApi {
|
||||
val res = controller.connectRemoteCtrl(desktopAddress = addr)
|
||||
if (controller.findKnownRemoteCtrl()) {
|
||||
chatModel.remoteCtrlSession.value = RemoteCtrlSession(
|
||||
ctrlAppInfo = null,
|
||||
appVersion = "",
|
||||
sessionState = UIRemoteCtrlSessionState.Searching
|
||||
)
|
||||
showConnectScreen.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun confirmKnownDesktop(sessionAddress: MutableState<String>, rc: RemoteCtrlInfo) {
|
||||
connectDesktop(sessionAddress) {
|
||||
controller.confirmRemoteCtrl(rc.remoteCtrlId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun connectDesktopAddress(sessionAddress: MutableState<String>, addr: String) {
|
||||
connectDesktop(sessionAddress) {
|
||||
controller.connectRemoteCtrl(addr)
|
||||
}
|
||||
}
|
||||
|
||||
private fun connectDesktop(sessionAddress: MutableState<String>, connect: suspend () -> Pair<SomeRemoteCtrl?, CR.ChatCmdError?>) {
|
||||
withBGApi {
|
||||
val res = connect()
|
||||
if (res.first != null) {
|
||||
val (rc_, ctrlAppInfo, v) = res.first!!
|
||||
sessionAddress.value = ""
|
||||
@@ -409,18 +524,25 @@ private fun verifyDesktopSessionCode(remoteCtrls: SnapshotStateList<RemoteCtrlIn
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DisconnectButton(onClick: () -> Unit) {
|
||||
private fun DisconnectButton(label: String = generalGetString(MR.strings.disconnect_remote_host), icon: ImageResource = MR.images.ic_close, onClick: () -> Unit) {
|
||||
SectionItemView(onClick) {
|
||||
Icon(painterResource(MR.images.ic_close), generalGetString(MR.strings.disconnect_remote_host), tint = MaterialTheme.colors.secondary)
|
||||
Icon(painterResource(icon), label, tint = MaterialTheme.colors.secondary)
|
||||
TextIconSpaced(false)
|
||||
Text(generalGetString(MR.strings.disconnect_remote_host))
|
||||
Text(label)
|
||||
}
|
||||
}
|
||||
|
||||
private fun useMulticast(remoteCtrls: List<RemoteCtrlInfo>): Boolean =
|
||||
controller.appPrefs.connectRemoteViaMulticast.get() && remoteCtrls.isNotEmpty()
|
||||
|
||||
private fun disconnectDesktop(close: (() -> Unit)? = null) {
|
||||
withBGApi {
|
||||
controller.stopRemoteCtrl()
|
||||
switchToLocalSession()
|
||||
if (chatModel.remoteCtrlSession.value?.sessionState is UIRemoteCtrlSessionState.Connected) {
|
||||
switchToLocalSession()
|
||||
} else {
|
||||
chatModel.remoteCtrlSession.value = null
|
||||
}
|
||||
close?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
+12
-6
@@ -30,6 +30,7 @@ import chat.simplex.common.views.chat.item.ItemAction
|
||||
import chat.simplex.common.views.chatlist.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.newchat.QRCode
|
||||
import chat.simplex.common.views.usersettings.PreferenceToggle
|
||||
import chat.simplex.common.views.usersettings.SettingsActionItemWithContent
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
@@ -90,6 +91,9 @@ fun ConnectMobileLayout(
|
||||
SectionView(generalGetString(MR.strings.this_device_name).uppercase()) {
|
||||
DeviceNameField(deviceName.value ?: "") { updateDeviceName(it) }
|
||||
SectionTextFooter(generalGetString(MR.strings.this_device_name_shared_with_mobile))
|
||||
PreferenceToggle(stringResource(MR.strings.multicast_discoverable_via_local_network), remember { controller.appPrefs.offerRemoteMulticast.state }.value) {
|
||||
controller.appPrefs.offerRemoteMulticast.set(it)
|
||||
}
|
||||
SectionDividerSpaced(maxBottomPadding = false)
|
||||
}
|
||||
SectionView(stringResource(MR.strings.devices).uppercase()) {
|
||||
@@ -235,7 +239,7 @@ private fun showAddingMobileDevice(connecting: MutableState<Boolean>) {
|
||||
ModalManager.start.showModalCloseable { close ->
|
||||
val invitation = rememberSaveable { mutableStateOf<String?>(null) }
|
||||
val port = rememberSaveable { mutableStateOf<String?>(null) }
|
||||
val pairing = remember { chatModel.newRemoteHostPairing }
|
||||
val pairing = remember { chatModel.remoteHostPairing }
|
||||
val sessionCode = when (val state = pairing.value?.second) {
|
||||
is RemoteHostSessionState.PendingConfirmation -> state.sessionCode
|
||||
else -> null
|
||||
@@ -266,11 +270,12 @@ private fun showAddingMobileDevice(connecting: MutableState<Boolean>) {
|
||||
}
|
||||
DisposableEffect(Unit) {
|
||||
withBGApi {
|
||||
val r = chatModel.controller.startRemoteHost(null)
|
||||
val r = chatModel.controller.startRemoteHost(null, controller.appPrefs.offerRemoteMulticast.get())
|
||||
if (r != null) {
|
||||
connecting.value = true
|
||||
invitation.value = r.second
|
||||
port.value = r.third
|
||||
chatModel.remoteHostPairing.value = null to RemoteHostSessionState.Starting
|
||||
}
|
||||
}
|
||||
onDispose {
|
||||
@@ -279,7 +284,7 @@ private fun showAddingMobileDevice(connecting: MutableState<Boolean>) {
|
||||
chatController.stopRemoteHost(null)
|
||||
}
|
||||
}
|
||||
chatModel.newRemoteHostPairing.value = null
|
||||
chatModel.remoteHostPairing.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -287,7 +292,7 @@ private fun showAddingMobileDevice(connecting: MutableState<Boolean>) {
|
||||
|
||||
private fun showConnectMobileDevice(rh: RemoteHostInfo, connecting: MutableState<Boolean>) {
|
||||
ModalManager.start.showModalCloseable { close ->
|
||||
val pairing = remember { chatModel.newRemoteHostPairing }
|
||||
val pairing = remember { chatModel.remoteHostPairing }
|
||||
val invitation = rememberSaveable { mutableStateOf<String?>(null) }
|
||||
val port = rememberSaveable { mutableStateOf<String?>(null) }
|
||||
val sessionCode = when (val state = pairing.value?.second) {
|
||||
@@ -308,13 +313,14 @@ private fun showConnectMobileDevice(rh: RemoteHostInfo, connecting: MutableState
|
||||
)
|
||||
var remoteHostId by rememberSaveable { mutableStateOf<Long?>(null) }
|
||||
LaunchedEffect(Unit) {
|
||||
val r = chatModel.controller.startRemoteHost(rh.remoteHostId)
|
||||
val r = chatModel.controller.startRemoteHost(rh.remoteHostId, controller.appPrefs.offerRemoteMulticast.get())
|
||||
if (r != null) {
|
||||
val (rh_, inv) = r
|
||||
connecting.value = true
|
||||
remoteHostId = rh_?.remoteHostId
|
||||
invitation.value = inv
|
||||
port.value = r.third
|
||||
chatModel.remoteHostPairing.value = null to RemoteHostSessionState.Starting
|
||||
}
|
||||
}
|
||||
LaunchedEffect(remember { chatModel.currentRemoteHost }.value) {
|
||||
@@ -334,7 +340,7 @@ private fun showConnectMobileDevice(rh: RemoteHostInfo, connecting: MutableState
|
||||
chatController.stopRemoteHost(remoteHostId)
|
||||
}
|
||||
}
|
||||
chatModel.newRemoteHostPairing.value = null
|
||||
chatModel.remoteHostPairing.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-7
@@ -28,7 +28,6 @@ import chat.simplex.common.model.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.item.ClickableText
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.views.helpers.annotatedStringResource
|
||||
import chat.simplex.res.MR
|
||||
|
||||
@@ -39,6 +38,7 @@ fun NetworkAndServersView(
|
||||
showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
|
||||
showCustomModal: (@Composable (ChatModel, () -> Unit) -> Unit) -> (() -> Unit),
|
||||
) {
|
||||
val currentRemoteHost by remember { chatModel.currentRemoteHost }
|
||||
// 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) }
|
||||
@@ -52,6 +52,7 @@ fun NetworkAndServersView(
|
||||
|
||||
val proxyPort = remember { derivedStateOf { chatModel.controller.appPrefs.networkProxyHostPort.state.value?.split(":")?.lastOrNull()?.toIntOrNull() ?: 9050 } }
|
||||
NetworkAndServersLayout(
|
||||
currentRemoteHost = currentRemoteHost,
|
||||
developerTools = developerTools,
|
||||
networkUseSocksProxy = networkUseSocksProxy,
|
||||
onionHosts = onionHosts,
|
||||
@@ -150,6 +151,7 @@ fun NetworkAndServersView(
|
||||
}
|
||||
|
||||
@Composable fun NetworkAndServersLayout(
|
||||
currentRemoteHost: RemoteHostInfo?,
|
||||
developerTools: Boolean,
|
||||
networkUseSocksProxy: MutableState<Boolean>,
|
||||
onionHosts: MutableState<OnionHosts>,
|
||||
@@ -172,14 +174,16 @@ fun NetworkAndServersView(
|
||||
|
||||
SettingsActionItem(painterResource(MR.images.ic_dns), stringResource(MR.strings.xftp_servers), showCustomModal { m, close -> ProtocolServersView(m, m.remoteHostId, ServerProtocol.XFTP, close) })
|
||||
|
||||
UseSocksProxySwitch(networkUseSocksProxy, proxyPort, toggleSocksProxy, showSettingsModal)
|
||||
UseOnionHosts(onionHosts, networkUseSocksProxy, showSettingsModal, useOnion)
|
||||
if (developerTools) {
|
||||
SessionModePicker(sessionMode, showSettingsModal, updateSessionMode)
|
||||
if (currentRemoteHost == null) {
|
||||
UseSocksProxySwitch(networkUseSocksProxy, proxyPort, toggleSocksProxy, showSettingsModal)
|
||||
UseOnionHosts(onionHosts, networkUseSocksProxy, showSettingsModal, useOnion)
|
||||
if (developerTools) {
|
||||
SessionModePicker(sessionMode, showSettingsModal, updateSessionMode)
|
||||
}
|
||||
SettingsActionItem(painterResource(MR.images.ic_cable), stringResource(MR.strings.network_settings), showSettingsModal { AdvancedNetworkSettingsView(it) })
|
||||
}
|
||||
SettingsActionItem(painterResource(MR.images.ic_cable), stringResource(MR.strings.network_settings), showSettingsModal { AdvancedNetworkSettingsView(it) })
|
||||
}
|
||||
if (networkUseSocksProxy.value) {
|
||||
if (currentRemoteHost == null && networkUseSocksProxy.value) {
|
||||
SectionCustomFooter {
|
||||
Column {
|
||||
Text(annotatedStringResource(MR.strings.disable_onion_hosts_when_not_supported))
|
||||
@@ -448,6 +452,7 @@ private fun showUpdateNetworkSettingsDialog(
|
||||
fun PreviewNetworkAndServersLayout() {
|
||||
SimpleXTheme {
|
||||
NetworkAndServersLayout(
|
||||
currentRemoteHost = null,
|
||||
developerTools = true,
|
||||
networkUseSocksProxy = remember { mutableStateOf(true) },
|
||||
proxyPort = remember { mutableStateOf(9050) },
|
||||
|
||||
+4
-1
@@ -21,7 +21,10 @@ import chat.simplex.res.MR
|
||||
fun PreferencesView(m: ChatModel, user: User, close: () -> Unit,) {
|
||||
var preferences by rememberSaveable(stateSaver = serializableSaver()) { mutableStateOf(user.fullPreferences) }
|
||||
var currentPreferences by rememberSaveable(stateSaver = serializableSaver()) { mutableStateOf(preferences) }
|
||||
|
||||
val u = remember { m.currentUser }
|
||||
KeyChangeEffect(u.value?.remoteHostId, u.value?.userId) {
|
||||
close()
|
||||
}
|
||||
fun savePrefs(afterSave: () -> Unit = {}) {
|
||||
withApi {
|
||||
val newProfile = user.profile.toProfile().copy(preferences = preferences.toPreferences())
|
||||
|
||||
+1
-1
@@ -155,7 +155,7 @@ fun SettingsLayout(
|
||||
}
|
||||
val profileHidden = rememberSaveable { mutableStateOf(false) }
|
||||
SettingsActionItem(painterResource(MR.images.ic_manage_accounts), stringResource(MR.strings.your_chat_profiles), { withAuth(generalGetString(MR.strings.auth_open_chat_profiles), generalGetString(MR.strings.auth_log_in_using_credential)) { showSettingsModalWithSearch { it, search -> UserProfilesView(it, search, profileHidden) } } }, disabled = stopped, extraPadding = true)
|
||||
SettingsActionItem(painterResource(MR.images.ic_qr_code), stringResource(MR.strings.your_simplex_contact_address), showCustomModal { it, close -> UserAddressView(it, it.currentUser.value?.remoteHostId, shareViaProfile = it.currentUser.value!!.addressShared, close = close) }, disabled = stopped, extraPadding = true)
|
||||
SettingsActionItem(painterResource(MR.images.ic_qr_code), stringResource(MR.strings.your_simplex_contact_address), showCustomModal { it, close -> UserAddressView(it, shareViaProfile = it.currentUser.value!!.addressShared, close = close) }, disabled = stopped, extraPadding = true)
|
||||
ChatPreferencesItem(showCustomModal, stopped = stopped)
|
||||
if (appPlatform.isDesktop) {
|
||||
SettingsActionItem(painterResource(MR.images.ic_smartphone), stringResource(if (remember { chatModel.remoteHosts }.isEmpty()) MR.strings.link_a_mobile else MR.strings.linked_mobiles), showModal { ConnectMobileView() }, disabled = stopped, extraPadding = true)
|
||||
|
||||
+13
-11
@@ -33,7 +33,6 @@ import chat.simplex.res.MR
|
||||
@Composable
|
||||
fun UserAddressView(
|
||||
chatModel: ChatModel,
|
||||
rhId: Long?,
|
||||
viaCreateLinkView: Boolean = false,
|
||||
shareViaProfile: Boolean = false,
|
||||
close: () -> Unit
|
||||
@@ -42,12 +41,15 @@ fun UserAddressView(
|
||||
val shareViaProfile = remember { mutableStateOf(shareViaProfile) }
|
||||
var progressIndicator by remember { mutableStateOf(false) }
|
||||
val onCloseHandler: MutableState<(close: () -> Unit) -> Unit> = remember { mutableStateOf({ _ -> }) }
|
||||
|
||||
val user = remember { chatModel.currentUser }
|
||||
KeyChangeEffect(user.value?.remoteHostId, user.value?.userId) {
|
||||
close()
|
||||
}
|
||||
fun setProfileAddress(on: Boolean) {
|
||||
progressIndicator = true
|
||||
withBGApi {
|
||||
try {
|
||||
val u = chatModel.controller.apiSetProfileAddress(rhId, on)
|
||||
val u = chatModel.controller.apiSetProfileAddress(user?.value?.remoteHostId, on)
|
||||
if (u != null) {
|
||||
chatModel.updateUser(u)
|
||||
}
|
||||
@@ -63,14 +65,14 @@ fun UserAddressView(
|
||||
val uriHandler = LocalUriHandler.current
|
||||
val showLayout = @Composable {
|
||||
UserAddressLayout(
|
||||
user = user.value,
|
||||
userAddress = userAddress.value,
|
||||
shareViaProfile,
|
||||
rhId,
|
||||
onCloseHandler,
|
||||
createAddress = {
|
||||
withApi {
|
||||
progressIndicator = true
|
||||
val connReqContact = chatModel.controller.apiCreateUserAddress(rhId)
|
||||
val connReqContact = chatModel.controller.apiCreateUserAddress(user?.value?.remoteHostId)
|
||||
if (connReqContact != null) {
|
||||
chatModel.userAddress.value = UserContactLinkRec(connReqContact)
|
||||
|
||||
@@ -115,7 +117,7 @@ fun UserAddressView(
|
||||
onConfirm = {
|
||||
progressIndicator = true
|
||||
withApi {
|
||||
val u = chatModel.controller.apiDeleteUserAddress(rhId)
|
||||
val u = chatModel.controller.apiDeleteUserAddress(user?.value?.remoteHostId)
|
||||
if (u != null) {
|
||||
chatModel.userAddress.value = null
|
||||
chatModel.updateUser(u)
|
||||
@@ -129,7 +131,7 @@ fun UserAddressView(
|
||||
},
|
||||
saveAas = { aas: AutoAcceptState, savedAAS: MutableState<AutoAcceptState> ->
|
||||
withBGApi {
|
||||
val address = chatModel.controller.userAddressAutoAccept(rhId, aas.autoAccept)
|
||||
val address = chatModel.controller.userAddressAutoAccept(user?.value?.remoteHostId, aas.autoAccept)
|
||||
if (address != null) {
|
||||
chatModel.userAddress.value = address
|
||||
savedAAS.value = aas
|
||||
@@ -168,9 +170,9 @@ fun UserAddressView(
|
||||
|
||||
@Composable
|
||||
private fun UserAddressLayout(
|
||||
user: User?,
|
||||
userAddress: UserContactLinkRec?,
|
||||
shareViaProfile: MutableState<Boolean>,
|
||||
rhId: Long?,
|
||||
onCloseHandler: MutableState<(close: () -> Unit) -> Unit>,
|
||||
createAddress: () -> Unit,
|
||||
learnMore: () -> Unit,
|
||||
@@ -183,7 +185,7 @@ private fun UserAddressLayout(
|
||||
Column(
|
||||
Modifier.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
AppBarTitle(stringResource(MR.strings.simplex_address), hostDevice(rhId), withPadding = false)
|
||||
AppBarTitle(stringResource(MR.strings.simplex_address), hostDevice(user?.remoteHostId), withPadding = false)
|
||||
Column(
|
||||
Modifier.fillMaxWidth().padding(bottom = DEFAULT_PADDING_HALF),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
@@ -432,6 +434,7 @@ private fun SaveAASButton(disabled: Boolean, onClick: () -> Unit) {
|
||||
fun PreviewUserAddressLayoutNoAddress() {
|
||||
SimpleXTheme {
|
||||
UserAddressLayout(
|
||||
user = User.sampleData,
|
||||
userAddress = null,
|
||||
createAddress = {},
|
||||
share = { _ -> },
|
||||
@@ -440,7 +443,6 @@ fun PreviewUserAddressLayoutNoAddress() {
|
||||
setProfileAddress = { _ -> },
|
||||
learnMore = {},
|
||||
shareViaProfile = remember { mutableStateOf(false) },
|
||||
rhId = null,
|
||||
onCloseHandler = remember { mutableStateOf({}) },
|
||||
sendEmail = {},
|
||||
)
|
||||
@@ -466,6 +468,7 @@ private fun showUnsavedChangesAlert(save: () -> Unit, revert: () -> Unit) {
|
||||
fun PreviewUserAddressLayoutAddressCreated() {
|
||||
SimpleXTheme {
|
||||
UserAddressLayout(
|
||||
user = User.sampleData,
|
||||
userAddress = UserContactLinkRec("https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D"),
|
||||
createAddress = {},
|
||||
share = { _ -> },
|
||||
@@ -474,7 +477,6 @@ fun PreviewUserAddressLayoutAddressCreated() {
|
||||
setProfileAddress = { _ -> },
|
||||
learnMore = {},
|
||||
shareViaProfile = remember { mutableStateOf(false) },
|
||||
rhId = null,
|
||||
onCloseHandler = remember { mutableStateOf({}) },
|
||||
sendEmail = {},
|
||||
)
|
||||
|
||||
+5
-1
@@ -29,7 +29,11 @@ import java.net.URI
|
||||
|
||||
@Composable
|
||||
fun UserProfileView(chatModel: ChatModel, close: () -> Unit) {
|
||||
val user = chatModel.currentUser.value
|
||||
val u = remember {chatModel.currentUser}
|
||||
val user = u.value
|
||||
KeyChangeEffect(u.value?.remoteHostId, u.value?.userId) {
|
||||
close()
|
||||
}
|
||||
if (user != null) {
|
||||
var profile by remember { mutableStateOf(user.profile.toProfile()) }
|
||||
UserProfileLayout(
|
||||
|
||||
@@ -1404,4 +1404,12 @@
|
||||
<string name="v5_3_simpler_incognito_mode_descr">فعّل وضع التخفي عند الاتصال.</string>
|
||||
<string name="member_contact_send_direct_message">أرسل رسالة مباشرة</string>
|
||||
<string name="rcv_group_event_member_created_contact">متصل مباشرةً</string>
|
||||
<string name="connect_plan_already_connecting">جارٍ الاتصال بالفعل!</string>
|
||||
<string name="v5_4_better_groups">مجموعات أفضل</string>
|
||||
<string name="rcv_group_and_other_events">و%d أحداث أخرى</string>
|
||||
<string name="connect_plan_already_joining_the_group">جارٍ انضمام بالفعل إلى المجموعة!</string>
|
||||
<string name="block_member_confirmation">حجب</string>
|
||||
<string name="bad_desktop_address">عنوان سطح المكتب غير صالح</string>
|
||||
<string name="block_member_desc">سيتم إخفاء كافة الرسائل الجديدة من %s!</string>
|
||||
<string name="blocked_item_description">محجوب</string>
|
||||
</resources>
|
||||
@@ -1670,6 +1670,8 @@
|
||||
<string name="desktop_connection_terminated">Connection terminated</string>
|
||||
<string name="session_code">Session code</string>
|
||||
<string name="connecting_to_desktop">Connecting to desktop</string>
|
||||
<string name="waiting_for_desktop">Waiting for desktop…</string>
|
||||
<string name="found_desktop">Found desktop</string>
|
||||
<string name="connect_to_desktop">Connect to desktop</string>
|
||||
<string name="connected_to_desktop">Connected to desktop</string>
|
||||
<string name="connected_desktop">Connected desktop</string>
|
||||
@@ -1681,9 +1683,12 @@
|
||||
<string name="scan_qr_code_from_desktop">Scan QR code from desktop</string>
|
||||
<string name="desktop_address">Desktop address</string>
|
||||
<string name="verify_connections">Verify connections</string>
|
||||
<string name="discover_on_network">Discover on network</string>
|
||||
<string name="discover_on_network">Discover via local network</string>
|
||||
<string name="multicast_discoverable_via_local_network">Discoverable via local network</string>
|
||||
<string name="multicast_connect_automatically">Connect automatically</string>
|
||||
<string name="paste_desktop_address">Paste desktop address</string>
|
||||
<string name="desktop_device">Desktop</string>
|
||||
<string name="not_compatible">Not compatible!</string>
|
||||
|
||||
<!-- Under development -->
|
||||
<string name="in_developing_title">Coming soon!</string>
|
||||
|
||||
@@ -1542,4 +1542,67 @@
|
||||
<string name="blocked_item_description">blockiert</string>
|
||||
<string name="encryption_renegotiation_error">Fehler bei der Neuverhandlung der Verschlüsselung</string>
|
||||
<string name="alert_text_encryption_renegotiation_failed">Neuverhandlung der Verschlüsselung fehlgeschlagen</string>
|
||||
<string name="v5_4_block_group_members">Gruppenmitglieder blockieren</string>
|
||||
<string name="v5_4_incognito_groups_descr">Erstellen Sie eine Gruppe mit einem zufälligen Profil.</string>
|
||||
<string name="connected_desktop">Verbundener Desktop</string>
|
||||
<string name="desktop_address">Desktop-Adresse</string>
|
||||
<string name="v5_4_better_groups">Bessere Gruppen</string>
|
||||
<string name="discover_on_network">Lokales Netzwerk durchsuchen</string>
|
||||
<string name="desktop_device">Desktop</string>
|
||||
<string name="connected_to_desktop">Mit dem Desktop verbunden</string>
|
||||
<string name="connecting_to_desktop">Mit dem Desktop verbinden</string>
|
||||
<string name="desktop_devices">Desktop-Geräte</string>
|
||||
<string name="connected_mobile">Verbundenes Mobiltelefon</string>
|
||||
<string name="desktop_connection_terminated">Verbindung beendet</string>
|
||||
<string name="enter_this_device_name">Geben Sie diesen Gerätenamen ein…</string>
|
||||
<string name="error">Fehler</string>
|
||||
<string name="connect_to_desktop">Mit dem Desktop verbinden</string>
|
||||
<string name="disconnect_remote_host">Trenne Verbindung</string>
|
||||
<string name="group_member_role_author">Autor</string>
|
||||
<string name="connected_to_mobile">Mit dem Mobiltelefon verbunden</string>
|
||||
<string name="bad_desktop_address">Falsche Desktop-Adresse</string>
|
||||
<string name="devices">Geräte</string>
|
||||
<string name="disconnect_desktop_question">Desktop-Verbindung trennen?</string>
|
||||
<string name="desktop_app_version_is_incompatible">Desktop-App-Version %s ist mit dieser App nicht kompatibel.</string>
|
||||
<string name="new_mobile_device">Neues Mobiltelefon-Gerät</string>
|
||||
<string name="only_one_device_can_work_at_the_same_time">Nur ein Gerät kann gleichzeitig genutzt werden</string>
|
||||
<string name="v5_4_link_mobile_desktop">Verknüpfe Mobiltelefon- und Desktop-Apps! 🔗</string>
|
||||
<string name="v5_4_link_mobile_desktop_descr">Über ein sicheres quantenbeständiges Protokoll</string>
|
||||
<string name="open_on_mobile_and_scan_qr_code"><![CDATA[Öffnen Sie in den Einstellungen der Mobiltelefon-App <i>Vom Desktop aus nutzen</i> und scannen Sie den QR-Code.]]></string>
|
||||
<string name="v5_4_block_group_members_descr">Um unerwünschte Nachrichten zu verbergen.</string>
|
||||
<string name="desktop_incompatible_version">Inkompatible Version</string>
|
||||
<string name="new_desktop"><![CDATA[<i>(Neu)</i>]]></string>
|
||||
<string name="unlink_desktop_question">Desktop entkoppeln?</string>
|
||||
<string name="linked_desktop_options">Verknüpfte Desktop-Optionen</string>
|
||||
<string name="linked_desktops">Verknüpfte Desktops</string>
|
||||
<string name="v5_4_incognito_groups">Inkognito-Gruppen</string>
|
||||
<string name="this_device">Dieses Gerät</string>
|
||||
<string name="remote_host_was_disconnected_toast"><![CDATA[Mobiltelefon <b>%s</b> wurde getrennt]]></string>
|
||||
<string name="v5_4_better_groups_descr">Schnellerer Gruppenbeitritt und zuverlässigere Nachrichtenzustellung.</string>
|
||||
<string name="linked_mobiles">Verknüpfte Mobiltelefone</string>
|
||||
<string name="this_device_name">Dieser Gerätename</string>
|
||||
<string name="waiting_for_mobile_to_connect_on_port"><![CDATA[Auf die Mobiltelefonverbindung über Port <i>%s</i> warten]]></string>
|
||||
<string name="loading_remote_file_title">Laden der Datei</string>
|
||||
<string name="link_a_mobile">Zu einem Mobiltelefon verbinden</string>
|
||||
<string name="settings_section_title_use_from_desktop">Vom Desktop aus nutzen</string>
|
||||
<string name="session_code">Sitzungscode</string>
|
||||
<string name="this_device_version"><![CDATA[<i>(Dieses Gerät hat v%s)</i>]]></string>
|
||||
<string name="unlink_desktop">Entkoppeln</string>
|
||||
<string name="this_device_name_shared_with_mobile">Der Gerätename wird mit dem verbundenen Mobiltelefon-Client geteilt.</string>
|
||||
<string name="verify_code_on_mobile">Code auf dem Mobiltelefon überprüfen</string>
|
||||
<string name="paste_desktop_address">Desktop-Adresse einfügen</string>
|
||||
<string name="verify_code_with_desktop">Code mit dem Desktop überprüfen</string>
|
||||
<string name="scan_qr_code_from_desktop">Den QR-Code vom Desktop scannen</string>
|
||||
<string name="v5_4_more_things_descr">- Optionale Benachrichtigung von gelöschten Kontakten.
|
||||
\n- Profilnamen mit Leerzeichen.
|
||||
\n- Und mehr!</string>
|
||||
<string name="scan_from_mobile">Vom Mobiltelefon scannen</string>
|
||||
<string name="verify_connections">Verbindungen überprüfen</string>
|
||||
<string name="loading_remote_file_desc">Bitte warten Sie, solange die Datei von dem verknüpften Mobiltelefon geladen wird</string>
|
||||
<string name="verify_connection">Verbindung überprüfen</string>
|
||||
<string name="multicast_connect_automatically">Automatisch verbinden</string>
|
||||
<string name="waiting_for_desktop">Auf Desktop warten…</string>
|
||||
<string name="found_desktop">Gefundener Desktop</string>
|
||||
<string name="not_compatible">Nicht kompatibel!</string>
|
||||
<string name="multicast_discoverable_via_local_network">Über das lokale Netzwerk auffindbar</string>
|
||||
</resources>
|
||||
@@ -1461,4 +1461,62 @@
|
||||
<string name="blocked_item_description">blocké</string>
|
||||
<string name="encryption_renegotiation_error">Erreur lors de la renégociation du chiffrement</string>
|
||||
<string name="alert_text_encryption_renegotiation_failed">La renégociation du chiffrement a échoué.</string>
|
||||
<string name="v5_4_block_group_members">Bloquer des membres d\'un groupe</string>
|
||||
<string name="v5_4_incognito_groups_descr">Création de groupes via un profil aléatoire.</string>
|
||||
<string name="connected_desktop">Bureau connecté</string>
|
||||
<string name="new_mobile_device">Nouvel appareil mobile</string>
|
||||
<string name="desktop_address">Adresse de bureau</string>
|
||||
<string name="only_one_device_can_work_at_the_same_time">Un seul appareil peut fonctionner en même temps</string>
|
||||
<string name="v5_4_link_mobile_desktop">Liez vos applications mobiles et de bureau ! 🔗</string>
|
||||
<string name="v5_4_link_mobile_desktop_descr">Via un protocole sécurisé de cryptographie post-quantique.</string>
|
||||
<string name="open_on_mobile_and_scan_qr_code"><![CDATA[Ouvrez <i>Utiliser depuis le bureau</i> dans l\'application mobile et scannez le code QR.]]></string>
|
||||
<string name="v5_4_block_group_members_descr">Pour cacher les messages indésirables.</string>
|
||||
<string name="desktop_incompatible_version">Version incompatible</string>
|
||||
<string name="new_desktop"><![CDATA[<i>(nouveau)</i>]]></string>
|
||||
<string name="unlink_desktop_question">Délier le bureau ?</string>
|
||||
<string name="v5_4_better_groups">Des groupes plus performants</string>
|
||||
<string name="linked_desktop_options">Options de bureau lié</string>
|
||||
<string name="linked_desktops">Bureaux liés</string>
|
||||
<string name="discover_on_network">Rechercher sur le réseau</string>
|
||||
<string name="v5_4_incognito_groups">Groupes incognito</string>
|
||||
<string name="this_device">Cet appareil</string>
|
||||
<string name="remote_host_was_disconnected_toast"><![CDATA[Le mobile <b>%s</b> a été déconnecté]]></string>
|
||||
<string name="v5_4_better_groups_descr">Connexion plus rapide et messages plus fiables.</string>
|
||||
<string name="linked_mobiles">Mobiles liés</string>
|
||||
<string name="desktop_device">Bureau</string>
|
||||
<string name="connected_to_desktop">Connecté au bureau</string>
|
||||
<string name="this_device_name">Ce nom d\'appareil</string>
|
||||
<string name="waiting_for_mobile_to_connect_on_port"><![CDATA[En attente d\'une connexion mobile sur le port <i>%s</i>]]></string>
|
||||
<string name="loading_remote_file_title">Chargement du fichier</string>
|
||||
<string name="connecting_to_desktop">Connexion au bureau</string>
|
||||
<string name="desktop_devices">Appareils de bureau</string>
|
||||
<string name="link_a_mobile">Lier un portable</string>
|
||||
<string name="settings_section_title_use_from_desktop">Utilisation depuis le bureau</string>
|
||||
<string name="connected_mobile">Mobile connecté</string>
|
||||
<string name="session_code">Code de session</string>
|
||||
<string name="desktop_connection_terminated">Connexion terminée</string>
|
||||
<string name="this_device_version"><![CDATA[<i>(cet appareil v%s)</i>]]></string>
|
||||
<string name="unlink_desktop">Délier</string>
|
||||
<string name="this_device_name_shared_with_mobile">Le nom de l\'appareil sera partagé avec le client mobile connecté.</string>
|
||||
<string name="verify_code_on_mobile">Vérifier le code sur le mobile</string>
|
||||
<string name="enter_this_device_name">Entrez le nom de l\'appareil…</string>
|
||||
<string name="error">Erreur</string>
|
||||
<string name="connect_to_desktop">Se connecter au bureau</string>
|
||||
<string name="disconnect_remote_host">Se déconnecter</string>
|
||||
<string name="group_member_role_author">auteur</string>
|
||||
<string name="connected_to_mobile">Connecté au portable</string>
|
||||
<string name="bad_desktop_address">Mauvaise adresse de bureau</string>
|
||||
<string name="paste_desktop_address">Coller l\'adresse du bureau</string>
|
||||
<string name="verify_code_with_desktop">Vérifier le code avec le bureau</string>
|
||||
<string name="scan_qr_code_from_desktop">Scanner le code QR du bureau</string>
|
||||
<string name="devices">Appareils</string>
|
||||
<string name="v5_4_more_things_descr">- option pour notifier les contacts supprimés.
|
||||
\n- noms de profil avec espaces.
|
||||
\n- et plus encore !</string>
|
||||
<string name="scan_from_mobile">Numériser depuis un mobile</string>
|
||||
<string name="verify_connections">Vérifier les connexions</string>
|
||||
<string name="disconnect_desktop_question">Déconnecter le bureau ?</string>
|
||||
<string name="loading_remote_file_desc">Veuillez patienter le temps que le fichier soit chargé depuis le mobile lié.</string>
|
||||
<string name="desktop_app_version_is_incompatible">La version de l\'application de bureau %s n\'est pas compatible avec cette application.</string>
|
||||
<string name="verify_connection">Vérifier la connexion</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,359 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="alert_text_decryption_error_n_messages_failed_to_decrypt">%1$d az üzenetek visszafejtése sikertelen</string>
|
||||
<string name="alert_text_decryption_error_too_many_skipped">%1$d üzenetek kihagyva.</string>
|
||||
<string name="integrity_msg_skipped">%1$d kihagyott üzenet(ek)</string>
|
||||
<string name="group_info_section_title_num_members">%1$s TAGOK</string>
|
||||
<string name="chat_item_ttl_month">1 hónap</string>
|
||||
<string name="chat_item_ttl_week">1 hét</string>
|
||||
<string name="v5_3_new_interface_languages">6 új kezelőfelület nyelv</string>
|
||||
<string name="send_disappearing_message_5_minutes">5 perc</string>
|
||||
<string name="send_disappearing_message_1_minute">1 perc</string>
|
||||
<string name="learn_more_about_address">A SimpleX azonosítóról</string>
|
||||
<string name="abort_switch_receiving_address_question">Címváltoztatás megszakítása?</string>
|
||||
<string name="abort_switch_receiving_address_confirm">Megszakítás</string>
|
||||
<string name="send_disappearing_message_30_seconds">30 másodperc</string>
|
||||
<string name="one_time_link_short">Egyszer használatos link</string>
|
||||
<string name="contact_wants_to_connect_via_call">%1$s szeretne kapcsolatba lépni veled</string>
|
||||
<string name="about_simplex_chat">A SimpleX chatről</string>
|
||||
<string name="chat_item_ttl_day">1 nap</string>
|
||||
<string name="abort_switch_receiving_address">Címváltoztatás megszakítása</string>
|
||||
<string name="about_simplex">A SimpleX-ről</string>
|
||||
<string name="color_primary">Kiemelőszín</string>
|
||||
<string name="callstatus_accepted">elfogadott hívás</string>
|
||||
<string name="network_enable_socks_info">Kapcsolódás a szerverekhez SOCKS proxy segítségével a %d? porton? A proxyt el kell indítani mielőtt bekapcsolná ezt az opciót.</string>
|
||||
<string name="accept_feature">Elfogad</string>
|
||||
<string name="accept_call_on_lock_screen">Elfogad</string>
|
||||
<string name="above_then_preposition_continuation">felül, majd pedig:</string>
|
||||
<string name="accept_contact_incognito_button">Elfogadás inkognítóban</string>
|
||||
<string name="accept_connection_request__question">Elfogadod a kapcsolatfelvételt?</string>
|
||||
<string name="accept_contact_button">Elfogad</string>
|
||||
<string name="accept">Elfogad</string>
|
||||
<string name="add_address_to_your_profile">Add hozzá az azonosítót a profilodhoz, így a kapcsolataid megosztatják azt más emberekkel. A profilod változtatásai így frissítésre kerülnek a kapcsolataidnál is!</string>
|
||||
<string name="color_primary_variant">További kiemelőszín</string>
|
||||
<string name="callstatus_error">hiba a hívásban</string>
|
||||
<string name="v5_4_block_group_members">Csoporttagok blokkolása</string>
|
||||
<string name="la_authenticate">Hitelesítés</string>
|
||||
<string name="empty_chat_profile_is_created">Egy üres chat profil létre lett hozva a megadott névvel és az app normál módon megnyílik.</string>
|
||||
<string name="feature_cancelled_item">megszakítva %s</string>
|
||||
<string name="smp_servers_preset_add">Adj hozzá egyedi szervereket</string>
|
||||
<string name="calls_prohibited_with_this_contact">Hang-/videóhívások megtiltva.</string>
|
||||
<string name="network_session_mode_entity_description">Külön TCP kapcsolat (és SOCKS bejelentkezési adatok) lesznek használva <b>minden ismerősre és csoport tagra</b>
|
||||
\n<b>Tudnivaló</b>: ha sok ismerősöd van, az akkumulátor- és adat használatod jelentősen megnőhet és néhány kapcsolódási kísérlet sikertelen lehet.</string>
|
||||
<string name="icon_descr_cancel_link_preview">URL link előnézet megszakítása</string>
|
||||
<string name="network_session_mode_user_description"><![CDATA[Külön TCP kapcsolat (és SOCKS bejelentkezési adatok) lesznek használva <b>minden chat profilodra az appban</b>.]]></string>
|
||||
<string name="both_you_and_your_contact_can_send_disappearing">Mindketten, te és az ismerősöd is küldhettek eltűnő üzeneteket.</string>
|
||||
<string name="keychain_is_storing_securely">Az Android Keystore-t jelmondat biztonságos tárolására használják - lehetővé teszi az értesítési szolgáltatás működését.</string>
|
||||
<string name="scan_QR_code_to_connect_to_contact_who_shows_QR_code"><![CDATA[<b>QR-kód beolvasása</b>: kapcsolódás ismerőshöz a megmutatott QR-kódja alapján]]></string>
|
||||
<string name="alert_title_msg_bad_hash">Téves üzenet hash</string>
|
||||
<string name="cant_delete_user_profile">Felhasználói profil törlése nem lehetséges!</string>
|
||||
<string name="color_background">Háttér</string>
|
||||
<string name="socks_proxy_setting_limitations"><![CDATA[<b>Tudnivaló</b>: az üzenet- és fájl relay szerverek SOCKS proxy által vannak kapcsolatban. A hívások és URL link előnézetek közvetlen kapcsolatot használnak.]]></string>
|
||||
<string name="full_backup">App adatmentés</string>
|
||||
<string name="database_initialization_error_title">Adatbázis inicializálása nem lehetséges</string>
|
||||
<string name="all_your_contacts_will_remain_connected_update_sent">A kapcsolat megmarad az összes Ismerősöddel. Profil változtatások frissítésre kerülnek az ismerőseidnél.</string>
|
||||
<string name="v4_5_transport_isolation_descr">Chat profile (alap beállítás) avagy kapcsolat által (BÉTA).</string>
|
||||
<string name="connect__a_new_random_profile_will_be_shared">Egy új véletlenszerű profil lesz megosztva.</string>
|
||||
<string name="allow_voice_messages_only_if">Hangüzenetek küldésének engedélyezése kizárólag az esetre ha a másik fél is engedélyezi.</string>
|
||||
<string name="app_version_code">App build: %s</string>
|
||||
<string name="audio_video_calls">Hang-/videóhívások</string>
|
||||
<string name="network_settings">Haladó hálózati beállítások</string>
|
||||
<string name="allow_your_contacts_to_send_voice_messages">Ismerősök küldhetnek hangüzeneteket engedélyezése.</string>
|
||||
<string name="settings_audio_video_calls">Hang- és videóhívások</string>
|
||||
<string name="v5_3_encrypt_local_files_descr">Az app titkosítja a helyi fájlokat (a videók kivételével).</string>
|
||||
<string name="answer_call">Hívás fogadása</string>
|
||||
<string name="allow_your_contacts_to_send_disappearing_messages">Ismerősök küldhetnek eltűnő üzeneteket engedélyezve.</string>
|
||||
<string name="connect_plan_already_connecting">Már kapcsolódik!</string>
|
||||
<string name="cannot_receive_file">Fájl fogadás nem lehetséges</string>
|
||||
<string name="auth_unavailable">Hitelesítés elérhetetlen</string>
|
||||
<string name="app_version_title">App verzió</string>
|
||||
<string name="button_add_welcome_message">Üdvözlőszöveg hozzáadása</string>
|
||||
<string name="snd_conn_event_ratchet_sync_started">titkosítás egyeztetése %s számára…</string>
|
||||
<string name="available_in_v51">"
|
||||
\nElérhető a v5.1-ben"</string>
|
||||
<string name="both_you_and_your_contacts_can_delete">Mindketten, te és az ismerősöd is visszaállíthatatlanul törölhettek elküldött üzeneteket.</string>
|
||||
<string name="v5_4_better_groups">Jobb csoportok</string>
|
||||
<string name="clear_chat_warning">Minden üzenet törlésre kerül - ez visszafordíthatatlan! Az üzenetek csak NÁLAD törlődnek.</string>
|
||||
<string name="icon_descr_call_ended">Hívás befejeződött</string>
|
||||
<string name="settings_section_title_calls">HÍVÁSOK</string>
|
||||
<string name="rcv_group_and_other_events">és %d egyéb események</string>
|
||||
<string name="address_section_title">Azonosító</string>
|
||||
<string name="connect_plan_already_joining_the_group">Már csatlakozik a csoporthoz!</string>
|
||||
<string name="auto_accept_contact">Automatikus elfogadás</string>
|
||||
<string name="notifications_mode_service_desc">Háttérszolgáltatás mindig fut - az értesítések azonnal megjelennek, amint üzenetek vannak.</string>
|
||||
<string name="allow_to_delete_messages">Elküldött üzenetek visszafordíthatatlan törlésének engedélyezése.</string>
|
||||
<string name="both_you_and_your_contact_can_send_voice">Mindketten, te és az ismerősöd is küldhettek hangüzeneteket.</string>
|
||||
<string name="alert_title_msg_bad_id">Téves üzenet ID</string>
|
||||
<string name="allow_your_contacts_adding_message_reactions">Üzenet rakciók -emojik- engedélyezése az ismerősei számára.</string>
|
||||
<string name="allow_to_send_voice">Hangüzenetek küldésének engedélyezése.</string>
|
||||
<string name="allow_message_reactions_only_if">Üzenet reakciók -emojik- engedélyezése kizárólag az esetre ha a másik fél is engedélyezi.</string>
|
||||
<string name="back">Vissza</string>
|
||||
<string name="it_can_disabled_via_settings_notifications_still_shown"><![CDATA[<b>Kikapcsolható a beállításokban</b> – az értesítések továbbra is megjelenítésre kerülnek amíg az app fut.]]></string>
|
||||
<string name="v4_2_group_links_desc">Adminok létrehozhatnak linkeket csoporthoz való csatlakozáshoz.</string>
|
||||
<string name="call_on_lock_screen">Hívások a lezárási képernyőn:</string>
|
||||
<string name="conn_event_ratchet_sync_started">titkosítás egyeztetése…</string>
|
||||
<string name="invite_prohibited">Ismerős meghívása sikertelen!</string>
|
||||
<string name="integrity_msg_bad_id">téves üzenet ID</string>
|
||||
<string name="v4_2_auto_accept_contact_requests">Ismerősnek jelölések automatikus elfogadása</string>
|
||||
<string name="impossible_to_recover_passphrase"><![CDATA[<b>Tudnivaló</b>: NEM fogod tudni helyreállítani vagy megváltoztatni a jelmondatot az esetben ha elveszíted.]]></string>
|
||||
<string name="callstatus_calling">hívás…</string>
|
||||
<string name="color_secondary_variant">További másodlagos</string>
|
||||
<string name="smp_servers_add_to_another_device">Hozzáadás másik eszközhöz</string>
|
||||
<string name="allow_message_reactions">Üzenet reakciók -emojik- engedélyezése.</string>
|
||||
<string name="icon_descr_cancel_file_preview">Fájl előnézet megszakítása</string>
|
||||
<string name="all_group_members_will_remain_connected">Minden csoporttag kapcsolatban marad.</string>
|
||||
<string name="onboarding_notifications_mode_service_desc"><![CDATA[<b>Több akkumulátort használ</b>! Háttérszolgáltatás mindig fut - értesítések megjelennek azonnal, ahogy új üzenetek érkeznek.]]></string>
|
||||
<string name="block_member_confirmation">Blokkolás</string>
|
||||
<string name="group_member_role_admin">admin</string>
|
||||
<string name="icon_descr_cancel_image_preview">Fénykép előnézet megszakítása</string>
|
||||
<string name="v5_1_self_destruct_passcode_descr">Minden adat törlődik amint bevitelre kerül.</string>
|
||||
<string name="icon_descr_video_asked_to_receive">Kérte a videó elfogadását</string>
|
||||
<string name="block_member_button">Tag blokkolása</string>
|
||||
<string name="v5_2_more_things">Néhány további dolog</string>
|
||||
<string name="authentication_cancelled">Hitelesítés megszakítva</string>
|
||||
<string name="allow_to_send_files">Fájlok és fotók/videók küldésének engedélyezése.</string>
|
||||
<string name="users_delete_all_chats_deleted">Minden chat illetve az összes üzenet törlésre kerül - ez visszafordíthatatlan!</string>
|
||||
<string name="icon_descr_audio_call">hanghívás</string>
|
||||
<string name="bold_text">vastagított</string>
|
||||
<string name="app_passcode_replaced_with_self_destruct">Az app számkód helyettesítésre kerül egy önmegsemmisítő számkóddal.</string>
|
||||
<string name="v5_3_new_interface_languages_descr">Arab, bulgár, finn, héber, thai és ukrán - köszönet a felhasználóknak és a Weblate-nek!</string>
|
||||
<string name="add_new_contact_to_create_one_time_QR_code"><![CDATA[<b>Új ismerős hozzáadása</b>: egyszer használatos QR-kód készítése az ismerős számára.]]></string>
|
||||
<string name="allow_voice_messages_question">Hangüzenetek engedélyezése?</string>
|
||||
<string name="always_use_relay">Mindig használt relay szervert</string>
|
||||
<string name="chat_preferences_always">mindig</string>
|
||||
<string name="call_already_ended">A hívás már befejeződött!</string>
|
||||
<string name="turn_off_battery_optimization_button">Engedélyez</string>
|
||||
<string name="all_your_contacts_will_remain_connected">A kapcsolat megmarad az összes Ismerősöddel.</string>
|
||||
<string name="icon_descr_cancel_live_message">Élő chat üzenet megszakítása</string>
|
||||
<string name="allow_irreversible_message_deletion_only_if">Helyreállíthatatlan üzenet törlés engedélyezése kizárólag az esetre ha a másik fél is engedélyezi.</string>
|
||||
<string name="v4_6_audio_video_calls">Hang- és videóhívások</string>
|
||||
<string name="integrity_msg_bad_hash">téves üzenet hash</string>
|
||||
<string name="notifications_mode_service">Mindig bekapcsolva</string>
|
||||
<string name="keychain_allows_to_receive_ntfs">Az Android Keystore fogja biztonságosan tárolni a jelmondatot app újraindítás vagy jelmondat változtatás után - lehetővé téve az értesítések fogadását.</string>
|
||||
<string name="all_app_data_will_be_cleared">Minden app adat törölve.</string>
|
||||
<string name="onboarding_notifications_mode_off_desc"><![CDATA[<b>Legjobb akkumulátoridő</b>. Kizárólag akkor kapsz értesítéseket amikor fut az app (NINCS háttérszolgáltatás).]]></string>
|
||||
<string name="appearance_settings">Megjelenés</string>
|
||||
<string name="turning_off_service_and_periodic">Akkumulátor optimizáció aktív, a háttérszolgáltatás és a rendszeres új üzenet ellenőrzés kikapcsolva. Újra bekapcsolhatod ezeket a beállításokban.</string>
|
||||
<string name="block_member_question">Tag blokkolása?</string>
|
||||
<string name="callstatus_ended">hívás befejeződött %1$s</string>
|
||||
<string name="onboarding_notifications_mode_periodic_desc"><![CDATA[<b>Jó akkumulátoridő</b>. A háttérszolgáltatás ellenőrzi az új üzeneteket 10 percenként. Hívásokról és fontos üzenetekről maradhatsz le.]]></string>
|
||||
<string name="group_member_role_author">szerző</string>
|
||||
<string name="allow_your_contacts_irreversibly_delete">Ismerősök visszafordíthatatlanul törölhetnek elküldött üzeneteket engedélyezve.</string>
|
||||
<string name="cancel_verb">Megszakítás</string>
|
||||
<string name="notifications_mode_off_desc">Az app csak akkor tud értesítéseket fogadni amikor fut, héttérszolgáltatás nem kerül elindításra.</string>
|
||||
<string name="v5_1_better_messages">Jobb üzenetek</string>
|
||||
<string name="abort_switch_receiving_address_desc">A cím változtatás megszakításra kerül. A régi fogadó cím marad használatban.</string>
|
||||
<string name="allow_verb">Engedélyez</string>
|
||||
<string name="bad_desktop_address">Rossz asztal cím</string>
|
||||
<string name="users_add">Adj hozzá profilt</string>
|
||||
<string name="attach">Csatolás</string>
|
||||
<string name="v5_0_app_passcode">App számkód</string>
|
||||
<string name="icon_descr_asked_to_receive">Kérte, hogy fogfaja a képet</string>
|
||||
<string name="use_camera_button">Fényképező</string>
|
||||
<string name="cannot_access_keychain">A Keystore-hoz nem sikerül hozzáférni az adatbázis jelszó elmentése végett</string>
|
||||
<string name="callstatus_in_progress">hívás folyamatban</string>
|
||||
<string name="auto_accept_images">Fényképek automatiklus elfogadása</string>
|
||||
<string name="allow_your_contacts_to_call">Hang- és videóhívás engedélyezése az ismerősei számára.</string>
|
||||
<string name="settings_section_title_icon">APP IKON</string>
|
||||
<string name="v4_3_improved_server_configuration_desc">Szerver hozzáadása QR kód befotózásával.</string>
|
||||
<string name="allow_to_send_disappearing">Eltünő üzenetek küldésének engedélyezése.</string>
|
||||
<string name="allow_disappearing_messages_only_if">Eltűnő üzenetek engedélyezése kizárólag az esetre ha a másik fél is engedélyezi.</string>
|
||||
<string name="icon_descr_audio_off">Hang kikapcsolva</string>
|
||||
<string name="allow_direct_messages">Közvetlen üzenetküldés tagok számára engedélyezett.</string>
|
||||
<string name="settings_section_title_app">APP</string>
|
||||
<string name="icon_descr_call_progress">Hívás folyamatban</string>
|
||||
<string name="both_you_and_your_contact_can_add_message_reactions">Mindketten, te és az ismerősöd is használhattok üzenet reakciókat (emojik).</string>
|
||||
<string name="both_you_and_your_contact_can_make_calls">Mindketten, te és az ismerősöd is tudok hívásokat indítani.</string>
|
||||
<string name="la_auth_failed">Hitelesítés sikertelen</string>
|
||||
<string name="block_member_desc">Minden új üzenet %s -tól/től elrejtésre kerül.</string>
|
||||
<string name="app_version_name">App verzió: v%s</string>
|
||||
<string name="allow_calls_only_if">Hívások engedélyezése kizárólag az esetre ha a másik fél is engedélyezi.</string>
|
||||
<string name="smp_servers_add">Szerver hozzáadása…</string>
|
||||
<string name="icon_descr_audio_on">Hang bekapcsolva</string>
|
||||
<string name="audio_call_no_encryption">hanghívás (nem e2e titkosított)</string>
|
||||
<string name="blocked_item_description">blokkolva</string>
|
||||
<string name="change_database_passphrase_question">Adatbázis jelmondat megváltoztatása?</string>
|
||||
<string name="callstate_connected">kapcsolódva</string>
|
||||
<string name="la_change_app_passcode">Számkód megváltoztatása</string>
|
||||
<string name="rcv_group_event_changed_member_role">%s to %s megváltozott szerepköre</string>
|
||||
<string name="switch_receiving_address">Fogadó szerver cím megváltoztatása</string>
|
||||
<string name="change_verb">Változtatás</string>
|
||||
<string name="confirm_passcode">Számkód megerősítése</string>
|
||||
<string name="confirm_password">Jelszó megerősítése</string>
|
||||
<string name="change_member_role_question">Csoport szerepkör megváltoztatása?</string>
|
||||
<string name="change_lock_mode">Lezárási mód megváltoztatása</string>
|
||||
<string name="notification_contact_connected">Kapcsolódva</string>
|
||||
<string name="rcv_group_event_member_connected">kapcsolódva</string>
|
||||
<string name="connect_via_link_verb">Kapcsolódás</string>
|
||||
<string name="group_member_status_connected">kapcsolódva</string>
|
||||
<string name="connected_mobile">Összekapcsolt telefon</string>
|
||||
<string name="server_connected">kapcsolódva</string>
|
||||
<string name="change_role">Szerepkör megváltoztatása</string>
|
||||
<string name="icon_descr_server_status_connected">Kapcsolódva</string>
|
||||
<string name="auth_confirm_credential">Belépési adatok megerősítése</string>
|
||||
<string name="switch_receiving_address_question">Fogadó szerver cím megváltoztatása</string>
|
||||
<string name="rcv_conn_event_switch_queue_phase_completed">megváltozott azonosító számodra</string>
|
||||
<string name="change_self_destruct_mode">Önmegsemmisítő mód megváltoztatása</string>
|
||||
<string name="rcv_group_event_changed_your_role">a szerepköröd megváltoztatva %s-ra(-re)</string>
|
||||
<string name="connect_button">Kapcsolódás</string>
|
||||
<string name="connect_via_member_address_alert_title">Kapcsolódás közvetlenül?</string>
|
||||
<string name="smp_server_test_connect">Kapcsolódás</string>
|
||||
<string name="rcv_group_event_member_created_contact">közvetlenül kapcsolódva</string>
|
||||
<string name="connection_local_display_name">kapcsolat %1$d</string>
|
||||
<string name="status_contact_has_e2e_encryption">az ismerősnél az e2e titkosítás elérhető</string>
|
||||
<string name="v5_4_incognito_groups_descr">Csoport létrehozása véletlenszerűen létrehozott profillal.</string>
|
||||
<string name="delete_contact_all_messages_deleted_cannot_undo_warning">Az ismerős és az összes üzenet törlésre kerül - ez visszafordíthatatlan!</string>
|
||||
<string name="contacts_can_mark_messages_for_deletion">Ismerősök megjelölhetik az üzeneteket törlendőként; de láthatod azokat.</string>
|
||||
<string name="connect_via_invitation_link">Kapcsolódás egy Egyszer használatos linkkel?</string>
|
||||
<string name="connect_via_link_or_qr">Kapcsolódás egy link / QR-kód által</string>
|
||||
<string name="connection_error_auth">Kapcsolódási hiba (AUTH)</string>
|
||||
<string name="notification_preview_mode_contact">Ismerős neve</string>
|
||||
<string name="connect_via_contact_link">Kapcsolódás ismerős azonosítója által?</string>
|
||||
<string name="create_address">Azonosító létrehozása</string>
|
||||
<string name="copy_verb">Másolás</string>
|
||||
<string name="continue_to_next_step">Folytatás</string>
|
||||
<string name="connect_plan_connect_via_link">Kapcsolódás egy linken keresztül?</string>
|
||||
<string name="contact_already_exists">Az ismerős már létezik</string>
|
||||
<string name="core_version">Fő verzió: v%s</string>
|
||||
<string name="icon_descr_contact_checked">Ismerős ellenőrizve</string>
|
||||
<string name="connect_plan_connect_to_yourself">Kapcsolódás saját magához?</string>
|
||||
<string name="copied">Kimásolva a vágólapra</string>
|
||||
<string name="connection_request_sent">Kapcsolódási kérés elküldve!</string>
|
||||
<string name="connecting_to_desktop">Kapcsolódás az asztalhoz</string>
|
||||
<string name="network_session_mode_entity">Kapcsolat</string>
|
||||
<string name="correct_name_to">Név helyesbítése erre: %s?</string>
|
||||
<string name="connection_timeout">Kapcsolat időtúllépés</string>
|
||||
<string name="connect_with_contact_name_question">Kapcsolódás %1$s által?</string>
|
||||
<string name="create_profile_button">Létrehozás</string>
|
||||
<string name="contact_preferences">Ismerős beállításai</string>
|
||||
<string name="info_row_connection">Kapcsolat</string>
|
||||
<string name="desktop_connection_terminated">Kapcsolat megszakítva</string>
|
||||
<string name="display_name_connection_established">Kapcsolat létrehozva</string>
|
||||
<string name="status_contact_has_no_e2e_encryption">az ismerősnél az e2e titkosítás nem elérhető</string>
|
||||
<string name="chat_preferences_contact_allows">Az ismerős engedélyezi</string>
|
||||
<string name="notification_preview_somebody">Ismerős elrejtve:</string>
|
||||
<string name="connect_to_desktop">Kapcsolódás az asztalhoz</string>
|
||||
<string name="icon_descr_context">Kontextus ikon</string>
|
||||
<string name="connect_via_link">Kapcsolódás egy linken keresztül</string>
|
||||
<string name="receipts_section_contacts">Ismerősök</string>
|
||||
<string name="connection_error">Kapcsolódási hiba</string>
|
||||
<string name="alert_title_contact_connection_pending">Az ismerős még nem kapcsolódott!</string>
|
||||
<string name="v5_3_discover_join_groups_descr">- kapcsolódás könyvtár szolgáltatáshoz (BÉTA)!
|
||||
\n- kézbesítési igazolások (20 tagig).
|
||||
\n- gyorsabb és stabilabb</string>
|
||||
<string name="contribute">Hozzájárulás</string>
|
||||
<string name="group_member_status_intro_invitation">kapcsolódás (meghívás bemutatkozásra)</string>
|
||||
<string name="create_simplex_address">SimpleX azonosító létrehozása</string>
|
||||
<string name="rcv_direct_event_contact_deleted">törölt ismerős</string>
|
||||
<string name="delete_member_message__question">Tag üzenetének törlése?</string>
|
||||
<string name="chat_is_running">A chat szolgáltatás működik (fut)</string>
|
||||
<string name="share_one_time_link">Egyszer használatos meghívó link létrehozása</string>
|
||||
<string name="delete_link">Link törlése</string>
|
||||
<string name="notifications_mode_periodic_desc">Új üzenetek ellenőrzése 10 percenként, legfeljebb 1 percen keresztül.</string>
|
||||
<string name="delete_database">Adatbázis törlése</string>
|
||||
<string name="create_group_button">Csoport létrehozása</string>
|
||||
<string name="network_session_mode_user">Chat profil</string>
|
||||
<string name="create_another_profile_button">Profil létrehozása</string>
|
||||
<string name="connected_desktop">Csatlakoztatott asztal</string>
|
||||
<string name="share_text_deleted_at">Törölve ekkor: %s</string>
|
||||
<string name="info_row_deleted_at">Törölve ekkor</string>
|
||||
<string name="v4_6_chinese_spanish_interface">Kínai és spanyol kezelőfelület.</string>
|
||||
<string name="alert_title_cant_invite_contacts">Ismerősök meghívása nem lehetséges!</string>
|
||||
<string name="chat_is_stopped_indication">A chat szolgáltatás leállt (nem fut)</string>
|
||||
<string name="theme_dark">Sötét</string>
|
||||
<string name="create_profile">Profil létrehozása</string>
|
||||
<string name="rcv_group_event_group_deleted">törölt csoport</string>
|
||||
<string name="full_deletion">Törlés mindenkinek</string>
|
||||
<string name="button_create_group_link">Link létrehozása</string>
|
||||
<string name="chat_preferences">Chat beállítások</string>
|
||||
<string name="chat_archive_header">Chat archívum</string>
|
||||
<string name="delete_profile">Profil törlése</string>
|
||||
<string name="la_current_app_passcode">Jelenlegi Számkód</string>
|
||||
<string name="group_member_status_connecting">kapcsolódás</string>
|
||||
<string name="confirm_new_passphrase">Új jelmondat megerősítése…</string>
|
||||
<string name="group_connection_pending">kapcsolódás…</string>
|
||||
<string name="delete_chat_profile">Chat profil törlés</string>
|
||||
<string name="custom_time_picker_custom">egyedi</string>
|
||||
<string name="callstatus_connecting">hívás kapcsolódik…</string>
|
||||
<string name="customize_theme_title">Színséma személyreszabása</string>
|
||||
<string name="maximum_supported_file_size">Jelenleg támogatott legnagyobb fájl méret: %1$s.</string>
|
||||
<string name="smp_server_test_delete_file">Fájl törlése</string>
|
||||
<string name="in_developing_title">Hamarosan!</string>
|
||||
<string name="snd_conn_event_switch_queue_phase_changing_for_member">azonosító megváltoztatása %s számára…</string>
|
||||
<string name="chat_database_imported">Chat adatbázis importálva</string>
|
||||
<string name="chat_archive_section">CHAT ARCHÍVUM</string>
|
||||
<string name="delete_messages">Üzenetek törlése?</string>
|
||||
<string name="clear_chat_menu_action">Kiürítés</string>
|
||||
<string name="icon_descr_close_button">Bezárás gomb</string>
|
||||
<string name="chat_is_stopped">A chat szolgáltatás leállt (nem fut)</string>
|
||||
<string name="item_info_current">(jelenlegi)</string>
|
||||
<string name="v5_1_custom_themes_descr">Színsémák személyreszabása és megosztása</string>
|
||||
<string name="delete_chat_profile_question">Chat profil törlése?</string>
|
||||
<string name="create_group">Titkos csoport létrehozása</string>
|
||||
<string name="connected_to_desktop">Kapcsolódva az asztalhoz</string>
|
||||
<string name="configure_ICE_servers">ICE sezrverek beállítása</string>
|
||||
<string name="button_delete_group">Csoport törlése</string>
|
||||
<string name="clear_verification">Chat hitelesításe</string>
|
||||
<string name="group_member_status_creator">szerző</string>
|
||||
<string name="confirm_verb">Megerősítés</string>
|
||||
<string name="for_me_only">Törlés nálam</string>
|
||||
<string name="delete_messages__question">%d üzenet törlése?</string>
|
||||
<string name="v5_1_custom_themes">Egyedi színsémák</string>
|
||||
<string name="group_member_status_accepted">kapcsolódás (elfogadva)</string>
|
||||
<string name="smp_servers_check_address">Szerver cím ellenőrzése és újrapróbálkozás.</string>
|
||||
<string name="delete_group_question">Csoport törlése?</string>
|
||||
<string name="confirm_database_upgrades">Adatbázis frissítés megerősítése</string>
|
||||
<string name="create_your_profile">Saját profil létrehozása</string>
|
||||
<string name="snd_conn_event_switch_queue_phase_changing">azonosító megváltoztatása…</string>
|
||||
<string name="display_name_connecting">kapcsolódás…</string>
|
||||
<string name="icon_descr_call_connecting">Hívás kapcsolása</string>
|
||||
<string name="delete_files_and_media_question">Fájlok illetve fotók/videók törlése?</string>
|
||||
<string name="group_member_status_complete">befejezve</string>
|
||||
<string name="chat_database_section">CHAT ADATBÁZIS</string>
|
||||
<string name="change_self_destruct_passcode">Önmegsemmisító számkód megváltoztatása</string>
|
||||
<string name="smp_server_test_create_queue">Várólista létrehozása</string>
|
||||
<string name="colored_text">színes</string>
|
||||
<string name="callstate_connecting">kapcsolódás…</string>
|
||||
<string name="dark_theme">Sötét színséma</string>
|
||||
<string name="deleted_description">törölve</string>
|
||||
<string name="users_delete_question">Chat profil törlése?</string>
|
||||
<string name="chat_with_developers">Chat a SimpleX fejlesztőivel</string>
|
||||
<string name="delete_link_question">Link törlése?</string>
|
||||
<string name="server_connecting">kapcsolódás</string>
|
||||
<string name="send_disappearing_message_custom_time">Személyreszabott idő</string>
|
||||
<string name="connect_via_link_incognito">Inkognítóban csatlakozva</string>
|
||||
<string name="settings_section_title_chats">CHATEK</string>
|
||||
<string name="v5_3_new_desktop_app_descr">Új profil létrehozása a számítógépen futó appban. 💻</string>
|
||||
<string name="group_member_status_announced">kapcsolódás (bejelentve)</string>
|
||||
<string name="contact_connection_pending">kapcsolódás…</string>
|
||||
<string name="chat_database_deleted">Chat adatbázis törölve</string>
|
||||
<string name="group_member_status_introduced">kapcsolódás (bejelentve)</string>
|
||||
<string name="create_group_link">Csoporthoz link létrehozása</string>
|
||||
<string name="chat_console">Chat konzol</string>
|
||||
<string name="delete_files_and_media_for_all_users">Fájlok törlése minden chat profil alatt</string>
|
||||
<string name="smp_server_test_delete_queue">Várólista törlése</string>
|
||||
<string name="button_delete_contact">Ismerős törlése</string>
|
||||
<string name="archive_created_on_ts">Létrehozva: %1$s</string>
|
||||
<string name="rcv_conn_event_switch_queue_phase_changing">azonosító megváltoztatása…</string>
|
||||
<string name="connected_to_mobile">Kapcsolódva a mobilhoz</string>
|
||||
<string name="current_passphrase">Jelenlegi jelmondat…</string>
|
||||
<string name="choose_file_title">Fájl választása</string>
|
||||
<string name="create_one_time_link">Egyszer használatos meghívó link létrehozása</string>
|
||||
<string name="delete_image">Kép törlése</string>
|
||||
<string name="smp_server_test_create_file">Fájl létrehozása</string>
|
||||
<string name="create_secret_group_title">Tikos csoport létrehozása</string>
|
||||
<string name="clear_contacts_selection_button">Kiürítés</string>
|
||||
<string name="delete_contact_question">Ismerős törlése?</string>
|
||||
<string name="clear_verb">Kiürítés</string>
|
||||
<string name="create_address_and_let_people_connect">Hozz létre egy azonosítót, hogy az ismerősök kapcsolatba léphessenek veled.</string>
|
||||
<string name="v4_4_verify_connection_security_desc">Biztonsági kódok ösezhasonlítása az ismerősökkel.</string>
|
||||
<string name="smp_server_test_compare_file">Fájl összehasonlítása</string>
|
||||
<string name="your_chats">Chatek</string>
|
||||
<string name="delete_message__question">Üzenet törlése?</string>
|
||||
<string name="delete_pending_connection__question">Függő kapcsolatfelvételi kérés törlése?</string>
|
||||
<string name="database_encrypted">Adatbázis titkosítva!</string>
|
||||
<string name="clear_chat_question">Chat kiürítése?</string>
|
||||
<string name="database_downgrade">Adatbázis downgrade?</string>
|
||||
<string name="clear_chat_button">Chat kiürítése</string>
|
||||
<string name="database_passphrase_will_be_updated">Adatbázis titkosítási jelmondat meg lesz változtatva.</string>
|
||||
</resources>
|
||||
@@ -1505,4 +1505,18 @@
|
||||
<string name="loading_remote_file_desc">Si prega di attendere mentre il file viene caricato dal cellulare collegato</string>
|
||||
<string name="desktop_app_version_is_incompatible">La versione dell\'app desktop %s non è compatibile con questa app.</string>
|
||||
<string name="verify_connection">Verifica la connessione</string>
|
||||
<string name="v5_4_block_group_members">Blocca i membri dei gruppi</string>
|
||||
<string name="v5_4_incognito_groups_descr">Crea un gruppo usando un profilo casuale.</string>
|
||||
<string name="v5_4_link_mobile_desktop">Collega le app mobile e desktop! 🔗</string>
|
||||
<string name="v5_4_link_mobile_desktop_descr">Tramite protocollo sicuro resistente alla quantistica.</string>
|
||||
<string name="v5_4_block_group_members_descr">Per nascondere messaggi indesiderati.</string>
|
||||
<string name="v5_4_better_groups">Gruppi migliorati</string>
|
||||
<string name="v5_4_incognito_groups">Gruppi in incognito</string>
|
||||
<string name="remote_host_was_disconnected_toast"><![CDATA[Il cellulare <b>%s</b> è stato disconnesso]]></string>
|
||||
<string name="v5_4_better_groups_descr">Ingresso più veloce e messaggi più affidabili.</string>
|
||||
<string name="v5_4_more_things_descr">- avvisa facoltativamente i contatti eliminati.
|
||||
\n- nomi del profilo con spazi.
|
||||
\n- e molto altro!</string>
|
||||
<string name="waiting_for_mobile_to_connect_on_port"><![CDATA[In attesa che il cellulare si connetta alla porta <i>%s</i>]]></string>
|
||||
<string name="group_member_role_author">autore</string>
|
||||
</resources>
|
||||
@@ -1469,7 +1469,7 @@
|
||||
<string name="unlink_desktop_question">Desktop ontkoppelen?</string>
|
||||
<string name="linked_desktop_options">Gekoppelde desktop opties</string>
|
||||
<string name="linked_desktops">Gelinkte desktops</string>
|
||||
<string name="discover_on_network">Ontdek via netwerk</string>
|
||||
<string name="discover_on_network">Ontdek via het lokale netwerk</string>
|
||||
<string name="this_device">Dit apparaat</string>
|
||||
<string name="linked_mobiles">Gekoppelde mobiele apparaten</string>
|
||||
<string name="desktop_device">Desktop</string>
|
||||
@@ -1503,4 +1503,23 @@
|
||||
<string name="loading_remote_file_desc">Wacht terwijl het bestand wordt geladen vanaf de gekoppelde mobiele telefoon</string>
|
||||
<string name="desktop_app_version_is_incompatible">Desktop-app-versie %s is niet compatibel met deze app.</string>
|
||||
<string name="verify_connection">Controleer de verbinding</string>
|
||||
<string name="v5_4_block_group_members">Groepsleden blokkeren</string>
|
||||
<string name="v5_4_incognito_groups_descr">Maak een groep met een willekeurig profiel.</string>
|
||||
<string name="v5_4_link_mobile_desktop">Koppel mobiele en desktop-apps! 🔗</string>
|
||||
<string name="v5_4_link_mobile_desktop_descr">Via een beveiligd kwantumbestendig protocol.</string>
|
||||
<string name="v5_4_block_group_members_descr">Om ongewenste berichten te verbergen.</string>
|
||||
<string name="v5_4_better_groups">Betere groepen</string>
|
||||
<string name="v5_4_incognito_groups">Incognitogroepen</string>
|
||||
<string name="v5_4_better_groups_descr">Snellere deelname en betrouwbaardere berichten.</string>
|
||||
<string name="v5_4_more_things_descr">- optioneel verwijderde contacten op de hoogte stellen.
|
||||
\n- profielnamen met spaties.
|
||||
\n- en meer!</string>
|
||||
<string name="remote_host_was_disconnected_toast"><![CDATA[Mobiele verbinding <b>%s</b> is verbroken]]></string>
|
||||
<string name="group_member_role_author">auteur</string>
|
||||
<string name="waiting_for_mobile_to_connect_on_port"><![CDATA[Wachten tot mobiel verbinding maakt op poort <i>%s</i>]]></string>
|
||||
<string name="multicast_connect_automatically">Automatisch verbinden</string>
|
||||
<string name="waiting_for_desktop">Wachten op desktop…</string>
|
||||
<string name="found_desktop">Desktop gevonden</string>
|
||||
<string name="not_compatible">Niet compatibel!</string>
|
||||
<string name="multicast_discoverable_via_local_network">Vindbaar via lokaal netwerk</string>
|
||||
</resources>
|
||||
@@ -3,11 +3,11 @@
|
||||
<string name="app_name">SimpleX</string>
|
||||
<string name="thousand_abbreviation">т</string>
|
||||
<!-- Connect via Link - MainActivity.kt -->
|
||||
<string name="connect_via_contact_link">Соединиться через ссылку-контакт?</string>
|
||||
<string name="connect_via_invitation_link">Соединиться через ссылку-приглашение?</string>
|
||||
<string name="connect_via_group_link">Соединиться через ссылку группы?</string>
|
||||
<string name="connect_via_contact_link">Соединиться через адрес?</string>
|
||||
<string name="connect_via_invitation_link">Соединиться через одноразовую ссылку?</string>
|
||||
<string name="connect_via_group_link">Вступить в группу?</string>
|
||||
<string name="profile_will_be_sent_to_contact_sending_link">Ваш профиль будет отправлен контакту, от которого Вы получили эту ссылку.</string>
|
||||
<string name="you_will_join_group">Вы вступите в группу, на которую ссылается эта ссылка.</string>
|
||||
<string name="you_will_join_group">Вы соединитесь со всеми членами группы.</string>
|
||||
<string name="connect_via_link_verb">Соединиться</string>
|
||||
<!-- Server info - ChatModel.kt -->
|
||||
<string name="server_connected">соединено</string>
|
||||
@@ -435,7 +435,7 @@
|
||||
<string name="your_profile_is_stored_on_your_device">Ваш профиль, контакты и доставленные сообщения хранятся на Вашем устройстве.</string>
|
||||
<string name="profile_is_only_shared_with_your_contacts">Профиль отправляется только Вашим контактам.</string>
|
||||
<string name="display_name_cannot_contain_whitespace">Имя профиля не может содержать пробелы.</string>
|
||||
<string name="display_name">Имя профиля</string>
|
||||
<string name="display_name">Введите ваше имя:</string>
|
||||
<string name="create_profile_button">Создать</string>
|
||||
<string name="about_simplex">О SimpleX</string>
|
||||
<!-- markdown demo - MarkdownHelpView.kt -->
|
||||
@@ -818,8 +818,8 @@
|
||||
<string name="switch_receiving_address">Переключить адрес получения</string>
|
||||
<!-- AddGroupView.kt -->
|
||||
<string name="create_secret_group_title">Создать скрытую группу</string>
|
||||
<string name="group_is_decentralized">Группа полностью децентрализована — она видна только членам.</string>
|
||||
<string name="group_display_name_field">Имя группы:</string>
|
||||
<string name="group_is_decentralized">Группа полностью децентрализована – она видна только членам.</string>
|
||||
<string name="group_display_name_field">Введите имя группы:</string>
|
||||
<string name="group_full_name_field">Полное имя:</string>
|
||||
<string name="group_main_profile_sent">Ваш профиль чата будет отправлен членам группы</string>
|
||||
<!-- GroupProfileView.kt -->
|
||||
@@ -1489,4 +1489,114 @@
|
||||
<string name="error_sending_message_contact_invitation">Ошибка отправки приглашения</string>
|
||||
<string name="member_contact_send_direct_message">Послать прямое сообщение</string>
|
||||
<string name="rcv_group_event_member_created_contact">соединен напрямую</string>
|
||||
<string name="expand_verb">Раскрыть</string>
|
||||
<string name="v5_4_block_group_members">Блокируйте членов группы</string>
|
||||
<string name="connect_plan_repeat_connection_request">Повторить запрос на соединение?</string>
|
||||
<string name="encryption_renegotiation_error">Ошибка нового соглашения о шифровании</string>
|
||||
<string name="rcv_direct_event_contact_deleted">удалил(а) контакт</string>
|
||||
<string name="error_alert_title">Ошибка</string>
|
||||
<string name="v5_4_incognito_groups_descr">Создайте группу, используя случайный профиль.</string>
|
||||
<string name="create_group_button">Создать группу</string>
|
||||
<string name="create_another_profile_button">Создать профиль</string>
|
||||
<string name="connected_desktop">Подключенный компьютер</string>
|
||||
<string name="group_members_2">%s и %s</string>
|
||||
<string name="new_mobile_device">Новое мобильное устройство</string>
|
||||
<string name="desktop_address">Адрес компьютера</string>
|
||||
<string name="only_one_device_can_work_at_the_same_time">Одновременно может работать только одно устройство</string>
|
||||
<string name="connect_plan_join_your_group">Вступить в вашу группу?</string>
|
||||
<string name="v5_4_link_mobile_desktop">Свяжите мобильное и настольное приложения! 🔗</string>
|
||||
<string name="marked_deleted_items_description">%d сообщений помечено удалёнными</string>
|
||||
<string name="connect_plan_group_already_exists">Группа уже существует!</string>
|
||||
<string name="open_on_mobile_and_scan_qr_code"><![CDATA[Откройте <i>Использовать с компьютера</i> в мобильном приложении и сосканируйте QR код]]></string>
|
||||
<string name="connect_plan_already_connecting">Уже соединяется!</string>
|
||||
<string name="desktop_incompatible_version">Несовместимая версия</string>
|
||||
<string name="new_desktop"><![CDATA[<i>(новое)</i>]]></string>
|
||||
<string name="v5_4_better_groups">Улучшенные группы</string>
|
||||
<string name="linked_desktop_options">Опции связанных компьютеров</string>
|
||||
<string name="linked_desktops">Связанные компьютеры</string>
|
||||
<string name="discover_on_network">Обнаружение по локальной сети</string>
|
||||
<string name="rcv_group_and_other_events">и %d других событий</string>
|
||||
<string name="connect_plan_connect_via_link">Соединиться через ссылку?</string>
|
||||
<string name="v5_4_incognito_groups">Инкогнито группы</string>
|
||||
<string name="connect_plan_already_joining_the_group">Вступление в группу уже начато!</string>
|
||||
<string name="moderated_items_description">%d сообщений модерировано членом %s</string>
|
||||
<string name="remote_host_was_disconnected_toast"><![CDATA[Удаленный хост был отключен: <b>%s</b>]]></string>
|
||||
<string name="v5_4_better_groups_descr">Быстрое вступление и надежная доставка сообщений.</string>
|
||||
<string name="connect_plan_connect_to_yourself">Соединиться с самим собой?</string>
|
||||
<string name="linked_mobiles">Связанные мобильные</string>
|
||||
<string name="desktop_device">Компьютер</string>
|
||||
<string name="connected_to_desktop">Компьютер подключен</string>
|
||||
<string name="loading_remote_file_title">Загрузка файла</string>
|
||||
<string name="connecting_to_desktop">Подключение к компьютеру</string>
|
||||
<string name="alert_text_encryption_renegotiation_failed">Ошибка нового соглашения о шифровании</string>
|
||||
<string name="desktop_devices">Компьютеры</string>
|
||||
<string name="correct_name_to">Исправить имя на %s?</string>
|
||||
<string name="delete_messages__question">Удалить %d сообщений?</string>
|
||||
<string name="link_a_mobile">Связать мобильный</string>
|
||||
<string name="connect_with_contact_name_question">Соединиться с %1$s?</string>
|
||||
<string name="remove_member_button">Удалить члена группы</string>
|
||||
<string name="block_member_confirmation">Заблокировать</string>
|
||||
<string name="blocked_items_description">%d сообщений заблокировано</string>
|
||||
<string name="block_member_button">Заблокировать члена группы</string>
|
||||
<string name="connected_mobile">Подключенный мобильный</string>
|
||||
<string name="connect_plan_repeat_join_request">Повторить запрос на вступление?</string>
|
||||
<string name="button_remove_member_question">Удалить члена группы?</string>
|
||||
<string name="delete_and_notify_contact">Удалить и уведомить контакт</string>
|
||||
<string name="connect_plan_open_group">Открыть группу</string>
|
||||
<string name="desktop_connection_terminated">Подключение прервано</string>
|
||||
<string name="this_device_version"><![CDATA[<i>(это устройство v%s)</i>]]></string>
|
||||
<string name="unblock_member_desc">Сообщения от %s будут показаны!</string>
|
||||
<string name="enter_this_device_name">Введите имя этого устройства…</string>
|
||||
<string name="error">Ошибка</string>
|
||||
<string name="connect_to_desktop">Подключиться к компьютеру</string>
|
||||
<string name="disconnect_remote_host">Отключить</string>
|
||||
<string name="block_member_question">Заблокировать члена группы?</string>
|
||||
<string name="rcv_group_events_count">%d событий</string>
|
||||
<string name="invalid_name">Неверное имя!</string>
|
||||
<string name="connected_to_mobile">Мобильный подключен</string>
|
||||
<string name="bad_desktop_address">Неверный адрес компьютера</string>
|
||||
<string name="paste_desktop_address">Вставить адрес компьютера</string>
|
||||
<string name="devices">Устройства</string>
|
||||
<string name="v5_4_more_things_descr">- опционально уведомляйте удалённые контакты.
|
||||
\n- имена профилей с пробелами.
|
||||
\n- и прочее!</string>
|
||||
<string name="non_content_uri_alert_title">Неверный путь к файлу</string>
|
||||
<string name="scan_from_mobile">Сканируйте с мобильного</string>
|
||||
<string name="disconnect_desktop_question">Отключить компьютер?</string>
|
||||
<string name="loading_remote_file_desc">Пожалуйста, подождите, пока файл загружается со связанного мобильного устройства.</string>
|
||||
<string name="block_member_desc">Все новые сообщения от %s будут скрыты!</string>
|
||||
<string name="desktop_app_version_is_incompatible">Версия настольного приложения %s несовместима с этим приложением.</string>
|
||||
<string name="blocked_item_description">заблокировано</string>
|
||||
<string name="connect_plan_you_are_already_connecting_to_vName">Вы уже соединяетесь с %1$s.</string>
|
||||
<string name="connect_plan_you_are_already_joining_the_group_via_this_link">Вы уже вступаете в группу по этой ссылке.</string>
|
||||
<string name="connect_plan_you_are_already_joining_the_group_vName">Вы уже вступаете в группу %1$s.</string>
|
||||
<string name="connect_plan_this_is_your_own_one_time_link">Это ваша собственная одноразовая ссылка!</string>
|
||||
<string name="v5_4_link_mobile_desktop_descr">Через безопасный квантово-устойчивый протокол.</string>
|
||||
<string name="v5_4_block_group_members_descr">Чтобы скрыть нежелательные сообщения.</string>
|
||||
<string name="unlink_desktop_question">Забыть компьютер?</string>
|
||||
<string name="video_decoding_exception_desc">Видео невозможно декодировать. Пожалуйста, попробуйте другое видео или свяжитесь с разработчиками.</string>
|
||||
<string name="rcv_group_event_1_member_connected">%@ соединен(а)</string>
|
||||
<string name="group_members_n">%s, %s и %d членов группы</string>
|
||||
<string name="this_device">Это устройство</string>
|
||||
<string name="unblock_member_button">Разблокировать члена группы</string>
|
||||
<string name="contact_tap_to_connect">Нажмите чтобы соединиться</string>
|
||||
<string name="this_device_name">Имя этого устройства</string>
|
||||
<string name="connect_plan_you_are_already_in_group_vName">Вы уже состоите в группе %1$s.</string>
|
||||
<string name="connect_plan_this_is_your_own_simplex_address">Это ваш собственный адрес SimpleX!</string>
|
||||
<string name="unblock_member_question">Разблокировать члена группы?</string>
|
||||
<string name="settings_section_title_use_from_desktop">Использовать с компьютера</string>
|
||||
<string name="session_code">Код сессии</string>
|
||||
<string name="connect_plan_you_are_already_connecting_via_this_one_time_link">Вы уже соединяетесь по этой одноразовой ссылке!</string>
|
||||
<string name="unlink_desktop">Забыть</string>
|
||||
<string name="this_device_name_shared_with_mobile">Имя устройства будет доступно подключенному мобильному клиенту.</string>
|
||||
<string name="verify_code_on_mobile">Сверьте код на мобильном</string>
|
||||
<string name="non_content_uri_alert_text">Указан неверный путь к файлу. Сообщите о проблеме разработчикам приложения.</string>
|
||||
<string name="connect_plan_this_is_your_link_for_group_vName">Это ваша ссылка на группу %1$s!</string>
|
||||
<string name="verify_code_with_desktop">Сверьте код с компьютером</string>
|
||||
<string name="scan_qr_code_from_desktop">Сканировать QR код с компьютера</string>
|
||||
<string name="unblock_member_confirmation">Разблокировать</string>
|
||||
<string name="connect_plan_you_have_already_requested_connection_via_this_address">Вы уже запросили соединение через этот адрес!</string>
|
||||
<string name="terminal_always_visible">Показывать консоль в новом окне</string>
|
||||
<string name="verify_connections">Проверять соединения</string>
|
||||
<string name="verify_connection">Проверить соединение</string>
|
||||
</resources>
|
||||
@@ -1048,4 +1048,27 @@
|
||||
<string name="users_delete_data_only">Sadece yerel profil verisi</string>
|
||||
<string name="info_row_local_name">Yerel ad</string>
|
||||
<string name="only_you_can_add_message_reactions">Sadece sen mesaj tepkileri ekleyebilirsin.</string>
|
||||
<string name="stop_file__confirm">Durdur</string>
|
||||
<string name="stop_file__action">Dosyayı durdur</string>
|
||||
<string name="error_alert_title">Hata</string>
|
||||
<string name="create_another_profile_button">ProfilProfil oluştur</string>
|
||||
<string name="stop_chat_to_enable_database_actions">Veri tabanı eylemlerini etkinleştirmek için sohbeti durdur.</string>
|
||||
<string name="stop_snd_file__title">Dosya göndermeyi durdur?</string>
|
||||
<string name="auth_stop_chat">Sohbeti durdur</string>
|
||||
<string name="connect_use_current_profile">Mevcut profili kullan</string>
|
||||
<string name="la_mode_system">Sistem</string>
|
||||
<string name="settings_section_title_support">SIMPLEX CHAT\'İ DESTEKLE</string>
|
||||
<string name="stop_chat_to_export_import_or_delete_chat_database">Sohbet veri tabanını dışa aktarmak, içe aktarmak veya silmek için sohbeti durdur. Sohbet durdurulduğunda mesaj alamaz ve gönderemezsiniz.</string>
|
||||
<string name="desktop_device">Masaüstü</string>
|
||||
<string name="contact_tap_to_connect">Bağlanmak için dokun</string>
|
||||
<string name="block_member_confirmation">Engelle</string>
|
||||
<string name="tap_to_activate_profile">Profili etkinleştirmek için dokun.</string>
|
||||
<string name="stop_chat_confirmation">Durdur</string>
|
||||
<string name="stop_sharing_address">Adres paylaşmayı durdur?</string>
|
||||
<string name="send_receipts_disabled">kapalı</string>
|
||||
<string name="turn_off_battery_optimization_button">İzin ver</string>
|
||||
<string name="error">Hata</string>
|
||||
<string name="stop_sharing">Paylaşmayı durdur</string>
|
||||
<string name="stop_rcv_file__title">Dosya almayı durdur?</string>
|
||||
<string name="stop_chat_question">Sohbeti durdur?</string>
|
||||
</resources>
|
||||
@@ -1471,7 +1471,7 @@
|
||||
<string name="unlink_desktop_question">取消链接桌面端?</string>
|
||||
<string name="linked_desktop_options">已链接桌面选项</string>
|
||||
<string name="linked_desktops">已链接桌面</string>
|
||||
<string name="discover_on_network">网络发现</string>
|
||||
<string name="discover_on_network">通过本地网络发现</string>
|
||||
<string name="this_device">此设备</string>
|
||||
<string name="linked_mobiles">已链接的移动设备</string>
|
||||
<string name="desktop_device">桌面</string>
|
||||
@@ -1505,4 +1505,23 @@
|
||||
<string name="loading_remote_file_desc">从已链接移动设备加载文件时请稍候片刻</string>
|
||||
<string name="desktop_app_version_is_incompatible">桌面应用版本 %s 不兼容此应用。</string>
|
||||
<string name="verify_connection">验证连接</string>
|
||||
<string name="v5_4_block_group_members">屏蔽群组成员</string>
|
||||
<string name="v5_4_incognito_groups_descr">使用随机身份创建群组</string>
|
||||
<string name="v5_4_link_mobile_desktop">连接移动端和桌面端应用程序!🔗</string>
|
||||
<string name="v5_4_link_mobile_desktop_descr">通过安全的、抗量子计算机破解的协议。</string>
|
||||
<string name="v5_4_block_group_members_descr">隐藏不需要的信息。</string>
|
||||
<string name="v5_4_better_groups">更佳的群组</string>
|
||||
<string name="v5_4_incognito_groups">匿名群组</string>
|
||||
<string name="remote_host_was_disconnected_toast"><![CDATA[远程主机 <b>%s</b> 已断开连接]]></string>
|
||||
<string name="v5_4_better_groups_descr">加入速度更快、信息更可靠。</string>
|
||||
<string name="v5_4_more_things_descr">- 可选择通知已删除的联系人。
|
||||
\n- 带空格的个人资料名称。
|
||||
\n- 以及更多!</string>
|
||||
<string name="waiting_for_mobile_to_connect_on_port"><![CDATA[正等待移动设备在端口 <i>%s</i> 进行连接]]></string>
|
||||
<string name="group_member_role_author">作者</string>
|
||||
<string name="multicast_connect_automatically">自动连接</string>
|
||||
<string name="waiting_for_desktop">等待桌面中…</string>
|
||||
<string name="found_desktop">找到了桌面</string>
|
||||
<string name="not_compatible">不兼容!</string>
|
||||
<string name="multicast_discoverable_via_local_network">可通过本地网络发现</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user