diff --git a/.gitignore b/.gitignore index 69111c9abb..d4b4d6cb33 100644 --- a/.gitignore +++ b/.gitignore @@ -75,3 +75,4 @@ website/package-lock.json # Ignore test files website/.cache website/test/stubs-layout-cache/_includes/*.js +apps/android/app/release diff --git a/apps/android/app/build.gradle b/apps/android/app/build.gradle index aca945b71a..60036f1b25 100644 --- a/apps/android/app/build.gradle +++ b/apps/android/app/build.gradle @@ -9,15 +9,12 @@ android { defaultConfig { applicationId "chat.simplex.app" - minSdk 29 + minSdk 26 targetSdk 32 - versionCode 105 - versionName "4.6-beta.1" + versionCode 107 + versionName "4.6" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - ndk { - abiFilters 'arm64-v8a' - } vectorDrawables { useSupportLibrary true } @@ -77,10 +74,27 @@ android { jniLibs.useLegacyPackaging = compression_level != "0" } def isRelease = gradle.getStartParameter().taskNames.find({ it.toLowerCase().contains("release") }) != null + def isBundle = gradle.getStartParameter().taskNames.find({ it.toLowerCase().contains("bundle") }) != null // if (isRelease) { // Comma separated list of languages that will be included in the apk android.defaultConfig.resConfigs("en", "cs", "de", "es", "fr", "it", "nl", "ru", "zh-rCN") // } + if (isBundle) { + defaultConfig.ndk.abiFilters 'arm64-v8a', 'armeabi-v7a' + } else { + splits { + abi { + enable true + reset() + if (isRelease) { + include 'arm64-v8a', 'armeabi-v7a' + } else { + include 'arm64-v8a', 'armeabi-v7a' + universalApk false + } + } + } + } } dependencies { @@ -196,6 +210,8 @@ tasks.register("compressApk") { if (project.properties['android.injected.signing.key.alias'] != null && buildType == 'release') { new File(outputDir, "app-release.apk").renameTo(new File(outputDir, "simplex.apk")) + new File(outputDir, "app-armeabi-v7a-release.apk").renameTo(new File(outputDir, "simplex-armv7a.apk")) + new File(outputDir, "app-arm64-v8a-release.apk").renameTo(new File(outputDir, "simplex.apk")) } // View all gradle properties set diff --git a/apps/android/app/src/main/cpp/CMakeLists.txt b/apps/android/app/src/main/cpp/CMakeLists.txt index e97f01708f..de688975c6 100644 --- a/apps/android/app/src/main/cpp/CMakeLists.txt +++ b/apps/android/app/src/main/cpp/CMakeLists.txt @@ -53,10 +53,6 @@ add_library( support SHARED IMPORTED ) set_target_properties( support PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libsupport.so) -add_library( crypto SHARED IMPORTED ) -set_target_properties( crypto PROPERTIES IMPORTED_LOCATION - ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libcrypto.so) - # Specifies libraries CMake should link to your target library. You # can link multiple libraries, such as libraries you define in this # build script, prebuilt third-party libraries, or system libraries. @@ -64,7 +60,7 @@ set_target_properties( crypto PROPERTIES IMPORTED_LOCATION target_link_libraries( # Specifies the target library. app-lib - simplex support crypto + simplex support # Links the target library to the log library # included in the NDK. diff --git a/apps/android/app/src/main/cpp/simplex-api.c b/apps/android/app/src/main/cpp/simplex-api.c index a5264e3359..edd07bd63b 100644 --- a/apps/android/app/src/main/cpp/simplex-api.c +++ b/apps/android/app/src/main/cpp/simplex-api.c @@ -7,6 +7,17 @@ void hs_init(int * argc, char **argv[]); void setLineBuffering(void); int pipe_std_to_socket(const char * name); +extern void __svfscanf(void){}; +extern void __vfwscanf(void){}; +extern void __memset_chk_fail(void){}; +extern void __strcpy_chk_generic(void){}; +extern void __strcat_chk_generic(void){}; +extern void __libc_globals(void){}; +extern void __rel_iplt_start(void){}; + +// Android 9 only, not 13 +extern void reallocarray(void){}; + JNIEXPORT jint JNICALL Java_chat_simplex_app_SimplexAppKt_pipeStdOutToSocket(JNIEnv *env, __unused jclass clazz, jstring socket_name) { const char *name = (*env)->GetStringUTFChars(env, socket_name, JNI_FALSE); @@ -30,6 +41,7 @@ extern char *chat_recv_msg(chat_ctrl ctrl); // deprecated extern char *chat_recv_msg_wait(chat_ctrl ctrl, const int wait); extern char *chat_parse_markdown(const char *str); extern char *chat_parse_server(const char *str); +extern char *chat_password_hash(const char *pwd, const char *salt); JNIEXPORT jobjectArray JNICALL Java_chat_simplex_app_SimplexAppKt_chatMigrateInit(JNIEnv *env, __unused jclass clazz, jstring dbPath, jstring dbKey) { @@ -85,3 +97,13 @@ Java_chat_simplex_app_SimplexAppKt_chatParseServer(JNIEnv *env, __unused jclass (*env)->ReleaseStringUTFChars(env, str, _str); return res; } + +JNIEXPORT jstring JNICALL +Java_chat_simplex_app_SimplexAppKt_chatPasswordHash(JNIEnv *env, __unused jclass clazz, jstring pwd, jstring salt) { + const char *_pwd = (*env)->GetStringUTFChars(env, pwd, JNI_FALSE); + const char *_salt = (*env)->GetStringUTFChars(env, salt, JNI_FALSE); + jstring res = (*env)->NewStringUTF(env, chat_password_hash(_pwd, _salt)); + (*env)->ReleaseStringUTFChars(env, pwd, _pwd); + (*env)->ReleaseStringUTFChars(env, salt, _salt); + return res; +} diff --git a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt index efe6b022c0..734f41cbd0 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt @@ -402,7 +402,7 @@ fun processNotificationIntent(intent: Intent?, chatModel: ChatModel) { if (chatId != null) { withBGApi { if (userId != null && userId != chatModel.currentUser.value?.userId) { - chatModel.controller.changeActiveUser(userId) + chatModel.controller.changeActiveUser(userId, null) } val cInfo = chatModel.getChat(chatId)?.chatInfo chatModel.clearOverlays.value = true @@ -414,7 +414,7 @@ fun processNotificationIntent(intent: Intent?, chatModel: ChatModel) { Log.d(TAG, "processNotificationIntent: ShowChatsAction") withBGApi { if (userId != null && userId != chatModel.currentUser.value?.userId) { - chatModel.controller.changeActiveUser(userId) + chatModel.controller.changeActiveUser(userId, null) } chatModel.chatId.value = null chatModel.clearOverlays.value = true diff --git a/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt b/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt index 923f71b970..dce371a351 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/SimplexApp.kt @@ -32,6 +32,7 @@ external fun chatRecvMsg(ctrl: ChatCtrl): String external fun chatRecvMsgWait(ctrl: ChatCtrl, timeout: Int): String external fun chatParseMarkdown(str: String): String external fun chatParseServer(str: String): String +external fun chatPasswordHash(pwd: String, salt: String): String class SimplexApp: Application(), LifecycleEventObserver { lateinit var chatController: ChatController diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt b/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt index 58238b48b3..e3351dc124 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/ChatModel.kt @@ -94,6 +94,32 @@ class ChatModel(val controller: ChatController) { val filesToDelete = mutableSetOf() val simplexLinkMode = mutableStateOf(controller.appPrefs.simplexLinkMode.get()) + fun getUser(userId: Long): User? = if (currentUser.value?.userId == userId) { + currentUser.value + } else { + users.firstOrNull { it.user.userId == userId }?.user + } + + private fun getUserIndex(user: User): Int = + users.indexOfFirst { it.user.userId == user.userId } + + fun updateUser(user: User) { + val i = getUserIndex(user) + if (i != -1) { + users[i] = users[i].copy(user = user) + } + if (currentUser.value?.userId == user.userId) { + currentUser.value = user + } + } + + fun removeUser(user: User) { + val i = getUserIndex(user) + if (i != -1 && users[i].user.userId != currentUser.value?.userId) { + users.removeAt(i) + } + } + fun hasChat(id: String): Boolean = chats.firstOrNull { it.id == id } != null fun getChat(id: String): Chat? = chats.firstOrNull { it.id == id } fun getContactChat(contactId: Long): Chat? = chats.firstOrNull { it.chatInfo is ChatInfo.Direct && it.chatInfo.apiId == contactId } @@ -422,13 +448,19 @@ data class User( val localDisplayName: String, val profile: LocalProfile, val fullPreferences: FullChatPreferences, - val activeUser: Boolean + val activeUser: Boolean, + val showNtfs: Boolean, + val viewPwdHash: UserPwdHash? ): NamedChat { override val displayName: String get() = profile.displayName override val fullName: String get() = profile.fullName override val image: String? get() = profile.image override val localAlias: String = "" + val hidden: Boolean = viewPwdHash != null + + val showNotifications: Boolean = activeUser || showNtfs + companion object { val sampleData = User( userId = 1, @@ -436,11 +468,19 @@ data class User( localDisplayName = "alice", profile = LocalProfile.sampleData, fullPreferences = FullChatPreferences.sampleData, - activeUser = true + activeUser = true, + showNtfs = true, + viewPwdHash = null, ) } } +@Serializable +data class UserPwdHash( + val hash: String, + val salt: String +) + @Serializable data class UserInfo( val user: User, diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/NtfManager.kt b/apps/android/app/src/main/java/chat/simplex/app/model/NtfManager.kt index b6c3c11653..a4c4c8aeab 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/NtfManager.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/NtfManager.kt @@ -80,7 +80,7 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference } fun notifyContactRequestReceived(user: User, cInfo: ChatInfo.ContactRequest) { - notifyMessageReceived( + displayNotification( user = user, chatId = cInfo.id, displayName = cInfo.displayName, @@ -91,7 +91,7 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference } fun notifyContactConnected(user: User, contact: Contact) { - notifyMessageReceived( + displayNotification( user = user, chatId = contact.id, displayName = contact.displayName, @@ -101,11 +101,11 @@ class NtfManager(val context: Context, private val appPreferences: AppPreference fun notifyMessageReceived(user: User, cInfo: ChatInfo, cItem: ChatItem) { if (!cInfo.ntfsEnabled) return - - notifyMessageReceived(user = user, chatId = cInfo.id, displayName = cInfo.displayName, msgText = hideSecrets(cItem)) + displayNotification(user = user, chatId = cInfo.id, displayName = cInfo.displayName, msgText = hideSecrets(cItem)) } - fun notifyMessageReceived(user: User, chatId: String, displayName: String, msgText: String, image: String? = null, actions: List = emptyList()) { + fun displayNotification(user: User, chatId: String, displayName: String, msgText: String, image: String? = null, actions: List = emptyList()) { + if (!user.showNotifications) return Log.d(TAG, "notifyMessageReceived $chatId") val now = Clock.System.now().toEpochMilliseconds() val recentNotification = (now - prevNtfTime.getOrDefault(chatId, 0) < msgNtfTimeoutMs) diff --git a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt index 58d41984b1..5d8f09c4ae 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/model/SimpleXAPI.kt @@ -133,6 +133,8 @@ class AppPreferences(val context: Context) { val incognito = mkBoolPreference(SHARED_PREFS_INCOGNITO, false) val connectViaLinkTab = mkStrPreference(SHARED_PREFS_CONNECT_VIA_LINK_TAB, ConnectViaLinkTab.SCAN.name) val liveMessageAlertShown = mkBoolPreference(SHARED_PREFS_LIVE_MESSAGE_ALERT_SHOWN, false) + val showHiddenProfilesNotice = mkBoolPreference(SHARED_PREFS_SHOW_HIDDEN_PROFILES_NOTICE, true) + val showMuteProfileAlert = mkBoolPreference(SHARED_PREFS_SHOW_MUTE_PROFILE_ALERT, true) val appLanguage = mkStrPreference(SHARED_PREFS_APP_LANGUAGE, null) val storeDBPassphrase = mkBoolPreference(SHARED_PREFS_STORE_DB_PASSPHRASE, true) @@ -235,6 +237,8 @@ class AppPreferences(val context: Context) { private const val SHARED_PREFS_INCOGNITO = "Incognito" private const val SHARED_PREFS_CONNECT_VIA_LINK_TAB = "ConnectViaLinkTab" private const val SHARED_PREFS_LIVE_MESSAGE_ALERT_SHOWN = "LiveMessageAlertShown" + private const val SHARED_PREFS_SHOW_HIDDEN_PROFILES_NOTICE = "ShowHiddenProfilesNotice" + private const val SHARED_PREFS_SHOW_MUTE_PROFILE_ALERT = "ShowMuteProfileAlert" private const val SHARED_PREFS_STORE_DB_PASSPHRASE = "StoreDBPassphrase" private const val SHARED_PREFS_INITIAL_RANDOM_DB_PASSPHRASE = "InitialRandomDBPassphrase" private const val SHARED_PREFS_ENCRYPTED_DB_PASSPHRASE = "EncryptedDBPassphrase" @@ -264,6 +268,16 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a chatModel.incognito.value = appPrefs.incognito.get() } + private fun currentUserId(funcName: String): Long { + val userId = chatModel.currentUser.value?.userId + if (userId == null) { + val error = "$funcName: no current user" + Log.e(TAG, error) + throw Exception(error) + } + return userId + } + suspend fun startChat(user: User) { Log.d(TAG, "user: $user") try { @@ -297,21 +311,26 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a } } - suspend fun changeActiveUser(toUserId: Long) { + suspend fun changeActiveUser(toUserId: Long, viewPwd: String?) { try { - changeActiveUser_(toUserId) + changeActiveUser_(toUserId, viewPwd) } catch (e: Exception) { Log.e(TAG, "Unable to set active user: ${e.stackTraceToString()}") AlertManager.shared.showAlertMsg(generalGetString(R.string.failed_to_active_user_title), e.stackTraceToString()) } } - suspend fun changeActiveUser_(toUserId: Long) { - chatModel.currentUser.value = apiSetActiveUser(toUserId) + suspend fun changeActiveUser_(toUserId: Long, viewPwd: String?) { + val currentUser = apiSetActiveUser(toUserId, viewPwd) + chatModel.currentUser.value = currentUser val users = listUsers() chatModel.users.clear() chatModel.users.addAll(users) getUserChatData() + val invitation = chatModel.callInvitations.values.firstOrNull { inv -> inv.user.userId == toUserId } + if (invitation != null) { + chatModel.callManager.reportNewIncomingCall(invitation.copy(user = currentUser)) + } } suspend fun getUserChatData() { @@ -408,15 +427,33 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a throw Exception("failed to list users ${r.responseType} ${r.details}") } - suspend fun apiSetActiveUser(userId: Long): User { - val r = sendCmd(CC.ApiSetActiveUser(userId)) + suspend fun apiSetActiveUser(userId: Long, viewPwd: String?): User { + val r = sendCmd(CC.ApiSetActiveUser(userId, viewPwd)) if (r is CR.ActiveUser) return r.user Log.d(TAG, "apiSetActiveUser: ${r.responseType} ${r.details}") throw Exception("failed to set the user as active ${r.responseType} ${r.details}") } - suspend fun apiDeleteUser(userId: Long, delSMPQueues: Boolean) { - val r = sendCmd(CC.ApiDeleteUser(userId, delSMPQueues)) + suspend fun apiHideUser(userId: Long, viewPwd: String): User = + setUserPrivacy(CC.ApiHideUser(userId, viewPwd)) + + suspend fun apiUnhideUser(userId: Long, viewPwd: String?): User = + setUserPrivacy(CC.ApiUnhideUser(userId, viewPwd)) + + suspend fun apiMuteUser(userId: Long, viewPwd: String?): User = + setUserPrivacy(CC.ApiMuteUser(userId, viewPwd)) + + suspend fun apiUnmuteUser(userId: Long, viewPwd: String?): User = + setUserPrivacy(CC.ApiUnmuteUser(userId, viewPwd)) + + private suspend fun setUserPrivacy(cmd: CC): User { + val r = sendCmd(cmd) + if (r is CR.UserPrivacy) return r.user + else throw Exception("Failed to change user privacy: ${r.responseType} ${r.details}") + } + + suspend fun apiDeleteUser(userId: Long, delSMPQueues: Boolean, viewPwd: String?) { + val r = sendCmd(CC.ApiDeleteUser(userId, delSMPQueues, viewPwd)) if (r is CR.CmdOk) return Log.d(TAG, "apiDeleteUser: ${r.responseType} ${r.details}") throw Exception("failed to delete the user ${r.responseType} ${r.details}") @@ -489,10 +526,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a } suspend fun apiGetChats(): List { - val userId = chatModel.currentUser.value?.userId ?: run { - Log.e(TAG, "apiGetChats: no current user") - return emptyList() - } + val userId = kotlin.runCatching { currentUserId("apiGetChats") }.getOrElse { return emptyList() } val r = sendCmd(CC.ApiGetChats(userId)) if (r is CR.ApiChats) return r.chats Log.e(TAG, "failed getting the list of chats: ${r.responseType} ${r.details}") @@ -544,10 +578,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a } private suspend fun getUserSMPServers(): Pair, List>? { - val userId = chatModel.currentUser.value?.userId ?: run { - Log.e(TAG, "getUserSMPServers: no current user") - return null - } + val userId = kotlin.runCatching { currentUserId("getUserSMPServers") }.getOrElse { return null } val r = sendCmd(CC.APIGetUserSMPServers(userId)) if (r is CR.UserSMPServers) return r.smpServers to r.presetSMPServers Log.e(TAG, "getUserSMPServers bad response: ${r.responseType} ${r.details}") @@ -555,10 +586,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a } suspend fun setUserSMPServers(smpServers: List): Boolean { - val userId = chatModel.currentUser.value?.userId ?: run { - Log.e(TAG, "setUserSMPServers: no current user") - return false - } + val userId = kotlin.runCatching { currentUserId("setUserSMPServers") }.getOrElse { return false } val r = sendCmd(CC.APISetUserSMPServers(userId, smpServers)) return when (r) { is CR.CmdOk -> true @@ -574,7 +602,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a } suspend fun testSMPServer(smpServer: String): SMPTestFailure? { - val userId = chatModel.currentUser.value?.userId ?: run { throw Exception("testSMPServer: no current user") } + val userId = currentUserId("testSMPServer") val r = sendCmd(CC.APITestSMPServer(userId, smpServer)) return when (r) { is CR.SmpTestResult -> r.smpTestFailure @@ -586,14 +614,14 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a } suspend fun getChatItemTTL(): ChatItemTTL { - val userId = chatModel.currentUser.value?.userId ?: run { throw Exception("getChatItemTTL: no current user") } + val userId = currentUserId("getChatItemTTL") val r = sendCmd(CC.APIGetChatItemTTL(userId)) if (r is CR.ChatItemTTL) return ChatItemTTL.fromSeconds(r.chatItemTTL) throw Exception("failed to get chat item TTL: ${r.responseType} ${r.details}") } suspend fun setChatItemTTL(chatItemTTL: ChatItemTTL) { - val userId = chatModel.currentUser.value?.userId ?: run { throw Exception("setChatItemTTL: no current user") } + val userId = currentUserId("setChatItemTTL") val r = sendCmd(CC.APISetChatItemTTL(userId, chatItemTTL.seconds)) if (r is CR.CmdOk) return throw Exception("failed to set chat item TTL: ${r.responseType} ${r.details}") @@ -777,10 +805,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a } suspend fun apiListContacts(): List? { - val userId = chatModel.currentUser.value?.userId ?: run { - Log.e(TAG, "apiListContacts: no current user") - return null - } + val userId = kotlin.runCatching { currentUserId("apiListContacts") }.getOrElse { return null } val r = sendCmd(CC.ApiListContacts(userId)) if (r is CR.ContactsList) return r.contacts Log.e(TAG, "apiListContacts bad response: ${r.responseType} ${r.details}") @@ -788,10 +813,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a } suspend fun apiUpdateProfile(profile: Profile): Profile? { - val userId = chatModel.currentUser.value?.userId ?: run { - Log.e(TAG, "apiUpdateProfile: no current user") - return null - } + val userId = kotlin.runCatching { currentUserId("apiUpdateProfile") }.getOrElse { return null } val r = sendCmd(CC.ApiUpdateProfile(userId, profile)) if (r is CR.UserProfileNoChange) return profile if (r is CR.UserProfileUpdated) return r.toProfile @@ -821,10 +843,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a } suspend fun apiCreateUserAddress(): String? { - val userId = chatModel.currentUser.value?.userId ?: run { - Log.e(TAG, "apiCreateUserAddress: no current user") - return null - } + val userId = kotlin.runCatching { currentUserId("apiCreateUserAddress") }.getOrElse { return null } val r = sendCmd(CC.ApiCreateMyAddress(userId)) return when (r) { is CR.UserContactLinkCreated -> r.connReqContact @@ -838,10 +857,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a } suspend fun apiDeleteUserAddress(): Boolean { - val userId = chatModel.currentUser.value?.userId ?: run { - Log.e(TAG, "apiDeleteUserAddress: no current user") - return false - } + val userId = kotlin.runCatching { currentUserId("apiDeleteUserAddress") }.getOrElse { return false } val r = sendCmd(CC.ApiDeleteMyAddress(userId)) if (r is CR.UserContactLinkDeleted) return true Log.e(TAG, "apiDeleteUserAddress bad response: ${r.responseType} ${r.details}") @@ -849,10 +865,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a } private suspend fun apiGetUserAddress(): UserContactLinkRec? { - val userId = chatModel.currentUser.value?.userId ?: run { - Log.e(TAG, "apiGetUserAddress: no current user") - return null - } + val userId = kotlin.runCatching { currentUserId("apiGetUserAddress") }.getOrElse { return null } val r = sendCmd(CC.ApiShowMyAddress(userId)) if (r is CR.UserContactLink) return r.contactLink if (r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorStore @@ -864,10 +877,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a } suspend fun userAddressAutoAccept(autoAccept: AutoAccept?): UserContactLinkRec? { - val userId = chatModel.currentUser.value?.userId ?: run { - Log.e(TAG, "userAddressAutoAccept: no current user") - return null - } + val userId = kotlin.runCatching { currentUserId("userAddressAutoAccept") }.getOrElse { return null } val r = sendCmd(CC.ApiAddressAutoAccept(userId, autoAccept)) if (r is CR.UserContactLinkUpdated) return r.contactLink if (r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorStore @@ -987,10 +997,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a } suspend fun apiNewGroup(p: GroupProfile): GroupInfo? { - val userId = chatModel.currentUser.value?.userId ?: run { - Log.e(TAG, "apiNewGroup: no current user") - return null - } + val userId = kotlin.runCatching { currentUserId("apiNewGroup") }.getOrElse { return null } val r = sendCmd(CC.ApiNewGroup(userId, p)) if (r is CR.GroupCreated) return r.groupInfo Log.e(TAG, "apiNewGroup bad response: ${r.responseType} ${r.details}") @@ -1223,7 +1230,11 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a val contactRequest = r.contactRequest val cInfo = ChatInfo.ContactRequest(contactRequest) if (active(r.user)) { - chatModel.addChat(Chat(chatInfo = cInfo, chatItems = listOf())) + if (chatModel.hasChat(contactRequest.id)) { + chatModel.updateChatInfo(cInfo) + } else { + chatModel.addChat(Chat(chatInfo = cInfo, chatItems = listOf())) + } } ntfManager.notifyContactRequestReceived(r.user, cInfo) } @@ -1309,7 +1320,7 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a val isLastChatItem = chatModel.getChat(cInfo.id)?.chatItems?.lastOrNull()?.id == cItem.id if (isLastChatItem && ntfManager.hasNotificationsForChat(cInfo.id)) { ntfManager.cancelNotificationsForChat(cInfo.id) - ntfManager.notifyMessageReceived( + ntfManager.displayNotification( r.user, cInfo.id, cInfo.displayName, @@ -1404,8 +1415,9 @@ open class ChatController(var ctrl: ChatCtrl?, val ntfManager: NtfManager, val a removeFile(appContext, fileName) } } - is CR.CallInvitation -> + is CR.CallInvitation -> { chatModel.callManager.reportNewIncomingCall(r.callInvitation) + } is CR.CallOffer -> { // TODO askConfirmation? // TODO check encryption is compatible @@ -1776,8 +1788,12 @@ sealed class CC { class ShowActiveUser: CC() class CreateActiveUser(val profile: Profile): CC() class ListUsers: CC() - class ApiSetActiveUser(val userId: Long): CC() - class ApiDeleteUser(val userId: Long, val delSMPQueues: Boolean): CC() + class ApiSetActiveUser(val userId: Long, val viewPwd: String?): CC() + class ApiHideUser(val userId: Long, val viewPwd: String): CC() + class ApiUnhideUser(val userId: Long, val viewPwd: String?): CC() + class ApiMuteUser(val userId: Long, val viewPwd: String?): CC() + class ApiUnmuteUser(val userId: Long, val viewPwd: String?): CC() + class ApiDeleteUser(val userId: Long, val delSMPQueues: Boolean, val viewPwd: String?): CC() class StartChat(val expire: Boolean): CC() class ApiStopChat: CC() class SetTempFolder(val tempFolder: String): CC() @@ -1855,8 +1871,12 @@ sealed class CC { is ShowActiveUser -> "/u" is CreateActiveUser -> "/create user ${profile.displayName} ${profile.fullName}" is ListUsers -> "/users" - is ApiSetActiveUser -> "/_user $userId" - is ApiDeleteUser -> "/_delete user $userId del_smp=${onOff(delSMPQueues)}" + is ApiSetActiveUser -> "/_user $userId${maybePwd(viewPwd)}" + is ApiHideUser -> "/_hide user $userId ${json.encodeToString(viewPwd)}" + is ApiUnhideUser -> "/_unhide user $userId${maybePwd(viewPwd)}" + is ApiMuteUser -> "/_mute user $userId${maybePwd(viewPwd)}" + is ApiUnmuteUser -> "/_unmute user $userId${maybePwd(viewPwd)}" + is ApiDeleteUser -> "/_delete user $userId del_smp=${onOff(delSMPQueues)}${maybePwd(viewPwd)}" is StartChat -> "/_start subscribe=on expire=${onOff(expire)}" is ApiStopChat -> "/_stop" is SetTempFolder -> "/_temp_folder $tempFolder" @@ -1936,6 +1956,10 @@ sealed class CC { is CreateActiveUser -> "createActiveUser" is ListUsers -> "listUsers" is ApiSetActiveUser -> "apiSetActiveUser" + is ApiHideUser -> "apiHideUser" + is ApiUnhideUser -> "apiUnhideUser" + is ApiMuteUser -> "apiMuteUser" + is ApiUnmuteUser -> "apiUnmuteUser" is ApiDeleteUser -> "apiDeleteUser" is StartChat -> "startChat" is ApiStopChat -> "apiStopChat" @@ -2020,13 +2044,28 @@ sealed class CC { val obfuscated: CC get() = when (this) { is ApiStorageEncryption -> ApiStorageEncryption(DBEncryptionConfig(obfuscate(config.currentKey), obfuscate(config.newKey))) + is ApiSetActiveUser -> ApiSetActiveUser(userId, obfuscateOrNull(viewPwd)) + is ApiHideUser -> ApiHideUser(userId, obfuscate(viewPwd)) + is ApiUnhideUser -> ApiUnhideUser(userId, obfuscateOrNull(viewPwd)) + is ApiMuteUser -> ApiMuteUser(userId, obfuscateOrNull(viewPwd)) + is ApiUnmuteUser -> ApiUnmuteUser(userId, obfuscateOrNull(viewPwd)) + is ApiDeleteUser -> ApiDeleteUser(userId, delSMPQueues, obfuscateOrNull(viewPwd)) else -> this } private fun obfuscate(s: String): String = if (s.isEmpty()) "" else "***" + private fun obfuscateOrNull(s: String?): String? = + if (s != null) { + obfuscate(s) + } else { + null + } + private fun onOff(b: Boolean): String = if (b) "on" else "off" + private fun maybePwd(pwd: String?): String = if (pwd == "" || pwd == null) "" else " " + json.encodeToString(pwd) + companion object { fun chatRef(chatType: ChatType, id: Long) = "${chatType.type}${id}" @@ -2882,6 +2921,13 @@ class APIResponse(val resp: CR, val corr: String? = null) { resp = CR.ApiChat(user, chat), corr = data["corr"]?.toString() ) + } else if (type == "chatCmdError") { + val userObject = resp["user_"]?.jsonObject + val user = runCatching { json.decodeFromJsonElement(userObject!!) }.getOrNull() + return APIResponse( + resp = CR.ChatCmdError(user, ChatError.ChatErrorInvalidJSON(json.encodeToString(resp["chatError"]))), + corr = data["corr"]?.toString() + ) } } catch (e: Exception) { Log.e(TAG, "Error while parsing chat(s): " + e.stackTraceToString()) @@ -2938,6 +2984,7 @@ sealed class CR { @Serializable @SerialName("chatCleared") class ChatCleared(val user: User, val chatInfo: ChatInfo): CR() @Serializable @SerialName("userProfileNoChange") class UserProfileNoChange(val user: User): CR() @Serializable @SerialName("userProfileUpdated") class UserProfileUpdated(val user: User, val fromProfile: Profile, val toProfile: Profile): CR() + @Serializable @SerialName("userPrivacy") class UserPrivacy(val user: User): CR() @Serializable @SerialName("contactAliasUpdated") class ContactAliasUpdated(val user: User, val toContact: Contact): CR() @Serializable @SerialName("connectionAliasUpdated") class ConnectionAliasUpdated(val user: User, val toConnection: PendingContactConnection): CR() @Serializable @SerialName("contactPrefsUpdated") class ContactPrefsUpdated(val user: User, val fromContact: Contact, val toContact: Contact): CR() @@ -3011,8 +3058,8 @@ sealed class CR { @Serializable @SerialName("versionInfo") class VersionInfo(val versionInfo: CoreVersionInfo): CR() @Serializable @SerialName("apiParsedMarkdown") class ParsedMarkdown(val formattedText: List? = null): CR() @Serializable @SerialName("cmdOk") class CmdOk(val user: User?): CR() - @Serializable @SerialName("chatCmdError") class ChatCmdError(val user: User?, val chatError: ChatError): CR() - @Serializable @SerialName("chatError") class ChatRespError(val user: User?, val chatError: ChatError): CR() + @Serializable @SerialName("chatCmdError") class ChatCmdError(val user_: User?, val chatError: ChatError): CR() + @Serializable @SerialName("chatError") class ChatRespError(val user_: User?, val chatError: ChatError): CR() @Serializable class Response(val type: String, val json: String): CR() @Serializable class Invalid(val str: String): CR() @@ -3041,6 +3088,7 @@ sealed class CR { is ChatCleared -> "chatCleared" is UserProfileNoChange -> "userProfileNoChange" is UserProfileUpdated -> "userProfileUpdated" + is UserPrivacy -> "userPrivacy" is ContactAliasUpdated -> "contactAliasUpdated" is ConnectionAliasUpdated -> "connectionAliasUpdated" is ContactPrefsUpdated -> "contactPrefsUpdated" @@ -3142,6 +3190,7 @@ sealed class CR { is ChatCleared -> withUser(user, json.encodeToString(chatInfo)) is UserProfileNoChange -> withUser(user, noDetails()) is UserProfileUpdated -> withUser(user, json.encodeToString(toProfile)) + is UserPrivacy -> withUser(user, "") is ContactAliasUpdated -> withUser(user, json.encodeToString(toContact)) is ConnectionAliasUpdated -> withUser(user, json.encodeToString(toConnection)) is ContactPrefsUpdated -> withUser(user, "fromContact: $fromContact\ntoContact: \n${json.encodeToString(toContact)}") @@ -3212,8 +3261,8 @@ sealed class CR { is ContactConnectionDeleted -> withUser(user, json.encodeToString(connection)) is VersionInfo -> json.encodeToString(versionInfo) is CmdOk -> withUser(user, noDetails()) - is ChatCmdError -> withUser(user, chatError.string) - is ChatRespError -> withUser(user, chatError.string) + is ChatCmdError -> withUser(user_, chatError.string) + is ChatRespError -> withUser(user_, chatError.string) is Response -> json is Invalid -> str } @@ -3285,11 +3334,13 @@ sealed class ChatError { is ChatErrorAgent -> "agent ${agentError.string}" is ChatErrorStore -> "store ${storeError.string}" is ChatErrorDatabase -> "database ${databaseError.string}" + is ChatErrorInvalidJSON -> "invalid json ${json}" } @Serializable @SerialName("error") class ChatErrorChat(val errorType: ChatErrorType): ChatError() @Serializable @SerialName("errorAgent") class ChatErrorAgent(val agentError: AgentErrorType): ChatError() @Serializable @SerialName("errorStore") class ChatErrorStore(val storeError: StoreError): ChatError() @Serializable @SerialName("errorDatabase") class ChatErrorDatabase(val databaseError: DatabaseError): ChatError() + @Serializable @SerialName("invalidJSON") class ChatErrorInvalidJSON(val json: String): ChatError() } @Serializable diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/call/CallManager.kt b/apps/android/app/src/main/java/chat/simplex/app/views/call/CallManager.kt index d5d4406460..2a4b397840 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/call/CallManager.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/call/CallManager.kt @@ -13,12 +13,14 @@ class CallManager(val chatModel: ChatModel) { Log.d(TAG, "CallManager.reportNewIncomingCall") with (chatModel) { callInvitations[invitation.contact.id] = invitation - if (Clock.System.now() - invitation.callTs <= 3.minutes) { - activeCallInvitation.value = invitation - controller.ntfManager.notifyCallInvitation(invitation) - } else { - val contact = invitation.contact - controller.ntfManager.notifyMessageReceived(user = invitation.user, chatId = contact.id, displayName = contact.displayName, msgText = invitation.callTypeText) + if (invitation.user.showNotifications) { + if (Clock.System.now() - invitation.callTs <= 3.minutes) { + activeCallInvitation.value = invitation + controller.ntfManager.notifyCallInvitation(invitation) + } else { + val contact = invitation.contact + controller.ntfManager.displayNotification(user = invitation.user, chatId = contact.id, displayName = contact.displayName, msgText = invitation.callTypeText) + } } } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/call/CallView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/call/CallView.kt index 26a3c23c66..641cdb930b 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/call/CallView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/call/CallView.kt @@ -92,7 +92,7 @@ fun ActiveCallView(chatModel: ChatModel) { am.registerAudioDeviceCallback(audioCallback, null) val pm = (SimplexApp.context.getSystemService(Context.POWER_SERVICE) as PowerManager) val proximityLock = if (pm.isWakeLockLevelSupported(PROXIMITY_SCREEN_OFF_WAKE_LOCK)) { - pm.newWakeLock(PROXIMITY_SCREEN_OFF_WAKE_LOCK, "proximityLock") + pm.newWakeLock(PROXIMITY_SCREEN_OFF_WAKE_LOCK, SimplexApp.context.packageName + ":proximityLock") } else { null } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt index 7d0ebe38c3..d63385ee2e 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/ComposeView.kt @@ -7,11 +7,10 @@ import android.Manifest import android.app.Activity import android.content.* import android.content.pm.PackageManager -import android.graphics.Bitmap -import android.graphics.ImageDecoder -import android.graphics.ImageDecoder.DecodeException +import android.graphics.* import android.graphics.drawable.AnimatedImageDrawable import android.net.Uri +import android.os.Build import android.provider.MediaStore import android.util.Log import android.widget.Toast @@ -36,6 +35,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat +import androidx.core.net.toFile import chat.simplex.app.* import chat.simplex.app.R import chat.simplex.app.model.* @@ -186,10 +186,11 @@ fun ComposeView( val textStyle = remember { mutableStateOf(smallFont) } val cameraLauncher = rememberCameraLauncher { uri: Uri? -> if (uri != null) { - val source = ImageDecoder.createSource(SimplexApp.context.contentResolver, uri) - val bitmap = ImageDecoder.decodeBitmap(source) - val imagePreview = resizeImageToStrSize(bitmap, maxDataSize = 14000) - composeState.value = composeState.value.copy(preview = ComposePreview.ImagePreview(listOf(imagePreview), listOf(UploadContent.SimpleImage(uri)))) + val bitmap: Bitmap? = getBitmapFromUri(uri) + if (bitmap != null) { + val imagePreview = resizeImageToStrSize(bitmap, maxDataSize = 14000) + composeState.value = composeState.value.copy(preview = ComposePreview.ImagePreview(listOf(imagePreview), listOf(UploadContent.SimpleImage(uri)))) + } } } val cameraPermissionLauncher = rememberPermissionLauncher { isGranted: Boolean -> @@ -203,19 +204,12 @@ fun ComposeView( val content = ArrayList() val imagesPreview = ArrayList() uris.forEach { uri -> - val source = ImageDecoder.createSource(context.contentResolver, uri) - val drawable = try { - ImageDecoder.decodeDrawable(source) - } catch (e: DecodeException) { - AlertManager.shared.showAlertMsg( - title = generalGetString(R.string.image_decoding_exception_title), - text = generalGetString(R.string.image_decoding_exception_desc) - ) - Log.e(TAG, "Error while decoding drawable: ${e.stackTraceToString()}") - null - } - var bitmap: Bitmap? = if (drawable != null) ImageDecoder.decodeBitmap(source) else null - if (drawable is AnimatedImageDrawable) { + val drawable = getDrawableFromUri(uri) + var bitmap: Bitmap? = if (drawable != null) getBitmapFromUri(uri) else null + val isAnimNewApi = Build.VERSION.SDK_INT >= 28 && drawable is AnimatedImageDrawable + val isAnimOldApi = Build.VERSION.SDK_INT < 28 && + (getFileName(SimplexApp.context, uri)?.endsWith(".gif") == true || getFileName(SimplexApp.context, uri)?.endsWith(".webp") == true) + if (isAnimNewApi || isAnimOldApi) { // It's a gif or webp val fileSize = getFileSize(context, uri) if (fileSize != null && fileSize <= MAX_FILE_SIZE) { diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt index d213296a3e..f1b3b47db5 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/SendMsgView.kt @@ -8,10 +8,12 @@ import android.content.pm.ActivityInfo import android.content.res.Configuration import android.os.Build import android.text.InputType +import android.util.Log import android.view.ViewGroup import android.view.WindowManager import android.view.inputmethod.* import android.widget.EditText +import android.widget.TextView import androidx.compose.animation.core.* import androidx.compose.foundation.* import androidx.compose.foundation.interaction.MutableInteractionSource @@ -50,6 +52,7 @@ import chat.simplex.app.views.chat.item.ItemAction import chat.simplex.app.views.helpers.* import com.google.accompanist.permissions.rememberMultiplePermissionsState import kotlinx.coroutines.* +import java.lang.reflect.Field @Composable fun SendMsgView( @@ -240,7 +243,17 @@ private fun NativeKeyboard( editText.background = drawable editText.setPadding(paddingStart, paddingTop, paddingEnd, paddingBottom) editText.setText(cs.message) - editText.textCursorDrawable?.let { DrawableCompat.setTint(it, HighOrLowlight.toArgb()) } + if (Build.VERSION.SDK_INT >= 29) { + editText.textCursorDrawable?.let { DrawableCompat.setTint(it, HighOrLowlight.toArgb()) } + } else { + try { + val f: Field = TextView::class.java.getDeclaredField("mCursorDrawableRes") + f.isAccessible = true + f.set(editText, R.drawable.edit_text_cursor) + } catch (e: Exception) { + Log.e(chat.simplex.app.TAG, e.stackTraceToString()) + } + } editText.doOnTextChanged { text, _, _, _ -> onMessageChange(text.toString()) } editText.doAfterTextChanged { text -> if (composeState.value.preview is ComposePreview.VoicePreview && text.toString() != "") editText.setText("") } editText diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt index dee77e4a4d..432fb728fa 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/ChatItemView.kt @@ -1,5 +1,7 @@ package chat.simplex.app.views.chat.item +import android.Manifest +import android.os.Build import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape @@ -25,6 +27,7 @@ import chat.simplex.app.ui.theme.SimpleXTheme import chat.simplex.app.views.chat.ComposeContextItem import chat.simplex.app.views.chat.ComposeState import chat.simplex.app.views.helpers.* +import com.google.accompanist.permissions.rememberPermissionState import kotlinx.datetime.Clock // TODO refactor so that FramedItemView can show all CIContent items if they're deleted (see Swift code) @@ -131,9 +134,16 @@ fun ChatItemView( if (cItem.content.msgContent is MsgContent.MCImage || cItem.content.msgContent is MsgContent.MCFile || cItem.content.msgContent is MsgContent.MCVoice) { val filePath = getLoadedFilePath(context, cItem.file) if (filePath != null) { + val writePermissionState = rememberPermissionState(permission = Manifest.permission.WRITE_EXTERNAL_STORAGE) ItemAction(stringResource(R.string.save_verb), Icons.Outlined.SaveAlt, onClick = { when (cItem.content.msgContent) { - is MsgContent.MCImage -> saveImage(context, cItem.file) + is MsgContent.MCImage -> { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R || writePermissionState.hasPermission) { + saveImage(context, cItem.file) + } else { + writePermissionState.launchPermissionRequest() + } + } is MsgContent.MCFile -> saveFileLauncher.launch(cItem.file?.fileName) is MsgContent.MCVoice -> saveFileLauncher.launch(cItem.file?.fileName) else -> {} diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/MarkedDeletedItemView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/MarkedDeletedItemView.kt index 31a570b3ae..a63a232939 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/MarkedDeletedItemView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chat/item/MarkedDeletedItemView.kt @@ -1,8 +1,7 @@ package chat.simplex.app.views.chat.item import android.content.res.Configuration -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* import androidx.compose.runtime.Composable @@ -31,10 +30,12 @@ fun MarkedDeletedItemView(ci: ChatItem, timedMessagesTTL: Int?, showMember: Bool Modifier.padding(horizontal = 12.dp, vertical = 6.dp), verticalAlignment = Alignment.CenterVertically ) { - if (ci.meta.itemDeleted is CIDeleted.Moderated) { - MarkedDeletedText(String.format(generalGetString(R.string.moderated_item_description), ci.meta.itemDeleted.byGroupMember.chatViewName)) - } else { - MarkedDeletedText(generalGetString(R.string.marked_deleted_description)) + Box(Modifier.weight(1f, false)) { + if (ci.meta.itemDeleted is CIDeleted.Moderated) { + MarkedDeletedText(String.format(generalGetString(R.string.moderated_item_description), ci.meta.itemDeleted.byGroupMember.chatViewName)) + } else { + MarkedDeletedText(generalGetString(R.string.marked_deleted_description)) + } } CIMetaView(ci, timedMessagesTTL) } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt index 8f3d7e6851..86647a0ef1 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt @@ -208,9 +208,9 @@ private fun ChatListToolbar(chatModel: ChatModel, drawerState: DrawerState, user } else if (chatModel.users.isEmpty()) { NavigationButtonMenu { scope.launch { if (drawerState.isOpen) drawerState.close() else drawerState.open() } } } else { - val users by remember { derivedStateOf { chatModel.users.toList() } } + val users by remember { derivedStateOf { chatModel.users.filter { u -> u.user.activeUser || !u.user.hidden } } } val allRead = users - .filter { !it.user.activeUser } + .filter { u -> !u.user.activeUser && !u.user.hidden } .all { u -> u.unreadCount == 0 } UserProfileButton(chatModel.currentUser.value?.profile?.image, allRead) { if (users.size == 1) { @@ -247,7 +247,7 @@ private fun ChatListToolbar(chatModel: ChatModel, drawerState: DrawerState, user } @Composable -private fun UserProfileButton(image: String?, allRead: Boolean, onButtonClicked: () -> Unit) { +fun UserProfileButton(image: String?, allRead: Boolean, onButtonClicked: () -> Unit) { IconButton(onClick = onButtonClicked) { Box { ProfileImage( diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ShareListView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ShareListView.kt index 8c8b20fc1c..bd97135c46 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ShareListView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ShareListView.kt @@ -24,12 +24,15 @@ import chat.simplex.app.model.* import chat.simplex.app.ui.theme.HighOrLowlight import chat.simplex.app.ui.theme.Indigo import chat.simplex.app.views.helpers.* +import kotlinx.coroutines.flow.MutableStateFlow @Composable fun ShareListView(chatModel: ChatModel, stopped: Boolean) { var searchInList by rememberSaveable { mutableStateOf("") } + val userPickerState by rememberSaveable(stateSaver = AnimatedViewState.saver()) { mutableStateOf(MutableStateFlow(AnimatedViewState.GONE)) } + val switchingUsers = rememberSaveable { mutableStateOf(false) } Scaffold( - topBar = { Column { ShareListToolbar(chatModel, stopped) { searchInList = it.trim() } } }, + topBar = { Column { ShareListToolbar(chatModel, userPickerState, stopped) { searchInList = it.trim() } } }, ) { Box(Modifier.padding(it)) { Column( @@ -45,23 +48,41 @@ fun ShareListView(chatModel: ChatModel, stopped: Boolean) { } } } + UserPicker(chatModel, userPickerState, switchingUsers, showSettings = false, showCancel = true, cancelClicked = { + chatModel.sharedContent.value = null + }) } @Composable private fun EmptyList() { - Box { - Text(stringResource(R.string.you_have_no_chats), Modifier.align(Alignment.Center), color = HighOrLowlight) + Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { + Text(stringResource(R.string.you_have_no_chats), color = HighOrLowlight) } } @Composable -private fun ShareListToolbar(chatModel: ChatModel, stopped: Boolean, onSearchValueChanged: (String) -> Unit) { +private fun ShareListToolbar(chatModel: ChatModel, userPickerState: MutableStateFlow, stopped: Boolean, onSearchValueChanged: (String) -> Unit) { var showSearch by rememberSaveable { mutableStateOf(false) } val hideSearchOnBack = { onSearchValueChanged(""); showSearch = false } if (showSearch) { BackHandler(onBack = hideSearchOnBack) } val barButtons = arrayListOf<@Composable RowScope.() -> Unit>() + val users by remember { derivedStateOf { chatModel.users.filter { u -> u.user.activeUser || !u.user.hidden } } } + val navButton: @Composable RowScope.() -> Unit = { + when { + showSearch -> NavigationButtonBack(hideSearchOnBack) + users.size > 1 -> { + val allRead = users + .filter { u -> !u.user.activeUser && !u.user.hidden } + .all { u -> u.unreadCount == 0 } + UserProfileButton(chatModel.currentUser.value?.profile?.image, allRead) { + userPickerState.value = AnimatedViewState.VISIBLE + } + } + else -> NavigationButtonBack { chatModel.sharedContent.value = null } + } + } if (chatModel.chats.size >= 8) { barButtons.add { IconButton({ showSearch = true }) { @@ -87,7 +108,7 @@ private fun ShareListToolbar(chatModel: ChatModel, stopped: Boolean, onSearchVal } DefaultTopAppBar( - navigationButton = { if (showSearch) NavigationButtonBack(hideSearchOnBack) else NavigationButtonBack { chatModel.sharedContent.value = null } }, + navigationButton = navButton, title = { Row(verticalAlignment = Alignment.CenterVertically) { Text( diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/UserPicker.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/UserPicker.kt index 9b10062d36..5d37695616 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/UserPicker.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/UserPicker.kt @@ -9,11 +9,12 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.* import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Done -import androidx.compose.material.icons.outlined.Settings +import androidx.compose.material.icons.filled.* +import androidx.compose.material.icons.outlined.* import androidx.compose.runtime.* import androidx.compose.ui.* import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalDensity @@ -33,10 +34,24 @@ import kotlinx.coroutines.launch import kotlin.math.roundToInt @Composable -fun UserPicker(chatModel: ChatModel, userPickerState: MutableStateFlow, switchingUsers: MutableState, openSettings: () -> Unit) { +fun UserPicker( + chatModel: ChatModel, + userPickerState: MutableStateFlow, + switchingUsers: MutableState, + showSettings: Boolean = true, + showCancel: Boolean = false, + cancelClicked: () -> Unit = {}, + settingsClicked: () -> Unit = {}, +) { val scope = rememberCoroutineScope() var newChat by remember { mutableStateOf(userPickerState.value) } - val users by remember { derivedStateOf { chatModel.users.sortedByDescending { it.user.activeUser } } } + val users by remember { + derivedStateOf { + chatModel.users + .filter { u -> u.user.activeUser || !u.user.hidden } + .sortedByDescending { it.user.activeUser } + } + } val animatedFloat = remember { Animatable(if (newChat.isVisible()) 0f else 1f) } LaunchedEffect(Unit) { launch { @@ -94,23 +109,22 @@ fun UserPicker(chatModel: ChatModel, userPickerState: MutableStateFlow UserProfilePickerItem(u.user, u.unreadCount, openSettings = { - openSettings() + settingsClicked() userPickerState.value = AnimatedViewState.GONE }) { userPickerState.value = AnimatedViewState.HIDING if (!u.user.activeUser) { - chatModel.chats.clear() scope.launch { val job = launch { delay(500) switchingUsers.value = true } - chatModel.controller.changeActiveUser(u.user.userId) + chatModel.controller.changeActiveUser(u.user.userId, null) job.cancel() switchingUsers.value = false } @@ -120,9 +134,17 @@ fun UserPicker(chatModel: ChatModel, userPickerState: MutableStateFlow Unit horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { - Row( - Modifier - .widthIn(max = LocalConfiguration.current.screenWidthDp.dp * 0.7f) - .padding(vertical = 8.dp), - verticalAlignment = Alignment.CenterVertically - ) { - ProfileImage( - image = u.image, - size = 54.dp - ) - Text( - u.displayName, - modifier = Modifier - .padding(start = 8.dp, end = 8.dp), - fontWeight = if (u.activeUser) FontWeight.Medium else FontWeight.Normal - ) - } + UserProfileRow(u) if (u.activeUser) { - Icon(Icons.Filled.Done, null, Modifier.size(20.dp), tint = MaterialTheme.colors.onBackground) + Icon(Icons.Filled.Done, null, Modifier.size(20.dp), tint = MaterialTheme.colors.onBackground) + } else if (u.hidden) { + Icon(Icons.Outlined.Lock, null, Modifier.size(20.dp), tint = HighOrLowlight) } else if (unreadCount > 0) { Row { Text( unreadCountStr(unreadCount), - color = MaterialTheme.colors.onPrimary, + color = Color.White, fontSize = 11.sp, modifier = Modifier - .background(MaterialTheme.colors.primary, shape = CircleShape) + .background(if (u.showNtfs) MaterialTheme.colors.primary else HighOrLowlight, shape = CircleShape) .sizeIn(minWidth = 20.dp, minHeight = 20.dp) .padding(horizontal = 3.dp) .padding(vertical = 1.dp), @@ -179,12 +187,35 @@ fun UserProfilePickerItem(u: User, unreadCount: Int = 0, onLongClick: () -> Unit ) Spacer(Modifier.width(2.dp)) } - } else { + } else if (!u.showNtfs) { + Icon(Icons.Outlined.NotificationsOff, null, Modifier.size(20.dp), tint = HighOrLowlight) + } else { Box(Modifier.size(20.dp)) } } } +@Composable +fun UserProfileRow(u: User) { + Row( + Modifier + .widthIn(max = LocalConfiguration.current.screenWidthDp.dp * 0.7f) + .padding(vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + ProfileImage( + image = u.image, + size = 54.dp + ) + Text( + u.displayName, + modifier = Modifier + .padding(start = 8.dp, end = 8.dp), + fontWeight = if (u.activeUser) FontWeight.Medium else FontWeight.Normal + ) + } +} + @Composable private fun SettingsPickerItem(onClick: () -> Unit) { SectionItemViewSpaceBetween(onClick, minHeight = 68.dp) { @@ -196,3 +227,15 @@ private fun SettingsPickerItem(onClick: () -> Unit) { Icon(Icons.Outlined.Settings, text, Modifier.size(20.dp), tint = MaterialTheme.colors.onBackground) } } + +@Composable +private fun CancelPickerItem(onClick: () -> Unit) { + SectionItemViewSpaceBetween(onClick, minHeight = 68.dp) { + val text = generalGetString(R.string.cancel_verb) + Text( + text, + color = MaterialTheme.colors.onBackground, + ) + Icon(Icons.Outlined.Close, text, Modifier.size(20.dp), tint = MaterialTheme.colors.onBackground) + } +} diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseEncryptionView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseEncryptionView.kt index fa511e0b81..634028ce4c 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseEncryptionView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseEncryptionView.kt @@ -1,5 +1,6 @@ package chat.simplex.app.views.database +import SectionDivider import SectionItemView import SectionItemViewSpaceBetween import SectionTextFooter @@ -25,13 +26,13 @@ import androidx.compose.ui.text.* import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.* import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp +import androidx.compose.ui.unit.* import chat.simplex.app.R import chat.simplex.app.SimplexApp import chat.simplex.app.model.* import chat.simplex.app.ui.theme.* import chat.simplex.app.views.helpers.* +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.datetime.Clock import kotlin.math.log2 @@ -161,7 +162,9 @@ fun DatabaseEncryptionLayout( } if (!initialRandomDBPassphrase.value && chatDbEncrypted == true) { - DatabaseKeyField( + SectionDivider() + + PassphraseField( currentKey, generalGetString(R.string.current_passphrase), modifier = Modifier.padding(horizontal = DEFAULT_PADDING), @@ -170,7 +173,9 @@ fun DatabaseEncryptionLayout( ) } - DatabaseKeyField( + SectionDivider() + + PassphraseField( newKey, generalGetString(R.string.new_passphrase), modifier = Modifier.padding(horizontal = DEFAULT_PADDING), @@ -201,7 +206,9 @@ fun DatabaseEncryptionLayout( !validKey(newKey.value) || progressIndicator.value - DatabaseKeyField( + SectionDivider() + + PassphraseField( confirmNewKey, generalGetString(R.string.confirm_new_passphrase), modifier = Modifier.padding(horizontal = DEFAULT_PADDING), @@ -212,7 +219,9 @@ fun DatabaseEncryptionLayout( }), ) - SectionItemViewSpaceBetween(onClickUpdate, disabled = disabled) { + SectionDivider() + + SectionItemViewSpaceBetween(onClickUpdate, disabled = disabled, minHeight = TextFieldDefaults.MinHeight) { Text(generalGetString(R.string.update_database_passphrase), color = if (disabled) HighOrLowlight else MaterialTheme.colors.primary) } } @@ -285,9 +294,10 @@ fun SavePassphraseSetting( initialRandomDBPassphrase: Boolean, storedKey: Boolean, progressIndicator: Boolean, + minHeight: Dp = TextFieldDefaults.MinHeight, onCheckedChange: (Boolean) -> Unit, ) { - SectionItemView { + SectionItemView(minHeight = minHeight) { Row(verticalAlignment = Alignment.CenterVertically) { Icon( if (storedKey) Icons.Filled.VpnKey else Icons.Filled.VpnKeyOff, @@ -349,13 +359,14 @@ private fun operationEnded(m: ChatModel, progressIndicator: MutableState, placeholder: String, modifier: Modifier = Modifier, showStrength: Boolean = false, isValid: (String) -> Boolean, keyboardActions: KeyboardActions = KeyboardActions(), + dependsOn: MutableState? = null, ) { var valid by remember { mutableStateOf(validKey(key.value)) } var showKey by remember { mutableStateOf(false) } @@ -436,6 +447,13 @@ fun DatabaseKeyField( ) } ) + LaunchedEffect(Unit) { + snapshotFlow { dependsOn?.value } + .distinctUntilChanged() + .collect { + valid = isValid(state.value.text) + } + } } // based on https://generatepasswords.org/how-to-calculate-entropy/ diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseErrorView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseErrorView.kt index 1dc25440ac..9f69ca2799 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseErrorView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseErrorView.kt @@ -206,7 +206,7 @@ private fun restoreDb(restoreDbFromBackup: MutableState, prefs: AppPref @Composable private fun DatabaseKeyField(text: MutableState, enabled: Boolean, onClick: (() -> Unit)? = null) { - DatabaseKeyField( + PassphraseField( text, generalGetString(R.string.enter_passphrase), isValid = ::validKey, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseView.kt index b0e2524d17..4c9bcf6b9f 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/database/DatabaseView.kt @@ -8,7 +8,6 @@ import SectionView import android.content.Context import android.content.res.Configuration import android.net.Uri -import android.os.FileUtils import android.util.Log import android.widget.Toast import androidx.activity.compose.ManagedActivityResultLauncher @@ -40,6 +39,7 @@ import chat.simplex.app.views.helpers.* import chat.simplex.app.views.usersettings.* import kotlinx.coroutines.* import kotlinx.datetime.* +import org.apache.commons.io.IOUtils import java.io.* import java.text.SimpleDateFormat import java.util.* @@ -620,7 +620,7 @@ private fun saveArchiveFromUri(context: Context, importedArchiveUri: Uri): Strin if (inputStream != null && archiveName != null) { val archivePath = "${context.cacheDir}/$archiveName" val destFile = File(archivePath) - FileUtils.copy(inputStream, FileOutputStream(destFile)) + IOUtils.copy(inputStream, FileOutputStream(destFile)) archivePath } else { Log.e(TAG, "saveArchiveFromUri null inputStream") diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/CloseSheetBar.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/CloseSheetBar.kt index 0d0d6675b3..6a7ab82771 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/CloseSheetBar.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/CloseSheetBar.kt @@ -4,6 +4,7 @@ import android.content.res.Configuration import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -22,7 +23,11 @@ fun CloseSheetBar(close: () -> Unit, endButtons: @Composable RowScope.() -> Unit Modifier .padding(top = 4.dp), // Like in DefaultAppBar content = { - Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { + Row( + Modifier.fillMaxWidth().height(TextFieldDefaults.MinHeight), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { NavigationButtonBack(close) Row { endButtons() diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultTopAppBar.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultTopAppBar.kt index 827542c68a..8544fdc435 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultTopAppBar.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/DefaultTopAppBar.kt @@ -34,7 +34,7 @@ fun DefaultTopAppBar( if (!showSearch) { title?.invoke() } else { - SearchTextField(Modifier.fillMaxWidth(), stringResource(android.R.string.search_go), onSearchValueChanged) + SearchTextField(Modifier.fillMaxWidth(), stringResource(android.R.string.search_go), alwaysVisible = false, onSearchValueChanged) } }, backgroundColor = if (isInDarkTheme()) ToolbarDark else ToolbarLight, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt index 1ccc6803af..9356ebfa65 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/GetImageView.kt @@ -6,7 +6,6 @@ import android.content.* import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.content.pm.PackageManager import android.graphics.* -import android.graphics.ImageDecoder.DecodeException import android.net.Uri import android.provider.MediaStore import android.util.Base64 @@ -114,7 +113,7 @@ fun base64ToBitmap(base64ImageString: String): Bitmap { class CustomTakePicturePreview(var uri: Uri?, var tmpFile: File?): ActivityResultContract() { @CallSuper override fun createIntent(context: Context, input: Void?): Intent { - tmpFile = File.createTempFile("image", ".bmp", context.filesDir) + tmpFile = File.createTempFile("image", ".bmp", File(getAppFilesDirectory(SimplexApp.context))) // Since the class should return Uri, the file should be deleted somewhere else. And in order to be sure, delegate this to system tmpFile?.deleteOnExit() uri = FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.provider", tmpFile!!) @@ -205,17 +204,10 @@ fun GetImageBottomSheet( val context = LocalContext.current val processPickedImage = { uri: Uri? -> if (uri != null) { - val source = ImageDecoder.createSource(context.contentResolver, uri) - try { - val bitmap = ImageDecoder.decodeBitmap(source) + val bitmap = getBitmapFromUri(uri) + if (bitmap != null) { imageBitmap.value = uri onImageChange(bitmap) - } catch (e: DecodeException) { - Log.e(TAG, "Unable to decode the image: ${e.stackTraceToString()}") - AlertManager.shared.showAlertMsg( - title = generalGetString(R.string.image_decoding_exception_title), - text = generalGetString(R.string.image_decoding_exception_desc) - ) } } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/SearchTextField.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/SearchTextField.kt index 261d4f8acc..38c6a2647b 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/SearchTextField.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/SearchTextField.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.* +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle @@ -29,15 +30,18 @@ import kotlinx.coroutines.delay @OptIn(ExperimentalComposeUiApi::class) @Composable -fun SearchTextField(modifier: Modifier, placeholder: String, onValueChange: (String) -> Unit) { +fun SearchTextField(modifier: Modifier, placeholder: String, alwaysVisible: Boolean, onValueChange: (String) -> Unit) { var searchText by rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) } val focusRequester = remember { FocusRequester() } + val focusManager = LocalFocusManager.current val keyboard = LocalSoftwareKeyboardController.current - LaunchedEffect(Unit) { - focusRequester.requestFocus() - delay(200) - keyboard?.show() + if (!alwaysVisible) { + LaunchedEffect(Unit) { + focusRequester.requestFocus() + delay(200) + keyboard?.show() + } } DisposableEffect(Unit) { @@ -87,7 +91,14 @@ fun SearchTextField(modifier: Modifier, placeholder: String, onValueChange: (Str Text(placeholder) }, trailingIcon = if (searchText.text.isNotEmpty()) {{ - IconButton({ searchText = TextFieldValue(""); onValueChange("") }) { + IconButton({ + if (alwaysVisible) { + keyboard?.hide() + focusManager.clearFocus() + } + searchText = TextFieldValue(""); + onValueChange("") + }) { Icon(Icons.Default.Close, stringResource(R.string.icon_descr_close_button), tint = MaterialTheme.colors.primary,) } }} else null, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Share.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Share.kt index 4a1b8a4f82..8c05a17ecc 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Share.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Share.kt @@ -1,5 +1,6 @@ package chat.simplex.app.views.helpers +import android.Manifest import android.content.* import android.net.Uri import android.provider.MediaStore @@ -81,6 +82,7 @@ fun imageMimeType(fileName: String): String { } } +/** Before calling, make sure the user allows to write to external storage [Manifest.permission.WRITE_EXTERNAL_STORAGE] */ fun saveImage(cxt: Context, ciFile: CIFile?) { val filePath = getLoadedFilePath(cxt, ciFile) val fileName = ciFile?.fileName diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt index d5e2978d9c..cdb5675545 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/helpers/Util.kt @@ -9,6 +9,7 @@ import android.content.res.Configuration import android.content.res.Resources import android.graphics.* import android.graphics.Typeface +import android.graphics.drawable.Drawable import android.net.Uri import android.os.* import android.provider.OpenableColumns @@ -33,10 +34,12 @@ import androidx.compose.ui.unit.* import androidx.core.content.FileProvider import androidx.core.text.HtmlCompat import chat.simplex.app.* +import chat.simplex.app.R import chat.simplex.app.model.* import kotlinx.coroutines.* import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString +import org.apache.commons.io.IOUtils import java.io.* import java.text.SimpleDateFormat import java.util.* @@ -327,6 +330,14 @@ fun getFileName(context: Context, uri: Uri): String? { } } +fun getAppFilePath(context: Context, uri: Uri): String? { + return context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> + val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME) + cursor.moveToFirst() + getAppFilePath(context, cursor.getString(nameIndex)) + } +} + fun getFileSize(context: Context, uri: Uri): Long? { return context.contentResolver.query(uri, null, null, null, null)?.use { cursor -> val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE) @@ -335,9 +346,48 @@ fun getFileSize(context: Context, uri: Uri): Long? { } } +fun getBitmapFromUri(uri: Uri, withAlertOnException: Boolean = true): Bitmap? { + return if (Build.VERSION.SDK_INT >= 28) { + val source = ImageDecoder.createSource(SimplexApp.context.contentResolver, uri) + try { + ImageDecoder.decodeBitmap(source) + } catch (e: android.graphics.ImageDecoder.DecodeException) { + Log.e(TAG, "Unable to decode the image: ${e.stackTraceToString()}") + if (withAlertOnException) { + AlertManager.shared.showAlertMsg( + title = generalGetString(R.string.image_decoding_exception_title), + text = generalGetString(R.string.image_decoding_exception_desc) + ) + } + null + } + } else { + BitmapFactory.decodeFile(getAppFilePath(SimplexApp.context, uri)) + } +} + +fun getDrawableFromUri(uri: Uri, withAlertOnException: Boolean = true): Drawable? { + return if (Build.VERSION.SDK_INT >= 28) { + val source = ImageDecoder.createSource(SimplexApp.context.contentResolver, uri) + try { + ImageDecoder.decodeDrawable(source) + } catch (e: android.graphics.ImageDecoder.DecodeException) { + if (withAlertOnException) { + AlertManager.shared.showAlertMsg( + title = generalGetString(R.string.image_decoding_exception_title), + text = generalGetString(R.string.image_decoding_exception_desc) + ) + } + Log.e(TAG, "Error while decoding drawable: ${e.stackTraceToString()}") + null + } + } else { + Drawable.createFromPath(getAppFilePath(SimplexApp.context, uri)) + } +} + fun saveImage(context: Context, uri: Uri): String? { - val source = ImageDecoder.createSource(SimplexApp.context.contentResolver, uri) - val bitmap = ImageDecoder.decodeBitmap(source) + val bitmap = getBitmapFromUri(uri) ?: return null return saveImage(context, bitmap) } @@ -408,7 +458,7 @@ fun saveFileFromUri(context: Context, uri: Uri): String? { if (inputStream != null && fileToSave != null) { val destFileName = uniqueCombine(context, fileToSave) val destFile = File(getAppFilePath(context, destFileName)) - FileUtils.copy(inputStream, FileOutputStream(destFile)) + IOUtils.copy(inputStream, FileOutputStream(destFile)) destFileName } else { Log.e(chat.simplex.app.TAG, "Util.kt saveFileFromUri null inputStream") diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HiddenProfileView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HiddenProfileView.kt new file mode 100644 index 0000000000..b9fa39b11d --- /dev/null +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/HiddenProfileView.kt @@ -0,0 +1,89 @@ +package chat.simplex.app.views.usersettings + +import SectionDivider +import SectionItemView +import SectionItemViewSpaceBetween +import SectionSpacer +import SectionTextFooter +import SectionView +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.* +import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import chat.simplex.app.R +import chat.simplex.app.model.ChatModel +import chat.simplex.app.model.User +import chat.simplex.app.ui.theme.* +import chat.simplex.app.views.chatlist.UserProfileRow +import chat.simplex.app.views.database.PassphraseField +import chat.simplex.app.views.helpers.* + +@Composable +fun HiddenProfileView( + m: ChatModel, + user: User, + close: () -> Unit, +) { + HiddenProfileLayout( + user, + saveProfilePassword = { hidePassword -> + withBGApi { + try { + val u = m.controller.apiHideUser(user.userId, hidePassword) + m.updateUser(u) + close() + } catch (e: Exception) { + AlertManager.shared.showAlertMsg( + title = generalGetString(R.string.error_saving_user_password), + text = e.stackTraceToString() + ) + } + } + } + ) +} + +@Composable +private fun HiddenProfileLayout( + user: User, + saveProfilePassword: (String) -> Unit +) { + Column( + Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + .padding(bottom = DEFAULT_BOTTOM_PADDING), + ) { + AppBarTitle(stringResource(R.string.hide_profile)) + SectionView(padding = PaddingValues(start = 8.dp, end = DEFAULT_PADDING)) { + UserProfileRow(user) + } + SectionSpacer() + + val hidePassword = rememberSaveable { mutableStateOf("") } + val confirmHidePassword = rememberSaveable { mutableStateOf("") } + val confirmValid by remember { derivedStateOf { confirmHidePassword.value == "" || hidePassword.value == confirmHidePassword.value } } + val saveDisabled by remember { derivedStateOf { hidePassword.value == "" || confirmHidePassword.value == "" || !confirmValid } } + SectionView(stringResource(R.string.hidden_profile_password).uppercase()) { + SectionItemView { + PassphraseField(hidePassword, generalGetString(R.string.password_to_show), isValid = { true }, showStrength = true) + } + SectionDivider() + SectionItemView { + PassphraseField(confirmHidePassword, stringResource(R.string.confirm_password), isValid = { confirmValid }, dependsOn = hidePassword) + } + SectionDivider() + SectionItemViewSpaceBetween({ saveProfilePassword(hidePassword.value) }, disabled = saveDisabled, minHeight = TextFieldDefaults.MinHeight) { + Text(generalGetString(R.string.save_profile_password), color = if (saveDisabled) HighOrLowlight else MaterialTheme.colors.primary) + } + } + SectionTextFooter(stringResource(R.string.to_reveal_profile_enter_password)) + } +} \ No newline at end of file diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt index 5d64b85f93..22774a0ab9 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/SettingsView.kt @@ -6,6 +6,7 @@ import SectionSpacer import SectionView import android.content.Context import android.content.res.Configuration +import androidx.activity.compose.BackHandler import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.material.* @@ -13,6 +14,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.outlined.* import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -20,7 +22,9 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.* import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.capitalize import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.* @@ -58,6 +62,18 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit) { setPerformLA = setPerformLA, showModal = { modalView -> { ModalManager.shared.showModal { modalView(chatModel) } } }, showSettingsModal = { modalView -> { ModalManager.shared.showModal(true) { modalView(chatModel) } } }, + showSettingsModalWithSearch = { modalView -> + ModalManager.shared.showCustomModal { close -> + val search = rememberSaveable { mutableStateOf("") } + ModalView( + { close() }, + if (isInDarkTheme()) MaterialTheme.colors.background else SettingsBackgroundLight, + endButtons = { + SearchTextField(Modifier.fillMaxWidth(), stringResource(android.R.string.search_go), alwaysVisible = true) { search.value = it } + }, + content = { modalView(chatModel, search) }) + } + }, showCustomModal = { modalView -> { ModalManager.shared.showCustomModal { close -> modalView(chatModel, close) } } }, showVersion = { withApi { @@ -115,6 +131,7 @@ fun SettingsLayout( setPerformLA: (Boolean) -> Unit, showModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit), showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit), + showSettingsModalWithSearch: (@Composable (ChatModel, MutableState) -> Unit) -> Unit, showCustomModal: (@Composable (ChatModel, () -> Unit) -> Unit) -> (() -> Unit), showVersion: () -> Unit, withAuth: (block: () -> Unit) -> Unit @@ -141,7 +158,8 @@ fun SettingsLayout( ProfilePreview(profile, stopped = stopped) } SectionDivider() - SettingsActionItem(Icons.Outlined.ManageAccounts, stringResource(R.string.your_chat_profiles), { withAuth { showSettingsModal { UserProfilesView(it) }() } }, disabled = stopped) + val profileHidden = rememberSaveable { mutableStateOf(false) } + SettingsActionItem(Icons.Outlined.ManageAccounts, stringResource(R.string.your_chat_profiles), { withAuth { showSettingsModalWithSearch { it, search -> UserProfilesView(it, search, profileHidden) } } }, disabled = stopped) SectionDivider() SettingsIncognitoActionItem(incognitoPref, incognito, stopped) { showModal { IncognitoView() }() } SectionDivider() @@ -531,6 +549,7 @@ fun PreviewSettingsLayout() { setPerformLA = {}, showModal = { {} }, showSettingsModal = { {} }, + showSettingsModalWithSearch = { }, showCustomModal = { {} }, showVersion = {}, withAuth = {}, diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfilesView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfilesView.kt index 525e521230..960da1a88d 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfilesView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/usersettings/UserProfilesView.kt @@ -2,6 +2,7 @@ package chat.simplex.app.views.usersettings import SectionDivider import SectionItemView +import SectionSpacer import SectionTextFooter import SectionView import androidx.compose.foundation.* @@ -10,6 +11,7 @@ import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.* import androidx.compose.runtime.* +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource @@ -17,18 +19,28 @@ import androidx.compose.ui.text.* import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import chat.simplex.app.R +import chat.simplex.app.chatPasswordHash import chat.simplex.app.model.* import chat.simplex.app.ui.theme.* import chat.simplex.app.views.chat.item.ItemAction import chat.simplex.app.views.chatlist.UserProfilePickerItem import chat.simplex.app.views.helpers.* import chat.simplex.app.views.onboarding.CreateProfile +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch @Composable -fun UserProfilesView(m: ChatModel) { +fun UserProfilesView(m: ChatModel, search: MutableState, profileHidden: MutableState) { + val searchTextOrPassword = rememberSaveable { search } val users by remember { derivedStateOf { m.users.map { it.user } } } + val filteredUsers by remember { derivedStateOf { filteredUsers(m, searchTextOrPassword.value) } } UserProfilesView( users = users, + filteredUsers = filteredUsers, + profileHidden = profileHidden, + searchTextOrPassword = searchTextOrPassword, + showHiddenProfilesNotice = m.controller.appPrefs.showHiddenProfilesNotice, + visibleUsersCount = visibleUsersCount(m), addUser = { ModalManager.shared.showModalCloseable { close -> CreateProfile(m, close) @@ -36,38 +48,72 @@ fun UserProfilesView(m: ChatModel) { }, activateUser = { user -> withBGApi { - m.controller.changeActiveUser(user.userId) + m.controller.changeActiveUser(user.userId, userViewPassword(user, searchTextOrPassword.value)) } }, removeUser = { user -> - val text = buildAnnotatedString { - append(generalGetString(R.string.users_delete_all_chats_deleted) + "\n\n" + generalGetString(R.string.users_delete_profile_for) + " ") - withStyle(SpanStyle(fontWeight = FontWeight.Bold)) { - append(user.displayName) + if (m.users.size > 1 && (user.hidden || visibleUsersCount(m) > 1)) { + val text = buildAnnotatedString { + append(generalGetString(R.string.users_delete_all_chats_deleted) + "\n\n" + generalGetString(R.string.users_delete_profile_for) + " ") + withStyle(SpanStyle(fontWeight = FontWeight.Bold)) { + append(user.displayName) + } + append(":") } - append(":") - } - AlertManager.shared.showAlertDialogButtonsColumn( - title = generalGetString(R.string.users_delete_question), - text = text, - buttons = { - Column { - SectionItemView({ - AlertManager.shared.hideAlert() - removeUser(m, user, users, true) - }) { - Text(stringResource(R.string.users_delete_with_connections), color = Color.Red) - } - SectionItemView({ - AlertManager.shared.hideAlert() - removeUser(m, user, users, false) - } - ) { - Text(stringResource(R.string.users_delete_data_only), color = Color.Red) + AlertManager.shared.showAlertDialogButtonsColumn( + title = generalGetString(R.string.users_delete_question), + text = text, + buttons = { + Column { + SectionItemView({ + AlertManager.shared.hideAlert() + removeUser(m, user, users, true, searchTextOrPassword.value) + }) { + Text(stringResource(R.string.users_delete_with_connections), color = Color.Red) + } + SectionItemView({ + AlertManager.shared.hideAlert() + removeUser(m, user, users, false, searchTextOrPassword.value) + } + ) { + Text(stringResource(R.string.users_delete_data_only), color = Color.Red) + } } } + ) + } else { + AlertManager.shared.showAlertMsg( + title = generalGetString(R.string.cant_delete_user_profile), + text = if (m.users.size > 1) { + generalGetString(R.string.should_be_at_least_one_visible_profile) + } else { + generalGetString(R.string.should_be_at_least_one_profile) + } + ) + } + }, + unhideUser = { user -> + setUserPrivacy(m) { m.controller.apiUnhideUser(user.userId, userViewPassword(user, searchTextOrPassword.value)) } + }, + muteUser = { user -> + setUserPrivacy(m, onSuccess = { if (m.controller.appPrefs.showMuteProfileAlert.get()) showMuteProfileAlert(m.controller.appPrefs.showMuteProfileAlert) }) { + m.controller.apiMuteUser(user.userId, userViewPassword(user, searchTextOrPassword.value)) + } + }, + unmuteUser = { user -> + setUserPrivacy(m) { m.controller.apiUnmuteUser(user.userId, userViewPassword(user, searchTextOrPassword.value)) } + }, + showHiddenProfile = { user -> + ModalManager.shared.showModalCloseable(true) { close -> + HiddenProfileView(m, user) { + profileHidden.value = true + withBGApi { + delay(10_000) + profileHidden.value = false + } + close() } - ) + } } ) } @@ -75,9 +121,18 @@ fun UserProfilesView(m: ChatModel) { @Composable private fun UserProfilesView( users: List, + filteredUsers: List, + searchTextOrPassword: MutableState, + profileHidden: MutableState, + visibleUsersCount: Int, + showHiddenProfilesNotice: SharedPreference, addUser: () -> Unit, activateUser: (User) -> Unit, removeUser: (User) -> Unit, + unhideUser: (User) -> Unit, + muteUser: (User) -> Unit, + unmuteUser: (User) -> Unit, + showHiddenProfile: (User) -> Unit, ) { Column( Modifier @@ -85,25 +140,59 @@ private fun UserProfilesView( .verticalScroll(rememberScrollState()) .padding(bottom = DEFAULT_PADDING), ) { + if (profileHidden.value) { + SectionView { + SettingsActionItem(Icons.Outlined.LockOpen, stringResource(R.string.enter_password_to_show), click = { + profileHidden.value = false + } + ) + } + SectionSpacer() + } AppBarTitle(stringResource(R.string.your_chat_profiles)) SectionView { - for (user in users) { - UserView(user, users, activateUser, removeUser) + for (user in filteredUsers) { + UserView(user, users, visibleUsersCount, activateUser, removeUser, unhideUser, muteUser, unmuteUser, showHiddenProfile) SectionDivider() } - SectionItemView(addUser, minHeight = 68.dp) { - Icon(Icons.Outlined.Add, stringResource(R.string.users_add), tint = MaterialTheme.colors.primary) - Spacer(Modifier.padding(horizontal = 4.dp)) - Text(stringResource(R.string.users_add), color = MaterialTheme.colors.primary) + if (searchTextOrPassword.value.isEmpty()) { + SectionItemView(addUser, minHeight = 68.dp) { + Icon(Icons.Outlined.Add, stringResource(R.string.users_add), tint = MaterialTheme.colors.primary) + Spacer(Modifier.padding(horizontal = 4.dp)) + Text(stringResource(R.string.users_add), color = MaterialTheme.colors.primary) + } + } + } + SectionTextFooter(stringResource(R.string.tap_to_activate_profile)) + LaunchedEffect(Unit) { + if (showHiddenProfilesNotice.state.value && users.size > 1) { + AlertManager.shared.showAlertDialog( + title = generalGetString(R.string.make_profile_private), + text = generalGetString(R.string.you_can_hide_or_mute_user_profile), + confirmText = generalGetString(R.string.ok), + dismissText = generalGetString(R.string.dont_show_again), + onDismiss = { + showHiddenProfilesNotice.set(false) + }, + ) } } - SectionTextFooter(stringResource(R.string.your_chat_profiles_stored_locally)) } } @Composable -private fun UserView(user: User, users: List, activateUser: (User) -> Unit, removeUser: (User) -> Unit) { +private fun UserView( + user: User, + users: List, + visibleUsersCount: Int, + activateUser: (User) -> Unit, + removeUser: (User) -> Unit, + unhideUser: (User) -> Unit, + muteUser: (User) -> Unit, + unmuteUser: (User) -> Unit, + showHiddenProfile: (User) -> Unit, +) { var showDropdownMenu by remember { mutableStateOf(false) } UserProfilePickerItem(user, onLongClick = { if (users.size > 1) showDropdownMenu = true }) { activateUser(user) @@ -114,28 +203,103 @@ private fun UserView(user: User, users: List, activateUser: (User) -> Unit onDismissRequest = { showDropdownMenu = false }, Modifier.width(220.dp) ) { + if (user.hidden) { + ItemAction(stringResource(R.string.user_unhide), Icons.Outlined.LockOpen, onClick = { + showDropdownMenu = false + unhideUser(user) + }) + } else { + if (visibleUsersCount > 1) { + ItemAction(stringResource(R.string.user_hide), Icons.Outlined.Lock, onClick = { + showDropdownMenu = false + showHiddenProfile(user) + }) + } + if (user.showNtfs) { + ItemAction(stringResource(R.string.user_mute), Icons.Outlined.NotificationsOff, onClick = { + showDropdownMenu = false + muteUser(user) + }) + } else { + ItemAction(stringResource(R.string.user_unmute), Icons.Outlined.Notifications, onClick = { + showDropdownMenu = false + unmuteUser(user) + }) + } + } ItemAction(stringResource(R.string.delete_verb), Icons.Outlined.Delete, color = Color.Red, onClick = { removeUser(user) showDropdownMenu = false - } - ) + }) } } } -private fun removeUser(m: ChatModel, user: User, users: List, delSMPQueues: Boolean) { +private fun filteredUsers(m: ChatModel, searchTextOrPassword: String): List { + val s = searchTextOrPassword.trim() + val lower = s.lowercase() + return m.users.filter { u -> + if ((u.user.activeUser || u.user.viewPwdHash == null) && (s == "" || u.user.chatViewName.lowercase().contains(lower))) { + true + } else if (u.user.viewPwdHash != null) { + s != "" && chatPasswordHash(s, u.user.viewPwdHash.salt) == u.user.viewPwdHash.hash + } else { + false + } + }.map { it.user } +} + +private fun visibleUsersCount(m: ChatModel): Int = m.users.filter { u -> !u.user.hidden }.size + +private fun userViewPassword(user: User, searchTextOrPassword: String): String? = + if (user.activeUser || !user.hidden) null else searchTextOrPassword + +private fun removeUser(m: ChatModel, user: User, users: List, delSMPQueues: Boolean, searchTextOrPassword: String) { if (users.size < 2) return withBGApi { + suspend fun deleteUser(user: User) { + m.controller.apiDeleteUser(user.userId, delSMPQueues, userViewPassword(user, searchTextOrPassword)) + m.removeUser(user) + } try { if (user.activeUser) { - val newActive = users.first { !it.activeUser } - m.controller.changeActiveUser_(newActive.userId) + val newActive = users.firstOrNull { u -> !u.activeUser && !u.hidden } + if (newActive != null) { + m.controller.changeActiveUser_(newActive.userId, null) + deleteUser(user.copy(activeUser = false)) + } + } else { + deleteUser(user) } - m.controller.apiDeleteUser(user.userId, delSMPQueues) - m.users.removeAll { it.user.userId == user.userId } } catch (e: Exception) { AlertManager.shared.showAlertMsg(generalGetString(R.string.error_deleting_user), e.stackTraceToString()) } } } + +private fun setUserPrivacy(m: ChatModel, onSuccess: (() -> Unit)? = null, api: suspend () -> User) { + withBGApi { + try { + m.updateUser(api()) + onSuccess?.invoke() + } catch (e: Exception) { + AlertManager.shared.showAlertMsg( + title = generalGetString(R.string.error_updating_user_privacy), + text = e.stackTraceToString() + ) + } + } +} + +private fun showMuteProfileAlert(showMuteProfileAlert: SharedPreference) { + AlertManager.shared.showAlertDialog( + title = generalGetString(R.string.muted_when_inactive), + text = generalGetString(R.string.you_will_still_receive_calls_and_ntfs), + confirmText = generalGetString(R.string.ok), + dismissText = generalGetString(R.string.dont_show_again), + onDismiss = { + showMuteProfileAlert.set(false) + }, + ) +} \ No newline at end of file diff --git a/apps/android/app/src/main/res/drawable/edit_text_cursor.xml b/apps/android/app/src/main/res/drawable/edit_text_cursor.xml new file mode 100644 index 0000000000..683c3a4dd4 --- /dev/null +++ b/apps/android/app/src/main/res/drawable/edit_text_cursor.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/android/app/src/main/res/values-cs/strings.xml b/apps/android/app/src/main/res/values-cs/strings.xml index d72c313d8d..f11ae67ea8 100644 --- a/apps/android/app/src/main/res/values-cs/strings.xml +++ b/apps/android/app/src/main/res/values-cs/strings.xml @@ -1,6 +1,6 @@ - Povolte hlasové zprávy, pouze pokud je váš kontakt povolí. + Povolit hlasové zprávy, pokud je váš kontakt povolí. Mizící zprávy povoleny. Hlasové zprávy povoleny. Správci mohou vytvářet odkazy pro připojení ke skupinám. @@ -48,7 +48,7 @@ SERVERY Příjímáno přez Vytvoření tajné skupiny - Zobrazení názvu skupiny: + Zobrazený název skupiny: Úplný název skupiny: Váš chat profil bude zaslán členům skupiny Profil skupiny je uložen v zařízeních členů, nikoli na serverech. @@ -854,7 +854,7 @@ Přeskočit pozvání členů Vybrat kontakty Zkontrolované kontakty - %1$s kontakt(y) vybrán(y) + %d kontakt(y) vybrán(y) Pozvat členy %1$s MEMBERS vy: %1$s @@ -906,7 +906,7 @@ Světlé Tmavé Téma - Kontakt povolen + Kontakt povolil zapnuto vypnuto Chat předvolby @@ -963,7 +963,6 @@ Vaše adresa Váš chat profil bude odeslán \nvašemu kontaktu - Vaše chat profily jsou uloženy lokálně, pouze ve vašem zařízení. Vaše konverzace Do níže uvedeného pole vložte odkaz, který jste obdrželi pro spojení s kontaktem. Sdílet pozvánku @@ -981,4 +980,41 @@ Chyba aktualizace odkazu skupiny Počáteční role Systém + Uložit servery\? + Znovu neukazuj + Nemohu smazat uživatelský profil! + Přidat uvítací zprávu + Čínské a Španělské rozhranní + Hlasové a video hovory + Potvrdit heslo + Pro zobrazení zadejte heslo výše! + Další snížení spotřeby baterie + Chyba ukládání hesla uživatele + Chyba aktualizace soukromí uživatele + Správa skupin + Uvítací zpráva skupin + Skryté chat profily + Hesla skrytých profilů + Skrýt + Skrýt profil + Změnit profil na soukromý! + Další vylepšení již brzy! + Nyní mohou správci: +\n- mazat zprávy členů. +\n- zakázat členy (role \"pozorovatel\") + Uložit heslo profilu + Ztlumit + Chraňte své chat profily heslem! + Uložit a aktualizovat profil skupiny + Ztlumit při neaktivitě! + Heslo k zobrazení + Uložit uvítací zprávu\? + Nastavte zprávu zobrazenou novým členům! + Podpora bluetooth a další vylepšení. + Klepnutím aktivujete profil. + Díky uživatelům - překládejte prostřednictvím Weblate! + Měl by tam být alespoň jeden uživatelský profil. + Uvítací zpráva + Uvítací zpráva + Zrušit ztlumení \ No newline at end of file diff --git a/apps/android/app/src/main/res/values-de/strings.xml b/apps/android/app/src/main/res/values-de/strings.xml index 960c3b2ea0..ed90ed6678 100644 --- a/apps/android/app/src/main/res/values-de/strings.xml +++ b/apps/android/app/src/main/res/values-de/strings.xml @@ -765,7 +765,7 @@ Kontakte auswählen Kontakt geprüft Löschen - %1$s Kontakt(e) ausgewählt + %d Kontakt(e) ausgewählt Keine Kontakte ausgewählt Kontakt kann nicht eingeladen werden! Sie versuchen, einen Kontakt, mit dem Sie ein Inkognito-Profil geteilt haben, in die Gruppe einzuladen, in der Sie Ihr Hauptprofil verwenden. @@ -1023,7 +1023,6 @@ Nur lokale Profildaten Profil und Serververbindungen Diese Einstellung gilt für Nachrichten in Ihrem aktuellen Chat-Profil - Ihre Chat-Profile werden nur lokal auf Ihrem Endgerät gespeichert Doppelter Anzeigename! Fehler beim Erstellen des Profils! Fehler beim Umschalten des Profils! @@ -1055,4 +1054,46 @@ Bitte kontaktieren Sie den Gruppen-Administrator. Diese Nachricht wird für alle Gruppenmitglieder gelöscht. System + Bestätigen Sie das Passwort + Das Benutzerprofil kann nicht gelöscht werden! + Nicht nochmals anzeigen + Chinesische und spanische Bedienoberfläche + Audio- und Videoanrufe + Fügen Sie eine Begrüßungsmeldung hinzu + Fehler beim Aktualisieren der Benutzer-Privatsphäre + Alle Server speichern\? + Verberge das Profil + Passwort anzeigen + Profil-Passwort speichern + Fehler beim Speichern des Benutzer-Passworts + Verborgenes Profil-Passwort + Begrüßungsmeldung + Begrüßungsmeldung speichern\? + Verbergen aufheben + Geben Sie oben das Passwort für die Anzeige an! + Erzeugen Sie ein privates Profil! + Stummschalten + Tippen Sie, um das Profil zu aktivieren. + Es muss mindestens ein Benutzer-Profil vorhanden sein. + Es muss mindestens ein sichtbares Benutzer-Profil vorhanden sein. + Stummschaltung aufheben + Bei Inaktivität stummgeschaltet! + Schützen Sie Ihre Chat-Profile mit einem Passwort! + Bluetooth-Unterstützung und weitere Verbesserungen. + Administratoren können nun +\n- Nachrichten von Gruppenmitgliedern löschen +\n- Gruppenmitglieder deaktivieren (\"Beobachter\"-Rolle) + Gruppen-Begrüßungsmeldung + Weiter reduzierter Batterieverbrauch + Weitere Verbesserungen sind bald verfügbar! + Legen Sie die Nachricht fest, die neuen Mitgliedern angezeigt werden soll! + Dank der Nutzer - Tragen Sie per Weblate bei! + Gruppenmoderation + Verborgene Chat-Profile + Verberge + Sichern und aktualisieren des Gruppen-Profils + Sie können Anrufe und Benachrichtigungen auch von stummgeschalteten Profilen empfangen, solange diese aktiv sind. + Begrüßungsmeldung + Sie können ein Benutzerprofil verbergen oder stummschalten - für das Menü gedrückt halten. + Geben Sie ein vollständiges Passwort in das Suchfeld auf der Seite \"Meine Chat-Profile\" ein, um Ihr verborgenes Profil zu sehen. \ No newline at end of file diff --git a/apps/android/app/src/main/res/values-es/strings.xml b/apps/android/app/src/main/res/values-es/strings.xml index 6eda6906ea..880ec2f084 100644 --- a/apps/android/app/src/main/res/values-es/strings.xml +++ b/apps/android/app/src/main/res/values-es/strings.xml @@ -10,20 +10,20 @@ un dia un mes una semana - Permitir que desaparezcan los mensajes sólo si su contacto lo permite. + Permitir mensajes temporales sólo si tu contacto los permite. Añadir servidores escaneando códigos QR. Añadir servidores predefinidos Todos los miembros del grupo permanecerán conectados. - Permitir la eliminación irreversible de mensajes sólo si tu contacto también lo permite para tí. + Permitir la eliminación irreversible de mensajes sólo si tu contacto también lo permite. Android Keystore se usará para almacenar de forma segura la frase de contraseña después de reiniciar la aplicación o cambiar la frase de contraseña - permitirá recibir notificaciones. - Permitir a tus contactos enviar mensajes que desaparecen. + Permitir a tus contactos enviar mensajes temporales Permitir a tus contactos enviar mensajes de voz. siempre La aplicación sólo puede recibir notificaciones cuando se está ejecutando, no se iniciará ningún servicio en segundo plano. ICONO DE LA APLICACIÓN Se enviará un perfil aleatorio al contacto del que recibió este enlace La optimización de la batería está activa, desactivando el servicio en segundo plano y las solicitudes periódicas de nuevos mensajes. Puedes volver a activarlos en Configuración. - El servicio en segundo plano está siempre en funcionamiento – las notificaciones se mostrarán en cuanto los mensajes estén disponibles. + El servicio en segundo plano está siempre en funcionamiento – las notificaciones se muestran en cuanto los mensajes estén disponibles. Se puede desactivar en Configuración – las notificaciones se seguirán mostrando mientras la app esté en funcionamiento. Siempre activo Permitir @@ -80,12 +80,12 @@ Solicita recibir la imagen Ten en cuenta: NO podrás recuperar o cambiar la contraseña si la pierdes. Tanto tú como tu contacto podéis enviar mensajes de voz. - ¡Usa más batería! El servicio en segundo plano está siempre en funcionamiento - las notificaciones se mostrarán tan pronto como los mensajes estén disponibles. + ¡Gasta más batería! El servicio en segundo plano está siempre en funcionamiento - las notificaciones se mostrarán tan pronto como los mensajes estén disponibles. Tanto tú como tu contacto podéis eliminar de forma irreversible los mensajes enviados. Tanto tú como tu contacto podéis enviar mensajes temporales. Escanear código QR: para conectar con tu contacto que te muestre código QR. Crear - Crear enlace único de invitación + Crear enlace de invitación de un uso. Crear grupo secreto La contraseña de cifrado de la base de datos será actualizada. ID de la base de datos @@ -152,7 +152,7 @@ Nombre del contacto Copiar Crear tu perfil - Conectar mediante relay + Siempre usar relay La base de datos está cifrada con una contraseña aleatoria. Cámbiala antes de exportar. %d archivo(s) con tamaño total de %s ¿Activar eliminación automática de mensajes\? @@ -166,7 +166,7 @@ Desconectado Conectado Copiado en portapapeles - Crear enlace único de invitación + Crear enlace de invitación de un uso. 💻 PC: escanéa el código QR desde la app mediante Escanéo de código QR Eliminar Eliminar @@ -272,7 +272,7 @@ llamando llamada en curso coloreado - su rol a cambiado a %s + ha cambiado tu rol a %s cambiando dirección por %s completado ¡No se puede invitar el contacto! @@ -283,7 +283,7 @@ El chat está detenido rol de %s cambiado a %s Cambiar rol - A través del perfil de chat (por defecto) or a través de conexión (BETA) + Mediante perfil de Chat (por defecto) o por conexión (BETA) cambiando dirección… Preferencias de chat cancelado %s @@ -319,7 +319,7 @@ Si confirmas los servidores de mensajería podrán ver tu IP, y tu proveedor de acceso a internet a qué servidores te estás conectando. Imagen guardada en la Galería El archivo se recibirá cuando tu contacto esté en línea, por favor espera o compruébalo más tarde. - Enlace único de invitación + Enlace de invitación de un uso Pegar enlace recibido Error guardando perfil de grupo Salir sin guardar @@ -329,7 +329,7 @@ La invitación al grupo ya no es válida, ha sido eliminada por el remitente. El grupo se eliminará para tí. ¡No puede deshacerse! Cómo usar el marcador - Incógnito mediante enlace único + Incógnito mediante enlace de un uso Dirección de contacto SimpleX Error guardando servidores SMP Abrir el enlace en el navegador puede reducir la privacidad y seguridad de la conexión. Los enlaces SimpleX que no son de confianza aparecerán en rojo. @@ -337,7 +337,7 @@ Error creando dirección Error eliminando perfil de usuario Activar SimpleX Lock - Enlace único de invitación + Enlace de invitación de un uso Servidores SMP Características experimentales Error importando la base de datos @@ -449,7 +449,7 @@ Instalar SimpleX Chat para terminal invitación al grupo %1$s invitado %1$s - Permite tener muchas conexiones anónimas sin datos compartidos entre estas en un único perfil de chat. + Permite tener varias conexiones anónimas sin datos compartidos entre estas en un único perfil de chat. Invitar al grupo Para comprobar el cifrado de extremo a extremo con su contacto compare (o escanee) el código en sus dispositivos. La base de datos no está cifrada. Establece una contraseña para protegerla. @@ -698,7 +698,7 @@ Evaluación de la seguridad la recepción de archivos aún no está disponible el envío de archivos aún no está disponible - entre particulares + p2p Llamada rechazada Eliminar ¿Eliminar contraseña de Keystore\? @@ -801,8 +801,8 @@ Este grupo ya no existe. Para encontrar el perfil usado en una conexión en modo incógnito, pulsa el nombre del contacto o del grupo en la parte superior del chat. Establecer 1 día - Gracias a los usuarios: ¡contribuye a través de Weblate! - Gracias a los usuarios: ¡contribuye a través de Weblate! + Agradecimientos a los usuarios. ¡Contribuye a través de Weblate! + Agradecimientos a los usuarios. ¡Contribuye a través de Weblate! Para proteger la zona horaria, los archivos de imagen/voz usan la hora UTC. Aislamiento de transporte (para compartir con tu contacto) @@ -855,7 +855,7 @@ Usar servidor Usar para conexiones nuevas Sistema - mediante enlace único + mediante enlace de un uso Tus chats Mensaje de voz… Tu dirección de contacto @@ -879,7 +879,7 @@ Tus servidores ICE Has rechazado la invitación del grupo. has cambiado el rol de %s a %s - mediante servidor de retransmisión + mediante relay ¡quiere contactar contigo! Mensaje de voz Esperando imagen @@ -939,9 +939,8 @@ Base de datos de Chat Puedes iniciar el chat en Configuración / Base de datos o reiniciando la aplicación. Has enviado una invitación de grupo - %1$s contacto(s) seleccionado(s) + %d contacto(s) seleccionado(s) %1$s MIEMBROS - Tus perfiles de chat se almacenan localmente, sólo en tu dispositivo Los mensajes de voz están prohibidos en este chat. Novedades La contraseña no se almacena en el dispositivo, tienes que introducirla cada vez que inicies la aplicación. @@ -955,8 +954,8 @@ Estás conectado al servidor utilizado para recibir mensajes de este contacto. mediante enlace de dirección de contacto mediante enlace de grupo - has compartido un enlace único - has compartido un enlace único en módo incógnito + has compartido enlace de un uso + has compartido enlace de un uso en módo incógnito No tienes chats El contacto ha enviado un archivo mayor al máximo admitido (%1$s ). %1$d mensaje(s) omitido(s) @@ -979,4 +978,48 @@ \n \nLos servidores SimpleX no pueden ver tu perfil. Sistema + Agregar mensaje de bienvenida + Llamadas y videollamadas + ¿Guardar servidores\? + Ocultar perfil + Guardar contraseña de perfil + Contraseña para hacerlo visible + Error guardando la contraseña de usuario + El relay sólo se usa en caso de necesidad. Un tercero podría ver tu IP. + El servidor relay protege tu IP pero puede ver la duración de la llamada. + ¡No se puede eliminar el perfil! + Introduce la contraseña + Ocultar + Silenciar + Guardar y actualizar perfil del grupo + Pulsa para activar el perfil. + Debe haber al menos un perfil de usuario visible. + Mostrar + Mensaje de bienvenida + Mensaje de bienvenida + Debe haber al menos un perfil de usuario. + ¡Hacer un perfil privado! + No mostrar de nuevo + ¡Silenciado cuando está inactivo! + Moderación de grupos + Perfiles Chat ocultos + ¡Proteje los perfiles de Chat con contraseña! + Soporte bluetooth y otras mejoras. + ¡Establece el mensaje mostrado a los miembros nuevos! + Interfaz en chino y español + Agradecimientos a los usuarios. ¡Contribuye a través de Weblate! + Error actualizando la privacidad de usuario + Confirmar contraseña + Consumo de batería reducido aun más + Mensaje de bienvenida en grupos + ¡Más mejoras en camino! + Contraseña de perfil oculto + ¿Guardar mensaje de bienvenida\? + Activar audio + Puedes ocultar o silenciar un perfil. Mantenlo pulsado para abrir el menú. + Seguirás recibiendo llamadas y notificaciones de los perfiles silenciados cuando estén activos. + Ahora los administradores pueden +\n- borrar mensajes de los miembros. +\n- desactivar el rol a miembros (a rol \"observador\") + Para hacer visible tu perfil oculto, introduce la contraseña completa en el campo de búsqueda de la página Tus perfiles Chat. \ No newline at end of file diff --git a/apps/android/app/src/main/res/values-fi/strings.xml b/apps/android/app/src/main/res/values-fi/strings.xml new file mode 100644 index 0000000000..a6b3daec93 --- /dev/null +++ b/apps/android/app/src/main/res/values-fi/strings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/apps/android/app/src/main/res/values-fr/strings.xml b/apps/android/app/src/main/res/values-fr/strings.xml index b0efa0df94..1e63aabd4d 100644 --- a/apps/android/app/src/main/res/values-fr/strings.xml +++ b/apps/android/app/src/main/res/values-fr/strings.xml @@ -744,7 +744,7 @@ Le rôle sera changé pour «%s». Les membres du groupe seront notifiés. Contact vérifié⸱e Effacer - %1$s contact·s sélectionné·e·s + %d contact·s sélectionné·e·s Passer l’invitation de membres Sélectionnez des contacts Aucun contact sélectionné @@ -942,7 +942,6 @@ Profil et connexions au serveur Isolement du transport Mettre à jour le mode d\'isolation du transport \? - Vos profils de chat sont stockés localement, uniquement sur votre appareil Une connexion TCP distincte (et identifiant SOCKS) sera utilisée pour chaque contact et membre de groupe. \nVeuillez noter : si vous avez de nombreuses connexions, votre consommation de batterie et de réseau peut être nettement plus élevée et certaines liaisons peuvent échouer. Profil de chat @@ -981,4 +980,46 @@ Vous ne pouvez pas envoyer de messages ! observateur Système + Sauvegarder les serveurs \? + Ne plus afficher + Ajouter un message d\'accueil + Impossible de supprimer le profil d\'utilisateur ! + Modération de groupe + Cacher + Mute en cas d\'inactivité ! + Confirmer le mot de passe + Réduction accrue de l\'utilisation de la batterie + Interface en chinois et en espagnol + Entrez le mot de passe ci-dessus pour continuer ! + Appels audio et vidéo + Message d\'accueil du groupe + Erreur d\'enregistrement du mot de passe de l\'utilisateur + Erreur de mise à jour de la confidentialité de l\'utilisateur + Mot de passe de profil caché + Profils de chat cachés + Désormais, les administrateurs peuvent : +\n- supprimer les messages des membres. +\n- désactiver des membres (rôle \"observateur\") + Sauvegarder le message d\'accueil \? + Choisissez un message à l\'attention des nouveaux membres ! + Masquer le profil + D\'autres améliorations sont à venir ! + Mot de passe à afficher + Rendre le profil privé ! + Mute + Protégez vos profils de chat par un mot de passe ! + Appuyez pour activer le profil. + Sauvegarder et mettre à jour le profil du groupe + Enregistrer le mot de passe du profil + Pour révéler votre profil caché, entrez un mot de passe complet dans le champ de recherche de la page Profils de chat. + Prise en charge du Bluetooth et autres améliorations. + Merci aux utilisateurs - contribuez via Weblate ! + Il doit y avoir au moins un profil d\'utilisateur. + Il doit y avoir au moins un profil d\'utilisateur visible. + Dévoiler + Démute + Message d\'accueil + Message d\'accueil + Vous pouvez masquer ou mettre en sourdine un profil d\'utilisateur - maintenez-le enfoncé pour accéder au menu. + Vous continuerez à recevoir des appels et des notifications des profils mis en sourdine lorsqu\'ils sont actifs. \ No newline at end of file diff --git a/apps/android/app/src/main/res/values-it/strings.xml b/apps/android/app/src/main/res/values-it/strings.xml index 0d980a3c29..1579452039 100644 --- a/apps/android/app/src/main/res/values-it/strings.xml +++ b/apps/android/app/src/main/res/values-it/strings.xml @@ -819,7 +819,7 @@ Invia messaggio diretto Salta l\'invito di membri Cambia - %1$s contatto/i selezionato/i + %d contatto/i selezionato/i %1$s MEMBRI Puoi condividere un link o un codice QR: chiunque potrà unirsi al gruppo. Non perderai i membri del gruppo se in seguito lo elimini. Stai tentando di invitare un contatto con cui hai condiviso un profilo in incognito nel gruppo in cui stai usando il tuo profilo principale @@ -948,7 +948,6 @@ Elimina i file per tutti i profili di chat Errore nel cambio di profilo! Errore nella creazione del profilo! - I tuoi profili di chat sono memorizzati localmente, solo sul tuo dispositivo Errore nell\'eliminazione del profilo utente Profilo e connessioni al server I tuoi profili di chat @@ -981,4 +980,46 @@ Contatta l\'amministratore del gruppo. Non puoi inviare messaggi! Sistema + Aggiungi messaggio di benvenuto + Messaggio di benvenuto + Salvare il messaggio di benvenuto\? + Messaggio di benvenuto + Salva e aggiorna il profilo del gruppo + Inserisci la password sopra per mostrare! + Silenzia + Tocca per attivare il profilo. + Svela + Rendi privato il profilo! + Deve esserci almeno un profilo utente. + Deve esserci almeno un profilo utente visibile. + Puoi nascondere o silenziare un profilo utente - tienilo premuto per il menu. + Non mostrare più + Silenzioso quando inattivo! + Continuerai a ricevere chiamate e notifiche da profili silenziati quando sono attivi. + Chiamate audio e video + Moderazione del gruppo + Proteggi i tuoi profili di chat con una password! + Supporto a bluetooth e altri miglioramenti. + Messaggio di benvenuto del gruppo + Altri miglioramenti sono in arrivo! + Interfaccia cinese e spagnola + Grazie agli utenti – contribuite via Weblate! + Password del profilo nascosta + Salva la password del profilo + Per rivelare il tuo profilo nascosto, inserisci una password completa in un campo di ricerca nella pagina \"I tuoi profili di chat\". + Password per mostrare + Ora gli amministratori possono: +\n- eliminare i messaggi dei membri. +\n- disattivare i membri (ruolo \"osservatore\") + Impossibile eliminare il profilo utente! + Nascondi il profilo + Conferma password + Errore nell\'aggiornamento della privacy dell\'utente + Salvare i server\? + Errore nel salvataggio della password utente + Ulteriore riduzione del consumo della batteria + Profili di chat nascosti + Nascondi + Imposta il messaggio mostrato ai nuovi membri! + Riattiva audio \ No newline at end of file diff --git a/apps/android/app/src/main/res/values-ja/strings.xml b/apps/android/app/src/main/res/values-ja/strings.xml index 0530ae4ec6..fa9a28cdab 100644 --- a/apps/android/app/src/main/res/values-ja/strings.xml +++ b/apps/android/app/src/main/res/values-ja/strings.xml @@ -802,7 +802,7 @@ グループの招待を送りました %sの役割を次に変えました:%s ダイレクトメッセージを送信 - %1$s連絡先が選択中 + %d 連絡先が選択中 除名しました: %1$s 切り替える 役割が「%s」となります。グループの全員に通知が出ます。 @@ -812,7 +812,6 @@ 保存 ネットワーク設定を更新しますか? 設定を更新すると、全サーバにクライントの再接続が行われます。 - チャットプロフィールはローカルであなたの端末だけに保存されます。 色を保存 あなたが次を許可しています: はい diff --git a/apps/android/app/src/main/res/values-lt/strings.xml b/apps/android/app/src/main/res/values-lt/strings.xml index 40df1e30c2..710bc8853c 100644 --- a/apps/android/app/src/main/res/values-lt/strings.xml +++ b/apps/android/app/src/main/res/values-lt/strings.xml @@ -8,4 +8,43 @@ Apie SimpleX Pridėti serverį… Pridėti serverius skenuojant QR kodus. + Išvaizda + Programėlės versija + Programėlės versija: v%s + Programėlės darinys: %s + Automatiškai + skambinama… + skambučio klaida + Skambutis jau baigtas! + Atsiliepti + Skambutis baigtas + SKAMBUČIAI + Leisti jūsų adresatams negrįžtamai ištrinti išsiųstas žinutes. + Atgal + PROGRAMĖLĖS PIKTOGRAMA + Adresatui, iš kurio gavote šią nuorodą, bus išsiųstas atsitiktinis profilis + visada + Leisti jūsų adresatams siųsti balso žinutes. + Leisti negrįžtamą žinučių ištrynimą tik tuo atveju, jei jūsų adresatas jums tai leidžia. + Leisti balso žinutes tik tuo atveju, jei jūsų adresatas jas leidžia. + Leisti + Leisti balso žinutes\? + pusjuodis + skambutis baigtas %1$s + garso skambutis + Garso ir vaizdo skambučiai + bloga žinutės maiša + blogas žinutės ID + Jūsų adresatui bus išsiųstas atsitiktinis profilis + Leisti išnykstančias žinutes tik tuo atveju, jei jūsų adresatas jas leidžia. + Visos žinutės bus ištrintos – to neįmanoma bus atšaukti! Žinutės bus ištrintos TIK jums. + Leisti negrįžtamai ištrinti išsiųstas žinutes. + Leisti siųsti išnykstančias žinutes. + Leisti siųsti balso žinutes. + Leisti siųsti tiesiogines žinutes nariams. + Garso ir vaizdo skambučiai + Leisti jūsų adresatams siųsti išnykstančias žinutes. + Tapatybės nustatymas neprieinamas + Turėkite omenyje: jeigu prarasite slaptafrazę, NEBEGALĖSITE jos atkurti ar pakeisti. + Atsisakyti \ No newline at end of file diff --git a/apps/android/app/src/main/res/values-nl/strings.xml b/apps/android/app/src/main/res/values-nl/strings.xml index 455f75671a..deeb72e2a4 100644 --- a/apps/android/app/src/main/res/values-nl/strings.xml +++ b/apps/android/app/src/main/res/values-nl/strings.xml @@ -74,7 +74,7 @@ 1 maand Over SimpleX Over SimpleX Chat - hierboven, dan: + hier boven, dan: Verzoeken accepteren Alle gesprekken en berichten worden verwijderd, dit kan niet ongedaan worden gemaakt! Alle berichten worden verwijderd, dit kan niet ongedaan worden gemaakt! De berichten worden ALLEEN voor jou verwijderd. @@ -491,7 +491,7 @@ \nWe zullen serverredundantie toevoegen om verloren berichten te voorkomen. Deel nemen aan groep Verlaten - gebruiker + Gebruiker link voorbeeld afbeelding GEBRUIKER BERICHTEN @@ -576,7 +576,7 @@ voorgesteld %s: %2s Oud database archief Voer het juiste huidige wachtwoord in. - eigenaar + Eigenaar PING telling PING interval Bewaar het laatste berichtconcept, met bijlagen. @@ -729,7 +729,7 @@ SOCKS-proxy gebruiken (poort 9050) Voorkeuren opslaan\? Opslaan en Contact melden - WELKOMS BERICHT + WELKOMST BERICHT Je huidige profiel Opslaan en Contacten melden Opslaan en Groepsleden melden @@ -815,7 +815,7 @@ je bent van adres veranderd Selecteer contacten Sla het uitnodigen van leden over - %1$s contact(en) geselecteerd + %d contact(en) geselecteerd Verwijderen Gebruiker verwijderen Rol @@ -883,7 +883,7 @@ Verbied het sturen van directe berichten naar leden. Verbieden het verzenden van spraak berichten. De beveiliging van SimpleX Chat is gecontroleerd door Trail of Bits. - Met optioneel welkomstbericht. + Met optioneel welkomst bericht. Spraak berichten Uw contacten kunnen volledige verwijdering van berichten toestaan. U moet elke keer dat de app start het wachtwoord invoeren, deze wordt niet op het apparaat opgeslagen. @@ -898,7 +898,6 @@ Netwerk instellingen bijwerken\? Door de instellingen bij te werken, wordt de client opnieuw verbonden met alle servers. Update - Uw chat profielen worden lokaal opgeslagen, alleen op uw apparaat Je willekeurige profiel Verbied het verzenden van verdwijnende berichten. Verbieden het verzenden van spraak berichten. @@ -976,8 +975,50 @@ Neem contact op met de groep beheerder. Fout bij bijwerken van groep link Initiële rol - waarnemer + Waarnemer Je kunt geen berichten versturen! jij bent waarnemer Systeem + Audio en video oproepen + Kan gebruikers profiel niet verwijderen! + Bevestig wachtwoord + Chinese en Spaanse interface + Voer wachtwoord in bij zoeken + Fout bij opslaan gebruikers wachtwoord + Welkomst bericht toevoegen + Niet meer weergeven + Groep moderatie + Fout bij updaten van gebruikers privacy + Verder verminderd batterij verbruik + Groep welkomst bericht + Verborgen chat profielen + Profiel verbergen + Verbergen + Verborgen profiel wachtwoord + Gedempt wanneer inactief! + Dempen + Meer verbeteringen volgen snel! + Profiel privé maken! + Nu kunnen beheerders: +\n- berichten van leden verwijderen. +\n- schakel leden uit (\"waarnemer\" rol) + Bescherm je chat profielen met een wachtwoord! + Wachtwoord om weer te geven + Groep profiel opslaan en bijwerken + Servers opslaan\? + Bewaar profiel wachtwoord + Stel het getoonde bericht in voor nieuwe leden! + Welkomst bericht opslaan\? + Ondersteuning voor bluetooth en andere verbeteringen. + Tik om profiel te activeren. + Dank aan de gebruikers – draag bij via Weblate! + Er moet ten minste één gebruikers profiel zijn. + U kunt een gebruikers profiel verbergen of dempen - houd het vast voor het menu. + zichtbaar maken + Dempen opheffen + Welkomst bericht + "Er moet ten minste één zichtbaar gebruikers profiel zijn." + Om uw verborgen profiel te onthullen, voert u een volledig wachtwoord in een zoekveld in op de pagina Uw chat profielen. + Welkomst bericht + U ontvangt nog steeds oproepen en meldingen van gedempte profielen wanneer deze actief zijn. \ No newline at end of file diff --git a/apps/android/app/src/main/res/values-ru/strings.xml b/apps/android/app/src/main/res/values-ru/strings.xml index c61e70b493..1bf59af0dd 100644 --- a/apps/android/app/src/main/res/values-ru/strings.xml +++ b/apps/android/app/src/main/res/values-ru/strings.xml @@ -767,7 +767,7 @@ Выберите контакты Контакт выбран Очистить - Выбрано контактов: %1$s + Выбрано контактов: %d Контакты не выбраны Нельзя пригласить контакт! Вы пытаетесь пригласить инкогнито контакт в группу, где вы используете свой основной профиль @@ -1007,7 +1007,6 @@ Только локальные данные профиля Сообщения Серверы для новых соединений вашего текущего профиля чата - Ваши профили чата хранятся локально, только на вашем устройстве Ваши профили чата Все чаты и сообщения будут удалены - это нельзя отменить! Сборка приложения: %s @@ -1052,4 +1051,47 @@ читатель Роль при вступлении Ошибка обновления ссылки группы + Системный + Аудио и видео звонки + Ошибка при сохранении пароля пользователя + Сохранить серверы\? + Должен быть хотя бы один профиль пользователя. + Должен быть хотя бы один открытый профиль пользователя. + Чтобы показать Ваш скрытый профиль, введите пароль в поле поиска на странице Ваши профили чата. + Уведомлять + Приветственное сообщение + Подтвердить пароль + Добавить приветственное сообщение + Приветственное сообщение + Сохранить сообщение и обновить группу + Без звука, когда не активный! + Нельзя удалить профиль пользователя! + Введите пароль в поиске! + Сохранить пароль профиля + Китайский и Испанский интерфейс + Не показывать + Скрыть + Уменьшенное потребление батареи + Модерация группы + Пароль скрытого профиля + Пароль чтобы раскрыть + Скрытые профили чата + Ошибка при обновлении конфиденциальности + Приветственное сообщение группы + Скрыть профиль + Без звука + Сделайте профиль конфиденциальным! + Дополнительные улучшения скоро! + Теперь админы могут: +\n- удалять сообщения членов. +\n- приостанавливать членов (роль \"наблюдатель\") + Защитите ваши профили чата паролем! + Раскрыть + Поддержка bluetooth и другие улучшения. + Сохранить приветственное сообщение\? + Установить сообщение для новых членов группы! + Нажмите, чтобы сделать профиль активным. + Благодаря пользователям – добавьте переводы через Weblate! + Вы все равно получите звонки и уведомления в профилях без звука, когда они активные. + Вы можете скрыть профиль или выключить уведомления - подержите, чтобы увидеть меню. \ No newline at end of file diff --git a/apps/android/app/src/main/res/values-zh-rCN/strings.xml b/apps/android/app/src/main/res/values-zh-rCN/strings.xml index 87f833b99d..d771408eb8 100644 --- a/apps/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/apps/android/app/src/main/res/values-zh-rCN/strings.xml @@ -112,7 +112,7 @@ 错误消息散列 错误消息 ID 语音和视频通话 - 自动地 + 自动 激活电池优化,关闭了后台服务和新消息的定期请求。您可以通过设置重新启用它们。 后台服务一直在运行——一旦有消息,就会显示通知。 关闭音频 @@ -132,7 +132,7 @@ 可以通过设置禁用它 - 应用程序运行时仍会显示通知。 使用更多电池 !后台服务一直在运行——一旦收到消息,就会显示通知。 请注意:如果您丢失密码,您将无法恢复或者更改密码。 - 通话已经结束! + 通话已结束! 扫描二维码 :与向您展示二维码的联系人联系。 无法邀请联系人! 无法邀请联系人! @@ -234,7 +234,7 @@ 清除聊天记录? 与开发者聊天 清除 - 有色 + 彩色 已连接 连接 连接 @@ -266,7 +266,7 @@ 删除待定的联系人连接错误 接收文件错误 切换资料错误! - 已隐藏 + 隐藏 编辑 隐藏联系人和消息 已编辑 @@ -510,7 +510,7 @@ 只有客户端设备存储用户配置文件、联系人、群组和使用 双层端到端加密 发送的消息。 视频通话(非端到端加密) 定期 - 私人通知 + 私密通知 应用程序运行时 无端到端加密 显示 @@ -592,7 +592,6 @@ 您的聊天资料 未接来电 待定来电 - 您的聊天资料存储在本地,仅存储在您的设备上 除非您的联系人已删除此连接或此链接已被使用,否则它可能是一个错误——请报告。 \n如果要连接,请让您的联系人创建另一个连接链接,并检查您的网络连接是否稳定。 您已经连接到 %1$s! @@ -628,7 +627,7 @@ \nSimpleX 服务器无法看见您的资料。 您的资料、联系人和发送的消息存储在您的设备上。 该资料仅与您的联系人共享。 - + 开启 此操作无法撤消——您的个人资料、联系人、消息和文件将不可撤回地丢失。 此设置适用于您当前聊天资料中的消息 恢复数据库错误 @@ -715,7 +714,7 @@ %s 已验证 用于新连接 使用直接互联网连接? - 必要 + 必须 保存并通知联系人 保存并通知联系人 在我们的 GitHub 仓库中阅读更多内容。 @@ -843,7 +842,7 @@ 点击加入 停止 重新启动应用程序以使用导入的聊天数据库。 - 所有者 + 群主 已删除 角色 @@ -956,7 +955,7 @@ 你加入了这个群组。连接到邀请组成员。 您更改了 %s 的地址 您已离开 - %1$d 已选择联系人 + %d 已选择联系人 您允许 带有可选的欢迎消息。 %dm @@ -972,7 +971,7 @@ 由 %s 审核 管理员移除 将为所有成员删除该消息。 - 该消息将对所有成员显示为已审核。 + 该消息将对所有成员标记为已被管理员移除。 删除成员消息? 观察者 您是观察者 @@ -981,4 +980,46 @@ 初始角色 请联系群组管理员。 系统 + 用于显示的密码 + 保存个人资料密码 + 添加欢迎消息 + 隐藏 + 将个人资料设置为私密! + 静音 + 保存并更新组配置文件 + 不再显示 + 不活跃时静音! + 语音和视频通话 + 中文和西班牙文界面 + 进一步减少电池使用 + 更多改进即将推出! + 现在管理员可以: +\n- 删除成员的消息。 +\n- 禁用成员(“观察员”角色) + 使用密码保护您的聊天资料! + 确认密码 + 更新用户隐私错误 + 无法删除用户资料! + 保存用户密码错误 + 在搜索中输入密码 + 群组欢迎消息 + 群组管理员移除 + 隐藏的个人资料密码 + 隐藏的聊天资料 + 隐藏个人资料 + 保存服务器? + 要显示您的隐藏的个人资料,请在您的聊天个人资料页面的搜索字段中输入完整密码。 + 保存欢迎信息? + 点击以激活个人资料。 + 应该至少有一个用户资料。 + 解除隐藏 + 设置向新成员显示的消息! + 支持蓝牙和其他改进。 + 感谢用户——通过 Weblate 做出贡献! + 应该至少有一个可见的用户资料。 + 解除静音 + 欢迎信息 + 当静音配置文件处于活动状态时,您仍会收到来自静音配置文件的电话和通知。 + 您可以隐藏或静音用户配置文件——长按以显示菜单。 + 欢迎信息 \ No newline at end of file diff --git a/apps/android/app/src/main/res/values-zh-rTW/strings.xml b/apps/android/app/src/main/res/values-zh-rTW/strings.xml index 3fb0959a6e..0b0908c26f 100644 --- a/apps/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/apps/android/app/src/main/res/values-zh-rTW/strings.xml @@ -765,7 +765,6 @@ 儲存 更新網路設定? 更新 - 你的個人檔案只會儲存於你的本機裝置內。 更新設定會將客戶端重新連接到所有的伺服器。 刪除個人檔案? 刪除個人檔案 @@ -927,7 +926,7 @@ 你將停止接收來自此群組的訊息。群組內的記錄會保留。 你已拒絕加入群組 連線中(宣布階段) - %1$s 已選擇多個聊絡人 + %d 已選擇多個聊絡人 你的 ICE 伺服器 WebRTC ICE 伺服器 更新 @@ -983,4 +982,48 @@ 請聯繫群管理員。 初始角色 系統 + 您可以隱藏或靜音用戶配置文件 - 按住它以顯示菜單。 + 保存服務器? + 確認密碼 + 隱藏的個人資料密碼 + 隱藏個人資料 + 顯示密碼 + 保存個人資料密碼 + 要顯示您的隱藏個人資料,請在您的聊天個人資料頁面的搜索字段中輸入完整密碼。 + 歡迎信息 + 保存和更新組配置文件 + 保存歡迎信息? + 無法刪除用戶個人資料! + 隱藏 + 將個人資料設為私密! + 音視頻通話 + 中文和西班牙文界面 + 進一步減少電池使用 + 小組審核 + 更多改進即將推出! + 現在管理員可以: +\n- 刪除成員的消息。 +\n- 禁用成員(“觀察員”角色) + 使用密碼保護您的聊天資料! + 中繼服務器保護您的 IP 地址,但它可以觀察通話的持續時間。 + 添加歡迎信息 + 保存用戶密碼時出錯 + 更新用戶隱私時出錯 + 中繼服務器僅在必要時使用。 另一方可以觀察到您的 IP 地址。 + 在上面輸入密碼以顯示! + 群组欢迎信息 + 隱藏的聊天資料 + 不再顯示 + 靜音 + Muted when inactive! + 設置向新成員顯示的消息! + 點擊以激活配置文件。 + 支持藍牙和其他改進。 + 應該至少有一個可見的用戶配置文件。 + 歡迎信息 + 感謝用戶——通過 Weblate 做出貢獻! + 應該至少有一個用戶配置文件。 + 取消靜音 + 當靜音配置文件處於活動狀態時,您仍會收到來自靜音配置文件的電話和通知。 + 取消隱藏 \ No newline at end of file diff --git a/apps/android/app/src/main/res/values/colors.xml b/apps/android/app/src/main/res/values/colors.xml index 1833a6d9a3..e1a994e57f 100644 --- a/apps/android/app/src/main/res/values/colors.xml +++ b/apps/android/app/src/main/res/values/colors.xml @@ -2,5 +2,6 @@ #FF000000 #FFFFFFFF + #8b8786 #121212 \ No newline at end of file diff --git a/apps/android/app/src/main/res/values/strings.xml b/apps/android/app/src/main/res/values/strings.xml index 5eca754ca3..788eccd87f 100644 --- a/apps/android/app/src/main/res/values/strings.xml +++ b/apps/android/app/src/main/res/values/strings.xml @@ -922,7 +922,7 @@ Select contacts Contact checked Clear - %1$s contact(s) selected + %d contact(s) selected No contacts selected Can\'t invite contact! You\'re trying to invite contact with whom you\'ve shared an incognito profile to the group in which you\'re using your main profile @@ -1017,7 +1017,6 @@ Update - Your chat profiles are stored locally, only on your device Add profile Delete chat profile? All chats and messages will be deleted - this cannot be undone! @@ -1028,13 +1027,13 @@ Unhide Mute Unmute - Enter password above to show! + Enter password in search Tap to activate profile. Can\'t delete user profile! There should be at least one visible user profile. There should be at least one user profile. Make profile private! - You can hide or mute a user profile - hold it for the menu.\nSimpleX Lock must be enabled. + You can hide or mute a user profile - hold it for the menu. Don\'t show again Muted when inactive! You will still receive calls and notifications from muted profiles when they are active. diff --git a/apps/android/build.gradle b/apps/android/build.gradle index e7be963f32..08dbb3f98d 100644 --- a/apps/android/build.gradle +++ b/apps/android/build.gradle @@ -8,6 +8,7 @@ buildscript { compose_version = localProperties['compose_version'] ?: '1.2.0-beta02' kotlin_version = localProperties['kotlin_version'] ?: '1.6.21' gradle_plugin_version = localProperties['gradle_plugin_version'] ?: '7.2.0' + abi_filter = localProperties['abi_filter'] ?: 'arm64-v8a' // Name that will be shown for debug build. By default it is from strings app_name = localProperties['app_name'] ?: "@string/app_name" diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 7c22e69e18..26e71dbf6c 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -984,7 +984,7 @@ func apiGetGroupLink(_ groupId: Int64) throws -> (String, GroupMemberRole)? { func apiGetVersion() throws -> CoreVersionInfo { let r = chatSendCmdSync(.showVersion) - if case let .versionInfo(info) = r { return info } + if case let .versionInfo(info, _, _) = r { return info } throw r } @@ -995,10 +995,10 @@ private func currentUserId(_ funcName: String) throws -> Int64 { throw RuntimeError("\(funcName): no current user") } -func initializeChat(start: Bool, dbKey: String? = nil, refreshInvitations: Bool = true) throws { +func initializeChat(start: Bool, dbKey: String? = nil, refreshInvitations: Bool = true, confirmMigrations: MigrationConfirmation? = nil) throws { logger.debug("initializeChat") let m = ChatModel.shared - (m.chatDbEncrypted, m.chatDbStatus) = chatMigrateInit(dbKey) + (m.chatDbEncrypted, m.chatDbStatus) = chatMigrateInit(dbKey, confirmMigrations: confirmMigrations) if m.chatDbStatus != .ok { return } // If we migrated successfully means previous re-encryption process on database level finished successfully too if encryptionStartedDefault.get() { diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index 0b2a3f1a9e..78ed48ed25 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -71,7 +71,7 @@ struct ChatListView: View { .toolbar { ToolbarItem(placement: .navigationBarLeading) { Button { - if chatModel.users.count > 1 { + if chatModel.users.filter { u in u.user.activeUser || !u.user.hidden }.count > 1 { withAnimation { userPickerVisible.toggle() } diff --git a/apps/ios/Shared/Views/Database/DatabaseErrorView.swift b/apps/ios/Shared/Views/Database/DatabaseErrorView.swift index 4830c81729..cd18b244c2 100644 --- a/apps/ios/Shared/Views/Database/DatabaseErrorView.swift +++ b/apps/ios/Shared/Views/Database/DatabaseErrorView.swift @@ -16,40 +16,68 @@ struct DatabaseErrorView: View { @State private var storedDBKey = getDatabaseKey() @State private var useKeychain = storeDBPassphraseGroupDefault.get() @State private var showRestoreDbButton = false + @State private var starting = false var body: some View { + ZStack { + databaseErrorView().disabled(starting) + if starting { + ProgressView().scaleEffect(2) + } + } + } + + @ViewBuilder private func databaseErrorView() -> some View { VStack(alignment: .leading, spacing: 16) { switch status { case let .errorNotADatabase(dbFile): if useKeychain && storedDBKey != nil && storedDBKey != "" { - Text("Wrong database passphrase").font(.title) + titleText("Wrong database passphrase") Text("Database passphrase is different from saved in the keychain.") databaseKeyField(onSubmit: saveAndRunChat) saveAndOpenButton() - Text("File: \(dbFile)") + fileNameText(dbFile) } else { - Text("Encrypted database").font(.title) + titleText("Encrypted database") Text("Database passphrase is required to open chat.") if useKeychain { databaseKeyField(onSubmit: saveAndRunChat) saveAndOpenButton() } else { - databaseKeyField(onSubmit: runChat) + databaseKeyField(onSubmit: { runChat() }) openChatButton() } } - case let .error(dbFile, migrationError): - Text("Database error") - .font(.title) - Text("File: \(dbFile)") - Text("Error: \(migrationError)") + case let .errorMigration(dbFile, migrationError): + switch migrationError { + case let .upgrade(upMigrations): + titleText("Database upgrade") + Button("Upgrade and open chat") { runChat(confirmMigrations: .yesUp) } + fileNameText(dbFile) + migrationsText(upMigrations.map(\.upName)) + case let .downgrade(downMigrations): + titleText("Database downgrade") + Text("Warning: you may lose some data!").bold() + Button("Downgrade and open chat") { runChat(confirmMigrations: .yesUpDown) } + fileNameText(dbFile) + migrationsText(downMigrations) + case let .migrationError(mtrError): + titleText("Incompatible database version") + fileNameText(dbFile) + Text("Error: ") + Text(mtrErrorDescription(mtrError)) + } + case let .errorSQL(dbFile, migrationSQLError): + titleText("Database error") + fileNameText(dbFile) + Text("Error: \(migrationSQLError)") case .errorKeychain: - Text("Keychain error") - .font(.title) + titleText("Keychain error") Text("Cannot access keychain to save database password") + case .invalidConfirmation: + // this can only happen if incorrect parameter is passed + Text(String("Invalid migration confirmation")).font(.title) case let .unknown(json): - Text("Database error") - .font(.title) + titleText("Database error") Text("Unknown database error: \(json)") case .ok: EmptyView() @@ -61,10 +89,31 @@ struct DatabaseErrorView: View { } } .padding() - .frame(maxHeight: .infinity, alignment: .topLeading) + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) .onAppear() { showRestoreDbButton = shouldShowRestoreDbButton() } } + private func titleText(_ s: LocalizedStringKey) -> Text { + Text(s).font(.title) + } + + private func fileNameText(_ f: String) -> Text { + Text("File: \((f as NSString).lastPathComponent)") + } + + private func migrationsText(_ ms: [String]) -> Text { + Text("Migrations: \(ms.joined(separator: ", "))") + } + + private func mtrErrorDescription(_ err: MTRError) -> LocalizedStringKey { + switch err { + case let .noDown(dbMigrations): + return "database version is newer than the app, but no down migration for: \(dbMigrations.joined(separator: ", "))" + case let .different(appMigration, dbMigration): + return "different migration in the app/database: \(appMigration) / \(dbMigration)" + } + } + private func databaseKeyField(onSubmit: @escaping () -> Void) -> some View { PassphraseField(key: $dbKey, placeholder: "Enter passphrase…", valid: validKey(dbKey), onSubmit: onSubmit) } @@ -89,14 +138,24 @@ struct DatabaseErrorView: View { runChat() } - private func runChat() { + private func runChat(confirmMigrations: MigrationConfirmation? = nil) { + starting = true + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + runChatSync(confirmMigrations: confirmMigrations) + starting = false + } + } + + private func runChatSync(confirmMigrations: MigrationConfirmation? = nil) { do { resetChatCtrl() - try initializeChat(start: m.v3DBMigration.startChat, dbKey: dbKey) + try initializeChat(start: m.v3DBMigration.startChat, dbKey: useKeychain ? nil : dbKey, confirmMigrations: confirmMigrations) if let s = m.chatDbStatus { status = s let am = AlertManager.shared switch s { + case .invalidConfirmation: + am.showAlert(Alert(title: Text(String("Invalid migration confirmation")))) case .errorNotADatabase: am.showAlertMsg( title: "Wrong passphrase!", @@ -104,7 +163,7 @@ struct DatabaseErrorView: View { ) case .errorKeychain: am.showAlertMsg(title: "Keychain error") - case let .error(_, error): + case let .errorSQL(_, error): am.showAlert(Alert( title: Text("Database error"), message: Text(error) @@ -114,6 +173,7 @@ struct DatabaseErrorView: View { title: Text("Unknown error"), message: Text(error) )) + case .errorMigration: () case .ok: () } } diff --git a/apps/ios/Shared/Views/UserSettings/CallSettings.swift b/apps/ios/Shared/Views/UserSettings/CallSettings.swift index ca43faab03..43c715523e 100644 --- a/apps/ios/Shared/Views/UserSettings/CallSettings.swift +++ b/apps/ios/Shared/Views/UserSettings/CallSettings.swift @@ -11,7 +11,7 @@ import SimpleXChat struct CallSettings: View { @AppStorage(DEFAULT_WEBRTC_POLICY_RELAY) private var webrtcPolicyRelay = true - @AppStorage(GROUP_DEFAULT_CALL_KIT_ENABLED, store: UserDefaults(suiteName: APP_GROUP_NAME)!) private var callKitEnabled = true + @AppStorage(GROUP_DEFAULT_CALL_KIT_ENABLED, store: groupDefaults) private var callKitEnabled = true @AppStorage(DEFAULT_CALL_KIT_CALLS_IN_RECENTS) private var callKitCallsInRecents = false @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false private let allowChangingCallsHistory = false diff --git a/apps/ios/Shared/Views/UserSettings/DeveloperView.swift b/apps/ios/Shared/Views/UserSettings/DeveloperView.swift new file mode 100644 index 0000000000..0d7435d909 --- /dev/null +++ b/apps/ios/Shared/Views/UserSettings/DeveloperView.swift @@ -0,0 +1,50 @@ +// +// DeveloperView.swift +// SimpleX (iOS) +// +// Created by Evgeny on 26/03/2023. +// Copyright © 2023 SimpleX Chat. All rights reserved. +// + +import SwiftUI +import SimpleXChat + +struct DeveloperView: View { + @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false + @AppStorage(GROUP_DEFAULT_CONFIRM_DB_UPGRADES, store: groupDefaults) private var confirmDatabaseUpgrades = false + @Environment(\.colorScheme) var colorScheme + + var body: some View { + VStack { + List { + Section { + NavigationLink { + TerminalView() + } label: { + settingsRow("terminal") { Text("Chat console") } + } + settingsRow("chevron.left.forwardslash.chevron.right") { + Toggle("Show developer options", isOn: $developerTools) + } + settingsRow("internaldrive") { + Toggle("Confirm database upgrades", isOn: $confirmDatabaseUpgrades) + } + ZStack(alignment: .leading) { + Image(colorScheme == .dark ? "github_light" : "github") + .resizable() + .frame(width: 24, height: 24) + .opacity(0.5) + Text("Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)") + .padding(.leading, 36) + } + } + } + } + } +} + +struct DeveloperView_Previews: PreviewProvider { + static var previews: some View { + DeveloperView() + } +} diff --git a/apps/ios/Shared/Views/UserSettings/SettingsView.swift b/apps/ios/Shared/Views/UserSettings/SettingsView.swift index 7d5f0115da..715533481a 100644 --- a/apps/ios/Shared/Views/UserSettings/SettingsView.swift +++ b/apps/ios/Shared/Views/UserSettings/SettingsView.swift @@ -108,7 +108,6 @@ struct SettingsView: View { @EnvironmentObject var chatModel: ChatModel @EnvironmentObject var sceneDelegate: SceneDelegate @Binding var showSettings: Bool - @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false @State private var settingsSheet: SettingsSheet? var body: some View { @@ -259,23 +258,11 @@ struct SettingsView: View { } Section("Develop") { - settingsRow("chevron.left.forwardslash.chevron.right") { - Toggle("Developer tools", isOn: $developerTools) - } - if developerTools { - NavigationLink { - TerminalView() - } label: { - settingsRow("terminal") { Text("Chat console") } - } - ZStack(alignment: .leading) { - Image(colorScheme == .dark ? "github_light" : "github") - .resizable() - .frame(width: 24, height: 24) - .opacity(0.5) - Text("Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)") - .padding(.leading, indent) - } + NavigationLink { + DeveloperView() + .navigationTitle("Developer tools") + } label: { + settingsRow("chevron.left.forwardslash.chevron.right") { Text("Developer tools") } } NavigationLink { ExperimentalFeaturesView() diff --git a/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff b/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff index 364fba900b..ec5910cb3b 100644 --- a/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff +++ b/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff @@ -387,6 +387,7 @@ Add welcome message + Přidat uvítací zprávu No comment provided by engineer. @@ -531,6 +532,7 @@ Audio and video calls + Hlasové a video hovory No comment provided by engineer. @@ -600,6 +602,7 @@ Can't delete user profile! + Nemohu smazat uživatelský profil! No comment provided by engineer. @@ -709,6 +712,7 @@ Chinese and Spanish interface + Čínské a Španělské rozhranní No comment provided by engineer. @@ -768,6 +772,7 @@ Confirm password + Potvrdit heslo No comment provided by engineer. @@ -1300,6 +1305,7 @@ Don't show again + Znovu neukazuj No comment provided by engineer. @@ -1563,6 +1569,7 @@ Error saving user password + Chyba ukládání hesla uživatele No comment provided by engineer. @@ -1602,6 +1609,7 @@ Error updating user privacy + Chyba aktualizace soukromí uživatele No comment provided by engineer. @@ -1695,6 +1703,7 @@ Further reduced battery usage + Další snížení spotřeby baterie No comment provided by engineer. @@ -1774,6 +1783,7 @@ Group moderation + Správa skupin No comment provided by engineer. @@ -1793,6 +1803,7 @@ Group welcome message + Uvítací zpráva skupin No comment provided by engineer. @@ -1817,10 +1828,12 @@ Hidden chat profiles + Skryté chat profily No comment provided by engineer. Hidden profile password + Hesla skrytých profilů No comment provided by engineer. @@ -1835,6 +1848,7 @@ Hide profile + Skrýt profil No comment provided by engineer. @@ -1962,6 +1976,11 @@ Nesprávný bezpečnostní kód! No comment provided by engineer. + + Initial role + Počáteční role + No comment provided by engineer. + Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) Nainstalujte [SimpleX Chat pro terminál](https://github.com/simplex-chat/simplex-chat) @@ -2141,6 +2160,7 @@ Budeme přidávat redundantní servery, abychom zabránili ztrátě zpráv. Make profile private! + Změnit profil na soukromý! No comment provided by engineer. @@ -2270,6 +2290,7 @@ Budeme přidávat redundantní servery, abychom zabránili ztrátě zpráv. Muted when inactive! + Ztlumit při neaktivitě! No comment provided by engineer. @@ -2376,6 +2397,9 @@ Budeme přidávat redundantní servery, abychom zabránili ztrátě zpráv.Now admins can: - delete members' messages. - disable members ("observer" role) + Nyní mohou správci: +- mazat zprávy členů. +- zakázat členy (role "pozorovatel") No comment provided by engineer. @@ -2505,6 +2529,7 @@ Budeme přidávat redundantní servery, abychom zabránili ztrátě zpráv. Password to show + Heslo k zobrazení No comment provided by engineer. @@ -2659,6 +2684,7 @@ Budeme přidávat redundantní servery, abychom zabránili ztrátě zpráv. Protect your chat profiles with a password! + Chraňte své chat profily heslem! No comment provided by engineer. @@ -2858,6 +2884,7 @@ Budeme přidávat redundantní servery, abychom zabránili ztrátě zpráv. Save and update group profile + Uložit a aktualizovat profil skupiny No comment provided by engineer. @@ -2887,6 +2914,7 @@ Budeme přidávat redundantní servery, abychom zabránili ztrátě zpráv. Save profile password + Uložit heslo profilu No comment provided by engineer. @@ -2896,10 +2924,12 @@ Budeme přidávat redundantní servery, abychom zabránili ztrátě zpráv. Save servers? + Uložit servery? No comment provided by engineer. Save welcome message? + Uložit uvítací zprávu? No comment provided by engineer. @@ -3054,6 +3084,7 @@ Budeme přidávat redundantní servery, abychom zabránili ztrátě zpráv. Set the message shown to new members! + Nastavte zprávu zobrazenou novým členům! No comment provided by engineer. @@ -3238,6 +3269,7 @@ Budeme přidávat redundantní servery, abychom zabránili ztrátě zpráv. Tap to activate profile. + Klepnutím aktivujete profil. No comment provided by engineer. @@ -3367,6 +3399,7 @@ Budeme přidávat redundantní servery, abychom zabránili ztrátě zpráv. There should be at least one user profile. + Měl by tam být alespoň jeden uživatelský profil. No comment provided by engineer. diff --git a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff index 90c6260ece..49e903eb02 100644 --- a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff +++ b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff @@ -387,6 +387,7 @@ Add welcome message + Fügen Sie eine Begrüßungsmeldung hinzu No comment provided by engineer. @@ -531,6 +532,7 @@ Audio and video calls + Audio- und Videoanrufe No comment provided by engineer. @@ -600,6 +602,7 @@ Can't delete user profile! + Das Benutzerprofil kann nicht gelöscht werden! No comment provided by engineer. @@ -709,6 +712,7 @@ Chinese and Spanish interface + Chinesische und spanische Bedienoberfläche No comment provided by engineer. @@ -768,6 +772,7 @@ Confirm password + Bestätigen Sie das Passwort No comment provided by engineer. @@ -1300,6 +1305,7 @@ Don't show again + Nicht nochmals anzeigen No comment provided by engineer. @@ -1404,6 +1410,7 @@ Enter password above to show! + Geben Sie oben das Passwort für die Anzeige an! No comment provided by engineer. @@ -1563,6 +1570,7 @@ Error saving user password + Fehler beim Speichern des Benutzer-Passworts No comment provided by engineer. @@ -1602,6 +1610,7 @@ Error updating user privacy + Fehler beim Aktualisieren der Benutzer-Privatsphäre No comment provided by engineer. @@ -1691,10 +1700,12 @@ Fully re-implemented - work in background! + Komplett neu umgesetzt - arbeitet nun im Hintergrund! No comment provided by engineer. Further reduced battery usage + Weiter reduzierter Batterieverbrauch No comment provided by engineer. @@ -1774,6 +1785,7 @@ Group moderation + Gruppenmoderation No comment provided by engineer. @@ -1793,6 +1805,7 @@ Group welcome message + Gruppen-Begrüßungsmeldung No comment provided by engineer. @@ -1817,10 +1830,12 @@ Hidden chat profiles + Verborgene Chat-Profile No comment provided by engineer. Hidden profile password + Verborgenes Profil-Passwort No comment provided by engineer. @@ -1835,6 +1850,7 @@ Hide profile + Verberge das Profil No comment provided by engineer. @@ -1962,6 +1978,11 @@ Falscher Sicherheitscode! No comment provided by engineer. + + Initial role + Anfängliche Rolle + No comment provided by engineer. + Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) Installieren Sie [SimpleX Chat als Terminalanwendung](https://github.com/simplex-chat/simplex-chat) @@ -2141,6 +2162,7 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Make profile private! + Erzeugen Sie ein privates Profil! No comment provided by engineer. @@ -2270,6 +2292,7 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Muted when inactive! + Bei Inaktivität stummgeschaltet! No comment provided by engineer. @@ -2376,6 +2399,9 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Now admins can: - delete members' messages. - disable members ("observer" role) + Administratoren können nun +- Nachrichten von Gruppenmitgliedern löschen +- Gruppenmitglieder deaktivieren ("Beobachter"-Rolle) No comment provided by engineer. @@ -2505,6 +2531,7 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Password to show + Passwort anzeigen No comment provided by engineer. @@ -2659,6 +2686,7 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Protect your chat profiles with a password! + Schützen Sie Ihre Chat-Profile mit einem Passwort! No comment provided by engineer. @@ -2858,6 +2886,7 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Save and update group profile + Sichern und aktualisieren des Gruppen-Profils No comment provided by engineer. @@ -2887,6 +2916,7 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Save profile password + Profil-Passwort speichern No comment provided by engineer. @@ -2896,10 +2926,12 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Save servers? + Alle Server speichern? No comment provided by engineer. Save welcome message? + Begrüßungsmeldung speichern? No comment provided by engineer. @@ -3054,6 +3086,7 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Set the message shown to new members! + Legen Sie die Nachricht fest, die neuen Mitgliedern angezeigt werden soll! No comment provided by engineer. @@ -3238,6 +3271,7 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v Tap to activate profile. + Tippen Sie, um das Profil zu aktivieren. No comment provided by engineer. @@ -3367,10 +3401,12 @@ Wir werden Serverredundanzen hinzufügen, um verloren gegangene Nachrichten zu v There should be at least one user profile. + Es muss mindestens ein Benutzer-Profil vorhanden sein. No comment provided by engineer. There should be at least one visible user profile. + Es muss mindestens ein sichtbares Benutzer-Profil vorhanden sein. No comment provided by engineer. @@ -3442,6 +3478,7 @@ Sie werden aufgefordert, die Authentifizierung abzuschließen, bevor diese Funkt To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page. + Geben Sie ein vollständiges Passwort in das Suchfeld auf der Seite **Meine Chat-Profile** ein, um Ihr verborgenes Profil zu sehen. No comment provided by engineer. @@ -3501,6 +3538,7 @@ Sie werden aufgefordert, die Authentifizierung abzuschließen, bevor diese Funkt Unhide + Verbergen aufheben No comment provided by engineer. @@ -3778,6 +3816,8 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s You can hide or mute a user profile - swipe it to the right. SimpleX Lock must be enabled. + Sie können ein Benutzerprofil verbergen oder stummschalten - wischen Sie es nach rechts. +Dafür muss die SimpleX Sperre aktiviert sein. No comment provided by engineer. @@ -3897,6 +3937,7 @@ SimpleX Lock must be enabled. You will still receive calls and notifications from muted profiles when they are active. + Sie können Anrufe und Benachrichtigungen auch von stummgeschalteten Profilen empfangen, solange diese aktiv sind. No comment provided by engineer. diff --git a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff index 2f3829d108..ba331f0dcc 100644 --- a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff +++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff @@ -1978,6 +1978,11 @@ Incorrect security code! No comment provided by engineer. + + Initial role + Initial role + No comment provided by engineer. + Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) diff --git a/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff b/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff index 7727cecc17..69d24fb923 100644 --- a/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff +++ b/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff @@ -189,7 +189,7 @@ **Add new contact**: to create your one-time QR Code or link for your contact. - **Añadir nuevo contacto**: para crear tu código QR único o un enlace para tu contacto. + **Añadir nuevo contacto**: para crear tu código QR o enlace de un uso para tu contacto. No comment provided by engineer. @@ -387,6 +387,7 @@ Add welcome message + Agregar mensaje de bienvenida No comment provided by engineer. @@ -426,7 +427,7 @@ Allow disappearing messages only if your contact allows it to you. - Permitir mensajes temporales sólo si tu contacto también los permite para tí. + Permitir mensajes temporales sólo si tu contacto también los permite. No comment provided by engineer. @@ -471,7 +472,7 @@ Allow your contacts to send disappearing messages. - Permitir a tus contactos enviar mensajes que desaparecen. + Permitir a tus contactos enviar mensajes temporales. No comment provided by engineer. @@ -486,7 +487,7 @@ Always use relay - Siempre usa relay + Siempre usar relay No comment provided by engineer. @@ -531,6 +532,7 @@ Audio and video calls + Llamadas y videollamadas No comment provided by engineer. @@ -585,7 +587,7 @@ By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). - Por perfil de Chat (por defecto) o [por conexión](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). + Mediante perfil de Chat (por defecto) o [por conexión](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). No comment provided by engineer. @@ -600,6 +602,7 @@ Can't delete user profile! + ¡No se puede eliminar el perfil! No comment provided by engineer. @@ -709,6 +712,7 @@ Chinese and Spanish interface + Interfaz en chino y español No comment provided by engineer. @@ -768,6 +772,7 @@ Confirm password + Confirmar contraseña No comment provided by engineer. @@ -797,7 +802,7 @@ Connect via one-time link? - ¿Conectar mediante enlace único? + ¿Conectar mediante enlace de un uso? No comment provided by engineer. @@ -927,7 +932,7 @@ Create one-time invitation link - Crear enlace único de invitación + Crear enlace de invitación de un uso No comment provided by engineer. @@ -1300,6 +1305,7 @@ Don't show again + No mostrar de nuevo No comment provided by engineer. @@ -1404,6 +1410,7 @@ Enter password above to show! + ¡Introduce la contraseña arriba para mostrar! No comment provided by engineer. @@ -1563,6 +1570,7 @@ Error saving user password + Error guardando la contraseña de usuario No comment provided by engineer. @@ -1602,6 +1610,7 @@ Error updating user privacy + Error actualizando la privacidad de usuario No comment provided by engineer. @@ -1691,10 +1700,12 @@ Fully re-implemented - work in background! + Completamente reimplementado: ¡funciona en segundo plano! No comment provided by engineer. Further reduced battery usage + Consumo de batería reducido aun más No comment provided by engineer. @@ -1774,6 +1785,7 @@ Group moderation + Moderación de grupos No comment provided by engineer. @@ -1793,6 +1805,7 @@ Group welcome message + Mensaje de bienvenida en grupos No comment provided by engineer. @@ -1817,10 +1830,12 @@ Hidden chat profiles + Perfiles Chat ocultos No comment provided by engineer. Hidden profile password + Contraseña de perfil oculto No comment provided by engineer. @@ -1835,6 +1850,7 @@ Hide profile + Ocultar perfil No comment provided by engineer. @@ -1962,6 +1978,11 @@ ¡Código de seguridad incorrecto! No comment provided by engineer. + + Initial role + Rol inicial + No comment provided by engineer. + Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) Instalar [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) @@ -2026,7 +2047,7 @@ It allows having many anonymous connections without any shared data between them in a single chat profile. - Permite tener muchas conexiones anónimas sin datos compartidos entre estas en un único perfil de chat. + Permite tener varias conexiones anónimas sin datos compartidos entre estas en un único perfil de chat. No comment provided by engineer. @@ -2141,6 +2162,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes. Make profile private! + ¡Hacer un perfil privado! No comment provided by engineer. @@ -2270,6 +2292,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes. Muted when inactive! + ¡Silenciado cuando está inactivo! No comment provided by engineer. @@ -2376,6 +2399,9 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes.Now admins can: - delete members' messages. - disable members ("observer" role) + Ahora los administradores pueden +- borrar mensajes de los miembros. +- desactivar el rol a miembros (a rol "observador") No comment provided by engineer. @@ -2400,7 +2426,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes. One-time invitation link - Enlace único de invitación + Enlace único de invitación de un uso No comment provided by engineer. @@ -2505,6 +2531,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes. Password to show + Contraseña para hacerlo visible No comment provided by engineer. @@ -2659,6 +2686,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes. Protect your chat profiles with a password! + ¡Proteje los perfiles de Chat con contraseña! No comment provided by engineer. @@ -2728,12 +2756,12 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes. Relay server is only used if necessary. Another party can observe your IP address. - El servidor de retransmisión sólo se utiliza en caso necesario. Una tercera persona puede observar tu dirección IP. + El relay sólo se usa en caso de necesidad. Un tercero podría ver tu IP. No comment provided by engineer. Relay server protects your IP address, but it can observe the duration of the call. - El servidor de retransmisión protege tu IP pero puede observar la duración de la llamada. + El servidor relay protege tu IP pero puede observar la duración de la llamada. No comment provided by engineer. @@ -2858,6 +2886,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes. Save and update group profile + Guardar y actualizar perfil del grupo No comment provided by engineer. @@ -2887,6 +2916,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes. Save profile password + Guardar contraseña de perfil No comment provided by engineer. @@ -2896,10 +2926,12 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes. Save servers? + ¿Guardar servidores? No comment provided by engineer. Save welcome message? + ¿Guardar mensaje de bienvenida? No comment provided by engineer. @@ -3054,6 +3086,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes. Set the message shown to new members! + ¡Establece el mensaje mostrado a los miembros nuevos! No comment provided by engineer. @@ -3083,7 +3116,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes. Share one-time invitation link - Compartir enlace único de invitación + Compartir enlace de invitación de un uso No comment provided by engineer. @@ -3138,7 +3171,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes. SimpleX one-time invitation - Invitación única SimpleX + Invitación SimpleX de un uso simplex link type @@ -3238,6 +3271,7 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes. Tap to activate profile. + Pulsa para activar el perfil. No comment provided by engineer. @@ -3367,10 +3401,12 @@ Añadiremos redundancia de servidores para evitar la pérdida de mensajes. There should be at least one user profile. + Debe haber al menos un perfil de usuario. No comment provided by engineer. There should be at least one visible user profile. + Debe haber al menos un perfil de usuario visible. No comment provided by engineer. @@ -3442,6 +3478,7 @@ Se te pedirá que completes la autenticación antes de activar esta función. To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page. + Para hacer visible tu perfil oculto, introduce la contraseña completa en el campo de búsqueda de la página **Tus perfiles Chat**. No comment provided by engineer. @@ -3501,6 +3538,7 @@ Se te pedirá que completes la autenticación antes de activar esta función. Unhide + Mostrar No comment provided by engineer. @@ -3779,6 +3817,8 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb You can hide or mute a user profile - swipe it to the right. SimpleX Lock must be enabled. + Puedes ocultar o silenciar un perfil de usuario: deslízalo hacia la derecha. +SimpleX Lock debe estar activado. No comment provided by engineer. @@ -3898,6 +3938,7 @@ SimpleX Lock must be enabled. You will still receive calls and notifications from muted profiles when they are active. + Seguirás recibiendo llamadas y notificaciones de los perfiles silenciados cuando estén activos. No comment provided by engineer. @@ -4331,7 +4372,7 @@ Los servidores de SimpleX no pueden ver tu perfil. incognito via one-time link - Incógnito mediante enlace único + Incógnito mediante enlace de un uso chat list item description @@ -4487,7 +4528,7 @@ Los servidores de SimpleX no pueden ver tu perfil. peer-to-peer - entre particulares + p2p No comment provided by engineer. @@ -4572,12 +4613,12 @@ Los servidores de SimpleX no pueden ver tu perfil. via one-time link - mediante enlace único + mediante enlace de un uso chat list item description via relay - mediante servidor de retransmisión + mediante servidor relay No comment provided by engineer. @@ -4647,12 +4688,12 @@ Los servidores de SimpleX no pueden ver tu perfil. you shared one-time link - has compartido un enlace único + has compartido un enlace de un uso chat list item description you shared one-time link incognito - has compartido un enlace único en módo incógnito + has compartido enlace de un uso en modo incógnito chat list item description diff --git a/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff b/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff new file mode 100644 index 0000000000..c82a08c4be --- /dev/null +++ b/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff @@ -0,0 +1,3801 @@ + + + +
+ +
+ + + + + No comment provided by engineer. + + + + No comment provided by engineer. + + + + No comment provided by engineer. + + + + No comment provided by engineer. + + + ( + No comment provided by engineer. + + + (can be copied) + No comment provided by engineer. + + + !1 colored! + No comment provided by engineer. + + + #secret# + No comment provided by engineer. + + + %@ + No comment provided by engineer. + + + %@ %@ + No comment provided by engineer. + + + %@ / %@ + No comment provided by engineer. + + + %@ is connected! + notification title + + + %@ is not verified + No comment provided by engineer. + + + %@ is verified + No comment provided by engineer. + + + %@ wants to connect! + notification title + + + %d days + message ttl + + + %d hours + message ttl + + + %d min + message ttl + + + %d months + message ttl + + + %d sec + message ttl + + + %d skipped message(s) + integrity error chat item + + + %lld + No comment provided by engineer. + + + %lld %@ + No comment provided by engineer. + + + %lld contact(s) selected + No comment provided by engineer. + + + %lld file(s) with total size of %@ + No comment provided by engineer. + + + %lld members + No comment provided by engineer. + + + %lld second(s) + No comment provided by engineer. + + + %lldd + No comment provided by engineer. + + + %lldh + No comment provided by engineer. + + + %lldk + No comment provided by engineer. + + + %lldm + No comment provided by engineer. + + + %lldmth + No comment provided by engineer. + + + %llds + No comment provided by engineer. + + + %lldw + No comment provided by engineer. + + + ( + No comment provided by engineer. + + + ) + No comment provided by engineer. + + + **Add new contact**: to create your one-time QR Code or link for your contact. + No comment provided by engineer. + + + **Create link / QR code** for your contact to use. + No comment provided by engineer. + + + **More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have. + No comment provided by engineer. + + + **Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app). + No comment provided by engineer. + + + **Paste received link** or open it in the browser and tap **Open in mobile app**. + No comment provided by engineer. + + + **Please note**: you will NOT be able to recover or change passphrase if you lose it. + No comment provided by engineer. + + + **Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from. + No comment provided by engineer. + + + **Scan QR code**: to connect to your contact in person or via video call. + No comment provided by engineer. + + + **Warning**: Instant push notifications require passphrase saved in Keychain. + No comment provided by engineer. + + + **e2e encrypted** audio call + No comment provided by engineer. + + + **e2e encrypted** video call + No comment provided by engineer. + + + \*bold* + No comment provided by engineer. + + + , + No comment provided by engineer. + + + . + No comment provided by engineer. + + + 1 day + message ttl + + + 1 hour + message ttl + + + 1 month + message ttl + + + 1 week + message ttl + + + 2 weeks + message ttl + + + 6 + No comment provided by engineer. + + + : + No comment provided by engineer. + + + A new contact + notification title + + + A random profile will be sent to the contact that you received this link from + No comment provided by engineer. + + + A random profile will be sent to your contact + No comment provided by engineer. + + + A separate TCP connection will be used **for each chat profile you have in the app**. + No comment provided by engineer. + + + A separate TCP connection will be used **for each contact and group member**. +**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail. + No comment provided by engineer. + + + About SimpleX + No comment provided by engineer. + + + About SimpleX Chat + No comment provided by engineer. + + + Accent color + No comment provided by engineer. + + + Accept + accept contact request via notification + accept incoming call via notification + + + Accept contact + No comment provided by engineer. + + + Accept contact request from %@? + notification body + + + Accept incognito + No comment provided by engineer. + + + Accept requests + No comment provided by engineer. + + + Add preset servers + No comment provided by engineer. + + + Add profile + No comment provided by engineer. + + + Add servers by scanning QR codes. + No comment provided by engineer. + + + Add server… + No comment provided by engineer. + + + Add to another device + No comment provided by engineer. + + + Add welcome message + No comment provided by engineer. + + + Admins can create the links to join groups. + No comment provided by engineer. + + + Advanced network settings + No comment provided by engineer. + + + All chats and messages will be deleted - this cannot be undone! + No comment provided by engineer. + + + All group members will remain connected. + No comment provided by engineer. + + + All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you. + No comment provided by engineer. + + + All your contacts will remain connected + No comment provided by engineer. + + + Allow + No comment provided by engineer. + + + Allow disappearing messages only if your contact allows it to you. + No comment provided by engineer. + + + Allow irreversible message deletion only if your contact allows it to you. + No comment provided by engineer. + + + Allow sending direct messages to members. + No comment provided by engineer. + + + Allow sending disappearing messages. + No comment provided by engineer. + + + Allow to irreversibly delete sent messages. + No comment provided by engineer. + + + Allow to send voice messages. + No comment provided by engineer. + + + Allow voice messages only if your contact allows them. + No comment provided by engineer. + + + Allow voice messages? + No comment provided by engineer. + + + Allow your contacts to irreversibly delete sent messages. + No comment provided by engineer. + + + Allow your contacts to send disappearing messages. + No comment provided by engineer. + + + Allow your contacts to send voice messages. + No comment provided by engineer. + + + Already connected? + No comment provided by engineer. + + + Always use relay + No comment provided by engineer. + + + Answer call + No comment provided by engineer. + + + App build: %@ + No comment provided by engineer. + + + App icon + No comment provided by engineer. + + + App version + No comment provided by engineer. + + + App version: v%@ + No comment provided by engineer. + + + Appearance + No comment provided by engineer. + + + Attach + No comment provided by engineer. + + + Audio & video calls + No comment provided by engineer. + + + Audio and video calls + No comment provided by engineer. + + + Authentication failed + No comment provided by engineer. + + + Authentication is required before the call is connected, but you may miss calls. + No comment provided by engineer. + + + Authentication unavailable + No comment provided by engineer. + + + Auto-accept contact requests + No comment provided by engineer. + + + Auto-accept images + No comment provided by engineer. + + + Automatically + No comment provided by engineer. + + + Back + No comment provided by engineer. + + + Both you and your contact can irreversibly delete sent messages. + No comment provided by engineer. + + + Both you and your contact can send disappearing messages. + No comment provided by engineer. + + + Both you and your contact can send voice messages. + No comment provided by engineer. + + + By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA). + No comment provided by engineer. + + + Call already ended! + No comment provided by engineer. + + + Calls + No comment provided by engineer. + + + Can't delete user profile! + No comment provided by engineer. + + + Can't invite contact! + No comment provided by engineer. + + + Can't invite contacts! + No comment provided by engineer. + + + Cancel + No comment provided by engineer. + + + Cannot access keychain to save database password + No comment provided by engineer. + + + Cannot receive file + No comment provided by engineer. + + + Change + No comment provided by engineer. + + + Change database passphrase? + No comment provided by engineer. + + + Change member role? + No comment provided by engineer. + + + Change receiving address + No comment provided by engineer. + + + Change receiving address? + No comment provided by engineer. + + + Change role + No comment provided by engineer. + + + Chat archive + No comment provided by engineer. + + + Chat console + No comment provided by engineer. + + + Chat database + No comment provided by engineer. + + + Chat database deleted + No comment provided by engineer. + + + Chat database imported + No comment provided by engineer. + + + Chat is running + No comment provided by engineer. + + + Chat is stopped + No comment provided by engineer. + + + Chat preferences + No comment provided by engineer. + + + Chats + No comment provided by engineer. + + + Check server address and try again. + No comment provided by engineer. + + + Chinese and Spanish interface + No comment provided by engineer. + + + Choose file + No comment provided by engineer. + + + Choose from library + No comment provided by engineer. + + + Clear + No comment provided by engineer. + + + Clear conversation + No comment provided by engineer. + + + Clear conversation? + No comment provided by engineer. + + + Clear verification + No comment provided by engineer. + + + Colors + No comment provided by engineer. + + + Compare security codes with your contacts. + No comment provided by engineer. + + + Configure ICE servers + No comment provided by engineer. + + + Confirm + No comment provided by engineer. + + + Confirm new passphrase… + No comment provided by engineer. + + + Confirm password + No comment provided by engineer. + + + Connect + server test step + + + Connect via contact link? + No comment provided by engineer. + + + Connect via group link? + No comment provided by engineer. + + + Connect via link + No comment provided by engineer. + + + Connect via link / QR code + No comment provided by engineer. + + + Connect via one-time link? + No comment provided by engineer. + + + Connecting to server… + No comment provided by engineer. + + + Connecting to server… (error: %@) + No comment provided by engineer. + + + Connection + No comment provided by engineer. + + + Connection error + No comment provided by engineer. + + + Connection error (AUTH) + No comment provided by engineer. + + + Connection request + No comment provided by engineer. + + + Connection request sent! + No comment provided by engineer. + + + Connection timeout + No comment provided by engineer. + + + Contact allows + No comment provided by engineer. + + + Contact already exists + No comment provided by engineer. + + + Contact and all messages will be deleted - this cannot be undone! + No comment provided by engineer. + + + Contact hidden: + notification + + + Contact is connected + notification + + + Contact is not connected yet! + No comment provided by engineer. + + + Contact name + No comment provided by engineer. + + + Contact preferences + No comment provided by engineer. + + + Contact requests + No comment provided by engineer. + + + Contacts can mark messages for deletion; you will be able to view them. + No comment provided by engineer. + + + Copy + chat item action + + + Core built at: %@ + No comment provided by engineer. + + + Core version: v%@ + No comment provided by engineer. + + + Create + No comment provided by engineer. + + + Create address + No comment provided by engineer. + + + Create group link + No comment provided by engineer. + + + Create link + No comment provided by engineer. + + + Create one-time invitation link + No comment provided by engineer. + + + Create queue + server test step + + + Create secret group + No comment provided by engineer. + + + Create your profile + No comment provided by engineer. + + + Created on %@ + No comment provided by engineer. + + + Current passphrase… + No comment provided by engineer. + + + Currently maximum supported file size is %@. + No comment provided by engineer. + + + Dark + No comment provided by engineer. + + + Database ID + No comment provided by engineer. + + + Database encrypted! + No comment provided by engineer. + + + Database encryption passphrase will be updated and stored in the keychain. + + No comment provided by engineer. + + + Database encryption passphrase will be updated. + + No comment provided by engineer. + + + Database error + No comment provided by engineer. + + + Database is encrypted using a random passphrase, you can change it. + No comment provided by engineer. + + + Database is encrypted using a random passphrase. Please change it before exporting. + No comment provided by engineer. + + + Database passphrase + No comment provided by engineer. + + + Database passphrase & export + No comment provided by engineer. + + + Database passphrase is different from saved in the keychain. + No comment provided by engineer. + + + Database passphrase is required to open chat. + No comment provided by engineer. + + + Database will be encrypted and the passphrase stored in the keychain. + + No comment provided by engineer. + + + Database will be encrypted. + + No comment provided by engineer. + + + Database will be migrated when the app restarts + No comment provided by engineer. + + + Decentralized + No comment provided by engineer. + + + Delete + chat item action + + + Delete Contact + No comment provided by engineer. + + + Delete address + No comment provided by engineer. + + + Delete address? + No comment provided by engineer. + + + Delete after + No comment provided by engineer. + + + Delete all files + No comment provided by engineer. + + + Delete archive + No comment provided by engineer. + + + Delete chat archive? + No comment provided by engineer. + + + Delete chat profile? + No comment provided by engineer. + + + Delete connection + No comment provided by engineer. + + + Delete contact + No comment provided by engineer. + + + Delete contact? + No comment provided by engineer. + + + Delete database + No comment provided by engineer. + + + Delete files and media? + No comment provided by engineer. + + + Delete files for all chat profiles + No comment provided by engineer. + + + Delete for everyone + chat feature + + + Delete for me + No comment provided by engineer. + + + Delete group + No comment provided by engineer. + + + Delete group? + No comment provided by engineer. + + + Delete invitation + No comment provided by engineer. + + + Delete link + No comment provided by engineer. + + + Delete link? + No comment provided by engineer. + + + Delete member message? + No comment provided by engineer. + + + Delete message? + No comment provided by engineer. + + + Delete messages + No comment provided by engineer. + + + Delete messages after + No comment provided by engineer. + + + Delete old database + No comment provided by engineer. + + + Delete old database? + No comment provided by engineer. + + + Delete pending connection + No comment provided by engineer. + + + Delete pending connection? + No comment provided by engineer. + + + Delete queue + server test step + + + Delete user profile? + No comment provided by engineer. + + + Description + No comment provided by engineer. + + + Develop + No comment provided by engineer. + + + Developer tools + No comment provided by engineer. + + + Device + No comment provided by engineer. + + + Device authentication is disabled. Turning off SimpleX Lock. + No comment provided by engineer. + + + Device authentication is not enabled. You can turn on SimpleX Lock via Settings, once you enable device authentication. + No comment provided by engineer. + + + Different names, avatars and transport isolation. + No comment provided by engineer. + + + Direct messages + chat feature + + + Direct messages between members are prohibited in this group. + No comment provided by engineer. + + + Disable SimpleX Lock + authentication reason + + + Disappearing messages + chat feature + + + Disappearing messages are prohibited in this chat. + No comment provided by engineer. + + + Disappearing messages are prohibited in this group. + No comment provided by engineer. + + + Disconnect + server test step + + + Display name + No comment provided by engineer. + + + Display name: + No comment provided by engineer. + + + Do NOT use SimpleX for emergency calls. + No comment provided by engineer. + + + Do it later + No comment provided by engineer. + + + Don't show again + No comment provided by engineer. + + + Duplicate display name! + No comment provided by engineer. + + + Edit + chat item action + + + Edit group profile + No comment provided by engineer. + + + Enable + No comment provided by engineer. + + + Enable SimpleX Lock + authentication reason + + + Enable TCP keep-alive + No comment provided by engineer. + + + Enable automatic message deletion? + No comment provided by engineer. + + + Enable instant notifications? + No comment provided by engineer. + + + Enable notifications + No comment provided by engineer. + + + Enable periodic notifications? + No comment provided by engineer. + + + Encrypt + No comment provided by engineer. + + + Encrypt database? + No comment provided by engineer. + + + Encrypted database + No comment provided by engineer. + + + Encrypted message or another event + notification + + + Encrypted message: database error + notification + + + Encrypted message: keychain error + notification + + + Encrypted message: no passphrase + notification + + + Encrypted message: unexpected error + notification + + + Enter correct passphrase. + No comment provided by engineer. + + + Enter passphrase… + No comment provided by engineer. + + + Enter password above to show! + No comment provided by engineer. + + + Enter server manually + No comment provided by engineer. + + + Error + No comment provided by engineer. + + + Error accepting contact request + No comment provided by engineer. + + + Error accessing database file + No comment provided by engineer. + + + Error adding member(s) + No comment provided by engineer. + + + Error changing address + No comment provided by engineer. + + + Error changing role + No comment provided by engineer. + + + Error changing setting + No comment provided by engineer. + + + Error creating address + No comment provided by engineer. + + + Error creating group + No comment provided by engineer. + + + Error creating group link + No comment provided by engineer. + + + Error creating profile! + No comment provided by engineer. + + + Error deleting chat database + No comment provided by engineer. + + + Error deleting chat! + No comment provided by engineer. + + + Error deleting connection + No comment provided by engineer. + + + Error deleting contact + No comment provided by engineer. + + + Error deleting database + No comment provided by engineer. + + + Error deleting old database + No comment provided by engineer. + + + Error deleting token + No comment provided by engineer. + + + Error deleting user profile + No comment provided by engineer. + + + Error enabling notifications + No comment provided by engineer. + + + Error encrypting database + No comment provided by engineer. + + + Error exporting chat database + No comment provided by engineer. + + + Error importing chat database + No comment provided by engineer. + + + Error joining group + No comment provided by engineer. + + + Error receiving file + No comment provided by engineer. + + + Error removing member + No comment provided by engineer. + + + Error saving ICE servers + No comment provided by engineer. + + + Error saving SMP servers + No comment provided by engineer. + + + Error saving group profile + No comment provided by engineer. + + + Error saving passphrase to keychain + No comment provided by engineer. + + + Error saving user password + No comment provided by engineer. + + + Error sending message + No comment provided by engineer. + + + Error starting chat + No comment provided by engineer. + + + Error stopping chat + No comment provided by engineer. + + + Error switching profile! + No comment provided by engineer. + + + Error updating group link + No comment provided by engineer. + + + Error updating message + No comment provided by engineer. + + + Error updating settings + No comment provided by engineer. + + + Error updating user privacy + No comment provided by engineer. + + + Error: %@ + No comment provided by engineer. + + + Error: URL is invalid + No comment provided by engineer. + + + Error: no database file + No comment provided by engineer. + + + Exit without saving + No comment provided by engineer. + + + Export database + No comment provided by engineer. + + + Export error: + No comment provided by engineer. + + + Exported database archive. + No comment provided by engineer. + + + Exporting database archive... + No comment provided by engineer. + + + Failed to remove passphrase + No comment provided by engineer. + + + File will be received when your contact is online, please wait or check later! + No comment provided by engineer. + + + File: %@ + No comment provided by engineer. + + + Files & media + No comment provided by engineer. + + + For console + No comment provided by engineer. + + + French interface + No comment provided by engineer. + + + Full link + No comment provided by engineer. + + + Full name (optional) + No comment provided by engineer. + + + Full name: + No comment provided by engineer. + + + Fully re-implemented - work in background! + No comment provided by engineer. + + + Further reduced battery usage + No comment provided by engineer. + + + GIFs and stickers + No comment provided by engineer. + + + Group + No comment provided by engineer. + + + Group display name + No comment provided by engineer. + + + Group full name (optional) + No comment provided by engineer. + + + Group image + No comment provided by engineer. + + + Group invitation + No comment provided by engineer. + + + Group invitation expired + No comment provided by engineer. + + + Group invitation is no longer valid, it was removed by sender. + No comment provided by engineer. + + + Group link + No comment provided by engineer. + + + Group links + No comment provided by engineer. + + + Group members can irreversibly delete sent messages. + No comment provided by engineer. + + + Group members can send direct messages. + No comment provided by engineer. + + + Group members can send disappearing messages. + No comment provided by engineer. + + + Group members can send voice messages. + No comment provided by engineer. + + + Group message: + notification + + + Group moderation + No comment provided by engineer. + + + Group preferences + No comment provided by engineer. + + + Group profile + No comment provided by engineer. + + + Group profile is stored on members' devices, not on the servers. + No comment provided by engineer. + + + Group welcome message + No comment provided by engineer. + + + Group will be deleted for all members - this cannot be undone! + No comment provided by engineer. + + + Group will be deleted for you - this cannot be undone! + No comment provided by engineer. + + + Help + No comment provided by engineer. + + + Hidden + No comment provided by engineer. + + + Hidden chat profiles + No comment provided by engineer. + + + Hidden profile password + No comment provided by engineer. + + + Hide + chat item action + + + Hide app screen in the recent apps. + No comment provided by engineer. + + + Hide profile + No comment provided by engineer. + + + How SimpleX works + No comment provided by engineer. + + + How it works + No comment provided by engineer. + + + How to + No comment provided by engineer. + + + How to use it + No comment provided by engineer. + + + How to use your servers + No comment provided by engineer. + + + ICE servers (one per line) + No comment provided by engineer. + + + If you can't meet in person, **show QR code in the video call**, or share the link. + No comment provided by engineer. + + + If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link. + No comment provided by engineer. + + + If you need to use the chat now tap **Do it later** below (you will be offered to migrate the database when you restart the app). + No comment provided by engineer. + + + Ignore + No comment provided by engineer. + + + Image will be received when your contact is online, please wait or check later! + No comment provided by engineer. + + + Immune to spam and abuse + No comment provided by engineer. + + + Import + No comment provided by engineer. + + + Import chat database? + No comment provided by engineer. + + + Import database + No comment provided by engineer. + + + Improved privacy and security + No comment provided by engineer. + + + Improved server configuration + No comment provided by engineer. + + + Incognito + No comment provided by engineer. + + + Incognito mode + No comment provided by engineer. + + + Incognito mode is not supported here - your main profile will be sent to group members + No comment provided by engineer. + + + Incognito mode protects the privacy of your main profile name and image — for each new contact a new random profile is created. + No comment provided by engineer. + + + Incoming audio call + notification + + + Incoming call + notification + + + Incoming video call + notification + + + Incorrect security code! + No comment provided by engineer. + + + Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) + No comment provided by engineer. + + + Instant push notifications will be hidden! + + No comment provided by engineer. + + + Instantly + No comment provided by engineer. + + + Interface + No comment provided by engineer. + + + Invalid connection link + No comment provided by engineer. + + + Invalid server address! + No comment provided by engineer. + + + Invitation expired! + No comment provided by engineer. + + + Invite members + No comment provided by engineer. + + + Invite to group + No comment provided by engineer. + + + Irreversible message deletion + No comment provided by engineer. + + + Irreversible message deletion is prohibited in this chat. + No comment provided by engineer. + + + Irreversible message deletion is prohibited in this group. + No comment provided by engineer. + + + It allows having many anonymous connections without any shared data between them in a single chat profile. + No comment provided by engineer. + + + It can happen when: +1. The messages expire on the server if they were not received for 30 days, +2. The server you use to receive the messages from this contact was updated and restarted. +3. The connection is compromised. +Please connect to the developers via Settings to receive the updates about the servers. +We will be adding server redundancy to prevent lost messages. + No comment provided by engineer. + + + It seems like you are already connected via this link. If it is not the case, there was an error (%@). + No comment provided by engineer. + + + Italian interface + No comment provided by engineer. + + + Join + No comment provided by engineer. + + + Join group + No comment provided by engineer. + + + Join incognito + No comment provided by engineer. + + + Joining group + No comment provided by engineer. + + + Keychain error + No comment provided by engineer. + + + LIVE + No comment provided by engineer. + + + Large file! + No comment provided by engineer. + + + Leave + No comment provided by engineer. + + + Leave group + No comment provided by engineer. + + + Leave group? + No comment provided by engineer. + + + Light + No comment provided by engineer. + + + Limitations + No comment provided by engineer. + + + Live message! + No comment provided by engineer. + + + Live messages + No comment provided by engineer. + + + Local name + No comment provided by engineer. + + + Local profile data only + No comment provided by engineer. + + + Make a private connection + No comment provided by engineer. + + + Make profile private! + No comment provided by engineer. + + + Make sure SMP server addresses are in correct format, line separated and are not duplicated (%@). + No comment provided by engineer. + + + Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated. + No comment provided by engineer. + + + Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?* + No comment provided by engineer. + + + Mark deleted for everyone + No comment provided by engineer. + + + Mark read + No comment provided by engineer. + + + Mark verified + No comment provided by engineer. + + + Markdown in messages + No comment provided by engineer. + + + Max 30 seconds, received instantly. + No comment provided by engineer. + + + Member + No comment provided by engineer. + + + Member role will be changed to "%@". All group members will be notified. + No comment provided by engineer. + + + Member role will be changed to "%@". The member will receive a new invitation. + No comment provided by engineer. + + + Member will be removed from group - this cannot be undone! + No comment provided by engineer. + + + Message delivery error + No comment provided by engineer. + + + Message draft + No comment provided by engineer. + + + Message text + No comment provided by engineer. + + + Messages + No comment provided by engineer. + + + Migrating database archive... + No comment provided by engineer. + + + Migration error: + No comment provided by engineer. + + + Migration failed. Tap **Skip** below to continue using the current database. Please report the issue to the app developers via chat or email [chat@simplex.chat](mailto:chat@simplex.chat). + No comment provided by engineer. + + + Migration is completed + No comment provided by engineer. + + + Moderate + chat item action + + + More improvements are coming soon! + No comment provided by engineer. + + + Most likely this contact has deleted the connection with you. + No comment provided by engineer. + + + Multiple chat profiles + No comment provided by engineer. + + + Mute + No comment provided by engineer. + + + Muted when inactive! + No comment provided by engineer. + + + Name + No comment provided by engineer. + + + Network & servers + No comment provided by engineer. + + + Network settings + No comment provided by engineer. + + + Network status + No comment provided by engineer. + + + New contact request + notification + + + New contact: + notification + + + New database archive + No comment provided by engineer. + + + New in %@ + No comment provided by engineer. + + + New member role + No comment provided by engineer. + + + New message + notification + + + New passphrase… + No comment provided by engineer. + + + No + No comment provided by engineer. + + + No contacts selected + No comment provided by engineer. + + + No contacts to add + No comment provided by engineer. + + + No device token! + No comment provided by engineer. + + + Group not found! + No comment provided by engineer. + + + No permission to record voice message + No comment provided by engineer. + + + No received or sent files + No comment provided by engineer. + + + Notifications + No comment provided by engineer. + + + Notifications are disabled! + No comment provided by engineer. + + + Now admins can: +- delete members' messages. +- disable members ("observer" role) + No comment provided by engineer. + + + Off (Local) + No comment provided by engineer. + + + Ok + No comment provided by engineer. + + + Old database + No comment provided by engineer. + + + Old database archive + No comment provided by engineer. + + + One-time invitation link + No comment provided by engineer. + + + Onion hosts will be required for connection. Requires enabling VPN. + No comment provided by engineer. + + + Onion hosts will be used when available. Requires enabling VPN. + No comment provided by engineer. + + + Onion hosts will not be used. + No comment provided by engineer. + + + Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. + No comment provided by engineer. + + + Only group owners can change group preferences. + No comment provided by engineer. + + + Only group owners can enable voice messages. + No comment provided by engineer. + + + Only you can irreversibly delete messages (your contact can mark them for deletion). + No comment provided by engineer. + + + Only you can send disappearing messages. + No comment provided by engineer. + + + Only you can send voice messages. + No comment provided by engineer. + + + Only your contact can irreversibly delete messages (you can mark them for deletion). + No comment provided by engineer. + + + Only your contact can send disappearing messages. + No comment provided by engineer. + + + Only your contact can send voice messages. + No comment provided by engineer. + + + Open Settings + No comment provided by engineer. + + + Open chat + No comment provided by engineer. + + + Open chat console + authentication reason + + + Open user profiles + authentication reason + + + Open-source protocol and code – anybody can run the servers. + No comment provided by engineer. + + + Opening the link in the browser may reduce connection privacy and security. Untrusted SimpleX links will be red. + No comment provided by engineer. + + + PING count + No comment provided by engineer. + + + PING interval + No comment provided by engineer. + + + Password to show + No comment provided by engineer. + + + Paste + No comment provided by engineer. + + + Paste image + No comment provided by engineer. + + + Paste received link + No comment provided by engineer. + + + Paste the link you received into the box below to connect with your contact. + No comment provided by engineer. + + + People can connect to you only via the links you share. + No comment provided by engineer. + + + Periodically + No comment provided by engineer. + + + Please ask your contact to enable sending voice messages. + No comment provided by engineer. + + + Please check that you used the correct link or ask your contact to send you another one. + No comment provided by engineer. + + + Please check your network connection with %@ and try again. + No comment provided by engineer. + + + Please check yours and your contact preferences. + No comment provided by engineer. + + + Please contact group admin. + No comment provided by engineer. + + + Please enter correct current passphrase. + No comment provided by engineer. + + + Please enter the previous password after restoring database backup. This action can not be undone. + No comment provided by engineer. + + + Please restart the app and migrate the database to enable push notifications. + No comment provided by engineer. + + + Please store passphrase securely, you will NOT be able to access chat if you lose it. + No comment provided by engineer. + + + Please store passphrase securely, you will NOT be able to change it if you lose it. + No comment provided by engineer. + + + Possibly, certificate fingerprint in server address is incorrect + server test error + + + Preserve the last message draft, with attachments. + No comment provided by engineer. + + + Preset server + No comment provided by engineer. + + + Preset server address + No comment provided by engineer. + + + Privacy & security + No comment provided by engineer. + + + Privacy redefined + No comment provided by engineer. + + + Private filenames + No comment provided by engineer. + + + Profile and server connections + No comment provided by engineer. + + + Profile image + No comment provided by engineer. + + + Prohibit irreversible message deletion. + No comment provided by engineer. + + + Prohibit sending direct messages to members. + No comment provided by engineer. + + + Prohibit sending disappearing messages. + No comment provided by engineer. + + + Prohibit sending voice messages. + No comment provided by engineer. + + + Protect app screen + No comment provided by engineer. + + + Protect your chat profiles with a password! + No comment provided by engineer. + + + Protocol timeout + No comment provided by engineer. + + + Push notifications + No comment provided by engineer. + + + Rate the app + No comment provided by engineer. + + + Read + No comment provided by engineer. + + + Read more in our GitHub repository. + No comment provided by engineer. + + + Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme). + No comment provided by engineer. + + + Received file event + notification + + + Receiving via + No comment provided by engineer. + + + Recipients see updates as you type them. + No comment provided by engineer. + + + Reduced battery usage + No comment provided by engineer. + + + Reject + reject incoming call via notification + + + Reject contact (sender NOT notified) + No comment provided by engineer. + + + Reject contact request + No comment provided by engineer. + + + Relay server is only used if necessary. Another party can observe your IP address. + No comment provided by engineer. + + + Relay server protects your IP address, but it can observe the duration of the call. + No comment provided by engineer. + + + Remove + No comment provided by engineer. + + + Remove member + No comment provided by engineer. + + + Remove member? + No comment provided by engineer. + + + Remove passphrase from keychain? + No comment provided by engineer. + + + Reply + chat item action + + + Required + No comment provided by engineer. + + + Reset + No comment provided by engineer. + + + Reset colors + No comment provided by engineer. + + + Reset to defaults + No comment provided by engineer. + + + Restart the app to create a new chat profile + No comment provided by engineer. + + + Restart the app to use imported chat database + No comment provided by engineer. + + + Restore + No comment provided by engineer. + + + Restore database backup + No comment provided by engineer. + + + Restore database backup? + No comment provided by engineer. + + + Restore database error + No comment provided by engineer. + + + Reveal + chat item action + + + Revert + No comment provided by engineer. + + + Role + No comment provided by engineer. + + + Run chat + No comment provided by engineer. + + + SMP servers + No comment provided by engineer. + + + Save + chat item action + + + Save (and notify contacts) + No comment provided by engineer. + + + Save and notify contact + No comment provided by engineer. + + + Save and notify group members + No comment provided by engineer. + + + Save and update group profile + No comment provided by engineer. + + + Save archive + No comment provided by engineer. + + + Save group profile + No comment provided by engineer. + + + Save passphrase and open chat + No comment provided by engineer. + + + Save passphrase in Keychain + No comment provided by engineer. + + + Save preferences? + No comment provided by engineer. + + + Save profile password + No comment provided by engineer. + + + Save servers + No comment provided by engineer. + + + Save servers? + No comment provided by engineer. + + + Save welcome message? + No comment provided by engineer. + + + Saved WebRTC ICE servers will be removed + No comment provided by engineer. + + + Scan QR code + No comment provided by engineer. + + + Scan code + No comment provided by engineer. + + + Scan security code from your contact's app. + No comment provided by engineer. + + + Scan server QR code + No comment provided by engineer. + + + Search + No comment provided by engineer. + + + Secure queue + server test step + + + Security assessment + No comment provided by engineer. + + + Security code + No comment provided by engineer. + + + Send + No comment provided by engineer. + + + Send a live message - it will update for the recipient(s) as you type it + No comment provided by engineer. + + + Send direct message + No comment provided by engineer. + + + Send link previews + No comment provided by engineer. + + + Send live message + No comment provided by engineer. + + + Send notifications + No comment provided by engineer. + + + Send notifications: + No comment provided by engineer. + + + Send questions and ideas + No comment provided by engineer. + + + Send them from gallery or custom keyboards. + No comment provided by engineer. + + + Sender cancelled file transfer. + No comment provided by engineer. + + + Sender may have deleted the connection request. + No comment provided by engineer. + + + Sending via + No comment provided by engineer. + + + Sent file event + notification + + + Sent messages will be deleted after set time. + No comment provided by engineer. + + + Server requires authorization to create queues, check password + server test error + + + Server test failed! + No comment provided by engineer. + + + Servers + No comment provided by engineer. + + + Set 1 day + No comment provided by engineer. + + + Set contact name… + No comment provided by engineer. + + + Set group preferences + No comment provided by engineer. + + + Set passphrase to export + No comment provided by engineer. + + + Set the message shown to new members! + No comment provided by engineer. + + + Set timeouts for proxy/VPN + No comment provided by engineer. + + + Settings + No comment provided by engineer. + + + Share + chat item action + + + Share invitation link + No comment provided by engineer. + + + Share link + No comment provided by engineer. + + + Share one-time invitation link + No comment provided by engineer. + + + Show QR code + No comment provided by engineer. + + + Show calls in phone history + No comment provided by engineer. + + + Show preview + No comment provided by engineer. + + + SimpleX Chat security was [audited by Trail of Bits](https://simplex.chat/blog/20221108-simplex-chat-v4.2-security-audit-new-website.html). + No comment provided by engineer. + + + SimpleX Lock + No comment provided by engineer. + + + SimpleX Lock turned on + No comment provided by engineer. + + + SimpleX contact address + simplex link type + + + SimpleX encrypted message or connection event + notification + + + SimpleX group link + simplex link type + + + SimpleX links + No comment provided by engineer. + + + SimpleX one-time invitation + simplex link type + + + Skip + No comment provided by engineer. + + + Skipped messages + No comment provided by engineer. + + + Somebody + notification title + + + Start a new chat + No comment provided by engineer. + + + Start chat + No comment provided by engineer. + + + Start migration + No comment provided by engineer. + + + Stop + No comment provided by engineer. + + + Stop SimpleX + authentication reason + + + Stop chat to enable database actions + No comment provided by engineer. + + + Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped. + No comment provided by engineer. + + + Stop chat? + No comment provided by engineer. + + + Support SimpleX Chat + No comment provided by engineer. + + + System + No comment provided by engineer. + + + TCP connection timeout + No comment provided by engineer. + + + TCP_KEEPCNT + No comment provided by engineer. + + + TCP_KEEPIDLE + No comment provided by engineer. + + + TCP_KEEPINTVL + No comment provided by engineer. + + + Take picture + No comment provided by engineer. + + + Tap button + No comment provided by engineer. + + + Tap to activate profile. + No comment provided by engineer. + + + Tap to join + No comment provided by engineer. + + + Tap to join incognito + No comment provided by engineer. + + + Tap to start a new chat + No comment provided by engineer. + + + Test failed at step %@. + server test failure + + + Test server + No comment provided by engineer. + + + Test servers + No comment provided by engineer. + + + Tests failed! + No comment provided by engineer. + + + Thank you for installing SimpleX Chat! + No comment provided by engineer. + + + Thanks to the users – [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#translate-the-apps)! + No comment provided by engineer. + + + Thanks to the users – contribute via Weblate! + No comment provided by engineer. + + + The 1st platform without any user identifiers – private by design. + No comment provided by engineer. + + + The app can notify you when you receive messages or contact requests - please open settings to enable. + No comment provided by engineer. + + + The attempt to change database passphrase was not completed. + No comment provided by engineer. + + + The connection you accepted will be cancelled! + No comment provided by engineer. + + + The contact you shared this link with will NOT be able to connect! + No comment provided by engineer. + + + The created archive is available via app Settings / Database / Old database archive. + No comment provided by engineer. + + + The group is fully decentralized – it is visible only to the members. + No comment provided by engineer. + + + The message will be deleted for all members. + No comment provided by engineer. + + + The message will be marked as moderated for all members. + No comment provided by engineer. + + + The next generation of private messaging + No comment provided by engineer. + + + The old database was not removed during the migration, it can be deleted. + No comment provided by engineer. + + + The profile is only shared with your contacts. + No comment provided by engineer. + + + The sender will NOT be notified + No comment provided by engineer. + + + The servers for new connections of your current chat profile **%@**. + No comment provided by engineer. + + + Theme + No comment provided by engineer. + + + There should be at least one user profile. + No comment provided by engineer. + + + There should be at least one visible user profile. + No comment provided by engineer. + + + This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain. + No comment provided by engineer. + + + This action cannot be undone - the messages sent and received earlier than selected will be deleted. It may take several minutes. + No comment provided by engineer. + + + This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost. + No comment provided by engineer. + + + This feature is experimental! It will only work if the other client has version 4.2 installed. You should see the message in the conversation once the address change is completed – please check that you can still receive messages from this contact (or group member). + No comment provided by engineer. + + + This group no longer exists. + No comment provided by engineer. + + + This setting applies to messages in your current chat profile **%@**. + No comment provided by engineer. + + + To ask any questions and to receive updates: + No comment provided by engineer. + + + To find the profile used for an incognito connection, tap the contact or group name on top of the chat. + No comment provided by engineer. + + + To make a new connection + No comment provided by engineer. + + + To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts. + No comment provided by engineer. + + + To protect timezone, image/voice files use UTC. + No comment provided by engineer. + + + To protect your information, turn on SimpleX Lock. +You will be prompted to complete authentication before this feature is enabled. + No comment provided by engineer. + + + To record voice message please grant permission to use Microphone. + No comment provided by engineer. + + + To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page. + No comment provided by engineer. + + + To support instant push notifications the chat database has to be migrated. + No comment provided by engineer. + + + To verify end-to-end encryption with your contact compare (or scan) the code on your devices. + No comment provided by engineer. + + + Transport isolation + No comment provided by engineer. + + + Trying to connect to the server used to receive messages from this contact (error: %@). + No comment provided by engineer. + + + Trying to connect to the server used to receive messages from this contact. + No comment provided by engineer. + + + Turn off + No comment provided by engineer. + + + Turn off notifications? + No comment provided by engineer. + + + Turn on + No comment provided by engineer. + + + Unable to record voice message + No comment provided by engineer. + + + Unexpected error: %@ + No comment provided by engineer. + + + Unexpected migration state + No comment provided by engineer. + + + Unhide + No comment provided by engineer. + + + Unknown caller + callkit banner + + + Unknown database error: %@ + No comment provided by engineer. + + + Unknown error + No comment provided by engineer. + + + Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. + No comment provided by engineer. + + + Unless your contact deleted the connection or this link was already used, it might be a bug - please report it. +To connect, please ask your contact to create another connection link and check that you have a stable network connection. + No comment provided by engineer. + + + Unlock + authentication reason + + + Unmute + No comment provided by engineer. + + + Unread + No comment provided by engineer. + + + Update + No comment provided by engineer. + + + Update .onion hosts setting? + No comment provided by engineer. + + + Update database passphrase + No comment provided by engineer. + + + Update network settings? + No comment provided by engineer. + + + Update transport isolation mode? + No comment provided by engineer. + + + Updating settings will re-connect the client to all servers. + No comment provided by engineer. + + + Updating this setting will re-connect the client to all servers. + No comment provided by engineer. + + + Use .onion hosts + No comment provided by engineer. + + + Use SimpleX Chat servers? + No comment provided by engineer. + + + Use chat + No comment provided by engineer. + + + Use for new connections + No comment provided by engineer. + + + Use iOS call interface + No comment provided by engineer. + + + Use server + No comment provided by engineer. + + + User profile + No comment provided by engineer. + + + Using .onion hosts requires compatible VPN provider. + No comment provided by engineer. + + + Using SimpleX Chat servers. + No comment provided by engineer. + + + Verify connection security + No comment provided by engineer. + + + Verify security code + No comment provided by engineer. + + + Via browser + No comment provided by engineer. + + + Video call + No comment provided by engineer. + + + View security code + No comment provided by engineer. + + + Voice messages + chat feature + + + Voice messages are prohibited in this chat. + No comment provided by engineer. + + + Voice messages are prohibited in this group. + No comment provided by engineer. + + + Voice messages prohibited! + No comment provided by engineer. + + + Voice message… + No comment provided by engineer. + + + Waiting for file + No comment provided by engineer. + + + Waiting for image + No comment provided by engineer. + + + WebRTC ICE servers + No comment provided by engineer. + + + Welcome %@! + No comment provided by engineer. + + + Welcome message + No comment provided by engineer. + + + What's new + No comment provided by engineer. + + + When available + No comment provided by engineer. + + + When you share an incognito profile with somebody, this profile will be used for the groups they invite you to. + No comment provided by engineer. + + + With optional welcome message. + No comment provided by engineer. + + + Wrong database passphrase + No comment provided by engineer. + + + Wrong passphrase! + No comment provided by engineer. + + + You + No comment provided by engineer. + + + You accepted connection + No comment provided by engineer. + + + You allow + No comment provided by engineer. + + + You already have a chat profile with the same display name. Please choose another name. + No comment provided by engineer. + + + You are already connected to %@. + No comment provided by engineer. + + + You are connected to the server used to receive messages from this contact. + No comment provided by engineer. + + + You are invited to group + No comment provided by engineer. + + + You can accept calls from lock screen, without device and app authentication. + No comment provided by engineer. + + + You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button. + No comment provided by engineer. + + + You can hide or mute a user profile - swipe it to the right. +SimpleX Lock must be enabled. + No comment provided by engineer. + + + You can now send messages to %@ + notification body + + + You can set lock screen notification preview via settings. + No comment provided by engineer. + + + You can share a link or a QR code - anybody will be able to join the group. You won't lose members of the group if you later delete it. + No comment provided by engineer. + + + You can share your address as a link or as a QR code - anybody will be able to connect to you. You won't lose your contacts if you later delete it. + No comment provided by engineer. + + + You can start chat via app Settings / Database or by restarting the app + No comment provided by engineer. + + + You can use markdown to format messages: + No comment provided by engineer. + + + You can't send messages! + No comment provided by engineer. + + + You control through which server(s) **to receive** the messages, your contacts – the servers you use to message them. + No comment provided by engineer. + + + You could not be verified; please try again. + No comment provided by engineer. + + + You have no chats + No comment provided by engineer. + + + You have to enter passphrase every time the app starts - it is not stored on the device. + No comment provided by engineer. + + + You invited your contact + No comment provided by engineer. + + + You joined this group + No comment provided by engineer. + + + You joined this group. Connecting to inviting group member. + No comment provided by engineer. + + + You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts. + No comment provided by engineer. + + + You need to allow your contact to send voice messages to be able to send them. + No comment provided by engineer. + + + You rejected group invitation + No comment provided by engineer. + + + You sent group invitation + No comment provided by engineer. + + + You will be connected to group when the group host's device is online, please wait or check later! + No comment provided by engineer. + + + You will be connected when your connection request is accepted, please wait or check later! + No comment provided by engineer. + + + You will be connected when your contact's device is online, please wait or check later! + No comment provided by engineer. + + + You will be required to authenticate when you start or resume the app after 30 seconds in background. + No comment provided by engineer. + + + You will join a group this link refers to and connect to its group members. + No comment provided by engineer. + + + You will still receive calls and notifications from muted profiles when they are active. + No comment provided by engineer. + + + You will stop receiving messages from this group. Chat history will be preserved. + No comment provided by engineer. + + + You're trying to invite contact with whom you've shared an incognito profile to the group in which you're using your main profile + No comment provided by engineer. + + + You're using an incognito profile for this group - to prevent sharing your main profile inviting contacts is not allowed + No comment provided by engineer. + + + Your ICE servers + No comment provided by engineer. + + + Your SMP servers + No comment provided by engineer. + + + Your SimpleX contact address + No comment provided by engineer. + + + Your calls + No comment provided by engineer. + + + Your chat database + No comment provided by engineer. + + + Your chat database is not encrypted - set passphrase to encrypt it. + No comment provided by engineer. + + + Your chat profile will be sent to group members + No comment provided by engineer. + + + Your chat profile will be sent to your contact + No comment provided by engineer. + + + Your chat profiles + No comment provided by engineer. + + + Your chats + No comment provided by engineer. + + + Your contact address + No comment provided by engineer. + + + Your contact can scan it from the app. + No comment provided by engineer. + + + Your contact needs to be online for the connection to complete. +You can cancel this connection and remove the contact (and try later with a new link). + No comment provided by engineer. + + + Your contact sent a file that is larger than currently supported maximum size (%@). + No comment provided by engineer. + + + Your contacts can allow full message deletion. + No comment provided by engineer. + + + Your current chat database will be DELETED and REPLACED with the imported one. + No comment provided by engineer. + + + Your current profile + No comment provided by engineer. + + + Your preferences + No comment provided by engineer. + + + Your privacy + No comment provided by engineer. + + + Your profile is stored on your device and shared only with your contacts. +SimpleX servers cannot see your profile. + No comment provided by engineer. + + + Your profile will be sent to the contact that you received this link from + No comment provided by engineer. + + + Your profile, contacts and delivered messages are stored on your device. + No comment provided by engineer. + + + Your random profile + No comment provided by engineer. + + + Your server + No comment provided by engineer. + + + Your server address + No comment provided by engineer. + + + Your settings + No comment provided by engineer. + + + [Contribute](https://github.com/simplex-chat/simplex-chat#contribute) + No comment provided by engineer. + + + [Send us email](mailto:chat@simplex.chat) + No comment provided by engineer. + + + [Star on GitHub](https://github.com/simplex-chat/simplex-chat) + No comment provided by engineer. + + + \_italic_ + No comment provided by engineer. + + + \`a + b` + No comment provided by engineer. + + + above, then choose: + No comment provided by engineer. + + + accepted call + call status + + + admin + member role + + + always + pref value + + + audio call (not e2e encrypted) + No comment provided by engineer. + + + bad message ID + integrity error chat item + + + bad message hash + integrity error chat item + + + bold + No comment provided by engineer. + + + call error + call status + + + call in progress + call status + + + calling… + call status + + + cancelled %@ + feature offered item + + + changed address for you + chat item text + + + changed role of %1$@ to %2$@ + rcv group event chat item + + + changed your role to %@ + rcv group event chat item + + + changing address for %@... + chat item text + + + changing address... + chat item text + + + colored + No comment provided by engineer. + + + complete + No comment provided by engineer. + + + connect to SimpleX Chat developers. + No comment provided by engineer. + + + connected + No comment provided by engineer. + + + connecting + No comment provided by engineer. + + + connecting (accepted) + No comment provided by engineer. + + + connecting (announced) + No comment provided by engineer. + + + connecting (introduced) + No comment provided by engineer. + + + connecting (introduction invitation) + No comment provided by engineer. + + + connecting call… + call status + + + connecting… + chat list item title + + + connection established + chat list item title (it should not be shown + + + connection:%@ + connection information + + + contact has e2e encryption + No comment provided by engineer. + + + contact has no e2e encryption + No comment provided by engineer. + + + creator + No comment provided by engineer. + + + default (%@) + pref value + + + deleted + deleted chat item + + + deleted group + rcv group event chat item + + + direct + connection level description + + + duplicate message + integrity error chat item + + + e2e encrypted + No comment provided by engineer. + + + enabled + enabled status + + + enabled for contact + enabled status + + + enabled for you + enabled status + + + ended + No comment provided by engineer. + + + ended call %@ + call status + + + error + No comment provided by engineer. + + + group deleted + No comment provided by engineer. + + + group profile updated + snd group event chat item + + + iOS Keychain is used to securely store passphrase - it allows receiving push notifications. + No comment provided by engineer. + + + iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications. + No comment provided by engineer. + + + incognito via contact address link + chat list item description + + + incognito via group link + chat list item description + + + incognito via one-time link + chat list item description + + + indirect (%d) + connection level description + + + invalid chat + invalid chat data + + + invalid chat data + No comment provided by engineer. + + + invalid data + invalid chat item + + + invitation to group %@ + group name + + + invited + No comment provided by engineer. + + + invited %@ + rcv group event chat item + + + invited to connect + chat list item title + + + invited via your group link + rcv group event chat item + + + italic + No comment provided by engineer. + + + join as %@ + No comment provided by engineer. + + + left + rcv group event chat item + + + marked deleted + marked deleted chat item preview text + + + member + member role + + + connected + rcv group event chat item + + + message received + notification + + + missed call + call status + + + moderated + moderated chat item + + + moderated by %@ + No comment provided by engineer. + + + never + No comment provided by engineer. + + + new message + notification + + + no + pref value + + + no e2e encryption + No comment provided by engineer. + + + observer + member role + + + off + enabled status + group pref value + + + offered %@ + feature offered item + + + offered %1$@: %2$@ + feature offered item + + + on + group pref value + + + or chat with the developers + No comment provided by engineer. + + + owner + member role + + + peer-to-peer + No comment provided by engineer. + + + received answer… + No comment provided by engineer. + + + received confirmation… + No comment provided by engineer. + + + rejected call + call status + + + removed + No comment provided by engineer. + + + removed %@ + rcv group event chat item + + + removed you + rcv group event chat item + + + sec + network option + + + secret + No comment provided by engineer. + + + starting… + No comment provided by engineer. + + + strike + No comment provided by engineer. + + + this contact + notification title + + + unknown + connection info + + + updated group profile + rcv group event chat item + + + v%@ (%@) + No comment provided by engineer. + + + via contact address link + chat list item description + + + via group link + chat list item description + + + via one-time link + chat list item description + + + via relay + No comment provided by engineer. + + + video call (not e2e encrypted) + No comment provided by engineer. + + + waiting for answer… + No comment provided by engineer. + + + waiting for confirmation… + No comment provided by engineer. + + + wants to connect to you! + No comment provided by engineer. + + + yes + pref value + + + you are invited to group + No comment provided by engineer. + + + you are observer + No comment provided by engineer. + + + you changed address + chat item text + + + you changed address for %@ + chat item text + + + you changed role for yourself to %@ + snd group event chat item + + + you changed role of %1$@ to %2$@ + snd group event chat item + + + you left + snd group event chat item + + + you removed %@ + snd group event chat item + + + you shared one-time link + chat list item description + + + you shared one-time link incognito + chat list item description + + + you: + No comment provided by engineer. + + + \~strike~ + No comment provided by engineer. + + +
+ +
+ +
+ + + SimpleX + Bundle name + + + SimpleX needs camera access to scan QR codes to connect to other users and for video calls. + Privacy - Camera Usage Description + + + SimpleX uses Face ID for local authentication + Privacy - Face ID Usage Description + + + SimpleX needs microphone access for audio and video calls, and to record voice messages. + Privacy - Microphone Usage Description + + + SimpleX needs access to Photo Library for saving captured and received media + Privacy - Photo Library Additions Usage Description + + +
+ +
+ +
+ + + SimpleX NSE + Bundle display name + + + SimpleX NSE + Bundle name + + + Copyright © 2022 SimpleX Chat. All rights reserved. + Copyright (human-readable) + + +
+
diff --git a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff index 099327af49..6c75657f10 100644 --- a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff @@ -387,6 +387,7 @@
Add welcome message + Ajouter un message d'accueil No comment provided by engineer. @@ -531,6 +532,7 @@ Audio and video calls + Appels audio et vidéo No comment provided by engineer. @@ -600,6 +602,7 @@ Can't delete user profile! + Impossible de supprimer le profil d'utilisateur ! No comment provided by engineer. @@ -709,6 +712,7 @@ Chinese and Spanish interface + Interface en chinois et en espagnol No comment provided by engineer. @@ -768,6 +772,7 @@ Confirm password + Confirmer le mot de passe No comment provided by engineer. @@ -1300,6 +1305,7 @@ Don't show again + Ne plus afficher No comment provided by engineer. @@ -1404,6 +1410,7 @@ Enter password above to show! + Entrez le mot de passe ci-dessus pour continuer ! No comment provided by engineer. @@ -1563,6 +1570,7 @@ Error saving user password + Erreur d'enregistrement du mot de passe de l'utilisateur No comment provided by engineer. @@ -1602,6 +1610,7 @@ Error updating user privacy + Erreur de mise à jour de la confidentialité de l'utilisateur No comment provided by engineer. @@ -1691,10 +1700,12 @@ Fully re-implemented - work in background! + Entièrement réimplémenté - fonctionne en arrière-plan ! No comment provided by engineer. Further reduced battery usage + Réduction accrue de l'utilisation de la batterie No comment provided by engineer. @@ -1774,6 +1785,7 @@ Group moderation + Modération de groupe No comment provided by engineer. @@ -1793,6 +1805,7 @@ Group welcome message + Message d'accueil du groupe No comment provided by engineer. @@ -1817,10 +1830,12 @@ Hidden chat profiles + Profils de chat cachés No comment provided by engineer. Hidden profile password + Mot de passe de profil caché No comment provided by engineer. @@ -1835,6 +1850,7 @@ Hide profile + Masquer le profil No comment provided by engineer. @@ -1962,6 +1978,11 @@ Code de sécurité incorrect ! No comment provided by engineer. + + Initial role + Rôle initial + No comment provided by engineer. + Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) Installer [SimpleX Chat pour terminal](https://github.com/simplex-chat/simplex-chat) @@ -2141,6 +2162,7 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message Make profile private! + Rendre le profil privé ! No comment provided by engineer. @@ -2270,6 +2292,7 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message Muted when inactive! + Mute en cas d'inactivité ! No comment provided by engineer. @@ -2376,6 +2399,9 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message Now admins can: - delete members' messages. - disable members ("observer" role) + Désormais, les administrateurs peuvent : +- supprimer les messages des membres. +- désactiver des membres (rôle "observateur") No comment provided by engineer. @@ -2505,6 +2531,7 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message Password to show + Mot de passe à afficher No comment provided by engineer. @@ -2659,6 +2686,7 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message Protect your chat profiles with a password! + Protégez vos profils de chat par un mot de passe ! No comment provided by engineer. @@ -2858,6 +2886,7 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message Save and update group profile + Sauvegarder et mettre à jour le profil du groupe No comment provided by engineer. @@ -2887,6 +2916,7 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message Save profile password + Enregistrer le mot de passe du profil No comment provided by engineer. @@ -2896,10 +2926,12 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message Save servers? + Sauvegarder les serveurs ? No comment provided by engineer. Save welcome message? + Sauvegarder le message d'accueil ? No comment provided by engineer. @@ -3054,6 +3086,7 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message Set the message shown to new members! + Choisissez un message à l'attention des nouveaux membres ! No comment provided by engineer. @@ -3238,6 +3271,7 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message Tap to activate profile. + Appuyez pour activer le profil. No comment provided by engineer. @@ -3367,10 +3401,12 @@ Nous allons ajouter une redondance des serveurs pour éviter la perte de message There should be at least one user profile. + Il doit y avoir au moins un profil d'utilisateur. No comment provided by engineer. There should be at least one visible user profile. + Il doit y avoir au moins un profil d'utilisateur visible. No comment provided by engineer. @@ -3442,6 +3478,7 @@ Vous serez invité à confirmer l'authentification avant que cette fonction ne s To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page. + Pour révéler votre profil caché, entrez un mot de passe complet dans le champ de recherche de la page **Vos profils de chat**. No comment provided by engineer. @@ -3501,6 +3538,7 @@ Vous serez invité à confirmer l'authentification avant que cette fonction ne s Unhide + Dévoiler No comment provided by engineer. @@ -3778,6 +3816,8 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien You can hide or mute a user profile - swipe it to the right. SimpleX Lock must be enabled. + Vous pouvez masquer ou mettre en sourdine un profil d'utilisateur - faites-le glisser vers la droite. +SimpleX Lock doit être activé. No comment provided by engineer. @@ -3897,6 +3937,7 @@ SimpleX Lock must be enabled. You will still receive calls and notifications from muted profiles when they are active. + Vous continuerez à recevoir des appels et des notifications des profils mis en sourdine lorsqu'ils sont actifs. No comment provided by engineer. diff --git a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff index 9f8ba15fc2..0c5ef67885 100644 --- a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff +++ b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff @@ -387,6 +387,7 @@ Add welcome message + Aggiungi messaggio di benvenuto No comment provided by engineer. @@ -531,6 +532,7 @@ Audio and video calls + Chiamate audio e video No comment provided by engineer. @@ -600,6 +602,7 @@ Can't delete user profile! + Impossibile eliminare il profilo utente! No comment provided by engineer. @@ -709,6 +712,7 @@ Chinese and Spanish interface + Interfaccia cinese e spagnola No comment provided by engineer. @@ -768,6 +772,7 @@ Confirm password + Conferma password No comment provided by engineer. @@ -1300,6 +1305,7 @@ Don't show again + Non mostrare più No comment provided by engineer. @@ -1404,6 +1410,7 @@ Enter password above to show! + Inserisci la password sopra per mostrare! No comment provided by engineer. @@ -1563,6 +1570,7 @@ Error saving user password + Errore nel salvataggio della password utente No comment provided by engineer. @@ -1602,6 +1610,7 @@ Error updating user privacy + Errore nell'aggiornamento della privacy dell'utente No comment provided by engineer. @@ -1691,10 +1700,12 @@ Fully re-implemented - work in background! + Completamente reimplementato - funziona in secondo piano! No comment provided by engineer. Further reduced battery usage + Ulteriore riduzione del consumo della batteria No comment provided by engineer. @@ -1774,6 +1785,7 @@ Group moderation + Moderazione del gruppo No comment provided by engineer. @@ -1793,6 +1805,7 @@ Group welcome message + Messaggio di benvenuto del gruppo No comment provided by engineer. @@ -1817,10 +1830,12 @@ Hidden chat profiles + Profili di chat nascosti No comment provided by engineer. Hidden profile password + Password del profilo nascosta No comment provided by engineer. @@ -1835,6 +1850,7 @@ Hide profile + Nascondi il profilo No comment provided by engineer. @@ -1962,6 +1978,11 @@ Codice di sicurezza sbagliato! No comment provided by engineer. + + Initial role + Ruolo iniziale + No comment provided by engineer. + Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) Installa [Simplex Chat per terminale](https://github.com/simplex-chat/simplex-chat) @@ -2141,6 +2162,7 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi. Make profile private! + Rendi privato il profilo! No comment provided by engineer. @@ -2270,6 +2292,7 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi. Muted when inactive! + Silenzioso quando inattivo! No comment provided by engineer. @@ -2376,6 +2399,9 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi.Now admins can: - delete members' messages. - disable members ("observer" role) + Ora gli amministratori possono: +- eliminare i messaggi dei membri. +- disattivare i membri (ruolo "osservatore") No comment provided by engineer. @@ -2505,6 +2531,7 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi. Password to show + Password per mostrare No comment provided by engineer. @@ -2659,6 +2686,7 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi. Protect your chat profiles with a password! + Proteggi i tuoi profili di chat con una password! No comment provided by engineer. @@ -2858,6 +2886,7 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi. Save and update group profile + Salva e aggiorna il profilo del gruppo No comment provided by engineer. @@ -2887,6 +2916,7 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi. Save profile password + Salva la password del profilo No comment provided by engineer. @@ -2896,10 +2926,12 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi. Save servers? + Salvare i server? No comment provided by engineer. Save welcome message? + Salvare il messaggio di benvenuto? No comment provided by engineer. @@ -3054,6 +3086,7 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi. Set the message shown to new members! + Imposta il messaggio mostrato ai nuovi membri! No comment provided by engineer. @@ -3238,6 +3271,7 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi. Tap to activate profile. + Tocca per attivare il profilo. No comment provided by engineer. @@ -3367,10 +3401,12 @@ Aggiungeremo la ridondanza del server per prevenire la perdita di messaggi. There should be at least one user profile. + Deve esserci almeno un profilo utente. No comment provided by engineer. There should be at least one visible user profile. + Deve esserci almeno un profilo utente visibile. No comment provided by engineer. @@ -3442,6 +3478,7 @@ Ti verrà chiesto di completare l'autenticazione prima di attivare questa funzio To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page. + Per rivelare il tuo profilo nascosto, inserisci una password completa in un campo di ricerca nella pagina **I tuoi profili di chat**. No comment provided by engineer. @@ -3501,6 +3538,7 @@ Ti verrà chiesto di completare l'autenticazione prima di attivare questa funzio Unhide + Svela No comment provided by engineer. @@ -3778,6 +3816,8 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e You can hide or mute a user profile - swipe it to the right. SimpleX Lock must be enabled. + Puoi nascondere o silenziare un profilo utente - scorrilo verso destra. +SimpleX Lock deve essere attivato. No comment provided by engineer. @@ -3897,6 +3937,7 @@ SimpleX Lock must be enabled. You will still receive calls and notifications from muted profiles when they are active. + Continuerai a ricevere chiamate e notifiche da profili silenziati quando sono attivi. No comment provided by engineer. diff --git a/apps/ios/SimpleX Localizations/lt.xcloc/Localized Contents/lt.xliff b/apps/ios/SimpleX Localizations/lt.xcloc/Localized Contents/lt.xliff index f576e2e2f0..184800bb1f 100644 --- a/apps/ios/SimpleX Localizations/lt.xcloc/Localized Contents/lt.xliff +++ b/apps/ios/SimpleX Localizations/lt.xcloc/Localized Contents/lt.xliff @@ -152,12 +152,14 @@ %lldw No comment provided by engineer. - + ( + ( No comment provided by engineer. - + ) + ) No comment provided by engineer. @@ -180,8 +182,9 @@ **Paste received link** or open it in the browser and tap **Open in mobile app**. No comment provided by engineer. - + **Please note**: you will NOT be able to recover or change passphrase if you lose it. + **Turėkite omenyje**: jeigu prarasite slaptafrazę, NEBEGALĖSITE jos atkurti ar pakeisti. No comment provided by engineer. @@ -208,52 +211,64 @@ \*bold* No comment provided by engineer. - + , + , No comment provided by engineer. - + . + . No comment provided by engineer. - + 1 day + 1 diena message ttl - + 1 hour + 1 valanda message ttl - + 1 month + 1 mėnuo message ttl - + 1 week + 1 savaitė message ttl - + 2 weeks + 2 savaitės message ttl - + 6 + 6 No comment provided by engineer. - + : + : No comment provided by engineer. - + A new contact + Naujas adresatas notification title - + A random profile will be sent to the contact that you received this link from + Adresatui, iš kurio gavote šią nuorodą, bus išsiųstas atsitiktinis profilis No comment provided by engineer. - + A random profile will be sent to your contact + Jūsų adresatui bus išsiųstas atsitiktinis profilis No comment provided by engineer. @@ -265,12 +280,14 @@ **Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail. No comment provided by engineer. - + About SimpleX + Apie SimpleX No comment provided by engineer. - + About SimpleX Chat + Apie SimpleX Chat No comment provided by engineer. @@ -302,16 +319,19 @@ Add preset servers No comment provided by engineer. - + Add profile + Pridėti profilį No comment provided by engineer. - + Add servers by scanning QR codes. + Pridėti serverius skenuojant QR kodus. No comment provided by engineer. - + Add server… + Pridėti serverį… No comment provided by engineer. @@ -322,72 +342,86 @@ Admins can create the links to join groups. No comment provided by engineer. - + Advanced network settings + Išplėstiniai tinklo nustatymai No comment provided by engineer. - + All chats and messages will be deleted - this cannot be undone! + Visos žinutės ir pokalbiai bus ištrinti – to neįmanoma bus atšaukti! No comment provided by engineer. All group members will remain connected. No comment provided by engineer. - + All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you. + Visos žinutės bus ištrintos – to neįmanoma bus atšaukti! Žinutės bus ištrintos TIK jums. No comment provided by engineer. All your contacts will remain connected No comment provided by engineer. - + Allow + Leisti No comment provided by engineer. Allow disappearing messages only if your contact allows it to you. No comment provided by engineer. - + Allow irreversible message deletion only if your contact allows it to you. + Leisti negrįžtamą žinučių ištrynimą tik tuo atveju, jei jūsų adresatas jums tai leidžia. No comment provided by engineer. - + Allow sending direct messages to members. + Leisti siųsti tiesiogines žinutes nariams. No comment provided by engineer. - + Allow sending disappearing messages. + Leisti siųsti išnykstančias žinutes. No comment provided by engineer. - + Allow to irreversibly delete sent messages. + Leisti negrįžtamai ištrinti išsiųstas žinutes. No comment provided by engineer. - + Allow to send voice messages. + Leisti siųsti balso žinutes. No comment provided by engineer. - + Allow voice messages only if your contact allows them. + Leisti balso žinutes tik tuo atveju, jei jūsų adresatas jas leidžia. No comment provided by engineer. - + Allow voice messages? + Leisti balso žinutes? No comment provided by engineer. - + Allow your contacts to irreversibly delete sent messages. + Leisti jūsų adresatams negrįžtamai ištrinti išsiųstas žinutes. No comment provided by engineer. - + Allow your contacts to send disappearing messages. + Leisti jūsų adresatams siųsti išnykstančias žinutes. No comment provided by engineer. - + Allow your contacts to send voice messages. + Leisti jūsų adresatams siųsti balso žinutes. No comment provided by engineer. @@ -398,48 +432,57 @@ Always use relay No comment provided by engineer. - + Answer call + Atsiliepti No comment provided by engineer. - + App build: %@ + Programėlės darinys: %@ No comment provided by engineer. - + App icon + Programėlės piktograma No comment provided by engineer. - + App version + Programėlės versija No comment provided by engineer. - + App version: v%@ + Programėlės versija: v%@ No comment provided by engineer. - + Appearance + Išvaizda No comment provided by engineer. Attach No comment provided by engineer. - + Audio & video calls + Garso ir vaizdo skambučiai No comment provided by engineer. - + Authentication failed + Nepavyko nustatyti tapatybės No comment provided by engineer. Authentication is required before the call is connected, but you may miss calls. No comment provided by engineer. - + Authentication unavailable + Tapatybės nustatymas neprieinamas No comment provided by engineer. @@ -3629,6 +3672,11 @@ SimpleX servers cannot see your profile. \~strike~ No comment provided by engineer. + + Audio and video calls + Garso ir vaizdo skambučiai + No comment provided by engineer. +
diff --git a/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff b/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff index 483d8395e0..1dc0604f52 100644 --- a/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff +++ b/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff @@ -387,6 +387,7 @@ Add welcome message + Welkomst bericht toevoegen No comment provided by engineer. @@ -531,6 +532,7 @@ Audio and video calls + Audio en video oproepen No comment provided by engineer. @@ -600,6 +602,7 @@ Can't delete user profile! + Kan gebruikers profiel niet verwijderen! No comment provided by engineer. @@ -709,6 +712,7 @@ Chinese and Spanish interface + Chinese en Spaanse interface No comment provided by engineer. @@ -768,6 +772,7 @@ Confirm password + Bevestig wachtwoord No comment provided by engineer. @@ -1205,7 +1210,7 @@ Delete user profile? - Gebruikersprofiel verwijderen? + Gebruikers profiel verwijderen? No comment provided by engineer. @@ -1300,6 +1305,7 @@ Don't show again + Niet meer weergeven No comment provided by engineer. @@ -1404,6 +1410,7 @@ Enter password above to show! + Voer hier boven het wachtwoord in om weer te geven! No comment provided by engineer. @@ -1563,6 +1570,7 @@ Error saving user password + Fout bij opslaan gebruikers wachtwoord No comment provided by engineer. @@ -1602,6 +1610,7 @@ Error updating user privacy + Fout bij updaten van gebruikers privacy No comment provided by engineer. @@ -1691,10 +1700,12 @@ Fully re-implemented - work in background! + Volledig opnieuw geïmplementeerd - werk op de achtergrond! No comment provided by engineer. Further reduced battery usage + Verder verminderd batterij verbruik No comment provided by engineer. @@ -1774,6 +1785,7 @@ Group moderation + Groep moderatie No comment provided by engineer. @@ -1793,6 +1805,7 @@ Group welcome message + Groep welkomst bericht No comment provided by engineer. @@ -1817,10 +1830,12 @@ Hidden chat profiles + Verborgen chat profielen No comment provided by engineer. Hidden profile password + Verborgen profiel wachtwoord No comment provided by engineer. @@ -1835,6 +1850,7 @@ Hide profile + Profiel verbergen No comment provided by engineer. @@ -1962,6 +1978,11 @@ Onjuiste beveiligingscode! No comment provided by engineer. + + Initial role + Initiële rol + No comment provided by engineer. + Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) Installeer [SimpleX Chat voor terminal](https://github.com/simplex-chat/simplex-chat) @@ -2141,6 +2162,7 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen. Make profile private! + Profiel privé maken! No comment provided by engineer. @@ -2270,6 +2292,7 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen. Muted when inactive! + Gedempt wanneer inactief! No comment provided by engineer. @@ -2376,6 +2399,9 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen.Now admins can: - delete members' messages. - disable members ("observer" role) + Nu kunnen beheerders: +- berichten van leden verwijderen. +- schakel leden uit ("waarnemer" rol) No comment provided by engineer. @@ -2420,7 +2446,7 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen. Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**. - Alleen client apparaten slaan gebruikersprofielen, contacten, groepen en berichten op die zijn verzonden met **2-laags end-to-end-codering**. + Alleen client apparaten slaan gebruikers profielen, contacten, groepen en berichten op die zijn verzonden met **2-laags end-to-end-codering**. No comment provided by engineer. @@ -2480,7 +2506,7 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen. Open user profiles - Gebruikersprofielen openen + Gebruikers profielen openen authentication reason @@ -2505,6 +2531,7 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen. Password to show + Wachtwoord om weer te geven No comment provided by engineer. @@ -2659,6 +2686,7 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen. Protect your chat profiles with a password! + Bescherm je chat profielen met een wachtwoord! No comment provided by engineer. @@ -2858,6 +2886,7 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen. Save and update group profile + Groep profiel opslaan en bijwerken No comment provided by engineer. @@ -2887,6 +2916,7 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen. Save profile password + Bewaar profiel wachtwoord No comment provided by engineer. @@ -2896,10 +2926,12 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen. Save servers? + Servers opslaan? No comment provided by engineer. Save welcome message? + Welkomst bericht opslaan? No comment provided by engineer. @@ -3054,6 +3086,7 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen. Set the message shown to new members! + Stel het getoonde bericht in voor nieuwe leden! No comment provided by engineer. @@ -3238,6 +3271,7 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen. Tap to activate profile. + Tik om profiel te activeren. No comment provided by engineer. @@ -3367,10 +3401,12 @@ We zullen serverredundantie toevoegen om verloren berichten te voorkomen. There should be at least one user profile. + Er moet ten minste één gebruikers profiel zijn. No comment provided by engineer. There should be at least one visible user profile. + Er moet ten minste één zichtbaar gebruikers profiel zijn. No comment provided by engineer. @@ -3442,6 +3478,7 @@ U wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingesc To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page. + Om uw verborgen profiel te onthullen, voert u een volledig wachtwoord in een zoek veld in op de pagina **Uw chat profielen**. No comment provided by engineer. @@ -3501,6 +3538,7 @@ U wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingesc Unhide + zichtbaar maken No comment provided by engineer. @@ -3612,7 +3650,7 @@ Om verbinding te maken, vraagt u uw contactpersoon om een andere verbinding link User profile - Gebruikersprofiel + Gebruikers profiel No comment provided by engineer. @@ -3697,7 +3735,7 @@ Om verbinding te maken, vraagt u uw contactpersoon om een andere verbinding link Welcome message - Welkoms bericht + Welkomst bericht No comment provided by engineer. @@ -3717,7 +3755,7 @@ Om verbinding te maken, vraagt u uw contactpersoon om een andere verbinding link With optional welcome message. - Met optioneel welkomstbericht. + Met optioneel welkomst bericht. No comment provided by engineer. @@ -3778,6 +3816,8 @@ Om verbinding te maken, vraagt u uw contactpersoon om een andere verbinding link You can hide or mute a user profile - swipe it to the right. SimpleX Lock must be enabled. + U kunt een gebruikers profiel verbergen of dempen - veeg het naar rechts. +SimpleX Lock moet ingeschakeld zijn. No comment provided by engineer. @@ -3897,6 +3937,7 @@ SimpleX Lock must be enabled. You will still receive calls and notifications from muted profiles when they are active. + U ontvangt nog steeds oproepen en meldingen van gedempte profielen wanneer deze actief zijn. No comment provided by engineer. @@ -4075,7 +4116,7 @@ SimpleX servers kunnen uw profiel niet zien. above, then choose: - hierboven, kies dan: + hier boven, kies dan: No comment provided by engineer. @@ -4450,7 +4491,7 @@ SimpleX servers kunnen uw profiel niet zien. observer - waarnemer + Waarnemer member role @@ -4481,7 +4522,7 @@ SimpleX servers kunnen uw profiel niet zien. owner - eigenaar + Eigenaar member role diff --git a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff index 1c469687bb..2f838981a1 100644 --- a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff @@ -387,6 +387,7 @@ Add welcome message + Добавить приветственное сообщение No comment provided by engineer. @@ -531,6 +532,7 @@ Audio and video calls + Аудио и видео звонки No comment provided by engineer. @@ -540,6 +542,7 @@ Authentication is required before the call is connected, but you may miss calls. + Аутентификация требуется до того, как звонок соединится, но вы можете пропустить звонки. No comment provided by engineer. @@ -599,6 +602,7 @@ Can't delete user profile! + Нельзя удалить профиль пользователя! No comment provided by engineer. @@ -708,6 +712,7 @@ Chinese and Spanish interface + Китайский и Испанский интерфейс No comment provided by engineer. @@ -767,6 +772,7 @@ Confirm password + Подтвердить пароль No comment provided by engineer. @@ -1299,6 +1305,7 @@ Don't show again + Не показывать No comment provided by engineer. @@ -1403,6 +1410,7 @@ Enter password above to show! + Введите пароль выше, чтобы раскрыть! No comment provided by engineer. @@ -1562,6 +1570,7 @@ Error saving user password + Ошибка при сохранении пароля пользователя No comment provided by engineer. @@ -1601,6 +1610,7 @@ Error updating user privacy + Ошибка при обновлении конфиденциальности No comment provided by engineer. @@ -1690,10 +1700,12 @@ Fully re-implemented - work in background! + Полностью обновлены - работают в фоне! No comment provided by engineer. Further reduced battery usage + Уменьшенное потребление батареи No comment provided by engineer. @@ -1773,6 +1785,7 @@ Group moderation + Модерация группы No comment provided by engineer. @@ -1792,6 +1805,7 @@ Group welcome message + Приветственное сообщение группы No comment provided by engineer. @@ -1816,10 +1830,12 @@ Hidden chat profiles + Скрытые профили чата No comment provided by engineer. Hidden profile password + Пароль скрытого профиля No comment provided by engineer. @@ -1834,6 +1850,7 @@ Hide profile + Скрыть профиль No comment provided by engineer. @@ -1961,6 +1978,11 @@ Неправильный код безопасности! No comment provided by engineer. + + Initial role + Роль при вступлении + No comment provided by engineer. + Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) [SimpleX Chat для терминала](https://github.com/simplex-chat/simplex-chat) @@ -1980,6 +2002,7 @@ Interface + Интерфейс No comment provided by engineer. @@ -2139,6 +2162,7 @@ We will be adding server redundancy to prevent lost messages. Make profile private! + Сделайте профиль скрытым! No comment provided by engineer. @@ -2268,6 +2292,7 @@ We will be adding server redundancy to prevent lost messages. Muted when inactive! + Без звука, когда не активный! No comment provided by engineer. @@ -2374,6 +2399,9 @@ We will be adding server redundancy to prevent lost messages. Now admins can: - delete members' messages. - disable members ("observer" role) + Теперь админы могут: +- удалять сообщения членов. +- приостанавливать членов (роль "наблюдатель") No comment provided by engineer. @@ -2503,6 +2531,7 @@ We will be adding server redundancy to prevent lost messages. Password to show + Пароль чтобы раскрыть No comment provided by engineer. @@ -2657,6 +2686,7 @@ We will be adding server redundancy to prevent lost messages. Protect your chat profiles with a password! + Защитите ваши профили чата паролем! No comment provided by engineer. @@ -2856,6 +2886,7 @@ We will be adding server redundancy to prevent lost messages. Save and update group profile + Сохранить сообщение и обновить группу No comment provided by engineer. @@ -2885,6 +2916,7 @@ We will be adding server redundancy to prevent lost messages. Save profile password + Сохранить пароль профиля No comment provided by engineer. @@ -2894,10 +2926,12 @@ We will be adding server redundancy to prevent lost messages. Save servers? + Сохранить серверы? No comment provided by engineer. Save welcome message? + Сохранить приветственное сообщение? No comment provided by engineer. @@ -3052,6 +3086,7 @@ We will be adding server redundancy to prevent lost messages. Set the message shown to new members! + Установить сообщение для новых членов группы! No comment provided by engineer. @@ -3091,6 +3126,7 @@ We will be adding server redundancy to prevent lost messages. Show calls in phone history + Показать звонки в истории телефона No comment provided by engineer. @@ -3235,6 +3271,7 @@ We will be adding server redundancy to prevent lost messages. Tap to activate profile. + Нажмите, чтобы сделать профиль активным. No comment provided by engineer. @@ -3364,10 +3401,12 @@ We will be adding server redundancy to prevent lost messages. There should be at least one user profile. + Должен быть хотя бы один профиль пользователя. No comment provided by engineer. There should be at least one visible user profile. + Должен быть хотя бы один открытый профиль пользователя. No comment provided by engineer. @@ -3439,6 +3478,7 @@ You will be prompted to complete authentication before this feature is enabled.< To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page. + Чтобы показать Ваш скрытый профиль, введите его пароль в поле поиска на странице **Ваши профили чата**. No comment provided by engineer. @@ -3498,10 +3538,12 @@ You will be prompted to complete authentication before this feature is enabled.< Unhide + Раскрыть No comment provided by engineer. Unknown caller + Неизвестный звонок callkit banner @@ -3516,6 +3558,7 @@ You will be prompted to complete authentication before this feature is enabled.< Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions. + Если вы не используете интерфейс iOS, включите режим Не отвлекать, чтобы звонок не прерывался. No comment provided by engineer. @@ -3597,6 +3640,7 @@ To connect, please ask your contact to create another connection link and check Use iOS call interface + Использовать интерфейс iOS для звонков No comment provided by engineer. @@ -3761,6 +3805,7 @@ To connect, please ask your contact to create another connection link and check You can accept calls from lock screen, without device and app authentication. + Вы можете принимать звонки на экране блокировки, без аутентификации. No comment provided by engineer. @@ -3771,6 +3816,8 @@ To connect, please ask your contact to create another connection link and check You can hide or mute a user profile - swipe it to the right. SimpleX Lock must be enabled. + Вы можете скрыть профиль или выключить уведомления - потяните его вправо. +Блокировка SimpleX должна быть включена. No comment provided by engineer. @@ -3890,6 +3937,7 @@ SimpleX Lock must be enabled. You will still receive calls and notifications from muted profiles when they are active. + Вы все равно получите звонки и уведомления в профилях без звука, когда они активные. No comment provided by engineer. diff --git a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff index 4c78565cd7..1eaacd0164 100644 --- a/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff +++ b/apps/ios/SimpleX Localizations/zh-Hans.xcloc/Localized Contents/zh-Hans.xliff @@ -39,7 +39,7 @@ !1 colored! - !1 色! + !1 种彩色! No comment provided by engineer. @@ -387,6 +387,7 @@ Add welcome message + 添加欢迎信息 No comment provided by engineer. @@ -531,6 +532,7 @@ Audio and video calls + 音视频通话 No comment provided by engineer. @@ -560,7 +562,7 @@ Automatically - 自动地 + 自动 No comment provided by engineer. @@ -590,7 +592,7 @@ Call already ended! - 通话已经结束! + 通话已结束! No comment provided by engineer. @@ -600,6 +602,7 @@ Can't delete user profile! + 无法删除用户个人资料! No comment provided by engineer. @@ -709,6 +712,7 @@ Chinese and Spanish interface + 中文和西班牙文界面 No comment provided by engineer. @@ -768,6 +772,7 @@ Confirm password + 确认密码 No comment provided by engineer. @@ -1300,6 +1305,7 @@ Don't show again + 不再显示 No comment provided by engineer. @@ -1404,6 +1410,7 @@ Enter password above to show! + 在上面输入密码以显示! No comment provided by engineer. @@ -1563,6 +1570,7 @@ Error saving user password + 保存用户密码时出错 No comment provided by engineer. @@ -1602,6 +1610,7 @@ Error updating user privacy + 更新用户隐私时出错 No comment provided by engineer. @@ -1691,10 +1700,12 @@ Fully re-implemented - work in background! + 完全重新实现 - 在后台工作! No comment provided by engineer. Further reduced battery usage + 进一步减少电池使用 No comment provided by engineer. @@ -1774,6 +1785,7 @@ Group moderation + 小组审核 No comment provided by engineer. @@ -1793,6 +1805,7 @@ Group welcome message + 群欢迎词 No comment provided by engineer. @@ -1812,15 +1825,17 @@ Hidden - 已隐藏 + 隐藏 No comment provided by engineer. Hidden chat profiles + 隐藏的聊天资料 No comment provided by engineer. Hidden profile password + 隐藏的个人资料密码 No comment provided by engineer. @@ -1835,6 +1850,7 @@ Hide profile + 隐藏个人资料 No comment provided by engineer. @@ -1962,6 +1978,11 @@ 安全码不正确! No comment provided by engineer. + + Initial role + 初始角色 + No comment provided by engineer. + Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) 安装[用于终端的 SimpleX Chat](https://github.com/simplex-chat/simplex-chat) @@ -2141,6 +2162,7 @@ We will be adding server redundancy to prevent lost messages. Make profile private! + 将个人资料设为私密! No comment provided by engineer. @@ -2235,7 +2257,7 @@ We will be adding server redundancy to prevent lost messages. Migration failed. Tap **Skip** below to continue using the current database. Please report the issue to the app developers via chat or email [chat@simplex.chat](mailto:chat@simplex.chat). - 迁移失败。点击下面的 **Skip** 继续使用当前数据库。请通过聊天或电子邮件 [chat@simplex.chat](mailto:chat@simplex.chat) 将问题报告给应用程序开发人员。 + 迁移失败。点击下面的 **Skip** 继续使用当前数据库。请通过通过聊天或电邮 [chat@simplex.chat](mailto:chat@simplex.chat) 将问题报告给应用程序开发人员。 No comment provided by engineer. @@ -2270,6 +2292,7 @@ We will be adding server redundancy to prevent lost messages. Muted when inactive! + 不活动时静音! No comment provided by engineer. @@ -2376,11 +2399,14 @@ We will be adding server redundancy to prevent lost messages. Now admins can: - delete members' messages. - disable members ("observer" role) + 现在管理员可以: +- 删除成员的消息。 +- 禁用成员(“观察员”角色) No comment provided by engineer. Off (Local) - 关闭 (本地) + 关闭(本地) No comment provided by engineer. @@ -2505,6 +2531,7 @@ We will be adding server redundancy to prevent lost messages. Password to show + 显示密码 No comment provided by engineer. @@ -2534,7 +2561,7 @@ We will be adding server redundancy to prevent lost messages. Periodically - 定期地 + 定期 No comment provided by engineer. @@ -2659,6 +2686,7 @@ We will be adding server redundancy to prevent lost messages. Protect your chat profiles with a password! + 使用密码保护您的聊天资料! No comment provided by engineer. @@ -2763,7 +2791,7 @@ We will be adding server redundancy to prevent lost messages. Required - 必要 + 必须 No comment provided by engineer. @@ -2858,6 +2886,7 @@ We will be adding server redundancy to prevent lost messages. Save and update group profile + 保存和更新组配置文件 No comment provided by engineer. @@ -2887,6 +2916,7 @@ We will be adding server redundancy to prevent lost messages. Save profile password + 保存个人资料密码 No comment provided by engineer. @@ -2896,10 +2926,12 @@ We will be adding server redundancy to prevent lost messages. Save servers? + 保存服务器? No comment provided by engineer. Save welcome message? + 保存欢迎信息? No comment provided by engineer. @@ -3054,6 +3086,7 @@ We will be adding server redundancy to prevent lost messages. Set the message shown to new members! + 设置向新成员显示的消息! No comment provided by engineer. @@ -3238,6 +3271,7 @@ We will be adding server redundancy to prevent lost messages. Tap to activate profile. + 点击以激活个人资料。 No comment provided by engineer. @@ -3332,7 +3366,7 @@ We will be adding server redundancy to prevent lost messages. The message will be marked as moderated for all members. - 该消息将对所有成员显示为已审核。 + 该消息将对所有成员标记为已被管理员移除。 No comment provided by engineer. @@ -3367,10 +3401,12 @@ We will be adding server redundancy to prevent lost messages. There should be at least one user profile. + 应该至少有一个用户资料。 No comment provided by engineer. There should be at least one visible user profile. + 应该至少有一个可见的用户资料。 No comment provided by engineer. @@ -3442,6 +3478,7 @@ You will be prompted to complete authentication before this feature is enabled.< To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page. + 要显示您的隐藏的个人资料,请在**您的聊天个人资料**页面的搜索字段中输入完整密码。 No comment provided by engineer. @@ -3471,7 +3508,7 @@ You will be prompted to complete authentication before this feature is enabled.< Turn off - 关掉 + 关闭 No comment provided by engineer. @@ -3501,6 +3538,7 @@ You will be prompted to complete authentication before this feature is enabled.< Unhide + 取消隐藏 No comment provided by engineer. @@ -3778,6 +3816,8 @@ To connect, please ask your contact to create another connection link and check You can hide or mute a user profile - swipe it to the right. SimpleX Lock must be enabled. + 您可以隐藏或静音用户个人资料——只需向右滑动。 +必须启用 SimpleX Lock。 No comment provided by engineer. @@ -3897,6 +3937,7 @@ SimpleX Lock must be enabled. You will still receive calls and notifications from muted profiles when they are active. + 当静音配置文件处于活动状态时,您仍会收到来自静音配置文件的电话和通知。 No comment provided by engineer. @@ -4055,7 +4096,7 @@ SimpleX 服务器无法看到您的资料。 [Send us email](mailto:chat@simplex.chat) - [发送邮件至](mailto:chat@simplex.chat) + [给我们发电邮](mailto:chat@simplex.chat) No comment provided by engineer. @@ -4160,7 +4201,7 @@ SimpleX 服务器无法看到您的资料。 colored - 有色 + 彩色 No comment provided by engineer. @@ -4471,7 +4512,7 @@ SimpleX 服务器无法看到您的资料。 on - + 开启 group pref value @@ -4481,7 +4522,7 @@ SimpleX 服务器无法看到您的资料。 owner - 所有者 + 群主 member role diff --git a/apps/ios/SimpleX NSE/NotificationService.swift b/apps/ios/SimpleX NSE/NotificationService.swift index 3740ba464b..27384f1575 100644 --- a/apps/ios/SimpleX NSE/NotificationService.swift +++ b/apps/ios/SimpleX NSE/NotificationService.swift @@ -204,7 +204,7 @@ var xftpConfig: XFTPFileConfig? = getXFTPCfg() func startChat() -> DBMigrationResult? { hs_init(0, nil) if chatStarted { return .ok } - let (_, dbStatus) = chatMigrateInit() + let (_, dbStatus) = chatMigrateInit(confirmMigrations: defaultMigrationConfirmation()) if dbStatus != .ok { resetChatCtrl() return dbStatus diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 1ff2641c22..08b902463b 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -58,6 +58,7 @@ 5C65DAF529CBA429003CEE45 /* libHSsimplex-chat-4.6.0.0-KxI2qGrpKDHEZQGy0eoUXU.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C65DAF029CBA429003CEE45 /* libHSsimplex-chat-4.6.0.0-KxI2qGrpKDHEZQGy0eoUXU.a */; }; 5C65DAF629CBA429003CEE45 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C65DAF129CBA429003CEE45 /* libgmpxx.a */; }; 5C65DAF729CBA429003CEE45 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C65DAF229CBA429003CEE45 /* libffi.a */; }; + 5C65DAF929D0CC20003CEE45 /* DeveloperView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C65DAF829D0CC20003CEE45 /* DeveloperView.swift */; }; 5C65F343297D45E100B67AF3 /* VersionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C65F341297D3F3600B67AF3 /* VersionView.swift */; }; 5C6AD81327A834E300348BD7 /* NewChatButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C6AD81227A834E300348BD7 /* NewChatButton.swift */; }; 5C6BA667289BD954009B8ECC /* DismissSheets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C6BA666289BD954009B8ECC /* DismissSheets.swift */; }; @@ -294,6 +295,7 @@ 5C65DAF029CBA429003CEE45 /* libHSsimplex-chat-4.6.0.0-KxI2qGrpKDHEZQGy0eoUXU.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-4.6.0.0-KxI2qGrpKDHEZQGy0eoUXU.a"; sourceTree = ""; }; 5C65DAF129CBA429003CEE45 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; 5C65DAF229CBA429003CEE45 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; + 5C65DAF829D0CC20003CEE45 /* DeveloperView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperView.swift; sourceTree = ""; }; 5C65F341297D3F3600B67AF3 /* VersionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionView.swift; sourceTree = ""; }; 5C6AD81227A834E300348BD7 /* NewChatButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewChatButton.swift; sourceTree = ""; }; 5C6BA666289BD954009B8ECC /* DismissSheets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DismissSheets.swift; sourceTree = ""; }; @@ -691,6 +693,7 @@ 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */, 18415845648CA4F5A8BCA272 /* UserProfilesView.swift */, 5C65F341297D3F3600B67AF3 /* VersionView.swift */, + 5C65DAF829D0CC20003CEE45 /* DeveloperView.swift */, ); path = UserSettings; sourceTree = ""; @@ -1022,6 +1025,7 @@ 5C93292F29239A170090FFF9 /* SMPServersView.swift in Sources */, 5CB924D727A8563F00ACCCDD /* SettingsView.swift in Sources */, 5CEACCE327DE9246000BD591 /* ComposeView.swift in Sources */, + 5C65DAF929D0CC20003CEE45 /* DeveloperView.swift in Sources */, 5C36027327F47AD5009F19D9 /* AppDelegate.swift in Sources */, 5CB924E127A867BA00ACCCDD /* UserProfile.swift in Sources */, 5CB0BA9A2827FD8800B3292C /* HowItWorks.swift in Sources */, @@ -1396,7 +1400,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 132; + CURRENT_PROJECT_VERSION = 134; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; ENABLE_PREVIEWS = YES; @@ -1438,7 +1442,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 132; + CURRENT_PROJECT_VERSION = 134; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; ENABLE_PREVIEWS = YES; @@ -1518,7 +1522,7 @@ CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 132; + CURRENT_PROJECT_VERSION = 134; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; GENERATE_INFOPLIST_FILE = YES; @@ -1550,7 +1554,7 @@ CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 132; + CURRENT_PROJECT_VERSION = 134; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; GENERATE_INFOPLIST_FILE = YES; diff --git a/apps/ios/SimpleXChat/API.swift b/apps/ios/SimpleXChat/API.swift index 11eec7f873..a400a82fb4 100644 --- a/apps/ios/SimpleXChat/API.swift +++ b/apps/ios/SimpleXChat/API.swift @@ -17,7 +17,7 @@ public func getChatCtrl(_ useKey: String? = nil) -> chat_ctrl { fatalError("chat controller not initialized") } -public func chatMigrateInit(_ useKey: String? = nil) -> (Bool, DBMigrationResult) { +public func chatMigrateInit(_ useKey: String? = nil, confirmMigrations: MigrationConfirmation? = nil) -> (Bool, DBMigrationResult) { if let res = migrationResult { return res } let dbPath = getAppDatabasePath().path var dbKey = "" @@ -34,12 +34,14 @@ public func chatMigrateInit(_ useKey: String? = nil) -> (Bool, DBMigrationResult dbKey = key } } - logger.debug("chatMigrateInit DB path: \(dbPath)") + let confirm = confirmMigrations ?? defaultMigrationConfirmation() + logger.debug("chatMigrateInit DB path: \(dbPath), confirm: \(confirm.rawValue)") // logger.debug("chatMigrateInit DB key: \(dbKey)") var cPath = dbPath.cString(using: .utf8)! var cKey = dbKey.cString(using: .utf8)! + var cConfirm = confirm.rawValue.cString(using: .utf8)! // the last parameter of chat_migrate_init is used to return the pointer to chat controller - let cjson = chat_migrate_init(&cPath, &cKey, &chatController)! + let cjson = chat_migrate_init(&cPath, &cKey, &cConfirm, &chatController)! let dbRes = dbMigrationResult(fromCString(cjson)) let encrypted = dbKey != "" let keychainErr = dbRes == .ok && useKeychain && encrypted && !setDatabaseKey(dbKey) @@ -207,12 +209,40 @@ func chatErrorString(_ err: ChatError) -> String { public enum DBMigrationResult: Decodable, Equatable { case ok + case invalidConfirmation case errorNotADatabase(dbFile: String) - case error(dbFile: String, migrationError: String) + case errorMigration(dbFile: String, migrationError: MigrationError) + case errorSQL(dbFile: String, migrationSQLError: String) case errorKeychain case unknown(json: String) } +public enum MigrationConfirmation: String { + case yesUp + case yesUpDown + case error +} + +public func defaultMigrationConfirmation() -> MigrationConfirmation { + confirmDBUpgradesGroupDefault.get() ? .error : .yesUp +} + +public enum MigrationError: Decodable, Equatable { + case upgrade(upMigrations: [UpMigration]) + case downgrade(downMigrations: [String]) + case migrationError(mtrError: MTRError) +} + +public struct UpMigration: Decodable, Equatable { + public var upName: String +// public var withDown: Bool +} + +public enum MTRError: Decodable, Equatable { + case noDown(dbMigrations: [String]) + case different(appMigration: String, dbMigration: String) +} + func dbMigrationResult(_ s: String) -> DBMigrationResult { let d = s.data(using: .utf8)! // TODO is there a way to do it without copying the data? e.g: diff --git a/apps/ios/SimpleXChat/APITypes.swift b/apps/ios/SimpleXChat/APITypes.swift index 0f9dffedb0..246aea2309 100644 --- a/apps/ios/SimpleXChat/APITypes.swift +++ b/apps/ios/SimpleXChat/APITypes.swift @@ -471,7 +471,7 @@ public enum ChatResponse: Decodable, Error { case ntfMessages(user_: User?, connEntity: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo]) case newContactConnection(user: User, connection: PendingContactConnection) case contactConnectionDeleted(user: User, connection: PendingContactConnection) - case versionInfo(versionInfo: CoreVersionInfo) + case versionInfo(versionInfo: CoreVersionInfo, chatMigrations: [UpMigration], agentMigrations: [UpMigration]) case cmdOk(user: User?) case chatCmdError(user_: User?, chatError: ChatError) case chatError(user_: User?, chatError: ChatError) @@ -690,7 +690,7 @@ public enum ChatResponse: Decodable, Error { case let .ntfMessages(u, connEntity, msgTs, ntfMessages): return withUser(u, "connEntity: \(String(describing: connEntity))\nmsgTs: \(String(describing: msgTs))\nntfMessages: \(String(describing: ntfMessages))") case let .newContactConnection(u, connection): return withUser(u, String(describing: connection)) case let .contactConnectionDeleted(u, connection): return withUser(u, String(describing: connection)) - case let .versionInfo(versionInfo): return String(describing: versionInfo) + case let .versionInfo(versionInfo, chatMigrations, agentMigrations): return "\(String(describing: versionInfo))\n\nchat migrations: \(chatMigrations.map(\.upName))\n\nagent migrations: \(agentMigrations.map(\.upName))" case .cmdOk: return noDetails case let .chatCmdError(u, chatError): return withUser(u, String(describing: chatError)) case let .chatError(u, chatError): return withUser(u, String(describing: chatError)) diff --git a/apps/ios/SimpleXChat/AppGroup.swift b/apps/ios/SimpleXChat/AppGroup.swift index a39419b438..a738dd5865 100644 --- a/apps/ios/SimpleXChat/AppGroup.swift +++ b/apps/ios/SimpleXChat/AppGroup.swift @@ -29,6 +29,7 @@ let GROUP_DEFAULT_NETWORK_TCP_KEEP_CNT = "networkTCPKeepCnt" let GROUP_DEFAULT_INCOGNITO = "incognito" let GROUP_DEFAULT_STORE_DB_PASSPHRASE = "storeDBPassphrase" let GROUP_DEFAULT_INITIAL_RANDOM_DB_PASSPHRASE = "initialRandomDBPassphrase" +public let GROUP_DEFAULT_CONFIRM_DB_UPGRADES = "confirmDBUpgrades" public let GROUP_DEFAULT_CALL_KIT_ENABLED = "callKitEnabled" public let GROUP_DEFAULT_XFTP_SEND_ENABLED = "xftpSendEnabled" @@ -53,8 +54,9 @@ public func registerGroupDefaults() { GROUP_DEFAULT_INITIAL_RANDOM_DB_PASSPHRASE: false, GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES: true, GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE: false, + GROUP_DEFAULT_CONFIRM_DB_UPGRADES: false, GROUP_DEFAULT_CALL_KIT_ENABLED: true, - GROUP_DEFAULT_XFTP_SEND_ENABLED: false + GROUP_DEFAULT_XFTP_SEND_ENABLED: false, ]) } @@ -123,6 +125,8 @@ public let storeDBPassphraseGroupDefault = BoolDefault(defaults: groupDefaults, public let initialRandomDBPassphraseGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_INITIAL_RANDOM_DB_PASSPHRASE) +public let confirmDBUpgradesGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_CONFIRM_DB_UPGRADES) + public let callKitEnabledGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_CALL_KIT_ENABLED) public let xftpSendEnabledGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_XFTP_SEND_ENABLED) diff --git a/apps/ios/SimpleXChat/Notifications.swift b/apps/ios/SimpleXChat/Notifications.swift index 035e849ff5..2e8c5c7124 100644 --- a/apps/ios/SimpleXChat/Notifications.swift +++ b/apps/ios/SimpleXChat/Notifications.swift @@ -127,12 +127,16 @@ public func createErrorNtf(_ dbStatus: DBMigrationResult) -> UNMutableNotificati switch dbStatus { case .errorNotADatabase: title = NSLocalizedString("Encrypted message: no passphrase", comment: "notification") - case .error: + case .errorMigration: + title = NSLocalizedString("Encrypted message: database migration error", comment: "notification") + case .errorSQL: title = NSLocalizedString("Encrypted message: database error", comment: "notification") case .errorKeychain: title = NSLocalizedString("Encrypted message: keychain error", comment: "notification") case .unknown: title = NSLocalizedString("Encrypted message: unexpected error", comment: "notification") + case .invalidConfirmation: + title = NSLocalizedString("Encrypted message or another event", comment: "notification") case .ok: title = NSLocalizedString("Encrypted message or another event", comment: "notification") } diff --git a/apps/ios/SimpleXChat/SimpleX.h b/apps/ios/SimpleXChat/SimpleX.h index 5d5f1e355a..199c688f26 100644 --- a/apps/ios/SimpleXChat/SimpleX.h +++ b/apps/ios/SimpleXChat/SimpleX.h @@ -16,7 +16,7 @@ extern void hs_init(int argc, char **argv[]); typedef void* chat_ctrl; // the last parameter is used to return the pointer to chat controller -extern char *chat_migrate_init(char *path, char *key, chat_ctrl *ctrl); +extern char *chat_migrate_init(char *path, char *key, char *confirm, chat_ctrl *ctrl); extern char *chat_send_cmd(chat_ctrl ctl, char *cmd); extern char *chat_recv_msg(chat_ctrl ctl); extern char *chat_recv_msg_wait(chat_ctrl ctl, int wait); diff --git a/apps/ios/cs.lproj/Localizable.strings b/apps/ios/cs.lproj/Localizable.strings index 5d3abdf919..8a0893d2d6 100644 --- a/apps/ios/cs.lproj/Localizable.strings +++ b/apps/ios/cs.lproj/Localizable.strings @@ -248,6 +248,9 @@ /* No comment provided by engineer. */ "Add to another device" = "Přidat do jiného zařízení"; +/* No comment provided by engineer. */ +"Add welcome message" = "Přidat uvítací zprávu"; + /* member role */ "admin" = "správce"; @@ -338,6 +341,9 @@ /* No comment provided by engineer. */ "Audio & video calls" = "Audio a video hovory"; +/* No comment provided by engineer. */ +"Audio and video calls" = "Hlasové a video hovory"; + /* No comment provided by engineer. */ "audio call (not e2e encrypted)" = "zvukový hovor (nešifrovaný e2e)"; @@ -398,6 +404,9 @@ /* No comment provided by engineer. */ "Calls" = "Hovory"; +/* No comment provided by engineer. */ +"Can't delete user profile!" = "Nemohu smazat uživatelský profil!"; + /* No comment provided by engineer. */ "Can't invite contact!" = "Nelze pozvat kontakt!"; @@ -479,6 +488,9 @@ /* No comment provided by engineer. */ "Check server address and try again." = "Zkontrolujte adresu serveru a zkuste to znovu."; +/* No comment provided by engineer. */ +"Chinese and Spanish interface" = "Čínské a Španělské rozhranní"; + /* No comment provided by engineer. */ "Choose file" = "Vybrat soubor"; @@ -518,6 +530,9 @@ /* No comment provided by engineer. */ "Confirm new passphrase…" = "Potvrdit novou heslovou frázi…"; +/* No comment provided by engineer. */ +"Confirm password" = "Potvrdit heslo"; + /* server test step */ "Connect" = "Připojit"; @@ -884,6 +899,9 @@ /* No comment provided by engineer. */ "Do NOT use SimpleX for emergency calls." = "NEpoužívejte SimpleX pro tísňová volání."; +/* No comment provided by engineer. */ +"Don't show again" = "Znovu neukazuj"; + /* No comment provided by engineer. */ "Duplicate display name!" = "Duplicitní zobrazované jméno!"; @@ -1061,6 +1079,9 @@ /* No comment provided by engineer. */ "Error saving SMP servers" = "Chyba při ukládání serverů SMP"; +/* No comment provided by engineer. */ +"Error saving user password" = "Chyba ukládání hesla uživatele"; + /* No comment provided by engineer. */ "Error sending message" = "Chyba při odesílání zprávy"; @@ -1082,6 +1103,9 @@ /* No comment provided by engineer. */ "Error updating settings" = "Chyba při aktualizaci nastavení"; +/* No comment provided by engineer. */ +"Error updating user privacy" = "Chyba aktualizace soukromí uživatele"; + /* No comment provided by engineer. */ "Error: %@" = "Chyba: %@"; @@ -1133,6 +1157,9 @@ /* No comment provided by engineer. */ "Full name:" = "Celé jméno:"; +/* No comment provided by engineer. */ +"Further reduced battery usage" = "Další snížení spotřeby baterie"; + /* No comment provided by engineer. */ "GIFs and stickers" = "GIFy a nálepky"; @@ -1181,6 +1208,9 @@ /* notification */ "Group message:" = "Skupinová zpráva:"; +/* No comment provided by engineer. */ +"Group moderation" = "Správa skupin"; + /* No comment provided by engineer. */ "Group preferences" = "Předvolby skupiny"; @@ -1193,6 +1223,9 @@ /* snd group event chat item */ "group profile updated" = "profil skupiny aktualizován"; +/* No comment provided by engineer. */ +"Group welcome message" = "Uvítací zpráva skupin"; + /* No comment provided by engineer. */ "Group will be deleted for all members - this cannot be undone!" = "Skupina bude smazána pro všechny členy - nelze to vzít zpět!"; @@ -1205,12 +1238,21 @@ /* No comment provided by engineer. */ "Hidden" = "Skryté"; +/* No comment provided by engineer. */ +"Hidden chat profiles" = "Skryté chat profily"; + +/* No comment provided by engineer. */ +"Hidden profile password" = "Hesla skrytých profilů"; + /* chat item action */ "Hide" = "Skrýt"; /* No comment provided by engineer. */ "Hide app screen in the recent apps." = "Skrytí obrazovky aplikace v posledních aplikacích."; +/* No comment provided by engineer. */ +"Hide profile" = "Skrýt profil"; + /* No comment provided by engineer. */ "How it works" = "Jak to funguje"; @@ -1298,6 +1340,9 @@ /* connection level description */ "indirect (%d)" = "nepřímé (%d)"; +/* No comment provided by engineer. */ +"Initial role" = "Počáteční role"; + /* No comment provided by engineer. */ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "Nainstalujte [SimpleX Chat pro terminál](https://github.com/simplex-chat/simplex-chat)"; @@ -1436,6 +1481,9 @@ /* No comment provided by engineer. */ "Make a private connection" = "Vytvořte si soukromé připojení"; +/* No comment provided by engineer. */ +"Make profile private!" = "Změnit profil na soukromý!"; + /* No comment provided by engineer. */ "Make sure SMP server addresses are in correct format, line separated and are not duplicated (%@)." = "Ujistěte se, že adresy SMP serverů jsou ve správném formátu, oddělené řádky a nejsou duplicitní (%@)."; @@ -1532,6 +1580,9 @@ /* No comment provided by engineer. */ "Mute" = "Ztlumit"; +/* No comment provided by engineer. */ +"Muted when inactive!" = "Ztlumit při neaktivitě!"; + /* No comment provided by engineer. */ "Name" = "Jméno"; @@ -1604,6 +1655,9 @@ /* No comment provided by engineer. */ "Notifications are disabled!" = "Oznámení jsou zakázána!"; +/* No comment provided by engineer. */ +"Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "Nyní mohou správci:\n- mazat zprávy členů.\n- zakázat členy (role \"pozorovatel\")"; + /* member role */ "observer" = "pozorovatel"; @@ -1695,6 +1749,9 @@ /* member role */ "owner" = "vlastník"; +/* No comment provided by engineer. */ +"Password to show" = "Heslo k zobrazení"; + /* No comment provided by engineer. */ "Paste" = "Vložit"; @@ -1794,6 +1851,9 @@ /* No comment provided by engineer. */ "Protect app screen" = "Ochrana obrazovky aplikace"; +/* No comment provided by engineer. */ +"Protect your chat profiles with a password!" = "Chraňte své chat profily heslem!"; + /* No comment provided by engineer. */ "Protocol timeout" = "Časový limit protokolu"; @@ -1926,6 +1986,9 @@ /* No comment provided by engineer. */ "Save and notify group members" = "Uložit a upozornit členy skupiny"; +/* No comment provided by engineer. */ +"Save and update group profile" = "Uložit a aktualizovat profil skupiny"; + /* No comment provided by engineer. */ "Save archive" = "Uložit archiv"; @@ -1941,9 +2004,18 @@ /* No comment provided by engineer. */ "Save preferences?" = "Uložit předvolby?"; +/* No comment provided by engineer. */ +"Save profile password" = "Uložit heslo profilu"; + /* No comment provided by engineer. */ "Save servers" = "Uložit servery"; +/* No comment provided by engineer. */ +"Save servers?" = "Uložit servery?"; + +/* No comment provided by engineer. */ +"Save welcome message?" = "Uložit uvítací zprávu?"; + /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Uložené servery WebRTC ICE budou odstraněny"; @@ -2040,6 +2112,9 @@ /* No comment provided by engineer. */ "Set passphrase to export" = "Nastavení přístupové fráze pro export"; +/* No comment provided by engineer. */ +"Set the message shown to new members!" = "Nastavte zprávu zobrazenou novým členům!"; + /* No comment provided by engineer. */ "Set timeouts for proxy/VPN" = "Nastavit časové limity pro proxy/VPN"; @@ -2145,6 +2220,9 @@ /* No comment provided by engineer. */ "Tap button " = "Klepněte na tlačítko "; +/* No comment provided by engineer. */ +"Tap to activate profile." = "Klepnutím aktivujete profil."; + /* No comment provided by engineer. */ "Tap to join" = "Klepnutím se připojíte"; @@ -2232,6 +2310,9 @@ /* No comment provided by engineer. */ "Theme" = "Téma"; +/* No comment provided by engineer. */ +"There should be at least one user profile." = "Měl by tam být alespoň jeden uživatelský profil."; + /* No comment provided by engineer. */ "This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Tuto akci nelze vrátit zpět - všechny přijaté a odeslané soubory a média budou smazány. Obrázky s nízkým rozlišením zůstanou zachovány."; diff --git a/apps/ios/de.lproj/Localizable.strings b/apps/ios/de.lproj/Localizable.strings index 48ca38b076..031e6cc992 100644 --- a/apps/ios/de.lproj/Localizable.strings +++ b/apps/ios/de.lproj/Localizable.strings @@ -248,6 +248,9 @@ /* No comment provided by engineer. */ "Add to another device" = "Einem anderen Gerät hinzufügen"; +/* No comment provided by engineer. */ +"Add welcome message" = "Fügen Sie eine Begrüßungsmeldung hinzu"; + /* member role */ "admin" = "Admin"; @@ -338,6 +341,9 @@ /* No comment provided by engineer. */ "Audio & video calls" = "Audio- & Videoanrufe"; +/* No comment provided by engineer. */ +"Audio and video calls" = "Audio- und Videoanrufe"; + /* No comment provided by engineer. */ "audio call (not e2e encrypted)" = "Audioanruf (nicht E2E verschlüsselt)"; @@ -398,6 +404,9 @@ /* No comment provided by engineer. */ "Calls" = "Anrufe"; +/* No comment provided by engineer. */ +"Can't delete user profile!" = "Das Benutzerprofil kann nicht gelöscht werden!"; + /* No comment provided by engineer. */ "Can't invite contact!" = "Kontakt kann nicht eingeladen werden!"; @@ -479,6 +488,9 @@ /* No comment provided by engineer. */ "Check server address and try again." = "Überprüfen Sie die Serveradresse und versuchen Sie es nochmal."; +/* No comment provided by engineer. */ +"Chinese and Spanish interface" = "Chinesische und spanische Bedienoberfläche"; + /* No comment provided by engineer. */ "Choose file" = "Datei auswählen"; @@ -518,6 +530,9 @@ /* No comment provided by engineer. */ "Confirm new passphrase…" = "Neues Passwort bestätigen…"; +/* No comment provided by engineer. */ +"Confirm password" = "Bestätigen Sie das Passwort"; + /* server test step */ "Connect" = "Verbinden"; @@ -884,6 +899,9 @@ /* No comment provided by engineer. */ "Do NOT use SimpleX for emergency calls." = "Nutzen Sie SimpleX nicht für Notrufe."; +/* No comment provided by engineer. */ +"Don't show again" = "Nicht nochmals anzeigen"; + /* No comment provided by engineer. */ "Duplicate display name!" = "Doppelter Anzeigename!"; @@ -965,6 +983,9 @@ /* No comment provided by engineer. */ "Enter passphrase…" = "Passwort eingeben…"; +/* No comment provided by engineer. */ +"Enter password above to show!" = "Geben Sie oben das Passwort für die Anzeige an!"; + /* No comment provided by engineer. */ "Enter server manually" = "Geben Sie den Server manuell ein"; @@ -1061,6 +1082,9 @@ /* No comment provided by engineer. */ "Error saving SMP servers" = "Fehler beim Speichern der SMP-Server"; +/* No comment provided by engineer. */ +"Error saving user password" = "Fehler beim Speichern des Benutzer-Passworts"; + /* No comment provided by engineer. */ "Error sending message" = "Fehler beim Senden der Nachricht"; @@ -1082,6 +1106,9 @@ /* No comment provided by engineer. */ "Error updating settings" = "Fehler beim Aktualisieren der Einstellungen"; +/* No comment provided by engineer. */ +"Error updating user privacy" = "Fehler beim Aktualisieren der Benutzer-Privatsphäre"; + /* No comment provided by engineer. */ "Error: %@" = "Fehler: %@"; @@ -1133,6 +1160,12 @@ /* No comment provided by engineer. */ "Full name:" = "Vollständiger Name:"; +/* No comment provided by engineer. */ +"Fully re-implemented - work in background!" = "Komplett neu umgesetzt - arbeitet nun im Hintergrund!"; + +/* No comment provided by engineer. */ +"Further reduced battery usage" = "Weiter reduzierter Batterieverbrauch"; + /* No comment provided by engineer. */ "GIFs and stickers" = "GIFs und Sticker"; @@ -1181,6 +1214,9 @@ /* notification */ "Group message:" = "Grppennachricht:"; +/* No comment provided by engineer. */ +"Group moderation" = "Gruppenmoderation"; + /* No comment provided by engineer. */ "Group preferences" = "Gruppenpräferenzen"; @@ -1193,6 +1229,9 @@ /* snd group event chat item */ "group profile updated" = "Gruppenprofil aktualisiert"; +/* No comment provided by engineer. */ +"Group welcome message" = "Gruppen-Begrüßungsmeldung"; + /* No comment provided by engineer. */ "Group will be deleted for all members - this cannot be undone!" = "Die Gruppe wird für alle Mitglieder gelöscht - dies kann nicht rückgängig gemacht werden!"; @@ -1205,12 +1244,21 @@ /* No comment provided by engineer. */ "Hidden" = "Verborgen"; +/* No comment provided by engineer. */ +"Hidden chat profiles" = "Verborgene Chat-Profile"; + +/* No comment provided by engineer. */ +"Hidden profile password" = "Verborgenes Profil-Passwort"; + /* chat item action */ "Hide" = "Verbergen"; /* No comment provided by engineer. */ "Hide app screen in the recent apps." = "App-Bildschirm in aktuellen Anwendungen verbergen."; +/* No comment provided by engineer. */ +"Hide profile" = "Verberge das Profil"; + /* No comment provided by engineer. */ "How it works" = "Wie es funktioniert"; @@ -1298,6 +1346,9 @@ /* connection level description */ "indirect (%d)" = "indirekt (%d)"; +/* No comment provided by engineer. */ +"Initial role" = "Anfängliche Rolle"; + /* No comment provided by engineer. */ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "Installieren Sie [SimpleX Chat als Terminalanwendung](https://github.com/simplex-chat/simplex-chat)"; @@ -1436,6 +1487,9 @@ /* No comment provided by engineer. */ "Make a private connection" = "Stellen Sie eine private Verbindung her"; +/* No comment provided by engineer. */ +"Make profile private!" = "Erzeugen Sie ein privates Profil!"; + /* No comment provided by engineer. */ "Make sure SMP server addresses are in correct format, line separated and are not duplicated (%@)." = "Stellen Sie sicher, dass die SMP-Server-Adressen das richtige Format haben, zeilenweise getrennt und nicht doppelt vorhanden sind (%@)."; @@ -1532,6 +1586,9 @@ /* No comment provided by engineer. */ "Mute" = "Stummschalten"; +/* No comment provided by engineer. */ +"Muted when inactive!" = "Bei Inaktivität stummgeschaltet!"; + /* No comment provided by engineer. */ "Name" = "Name"; @@ -1604,6 +1661,9 @@ /* No comment provided by engineer. */ "Notifications are disabled!" = "Benachrichtigungen sind deaktiviert!"; +/* No comment provided by engineer. */ +"Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "Administratoren können nun\n- Nachrichten von Gruppenmitgliedern löschen\n- Gruppenmitglieder deaktivieren (\"Beobachter\"-Rolle)"; + /* member role */ "observer" = "Beobachter"; @@ -1695,6 +1755,9 @@ /* member role */ "owner" = "Eigentümer"; +/* No comment provided by engineer. */ +"Password to show" = "Passwort anzeigen"; + /* No comment provided by engineer. */ "Paste" = "Einfügen"; @@ -1794,6 +1857,9 @@ /* No comment provided by engineer. */ "Protect app screen" = "App-Bildschirm schützen"; +/* No comment provided by engineer. */ +"Protect your chat profiles with a password!" = "Schützen Sie Ihre Chat-Profile mit einem Passwort!"; + /* No comment provided by engineer. */ "Protocol timeout" = "Protokollzeitüberschreitung"; @@ -1926,6 +1992,9 @@ /* No comment provided by engineer. */ "Save and notify group members" = "Speichern und Gruppenmitglieder benachrichtigen"; +/* No comment provided by engineer. */ +"Save and update group profile" = "Sichern und aktualisieren des Gruppen-Profils"; + /* No comment provided by engineer. */ "Save archive" = "Archiv speichern"; @@ -1941,9 +2010,18 @@ /* No comment provided by engineer. */ "Save preferences?" = "Präferenzen speichern?"; +/* No comment provided by engineer. */ +"Save profile password" = "Profil-Passwort speichern"; + /* No comment provided by engineer. */ "Save servers" = "Alle Server speichern"; +/* No comment provided by engineer. */ +"Save servers?" = "Alle Server speichern?"; + +/* No comment provided by engineer. */ +"Save welcome message?" = "Begrüßungsmeldung speichern?"; + /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Gespeicherte WebRTC ICE-Server werden entfernt"; @@ -2040,6 +2118,9 @@ /* No comment provided by engineer. */ "Set passphrase to export" = "Passwort für den Export festlegen"; +/* No comment provided by engineer. */ +"Set the message shown to new members!" = "Legen Sie die Nachricht fest, die neuen Mitgliedern angezeigt werden soll!"; + /* No comment provided by engineer. */ "Set timeouts for proxy/VPN" = "Zeitüberschreitungen für Proxy/VPN einstellen"; @@ -2145,6 +2226,9 @@ /* No comment provided by engineer. */ "Tap button " = "Schaltfläche antippen "; +/* No comment provided by engineer. */ +"Tap to activate profile." = "Tippen Sie, um das Profil zu aktivieren."; + /* No comment provided by engineer. */ "Tap to join" = "Zum Beitreten tippen"; @@ -2232,6 +2316,12 @@ /* No comment provided by engineer. */ "Theme" = "Design"; +/* No comment provided by engineer. */ +"There should be at least one user profile." = "Es muss mindestens ein Benutzer-Profil vorhanden sein."; + +/* No comment provided by engineer. */ +"There should be at least one visible user profile." = "Es muss mindestens ein sichtbares Benutzer-Profil vorhanden sein."; + /* No comment provided by engineer. */ "This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Diese Aktion kann nicht rückgängig gemacht werden! Alle empfangenen und gesendeten Dateien und Medien werden gelöscht. Bilder mit niedriger Auflösung bleiben erhalten."; @@ -2274,6 +2364,9 @@ /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "Bitte erlauben Sie die Nutzung des Mikrofons, um Sprachnachrichten aufnehmen zu können."; +/* No comment provided by engineer. */ +"To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "Geben Sie ein vollständiges Passwort in das Suchfeld auf der Seite **Meine Chat-Profile** ein, um Ihr verborgenes Profil zu sehen."; + /* No comment provided by engineer. */ "To support instant push notifications the chat database has to be migrated." = "Um sofortige Push-Benachrichtigungen zu unterstützen, muss die Chat-Datenbank migriert werden."; @@ -2307,6 +2400,9 @@ /* No comment provided by engineer. */ "Unexpected migration state" = "Unerwarteter Migrationsstatus"; +/* No comment provided by engineer. */ +"Unhide" = "Verbergen aufheben"; + /* connection info */ "unknown" = "Unbekannt"; @@ -2511,6 +2607,9 @@ /* No comment provided by engineer. */ "You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." = "Sie können sich auch verbinden, indem Sie auf den Link klicken. Wenn er im Browser geöffnet wird, klicken Sie auf die Schaltfläche **In mobiler App öffnen**."; +/* No comment provided by engineer. */ +"You can hide or mute a user profile - swipe it to the right.\nSimpleX Lock must be enabled." = "Sie können ein Benutzerprofil verbergen oder stummschalten - wischen Sie es nach rechts.\nDafür muss die SimpleX Sperre aktiviert sein."; + /* notification body */ "You can now send messages to %@" = "Sie können nun Nachrichten an %@ versenden"; @@ -2604,6 +2703,9 @@ /* No comment provided by engineer. */ "You will join a group this link refers to and connect to its group members." = "Sie werden der Gruppe beitreten, auf die sich dieser Link bezieht und sich mit deren Gruppenmitgliedern verbinden."; +/* No comment provided by engineer. */ +"You will still receive calls and notifications from muted profiles when they are active." = "Sie können Anrufe und Benachrichtigungen auch von stummgeschalteten Profilen empfangen, solange diese aktiv sind."; + /* No comment provided by engineer. */ "You will stop receiving messages from this group. Chat history will be preserved." = "Sie werden von dieser Gruppe keine Nachrichten mehr erhalten. Der Chatverlauf wird beibehalten."; diff --git a/apps/ios/es.lproj/Localizable.strings b/apps/ios/es.lproj/Localizable.strings index f29712eb89..a4ec19b296 100644 --- a/apps/ios/es.lproj/Localizable.strings +++ b/apps/ios/es.lproj/Localizable.strings @@ -47,7 +47,7 @@ "[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[Comienza en GitHub] (https://github.com/simplex-chat/simplex-chat)"; /* No comment provided by engineer. */ -"**Add new contact**: to create your one-time QR Code for your contact." = "**Añadir nuevo contacto**: para crear tu código QR único o un enlace para tu contacto."; +"**Add new contact**: to create your one-time QR Code for your contact." = "**Añadir nuevo contacto**: para crear tu código QR o enlace de un uso para tu contacto."; /* No comment provided by engineer. */ "**Create link / QR code** for your contact to use." = "**Crea enlace / código QR** para que tu contacto lo use."; @@ -248,6 +248,9 @@ /* No comment provided by engineer. */ "Add to another device" = "Añadir a otro dispositivo"; +/* No comment provided by engineer. */ +"Add welcome message" = "Agregar mensaje de bienvenida"; + /* member role */ "admin" = "administrador"; @@ -273,7 +276,7 @@ "Allow" = "Permitir"; /* No comment provided by engineer. */ -"Allow disappearing messages only if your contact allows it to you." = "Permitir mensajes temporales sólo si tu contacto también los permite para tí."; +"Allow disappearing messages only if your contact allows it to you." = "Permitir mensajes temporales sólo si tu contacto también los permite."; /* No comment provided by engineer. */ "Allow irreversible message deletion only if your contact allows it to you." = "Permitir la eliminación irreversible de mensajes sólo si tu contacto también lo permite para tí."; @@ -300,7 +303,7 @@ "Allow your contacts to irreversibly delete sent messages." = "Permitir a tus contactos eliminar irreversiblemente los mensajes enviados."; /* No comment provided by engineer. */ -"Allow your contacts to send disappearing messages." = "Permitir a tus contactos enviar mensajes que desaparecen."; +"Allow your contacts to send disappearing messages." = "Permitir a tus contactos enviar mensajes temporales."; /* No comment provided by engineer. */ "Allow your contacts to send voice messages." = "Permitir a tus contactos enviar mensajes de voz."; @@ -312,7 +315,7 @@ "always" = "siempre"; /* No comment provided by engineer. */ -"Always use relay" = "Siempre usa relay"; +"Always use relay" = "Siempre usar relay"; /* No comment provided by engineer. */ "Answer call" = "Responder llamada"; @@ -338,6 +341,9 @@ /* No comment provided by engineer. */ "Audio & video calls" = "Llamadas de audio y vídeo"; +/* No comment provided by engineer. */ +"Audio and video calls" = "Llamadas y videollamadas"; + /* No comment provided by engineer. */ "audio call (not e2e encrypted)" = "llamada de audio (sin cifrado e2e)"; @@ -381,7 +387,7 @@ "Both you and your contact can send voice messages." = "Tanto tú como tu contacto podéis enviar mensajes de voz."; /* No comment provided by engineer. */ -"By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)." = "Por perfil de Chat (por defecto) o [por conexión](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)."; +"By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)." = "Mediante perfil de Chat (por defecto) o [por conexión](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)."; /* No comment provided by engineer. */ "Call already ended!" = "¡La llamada ha terminado!"; @@ -398,6 +404,9 @@ /* No comment provided by engineer. */ "Calls" = "Llamadas"; +/* No comment provided by engineer. */ +"Can't delete user profile!" = "¡No se puede eliminar el perfil!"; + /* No comment provided by engineer. */ "Can't invite contact!" = "¡No se puede invitar el contacto!"; @@ -479,6 +488,9 @@ /* No comment provided by engineer. */ "Check server address and try again." = "Comprueba la dirección del servidor e inténtalo de nuevo."; +/* No comment provided by engineer. */ +"Chinese and Spanish interface" = "Interfaz en chino y español"; + /* No comment provided by engineer. */ "Choose file" = "Elije archivo"; @@ -518,6 +530,9 @@ /* No comment provided by engineer. */ "Confirm new passphrase…" = "Confirme nueva contraseña…"; +/* No comment provided by engineer. */ +"Confirm password" = "Confirmar contraseña"; + /* server test step */ "Connect" = "Conectar"; @@ -537,7 +552,7 @@ "Connect via link / QR code" = "Conectar mediante enlace / Código QR"; /* No comment provided by engineer. */ -"Connect via one-time link?" = "¿Conectar mediante enlace único?"; +"Connect via one-time link?" = "¿Conectar mediante enlace de un uso?"; /* No comment provided by engineer. */ "connected" = "conectado"; @@ -651,7 +666,7 @@ "Create link" = "Crear enlace"; /* No comment provided by engineer. */ -"Create one-time invitation link" = "Crear enlace único de invitación"; +"Create one-time invitation link" = "Crear enlace de invitación de un uso"; /* server test step */ "Create queue" = "Crear cola"; @@ -884,6 +899,9 @@ /* No comment provided by engineer. */ "Do NOT use SimpleX for emergency calls." = "NO uses SimpleX para llamadas de emergencia."; +/* No comment provided by engineer. */ +"Don't show again" = "No mostrar de nuevo"; + /* No comment provided by engineer. */ "Duplicate display name!" = "¡Nombre mostrado duplicado!"; @@ -965,6 +983,9 @@ /* No comment provided by engineer. */ "Enter passphrase…" = "Introduce la contraseña…"; +/* No comment provided by engineer. */ +"Enter password above to show!" = "¡Introduce la contraseña arriba para mostrar!"; + /* No comment provided by engineer. */ "Enter server manually" = "Introduce el servidor manualmente"; @@ -1061,6 +1082,9 @@ /* No comment provided by engineer. */ "Error saving SMP servers" = "Error guardando servidores SMP"; +/* No comment provided by engineer. */ +"Error saving user password" = "Error guardando la contraseña de usuario"; + /* No comment provided by engineer. */ "Error sending message" = "Error enviando mensaje"; @@ -1082,6 +1106,9 @@ /* No comment provided by engineer. */ "Error updating settings" = "Error actualizando configuración"; +/* No comment provided by engineer. */ +"Error updating user privacy" = "Error actualizando la privacidad de usuario"; + /* No comment provided by engineer. */ "Error: %@" = "Error: %@"; @@ -1133,6 +1160,12 @@ /* No comment provided by engineer. */ "Full name:" = "Nombre completo:"; +/* No comment provided by engineer. */ +"Fully re-implemented - work in background!" = "Completamente reimplementado: ¡funciona en segundo plano!"; + +/* No comment provided by engineer. */ +"Further reduced battery usage" = "Consumo de batería reducido aun más"; + /* No comment provided by engineer. */ "GIFs and stickers" = "GIFs y stickers"; @@ -1181,6 +1214,9 @@ /* notification */ "Group message:" = "Mensaje de grupo:"; +/* No comment provided by engineer. */ +"Group moderation" = "Moderación de grupos"; + /* No comment provided by engineer. */ "Group preferences" = "Preferencias del grupo"; @@ -1193,6 +1229,9 @@ /* snd group event chat item */ "group profile updated" = "perfil de grupo actualizado"; +/* No comment provided by engineer. */ +"Group welcome message" = "Mensaje de bienvenida en grupos"; + /* No comment provided by engineer. */ "Group will be deleted for all members - this cannot be undone!" = "El grupo se eliminará para todos los miembros. ¡No puede deshacerse!"; @@ -1205,12 +1244,21 @@ /* No comment provided by engineer. */ "Hidden" = "Oculto"; +/* No comment provided by engineer. */ +"Hidden chat profiles" = "Perfiles Chat ocultos"; + +/* No comment provided by engineer. */ +"Hidden profile password" = "Contraseña de perfil oculto"; + /* chat item action */ "Hide" = "Ocultar"; /* No comment provided by engineer. */ "Hide app screen in the recent apps." = "Ocultar pantalla de aplicaciones en aplicaciones recientes."; +/* No comment provided by engineer. */ +"Hide profile" = "Ocultar perfil"; + /* No comment provided by engineer. */ "How it works" = "Cómo funciona"; @@ -1281,7 +1329,7 @@ "incognito via group link" = "Incógnito mediante enlace de grupo"; /* chat list item description */ -"incognito via one-time link" = "Incógnito mediante enlace único"; +"incognito via one-time link" = "Incógnito mediante enlace de un uso"; /* notification */ "Incoming audio call" = "Llamada entrante"; @@ -1298,6 +1346,9 @@ /* connection level description */ "indirect (%d)" = "indirecto (%d)"; +/* No comment provided by engineer. */ +"Initial role" = "Rol inicial"; + /* No comment provided by engineer. */ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "Instalar [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)"; @@ -1365,7 +1416,7 @@ "Irreversible message deletion is prohibited in this group." = "La eliminación irreversible de mensajes está prohibida en este grupo."; /* No comment provided by engineer. */ -"It allows having many anonymous connections without any shared data between them in a single chat profile." = "Permite tener muchas conexiones anónimas sin datos compartidos entre estas en un único perfil de chat."; +"It allows having many anonymous connections without any shared data between them in a single chat profile." = "Permite tener varias conexiones anónimas sin datos compartidos entre estas en un único perfil de chat."; /* No comment provided by engineer. */ "It can happen when:\n1. The messages expire on the server if they were not received for 30 days,\n2. The server you use to receive the messages from this contact was updated and restarted.\n3. The connection is compromised.\nPlease connect to the developers via Settings to receive the updates about the servers.\nWe will be adding server redundancy to prevent lost messages." = "Esto puede suceder cuando:\n1. Los mensajes caducan en el servidor si no se han recibido durante 30 días.\n2. El servidor que utiliza para recibir los mensajes de este contacto fue actualizado y reiniciado.\n3. La conexión está comprometida.\nPor favor, contacta con los desarrolladores a través del menú Configuración para recibir actualizaciones sobre los servidores.\nAñadiremos redundancia de servidores para evitar la pérdida de mensajes."; @@ -1436,6 +1487,9 @@ /* No comment provided by engineer. */ "Make a private connection" = "Establecer una conexión privada"; +/* No comment provided by engineer. */ +"Make profile private!" = "¡Hacer un perfil privado!"; + /* No comment provided by engineer. */ "Make sure SMP server addresses are in correct format, line separated and are not duplicated (%@)." = "Asegúrate de que las direcciones del servidor SMP tienen el formato correcto, están separadas por líneas y no duplicadas (%@)."; @@ -1532,6 +1586,9 @@ /* No comment provided by engineer. */ "Mute" = "Silenciar"; +/* No comment provided by engineer. */ +"Muted when inactive!" = "¡Silenciado cuando está inactivo!"; + /* No comment provided by engineer. */ "Name" = "Nombre"; @@ -1604,6 +1661,9 @@ /* No comment provided by engineer. */ "Notifications are disabled!" = "¡Las notificaciones están desactivadas!"; +/* No comment provided by engineer. */ +"Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "Ahora los administradores pueden\n- borrar mensajes de los miembros.\n- desactivar el rol a miembros (a rol \"observador\")"; + /* member role */ "observer" = "observador"; @@ -1633,7 +1693,7 @@ "on" = "Activado"; /* No comment provided by engineer. */ -"One-time invitation link" = "Enlace único de invitación"; +"One-time invitation link" = "Enlace único de invitación de un uso"; /* No comment provided by engineer. */ "Onion hosts will be required for connection. Requires enabling VPN." = "Se requieren hosts .onion para la conexión. Requiere activación de la VPN."; @@ -1695,6 +1755,9 @@ /* member role */ "owner" = "propietario"; +/* No comment provided by engineer. */ +"Password to show" = "Contraseña para hacerlo visible"; + /* No comment provided by engineer. */ "Paste" = "Pegar"; @@ -1708,7 +1771,7 @@ "Paste the link you received into the box below to connect with your contact." = "Pega el enlace que has recibido en el recuadro para conectar con tu contacto."; /* No comment provided by engineer. */ -"peer-to-peer" = "entre particulares"; +"peer-to-peer" = "p2p"; /* No comment provided by engineer. */ "People can connect to you only via the links you share." = "Las personas pueden conectarse contigo solo mediante los enlaces que compartes."; @@ -1794,6 +1857,9 @@ /* No comment provided by engineer. */ "Protect app screen" = "Proteger la pantalla de la aplicación"; +/* No comment provided by engineer. */ +"Protect your chat profiles with a password!" = "¡Proteje los perfiles de Chat con contraseña!"; + /* No comment provided by engineer. */ "Protocol timeout" = "Tiempo de espera del protocolo"; @@ -1843,10 +1909,10 @@ "rejected call" = "llamada rechazada"; /* No comment provided by engineer. */ -"Relay server is only used if necessary. Another party can observe your IP address." = "El servidor de retransmisión sólo se utiliza en caso necesario. Una tercera persona puede observar tu dirección IP."; +"Relay server is only used if necessary. Another party can observe your IP address." = "El relay sólo se usa en caso de necesidad. Un tercero podría ver tu IP."; /* No comment provided by engineer. */ -"Relay server protects your IP address, but it can observe the duration of the call." = "El servidor de retransmisión protege tu IP pero puede observar la duración de la llamada."; +"Relay server protects your IP address, but it can observe the duration of the call." = "El servidor relay protege tu IP pero puede observar la duración de la llamada."; /* No comment provided by engineer. */ "Remove" = "Eliminar"; @@ -1926,6 +1992,9 @@ /* No comment provided by engineer. */ "Save and notify group members" = "Guardar y notificar a los miembros del grupo"; +/* No comment provided by engineer. */ +"Save and update group profile" = "Guardar y actualizar perfil del grupo"; + /* No comment provided by engineer. */ "Save archive" = "Guardar archivo"; @@ -1941,9 +2010,18 @@ /* No comment provided by engineer. */ "Save preferences?" = "¿Guardar preferencias?"; +/* No comment provided by engineer. */ +"Save profile password" = "Guardar contraseña de perfil"; + /* No comment provided by engineer. */ "Save servers" = "Guardar servidores"; +/* No comment provided by engineer. */ +"Save servers?" = "¿Guardar servidores?"; + +/* No comment provided by engineer. */ +"Save welcome message?" = "¿Guardar mensaje de bienvenida?"; + /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Los servidores WebRTC ICE guardados serán eliminados"; @@ -2040,6 +2118,9 @@ /* No comment provided by engineer. */ "Set passphrase to export" = "Seleccióna contraseña para exportar"; +/* No comment provided by engineer. */ +"Set the message shown to new members!" = "¡Establece el mensaje mostrado a los miembros nuevos!"; + /* No comment provided by engineer. */ "Set timeouts for proxy/VPN" = "Establecer tiempos de espera para proxy/VPN"; @@ -2056,7 +2137,7 @@ "Share link" = "Compartir enlace"; /* No comment provided by engineer. */ -"Share one-time invitation link" = "Compartir enlace único de invitación"; +"Share one-time invitation link" = "Compartir enlace de invitación de un uso"; /* No comment provided by engineer. */ "Show calls in phone history" = "Mostrar llamadas en el historial del teléfono"; @@ -2089,7 +2170,7 @@ "SimpleX Lock turned on" = "Bloqueo SimpleX activado"; /* simplex link type */ -"SimpleX one-time invitation" = "Invitación única SimpleX"; +"SimpleX one-time invitation" = "Invitación SimpleX de un uso"; /* No comment provided by engineer. */ "Skip" = "Omitir"; @@ -2145,6 +2226,9 @@ /* No comment provided by engineer. */ "Tap button " = "Pulsa el botón "; +/* No comment provided by engineer. */ +"Tap to activate profile." = "Pulsa para activar el perfil."; + /* No comment provided by engineer. */ "Tap to join" = "Pulsa para unirse"; @@ -2232,6 +2316,12 @@ /* No comment provided by engineer. */ "Theme" = "Tema"; +/* No comment provided by engineer. */ +"There should be at least one user profile." = "Debe haber al menos un perfil de usuario."; + +/* No comment provided by engineer. */ +"There should be at least one visible user profile." = "Debe haber al menos un perfil de usuario visible."; + /* No comment provided by engineer. */ "This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Esta acción no se puede deshacer. Se eliminarán todos los archivos y multimedia recibidos y enviados. Las imágenes de baja resolución permanecerán."; @@ -2274,6 +2364,9 @@ /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "Para grabar el mensaje de voz concede permiso para usar el micrófono."; +/* No comment provided by engineer. */ +"To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "Para hacer visible tu perfil oculto, introduce la contraseña completa en el campo de búsqueda de la página **Tus perfiles Chat**."; + /* No comment provided by engineer. */ "To support instant push notifications the chat database has to be migrated." = "Para permitir las notificaciones automáticas instantáneas, la base de datos se debe migrar."; @@ -2307,6 +2400,9 @@ /* No comment provided by engineer. */ "Unexpected migration state" = "Estado de migración inesperado"; +/* No comment provided by engineer. */ +"Unhide" = "Mostrar"; + /* connection info */ "unknown" = "desconocido"; @@ -2404,10 +2500,10 @@ "via group link" = "mediante enlace de grupo"; /* chat list item description */ -"via one-time link" = "mediante enlace único"; +"via one-time link" = "mediante enlace de un uso"; /* No comment provided by engineer. */ -"via relay" = "mediante servidor de retransmisión"; +"via relay" = "mediante servidor relay"; /* No comment provided by engineer. */ "Video call" = "Videollamada"; @@ -2511,6 +2607,9 @@ /* No comment provided by engineer. */ "You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." = "También puedes conectarte haciendo clic en el enlace. Si se abre en el navegador, haz clic en el botón **Abrir en aplicación móvil**."; +/* No comment provided by engineer. */ +"You can hide or mute a user profile - swipe it to the right.\nSimpleX Lock must be enabled." = "Puedes ocultar o silenciar un perfil de usuario: deslízalo hacia la derecha.\nSimpleX Lock debe estar activado."; + /* notification body */ "You can now send messages to %@" = "Ya puedes enviar mensajes a %@"; @@ -2584,10 +2683,10 @@ "You sent group invitation" = "Has enviado una invitación de grupo"; /* chat list item description */ -"you shared one-time link" = "has compartido un enlace único"; +"you shared one-time link" = "has compartido un enlace de un uso"; /* chat list item description */ -"you shared one-time link incognito" = "has compartido un enlace único en módo incógnito"; +"you shared one-time link incognito" = "has compartido enlace de un uso en modo incógnito"; /* No comment provided by engineer. */ "You will be connected to group when the group host's device is online, please wait or check later!" = "Te conectarás al grupo cuando el dispositivo del anfitrión esté en línea, por favor espera o compruébalo más tarde."; @@ -2604,6 +2703,9 @@ /* No comment provided by engineer. */ "You will join a group this link refers to and connect to its group members." = "Te unirás al grupo al que hace referencia este enlace y te conectarás con sus miembros."; +/* No comment provided by engineer. */ +"You will still receive calls and notifications from muted profiles when they are active." = "Seguirás recibiendo llamadas y notificaciones de los perfiles silenciados cuando estén activos."; + /* No comment provided by engineer. */ "You will stop receiving messages from this group. Chat history will be preserved." = "Dejarás de recibir mensajes de este grupo. El historial del chat se conservará."; diff --git a/apps/ios/fr.lproj/Localizable.strings b/apps/ios/fr.lproj/Localizable.strings index 657399536b..90072d73cc 100644 --- a/apps/ios/fr.lproj/Localizable.strings +++ b/apps/ios/fr.lproj/Localizable.strings @@ -248,6 +248,9 @@ /* No comment provided by engineer. */ "Add to another device" = "Ajouter à un autre appareil"; +/* No comment provided by engineer. */ +"Add welcome message" = "Ajouter un message d'accueil"; + /* member role */ "admin" = "admin"; @@ -338,6 +341,9 @@ /* No comment provided by engineer. */ "Audio & video calls" = "Appels audio et vidéo"; +/* No comment provided by engineer. */ +"Audio and video calls" = "Appels audio et vidéo"; + /* No comment provided by engineer. */ "audio call (not e2e encrypted)" = "appel audio (sans chiffrement)"; @@ -398,6 +404,9 @@ /* No comment provided by engineer. */ "Calls" = "Appels"; +/* No comment provided by engineer. */ +"Can't delete user profile!" = "Impossible de supprimer le profil d'utilisateur !"; + /* No comment provided by engineer. */ "Can't invite contact!" = "Impossible d'inviter le contact !"; @@ -479,6 +488,9 @@ /* No comment provided by engineer. */ "Check server address and try again." = "Vérifiez l'adresse du serveur et réessayez."; +/* No comment provided by engineer. */ +"Chinese and Spanish interface" = "Interface en chinois et en espagnol"; + /* No comment provided by engineer. */ "Choose file" = "Choisir le fichier"; @@ -518,6 +530,9 @@ /* No comment provided by engineer. */ "Confirm new passphrase…" = "Confirmer la nouvelle phrase secrète…"; +/* No comment provided by engineer. */ +"Confirm password" = "Confirmer le mot de passe"; + /* server test step */ "Connect" = "Se connecter"; @@ -884,6 +899,9 @@ /* No comment provided by engineer. */ "Do NOT use SimpleX for emergency calls." = "N'utilisez PAS SimpleX pour les appels d'urgence."; +/* No comment provided by engineer. */ +"Don't show again" = "Ne plus afficher"; + /* No comment provided by engineer. */ "Duplicate display name!" = "Nom d'affichage en double !"; @@ -965,6 +983,9 @@ /* No comment provided by engineer. */ "Enter passphrase…" = "Entrez la phrase secrète…"; +/* No comment provided by engineer. */ +"Enter password above to show!" = "Entrez le mot de passe ci-dessus pour continuer !"; + /* No comment provided by engineer. */ "Enter server manually" = "Entrer un serveur manuellement"; @@ -1061,6 +1082,9 @@ /* No comment provided by engineer. */ "Error saving SMP servers" = "Erreur lors de la sauvegarde des serveurs SMP"; +/* No comment provided by engineer. */ +"Error saving user password" = "Erreur d'enregistrement du mot de passe de l'utilisateur"; + /* No comment provided by engineer. */ "Error sending message" = "Erreur lors de l'envoi du message"; @@ -1082,6 +1106,9 @@ /* No comment provided by engineer. */ "Error updating settings" = "Erreur lors de la mise à jour des paramètres"; +/* No comment provided by engineer. */ +"Error updating user privacy" = "Erreur de mise à jour de la confidentialité de l'utilisateur"; + /* No comment provided by engineer. */ "Error: %@" = "Erreur : %@"; @@ -1133,6 +1160,12 @@ /* No comment provided by engineer. */ "Full name:" = "Nom complet :"; +/* No comment provided by engineer. */ +"Fully re-implemented - work in background!" = "Entièrement réimplémenté - fonctionne en arrière-plan !"; + +/* No comment provided by engineer. */ +"Further reduced battery usage" = "Réduction accrue de l'utilisation de la batterie"; + /* No comment provided by engineer. */ "GIFs and stickers" = "GIFs et stickers"; @@ -1181,6 +1214,9 @@ /* notification */ "Group message:" = "Message du groupe :"; +/* No comment provided by engineer. */ +"Group moderation" = "Modération de groupe"; + /* No comment provided by engineer. */ "Group preferences" = "Préférences du groupe"; @@ -1193,6 +1229,9 @@ /* snd group event chat item */ "group profile updated" = "mise à jour du profil de groupe"; +/* No comment provided by engineer. */ +"Group welcome message" = "Message d'accueil du groupe"; + /* No comment provided by engineer. */ "Group will be deleted for all members - this cannot be undone!" = "Le groupe va être supprimé pour tout les membres - impossible de revenir en arrière !"; @@ -1205,12 +1244,21 @@ /* No comment provided by engineer. */ "Hidden" = "Caché"; +/* No comment provided by engineer. */ +"Hidden chat profiles" = "Profils de chat cachés"; + +/* No comment provided by engineer. */ +"Hidden profile password" = "Mot de passe de profil caché"; + /* chat item action */ "Hide" = "Cacher"; /* No comment provided by engineer. */ "Hide app screen in the recent apps." = "Masquer l'écran de l'app dans les apps récentes."; +/* No comment provided by engineer. */ +"Hide profile" = "Masquer le profil"; + /* No comment provided by engineer. */ "How it works" = "Comment ça fonctionne"; @@ -1298,6 +1346,9 @@ /* connection level description */ "indirect (%d)" = "indirect (%d)"; +/* No comment provided by engineer. */ +"Initial role" = "Rôle initial"; + /* No comment provided by engineer. */ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "Installer [SimpleX Chat pour terminal](https://github.com/simplex-chat/simplex-chat)"; @@ -1436,6 +1487,9 @@ /* No comment provided by engineer. */ "Make a private connection" = "Établir une connexion privée"; +/* No comment provided by engineer. */ +"Make profile private!" = "Rendre le profil privé !"; + /* No comment provided by engineer. */ "Make sure SMP server addresses are in correct format, line separated and are not duplicated (%@)." = "Assurez-vous que les adresses des serveurs SMP sont au bon format et ne sont pas dupliquées, un par ligne."; @@ -1532,6 +1586,9 @@ /* No comment provided by engineer. */ "Mute" = "Muet"; +/* No comment provided by engineer. */ +"Muted when inactive!" = "Mute en cas d'inactivité !"; + /* No comment provided by engineer. */ "Name" = "Nom"; @@ -1604,6 +1661,9 @@ /* No comment provided by engineer. */ "Notifications are disabled!" = "Les notifications sont désactivées !"; +/* No comment provided by engineer. */ +"Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "Désormais, les administrateurs peuvent :\n- supprimer les messages des membres.\n- désactiver des membres (rôle \"observateur\")"; + /* member role */ "observer" = "observateur"; @@ -1695,6 +1755,9 @@ /* member role */ "owner" = "propriétaire"; +/* No comment provided by engineer. */ +"Password to show" = "Mot de passe à afficher"; + /* No comment provided by engineer. */ "Paste" = "Coller"; @@ -1794,6 +1857,9 @@ /* No comment provided by engineer. */ "Protect app screen" = "Protéger l'écran de l'app"; +/* No comment provided by engineer. */ +"Protect your chat profiles with a password!" = "Protégez vos profils de chat par un mot de passe !"; + /* No comment provided by engineer. */ "Protocol timeout" = "Délai du protocole"; @@ -1926,6 +1992,9 @@ /* No comment provided by engineer. */ "Save and notify group members" = "Sauvegarder et en informer les membres du groupe"; +/* No comment provided by engineer. */ +"Save and update group profile" = "Sauvegarder et mettre à jour le profil du groupe"; + /* No comment provided by engineer. */ "Save archive" = "Sauvegarder l'archive"; @@ -1941,9 +2010,18 @@ /* No comment provided by engineer. */ "Save preferences?" = "Sauvegarder les préférences ?"; +/* No comment provided by engineer. */ +"Save profile password" = "Enregistrer le mot de passe du profil"; + /* No comment provided by engineer. */ "Save servers" = "Sauvegarder les serveurs"; +/* No comment provided by engineer. */ +"Save servers?" = "Sauvegarder les serveurs ?"; + +/* No comment provided by engineer. */ +"Save welcome message?" = "Sauvegarder le message d'accueil ?"; + /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Les serveurs WebRTC ICE sauvegardés seront supprimés"; @@ -2040,6 +2118,9 @@ /* No comment provided by engineer. */ "Set passphrase to export" = "Définir la phrase secrète pour l'export"; +/* No comment provided by engineer. */ +"Set the message shown to new members!" = "Choisissez un message à l'attention des nouveaux membres !"; + /* No comment provided by engineer. */ "Set timeouts for proxy/VPN" = "Définir les délais pour le proxy/VPN"; @@ -2145,6 +2226,9 @@ /* No comment provided by engineer. */ "Tap button " = "Appuyez sur le bouton "; +/* No comment provided by engineer. */ +"Tap to activate profile." = "Appuyez pour activer le profil."; + /* No comment provided by engineer. */ "Tap to join" = "Appuyez pour rejoindre"; @@ -2232,6 +2316,12 @@ /* No comment provided by engineer. */ "Theme" = "Thème"; +/* No comment provided by engineer. */ +"There should be at least one user profile." = "Il doit y avoir au moins un profil d'utilisateur."; + +/* No comment provided by engineer. */ +"There should be at least one visible user profile." = "Il doit y avoir au moins un profil d'utilisateur visible."; + /* No comment provided by engineer. */ "This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Cette action ne peut être annulée - tous les fichiers et médias reçus et envoyés seront supprimés. Les photos à faible résolution seront conservées."; @@ -2274,6 +2364,9 @@ /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "Pour enregistrer un message vocal, veuillez accorder la permission d'utiliser le microphone."; +/* No comment provided by engineer. */ +"To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "Pour révéler votre profil caché, entrez un mot de passe complet dans le champ de recherche de la page **Vos profils de chat**."; + /* No comment provided by engineer. */ "To support instant push notifications the chat database has to be migrated." = "Pour prendre en charge les notifications push instantanées, la base de données du chat doit être migrée."; @@ -2307,6 +2400,9 @@ /* No comment provided by engineer. */ "Unexpected migration state" = "État de la migration inattendu"; +/* No comment provided by engineer. */ +"Unhide" = "Dévoiler"; + /* connection info */ "unknown" = "inconnu"; @@ -2511,6 +2607,9 @@ /* No comment provided by engineer. */ "You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." = "Vous pouvez également vous connecter en cliquant sur le lien. S'il s'ouvre dans le navigateur, cliquez sur le bouton **Open in mobile app**."; +/* No comment provided by engineer. */ +"You can hide or mute a user profile - swipe it to the right.\nSimpleX Lock must be enabled." = "Vous pouvez masquer ou mettre en sourdine un profil d'utilisateur - faites-le glisser vers la droite.\nSimpleX Lock doit être activé."; + /* notification body */ "You can now send messages to %@" = "Vous pouvez maintenant envoyer des messages à %@"; @@ -2604,6 +2703,9 @@ /* No comment provided by engineer. */ "You will join a group this link refers to and connect to its group members." = "Vous allez rejoindre le groupe correspondant à ce lien et être mis en relation avec les autres membres du groupe."; +/* No comment provided by engineer. */ +"You will still receive calls and notifications from muted profiles when they are active." = "Vous continuerez à recevoir des appels et des notifications des profils mis en sourdine lorsqu'ils sont actifs."; + /* No comment provided by engineer. */ "You will stop receiving messages from this group. Chat history will be preserved." = "Vous ne recevrez plus de messages de ce groupe. L'historique du chat sera conservé."; diff --git a/apps/ios/it.lproj/Localizable.strings b/apps/ios/it.lproj/Localizable.strings index bee4806687..ce495aefa0 100644 --- a/apps/ios/it.lproj/Localizable.strings +++ b/apps/ios/it.lproj/Localizable.strings @@ -248,6 +248,9 @@ /* No comment provided by engineer. */ "Add to another device" = "Aggiungi ad un altro dispositivo"; +/* No comment provided by engineer. */ +"Add welcome message" = "Aggiungi messaggio di benvenuto"; + /* member role */ "admin" = "amministratore"; @@ -338,6 +341,9 @@ /* No comment provided by engineer. */ "Audio & video calls" = "Chiamate audio e video"; +/* No comment provided by engineer. */ +"Audio and video calls" = "Chiamate audio e video"; + /* No comment provided by engineer. */ "audio call (not e2e encrypted)" = "chiamata audio (non crittografata e2e)"; @@ -398,6 +404,9 @@ /* No comment provided by engineer. */ "Calls" = "Chiamate"; +/* No comment provided by engineer. */ +"Can't delete user profile!" = "Impossibile eliminare il profilo utente!"; + /* No comment provided by engineer. */ "Can't invite contact!" = "Impossibile invitare il contatto!"; @@ -479,6 +488,9 @@ /* No comment provided by engineer. */ "Check server address and try again." = "Controlla l'indirizzo del server e riprova."; +/* No comment provided by engineer. */ +"Chinese and Spanish interface" = "Interfaccia cinese e spagnola"; + /* No comment provided by engineer. */ "Choose file" = "Scegli file"; @@ -518,6 +530,9 @@ /* No comment provided by engineer. */ "Confirm new passphrase…" = "Conferma password nuova…"; +/* No comment provided by engineer. */ +"Confirm password" = "Conferma password"; + /* server test step */ "Connect" = "Connetti"; @@ -884,6 +899,9 @@ /* No comment provided by engineer. */ "Do NOT use SimpleX for emergency calls." = "NON usare SimpleX per chiamate di emergenza."; +/* No comment provided by engineer. */ +"Don't show again" = "Non mostrare più"; + /* No comment provided by engineer. */ "Duplicate display name!" = "Nome da mostrare doppio!"; @@ -965,6 +983,9 @@ /* No comment provided by engineer. */ "Enter passphrase…" = "Inserisci la password…"; +/* No comment provided by engineer. */ +"Enter password above to show!" = "Inserisci la password sopra per mostrare!"; + /* No comment provided by engineer. */ "Enter server manually" = "Inserisci il server a mano"; @@ -1061,6 +1082,9 @@ /* No comment provided by engineer. */ "Error saving SMP servers" = "Errore nel salvataggio dei server SMP"; +/* No comment provided by engineer. */ +"Error saving user password" = "Errore nel salvataggio della password utente"; + /* No comment provided by engineer. */ "Error sending message" = "Errore nell'invio del messaggio"; @@ -1082,6 +1106,9 @@ /* No comment provided by engineer. */ "Error updating settings" = "Errore nell'aggiornamento delle impostazioni"; +/* No comment provided by engineer. */ +"Error updating user privacy" = "Errore nell'aggiornamento della privacy dell'utente"; + /* No comment provided by engineer. */ "Error: %@" = "Errore: %@"; @@ -1133,6 +1160,12 @@ /* No comment provided by engineer. */ "Full name:" = "Nome completo:"; +/* No comment provided by engineer. */ +"Fully re-implemented - work in background!" = "Completamente reimplementato - funziona in secondo piano!"; + +/* No comment provided by engineer. */ +"Further reduced battery usage" = "Ulteriore riduzione del consumo della batteria"; + /* No comment provided by engineer. */ "GIFs and stickers" = "GIF e adesivi"; @@ -1181,6 +1214,9 @@ /* notification */ "Group message:" = "Messaggio del gruppo:"; +/* No comment provided by engineer. */ +"Group moderation" = "Moderazione del gruppo"; + /* No comment provided by engineer. */ "Group preferences" = "Preferenze del gruppo"; @@ -1193,6 +1229,9 @@ /* snd group event chat item */ "group profile updated" = "profilo del gruppo aggiornato"; +/* No comment provided by engineer. */ +"Group welcome message" = "Messaggio di benvenuto del gruppo"; + /* No comment provided by engineer. */ "Group will be deleted for all members - this cannot be undone!" = "Il gruppo verrà eliminato per tutti i membri. Non è reversibile!"; @@ -1205,12 +1244,21 @@ /* No comment provided by engineer. */ "Hidden" = "Nascosta"; +/* No comment provided by engineer. */ +"Hidden chat profiles" = "Profili di chat nascosti"; + +/* No comment provided by engineer. */ +"Hidden profile password" = "Password del profilo nascosta"; + /* chat item action */ "Hide" = "Nascondi"; /* No comment provided by engineer. */ "Hide app screen in the recent apps." = "Nascondi la schermata dell'app nelle app recenti."; +/* No comment provided by engineer. */ +"Hide profile" = "Nascondi il profilo"; + /* No comment provided by engineer. */ "How it works" = "Come funziona"; @@ -1298,6 +1346,9 @@ /* connection level description */ "indirect (%d)" = "indiretta (%d)"; +/* No comment provided by engineer. */ +"Initial role" = "Ruolo iniziale"; + /* No comment provided by engineer. */ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "Installa [Simplex Chat per terminale](https://github.com/simplex-chat/simplex-chat)"; @@ -1436,6 +1487,9 @@ /* No comment provided by engineer. */ "Make a private connection" = "Crea una connessione privata"; +/* No comment provided by engineer. */ +"Make profile private!" = "Rendi privato il profilo!"; + /* No comment provided by engineer. */ "Make sure SMP server addresses are in correct format, line separated and are not duplicated (%@)." = "Assicurati che gli indirizzi dei server SMP siano nel formato corretto, uno per riga e non doppi (%@)."; @@ -1532,6 +1586,9 @@ /* No comment provided by engineer. */ "Mute" = "Silenzia"; +/* No comment provided by engineer. */ +"Muted when inactive!" = "Silenzioso quando inattivo!"; + /* No comment provided by engineer. */ "Name" = "Nome"; @@ -1604,6 +1661,9 @@ /* No comment provided by engineer. */ "Notifications are disabled!" = "Le notifiche sono disattivate!"; +/* No comment provided by engineer. */ +"Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "Ora gli amministratori possono:\n- eliminare i messaggi dei membri.\n- disattivare i membri (ruolo \"osservatore\")"; + /* member role */ "observer" = "osservatore"; @@ -1695,6 +1755,9 @@ /* member role */ "owner" = "proprietario"; +/* No comment provided by engineer. */ +"Password to show" = "Password per mostrare"; + /* No comment provided by engineer. */ "Paste" = "Incolla"; @@ -1794,6 +1857,9 @@ /* No comment provided by engineer. */ "Protect app screen" = "Proteggi la schermata dell'app"; +/* No comment provided by engineer. */ +"Protect your chat profiles with a password!" = "Proteggi i tuoi profili di chat con una password!"; + /* No comment provided by engineer. */ "Protocol timeout" = "Scadenza del protocollo"; @@ -1926,6 +1992,9 @@ /* No comment provided by engineer. */ "Save and notify group members" = "Salva e avvisa i membri del gruppo"; +/* No comment provided by engineer. */ +"Save and update group profile" = "Salva e aggiorna il profilo del gruppo"; + /* No comment provided by engineer. */ "Save archive" = "Salva archivio"; @@ -1941,9 +2010,18 @@ /* No comment provided by engineer. */ "Save preferences?" = "Salvare le preferenze?"; +/* No comment provided by engineer. */ +"Save profile password" = "Salva la password del profilo"; + /* No comment provided by engineer. */ "Save servers" = "Salva i server"; +/* No comment provided by engineer. */ +"Save servers?" = "Salvare i server?"; + +/* No comment provided by engineer. */ +"Save welcome message?" = "Salvare il messaggio di benvenuto?"; + /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "I server WebRTC ICE salvati verranno rimossi"; @@ -2040,6 +2118,9 @@ /* No comment provided by engineer. */ "Set passphrase to export" = "Imposta la password per esportare"; +/* No comment provided by engineer. */ +"Set the message shown to new members!" = "Imposta il messaggio mostrato ai nuovi membri!"; + /* No comment provided by engineer. */ "Set timeouts for proxy/VPN" = "Imposta scadenze per proxy/VPN"; @@ -2145,6 +2226,9 @@ /* No comment provided by engineer. */ "Tap button " = "Tocca il pulsante "; +/* No comment provided by engineer. */ +"Tap to activate profile." = "Tocca per attivare il profilo."; + /* No comment provided by engineer. */ "Tap to join" = "Tocca per entrare"; @@ -2232,6 +2316,12 @@ /* No comment provided by engineer. */ "Theme" = "Tema"; +/* No comment provided by engineer. */ +"There should be at least one user profile." = "Deve esserci almeno un profilo utente."; + +/* No comment provided by engineer. */ +"There should be at least one visible user profile." = "Deve esserci almeno un profilo utente visibile."; + /* No comment provided by engineer. */ "This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Questa azione non può essere annullata: tutti i file e i media ricevuti e inviati verranno eliminati. Rimarranno le immagini a bassa risoluzione."; @@ -2274,6 +2364,9 @@ /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "Per registrare un messaggio vocale, concedi l'autorizzazione all'uso del microfono."; +/* No comment provided by engineer. */ +"To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "Per rivelare il tuo profilo nascosto, inserisci una password completa in un campo di ricerca nella pagina **I tuoi profili di chat**."; + /* No comment provided by engineer. */ "To support instant push notifications the chat database has to be migrated." = "Per supportare le notifiche push istantanee, il database della chat deve essere migrato."; @@ -2307,6 +2400,9 @@ /* No comment provided by engineer. */ "Unexpected migration state" = "Stato di migrazione imprevisto"; +/* No comment provided by engineer. */ +"Unhide" = "Svela"; + /* connection info */ "unknown" = "sconosciuto"; @@ -2511,6 +2607,9 @@ /* No comment provided by engineer. */ "You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." = "Puoi anche connetterti cliccando il link. Se si apre nel browser, clicca il pulsante **Apri nell'app mobile**."; +/* No comment provided by engineer. */ +"You can hide or mute a user profile - swipe it to the right.\nSimpleX Lock must be enabled." = "Puoi nascondere o silenziare un profilo utente - scorrilo verso destra.\nSimpleX Lock deve essere attivato."; + /* notification body */ "You can now send messages to %@" = "Ora puoi inviare messaggi a %@"; @@ -2604,6 +2703,9 @@ /* No comment provided by engineer. */ "You will join a group this link refers to and connect to its group members." = "Entrerai in un gruppo a cui si riferisce questo link e ti connetterai ai suoi membri."; +/* No comment provided by engineer. */ +"You will still receive calls and notifications from muted profiles when they are active." = "Continuerai a ricevere chiamate e notifiche da profili silenziati quando sono attivi."; + /* No comment provided by engineer. */ "You will stop receiving messages from this group. Chat history will be preserved." = "Non riceverai più messaggi da questo gruppo. La cronologia della chat verrà conservata."; diff --git a/apps/ios/nl.lproj/Localizable.strings b/apps/ios/nl.lproj/Localizable.strings index b5d1b449d9..d28faeba08 100644 --- a/apps/ios/nl.lproj/Localizable.strings +++ b/apps/ios/nl.lproj/Localizable.strings @@ -209,7 +209,7 @@ "About SimpleX Chat" = "Over SimpleX Chat"; /* No comment provided by engineer. */ -"above, then choose:" = "hierboven, kies dan:"; +"above, then choose:" = "hier boven, kies dan:"; /* No comment provided by engineer. */ "Accent color" = "Accent kleur"; @@ -248,6 +248,9 @@ /* No comment provided by engineer. */ "Add to another device" = "Toevoegen aan een ander apparaat"; +/* No comment provided by engineer. */ +"Add welcome message" = "Welkomst bericht toevoegen"; + /* member role */ "admin" = "Beheerder"; @@ -338,6 +341,9 @@ /* No comment provided by engineer. */ "Audio & video calls" = "Audio en video gesprekken"; +/* No comment provided by engineer. */ +"Audio and video calls" = "Audio en video oproepen"; + /* No comment provided by engineer. */ "audio call (not e2e encrypted)" = "audio oproep (niet e2e versleuteld)"; @@ -398,6 +404,9 @@ /* No comment provided by engineer. */ "Calls" = "Oproepen"; +/* No comment provided by engineer. */ +"Can't delete user profile!" = "Kan gebruikers profiel niet verwijderen!"; + /* No comment provided by engineer. */ "Can't invite contact!" = "Kan contact niet uitnodigen!"; @@ -479,6 +488,9 @@ /* No comment provided by engineer. */ "Check server address and try again." = "Controleer het server adres en probeer het opnieuw."; +/* No comment provided by engineer. */ +"Chinese and Spanish interface" = "Chinese en Spaanse interface"; + /* No comment provided by engineer. */ "Choose file" = "Kies bestand"; @@ -518,6 +530,9 @@ /* No comment provided by engineer. */ "Confirm new passphrase…" = "Bevestig nieuw wachtwoord…"; +/* No comment provided by engineer. */ +"Confirm password" = "Bevestig wachtwoord"; + /* server test step */ "Connect" = "Verbind"; @@ -819,7 +834,7 @@ "Delete queue" = "Wachtrij verwijderen"; /* No comment provided by engineer. */ -"Delete user profile?" = "Gebruikersprofiel verwijderen?"; +"Delete user profile?" = "Gebruikers profiel verwijderen?"; /* deleted chat item */ "deleted" = "verwijderd"; @@ -884,6 +899,9 @@ /* No comment provided by engineer. */ "Do NOT use SimpleX for emergency calls." = "Gebruik SimpleX NIET voor noodoproepen."; +/* No comment provided by engineer. */ +"Don't show again" = "Niet meer weergeven"; + /* No comment provided by engineer. */ "Duplicate display name!" = "Dubbele weergavenaam!"; @@ -965,6 +983,9 @@ /* No comment provided by engineer. */ "Enter passphrase…" = "Voer wachtwoord in…"; +/* No comment provided by engineer. */ +"Enter password above to show!" = "Voer hier boven het wachtwoord in om weer te geven!"; + /* No comment provided by engineer. */ "Enter server manually" = "Voer de server handmatig in"; @@ -1061,6 +1082,9 @@ /* No comment provided by engineer. */ "Error saving SMP servers" = "Fout bij opslaan van SMP servers"; +/* No comment provided by engineer. */ +"Error saving user password" = "Fout bij opslaan gebruikers wachtwoord"; + /* No comment provided by engineer. */ "Error sending message" = "Fout bij verzenden van bericht"; @@ -1082,6 +1106,9 @@ /* No comment provided by engineer. */ "Error updating settings" = "Fout bij bijwerken van instellingen"; +/* No comment provided by engineer. */ +"Error updating user privacy" = "Fout bij updaten van gebruikers privacy"; + /* No comment provided by engineer. */ "Error: %@" = "Fout: %@"; @@ -1133,6 +1160,12 @@ /* No comment provided by engineer. */ "Full name:" = "Volledige naam:"; +/* No comment provided by engineer. */ +"Fully re-implemented - work in background!" = "Volledig opnieuw geïmplementeerd - werk op de achtergrond!"; + +/* No comment provided by engineer. */ +"Further reduced battery usage" = "Verder verminderd batterij verbruik"; + /* No comment provided by engineer. */ "GIFs and stickers" = "GIF's en stickers"; @@ -1181,6 +1214,9 @@ /* notification */ "Group message:" = "Groep bericht:"; +/* No comment provided by engineer. */ +"Group moderation" = "Groep moderatie"; + /* No comment provided by engineer. */ "Group preferences" = "Groep voorkeuren"; @@ -1193,6 +1229,9 @@ /* snd group event chat item */ "group profile updated" = "groep profiel bijgewerkt"; +/* No comment provided by engineer. */ +"Group welcome message" = "Groep welkomst bericht"; + /* No comment provided by engineer. */ "Group will be deleted for all members - this cannot be undone!" = "Groep wordt verwijderd voor alle leden, dit kan niet ongedaan worden gemaakt!"; @@ -1205,12 +1244,21 @@ /* No comment provided by engineer. */ "Hidden" = "Verborgen"; +/* No comment provided by engineer. */ +"Hidden chat profiles" = "Verborgen chat profielen"; + +/* No comment provided by engineer. */ +"Hidden profile password" = "Verborgen profiel wachtwoord"; + /* chat item action */ "Hide" = "Verbergen"; /* No comment provided by engineer. */ "Hide app screen in the recent apps." = "App scherm verbergen in de recente apps."; +/* No comment provided by engineer. */ +"Hide profile" = "Profiel verbergen"; + /* No comment provided by engineer. */ "How it works" = "Hoe het werkt"; @@ -1298,6 +1346,9 @@ /* connection level description */ "indirect (%d)" = "indirect (%d)"; +/* No comment provided by engineer. */ +"Initial role" = "Initiële rol"; + /* No comment provided by engineer. */ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "Installeer [SimpleX Chat voor terminal](https://github.com/simplex-chat/simplex-chat)"; @@ -1436,6 +1487,9 @@ /* No comment provided by engineer. */ "Make a private connection" = "Maak een privéverbinding"; +/* No comment provided by engineer. */ +"Make profile private!" = "Profiel privé maken!"; + /* No comment provided by engineer. */ "Make sure SMP server addresses are in correct format, line separated and are not duplicated (%@)." = "Zorg ervoor dat SMP server adressen de juiste indeling hebben, regel gescheiden zijn en niet gedupliceerd zijn (%@)."; @@ -1532,6 +1586,9 @@ /* No comment provided by engineer. */ "Mute" = "Dempen"; +/* No comment provided by engineer. */ +"Muted when inactive!" = "Gedempt wanneer inactief!"; + /* No comment provided by engineer. */ "Name" = "Naam"; @@ -1604,8 +1661,11 @@ /* No comment provided by engineer. */ "Notifications are disabled!" = "Meldingen zijn uitgeschakeld!"; +/* No comment provided by engineer. */ +"Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "Nu kunnen beheerders: \n- berichten van leden verwijderen.\n- schakel leden uit (\"waarnemer\" rol)"; + /* member role */ -"observer" = "waarnemer"; +"observer" = "Waarnemer"; /* enabled status group pref value */ @@ -1645,7 +1705,7 @@ "Onion hosts will not be used." = "Onion hosts worden niet gebruikt."; /* No comment provided by engineer. */ -"Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "Alleen client apparaten slaan gebruikersprofielen, contacten, groepen en berichten op die zijn verzonden met **2-laags end-to-end-codering**."; +"Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "Alleen client apparaten slaan gebruikers profielen, contacten, groepen en berichten op die zijn verzonden met **2-laags end-to-end-codering**."; /* No comment provided by engineer. */ "Only group owners can change group preferences." = "Alleen groep eigenaren kunnen groep voorkeuren wijzigen."; @@ -1681,7 +1741,7 @@ "Open Settings" = "Open instellingen"; /* authentication reason */ -"Open user profiles" = "Gebruikersprofielen openen"; +"Open user profiles" = "Gebruikers profielen openen"; /* No comment provided by engineer. */ "Open-source protocol and code – anybody can run the servers." = "Open-source protocol en code. Iedereen kan de servers draaien."; @@ -1693,7 +1753,10 @@ "or chat with the developers" = "of praat met de ontwikkelaars"; /* member role */ -"owner" = "eigenaar"; +"owner" = "Eigenaar"; + +/* No comment provided by engineer. */ +"Password to show" = "Wachtwoord om weer te geven"; /* No comment provided by engineer. */ "Paste" = "Plakken"; @@ -1794,6 +1857,9 @@ /* No comment provided by engineer. */ "Protect app screen" = "App scherm verbergen"; +/* No comment provided by engineer. */ +"Protect your chat profiles with a password!" = "Bescherm je chat profielen met een wachtwoord!"; + /* No comment provided by engineer. */ "Protocol timeout" = "Protocol timeout"; @@ -1926,6 +1992,9 @@ /* No comment provided by engineer. */ "Save and notify group members" = "Opslaan en Groep leden melden"; +/* No comment provided by engineer. */ +"Save and update group profile" = "Groep profiel opslaan en bijwerken"; + /* No comment provided by engineer. */ "Save archive" = "Bewaar archief"; @@ -1941,9 +2010,18 @@ /* No comment provided by engineer. */ "Save preferences?" = "Voorkeuren opslaan?"; +/* No comment provided by engineer. */ +"Save profile password" = "Bewaar profiel wachtwoord"; + /* No comment provided by engineer. */ "Save servers" = "Bewaar servers"; +/* No comment provided by engineer. */ +"Save servers?" = "Servers opslaan?"; + +/* No comment provided by engineer. */ +"Save welcome message?" = "Welkomst bericht opslaan?"; + /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Opgeslagen WebRTC ICE servers worden verwijderd"; @@ -2040,6 +2118,9 @@ /* No comment provided by engineer. */ "Set passphrase to export" = "Wachtwoord instellen om te exporteren"; +/* No comment provided by engineer. */ +"Set the message shown to new members!" = "Stel het getoonde bericht in voor nieuwe leden!"; + /* No comment provided by engineer. */ "Set timeouts for proxy/VPN" = "Stel timeouts in voor proxy/VPN"; @@ -2145,6 +2226,9 @@ /* No comment provided by engineer. */ "Tap button " = "Tik op de knop "; +/* No comment provided by engineer. */ +"Tap to activate profile." = "Tik om profiel te activeren."; + /* No comment provided by engineer. */ "Tap to join" = "Tik om mee te doen"; @@ -2232,6 +2316,12 @@ /* No comment provided by engineer. */ "Theme" = "Thema"; +/* No comment provided by engineer. */ +"There should be at least one user profile." = "Er moet ten minste één gebruikers profiel zijn."; + +/* No comment provided by engineer. */ +"There should be at least one visible user profile." = "Er moet ten minste één zichtbaar gebruikers profiel zijn."; + /* No comment provided by engineer. */ "This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Deze actie kan niet ongedaan worden gemaakt, alle ontvangen en verzonden bestanden en media worden verwijderd. Foto's met een lage resolutie blijven behouden."; @@ -2274,6 +2364,9 @@ /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "Geef toestemming om de microfoon te gebruiken om een spraakbericht op te nemen."; +/* No comment provided by engineer. */ +"To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "Om uw verborgen profiel te onthullen, voert u een volledig wachtwoord in een zoek veld in op de pagina **Uw chat profielen**."; + /* No comment provided by engineer. */ "To support instant push notifications the chat database has to be migrated." = "Om directe push meldingen te ondersteunen, moet de chat database worden gemigreerd."; @@ -2307,6 +2400,9 @@ /* No comment provided by engineer. */ "Unexpected migration state" = "Onverwachte migratiestatus"; +/* No comment provided by engineer. */ +"Unhide" = "zichtbaar maken"; + /* connection info */ "unknown" = "onbekend"; @@ -2377,7 +2473,7 @@ "Use SimpleX Chat servers?" = "SimpleX Chat servers gebruiken?"; /* No comment provided by engineer. */ -"User profile" = "Gebruikersprofiel"; +"User profile" = "Gebruikers profiel"; /* No comment provided by engineer. */ "Using .onion hosts requires compatible VPN provider." = "Het gebruik van .onion-hosts vereist een compatibele VPN-provider."; @@ -2455,7 +2551,7 @@ "Welcome %@!" = "Welkom %@!"; /* No comment provided by engineer. */ -"Welcome message" = "Welkoms bericht"; +"Welcome message" = "Welkomst bericht"; /* No comment provided by engineer. */ "What's new" = "Wat is er nieuw"; @@ -2467,7 +2563,7 @@ "When you share an incognito profile with somebody, this profile will be used for the groups they invite you to." = "Wanneer je een incognito profiel met iemand deelt, wordt dit profiel gebruikt voor de groepen waarvoor ze je uitnodigen."; /* No comment provided by engineer. */ -"With optional welcome message." = "Met optioneel welkomstbericht."; +"With optional welcome message." = "Met optioneel welkomst bericht."; /* No comment provided by engineer. */ "Wrong database passphrase" = "Verkeerd wachtwoord voor de database"; @@ -2511,6 +2607,9 @@ /* No comment provided by engineer. */ "You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." = "U kunt ook verbinding maken door op de link te klikken. Als het in de browser wordt geopend, klikt u op de knop **Openen in mobiele app**."; +/* No comment provided by engineer. */ +"You can hide or mute a user profile - swipe it to the right.\nSimpleX Lock must be enabled." = "U kunt een gebruikers profiel verbergen of dempen - veeg het naar rechts.\nSimpleX Lock moet ingeschakeld zijn."; + /* notification body */ "You can now send messages to %@" = "Je kunt nu berichten sturen naar %@"; @@ -2604,6 +2703,9 @@ /* No comment provided by engineer. */ "You will join a group this link refers to and connect to its group members." = "U wordt lid van een groep waarnaar deze link verwijst en maakt verbinding met de groepsleden."; +/* No comment provided by engineer. */ +"You will still receive calls and notifications from muted profiles when they are active." = "U ontvangt nog steeds oproepen en meldingen van gedempte profielen wanneer deze actief zijn."; + /* No comment provided by engineer. */ "You will stop receiving messages from this group. Chat history will be preserved." = "Je ontvangt geen berichten meer van deze groep. Je gesprek geschiedenis blijft behouden."; diff --git a/apps/ios/ru.lproj/Localizable.strings b/apps/ios/ru.lproj/Localizable.strings index 0b4c3851b3..df6bac9f4e 100644 --- a/apps/ios/ru.lproj/Localizable.strings +++ b/apps/ios/ru.lproj/Localizable.strings @@ -248,6 +248,9 @@ /* No comment provided by engineer. */ "Add to another device" = "Добавить на другое устройство"; +/* No comment provided by engineer. */ +"Add welcome message" = "Добавить приветственное сообщение"; + /* member role */ "admin" = "админ"; @@ -338,12 +341,18 @@ /* No comment provided by engineer. */ "Audio & video calls" = "Аудио- и видеозвонки"; +/* No comment provided by engineer. */ +"Audio and video calls" = "Аудио и видео звонки"; + /* No comment provided by engineer. */ "audio call (not e2e encrypted)" = "аудиозвонок (не e2e зашифрованный)"; /* No comment provided by engineer. */ "Authentication failed" = "Ошибка аутентификации"; +/* No comment provided by engineer. */ +"Authentication is required before the call is connected, but you may miss calls." = "Аутентификация требуется до того, как звонок соединится, но вы можете пропустить звонки."; + /* No comment provided by engineer. */ "Authentication unavailable" = "Аутентификация недоступна"; @@ -395,6 +404,9 @@ /* No comment provided by engineer. */ "Calls" = "Звонки"; +/* No comment provided by engineer. */ +"Can't delete user profile!" = "Нельзя удалить профиль пользователя!"; + /* No comment provided by engineer. */ "Can't invite contact!" = "Нельзя пригласить контакт!"; @@ -476,6 +488,9 @@ /* No comment provided by engineer. */ "Check server address and try again." = "Проверьте адрес сервера и попробуйте снова."; +/* No comment provided by engineer. */ +"Chinese and Spanish interface" = "Китайский и Испанский интерфейс"; + /* No comment provided by engineer. */ "Choose file" = "Выбрать файл"; @@ -515,6 +530,9 @@ /* No comment provided by engineer. */ "Confirm new passphrase…" = "Подтвердите новый пароль…"; +/* No comment provided by engineer. */ +"Confirm password" = "Подтвердить пароль"; + /* server test step */ "Connect" = "Соединиться"; @@ -881,6 +899,9 @@ /* No comment provided by engineer. */ "Do NOT use SimpleX for emergency calls." = "Не используйте SimpleX для экстренных звонков."; +/* No comment provided by engineer. */ +"Don't show again" = "Не показывать"; + /* No comment provided by engineer. */ "Duplicate display name!" = "Имя профиля уже используется!"; @@ -962,6 +983,9 @@ /* No comment provided by engineer. */ "Enter passphrase…" = "Введите пароль…"; +/* No comment provided by engineer. */ +"Enter password above to show!" = "Введите пароль выше, чтобы раскрыть!"; + /* No comment provided by engineer. */ "Enter server manually" = "Ввести сервер вручную"; @@ -1058,6 +1082,9 @@ /* No comment provided by engineer. */ "Error saving SMP servers" = "Ошибка при сохранении SMP серверов"; +/* No comment provided by engineer. */ +"Error saving user password" = "Ошибка при сохранении пароля пользователя"; + /* No comment provided by engineer. */ "Error sending message" = "Ошибка при отправке сообщения"; @@ -1079,6 +1106,9 @@ /* No comment provided by engineer. */ "Error updating settings" = "Ошибка при сохранении настроек сети"; +/* No comment provided by engineer. */ +"Error updating user privacy" = "Ошибка при обновлении конфиденциальности"; + /* No comment provided by engineer. */ "Error: %@" = "Ошибка: %@"; @@ -1130,6 +1160,12 @@ /* No comment provided by engineer. */ "Full name:" = "Полное имя:"; +/* No comment provided by engineer. */ +"Fully re-implemented - work in background!" = "Полностью обновлены - работают в фоне!"; + +/* No comment provided by engineer. */ +"Further reduced battery usage" = "Уменьшенное потребление батареи"; + /* No comment provided by engineer. */ "GIFs and stickers" = "ГИФ файлы и стикеры"; @@ -1178,6 +1214,9 @@ /* notification */ "Group message:" = "Групповое сообщение:"; +/* No comment provided by engineer. */ +"Group moderation" = "Модерация группы"; + /* No comment provided by engineer. */ "Group preferences" = "Предпочтения группы"; @@ -1190,6 +1229,9 @@ /* snd group event chat item */ "group profile updated" = "профиль группы обновлен"; +/* No comment provided by engineer. */ +"Group welcome message" = "Приветственное сообщение группы"; + /* No comment provided by engineer. */ "Group will be deleted for all members - this cannot be undone!" = "Группа будет удалена для всех членов - это действие нельзя отменить!"; @@ -1202,12 +1244,21 @@ /* No comment provided by engineer. */ "Hidden" = "Скрытое"; +/* No comment provided by engineer. */ +"Hidden chat profiles" = "Скрытые профили чата"; + +/* No comment provided by engineer. */ +"Hidden profile password" = "Пароль скрытого профиля"; + /* chat item action */ "Hide" = "Спрятать"; /* No comment provided by engineer. */ "Hide app screen in the recent apps." = "Скрыть экран приложения."; +/* No comment provided by engineer. */ +"Hide profile" = "Скрыть профиль"; + /* No comment provided by engineer. */ "How it works" = "Как это работает"; @@ -1295,6 +1346,9 @@ /* connection level description */ "indirect (%d)" = "непрямое (%d)"; +/* No comment provided by engineer. */ +"Initial role" = "Роль при вступлении"; + /* No comment provided by engineer. */ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "[SimpleX Chat для терминала](https://github.com/simplex-chat/simplex-chat)"; @@ -1304,6 +1358,9 @@ /* No comment provided by engineer. */ "Instantly" = "Мгновенно"; +/* No comment provided by engineer. */ +"Interface" = "Интерфейс"; + /* invalid chat data */ "invalid chat" = "ошибка чата"; @@ -1430,6 +1487,9 @@ /* No comment provided by engineer. */ "Make a private connection" = "Добавьте контакт"; +/* No comment provided by engineer. */ +"Make profile private!" = "Сделайте профиль скрытым!"; + /* No comment provided by engineer. */ "Make sure SMP server addresses are in correct format, line separated and are not duplicated (%@)." = "Пожалуйста, проверьте, что адреса SMP серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется (%@)."; @@ -1526,6 +1586,9 @@ /* No comment provided by engineer. */ "Mute" = "Без звука"; +/* No comment provided by engineer. */ +"Muted when inactive!" = "Без звука, когда не активный!"; + /* No comment provided by engineer. */ "Name" = "Имя"; @@ -1598,6 +1661,9 @@ /* No comment provided by engineer. */ "Notifications are disabled!" = "Уведомления выключены"; +/* No comment provided by engineer. */ +"Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "Теперь админы могут:\n- удалять сообщения членов.\n- приостанавливать членов (роль \"наблюдатель\")"; + /* member role */ "observer" = "читатель"; @@ -1689,6 +1755,9 @@ /* member role */ "owner" = "владелец"; +/* No comment provided by engineer. */ +"Password to show" = "Пароль чтобы раскрыть"; + /* No comment provided by engineer. */ "Paste" = "Вставить"; @@ -1788,6 +1857,9 @@ /* No comment provided by engineer. */ "Protect app screen" = "Защитить экран приложения"; +/* No comment provided by engineer. */ +"Protect your chat profiles with a password!" = "Защитите ваши профили чата паролем!"; + /* No comment provided by engineer. */ "Protocol timeout" = "Таймаут протокола"; @@ -1920,6 +1992,9 @@ /* No comment provided by engineer. */ "Save and notify group members" = "Сохранить и уведомить членов группы"; +/* No comment provided by engineer. */ +"Save and update group profile" = "Сохранить сообщение и обновить группу"; + /* No comment provided by engineer. */ "Save archive" = "Сохранить архив"; @@ -1935,9 +2010,18 @@ /* No comment provided by engineer. */ "Save preferences?" = "Сохранить предпочтения?"; +/* No comment provided by engineer. */ +"Save profile password" = "Сохранить пароль профиля"; + /* No comment provided by engineer. */ "Save servers" = "Сохранить серверы"; +/* No comment provided by engineer. */ +"Save servers?" = "Сохранить серверы?"; + +/* No comment provided by engineer. */ +"Save welcome message?" = "Сохранить приветственное сообщение?"; + /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "Сохраненные WebRTC ICE серверы будут удалены"; @@ -2034,6 +2118,9 @@ /* No comment provided by engineer. */ "Set passphrase to export" = "Установите пароль"; +/* No comment provided by engineer. */ +"Set the message shown to new members!" = "Установить сообщение для новых членов группы!"; + /* No comment provided by engineer. */ "Set timeouts for proxy/VPN" = "Установить таймауты для прокси/VPN"; @@ -2052,6 +2139,9 @@ /* No comment provided by engineer. */ "Share one-time invitation link" = "Поделиться ссылкой-приглашением"; +/* No comment provided by engineer. */ +"Show calls in phone history" = "Показать звонки в истории телефона"; + /* No comment provided by engineer. */ "Show preview" = "Показывать уведомления"; @@ -2136,6 +2226,9 @@ /* No comment provided by engineer. */ "Tap button " = "Нажмите кнопку "; +/* No comment provided by engineer. */ +"Tap to activate profile." = "Нажмите, чтобы сделать профиль активным."; + /* No comment provided by engineer. */ "Tap to join" = "Нажмите, чтобы вступить"; @@ -2223,6 +2316,12 @@ /* No comment provided by engineer. */ "Theme" = "Тема"; +/* No comment provided by engineer. */ +"There should be at least one user profile." = "Должен быть хотя бы один профиль пользователя."; + +/* No comment provided by engineer. */ +"There should be at least one visible user profile." = "Должен быть хотя бы один открытый профиль пользователя."; + /* No comment provided by engineer. */ "This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Это действие нельзя отменить — все полученные и отправленные файлы будут удалены. Изображения останутся в низком разрешении."; @@ -2265,6 +2364,9 @@ /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "Для записи голосового сообщения, пожалуйста разрешите доступ к микрофону."; +/* No comment provided by engineer. */ +"To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "Чтобы показать Ваш скрытый профиль, введите его пароль в поле поиска на странице **Ваши профили чата**."; + /* No comment provided by engineer. */ "To support instant push notifications the chat database has to be migrated." = "Для поддержки мгновенный доставки уведомлений данные чата должны быть перемещены."; @@ -2298,15 +2400,24 @@ /* No comment provided by engineer. */ "Unexpected migration state" = "Неожиданная ошибка при перемещении данных чата"; +/* No comment provided by engineer. */ +"Unhide" = "Раскрыть"; + /* connection info */ "unknown" = "неизвестно"; +/* callkit banner */ +"Unknown caller" = "Неизвестный звонок"; + /* No comment provided by engineer. */ "Unknown database error: %@" = "Неизвестная ошибка базы данных: %@"; /* No comment provided by engineer. */ "Unknown error" = "Неизвестная ошибка"; +/* No comment provided by engineer. */ +"Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions." = "Если вы не используете интерфейс iOS, включите режим Не отвлекать, чтобы звонок не прерывался."; + /* No comment provided by engineer. */ "Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "Возможно, ваш контакт удалил ссылку, или она уже была использована. Если это не так, то это может быть ошибкой - пожалуйста, сообщите нам об этом.\nЧтобы установить соединение, попросите ваш контакт создать еще одну ссылку и проверьте ваше соединение с сетью."; @@ -2352,6 +2463,9 @@ /* No comment provided by engineer. */ "Use for new connections" = "Использовать для новых соединений"; +/* No comment provided by engineer. */ +"Use iOS call interface" = "Использовать интерфейс iOS для звонков"; + /* No comment provided by engineer. */ "Use server" = "Использовать сервер"; @@ -2487,9 +2601,15 @@ /* No comment provided by engineer. */ "you are observer" = "только чтение сообщений"; +/* No comment provided by engineer. */ +"You can accept calls from lock screen, without device and app authentication." = "Вы можете принимать звонки на экране блокировки, без аутентификации."; + /* No comment provided by engineer. */ "You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." = "Вы также можете соединиться, открыв ссылку. Если ссылка откроется в браузере, нажмите кнопку **Open in mobile app**."; +/* No comment provided by engineer. */ +"You can hide or mute a user profile - swipe it to the right.\nSimpleX Lock must be enabled." = "Вы можете скрыть профиль или выключить уведомления - потяните его вправо.\nБлокировка SimpleX должна быть включена."; + /* notification body */ "You can now send messages to %@" = "Вы теперь можете отправлять сообщения %@"; @@ -2583,6 +2703,9 @@ /* No comment provided by engineer. */ "You will join a group this link refers to and connect to its group members." = "Вы вступите в группу, на которую ссылается эта ссылка, и соединитесь с её членами."; +/* No comment provided by engineer. */ +"You will still receive calls and notifications from muted profiles when they are active." = "Вы все равно получите звонки и уведомления в профилях без звука, когда они активные."; + /* No comment provided by engineer. */ "You will stop receiving messages from this group. Chat history will be preserved." = "Вы перестанете получать сообщения от этой группы. История чата будет сохранена."; diff --git a/apps/ios/zh-Hans.lproj/Localizable.strings b/apps/ios/zh-Hans.lproj/Localizable.strings index b7aac89721..5aee818249 100644 --- a/apps/ios/zh-Hans.lproj/Localizable.strings +++ b/apps/ios/zh-Hans.lproj/Localizable.strings @@ -26,7 +26,7 @@ ": " = ": "; /* No comment provided by engineer. */ -"!1 colored!" = "!1 色!"; +"!1 colored!" = "!1 种彩色!"; /* No comment provided by engineer. */ "." = "."; @@ -41,7 +41,7 @@ "[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)" = "[贡献](https://github.com/simplex-chat/simplex-chat#contribute)"; /* No comment provided by engineer. */ -"[Send us email](mailto:chat@simplex.chat)" = "[发送邮件至](mailto:chat@simplex.chat)"; +"[Send us email](mailto:chat@simplex.chat)" = "[给我们发电邮](mailto:chat@simplex.chat)"; /* No comment provided by engineer. */ "[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" = "[在 GitHub 上加星](https://github.com/simplex-chat/simplex-chat)"; @@ -248,6 +248,9 @@ /* No comment provided by engineer. */ "Add to another device" = "添加另一设备"; +/* No comment provided by engineer. */ +"Add welcome message" = "添加欢迎信息"; + /* member role */ "admin" = "管理员"; @@ -338,6 +341,9 @@ /* No comment provided by engineer. */ "Audio & video calls" = "语音和视频通话"; +/* No comment provided by engineer. */ +"Audio and video calls" = "音视频通话"; + /* No comment provided by engineer. */ "audio call (not e2e encrypted)" = "语音通话(非端到端加密)"; @@ -357,7 +363,7 @@ "Auto-accept images" = "自动接受图片"; /* No comment provided by engineer. */ -"Automatically" = "自动地"; +"Automatically" = "自动"; /* No comment provided by engineer. */ "Back" = "返回"; @@ -384,7 +390,7 @@ "By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)." = "通过聊天资料(默认)或者[通过连接](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)。"; /* No comment provided by engineer. */ -"Call already ended!" = "通话已经结束!"; +"Call already ended!" = "通话已结束!"; /* call status */ "call error" = "通话错误"; @@ -398,6 +404,9 @@ /* No comment provided by engineer. */ "Calls" = "通话"; +/* No comment provided by engineer. */ +"Can't delete user profile!" = "无法删除用户个人资料!"; + /* No comment provided by engineer. */ "Can't invite contact!" = "无法邀请联系人!"; @@ -479,6 +488,9 @@ /* No comment provided by engineer. */ "Check server address and try again." = "检查服务器地址并再试一次。"; +/* No comment provided by engineer. */ +"Chinese and Spanish interface" = "中文和西班牙文界面"; + /* No comment provided by engineer. */ "Choose file" = "选择文件"; @@ -498,7 +510,7 @@ "Clear verification" = "清除验证"; /* No comment provided by engineer. */ -"colored" = "有色"; +"colored" = "彩色"; /* No comment provided by engineer. */ "Colors" = "颜色"; @@ -518,6 +530,9 @@ /* No comment provided by engineer. */ "Confirm new passphrase…" = "确认新密码……"; +/* No comment provided by engineer. */ +"Confirm password" = "确认密码"; + /* server test step */ "Connect" = "连接"; @@ -884,6 +899,9 @@ /* No comment provided by engineer. */ "Do NOT use SimpleX for emergency calls." = "请勿使用 SimpleX 进行紧急通话。"; +/* No comment provided by engineer. */ +"Don't show again" = "不再显示"; + /* No comment provided by engineer. */ "Duplicate display name!" = "重复的显示名!"; @@ -965,6 +983,9 @@ /* No comment provided by engineer. */ "Enter passphrase…" = "输入密码……"; +/* No comment provided by engineer. */ +"Enter password above to show!" = "在上面输入密码以显示!"; + /* No comment provided by engineer. */ "Enter server manually" = "手动输入服务器"; @@ -1061,6 +1082,9 @@ /* No comment provided by engineer. */ "Error saving SMP servers" = "保存 SMP 服务器错误"; +/* No comment provided by engineer. */ +"Error saving user password" = "保存用户密码时出错"; + /* No comment provided by engineer. */ "Error sending message" = "发送消息错误"; @@ -1082,6 +1106,9 @@ /* No comment provided by engineer. */ "Error updating settings" = "更新设置错误"; +/* No comment provided by engineer. */ +"Error updating user privacy" = "更新用户隐私时出错"; + /* No comment provided by engineer. */ "Error: %@" = "错误: %@"; @@ -1133,6 +1160,12 @@ /* No comment provided by engineer. */ "Full name:" = "全名:"; +/* No comment provided by engineer. */ +"Fully re-implemented - work in background!" = "完全重新实现 - 在后台工作!"; + +/* No comment provided by engineer. */ +"Further reduced battery usage" = "进一步减少电池使用"; + /* No comment provided by engineer. */ "GIFs and stickers" = "GIF 和贴纸"; @@ -1181,6 +1214,9 @@ /* notification */ "Group message:" = "群组消息:"; +/* No comment provided by engineer. */ +"Group moderation" = "小组审核"; + /* No comment provided by engineer. */ "Group preferences" = "群组偏好设置"; @@ -1193,6 +1229,9 @@ /* snd group event chat item */ "group profile updated" = "群组资料已更新"; +/* No comment provided by engineer. */ +"Group welcome message" = "群欢迎词"; + /* No comment provided by engineer. */ "Group will be deleted for all members - this cannot be undone!" = "将为所有成员删除群组——此操作无法撤消!"; @@ -1203,7 +1242,13 @@ "Help" = "帮助"; /* No comment provided by engineer. */ -"Hidden" = "已隐藏"; +"Hidden" = "隐藏"; + +/* No comment provided by engineer. */ +"Hidden chat profiles" = "隐藏的聊天资料"; + +/* No comment provided by engineer. */ +"Hidden profile password" = "隐藏的个人资料密码"; /* chat item action */ "Hide" = "隐藏"; @@ -1211,6 +1256,9 @@ /* No comment provided by engineer. */ "Hide app screen in the recent apps." = "在最近的应用程序中隐藏应用程序屏幕。"; +/* No comment provided by engineer. */ +"Hide profile" = "隐藏个人资料"; + /* No comment provided by engineer. */ "How it works" = "工作原理"; @@ -1298,6 +1346,9 @@ /* connection level description */ "indirect (%d)" = "间接(%d)"; +/* No comment provided by engineer. */ +"Initial role" = "初始角色"; + /* No comment provided by engineer. */ "Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "安装[用于终端的 SimpleX Chat](https://github.com/simplex-chat/simplex-chat)"; @@ -1436,6 +1487,9 @@ /* No comment provided by engineer. */ "Make a private connection" = "建立私密连接"; +/* No comment provided by engineer. */ +"Make profile private!" = "将个人资料设为私密!"; + /* No comment provided by engineer. */ "Make sure SMP server addresses are in correct format, line separated and are not duplicated (%@)." = "请确保 SMP服 务器地址格式正确,每行一个地址并且不重复 (%@)。"; @@ -1503,7 +1557,7 @@ "Migration error:" = "迁移错误:"; /* No comment provided by engineer. */ -"Migration failed. Tap **Skip** below to continue using the current database. Please report the issue to the app developers via chat or email [chat@simplex.chat](mailto:chat@simplex.chat)." = "迁移失败。点击下面的 **Skip** 继续使用当前数据库。请通过聊天或电子邮件 [chat@simplex.chat](mailto:chat@simplex.chat) 将问题报告给应用程序开发人员。"; +"Migration failed. Tap **Skip** below to continue using the current database. Please report the issue to the app developers via chat or email [chat@simplex.chat](mailto:chat@simplex.chat)." = "迁移失败。点击下面的 **Skip** 继续使用当前数据库。请通过通过聊天或电邮 [chat@simplex.chat](mailto:chat@simplex.chat) 将问题报告给应用程序开发人员。"; /* No comment provided by engineer. */ "Migration is completed" = "迁移完成"; @@ -1532,6 +1586,9 @@ /* No comment provided by engineer. */ "Mute" = "静音"; +/* No comment provided by engineer. */ +"Muted when inactive!" = "不活动时静音!"; + /* No comment provided by engineer. */ "Name" = "名称"; @@ -1604,6 +1661,9 @@ /* No comment provided by engineer. */ "Notifications are disabled!" = "通知被禁用!"; +/* No comment provided by engineer. */ +"Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "现在管理员可以:\n- 删除成员的消息。\n- 禁用成员(“观察员”角色)"; + /* member role */ "observer" = "观察者"; @@ -1612,7 +1672,7 @@ "off" = "关闭"; /* No comment provided by engineer. */ -"Off (Local)" = "关闭 (本地)"; +"Off (Local)" = "关闭(本地)"; /* feature offered item */ "offered %@" = "已提供 %@"; @@ -1630,7 +1690,7 @@ "Old database archive" = "旧数据库存档"; /* group pref value */ -"on" = "在"; +"on" = "开启"; /* No comment provided by engineer. */ "One-time invitation link" = "一次性邀请链接"; @@ -1693,7 +1753,10 @@ "or chat with the developers" = "或与开发者聊天"; /* member role */ -"owner" = "所有者"; +"owner" = "群主"; + +/* No comment provided by engineer. */ +"Password to show" = "显示密码"; /* No comment provided by engineer. */ "Paste" = "粘贴"; @@ -1714,7 +1777,7 @@ "People can connect to you only via the links you share." = "人们只能通过您共享的链接与您建立联系。"; /* No comment provided by engineer. */ -"Periodically" = "定期地"; +"Periodically" = "定期"; /* No comment provided by engineer. */ "PING count" = "PING 次数"; @@ -1794,6 +1857,9 @@ /* No comment provided by engineer. */ "Protect app screen" = "保护应用程序屏幕"; +/* No comment provided by engineer. */ +"Protect your chat profiles with a password!" = "使用密码保护您的聊天资料!"; + /* No comment provided by engineer. */ "Protocol timeout" = "协议超时"; @@ -1873,7 +1939,7 @@ "Reply" = "回复"; /* No comment provided by engineer. */ -"Required" = "必要"; +"Required" = "必须"; /* No comment provided by engineer. */ "Reset" = "重置"; @@ -1926,6 +1992,9 @@ /* No comment provided by engineer. */ "Save and notify group members" = "保存并通知群组成员"; +/* No comment provided by engineer. */ +"Save and update group profile" = "保存和更新组配置文件"; + /* No comment provided by engineer. */ "Save archive" = "保存存档"; @@ -1941,9 +2010,18 @@ /* No comment provided by engineer. */ "Save preferences?" = "保存偏好设置?"; +/* No comment provided by engineer. */ +"Save profile password" = "保存个人资料密码"; + /* No comment provided by engineer. */ "Save servers" = "保存服务器"; +/* No comment provided by engineer. */ +"Save servers?" = "保存服务器?"; + +/* No comment provided by engineer. */ +"Save welcome message?" = "保存欢迎信息?"; + /* No comment provided by engineer. */ "Saved WebRTC ICE servers will be removed" = "已保存的WebRTC ICE服务器将被删除"; @@ -2040,6 +2118,9 @@ /* No comment provided by engineer. */ "Set passphrase to export" = "设置密码来导出"; +/* No comment provided by engineer. */ +"Set the message shown to new members!" = "设置向新成员显示的消息!"; + /* No comment provided by engineer. */ "Set timeouts for proxy/VPN" = "设置代理/VPN的超时时间"; @@ -2145,6 +2226,9 @@ /* No comment provided by engineer. */ "Tap button " = "点击按钮 "; +/* No comment provided by engineer. */ +"Tap to activate profile." = "点击以激活个人资料。"; + /* No comment provided by engineer. */ "Tap to join" = "点击加入"; @@ -2212,7 +2296,7 @@ "The message will be deleted for all members." = "将为所有成员删除该消息。"; /* No comment provided by engineer. */ -"The message will be marked as moderated for all members." = "该消息将对所有成员显示为已审核。"; +"The message will be marked as moderated for all members." = "该消息将对所有成员标记为已被管理员移除。"; /* No comment provided by engineer. */ "The next generation of private messaging" = "下一代私密通讯软件"; @@ -2232,6 +2316,12 @@ /* No comment provided by engineer. */ "Theme" = "主题"; +/* No comment provided by engineer. */ +"There should be at least one user profile." = "应该至少有一个用户资料。"; + +/* No comment provided by engineer. */ +"There should be at least one visible user profile." = "应该至少有一个可见的用户资料。"; + /* No comment provided by engineer. */ "This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "此操作无法撤消——所有接收和发送的文件和媒体都将被删除。 低分辨率图片将保留。"; @@ -2274,6 +2364,9 @@ /* No comment provided by engineer. */ "To record voice message please grant permission to use Microphone." = "请授权使用麦克风以录制语音消息。"; +/* No comment provided by engineer. */ +"To reveal your hidden profile, enter a full password into a search field in **Your chat profiles** page." = "要显示您的隐藏的个人资料,请在**您的聊天个人资料**页面的搜索字段中输入完整密码。"; + /* No comment provided by engineer. */ "To support instant push notifications the chat database has to be migrated." = "为了支持即时推送通知,聊天数据库必须被迁移。"; @@ -2290,7 +2383,7 @@ "Trying to connect to the server used to receive messages from this contact." = "正在尝试连接到用于从该联系人接收消息的服务器。"; /* No comment provided by engineer. */ -"Turn off" = "关掉"; +"Turn off" = "关闭"; /* No comment provided by engineer. */ "Turn off notifications?" = "关闭通知?"; @@ -2307,6 +2400,9 @@ /* No comment provided by engineer. */ "Unexpected migration state" = "未预料的迁移状态"; +/* No comment provided by engineer. */ +"Unhide" = "取消隐藏"; + /* connection info */ "unknown" = "未知"; @@ -2511,6 +2607,9 @@ /* No comment provided by engineer. */ "You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." = "您也可以通过点击链接进行连接。如果在浏览器中打开,请点击“在移动应用程序中打开”按钮。"; +/* No comment provided by engineer. */ +"You can hide or mute a user profile - swipe it to the right.\nSimpleX Lock must be enabled." = "您可以隐藏或静音用户个人资料——只需向右滑动。\n必须启用 SimpleX Lock。"; + /* notification body */ "You can now send messages to %@" = "您现在可以给 %@ 发送消息"; @@ -2604,6 +2703,9 @@ /* No comment provided by engineer. */ "You will join a group this link refers to and connect to its group members." = "您将加入此链接指向的群组并连接到其群组成员。"; +/* No comment provided by engineer. */ +"You will still receive calls and notifications from muted profiles when they are active." = "当静音配置文件处于活动状态时,您仍会收到来自静音配置文件的电话和通知。"; + /* No comment provided by engineer. */ "You will stop receiving messages from this group. Chat history will be preserved." = "您将停止接收来自该群组的消息。聊天记录将被保留。"; diff --git a/blog/20230328-simplex-chat-v4-6-hidden-profiles.md b/blog/20230328-simplex-chat-v4-6-hidden-profiles.md new file mode 100644 index 0000000000..839b1ef2f7 --- /dev/null +++ b/blog/20230328-simplex-chat-v4-6-hidden-profiles.md @@ -0,0 +1,124 @@ +--- +layout: layouts/article.html +title: "SimpleX Chat v4.6 – with hidden profiles, community moderation, improved audio/video calls and reduced battery usage." +date: 2023-03-28 +image: images/20230328-hidden-profiles2.png +imageBottom: true +previewBody: blog_previews/20230328.html +permalink: "/blog/20230328-simplex-chat-v4-6-hidden-profiles.html" +--- + +# SimpleX Chat v4.6 – with hidden profiles, community moderation, improved audio/video calls and reduced battery usage. + +**Published:** Mar 28, 2023 + +## What's new in v4.6 + +- [ARMv7a and Android 8+ support](#armv7a-32-bit-and-android-8-support) +- [hidden chat profiles](#hidden-chat-profiles) +- [community moderation](#community-moderation) +- [improved audio/video calls](#improved-audiovideo-calls) +- [reduced battery usage](#reduced-battery-usage). +- [SMP server monitoring: status bot and page](#smp-server-monitoring) + +Also, we added [Chinese and Spanish interface languages](#chinese-and-spanish-interface---you-can-choose-the-language-via-the-app), thanks to the users' community and Weblate! + +### ARMv7a (32 bit) and Android 8+ support! + +It increases the number of supported Android devices twice - now most of your friends should be able to install SimpleX Chat. SimpleX Chat still doesn't support Android 7 and erlier versions. + +If you install the app from GitHub or F-Droid you need to choose the correct APK, Play Store will provide it automatically. + +### Hidden chat profiles + +       + +For a long time the main way to protect SimpleX Chat app from people who have access to your phone was device authentication - it is requested when you open the app (and some of its sensitive functions). + +Many users asked to allow setting the app password or PIN independently from the device PIN, as some other apps do. But it did not seem a good enough solution - if somebody has your device PIN, then in most cases they can also ask you to provide the app PIN as well. + +So instead of having an app password, that reduces convenience and doesn't improve security too much, we did what we think could be better. You can now create hidden chat profiles, that are not visible anywhere in the app, and do not show any notifications, until you enter a correct passphrase. If multiple profiles are hidden with the same passphrase they will all show in the list when you enter it. + +It's important to remember that these hidden profiles are still stored locally on your device, so if somebody has access to your chat database (they need to know database passphrase, that is independent from device PIN or profile passphrases) or to chat console in the app, they will be able to access these profiles data and reset their passwords. We are considering how chat console can be better protected - e.g., by requiring a separate password or by providing an option to remove it from the UI permanently - tell us what you think. + +### Community moderation + +Initially we did not design SimpleX Chat to support communities - our focus has always been maximum privacy and security. + +SimpleX Chat supports small and fully decentralized groups, that are not hosted anywhere. But many users want to participate in and to discover communities. Since we added support for group links, the groups we created to let users test the app started to grow, and many other communities of over 100 people emerged. + +We are already observing some less-than-friendly messages and undesirable content that is not welcome in some communities. So this version adds features allowing to moderate groups. + +Firstly, group admins and owners can revoke members rights to send messages to the group by assigning members an "observer" role, and also make this role default for users joining via a group link. + +Secondly, group admins can now delete messages sent by other members (excluding the messages sent by the group owners). "Moderate" action in the message menu will either mark the message as deleted or delete it irreversibly for all members, as set in group preferences by the owners. + +These features will allow group owners to decide their own rules. More robust moderation tools are coming later when we build support for large communities. + +### Improved audio/video calls + + + +Prior to this version audio and video calls in iOS app were very limited – they only worked while the app was in foreground. This version fully re-implemented audio/video calls in iOS – it now uses native WebRTC library instead of web view. These calls are still end-to-end encrypted, and compatible with the calls in the previous versions of the app, both on iOS and on Android platforms. + +Where allowed by App Store policy, the calls on iOS now use Apple's native interface for calls CallKit, that allows to accept calls from the lock screen, prevents call interruption by incoming phone calls and optionally allows to include calls in the phone call history - the last option needs to be enabled separately. + +Calls on Android were also improved – they now support bluetooth headphones, allow changing volume in video calls and support proximity sensor during the audio call, to prevent accidental interruption when you hold the phone close to your ear. + +### Reduced battery usage + +We know that battery usage of SimpleX Chat is suboptimal, and we are committed to reduce it. Unfortunately, there is no simple change that we could make to solve this problem, it requires many systematic improvements and fixes. + +One of the big issues, particularly in large groups, was inefficient retry strategy for sending messages in cases when the receiving message queue (mailbox) was out of capacity. + +This version increases the maximum retry period for "out-of-capacity" scenario to 1 hour, and also preserves this retry period in the database. So, if previously before the message expired in 48 hours there were up to ~2800 delivery attempts and up to ~45Mb wasted traffic per recipient (depending on how frequently the app was restarted), now there will be only ~50 retries, resulting in not more than 0.8Mb of traffic - up to 56x traffic reduction when sending messages to the large groups. + +This issue might not have affected you at all, and also solving it won't reduce overall traffic/battery usage by that factor - there are other inefficiences we will be addressing. But if you actively sent messages to large groups you should observe a substantial reduction of battery and traffic consumption. + +Please share your experience. If the battery usage is still suboptimal, please share your usage statistics - they can be requested in chat console with `/get stats` command - it will return the aggregated number of network operations, per server, since the app was started. Please note that these statistics include the addresses of the servers you connect to, so if you want to keep them private, please redact them. You can also reset usage statistics with `/reset stats` command. + +### SMP server monitoring + +If you use preset servers in SimpleX Chat you can now see when we do any maintenance or when the server is down either by [connecting to status bot via the app](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FShQuD-rPokbDvkyotKx5NwM8P3oUXHxA%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEA6fSx1k9zrOmF0BJpCaTarZvnZpMTAVQhd3RkDQ35KT0%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) or by visiting a [status page](https://status.simplex.chat). Status bot always sends automatic messages before the server is restarted for maintenance, but in case of downtime if the same server is down that you use to receive the messages from the bot, you may miss them - check the status page in this case. + +### Chinese and Spanish interface - you can choose the language via the app! + +Thanks to our users' community and to Weblate providing a free hosting plan for SimpleX Chat translations we can now support more languages in the interface – this version adds Chinese and Spanish and more are in progress. + +You can [contribute the translation](https://github.com/simplex-chat/simplex-chat/tree/stable#translate-the-apps) of the apps to your language too! + +The app now supports 8 languages in addition to English - Czech, German, Spanish, French, Italian, Dutch, Russian and Chinese. You can now choose the language via the app settings (Appearance page), independently from the system settings. + +## SimpleX platform + +Some links to answer the most common questions: + +[SimpleX Chat security assessment](./20221108-simplex-chat-v4.2-security-audit-new-website.md). + +[How can SimpleX deliver messages without user identifiers](https://simplex.chat/#how-simplex-works). + +[What are the risks to have identifiers assigned to the users](https://simplex.chat/#why-ids-bad-for-privacy). + +[Technical details and limitations](https://github.com/simplex-chat/simplex-chat#privacy-technical-details-and-limitations). + +[How SimpleX is different from Session, Matrix, Signal, etc.](https://github.com/simplex-chat/simplex-chat/blob/stable/README.md#frequently-asked-questions). + +Visit our [website](https://simplex.chat) to learn more. + +## Help us with donations + +Huge thank you to everybody who donated to SimpleX Chat! + +We are prioritizing users privacy and security - it would be impossible without your support. + +Our pledge to our users is that SimpleX protocols are and will remain open, and in public domain, - so anybody can build the future implementations of the clients and the servers. We are building SimpleX platform based on the same principles as email and web, but much more private and secure. + +Your donations help us raise more funds – any amount, even the price of the cup of coffee, makes a big difference for us. + +See [this section](https://github.com/simplex-chat/simplex-chat/tree/master#help-us-with-donations) for the ways to donate. + +Thank you, + +Evgeny + +SimpleX Chat founder diff --git a/blog/images/20230328-call1.png b/blog/images/20230328-call1.png new file mode 100644 index 0000000000..51a6fcd174 Binary files /dev/null and b/blog/images/20230328-call1.png differ diff --git a/blog/images/20230328-call2.png b/blog/images/20230328-call2.png new file mode 100644 index 0000000000..ef2fef9791 Binary files /dev/null and b/blog/images/20230328-call2.png differ diff --git a/blog/images/20230328-hidden-profiles1.png b/blog/images/20230328-hidden-profiles1.png new file mode 100644 index 0000000000..4121598650 Binary files /dev/null and b/blog/images/20230328-hidden-profiles1.png differ diff --git a/blog/images/20230328-hidden-profiles2.png b/blog/images/20230328-hidden-profiles2.png new file mode 100644 index 0000000000..cd10d435c1 Binary files /dev/null and b/blog/images/20230328-hidden-profiles2.png differ diff --git a/blog/images/20230328-hidden-profiles3.png b/blog/images/20230328-hidden-profiles3.png new file mode 100644 index 0000000000..1fe36d1d4e Binary files /dev/null and b/blog/images/20230328-hidden-profiles3.png differ diff --git a/cabal.project b/cabal.project index b05e83ecd0..cc143cc0fb 100644 --- a/cabal.project +++ b/cabal.project @@ -7,7 +7,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: c5eb65fec873e0493c28af8b190c3458445d1811 + tag: 6a665a083387fe7145d161957f0fcab223a48838 source-repository-package type: git diff --git a/flake.lock b/flake.lock index c628113e63..2c133a9b63 100644 --- a/flake.lock +++ b/flake.lock @@ -51,11 +51,11 @@ "cabal-34": { "flake": false, "locked": { - "lastModified": 1640353650, - "narHash": "sha256-N1t6M3/wqj90AEdRkeC8i923gQYUpzSr8b40qVOZ1Rk=", + "lastModified": 1645834128, + "narHash": "sha256-wG3d+dOt14z8+ydz4SL7pwGfe7SiimxcD/LOuPCV6xM=", "owner": "haskell", "repo": "cabal", - "rev": "942639c18c0cd8ec53e0a6f8d120091af35312cd", + "rev": "5ff598c67f53f7c4f48e31d722ba37172230c462", "type": "github" }, "original": { @@ -68,11 +68,11 @@ "cabal-36": { "flake": false, "locked": { - "lastModified": 1641652457, - "narHash": "sha256-BlFPKP4C4HRUJeAbdembX1Rms1LD380q9s0qVDeoAak=", + "lastModified": 1669081697, + "narHash": "sha256-I5or+V7LZvMxfbYgZATU4awzkicBwwok4mVoje+sGmU=", "owner": "haskell", "repo": "cabal", - "rev": "f27667f8ec360c475027dcaee0138c937477b070", + "rev": "8fd619e33d34924a94e691c5fea2c42f0fc7f144", "type": "github" }, "original": { @@ -159,15 +159,16 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1635892615, - "narHash": "sha256-harGbMZr4hzat2BWBU+Y5OYXlu+fVz7E4WeQzHi5o8A=", + "lastModified": 1672831974, + "narHash": "sha256-z9k3MfslLjWQfnjBtEtJZdq3H7kyi2kQtUThfTgdRk0=", "owner": "input-output-hk", "repo": "flake-compat", - "rev": "eca47d3377946315596da653862d341ee5341318", + "rev": "45f2638735f8cdc40fe302742b79f248d23eb368", "type": "github" }, "original": { "owner": "input-output-hk", + "ref": "hkm/gitlab-fix", "repo": "flake-compat", "type": "github" } @@ -190,11 +191,11 @@ }, "flake-utils": { "locked": { - "lastModified": 1667395993, - "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "lastModified": 1676283394, + "narHash": "sha256-XX2f9c3iySLCw54rJ/CZs+ZK6IQy7GXNY4nSOyu2QG4=", "owner": "numtide", "repo": "flake-utils", - "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "rev": "3db36a8b464d0c4532ba1c7dda728f4576d6d073", "type": "github" }, "original": { @@ -205,11 +206,11 @@ }, "flake-utils_2": { "locked": { - "lastModified": 1644229661, - "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=", + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", "owner": "numtide", "repo": "flake-utils", - "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", "type": "github" }, "original": { @@ -248,21 +249,6 @@ "type": "github" } }, - "flake-utils_5": { - "locked": { - "lastModified": 1653893745, - "narHash": "sha256-0jntwV3Z8//YwuOjzhV2sgJJPt+HY6KhU7VZUL0fKZQ=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "1ed9fb1935d260de5fe1c2f7ee0ebaae17ed2fa1", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "ghc-8.6.5-iohk": { "flake": false, "locked": { @@ -302,11 +288,11 @@ "hackage": { "flake": false, "locked": { - "lastModified": 1672446463, - "narHash": "sha256-N5dcK1V+BLQeri5oB0ZSk2ABPs/hTGBC7jZvcY9CVLs=", + "lastModified": 1676679913, + "narHash": "sha256-nW7ApRgiA9uChV/UrW89HK75rIToLt7XtSrkodO0Nbc=", "owner": "input-output-hk", "repo": "hackage.nix", - "rev": "7289869780da23633bc193e11d2da94ecee1489d", + "rev": "c37cffd51315d8e27dd8d3faf75abf897e39c8c8", "type": "github" }, "original": { @@ -330,6 +316,7 @@ ], "hpc-coveralls": "hpc-coveralls", "hydra": "hydra", + "iserv-proxy": "iserv-proxy", "nixpkgs": [ "nixpkgs" ], @@ -337,21 +324,23 @@ "nixpkgs-2105": "nixpkgs-2105", "nixpkgs-2111": "nixpkgs-2111", "nixpkgs-2205": "nixpkgs-2205", + "nixpkgs-2211": "nixpkgs-2211", "nixpkgs-unstable": "nixpkgs-unstable", "old-ghc-nix": "old-ghc-nix", "stackage": "stackage", "tullia": "tullia" }, "locked": { - "lastModified": 1672501055, - "narHash": "sha256-Wy6KqoYqQOP1rBvfHUvM3ex8HIdBA4kwnvZj2qQ1VLU=", - "owner": "simplex-chat", + "lastModified": 1677975916, + "narHash": "sha256-dbe8lEEPyfzjdRwpePClv7J9p9lQg7BwbBqAMCw4RLw=", + "owner": "input-output-hk", "repo": "haskell.nix", - "rev": "dc719cd6dc318923c4a524d005c13f474c00d3d3", + "rev": "ab5efd87ce3fd8ade38a01d97693d29a4f1ae7e4", "type": "github" }, "original": { - "owner": "simplex-chat", + "owner": "input-output-hk", + "ref": "armv7a", "repo": "haskell.nix", "type": "github" } @@ -383,11 +372,11 @@ ] }, "locked": { - "lastModified": 1646878427, - "narHash": "sha256-KtbrofMtN8GlM7D+n90kixr7QpSlVmdN+vK5CA/aRzc=", + "lastModified": 1671755331, + "narHash": "sha256-hXsgJj0Cy0ZiCiYdW2OdBz5WmFyOMKuw4zyxKpgUKm4=", "owner": "NixOS", "repo": "hydra", - "rev": "28b682b85b7efc5cf7974065792a1f22203a5927", + "rev": "f48f00ee6d5727ae3e488cbf9ce157460853fea8", "type": "github" }, "original": { @@ -395,6 +384,46 @@ "type": "indirect" } }, + "incl": { + "inputs": { + "nixlib": [ + "haskellNix", + "tullia", + "std", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1669263024, + "narHash": "sha256-E/+23NKtxAqYG/0ydYgxlgarKnxmDbg6rCMWnOBqn9Q=", + "owner": "divnix", + "repo": "incl", + "rev": "ce7bebaee048e4cd7ebdb4cee7885e00c4e2abca", + "type": "github" + }, + "original": { + "owner": "divnix", + "repo": "incl", + "type": "github" + } + }, + "iserv-proxy": { + "flake": false, + "locked": { + "lastModified": 1670983692, + "narHash": "sha256-avLo34JnI9HNyOuauK5R69usJm+GfW3MlyGlYxZhTgY=", + "ref": "hkm/remote-iserv", + "rev": "50d0abb3317ac439a4e7495b185a64af9b7b9300", + "revCount": 10, + "type": "git", + "url": "https://gitlab.haskell.org/hamishmack/iserv-proxy.git" + }, + "original": { + "ref": "hkm/remote-iserv", + "type": "git", + "url": "https://gitlab.haskell.org/hamishmack/iserv-proxy.git" + } + }, "lowdown-src": { "flake": false, "locked": { @@ -411,25 +440,14 @@ "type": "github" } }, - "mdbook-kroki-preprocessor": { - "flake": false, - "locked": { - "lastModified": 1661755005, - "narHash": "sha256-1TJuUzfyMycWlOQH67LR63/ll2GDZz25I3JfScy/Jnw=", - "owner": "JoelCourtney", - "repo": "mdbook-kroki-preprocessor", - "rev": "93adb5716d035829efed27f65f2f0833a7d3e76f", - "type": "github" - }, - "original": { - "owner": "JoelCourtney", - "repo": "mdbook-kroki-preprocessor", - "type": "github" - } - }, "n2c": { "inputs": { - "flake-utils": "flake-utils_5", + "flake-utils": [ + "haskellNix", + "tullia", + "std", + "flake-utils" + ], "nixpkgs": [ "haskellNix", "tullia", @@ -458,16 +476,16 @@ "nixpkgs-regression": "nixpkgs-regression" }, "locked": { - "lastModified": 1643066034, - "narHash": "sha256-xEPeMcNJVOeZtoN+d+aRwolpW8mFSEQx76HTRdlhPhg=", + "lastModified": 1661606874, + "narHash": "sha256-9+rpYzI+SmxJn+EbYxjGv68Ucp22bdFUSy/4LkHkkDQ=", "owner": "NixOS", "repo": "nix", - "rev": "a1cd7e58606a41fcf62bf8637804cf8306f17f62", + "rev": "11e45768b34fdafdcf019ddbd337afa16127ff0f", "type": "github" }, "original": { "owner": "NixOS", - "ref": "2.6.0", + "ref": "2.11.0", "repo": "nix", "type": "github" } @@ -563,17 +581,18 @@ }, "nixpkgs": { "locked": { - "lastModified": 1632864508, - "narHash": "sha256-d127FIvGR41XbVRDPVvozUPQ/uRHbHwvfyKHwEt5xFM=", + "lastModified": 1657693803, + "narHash": "sha256-G++2CJ9u0E7NNTAi9n5G8TdDmGJXcIjkJ3NF8cetQB8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "82891b5e2c2359d7e58d08849e4c89511ab94234", + "rev": "365e1b3a859281cf11b94f87231adeabbdd878a2", "type": "github" }, "original": { - "id": "nixpkgs", - "ref": "nixos-21.05-small", - "type": "indirect" + "owner": "NixOS", + "ref": "nixos-22.05-small", + "repo": "nixpkgs", + "type": "github" } }, "nixpkgs-2003": { @@ -626,11 +645,11 @@ }, "nixpkgs-2205": { "locked": { - "lastModified": 1663981975, - "narHash": "sha256-TKaxWAVJR+a5JJauKZqibmaM5e/Pi5tBDx9s8fl/kSE=", + "lastModified": 1672580127, + "narHash": "sha256-3lW3xZslREhJogoOkjeZtlBtvFMyxHku7I/9IVehhT8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "309faedb8338d3ae8ad8f1043b3ccf48c9cc2970", + "rev": "0874168639713f547c05947c76124f78441ea46c", "type": "github" }, "original": { @@ -640,6 +659,22 @@ "type": "github" } }, + "nixpkgs-2211": { + "locked": { + "lastModified": 1675730325, + "narHash": "sha256-uNvD7fzO5hNlltNQUAFBPlcEjNG5Gkbhl/ROiX+GZU4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b7ce17b1ebf600a72178f6302c77b6382d09323f", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-22.11-darwin", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs-regression": { "locked": { "lastModified": 1643052045, @@ -650,18 +685,19 @@ "type": "github" }, "original": { - "id": "nixpkgs", + "owner": "NixOS", + "repo": "nixpkgs", "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", - "type": "indirect" + "type": "github" } }, "nixpkgs-unstable": { "locked": { - "lastModified": 1663905476, - "narHash": "sha256-0CSwRKaYravh9v6qSlBpM0gNg0UhKT2lL7Yn6Zbx7UM=", + "lastModified": 1675758091, + "narHash": "sha256-7gFSQbSVAFUHtGCNHPF7mPc5CcqDk9M2+inlVPZSneg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e14f9fb57315f0d4abde222364f19f88c77d2b79", + "rev": "747927516efcb5e31ba03b7ff32f61f6d47e7d87", "type": "github" }, "original": { @@ -720,20 +756,35 @@ }, "nixpkgs_5": { "locked": { - "lastModified": 1669833724, - "narHash": "sha256-/HEZNyGbnQecrgJnfE8d0WC5c1xuPSD2LUpB6YXlg4c=", - "owner": "nixos", + "lastModified": 1676726892, + "narHash": "sha256-M7OYVR6dKmzmlebIjybFf3l18S2uur8lMyWWnHQooLY=", + "owner": "angerman", "repo": "nixpkgs", - "rev": "4d2b37a84fad1091b9de401eb450aae66f1a741e", + "rev": "729469087592bdea58b360de59dadf6d58714c42", "type": "github" }, "original": { - "owner": "nixos", - "ref": "22.11", + "owner": "angerman", + "ref": "release-22.11", "repo": "nixpkgs", "type": "github" } }, + "nosys": { + "locked": { + "lastModified": 1667881534, + "narHash": "sha256-FhwJ15uPLRsvaxtt/bNuqE/ykMpNAPF0upozFKhTtXM=", + "owner": "divnix", + "repo": "nosys", + "rev": "2d0d5207f6a230e9d0f660903f8db9807b54814f", + "type": "github" + }, + "original": { + "owner": "divnix", + "repo": "nosys", + "type": "github" + } + }, "old-ghc-nix": { "flake": false, "locked": { @@ -762,11 +813,11 @@ "stackage": { "flake": false, "locked": { - "lastModified": 1669598217, - "narHash": "sha256-UioviNyxA3fexeguXLQpgMR6uWL9Q/wulipCbET3C8w=", + "lastModified": 1677888571, + "narHash": "sha256-YkhRNOaN6QVagZo1cfykYV8KqkI8/q6r2F5+jypOma4=", "owner": "input-output-hk", "repo": "stackage.nix", - "rev": "8400280d894949e26354123aebc20801a2165182", + "rev": "cb50e6fabdfb2d7e655059039012ad0623f06a27", "type": "github" }, "original": { @@ -777,17 +828,23 @@ }, "std": { "inputs": { + "arion": [ + "haskellNix", + "tullia", + "std", + "blank" + ], "blank": "blank", "devshell": "devshell", "dmerge": "dmerge", "flake-utils": "flake-utils_4", + "incl": "incl", "makes": [ "haskellNix", "tullia", "std", "blank" ], - "mdbook-kroki-preprocessor": "mdbook-kroki-preprocessor", "microvm": [ "haskellNix", "tullia", @@ -797,14 +854,15 @@ "n2c": "n2c", "nixago": "nixago", "nixpkgs": "nixpkgs_4", + "nosys": "nosys", "yants": "yants" }, "locked": { - "lastModified": 1665513321, - "narHash": "sha256-D6Pacw9yf/HMs84KYuCxHXnNDL7v43gtcka5URagFqE=", + "lastModified": 1674526466, + "narHash": "sha256-tMTaS0bqLx6VJ+K+ZT6xqsXNpzvSXJTmogkraBGzymg=", "owner": "divnix", "repo": "std", - "rev": "94a90eedb9cfc115b12ae8f6622d9904788559e4", + "rev": "516387e3d8d059b50e742a2ff1909ed3c8f82826", "type": "github" }, "original": { @@ -824,11 +882,11 @@ "std": "std" }, "locked": { - "lastModified": 1666200256, - "narHash": "sha256-cJPS8zBu30SMhxMe7I8DWutwqMuhPsEez87y9gxMKc4=", + "lastModified": 1675695930, + "narHash": "sha256-B7rEZ/DBUMlK1AcJ9ajnAPPxqXY6zW2SBX+51bZV0Ac=", "owner": "input-output-hk", "repo": "tullia", - "rev": "575362c2244498e8d2c97f72861510fa72e75d44", + "rev": "621365f2c725608f381b3ad5b57afef389fd4c31", "type": "github" }, "original": { @@ -862,11 +920,11 @@ ] }, "locked": { - "lastModified": 1660507851, - "narHash": "sha256-BKjq7JnVuUR/xDtcv6Vm9GYGKAblisXrAgybor9hT/s=", + "lastModified": 1667096281, + "narHash": "sha256-wRRec6ze0gJHmGn6m57/zhz/Kdvp9HS4Nl5fkQ+uIuA=", "owner": "divnix", "repo": "yants", - "rev": "0b895ca02a8fa72bad50b454cb3e7d8a66407c96", + "rev": "d18f356ec25cb94dc9c275870c3a7927a10f8c3c", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 5279db48e8..f7e86fbb6f 100644 --- a/flake.nix +++ b/flake.nix @@ -1,7 +1,7 @@ { description = "nix flake for simplex-chat"; - inputs.nixpkgs.url = "github:nixos/nixpkgs/22.11"; - inputs.haskellNix.url = "github:simplex-chat/haskell.nix"; + inputs.nixpkgs.url = "github:angerman/nixpkgs/release-22.11"; + inputs.haskellNix.url = "github:input-output-hk/haskell.nix/armv7a"; inputs.haskellNix.inputs.nixpkgs.follows = "nixpkgs"; inputs.hackage = { url = "github:input-output-hk/hackage.nix"; @@ -12,7 +12,23 @@ outputs = { self, haskellNix, nixpkgs, flake-utils, ... }: let systems = [ "x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin" ]; in flake-utils.lib.eachSystem systems (system: - let pkgs = haskellNix.legacyPackages.${system}; in + # this android26 overlay makes the pkgsCross.{aarch64-android,armv7a-android-prebuilt} to set stdVer to 26 (Android 8). + let android26 = final: prev: { + pkgsCross = prev.pkgsCross // { + aarch64-android = import prev.path { + inherit system; + inherit (prev) overlays; + crossSystem = prev.lib.systems.examples.aarch64-android // { sdkVer = "26"; }; + }; + armv7a-android-prebuilt = import prev.path { + inherit system; + inherit (prev) overlays; + crossSystem = prev.lib.systems.examples.armv7a-android-prebuilt // { sdkVer = "26"; }; + }; + }; + }; in + # `appendOverlays` with a singleton is identical to `extend`. + let pkgs = haskellNix.legacyPackages.${system}.appendOverlays [android26]; in let drv' = { extra-modules, pkgs', ... }: pkgs'.haskell-nix.project { compiler-nix-name = "ghc8107"; index-state = "2022-06-20T00:00:00Z"; @@ -81,6 +97,7 @@ "x86_64-linux" = let androidPkgs = pkgs.pkgsCross.aarch64-android; + android32Pkgs = pkgs.pkgsCross.armv7a-android-prebuilt; # For some reason building libiconv with nixpgks android setup produces # LANGINFO_CODESET to be found, which is not compatible with android sdk 23; # so we'll patch up iconv to not include that. @@ -96,11 +113,32 @@ androidFFI = androidPkgs.libffi.overrideAttrs (old: { dontDisableStatic = true; hardeningDisable = [ "fortify" ]; - postConfigure = '' - echo "#undef HAVE_MEMFD_CREATE" >> aarch64-unknown-linux-android/fficonfig.h - ''; + }); + android32FFI = android32Pkgs.libffi.overrideAttrs (old: { + dontDisableStatic = true; + hardeningDisable = [ "fortify" ]; } );in { + "${pkgs.pkgsCross.musl64.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.musl64).simplex-chat.components.exes.simplex-chat; + "${pkgs.pkgsCross.musl32.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.musl32).simplex-chat.components.exes.simplex-chat; + # "${pkgs.pkgsCross.muslpi.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.muslpi).simplex-chat.components.exes.simplex-chat; + "${pkgs.pkgsCross.aarch64-multiplatform-musl.hostPlatform.system}-static:exe:simplex-chat" = (drv pkgs.pkgsCross.aarch64-multiplatform-musl).simplex-chat.components.exes.simplex-chat; + "armv7a-android:lib:support" = (drv android32Pkgs).android-support.components.library.override { + smallAddressSpace = true; enableShared = false; + setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsupport.so" ]; + postInstall = '' + + mkdir -p $out/_pkg + cp libsupport.so $out/_pkg + ${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so.1 $out/_pkg/libsupport.so + (cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-armv7a-android-libsupport.zip *) + rm -fR $out/_pkg + + mkdir -p $out/nix-support + echo "file binary-dist \"$(echo $out/*.zip)\"" \ + > $out/nix-support/hydra-build-products + ''; + }; "aarch64-android:lib:support" = (drv androidPkgs).android-support.components.library.override { smallAddressSpace = true; enableShared = false; setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsupport.so" ]; @@ -117,6 +155,67 @@ > $out/nix-support/hydra-build-products ''; }; + "armv7a-android:lib:simplex-chat" = (drv' { + pkgs' = android32Pkgs; + extra-modules = [{ + packages.direct-sqlcipher.flags.openssl = true; + packages.direct-sqlcipher.components.library.libs = pkgs.lib.mkForce [ + (android32Pkgs.openssl.override { static = true; enableKTLS = false; }) + ]; + packages.direct-sqlcipher.patches = [ + ./scripts/nix/direct-sqlcipher-android-log.patch + ]; + }]; + }).simplex-chat.components.library.override { + smallAddressSpace = true; enableShared = false; + # for android we build a shared library, passing these arguments is a bit tricky, as + # we want only the threaded rts (HSrts_thr) and ffi to be linked, but not fed into iserv for + # template haskell cross compilation. Thus we just pass them as linker options (-optl). + setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsimplex.so" "-optl-lHSrts_thr" "-optl-lffi"]; + postInstall = '' + set -x + ${pkgs.tree}/bin/tree $out + mkdir -p $out/_pkg + # copy over includes, we might want those, but maybe not. + # cp -r $out/lib/*/*/include $out/_pkg/ + # find the libHS...ghc-X.Y.Z.a static library; this is the + # rolled up one with all dependencies included. + cp libsimplex.so $out/_pkg + # find ./dist -name "lib*.so" -exec cp {} $out/_pkg \; + # find ./dist -name "libHS*-ghc*.a" -exec cp {} $out/_pkg \; + # find ${android32FFI}/lib -name "*.a" -exec cp {} $out/_pkg \; + # find ${android32Pkgs.gmp6.override { withStatic = true; }}/lib -name "*.a" -exec cp {} $out/_pkg \; + # find ${androidIconv}/lib -name "*.a" -exec cp {} $out/_pkg \; + # find ${android32Pkgs.stdenv.cc.libc}/lib -name "*.a" -exec cp {} $out/_pkg \; + echo ${android32Pkgs.openssl.override { enableKTLS = false; }} + find ${(android32Pkgs.openssl.override { enableKTLS = false; }).out}/lib -name "*.so" -exec cp {} $out/_pkg \; + + # remove the .1 and other version suffixes from .so's. Androids linker + # doesn't play nice with them. + for lib in $out/_pkg/*.so; do + for dep in $(${pkgs.patchelf}/bin/patchelf --print-needed "$lib"); do + if [[ "''${dep##*.so}" ]]; then + echo "$lib : $dep -> ''${dep%%.so*}.so" + chmod +w "$lib" + ${pkgs.patchelf}/bin/patchelf --replace-needed "$dep" "''${dep%%.so*}.so" "$lib" + fi + done + done + + for lib in $out/_pkg/*.so; do + chmod +w "$lib" + ${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so "$lib" + [[ "$lib" != *libsimplex.so ]] && ${pkgs.patchelf}/bin/patchelf --set-soname "$(basename -a $lib)" "$lib" + done + + ${pkgs.tree}/bin/tree $out/_pkg + (cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-armv7a-android-libsimplex.zip *) + rm -fR $out/_pkg + mkdir -p $out/nix-support + echo "file binary-dist \"$(echo $out/*.zip)\"" \ + > $out/nix-support/hydra-build-products + ''; + }; "aarch64-android:lib:simplex-chat" = (drv' { pkgs' = androidPkgs; extra-modules = [{ @@ -178,110 +277,6 @@ > $out/nix-support/hydra-build-products ''; }; - "x86_64-android:lib:support" = (drv androidPkgs).android-support.components.library.override { - smallAddressSpace = true; enableShared = false; - setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsupport.so" ]; - postInstall = '' - - mkdir -p $out/_pkg - cp libsupport.so $out/_pkg - ${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so.1 $out/_pkg/libsupport.so - (cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-x86_64-android-libsupport.zip *) - rm -fR $out/_pkg - - mkdir -p $out/nix-support - echo "file binary-dist \"$(echo $out/*.zip)\"" \ - > $out/nix-support/hydra-build-products - ''; - }; - "x86_64-android:lib:simplex-chat" = (drv' { - pkgs' = androidPkgs; - extra-modules = [{ - packages.direct-sqlcipher.flags.openssl = true; - }]; - }).simplex-chat.components.library.override { - smallAddressSpace = true; enableShared = false; - # for android we build a shared library, passing these arguments is a bit tricky, as - # we want only the threaded rts (HSrts_thr) and ffi to be linked, but not fed into iserv for - # template haskell cross compilation. Thus we just pass them as linker options (-optl). - setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsimplex.so" "-optl-lHSrts_thr" "-optl-lffi"]; - postInstall = '' - ${pkgs.tree}/bin/tree $out - mkdir -p $out/_pkg - # copy over includes, we might want those, but maybe not. - # cp -r $out/lib/*/*/include $out/_pkg/ - # find the libHS...ghc-X.Y.Z.a static library; this is the - # rolled up one with all dependencies included. - cp libsimplex.so $out/_pkg - # find ./dist -name "lib*.so" -exec cp {} $out/_pkg \; - # find ./dist -name "libHS*-ghc*.a" -exec cp {} $out/_pkg \; - # find ${androidFFI}/lib -name "*.a" -exec cp {} $out/_pkg \; - # find ${androidPkgs.gmp6.override { withStatic = true; }}/lib -name "*.a" -exec cp {} $out/_pkg \; - # find ${androidIconv}/lib -name "*.a" -exec cp {} $out/_pkg \; - # find ${androidPkgs.stdenv.cc.libc}/lib -name "*.a" -exec cp {} $out/_pkg \; - - ${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so.1 $out/_pkg/libsimplex.so - - ${pkgs.tree}/bin/tree $out/_pkg - (cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-x86_64-android-libsimplex.zip *) - rm -fR $out/_pkg - mkdir -p $out/nix-support - echo "file binary-dist \"$(echo $out/*.zip)\"" \ - > $out/nix-support/hydra-build-products - ''; - }; - "x86_64-linux:lib:support" = (drv androidPkgs).android-support.components.library.override { - smallAddressSpace = true; enableShared = false; - setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsupport.so" ]; - postInstall = '' - - mkdir -p $out/_pkg - cp libsupport.so $out/_pkg - ${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so.1 $out/_pkg/libsupport.so - (cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-x86_64-linux-libsupport.zip *) - rm -fR $out/_pkg - - mkdir -p $out/nix-support - echo "file binary-dist \"$(echo $out/*.zip)\"" \ - > $out/nix-support/hydra-build-products - ''; - }; - "x86_64-linux:lib:simplex-chat" = (drv' { - pkgs' = androidPkgs; - extra-modules = [{ - packages.direct-sqlcipher.flags.openssl = true; - }]; - }).simplex-chat.components.library.override { - smallAddressSpace = true; enableShared = false; - # for android we build a shared library, passing these arguments is a bit tricky, as - # we want only the threaded rts (HSrts_thr) and ffi to be linked, but not fed into iserv for - # template haskell cross compilation. Thus we just pass them as linker options (-optl). - setupBuildFlags = map (x: "--ghc-option=${x}") [ "-shared" "-o" "libsimplex.so" "-optl-lHSrts_thr" "-optl-lffi"]; - postInstall = '' - ${pkgs.tree}/bin/tree $out - mkdir -p $out/_pkg - # copy over includes, we might want those, but maybe not. - # cp -r $out/lib/*/*/include $out/_pkg/ - # find the libHS...ghc-X.Y.Z.a static library; this is the - # rolled up one with all dependencies included. - cp libsimplex.so $out/_pkg - # find ./dist -name "lib*.so" -exec cp {} $out/_pkg \; - # find ./dist -name "libHS*-ghc*.a" -exec cp {} $out/_pkg \; - # find ${androidFFI}/lib -name "*.a" -exec cp {} $out/_pkg \; - # find ${androidPkgs.gmp6.override { withStatic = true; }}/lib -name "*.a" -exec cp {} $out/_pkg \; - # find ${androidIconv}/lib -name "*.a" -exec cp {} $out/_pkg \; - # find ${androidPkgs.stdenv.cc.libc}/lib -name "*.a" -exec cp {} $out/_pkg \; - - ${pkgs.patchelf}/bin/patchelf --remove-needed libunwind.so.1 $out/_pkg/libsimplex.so - - ${pkgs.tree}/bin/tree $out/_pkg - (cd $out/_pkg; ${pkgs.zip}/bin/zip -r -9 $out/pkg-x86_64-linux-libsimplex.zip *) - rm -fR $out/_pkg - mkdir -p $out/nix-support - echo "file binary-dist \"$(echo $out/*.zip)\"" \ - > $out/nix-support/hydra-build-products - ''; - }; }; # builds for iOS and iOS simulator diff --git a/scripts/android/build-android.sh b/scripts/android/build-android.sh index 9928a07366..6ff2f6e446 100755 --- a/scripts/android/build-android.sh +++ b/scripts/android/build-android.sh @@ -12,9 +12,9 @@ nix_install() { [ ! -d /nix ] && sudo sh -c "mkdir -p /nix && chown -R $u /nix" # Install nix - nix_ver="nix-2.11.1" + nix_ver="nix-2.14.1" nix_url="https://releases.nixos.org/nix/$nix_ver/install" - nix_hash="4569a01dc5f62056f29f3195673bc3242fc70bf2474927fb5d8549c4d997402d" + nix_hash="565974057264f0536f600c68d59395927cd73e9fc5a60f33c1906e8f7bc33fcf" curl -sSf "$nix_url" -o "$tmp/nix-install" printf "%s %s" "$nix_hash" "$tmp/nix-install" | sha256sum -c @@ -29,11 +29,16 @@ nix_setup() { } git_setup() { + [ "$folder" != "." ] && { + git clone --depth=1 https://github.com/simplex-chat/simplex-chat "$folder" + } + # Switch to nix-android branch git -C "$folder" checkout "$commit" # Create missing folders mkdir -p "$folder/apps/android/app/src/main/cpp/libs/arm64-v8a" + mkdir -p "$folder/apps/android/app/src/main/cpp/libs/armeabi-v7a" } checks() { @@ -55,10 +60,6 @@ checks() { esac done - [ "$folder" != "." ] && { - git clone https://github.com/simplex-chat/simplex-chat "$folder" - } - if [ -n "$commands_failed" ]; then commands_failed=${commands_failed% *} printf "%s is not found in your \$PATH. Please install them and re-run the script.\n" "$commands_failed" @@ -72,21 +73,31 @@ build() { # Build simplex lib nix build "$folder#hydraJobs.aarch64-android:lib:simplex-chat.x86_64-linux" unzip -o "$PWD/result/pkg-aarch64-android-libsimplex.zip" -d "$folder/apps/android/app/src/main/cpp/libs/arm64-v8a" + + nix build "$folder#hydraJobs.armv7a-android:lib:simplex-chat.x86_64-linux" + unzip -o "$PWD/result/pkg-armv7a-android-libsimplex.zip" -d "$folder/apps/android/app/src/main/cpp/libs/armeabi-v7a" # Build android suppprt lib nix build "$folder#hydraJobs.aarch64-android:lib:support.x86_64-linux" unzip -o "$PWD/result/pkg-aarch64-android-libsupport.zip" -d "$folder/apps/android/app/src/main/cpp/libs/arm64-v8a" + nix build "$folder#hydraJobs.armv7a-android:lib:support.x86_64-linux" + unzip -o "$PWD/result/pkg-armv7a-android-libsupport.zip" -d "$folder/apps/android/app/src/main/cpp/libs/armeabi-v7a" + sed -i.bak 's/${extract_native_libs}/true/' "$folder/apps/android/app/src/main/AndroidManifest.xml" + sed -i.bak '/android {/a lint {abortOnError false}' "$folder/apps/android/app/build.gradle" gradle -p "$folder/apps/android/" clean build assembleRelease - mkdir -p "$tmp/android" - unzip -oqd "$tmp/android/" "$folder/apps/android/app/build/outputs/apk/release/app-release-unsigned.apk" + mkdir -p "$tmp/android-aarch64" + unzip -oqd "$tmp/android-aarch64/" "$folder/apps/android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned.apk" + (cd "$tmp/android-aarch64" && zip -rq5 "$tmp/simplex-chat-aarch64.apk" . && zip -rq0 "$tmp/simplex-chat-aarch64.apk" resources.arsc res) + zipalign -p -f 4 "$tmp/simplex-chat-aarch64.apk" "$PWD/simplex-chat-aarch64.apk" - (cd "$tmp/android" && zip -rq5 "$tmp/simplex-chat.apk" . && zip -rq0 "$tmp/simplex-chat.apk" resources.arsc res) - - zipalign -p -f 4 "$tmp/simplex-chat.apk" "$PWD/simplex-chat.apk" + mkdir -p "$tmp/android-armv7" + unzip -oqd "$tmp/android-armv7/" "$folder/apps/android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned.apk" + (cd "$tmp/android-armv7" && zip -rq5 "$tmp/simplex-chat-armv7.apk" . && zip -rq0 "$tmp/simplex-chat-armv7.apk" resources.arsc res) + zipalign -p -f 4 "$tmp/simplex-chat-armv7.apk" "$PWD/simplex-chat-armv7.apk" } final() { diff --git a/scripts/android/compress-and-sign-apk.sh b/scripts/android/compress-and-sign-apk.sh index 1bc904af01..694dcade8d 100755 --- a/scripts/android/compress-and-sign-apk.sh +++ b/scripts/android/compress-and-sign-apk.sh @@ -18,29 +18,31 @@ fi cd $apk_parent_dir -ORIG_NAME=$(echo app*.apk) -unzip -o -q -d apk $ORIG_NAME +ORIG_NAMES=( $(echo app*.apk) ) +for ORIG_NAME in "${ORIG_NAMES[@]}"; do + unzip -o -q -d apk $ORIG_NAME -rm $ORIG_NAME + rm $ORIG_NAME -(cd apk && zip -r -q -$level ../$ORIG_NAME .) -# Shouldn't be compressed because of Android requirement -(cd apk && zip -r -q -0 ../$ORIG_NAME resources.arsc) -(cd apk && zip -r -q -0 ../$ORIG_NAME res) -#(cd apk && 7z a -r -mx=$level -tzip -x!resources.arsc ../$ORIG_NAME .) -#(cd apk && 7z a -r -mx=0 -tzip ../$ORIG_NAME resources.arsc) + (cd apk && zip -r -q -$level ../$ORIG_NAME .) + # Shouldn't be compressed because of Android requirement + (cd apk && zip -r -q -0 ../$ORIG_NAME resources.arsc) + (cd apk && zip -r -q -0 ../$ORIG_NAME res) + #(cd apk && 7z a -r -mx=$level -tzip -x!resources.arsc ../$ORIG_NAME .) + #(cd apk && 7z a -r -mx=0 -tzip ../$ORIG_NAME resources.arsc) -ALL_TOOLS=($sdk_dir/build-tools/*/) -BIN_DIR="${ALL_TOOLS[1]}" + ALL_TOOLS=($sdk_dir/build-tools/*/) + BIN_DIR="${ALL_TOOLS[1]}" -$BIN_DIR/zipalign -p -f 4 $ORIG_NAME $ORIG_NAME-2 + $BIN_DIR/zipalign -p -f 4 $ORIG_NAME $ORIG_NAME-2 -mv $ORIG_NAME{-2,} + mv $ORIG_NAME{-2,} -$BIN_DIR/apksigner sign \ - --ks "$store_file" --ks-key-alias "$key_alias" --ks-pass "pass:$store_password" \ - --key-pass "pass:$key_password" $ORIG_NAME + $BIN_DIR/apksigner sign \ + --ks "$store_file" --ks-key-alias "$key_alias" --ks-pass "pass:$store_password" \ + --key-pass "pass:$key_password" $ORIG_NAME -# cleanup -rm -rf apk || true -rm ${ORIG_NAME}.idsig 2> /dev/null || true + # cleanup + rm -rf apk || true + rm ${ORIG_NAME}.idsig 2> /dev/null || true +done \ No newline at end of file diff --git a/scripts/android/download-libs.sh b/scripts/android/download-libs.sh new file mode 100755 index 0000000000..dd1b1fb2d1 --- /dev/null +++ b/scripts/android/download-libs.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +set -e + +function readlink() { + echo "$(cd "$(dirname "$1")"; pwd -P)" +} + +if [ -z "${1}" ]; then + echo "Job repo is unset. Provide it via first argument like: $(readlink "$0")/download_libs.sh https://something.com/job/something/{master,stable}" + exit 1 +fi + +job_repo=$1 +default_arch=$2 + +arches=("aarch64" "armv7a") +output_arches=("arm64-v8a" "armeabi-v7a") + +if [ -z "${default_arch}" ]; then + # No custom architectures were specified, using defaults + echo "Libs for all supported architectures will be downloaded. To use single arch, pass one of the following values to the end of command: ${arches[*]}" +else + for ((i = 0 ; i < ${#output_arches[@]}; i++)); do + if [ "${arches[$i]}" == "$default_arch" ]; then + output_arches=("${output_arches[$i]}") + fi + done + arches=("$default_arch") +fi + +root_dir="$(dirname "$(dirname "$(readlink "$0")")")" +for ((i = 0 ; i < ${#arches[@]}; i++)); do + arch="${arches[$i]}" + output_arch="${output_arches[$i]}" + output_dir="$root_dir/apps/android/app/src/main/cpp/libs/$output_arch/" + + mkdir -p "$output_dir" 2> /dev/null + + curl --location -o libsupport.zip $job_repo/$arch-android:lib:support.x86_64-linux/latest/download/1 && \ + unzip -o libsupport.zip && \ + mv libsupport.so "$output_dir" && \ + rm libsupport.zip + + curl --location -o libsimplex.zip "$job_repo"/"$arch"-android:lib:simplex-chat.x86_64-linux/latest/download/1 && \ + unzip -o libsimplex.zip && \ + mv libsimplex.so "$output_dir" && \ + rm libsimplex.zip +done diff --git a/scripts/android/download_libs_aarch64.sh b/scripts/android/download_libs_aarch64.sh deleted file mode 100755 index d54a6fd23d..0000000000 --- a/scripts/android/download_libs_aarch64.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -set -e - -function readlink() { - echo $(cd $(dirname $1); pwd -P) -} - -if [ -z ${1} ]; then - echo "Job repo is unset. Provide it via first argument like: $(readlink $0)/download_libs_aarch64.sh https://something.com/job/something/{master,stable}" - exit 1 -fi - -job_repo=$1 -arch="aarch64" -#arch="x86_64" -output_arch="arm64-v8a" -#output_arch="x86_64" - -root_dir="$(dirname $(dirname $(readlink $0)))" -output_dir="$root_dir/apps/android/app/src/main/cpp/libs/$output_arch/" - -mkdir -p "$output_dir" 2> /dev/null - -curl --location -o libsupport.zip $job_repo/$arch-android:lib:support.x86_64-linux/latest/download/1 && \ -unzip -o libsupport.zip && \ -mv libsupport.so "$output_dir" && \ -rm libsupport.zip - -curl --location -o libsimplex.zip $job_repo/$arch-android:lib:simplex-chat.x86_64-linux/latest/download/1 && \ -unzip -o libsimplex.zip && \ -mv libsimplex.so "$output_dir" && \ -rm libsimplex.zip diff --git a/scripts/android/prepare.sh b/scripts/android/prepare.sh index 5fbe70aa6b..4947d0c2b3 100755 --- a/scripts/android/prepare.sh +++ b/scripts/android/prepare.sh @@ -1,8 +1,12 @@ #!/bin/sh -# libsimplex.so and libsupport.so binaries should be in ~/Downloads folder +# libsimplex.so and libsupport.so binaries should be in ~/Downloads folder in their directories based on archive name mkdir -p ./apps/android/app/src/main/cpp/libs/arm64-v8a/ rm ./apps/android/app/src/main/cpp/libs/arm64-v8a/* -cp ~/Downloads/libsupport.so ./apps/android/app/src/main/cpp/libs/arm64-v8a/ -cp ~/Downloads/pkg-aarch64-android-libsimplex/libsimplex.so ./apps/android/app/src/main/cpp/libs/arm64-v8a/ -cp ~/Downloads/pkg-aarch64-android-libsimplex/libcrypto.so ./apps/android/app/src/main/cpp/libs/arm64-v8a/ +unzip -o ~/Downloads/pkg-aarch64-android-libsupport.zip -d ./apps/android/app/src/main/cpp/libs/arm64-v8a +unzip -o ~/Downloads/pkg-aarch64-android-libsimplex.zip -d ./apps/android/app/src/main/cpp/libs/arm64-v8a/ + +mkdir -p ./apps/android/app/src/main/cpp/libs/armeabi-v7a/ +rm ./apps/android/app/src/main/cpp/libs/armeabi-v7a/* +unzip -o ~/Downloads/pkg-armv7a-android-libsupport.zip -d ./apps/android/app/src/main/cpp/libs/armeabi-v7a/ +unzip -o ~/Downloads/pkg-armv7a-android-libsimplex.zip -d ./apps/android/app/src/main/cpp/libs/armeabi-v7a/ diff --git a/scripts/ios/download-libs.sh b/scripts/ios/download-libs.sh new file mode 100755 index 0000000000..9d7e388870 --- /dev/null +++ b/scripts/ios/download-libs.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +set -e + +function readlink() { + echo "$(cd "$(dirname "$1")"; pwd -P)" +} + +if [ -z "${1}" ]; then + echo "Job repo is unset. Provide it via first argument like: $(readlink "$0")/download_libs.sh https://something.com/job/something/{master,stable}" + exit 1 +fi + +job_repo=$1 +default_arch=$2 + +arches=("aarch64" "x86_64") +output_arches=("aarch64" "x86_64") + +if [ -z "${default_arch}" ]; then + # No custom architectures were specified, using defaults + echo "Libs for all supported architectures will be downloaded. To use single arch, pass one of the following values to the end of command: ${arches[*]}" +else + for ((i = 0 ; i < ${#output_arches[@]}; i++)); do + if [ "${arches[$i]}" == "$default_arch" ]; then + output_arches=("${output_arches[$i]}") + fi + done + arches=("$default_arch") +fi + +root_dir="$(dirname "$(dirname "$(readlink "$0")")")" +for ((i = 0 ; i < ${#arches[@]}; i++)); do + arch="${arches[$i]}" + output_arch="${output_arches[$i]}" + output_dir="$HOME/Downloads" + + curl --location -o "$output_dir"/pkg-ios-"$arch"-swift-json.zip "$job_repo"/"$arch"-darwin-ios:lib:simplex-chat."$arch"-darwin/latest/download/1 && \ + unzip -o "$output_dir"/pkg-ios-"$output_arch"-swift-json.zip -d ~/Downloads/pkg-ios-"$output_arch"-swift-json +done +sh "$root_dir"/scripts/ios/prepare-x86_64.sh diff --git a/scripts/ios/download_libs.sh b/scripts/ios/download_libs.sh deleted file mode 100755 index b6d253a9c4..0000000000 --- a/scripts/ios/download_libs.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -set -e - -function readlink() { - echo $(cd $(dirname $1); pwd -P) -} - -if [ -z ${1} ]; then - echo "Job repo is unset. Provide it via first argument like: $(readlink $0)/download_libs_aarch64.sh https://something.com/job/something/{master,stable}" - exit 1 -fi - -job_repo=$1 - -root_dir="$(dirname $(dirname $(readlink $0)))" - -curl --location -o ~/Downloads/pkg-ios-aarch64-swift-json.zip $job_repo/aarch64-darwin-ios:lib:simplex-chat.aarch64-darwin/latest/download/1 && \ -unzip -o ~/Downloads/pkg-ios-aarch64-swift-json.zip -d ~/Downloads/pkg-ios-aarch64-swift-json - -curl --location -o ~/Downloads/pkg-ios-x86_64-swift-json.zip $job_repo/x86_64-darwin-ios:lib:simplex-chat.x86_64-darwin/latest/download/1 && \ -unzip -o ~/Downloads/pkg-ios-x86_64-swift-json.zip -d ~/Downloads/pkg-ios-x86_64-swift-json - -sh $root_dir/scripts/ios/prepare-x86_64.sh diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index e5f9c20ab3..9ffd79f722 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."c5eb65fec873e0493c28af8b190c3458445d1811" = "1cqxl2862fxfl9zv2i1ckvq4xcminslqwfgy5q1w71qk0g2gg96h"; + "https://github.com/simplex-chat/simplexmq.git"."6a665a083387fe7145d161957f0fcab223a48838" = "06nmqbnvalwx8zc8dndzcp31asm65clx519aplzpkipjcbyz93y4"; "https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "https://github.com/kazu-yamamoto/http2.git"."78e18f52295a7f89e828539a03fbcb24931461a3" = "05q165anvv0qrcxqbvq1dlvw0l8gmsa9kl6sazk1mfhz2g0yimdk"; "https://github.com/simplex-chat/direct-sqlcipher.git"."34309410eb2069b029b8fc1872deb1e0db123294" = "0kwkmhyfsn2lixdlgl15smgr1h5gjk7fky6abzh8rng2h5ymnffd"; diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 939c8985a1..9db725d880 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -63,10 +63,11 @@ import Simplex.FileTransfer.Description (ValidFileDescription, gb, kb, mb) import Simplex.FileTransfer.Protocol (FileParty (..)) import Simplex.Messaging.Agent as Agent import Simplex.Messaging.Agent.Client (AgentStatsKey (..)) -import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), AgentDatabase (..), InitialAgentServers (..), createAgentStore, defaultAgentConfig) +import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), InitialAgentServers (..), createAgentStore, defaultAgentConfig) import Simplex.Messaging.Agent.Lock import Simplex.Messaging.Agent.Protocol -import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore (dbNew), execSQL) +import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), MigrationError, SQLiteStore (dbNew), execSQL, upMigration) +import qualified Simplex.Messaging.Agent.Store.SQLite.Migrations as Migrations import Simplex.Messaging.Client (defaultNetworkConfig) import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Encoding @@ -94,11 +95,9 @@ defaultChatConfig = { agentConfig = defaultAgentConfig { tcpPort = undefined, -- agent does not listen to TCP - tbqSize = 1024, - database = AgentDBFile {dbFile = "simplex_v1_agent", dbKey = ""}, - yesToMigrations = False + tbqSize = 1024 }, - yesToMigrations = False, + confirmMigrations = MCConsole, defaultServers = DefaultAgentServers { smp = _defaultSMPServers, @@ -139,10 +138,10 @@ fixedImagePreview = ImageData "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEA logCfg :: LogConfig logCfg = LogConfig {lc_file = Nothing, lc_stderr = True} -createChatDatabase :: FilePath -> String -> Bool -> IO ChatDatabase -createChatDatabase filePrefix key yesToMigrations = do - chatStore <- createChatStore (chatStoreFile filePrefix) key yesToMigrations - agentStore <- createAgentStore (agentStoreFile filePrefix) key yesToMigrations +createChatDatabase :: FilePath -> String -> MigrationConfirmation -> IO (Either MigrationError ChatDatabase) +createChatDatabase filePrefix key confirmMigrations = runExceptT $ do + chatStore <- ExceptT $ createChatStore (chatStoreFile filePrefix) key confirmMigrations + agentStore <- ExceptT $ createAgentStore (agentStoreFile filePrefix) key confirmMigrations pure ChatDatabase {chatStore, agentStore} newChatController :: ChatDatabase -> Maybe User -> ChatConfig -> ChatOpts -> Maybe (Notification -> IO ()) -> IO ChatController @@ -153,9 +152,10 @@ newChatController ChatDatabase {chatStore, agentStore} user cfg@ChatConfig {agen firstTime = dbNew chatStore activeTo <- newTVarIO ActiveNone currentUser <- newTVarIO user - smpAgent <- getSMPAgentClient aCfg {tbqSize, database = AgentDB agentStore} =<< agentServers config + servers <- agentServers config + smpAgent <- getSMPAgentClient aCfg {tbqSize} servers agentStore agentAsync <- newTVarIO Nothing - idsDrg <- newTVarIO =<< drgNew + idsDrg <- newTVarIO =<< liftIO drgNew inputQ <- newTBQueueIO tbqSize outputQ <- newTBQueueIO tbqSize notifyQ <- newTBQueueIO tbqSize @@ -1445,7 +1445,11 @@ processChatCommand = \case updateGroupProfileByName gName $ \p -> p {groupPreferences = Just . setGroupPreference' SGFTimedMessages pref $ groupPreferences p} QuitChat -> liftIO exitSuccess - ShowVersion -> pure $ CRVersionInfo $ coreVersionInfo $(buildTimestampQ) $(simplexmqCommitQ) + ShowVersion -> do + let versionInfo = coreVersionInfo $(buildTimestampQ) $(simplexmqCommitQ) + chatMigrations <- map upMigration <$> withStore' Migrations.getCurrent + agentMigrations <- withAgent getAgentMigrations + pure $ CRVersionInfo {versionInfo, chatMigrations, agentMigrations} DebugLocks -> do chatLockName <- atomically . tryReadTMVar =<< asks chatLock agentLocks <- withAgent debugAgentLocks diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 7c37123970..1b13fc34fc 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -49,7 +49,7 @@ import Simplex.Messaging.Agent.Client (AgentLocks, SMPTestFailure) import Simplex.Messaging.Agent.Env.SQLite (AgentConfig, NetworkConfig) import Simplex.Messaging.Agent.Lock import Simplex.Messaging.Agent.Protocol -import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore) +import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation, SQLiteStore, UpMigration) import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Encoding.String import Simplex.Messaging.Notifications.Protocol (DeviceToken (..), NtfTknStatus) @@ -101,7 +101,7 @@ coreVersionInfo buildTimestamp simplexmqCommit = data ChatConfig = ChatConfig { agentConfig :: AgentConfig, - yesToMigrations :: Bool, + confirmMigrations :: MigrationConfirmation, defaultServers :: DefaultAgentServers, tbqSize :: Natural, fileChunkSize :: Integer, @@ -422,7 +422,7 @@ data ChatResponse | CRUserProfile {user :: User, profile :: Profile} | CRUserProfileNoChange {user :: User} | CRUserPrivacy {user :: User} - | CRVersionInfo {versionInfo :: CoreVersionInfo} + | CRVersionInfo {versionInfo :: CoreVersionInfo, chatMigrations :: [UpMigration], agentMigrations :: [UpMigration]} | CRInvitation {user :: User, connReqInvitation :: ConnReqInvitation} | CRSentConfirmation {user :: User} | CRSentInvitation {user :: User, customUserProfile :: Maybe Profile} diff --git a/src/Simplex/Chat/Core.hs b/src/Simplex/Chat/Core.hs index aa242a0899..a62ab5642b 100644 --- a/src/Simplex/Chat/Core.hs +++ b/src/Simplex/Chat/Core.hs @@ -11,18 +11,22 @@ import Simplex.Chat import Simplex.Chat.Controller import Simplex.Chat.Options (ChatOpts (..), CoreChatOpts (..)) import Simplex.Chat.Types +import System.Exit (exitFailure) import UnliftIO.Async simplexChatCore :: ChatConfig -> ChatOpts -> Maybe (Notification -> IO ()) -> (User -> ChatController -> IO ()) -> IO () -simplexChatCore cfg@ChatConfig {yesToMigrations} opts@ChatOpts {coreOptions = CoreChatOpts {dbFilePrefix, dbKey, logAgent}} sendToast chat = +simplexChatCore cfg@ChatConfig {confirmMigrations} opts@ChatOpts {coreOptions = CoreChatOpts {dbFilePrefix, dbKey, logAgent}} sendToast chat = case logAgent of Just level -> do setLogLevel level withGlobalLogging logCfg initRun _ -> initRun where - initRun = do - db@ChatDatabase {chatStore} <- createChatDatabase dbFilePrefix dbKey yesToMigrations + initRun = createChatDatabase dbFilePrefix dbKey confirmMigrations >>= either exit run + exit e = do + putStrLn $ "Error opening database: " <> show e + exitFailure + run db@ChatDatabase {chatStore} = do u <- getCreateActiveUser chatStore cc <- newChatController db (Just u) cfg opts sendToast runSimplexChat opts u cc chat diff --git a/src/Simplex/Chat/Migrations/M20230317_hidden_profiles.hs b/src/Simplex/Chat/Migrations/M20230317_hidden_profiles.hs index 27ae711a04..65e9cfeadd 100644 --- a/src/Simplex/Chat/Migrations/M20230317_hidden_profiles.hs +++ b/src/Simplex/Chat/Migrations/M20230317_hidden_profiles.hs @@ -12,3 +12,11 @@ ALTER TABLE users ADD COLUMN view_pwd_hash BLOB; ALTER TABLE users ADD COLUMN view_pwd_salt BLOB; ALTER TABLE users ADD COLUMN show_ntfs INTEGER NOT NULL DEFAULT 1; |] + +down_m20230317_hidden_profiles :: Query +down_m20230317_hidden_profiles = + [sql| +ALTER TABLE users DROP COLUMN view_pwd_hash; +ALTER TABLE users DROP COLUMN view_pwd_salt; +ALTER TABLE users DROP COLUMN show_ntfs; +|] diff --git a/src/Simplex/Chat/Migrations/chat_schema.sql b/src/Simplex/Chat/Migrations/chat_schema.sql index f5e7525e6e..ed1b303545 100644 --- a/src/Simplex/Chat/Migrations/chat_schema.sql +++ b/src/Simplex/Chat/Migrations/chat_schema.sql @@ -1,6 +1,7 @@ CREATE TABLE migrations( name TEXT NOT NULL, ts TEXT NOT NULL, + down TEXT, PRIMARY KEY(name) ); CREATE TABLE contact_profiles( diff --git a/src/Simplex/Chat/Mobile.hs b/src/Simplex/Chat/Mobile.hs index cf70291015..a4aea94bc5 100644 --- a/src/Simplex/Chat/Mobile.hs +++ b/src/Simplex/Chat/Mobile.hs @@ -12,6 +12,7 @@ import Control.Monad.Except import Control.Monad.Reader import Data.Aeson (ToJSON (..)) import qualified Data.Aeson as J +import Data.Bifunctor (first) import qualified Data.ByteString.Base64.URL as U import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy.Char8 as LB @@ -37,26 +38,17 @@ import Simplex.Chat.Mobile.WebRTC import Simplex.Chat.Options import Simplex.Chat.Store import Simplex.Chat.Types -import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (yesToMigrations), createAgentStore) -import Simplex.Messaging.Agent.Store.SQLite (closeSQLiteStore) +import Simplex.Messaging.Agent.Env.SQLite (createAgentStore) +import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), MigrationError) import Simplex.Messaging.Client (defaultNetworkConfig) import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Encoding.String import Simplex.Messaging.Parsers (dropPrefix, sumTypeJSON) import Simplex.Messaging.Protocol (BasicAuth (..), CorrId (..), ProtoServerWithAuth (..), ProtocolServer (..), SMPServerWithAuth) -import Simplex.Messaging.Util (catchAll, safeDecodeUtf8) +import Simplex.Messaging.Util (catchAll, liftEitherWith, safeDecodeUtf8) import System.Timeout (timeout) -foreign export ccall "chat_migrate_init" cChatMigrateInit :: CString -> CString -> Ptr (StablePtr ChatController) -> IO CJSONString - --- TODO remove -foreign export ccall "chat_migrate_db" cChatMigrateDB :: CString -> CString -> IO CJSONString - --- chat_init is deprecated -foreign export ccall "chat_init" cChatInit :: CString -> IO (StablePtr ChatController) - --- TODO remove -foreign export ccall "chat_init_key" cChatInitKey :: CString -> CString -> IO (StablePtr ChatController) +foreign export ccall "chat_migrate_init" cChatMigrateInit :: CString -> CString -> CString -> Ptr (StablePtr ChatController) -> IO CJSONString foreign export ccall "chat_send_cmd" cChatSendCmd :: StablePtr ChatController -> CString -> IO CJSONString @@ -75,35 +67,17 @@ foreign export ccall "chat_encrypt_media" cChatEncryptMedia :: CString -> Ptr Wo foreign export ccall "chat_decrypt_media" cChatDecryptMedia :: CString -> Ptr Word8 -> CInt -> IO CString -- | check / migrate database and initialize chat controller on success -cChatMigrateInit :: CString -> CString -> Ptr (StablePtr ChatController) -> IO CJSONString -cChatMigrateInit fp key ctrl = do +cChatMigrateInit :: CString -> CString -> CString -> Ptr (StablePtr ChatController) -> IO CJSONString +cChatMigrateInit fp key conf ctrl = do dbPath <- peekCAString fp dbKey <- peekCAString key + confirm <- peekCAString conf r <- - chatMigrateInit dbPath dbKey >>= \case + chatMigrateInit dbPath dbKey confirm >>= \case Right cc -> (newStablePtr cc >>= poke ctrl) $> DBMOk Left e -> pure e newCAString . LB.unpack $ J.encode r --- | check and migrate the database --- This function validates that the encryption is correct and runs migrations - it should be called before cChatInitKey --- TODO remove -cChatMigrateDB :: CString -> CString -> IO CJSONString -cChatMigrateDB fp key = - ((,) <$> peekCAString fp <*> peekCAString key) >>= uncurry chatMigrateDB >>= newCAString . LB.unpack . J.encode - --- | initialize chat controller (deprecated) --- The active user has to be created and the chat has to be started before most commands can be used. -cChatInit :: CString -> IO (StablePtr ChatController) -cChatInit fp = peekCAString fp >>= chatInit >>= newStablePtr - --- | initialize chat controller with encrypted database --- The active user has to be created and the chat has to be started before most commands can be used. --- TODO remove -cChatInitKey :: CString -> CString -> IO (StablePtr ChatController) -cChatInitKey fp key = - ((,) <$> peekCAString fp <*> peekCAString key) >>= uncurry chatInitKey >>= newStablePtr - -- | send command to chat (same syntax as in terminal for now) cChatSendCmd :: StablePtr ChatController -> CString -> IO CJSONString cChatSendCmd cPtr cCmd = do @@ -160,10 +134,7 @@ mobileChatOpts dbFilePrefix dbKey = defaultMobileConfig :: ChatConfig defaultMobileConfig = - defaultChatConfig - { yesToMigrations = True, - agentConfig = (agentConfig defaultChatConfig) {yesToMigrations = True} - } + defaultChatConfig {confirmMigrations = MCYesUp} type CJSONString = CString @@ -172,60 +143,36 @@ getActiveUser_ st = find activeUser <$> withTransaction st getUsers data DBMigrationResult = DBMOk + | DBMInvalidConfirmation | DBMErrorNotADatabase {dbFile :: String} - | DBMError {dbFile :: String, migrationError :: String} + | DBMErrorMigration {dbFile :: String, migrationError :: MigrationError} + | DBMErrorSQL {dbFile :: String, migrationSQLError :: String} deriving (Show, Generic) instance ToJSON DBMigrationResult where toJSON = J.genericToJSON . sumTypeJSON $ dropPrefix "DBM" toEncoding = J.genericToEncoding . sumTypeJSON $ dropPrefix "DBM" -chatMigrateInit :: String -> String -> IO (Either DBMigrationResult ChatController) -chatMigrateInit dbFilePrefix dbKey = runExceptT $ do - chatStore <- migrate createChatStore $ chatStoreFile dbFilePrefix - agentStore <- migrate createAgentStore $ agentStoreFile dbFilePrefix +chatMigrateInit :: String -> String -> String -> IO (Either DBMigrationResult ChatController) +chatMigrateInit dbFilePrefix dbKey confirm = runExceptT $ do + confirmMigrations <- liftEitherWith (const DBMInvalidConfirmation) $ strDecode $ B.pack confirm + chatStore <- migrate createChatStore (chatStoreFile dbFilePrefix) confirmMigrations + agentStore <- migrate createAgentStore (agentStoreFile dbFilePrefix) confirmMigrations liftIO $ initialize chatStore ChatDatabase {chatStore, agentStore} where initialize st db = do user_ <- getActiveUser_ st newChatController db user_ defaultMobileConfig (mobileChatOpts dbFilePrefix dbKey) Nothing - migrate createStore dbFile = + migrate createStore dbFile confirmMigrations = ExceptT $ - (Right <$> createStore dbFile dbKey True) + (first (DBMErrorMigration dbFile) <$> createStore dbFile dbKey confirmMigrations) `catch` (pure . checkDBError) `catchAll` (pure . dbError) where checkDBError e = case sqlError e of DB.ErrorNotADatabase -> Left $ DBMErrorNotADatabase dbFile _ -> dbError e - dbError e = Left . DBMError dbFile $ show e - --- TODO remove -chatMigrateDB :: String -> String -> IO DBMigrationResult -chatMigrateDB dbFilePrefix dbKey = - migrate createChatStore (chatStoreFile dbFilePrefix) >>= \case - DBMOk -> migrate createAgentStore (agentStoreFile dbFilePrefix) - e -> pure e - where - migrate createStore dbFile = - ((createStore dbFile dbKey True >>= closeSQLiteStore) $> DBMOk) - `catch` (pure . checkDBError) - `catchAll` (pure . dbError) - where - checkDBError e = case sqlError e of - DB.ErrorNotADatabase -> DBMErrorNotADatabase dbFile - _ -> dbError e - dbError e = DBMError dbFile $ show e - -chatInit :: String -> IO ChatController -chatInit = (`chatInitKey` "") - --- TODO remove -chatInitKey :: String -> String -> IO ChatController -chatInitKey dbFilePrefix dbKey = do - db@ChatDatabase {chatStore} <- createChatDatabase dbFilePrefix dbKey True - user_ <- getActiveUser_ chatStore - newChatController db user_ defaultMobileConfig (mobileChatOpts dbFilePrefix dbKey) Nothing + dbError e = Left . DBMErrorSQL dbFile $ show e chatSendCmd :: ChatController -> String -> IO JSONString chatSendCmd cc s = LB.unpack . J.encode . APIResponse Nothing <$> runReaderT (execChatCommand $ B.pack s) cc diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index 52eb876d6b..4e686d2e80 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -445,7 +445,7 @@ instance ToJSON MsgContent where MCText t -> J.object ["type" .= MCText_, "text" .= t] MCLink {text, preview} -> J.object ["type" .= MCLink_, "text" .= text, "preview" .= preview] MCImage {text, image} -> J.object ["type" .= MCImage_, "text" .= text, "image" .= image] - MCVideo {text, image, duration} -> J.object ["type" .= MCImage_, "text" .= text, "image" .= image, "duration" .= duration] + MCVideo {text, image, duration} -> J.object ["type" .= MCVideo_, "text" .= text, "image" .= image, "duration" .= duration] MCVoice {text, duration} -> J.object ["type" .= MCVoice_, "text" .= text, "duration" .= duration] MCFile t -> J.object ["type" .= MCFile_, "text" .= t] toEncoding = \case @@ -453,7 +453,7 @@ instance ToJSON MsgContent where MCText t -> J.pairs $ "type" .= MCText_ <> "text" .= t MCLink {text, preview} -> J.pairs $ "type" .= MCLink_ <> "text" .= text <> "preview" .= preview MCImage {text, image} -> J.pairs $ "type" .= MCImage_ <> "text" .= text <> "image" .= image - MCVideo {text, image, duration} -> J.pairs $ "type" .= MCImage_ <> "text" .= text <> "image" .= image <> "duration" .= duration + MCVideo {text, image, duration} -> J.pairs $ "type" .= MCVideo_ <> "text" .= text <> "image" .= image <> "duration" .= duration MCVoice {text, duration} -> J.pairs $ "type" .= MCVoice_ <> "text" .= text <> "duration" .= duration MCFile t -> J.pairs $ "type" .= MCFile_ <> "text" .= t diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index d9f01985bc..aa3ff98333 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -23,6 +23,7 @@ module Simplex.Chat.Store UserContactLink (..), AutoAccept (..), createChatStore, + migrations, -- used in tests chatStoreFile, agentStoreFile, createUserRecord, @@ -286,10 +287,9 @@ import Data.Bifunctor (first) import qualified Data.ByteString.Base64 as B64 import Data.ByteString.Char8 (ByteString) import Data.Either (rights) -import Data.Function (on) import Data.Functor (($>)) import Data.Int (Int64) -import Data.List (sortBy, sortOn) +import Data.List (sortOn) import Data.List.NonEmpty (NonEmpty) import qualified Data.List.NonEmpty as L import Data.Maybe (fromMaybe, isJust, isNothing, listToMaybe, mapMaybe) @@ -367,7 +367,7 @@ import Simplex.Chat.Protocol import Simplex.Chat.Types import Simplex.Chat.Util (week) import Simplex.Messaging.Agent.Protocol (ACorrId, AgentMsgId, ConnId, InvitationId, MsgMeta (..), UserId) -import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore (..), createSQLiteStore, firstRow, firstRow', maybeFirstRow, withTransaction) +import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation, MigrationError, SQLiteStore (..), createSQLiteStore, firstRow, firstRow', maybeFirstRow, withTransaction) import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..)) import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Parsers (dropPrefix, sumTypeJSON) @@ -376,72 +376,72 @@ import Simplex.Messaging.Transport.Client (TransportHost) import Simplex.Messaging.Util (eitherToMaybe, safeDecodeUtf8) import UnliftIO.STM -schemaMigrations :: [(String, Query)] +schemaMigrations :: [(String, Query, Maybe Query)] schemaMigrations = - [ ("20220101_initial", m20220101_initial), - ("20220122_v1_1", m20220122_v1_1), - ("20220205_chat_item_status", m20220205_chat_item_status), - ("20220210_deduplicate_contact_requests", m20220210_deduplicate_contact_requests), - ("20220224_messages_fks", m20220224_messages_fks), - ("20220301_smp_servers", m20220301_smp_servers), - ("20220302_profile_images", m20220302_profile_images), - ("20220304_msg_quotes", m20220304_msg_quotes), - ("20220321_chat_item_edited", m20220321_chat_item_edited), - ("20220404_files_status_fields", m20220404_files_status_fields), - ("20220514_profiles_user_id", m20220514_profiles_user_id), - ("20220626_auto_reply", m20220626_auto_reply), - ("20220702_calls", m20220702_calls), - ("20220715_groups_chat_item_id", m20220715_groups_chat_item_id), - ("20220811_chat_items_indices", m20220811_chat_items_indices), - ("20220812_incognito_profiles", m20220812_incognito_profiles), - ("20220818_chat_notifications", m20220818_chat_notifications), - ("20220822_groups_host_conn_custom_user_profile_id", m20220822_groups_host_conn_custom_user_profile_id), - ("20220823_delete_broken_group_event_chat_items", m20220823_delete_broken_group_event_chat_items), - ("20220824_profiles_local_alias", m20220824_profiles_local_alias), - ("20220909_commands", m20220909_commands), - ("20220926_connection_alias", m20220926_connection_alias), - ("20220928_settings", m20220928_settings), - ("20221001_shared_msg_id_indices", m20221001_shared_msg_id_indices), - ("20221003_delete_broken_integrity_error_chat_items", m20221003_delete_broken_integrity_error_chat_items), - ("20221004_idx_msg_deliveries_message_id", m20221004_idx_msg_deliveries_message_id), - ("20221011_user_contact_links_group_id", m20221011_user_contact_links_group_id), - ("20221012_inline_files", m20221012_inline_files), - ("20221019_unread_chat", m20221019_unread_chat), - ("20221021_auto_accept__group_links", m20221021_auto_accept__group_links), - ("20221024_contact_used", m20221024_contact_used), - ("20221025_chat_settings", m20221025_chat_settings), - ("20221029_group_link_id", m20221029_group_link_id), - ("20221112_server_password", m20221112_server_password), - ("20221115_server_cfg", m20221115_server_cfg), - ("20221129_delete_group_feature_items", m20221129_delete_group_feature_items), - ("20221130_delete_item_deleted", m20221130_delete_item_deleted), - ("20221209_verified_connection", m20221209_verified_connection), - ("20221210_idxs", m20221210_idxs), - ("20221211_group_description", m20221211_group_description), - ("20221212_chat_items_timed", m20221212_chat_items_timed), - ("20221214_live_message", m20221214_live_message), - ("20221222_chat_ts", m20221222_chat_ts), - ("20221223_idx_chat_items_item_status", m20221223_idx_chat_items_item_status), - ("20221230_idxs", m20221230_idxs), - ("20230107_connections_auth_err_counter", m20230107_connections_auth_err_counter), - ("20230111_users_agent_user_id", m20230111_users_agent_user_id), - ("20230117_fkey_indexes", m20230117_fkey_indexes), - ("20230118_recreate_smp_servers", m20230118_recreate_smp_servers), - ("20230129_drop_chat_items_group_idx", m20230129_drop_chat_items_group_idx), - ("20230206_item_deleted_by_group_member_id", m20230206_item_deleted_by_group_member_id), - ("20230303_group_link_role", m20230303_group_link_role), - ("20230317_hidden_profiles", m20230317_hidden_profiles), - ("20230318_file_description", m20230318_file_description), - ("20230321_agent_file_deleted", m20230321_agent_file_deleted) + [ ("20220101_initial", m20220101_initial, Nothing), + ("20220122_v1_1", m20220122_v1_1, Nothing), + ("20220205_chat_item_status", m20220205_chat_item_status, Nothing), + ("20220210_deduplicate_contact_requests", m20220210_deduplicate_contact_requests, Nothing), + ("20220224_messages_fks", m20220224_messages_fks, Nothing), + ("20220301_smp_servers", m20220301_smp_servers, Nothing), + ("20220302_profile_images", m20220302_profile_images, Nothing), + ("20220304_msg_quotes", m20220304_msg_quotes, Nothing), + ("20220321_chat_item_edited", m20220321_chat_item_edited, Nothing), + ("20220404_files_status_fields", m20220404_files_status_fields, Nothing), + ("20220514_profiles_user_id", m20220514_profiles_user_id, Nothing), + ("20220626_auto_reply", m20220626_auto_reply, Nothing), + ("20220702_calls", m20220702_calls, Nothing), + ("20220715_groups_chat_item_id", m20220715_groups_chat_item_id, Nothing), + ("20220811_chat_items_indices", m20220811_chat_items_indices, Nothing), + ("20220812_incognito_profiles", m20220812_incognito_profiles, Nothing), + ("20220818_chat_notifications", m20220818_chat_notifications, Nothing), + ("20220822_groups_host_conn_custom_user_profile_id", m20220822_groups_host_conn_custom_user_profile_id, Nothing), + ("20220823_delete_broken_group_event_chat_items", m20220823_delete_broken_group_event_chat_items, Nothing), + ("20220824_profiles_local_alias", m20220824_profiles_local_alias, Nothing), + ("20220909_commands", m20220909_commands, Nothing), + ("20220926_connection_alias", m20220926_connection_alias, Nothing), + ("20220928_settings", m20220928_settings, Nothing), + ("20221001_shared_msg_id_indices", m20221001_shared_msg_id_indices, Nothing), + ("20221003_delete_broken_integrity_error_chat_items", m20221003_delete_broken_integrity_error_chat_items, Nothing), + ("20221004_idx_msg_deliveries_message_id", m20221004_idx_msg_deliveries_message_id, Nothing), + ("20221011_user_contact_links_group_id", m20221011_user_contact_links_group_id, Nothing), + ("20221012_inline_files", m20221012_inline_files, Nothing), + ("20221019_unread_chat", m20221019_unread_chat, Nothing), + ("20221021_auto_accept__group_links", m20221021_auto_accept__group_links, Nothing), + ("20221024_contact_used", m20221024_contact_used, Nothing), + ("20221025_chat_settings", m20221025_chat_settings, Nothing), + ("20221029_group_link_id", m20221029_group_link_id, Nothing), + ("20221112_server_password", m20221112_server_password, Nothing), + ("20221115_server_cfg", m20221115_server_cfg, Nothing), + ("20221129_delete_group_feature_items", m20221129_delete_group_feature_items, Nothing), + ("20221130_delete_item_deleted", m20221130_delete_item_deleted, Nothing), + ("20221209_verified_connection", m20221209_verified_connection, Nothing), + ("20221210_idxs", m20221210_idxs, Nothing), + ("20221211_group_description", m20221211_group_description, Nothing), + ("20221212_chat_items_timed", m20221212_chat_items_timed, Nothing), + ("20221214_live_message", m20221214_live_message, Nothing), + ("20221222_chat_ts", m20221222_chat_ts, Nothing), + ("20221223_idx_chat_items_item_status", m20221223_idx_chat_items_item_status, Nothing), + ("20221230_idxs", m20221230_idxs, Nothing), + ("20230107_connections_auth_err_counter", m20230107_connections_auth_err_counter, Nothing), + ("20230111_users_agent_user_id", m20230111_users_agent_user_id, Nothing), + ("20230117_fkey_indexes", m20230117_fkey_indexes, Nothing), + ("20230118_recreate_smp_servers", m20230118_recreate_smp_servers, Nothing), + ("20230129_drop_chat_items_group_idx", m20230129_drop_chat_items_group_idx, Nothing), + ("20230206_item_deleted_by_group_member_id", m20230206_item_deleted_by_group_member_id, Nothing), + ("20230303_group_link_role", m20230303_group_link_role, Nothing), + ("20230317_hidden_profiles", m20230317_hidden_profiles, Just down_m20230317_hidden_profiles), + ("20230318_file_description", m20230318_file_description, Nothing), + ("20230321_agent_file_deleted", m20230321_agent_file_deleted, Nothing) ] -- | The list of migrations in ascending order by date migrations :: [Migration] -migrations = sortBy (compare `on` name) $ map migration schemaMigrations +migrations = sortOn name $ map migration schemaMigrations where - migration (name, query) = Migration {name = name, up = fromQuery query} + migration (name, up, down) = Migration {name, up = fromQuery up, down = fromQuery <$> down} -createChatStore :: FilePath -> String -> Bool -> IO SQLiteStore +createChatStore :: FilePath -> String -> MigrationConfirmation -> IO (Either MigrationError SQLiteStore) createChatStore dbFilePath dbKey = createSQLiteStore dbFilePath dbKey migrations chatStoreFile :: FilePath -> FilePath diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 27ebf9c5de..a989a09bc5 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -117,7 +117,7 @@ responseToView user_ ChatConfig {logLevel, testView} liveItems ts = \case CRUserProfile u p -> ttyUser u $ viewUserProfile p CRUserProfileNoChange u -> ttyUser u ["user profile did not change"] CRUserPrivacy u -> ttyUserPrefix u $ viewUserPrivacy u - CRVersionInfo info -> viewVersionInfo logLevel info + CRVersionInfo info _ _ -> viewVersionInfo logLevel info CRInvitation u cReq -> ttyUser u $ viewConnReqInvitation cReq CRSentConfirmation u -> ttyUser u ["confirmation sent!"] CRSentInvitation u customUserProfile -> ttyUser u $ viewSentInvitation customUserProfile testView diff --git a/stack.yaml b/stack.yaml index b19382c2a9..ff289f8040 100644 --- a/stack.yaml +++ b/stack.yaml @@ -49,7 +49,7 @@ extra-deps: # - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561 # - ../simplexmq - github: simplex-chat/simplexmq - commit: c5eb65fec873e0493c28af8b190c3458445d1811 + commit: 6a665a083387fe7145d161957f0fcab223a48838 - github: kazu-yamamoto/http2 commit: 78e18f52295a7f89e828539a03fbcb24931461a3 # - ../direct-sqlcipher diff --git a/tests/ChatClient.hs b/tests/ChatClient.hs index c0107c6f7b..dcb8f3fc40 100644 --- a/tests/ChatClient.hs +++ b/tests/ChatClient.hs @@ -31,6 +31,7 @@ import Simplex.FileTransfer.Server (runXFTPServerBlocking) import Simplex.FileTransfer.Server.Env (XFTPServerConfig (..), defaultFileExpiration) import Simplex.Messaging.Agent.Env.SQLite import Simplex.Messaging.Agent.RetryInterval +import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..)) import Simplex.Messaging.Client (ProtocolClientConfig (..), defaultNetworkConfig) import Simplex.Messaging.Server (runSMPServerBlocking) import Simplex.Messaging.Server.Env.STM @@ -122,13 +123,13 @@ testCfgV1 = testCfg {agentConfig = testAgentCfgV1} createTestChat :: FilePath -> ChatConfig -> ChatOpts -> String -> Profile -> IO TestCC createTestChat tmp cfg opts@ChatOpts {coreOptions = CoreChatOpts {dbKey}} dbPrefix profile = do - db@ChatDatabase {chatStore} <- createChatDatabase (tmp dbPrefix) dbKey False + Right db@ChatDatabase {chatStore} <- createChatDatabase (tmp dbPrefix) dbKey MCError Right user <- withTransaction chatStore $ \db' -> runExceptT $ createUserRecord db' (AgentUserId 1) profile True startTestChat_ db cfg opts user startTestChat :: FilePath -> ChatConfig -> ChatOpts -> String -> IO TestCC startTestChat tmp cfg opts@ChatOpts {coreOptions = CoreChatOpts {dbKey}} dbPrefix = do - db@ChatDatabase {chatStore} <- createChatDatabase (tmp dbPrefix) dbKey False + Right db@ChatDatabase {chatStore} <- createChatDatabase (tmp dbPrefix) dbKey MCError Just user <- find activeUser <$> withTransaction chatStore getUsers startTestChat_ db cfg opts user diff --git a/tests/MobileTests.hs b/tests/MobileTests.hs index 432f2c0243..feaed08a8d 100644 --- a/tests/MobileTests.hs +++ b/tests/MobileTests.hs @@ -7,6 +7,7 @@ import Control.Monad.Except import Simplex.Chat.Mobile import Simplex.Chat.Store import Simplex.Chat.Types (AgentUserId (..), Profile (..)) +import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..)) import System.FilePath (()) import Test.Hspec @@ -85,8 +86,8 @@ parsedMarkdown = "{\"formattedText\":[{\"format\":{\"type\":\"bold\"},\"text\":\ testChatApiNoUser :: FilePath -> IO () testChatApiNoUser tmp = do let dbPrefix = tmp "1" - Right cc <- chatMigrateInit dbPrefix "" - Left (DBMErrorNotADatabase _) <- chatMigrateInit dbPrefix "myKey" + Right cc <- chatMigrateInit dbPrefix "" "yesUp" + Left (DBMErrorNotADatabase _) <- chatMigrateInit dbPrefix "myKey" "yesUp" chatSendCmd cc "/u" `shouldReturn` noActiveUser chatSendCmd cc "/_start" `shouldReturn` noActiveUser chatSendCmd cc "/create user alice Alice" `shouldReturn` activeUser @@ -96,11 +97,11 @@ testChatApi :: FilePath -> IO () testChatApi tmp = do let dbPrefix = tmp "1" f = chatStoreFile dbPrefix - st <- createChatStore f "myKey" True + Right st <- createChatStore f "myKey" MCYesUp Right _ <- withTransaction st $ \db -> runExceptT $ createUserRecord db (AgentUserId 1) aliceProfile {preferences = Nothing} True - Right cc <- chatMigrateInit dbPrefix "myKey" - Left (DBMErrorNotADatabase _) <- chatMigrateInit dbPrefix "" - Left (DBMErrorNotADatabase _) <- chatMigrateInit dbPrefix "anotherKey" + Right cc <- chatMigrateInit dbPrefix "myKey" "yesUp" + Left (DBMErrorNotADatabase _) <- chatMigrateInit dbPrefix "" "yesUp" + Left (DBMErrorNotADatabase _) <- chatMigrateInit dbPrefix "anotherKey" "yesUp" chatSendCmd cc "/u" `shouldReturn` activeUser chatSendCmd cc "/create user alice Alice" `shouldReturn` activeUserExists chatSendCmd cc "/_start" `shouldReturn` chatStarted diff --git a/tests/SchemaDump.hs b/tests/SchemaDump.hs index 4cb2662014..96197f1295 100644 --- a/tests/SchemaDump.hs +++ b/tests/SchemaDump.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} module SchemaDump where @@ -5,26 +6,63 @@ module SchemaDump where import ChatClient (withTmpFiles) import Control.DeepSeq import Control.Monad (void) +import Data.List (dropWhileEnd) +import Data.Maybe (fromJust, isJust) import Simplex.Chat.Store (createChatStore) +import qualified Simplex.Chat.Store as Store +import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..), closeSQLiteStore, createSQLiteStore, withConnection) +import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..), MigrationsToRun (..), toDownMigration) +import qualified Simplex.Messaging.Agent.Store.SQLite.Migrations as Migrations +import Simplex.Messaging.Util (ifM) +import System.Directory (doesFileExist, removeFile) import System.Process (readCreateProcess, shell) import Test.Hspec testDB :: FilePath testDB = "tests/tmp/test_chat.db" -schema :: FilePath -schema = "src/Simplex/Chat/Migrations/chat_schema.sql" +appSchema :: FilePath +appSchema = "src/Simplex/Chat/Migrations/chat_schema.sql" + +testSchema :: FilePath +testSchema = "tests/tmp/test_agent_schema.sql" schemaDumpTest :: Spec -schemaDumpTest = +schemaDumpTest = do it "verify and overwrite schema dump" testVerifySchemaDump + it "verify schema down migrations" testSchemaMigrations testVerifySchemaDump :: IO () testVerifySchemaDump = withTmpFiles $ do - void $ createChatStore testDB "" False - void $ readCreateProcess (shell $ "touch " <> schema) "" - savedSchema <- readFile schema + savedSchema <- ifM (doesFileExist appSchema) (readFile appSchema) (pure "") savedSchema `deepseq` pure () - void $ readCreateProcess (shell $ "sqlite3 " <> testDB <> " '.schema --indent' > " <> schema) "" - currentSchema <- readFile schema - savedSchema `shouldBe` currentSchema + void $ createChatStore testDB "" MCError + getSchema testDB appSchema `shouldReturn` savedSchema + removeFile testDB + +testSchemaMigrations :: IO () +testSchemaMigrations = withTmpFiles $ do + let noDownMigrations = dropWhileEnd (\Migration {down} -> isJust down) Store.migrations + Right st <- createSQLiteStore testDB "" noDownMigrations MCError + mapM_ (testDownMigration st) $ drop (length noDownMigrations) Store.migrations + closeSQLiteStore st + removeFile testDB + removeFile testSchema + where + testDownMigration st m = do + putStrLn $ "down migration " <> name m + let downMigr = fromJust $ toDownMigration m + schema <- getSchema testDB testSchema + withConnection st (`Migrations.run` MTRUp [m]) + schema' <- getSchema testDB testSchema + schema' `shouldNotBe` schema + withConnection st (`Migrations.run` MTRDown [downMigr]) + schema'' <- getSchema testDB testSchema + schema'' `shouldBe` schema + withConnection st (`Migrations.run` MTRUp [m]) + +getSchema :: FilePath -> FilePath -> IO String +getSchema dpPath schemaPath = do + void $ readCreateProcess (shell $ "sqlite3 " <> dpPath <> " '.schema --indent' > " <> schemaPath) "" + sch <- readFile schemaPath + sch `deepseq` pure sch diff --git a/website/langs/es.json b/website/langs/es.json index dc0c372294..93017deea6 100644 --- a/website/langs/es.json +++ b/website/langs/es.json @@ -1,43 +1,103 @@ { "reference": "Referencia", - "why-simplex": "Por que Simplex", + "why-simplex": "Por qué Simplex", "simplex-privacy": "Privacidad de SimpleX", - "simplex-explained-tab-1-text": "1. Lo que experimentan los usuarios", - "simplex-explained-tab-2-text": "2. ¿Cómo funciona?", - "simplex-network": "La red Simplex", + "simplex-explained-tab-1-text": "1. Experiencia del usuario", + "simplex-explained-tab-2-text": "2. Cómo funciona", + "simplex-network": "Red Simplex", "simplex-explained-tab-3-text": "3. Qué ven los servidores", - "simplex-explained-tab-2-p-2": "Los servidores solo pasan mensajes de una manera, sin tener la imagen completa de la conversación o las conexiones del usuario.", - "simplex-explained-tab-3-p-2": "Los usuarios pueden mejorar aún más la privacidad de los metadatos utilizando Tor para acceder a los servidores, evitando la correlación por dirección IP.", + "simplex-explained-tab-2-p-2": "Los servidores solo transmiten mensajes en un sentido para no disponer de la imagen completa de la conversación o las conexiones del usuario.", + "simplex-explained-tab-3-p-2": "El usuario puede mejorar aún más la privacidad de los metadatos usando la red Tor para el acceso a los servidores y evitando así la correlación por dirección IP.", "smp-protocol": "Protocolo SMP", "donate": "Donar", - "copyright-label": "© 2020-2023 SimpleX | Proyecto Open-Source", + "copyright-label": "© 2020-2023 SimpleX | Proyecto de Código Abierto", "simplex-chat-protocol": "Protocolo de SimpleX Chat", "terms-and-privacy-policy": "Términos y Política de Privacidad", - "hero-header": "La privacidad redefinida", - "hero-overlay-1-textlink": "¿Por qué los ID de usuario son malos para la privacidad?", + "hero-header": "Privacidad redefinida", + "hero-overlay-1-textlink": "¿Por qué los ID de usuario son perjudiciales para la privacidad?", "hero-overlay-2-textlink": "¿Cómo funciona SimpleX?", - "hero-2-header": "Haz una conexión privada", + "hero-2-header": "Hacer una conexión privada", "hero-overlay-1-title": "¿Cómo funciona SimpleX?", - "hero-overlay-2-title": "¿Por qué los ID de usuario son malos para la privacidad?", - "feature-1-title": "Mensajes cifrados E2E con markdown y edición", - "feature-3-title": "Grupos secretos descentralizados —
sólo los usuarios saben que existen", - "feature-6-title": "Llamadas de audio y vídeo con cifrado E2E", + "hero-overlay-2-title": "¿Por qué los ID de usuario son perjudiciales para la privacidad?", + "feature-1-title": "Mensajes cifrados E2E con marcadores y edición", + "feature-3-title": "Grupos secretos descentralizados —
sólo los usuarios saben de su existencia", + "feature-6-title": "Llamadas y videollamadas con cifrado E2E", "simplex-network-overlay-1-title": "Comparación con protocolos de mensajería P2P", "developers": "Desarrolladores", "features": "Características", "blog": "Blog", "home": "Inicio", - "simplex-explained-tab-3-p-1": "Los servidores tienen credenciales anónimas separadas para cada cola y no saben a qué usuarios pertenecen.", + "simplex-explained-tab-3-p-1": "Los servidores tienen credenciales diferentes y anónimas por cada cola y desconocen a qué usuarios pertenecen.", "hero-p-1": "Otras aplicaciones tienen ID de usuario: Signal, Matrix, Session, Briar, Jami, Cwtch, etc.
SimpleX no los tiene, ni siquiera números aleatorios.
Esto mejora radicalmente su privacidad.", - "hero-2-header-desc": "El video muestra cómo te conectas con tu amigo a través de su código QR de un solo uso, en persona o a través de un enlace de video. También puede conectarse compartiendo un enlace de invitación.", - "feature-7-title": "Base de datos cifrada portátil— mueva su perfil a otro dispositivo", - "simplex-private-card-4-point-2": "Para usar SimpleX a través de Tor, instale Orbot app y active el proxy SOCKS5 (o VPN en iOS).", - "simplex-private-card-3-point-1": "Para las conexiones cliente-servidor sólo se utiliza TLS 1.2/1.3 con algoritmos robustos.", - "simplex-private-card-4-point-1": "Para proteger su dirección IP, puede acceder a los servidores a través de Tor u otra red de transporte superpuesta.", + "hero-2-header-desc": "El video muestra cómo te conectas con tu amigo a través de su código QR de un solo uso, en persona o a través de un enlace de video. También puedes conectarte compartiendo un enlace de invitación.", + "feature-7-title": "Base de datos portable cifrada — transfiere tu perfil a otro dispositivo", + "simplex-private-card-4-point-2": "Para usar SimpleX a través de Tor, instala Orbot app y activa el proxy SOCKS5 (o VPN en iOS).", + "simplex-private-card-3-point-1": "Para las conexiones cliente-servidor sólo se usa TLS 1.2/1.3 con algoritmos robustos.", + "simplex-private-card-4-point-1": "Para proteger tu dirección IP puedes acceder a los servidores a través de la red Tor u otra red de transporte superpuesta.", "simplex-private-card-5-point-2": "Esto hace que mensajes de distintos tamaños parezcan iguales a los ojos de los servidores y observadores de la red.", "simplex-private-card-6-point-1": "Muchas plataformas de comunicación son vulnerables a ataques MITM por parte de servidores o proveedores de red.", - "simplex-private-card-7-point-1": "Para garantizar la integridad, los mensajes se numeran secuencialmente e incluyen el hash del mensaje anterior.", - "simplex-private-card-7-point-2": "Si se añade, elimina o modifica algún mensaje, se avisará al destinatario.", + "simplex-private-card-7-point-1": "Para garantizar la integridad los mensajes se numeran secuencialmente e incluyen el hash del mensaje anterior.", + "simplex-private-card-7-point-2": "Si se añade, elimina o modifica algún mensaje, se alerta al destinatario.", "simplex-private-card-8-point-1": "Los servidores de SimpleX actúan como nodos de mezcla de baja latencia — los mensajes entrantes y salientes tienen un orden diferente.", - "simplex-private-card-9-point-1": "Cada cola de mensajes pasa mensajes en una dirección, con diferentes direcciones de envío y recepción." -} + "simplex-private-card-9-point-1": "Cada cola de mensajes transmite las distintas direcciones de envío y recepción en un solo sentido.", + "simplex-explained": "Simplex explicado", + "simplex-explained-tab-1-p-1": "Puedes crear contactos y grupos, y mantener conversaciones bidireccionales como en cualquier aplicación de mensajería.", + "simplex-explained-tab-1-p-2": "¿Cómo puede funcionar con colas unidireccionales y sin identificadores de usuario?", + "simplex-explained-tab-2-p-1": "Por cada conexión se usan dos colas de mensajes separadas para que el envío y recepción sean a través de servidores diferentes.", + "chat-bot-example": "Ejemplo de bot de Chat", + "chat-protocol": "Protocolo Chat", + "terminal-cli": "Terminal CLI", + "hero-subheader": "El primer mensajero
sin ID de usuario", + "feature-2-title": "Cifrado E2E
de imágenes y archivos", + "feature-8-title": "Modo Incógnito —
único de SimpleX Chat", + "simplex-private-1-title": "2 capas de
cifrado de extremo a extremo", + "simplex-private-2-title": "Capa adicional de
cifrado del servidor", + "simplex-private-3-title": "Transporte TLS
seguro y auténticado", + "simplex-private-4-title": "Acceso opcional
a través de Tor", + "simplex-private-7-title": "Verificación de la
integridad del mensaje", + "feature-4-title": "Mensajes de voz cifrados E2E", + "feature-5-title": "Mensajes temporales", + "simplex-private-6-title": "Intercambio de claves
fuera de banda", + "simplex-private-5-title": "Múltiples capas
de contenido acolchado", + "simplex-private-8-title": "Mezcla de mensajes
para reducir la correlación", + "simplex-private-10-title": "Identificadores por pares anónimos temporales", + "simplex-private-9-title": "Colas de mensajes
unidireccionales", + "simplex-private-card-1-point-1": "Protocolo de doble contraseña —Mensajería OTR (Off-the-Record) con perfecto secreto hacia adelante (PFS) y recuperación de intrusión.", + "simplex-private-card-1-point-2": "NaCL cryptobox en cada cola para evitar la correlación de tráfico entre colas de mensajes si la TLS se ve comprometida.", + "simplex-private-card-2-point-1": "Capa adicional de cifrado del servidor para la entrega al destinatario que evita la correlación entre el tráfico de recepción y envío del servidor si la TLS se ve comprometida.", + "simplex-private-card-3-point-2": "La huella digital del servidor y la vinculación de canales evitan los ataques MITM (ataque de intermediario) y de repetición.", + "simplex-private-card-3-point-3": "La reanudación de la conexión está deshabilitada para evitar ataques de sesión.", + "simplex-private-card-5-point-1": "SimpleX utiliza relleno de contenido en cada capa de cifrado para frustrar los ataques al tamaño de los mensajes.", + "simplex-private-card-6-point-2": "Para evitarlo las aplicaciones SimpleX pasan claves de un solo uso fuera de banda cuando compartes un enlace de dirección o un código QR.", + "simplex-private-card-9-point-2": "Esto reduce los vectores de ataque, en comparación con los agentes de mensajes tradicionales, y los metadatos disponibles.", + "simplex-private-card-10-point-1": "SimpleX usa direcciones y credenciales temporales anónimas por pares para cada contacto de usuario o miembro de grupo.", + "privacy-matters-1-title": "Publicidad y discriminación de precios", + "privacy-matters-1-overlay-1-title": "La privacidad te ahorra dinero", + "privacy-matters-1-overlay-1-linkText": "La privacidad te ahorra dinero", + "privacy-matters-2-overlay-1-title": "La privacidad te da poder", + "privacy-matters-3-title": "Enjuiciamiento por asociación inocente", + "privacy-matters-3-overlay-1-linkText": "La privacidad protege tu libertad", + "simplex-unique-1-title": "Tienes total privacidad", + "simplex-unique-2-title": "Estás protegido
contra el spam y el abuso", + "simplex-unique-3-title": "Tú controlas tus datos", + "simplex-unique-4-title": "Eres dueño de la red SimpleX", + "hero-overlay-card-1-p-1": "Muchos usuarios se preguntan: si SimpleX no tiene identificadores de usuario, ¿cómo puede saber dónde entregar los mensajes?", + "hero-overlay-card-1-p-4": "Este diseño evita que se filtren metadatos de usuario a nivel de aplicación. Para mejorar aún más la privacidad y proteger tu dirección IP, puedes conectarte a los servidores de mensajería a través de la red Tor.", + "hero-overlay-card-1-p-6": "Más información en el artículo técnico SimpleX.", + "hero-overlay-card-2-p-1": "Cuando los usuarios asumen identidades persistentes, aunque sólo se trate de un número aleatorio como un identificador de sesión, existe el riesgo de que el proveedor o un atacante puedan observar cómo se conectan los usuarios y cuántos mensajes envían.", + "hero-overlay-card-2-p-2": "A continuación podrían correlacionar esta información con las redes sociales públicas existentes y averiguar las identidades reales.", + "hero-overlay-card-2-p-3": "Incluso con las aplicaciones más privadas que utilizan los servicios de Tor v3, si hablas con dos contactos a través de un mismo perfil se puede probar que están conectados con la misma persona.", + "privacy-matters-2-overlay-1-linkText": "La privacidad te da poder", + "simplex-private-card-10-point-2": "Esto permite entregar mensajes sin identificadores de perfil de usuario y proporcionando una mayor privacidad de metadatos que las alternativas.", + "privacy-matters-3-overlay-1-title": "La privacidad protege tu libertad", + "simplex-unique-1-overlay-1-title": "Privacidad total de tu identidad, perfil, contactos y metadatos", + "privacy-matters-2-title": "Manipulación de elecciones", + "simplex-unique-2-overlay-1-title": "La mejor protección contra el spam y el abuso", + "simplex-unique-3-overlay-1-title": "Titularidad, control y seguridad de tus datos", + "simplex-unique-4-overlay-1-title": "Totalmente descentralizado — los usuarios son dueños de la red SimpleX", + "hero-overlay-card-1-p-2": "Para entregar los mensajes, en lugar de los identificadores de usuario utilizados por todas las demás plataformas, SimpleX usa identificadores por pares anónimos y temporales de colas de mensajes, independientes para cada una de sus conexiones — no hay identificadores a largo plazo.", + "hero-overlay-card-1-p-5": "Los perfiles de usuario, contactos y grupos sólo se almacenan en los dispositivos cliente; los mensajes se envían de extremo a extremo con cifrado de doble capa.", + "hero-overlay-card-1-p-3": "Tú defines qué servidor(es) usas para recibir los mensajes, tus contactos — los servidores que utilizas para enviar los mensajes. Es probable que cada conversación use dos servidores distintos.", + "hero-overlay-card-2-p-4": "SimpleX protege contra estos ataques al no disponer de ID de usuario en su diseño. Y si usas el modo incógnito, tendrás un nombre mostrado diferente por cada contacto, evitando cualquier dato compartido entre ellos.", + "simplex-network-overlay-card-1-p-1": "Los protocolos y aplicaciones de mensajería P2P presentan varios problemas que los hacen menos fiables que SimpleX, más complejos de analizar y más vulnerables a varios tipos de ataques." +} \ No newline at end of file diff --git a/website/langs/nl.json b/website/langs/nl.json index d7adc5049d..42f36eae08 100644 --- a/website/langs/nl.json +++ b/website/langs/nl.json @@ -11,7 +11,7 @@ "simplex-explained-tab-1-text": "1. Wat gebruikers ervaren", "simplex-explained-tab-3-text": "3. Wat servers zien", "simplex-explained-tab-1-p-1": "U kunt contacten en groepen maken en tweerichtings gesprekken voeren, zoals in elke andere messenger.", - "simplex-explained-tab-1-p-2": "Hoe kan het werken met unidirectionele wachtrijen en zonder gebruikersprofiel-ID's?", + "simplex-explained-tab-1-p-2": "Hoe kan het werken met unidirectionele wachtrijen en zonder gebruikers profiel ID's?", "simplex-explained-tab-3-p-1": "De servers hebben afzonderlijke anonieme inloggegevens voor elke wachtrij en weten niet tot welke gebruikers ze behoren.", "simplex-explained-tab-3-p-2": "Gebruikers kunnen de privacy van metadata verder verbeteren door Tor te gebruiken om toegang te krijgen tot servers, waardoor corellatie op IP-adres wordt voorkomen.", "chat-bot-example": "Chatbot voorbeeld", @@ -51,7 +51,7 @@ "simplex-private-card-6-point-1": "Veel communicatie platforms zijn kwetsbaar voor MITM aanvallen door servers of netwerk providers.", "simplex-private-card-7-point-2": "Als een bericht wordt toegevoegd, verwijderd of gewijzigd, wordt de ontvanger gewaarschuwd.", "simplex-private-card-8-point-1": "SimpleX servers fungeren als mix nodes met lage latentie — de inkomende en uitgaande berichten hebben een andere volgorde.", - "simplex-private-card-9-point-2": "Het vermindert de aanvals mogenlijkheden, in vergelijking met traditionele berichten diensten, en de beschikbare metadata.", + "simplex-private-card-9-point-2": "Het vermindert de aanvals mogenlijkheden, in vergelijking met traditionele berichten diensten en de beschikbare metadata.", "simplex-private-card-10-point-1": "SimpleX gebruikt tijdelijke anonieme paarsgewijze adressen en inloggegevens voor elk gebruikers contact of groepslid.", "simplex-explained-tab-2-text": "2. Hoe werkt het", "chat-protocol": "Chat protocol", @@ -64,7 +64,7 @@ "simplex-private-card-1-point-1": "Protocol met double-ratchet -
OTR-berichten met perfecte voorwaartse geheimhouding en inbraak herstel.", "simplex-private-6-title": "Out-of-band
sleuteluitwisseling", "simplex-private-7-title": "Bericht integriteit
verificatie", - "simplex-private-card-4-point-1": "Om uw IP-adres te beschermen, kunt u via Tor of een ander transport overlay netwerk toegang krijgen tot de servers.", + "simplex-private-card-4-point-1": "Om uw IP-adres te beschermen kunt u via Tor of een ander transport overlay netwerk toegang krijgen tot de servers.", "simplex-private-card-4-point-2": "Installeer de Orbot-app en schakel de SOCKS5-proxy in (of VPN op iOS ) om SimpleX via Tor te gebruiken.", "simplex-private-card-6-point-2": "Om te voorkomen dat SimpleX apps eenmalige sleutels out-of-band doorgeven, wanneer u een adres deelt als een link of een QR-code.", "simplex-private-card-7-point-1": "Om de integriteit te garanderen, zijn de berichten opeenvolgend genummerd en bevatten ze de hash van het vorige bericht.", @@ -87,9 +87,9 @@ "simplex-unique-3-overlay-1-title": "Eigendom, controle en beveiliging van uw gegevens", "simplex-unique-4-title": "U bent eigenaar van het SimpleX netwerk", "simplex-unique-4-overlay-1-title": "Volledig gedecentraliseerd — gebruikers zijn eigenaar van het SimpleX netwerk", - "hero-overlay-card-1-p-3": "U definieert welke server(s) u wilt gebruiken om de berichten te ontvangen, uw contacten — de servers die u gebruikt om de berichten naar hen te verzenden. Elk gesprek gebruikt waarschijnlijk twee verschillende servers.", - "hero-overlay-card-2-p-1": "Wanneer gebruikers een blijvende identiteit hebben, zelfs als dit slechts een willekeurig nummer is, zoals een sessie-ID, bestaat het risico dat de provider of een aanvaller kan zien hoe de gebruikers zijn verbonden en hoeveel berichten ze verzenden.", - "hero-overlay-card-1-p-4": "Dit ontwerp voorkomt het lekken van eventuele gebruikersgegevens. metadata op applicatie niveau. Om de privacy verder te verbeteren en uw IP-adres te beschermen, kunt u via Tor verbinding maken met berichten servers.", + "hero-overlay-card-1-p-3": "U definieert welke server(s) u wilt gebruiken om de berichten te ontvangen. Uw contacten de servers die u gebruikt om de berichten naar hen te verzenden. Elk gesprek gebruikt waarschijnlijk twee verschillende servers.", + "hero-overlay-card-2-p-1": "Wanneer gebruikers een blijvende identiteit hebben, zelfs als dit slechts een willekeurig nummer is, zoals een sessie ID, bestaat het risico dat de provider of een aanvaller kan zien hoe de gebruikers zijn verbonden en hoeveel berichten ze verzenden.", + "hero-overlay-card-1-p-4": "Dit ontwerp voorkomt het lekken van eventuele gebruikersgegevens en metadata op applicatie niveau. Om de privacy verder te verbeteren en uw IP-adres te beschermen kunt u via Tor verbinding maken met berichten servers.", "hero-overlay-card-1-p-5": "Alleen client apparaten slaan gebruikers profielen, contacten en groepen op; de berichten worden verzonden met 2-laags end-to-end versleuteling.", "hero-overlay-card-1-p-6": "Lees meer in SimpleX-whitepaper.", "hero-overlay-card-2-p-2": "Ze zouden deze informatie vervolgens kunnen correleren met de bestaande openbare sociale netwerken en een aantal echte identiteiten kunnen bepalen.", @@ -101,7 +101,7 @@ "hero-overlay-card-2-p-4": "SimpleX beschermt tegen deze aanvallen door geen gebruikers ID's in het ontwerp te hebben. En als u de incognitomodus gebruikt, heeft u voor elk contact een andere weergavenaam, waardoor gedeelde gegevens tussen hen worden vermeden.", "simplex-network-overlay-card-1-p-1": "P2P-berichtenprotocollen en -apps hebben verschillende problemen waardoor ze minder betrouwbaar zijn dan SimpleX, complexer om te analyseren en kwetsbaarder voor verschillende soorten aanvallen.", "simplex-network-overlay-card-1-li-1": "P2P netwerken vertrouwen op een variant van DHT om berichten te routeren. DHT ontwerpen moeten een balans vinden tussen leveringsgarantie en latentie. SimpleX heeft zowel een betere leveringsgarantie als een lagere latentie dan P2P, omdat het bericht redundant via meerdere servers parallel kan worden doorgegeven, met behulp van de servers die door de ontvanger zijn gekozen. In P2P netwerken wordt het bericht achtereenvolgens door O(log N)-knooppunten geleid, met behulp van door het algoritme gekozen knooppunten.", - "simplex-network-overlay-card-1-li-2": "SimpleX ontwerp heeft, in tegenstelling tot de meeste P2P netwerken, geen globale gebruikers ID's van welke soort dan ook, zelfs niet tijdelijk, en gebruikt alleen tijdelijke paarsgewijze ID's, wat een betere anonimiteit en metadata bescherming biedt.", + "simplex-network-overlay-card-1-li-2": "SimpleX ontwerp heeft, in tegenstelling tot de meeste P2P netwerken, geen globale gebruikers ID's van welke soort dan ook, zelfs niet tijdelijk en gebruikt alleen tijdelijke paarsgewijze ID's wat een betere anonimiteit en metadata bescherming biedt.", "simplex-network-overlay-card-1-li-3": "P2P lost het probleem MITM-aanval niet op en de meeste bestaande implementaties gebruiken geen out-of-band berichten voor de initiële sleuteluitwisseling. SimpleX gebruikt out-of-band berichten of, in sommige gevallen, reeds bestaande veilige en vertrouwde verbindingen voor de eerste sleuteluitwisseling.", "simplex-network-overlay-card-1-li-4": "P2P implementaties kunnen door sommige internetproviders worden geblokkeerd (zoals BitTorrent). SimpleX is transport-agnostisch - het kan werken via standaard webprotocollen, b.v. WebSockets.", "simplex-network-overlay-card-1-li-5": "Alle bekende P2P netwerken kunnen kwetsbaar zijn voor Sybil-aanval, omdat elk knooppunt vindbaar is en het netwerk als een geheel opereert. Bekende maatregelen om het te verminderen, vereisen een gecentraliseerde component of duur bewijs van werk . Het SimpleX-netwerk kan niet worden gedetecteerd door de server, het is gefragmenteerd en werkt als meerdere geïsoleerde subnetwerken, waardoor netwerkbrede aanvallen onmogelijk zijn.", @@ -149,12 +149,12 @@ "simplex-network-section-header": "SimpleX Netwerk", "simplex-network-section-desc": "Simplex Chat biedt de beste privacy door de voordelen van P2P en gefedereerde netwerken te combineren.", "simplex-network-1-header": "In tegenstelling tot P2P netwerken", - "simplex-network-1-desc": "Alle berichten worden verzonden via de servers, die beide zorgen voor een betere metadata privacy en een betrouwbare asynchrone bezorging van berichten, terwijl er veel worden vermeden", - "simplex-network-1-overlay-linktext": "problemen van P2P netwerken", + "simplex-network-1-desc": "Alle berichten worden verzonden via servers, die zorgen voor een betere metadata privacy en een betrouwbare asynchrone bezorging van berichten, terwijl er veel word vermeden", + "simplex-network-1-overlay-linktext": "van problemen met P2P netwerken", "simplex-network-2-header": "In tegenstelling tot federatieve netwerken", "simplex-network-3-header": "SimpleX netwerk", - "simplex-network-3-desc": "servers bieden unidirectionele wachtrijen om de gebruikers te verbinden, maar ze hebben geen zicht op de netwerkverbindingsgrafiek — alleen de gebruikers.", - "simplex-network-2-desc": "SimpleX relay servers slaan GEEN gebruikersprofielen, contacten en afgeleverde berichten op, maken GEEN verbinding met elkaar en er is GEEN serverdirectory.", + "simplex-network-3-desc": "servers bieden unidirectionele wachtrijen om de gebruikers te verbinden, maar ze hebben geen zicht op de netwerk verbindings grafiek — alleen de gebruikers.", + "simplex-network-2-desc": "SimpleX relay servers slaan GEEN gebruikers profielen, contacten en afgeleverde berichten op, maken GEEN verbinding met elkaar en er is GEEN server directory.", "comparison-section-header": "Vergelijking met andere protocollen", "protocol-1-text": "Signaal, grote platforms", "protocol-2-text": "XMPP, Matrix", @@ -176,7 +176,7 @@ "privacy-matters-overlay-card-1-p-2": "Webwinkels weten dat mensen met lagere inkomens eerder geneigd zijn om dringende aankopen te doen, dus kunnen ze hogere prijzen vragen of kortingen intrekken.", "privacy-matters-overlay-card-1-p-3": "Sommige financiële en verzekeringsmaatschappijen gebruiken sociale grafieken om rentetarieven en premies te bepalen. Het zorgt er vaak voor dat mensen met lagere inkomens meer betalen — het staat bekend als 'armoedepremie'.", "privacy-matters-overlay-card-1-p-4": "Het SimpleX platform beschermt de privacy van uw verbindingen beter dan welk alternatief dan ook, waardoor volledig wordt voorkomen dat uw sociale grafiek beschikbaar wordt voor bedrijven of organisaties. Zelfs wanneer mensen servers van SimpleX Chat gebruiken, weten we niet hoeveel gebruikers of hun verbindingen zijn.", - "privacy-matters-overlay-card-2-p-2": "Om objectief te zijn en onafhankelijke beslissingen te kunnen nemen, moet u de controle hebben over uw informatie ruimte. Het is alleen mogelijk als u een privé communicatieplatform gebruikt dat geen toegang heeft tot uw sociale grafiek.", + "privacy-matters-overlay-card-2-p-2": "Om objectief te zijn en onafhankelijke beslissingen te kunnen nemen, moet u de controle hebben over uw informatie. Het is alleen mogelijk als u een privé communicatieplatform gebruikt dat geen toegang heeft tot uw sociale grafiek.", "privacy-matters-overlay-card-2-p-3": "SimpleX is het eerste platform dat standaard geen gebruikers ID's heeft, waardoor uw verbindingsgrafiek beter wordt beschermd dan welk alternatief dan ook.", "privacy-matters-overlay-card-3-p-1": "Iedereen zou zich zorgen moeten maken over de privacy en veiligheid van hun communicatie — onschuldige gesprekken kunnen je in gevaar brengen, zelfs als je niets te verbergen hebt.", "simplex-unique-overlay-card-1-p-1": "In tegenstelling tot andere berichten platforms heeft SimpleX geen identifiers toegewezen aan de gebruikers . Het vertrouwt niet op telefoonnummers, domein gebaseerde adressen (zoals e-mail of XMPP), gebruikersnamen, openbare sleutels of zelfs willekeurige nummers om zijn gebruikers te identificeren — we weten niet ' hoeveel mensen onze SimpleX servers gebruiken.", @@ -187,9 +187,9 @@ "simplex-unique-overlay-card-1-p-3": "Dit ontwerp beschermt de privacy van met wie u communiceert en verbergt deze voor SimpleX platform servers en voor waarnemers. Om uw IP-adres voor de servers te verbergen, kunt u verbinding maken met SimpleX servers via Tor .", "simplex-unique-overlay-card-2-p-1": "Omdat je geen identificatie hebt op het SimpleX platform, kan niemand contact met je opnemen, tenzij je een eenmalig of tijdelijk gebruikers adres deelt, als een QR-code of een link.", "simplex-unique-overlay-card-2-p-2": "Zelfs met het optionele gebruikers adres, hoewel het kan worden gebruikt om spam-contact verzoeken te verzenden, kunt u het wijzigen of volledig verwijderen zonder uw verbindingen te verliezen.", - "simplex-unique-overlay-card-3-p-1": "SimpleX Chat slaat alle gebruikersgegevens alleen op clientapparaten op met behulp van een draagbaar gecodeerd databaseformaat dat kan worden geëxporteerd en overgebracht naar elk ondersteund apparaat.", + "simplex-unique-overlay-card-3-p-1": "SimpleX Chat slaat alle gebruikersgegevens alleen op client apparaten op met behulp van een draagbaar gecodeerd databaseformaat dat kan worden geëxporteerd en overgebracht naar elk ondersteund apparaat.", "simplex-unique-overlay-card-3-p-2": "De end-to-end versleutelde berichten worden tijdelijk op SimpleX relayservers bewaard totdat ze zijn ontvangen, waarna ze permanent worden verwijderd.", - "simplex-unique-overlay-card-3-p-3": "In tegenstelling tot gefedereerde netwerkservers (e-mail, XMPP of Matrix), slaan SimpleX servers geen gebruikersaccounts op, ze sturen alleen berichten door, waardoor de privacy van beide partijen wordt beschermd.", + "simplex-unique-overlay-card-3-p-3": "In tegenstelling tot gefedereerde netwerkservers (e-mail, XMPP of Matrix), slaan SimpleX servers geen gebruikers account op, ze sturen alleen berichten door, waardoor de privacy van beide partijen wordt beschermd.", "simplex-unique-overlay-card-4-p-1": "U kunt SimpleX gebruiken met uw eigen servers en nog steeds communiceren met mensen die de door ons geleverde vooraf geconfigureerde servers gebruiken.", "simplex-unique-overlay-card-4-p-3": "Als u overweegt om voor het SimpleX platform te ontwikkelen, bijvoorbeeld de chatbot voor gebruikers van de SimpleX app, of de integratie van de SimpleX Chat bibliotheek in uw mobiele apps, get in touch for any advice en ondersteuning.", "simplex-unique-card-1-p-1": "SimpleX beschermt de privacy van uw profiel, contacten en metadata en verbergt deze voor SimpleX platformservers en eventuele waarnemers.", @@ -201,7 +201,7 @@ "privacy-matters-overlay-card-2-p-1": "Nog niet zo lang geleden zagen we dat de grote verkiezingen werden gemanipuleerd door een gerenommeerd adviesbureau dat onze sociale grafieken gebruikte om ons beeld van de echte wereld te vervormen en onze stemmen te manipuleren.", "simplex-unique-overlay-card-3-p-4": "Er zijn geen identifiers of gecodeerde tekst tussen verzonden en ontvangen serververkeer — als iemand het waarneemt, kunnen ze niet gemakkelijk bepalen wie met wie communiceert, zelfs als TLS is gecompromitteerd.", "simplex-unique-overlay-card-4-p-2": "Het SimpleX platform gebruikt een open protocol en biedt SDK om chatbots te maken, waardoor services kunnen worden geïmplementeerd waarmee gebruikers kunnen communiceren via SimpleX Chat apps — waar we' erg naar uitkijken om te zien welke SimpleX services u kunt bouwen.", - "simplex-unique-card-1-p-2": "In tegenstelling tot elk ander bestaand berichtenplatform, heeft SimpleX geen identificatiecodes toegewezen aan de gebruikers — zelfs geen willekeurige nummers .", + "simplex-unique-card-1-p-2": "In tegenstelling tot elk ander bestaand berichten platform, heeft SimpleX geen identificatiecodes toegewezen aan de gebruikers — zelfs geen willekeurige nummers .", "comparison-section-list-point-1": "Meestal op basis van een telefoonnummer, in sommige gevallen op gebruikersnamen", "comparison-section-list-point-2": "Op DNS gebaseerde adressen", "comparison-section-list-point-4": "Als de servers van de operator zijn aangetast", diff --git a/website/src/_includes/blog_previews/20230328.html b/website/src/_includes/blog_previews/20230328.html new file mode 100644 index 0000000000..f593209230 --- /dev/null +++ b/website/src/_includes/blog_previews/20230328.html @@ -0,0 +1,12 @@ +

v4.6 is released:

+ +
    +
  • ARMv7a and Android 8+ - 2x more supported devices!
  • +
  • hidden chat profiles
  • +
  • community moderation
  • +
  • improved audio/video calls
  • +
  • reduced battery usage
  • +
  • SMP server monitoring: status bot and page
  • +
+ +

Also, the app interface is now available in Chinese and Spanish - thanks our users!

diff --git a/website/src/blog.html b/website/src/blog.html index d0394e996c..1cd8d9952a 100644 --- a/website/src/blog.html +++ b/website/src/blog.html @@ -38,6 +38,7 @@ active_blog: true

Latest news

{% for blog in collections.blogs %} + {% if not(blog.data.draft) %}
@@ -72,6 +73,7 @@ active_blog: true Read More
+ {% endif %} {% endfor %} \ No newline at end of file