From e9871b0383bccb315caa95cd88e18bf55ff3ddc3 Mon Sep 17 00:00:00 2001 From: SimpleX Chat Date: Sat, 23 May 2026 12:25:45 +0530 Subject: [PATCH 01/20] ios: update core library --- apps/ios/SimpleX.xcodeproj/project.pbxproj | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index f0bd6d9118..f19445700b 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -183,8 +183,8 @@ 64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */; }; 64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829982D54AEED006B9E89 /* libgmp.a */; }; 64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829992D54AEEE006B9E89 /* libffi.a */; }; - 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.2.0-8CNxlktzYRPIWqOtKFvRIa-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.2.0-8CNxlktzYRPIWqOtKFvRIa-ghc9.6.3.a */; }; - 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.5.2.0-8CNxlktzYRPIWqOtKFvRIa.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.2.0-8CNxlktzYRPIWqOtKFvRIa.a */; }; + 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy-ghc9.6.3.a */; }; + 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy.a */; }; 64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */; }; 64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */; }; 64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */; }; @@ -561,8 +561,8 @@ 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTimePicker.swift; sourceTree = ""; }; 64C829982D54AEED006B9E89 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; 64C829992D54AEEE006B9E89 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; - 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.2.0-8CNxlktzYRPIWqOtKFvRIa-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.5.2.0-8CNxlktzYRPIWqOtKFvRIa-ghc9.6.3.a"; sourceTree = ""; }; - 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.2.0-8CNxlktzYRPIWqOtKFvRIa.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.5.2.0-8CNxlktzYRPIWqOtKFvRIa.a"; sourceTree = ""; }; + 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy-ghc9.6.3.a"; sourceTree = ""; }; + 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy.a"; sourceTree = ""; }; 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressView.swift; sourceTree = ""; }; 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressLearnMore.swift; sourceTree = ""; }; @@ -731,8 +731,8 @@ 64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */, 64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */, 64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */, - 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.2.0-8CNxlktzYRPIWqOtKFvRIa-ghc9.6.3.a in Frameworks */, - 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.5.2.0-8CNxlktzYRPIWqOtKFvRIa.a in Frameworks */, + 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy-ghc9.6.3.a in Frameworks */, + 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy.a in Frameworks */, CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -818,8 +818,8 @@ 64C829992D54AEEE006B9E89 /* libffi.a */, 64C829982D54AEED006B9E89 /* libgmp.a */, 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */, - 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.2.0-8CNxlktzYRPIWqOtKFvRIa-ghc9.6.3.a */, - 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.2.0-8CNxlktzYRPIWqOtKFvRIa.a */, + 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy-ghc9.6.3.a */, + 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy.a */, ); path = Libraries; sourceTree = ""; From 25ab10ffa3a2d3689a7446134323798085e62d97 Mon Sep 17 00:00:00 2001 From: SimpleX Chat Date: Sat, 23 May 2026 12:26:59 +0530 Subject: [PATCH 02/20] 6.5.3: ios 333 --- apps/ios/SimpleX.xcodeproj/project.pbxproj | 40 +++++++++++----------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index f19445700b..3d274bcc85 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -2073,7 +2073,7 @@ CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 332; + CURRENT_PROJECT_VERSION = 333; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; @@ -2098,7 +2098,7 @@ "@executable_path/Frameworks", ); LLVM_LTO = YES_THIN; - MARKETING_VERSION = 6.5.2; + MARKETING_VERSION = 6.5.3; OTHER_LDFLAGS = "-Wl,-stack_size,0x1000000"; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_NAME = SimpleX; @@ -2123,7 +2123,7 @@ CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 332; + CURRENT_PROJECT_VERSION = 333; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; @@ -2148,7 +2148,7 @@ "@executable_path/Frameworks", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.5.2; + MARKETING_VERSION = 6.5.3; OTHER_LDFLAGS = "-Wl,-stack_size,0x1000000"; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_NAME = SimpleX; @@ -2165,11 +2165,11 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 332; + CURRENT_PROJECT_VERSION = 333; DEVELOPMENT_TEAM = 5NN7GUYB6T; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 6.5.2; + MARKETING_VERSION = 6.5.3; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2185,11 +2185,11 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 332; + CURRENT_PROJECT_VERSION = 333; DEVELOPMENT_TEAM = 5NN7GUYB6T; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MARKETING_VERSION = 6.5.2; + MARKETING_VERSION = 6.5.3; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2210,7 +2210,7 @@ CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 332; + CURRENT_PROJECT_VERSION = 333; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; GCC_OPTIMIZATION_LEVEL = s; @@ -2225,7 +2225,7 @@ "@executable_path/../../Frameworks", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.5.2; + MARKETING_VERSION = 6.5.3; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2247,7 +2247,7 @@ CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 332; + CURRENT_PROJECT_VERSION = 333; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; ENABLE_CODE_COVERAGE = NO; @@ -2262,7 +2262,7 @@ "@executable_path/../../Frameworks", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.5.2; + MARKETING_VERSION = 6.5.3; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2284,7 +2284,7 @@ CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 332; + CURRENT_PROJECT_VERSION = 333; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; DYLIB_COMPATIBILITY_VERSION = 1; @@ -2310,7 +2310,7 @@ "$(PROJECT_DIR)/Libraries/sim", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.5.2; + MARKETING_VERSION = 6.5.3; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; @@ -2335,7 +2335,7 @@ CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 332; + CURRENT_PROJECT_VERSION = 333; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; DYLIB_COMPATIBILITY_VERSION = 1; @@ -2362,7 +2362,7 @@ "$(PROJECT_DIR)/Libraries/sim", ); LLVM_LTO = YES; - MARKETING_VERSION = 6.5.2; + MARKETING_VERSION = 6.5.3; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; @@ -2389,7 +2389,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 332; + CURRENT_PROJECT_VERSION = 333; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -2404,7 +2404,7 @@ "@executable_path/../../Frameworks", ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 6.5.2; + MARKETING_VERSION = 6.5.3; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2423,7 +2423,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 332; + CURRENT_PROJECT_VERSION = 333; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -2438,7 +2438,7 @@ "@executable_path/../../Frameworks", ); LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 6.5.2; + MARKETING_VERSION = 6.5.3; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; From 1a82732f88af659bb197f73613b87763daeabd22 Mon Sep 17 00:00:00 2001 From: SimpleX Chat Date: Fri, 22 May 2026 11:50:50 +0000 Subject: [PATCH 03/20] 6.5.3: android 351, desktop 144 --- apps/multiplatform/gradle.properties | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/multiplatform/gradle.properties b/apps/multiplatform/gradle.properties index 4d504e069e..3d4bf66913 100644 --- a/apps/multiplatform/gradle.properties +++ b/apps/multiplatform/gradle.properties @@ -24,13 +24,13 @@ android.nonTransitiveRClass=true kotlin.mpp.androidSourceSetLayoutVersion=2 kotlin.jvm.target=11 -android.version_name=6.5.2 -android.version_code=349 +android.version_name=6.5.3 +android.version_code=351 android.bundle=false -desktop.version_name=6.5.2 -desktop.version_code=143 +desktop.version_name=6.5.3 +desktop.version_code=144 kotlin.version=2.1.20 gradle.plugin.version=8.7.0 From 9bd9e6a16c4840484c2d0891713aea1121208cf4 Mon Sep 17 00:00:00 2001 From: Narasimha-sc <166327228+Narasimha-sc@users.noreply.github.com> Date: Mon, 25 May 2026 15:08:48 +0000 Subject: [PATCH 04/20] desktop: fix in-app updater on Windows, AppImage, and aarch64 (#6985) * desktop: fix in-app updater silently failing on Windows chooseGitHubReleaseAssets ran `which dpkg` unconditionally to probe for Debian-derivative systems. On Windows there is no which.exe, so Runtime.exec threw IOException, which the outer catch in checkForUpdate logged and swallowed -- the update dialog never appeared. Gate the probe on desktopPlatform.isLinux(). * desktop: fix in-app updater install step on AppImage xdg-open on the downloaded .AppImage opened it in whatever the desktop environment's default handler for the AppImage MIME type is -- usually an archive viewer, which reports 'Archive format not recognized'. The running AppImage was never replaced. Detect $APPIMAGE (set by the AppImage runtime to the path of the running .AppImage file). Copy the downloaded file to a staging file in the target's own directory, mark it executable, then atomic-move it onto $APPIMAGE. Staging in the target directory keeps the final move a same-filesystem rename(2), so an interrupted copy never leaves the running AppImage partially overwritten. On failure (permission denied, target read-only, etc.) fall back to opening the parent directory so the user can install manually -- the same fallback the existing xdg-open path already used. * desktop: fix in-app updater silently failing on aarch64 AppImage The LINUX_AARCH64 githubAssetName had a literal leading space (" simplex-desktop-aarch64.AppImage"), so the exact-name filter in chooseGitHubReleaseAssets never matched the real release asset name "simplex-desktop-aarch64.AppImage". The asset list came back empty and checkForUpdate's early-return at "No assets to download for current system" suppressed the dialog. Same silent-failure pattern as the Windows bug. * plans: justify desktop in-app updater fixes --- .../common/platform/Platform.desktop.kt | 2 +- .../common/views/helpers/AppUpdater.kt | 50 ++++++--- plans/2026-05-16-desktop-updater-fixes.md | 100 ++++++++++++++++++ 3 files changed, 139 insertions(+), 13 deletions(-) create mode 100644 plans/2026-05-16-desktop-updater-fixes.md diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Platform.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Platform.desktop.kt index 97de08b07e..7ea41d3593 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Platform.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Platform.desktop.kt @@ -10,7 +10,7 @@ val desktopPlatform = detectDesktopPlatform() enum class DesktopPlatform(val libExtension: String, val configPath: String, val dataPath: String, val githubAssetName: String) { LINUX_X86_64("so", unixConfigPath, unixDataPath, "simplex-desktop-x86_64.AppImage"), - LINUX_AARCH64("so", unixConfigPath, unixDataPath, " simplex-desktop-aarch64.AppImage"), + LINUX_AARCH64("so", unixConfigPath, unixDataPath, "simplex-desktop-aarch64.AppImage"), WINDOWS_X86_64("dll", System.getenv("AppData") + File.separator + "SimpleX", System.getenv("AppData") + File.separator + "SimpleX", "simplex-desktop-windows-x86_64.msi"), MAC_X86_64("dylib", unixConfigPath, unixDataPath, "simplex-desktop-macos-x86_64.dmg"), MAC_AARCH64("dylib", unixConfigPath, unixDataPath, "simplex-desktop-macos-aarch64.dmg"); diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/helpers/AppUpdater.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/helpers/AppUpdater.kt index 974578882d..f6a6023d47 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/helpers/AppUpdater.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/helpers/AppUpdater.kt @@ -26,6 +26,8 @@ import java.io.Closeable import java.io.File import java.net.InetSocketAddress import java.net.Proxy +import java.nio.file.Files +import java.nio.file.StandardCopyOption import kotlin.math.min data class SemVer( @@ -376,7 +378,7 @@ private fun chooseGitHubReleaseAssets(release: GitHubRelease): List val res = if (isRunningFromFlatpak()) { // No need to show download options for Flatpak users emptyList() - } else if (!isRunningFromAppImage() && Runtime.getRuntime().exec("which dpkg").onExit().join().exitValue() == 0) { + } else if (desktopPlatform.isLinux() && !isRunningFromAppImage() && Runtime.getRuntime().exec("which dpkg").onExit().join().exitValue() == 0) { // Show all available .deb packages and user will choose the one that works on his system (for Debian derivatives) release.assets.filter { it.name.lowercase().endsWith(".deb") } } else { @@ -388,18 +390,42 @@ private fun chooseGitHubReleaseAssets(release: GitHubRelease): List private suspend fun installAppUpdate(file: File) = withContext(Dispatchers.IO) { when { desktopPlatform.isLinux() -> { - val process = Runtime.getRuntime().exec("xdg-open ${file.absolutePath}").onExit().join() - val startedInstallation = process.exitValue() == 0 && process.children().count() > 0 - if (!startedInstallation) { - Log.e(TAG, "Error starting installation: ${process.inputReader().use { it.readLines().joinToString("\n") }}${process.errorStream.use { String(it.readAllBytes()) }}") - // Failed to start installation. show directory with the file for manual installation - desktopOpenDir(file.parentFile) + val appImagePath = System.getenv("APPIMAGE") + if (appImagePath != null) { + // Replace the running AppImage crash-safely: copy onto the target's own + // filesystem first (an atomic rename only works within one filesystem, and + // the download lives in the temp dir which is usually a different one), + // then atomically move the staged file onto $APPIMAGE. + val target = File(appImagePath) + val staging = File(target.parentFile, ".${target.name}.update") + try { + Files.copy(file.toPath(), staging.toPath(), StandardCopyOption.REPLACE_EXISTING) + staging.setExecutable(true, false) + Files.move(staging.toPath(), target.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING) + file.delete() + AlertManager.shared.showAlertMsg( + title = generalGetString(MR.strings.app_check_for_updates_installed_successfully_title), + text = generalGetString(MR.strings.app_check_for_updates_installed_successfully_desc) + ) + } catch (e: Exception) { + Log.e(TAG, "Failed to replace AppImage: ${e.stackTraceToString()}") + staging.delete() + desktopOpenDir(file.parentFile) + } } else { - AlertManager.shared.showAlertMsg( - title = generalGetString(MR.strings.app_check_for_updates_installed_successfully_title), - text = generalGetString(MR.strings.app_check_for_updates_installed_successfully_desc) - ) - file.delete() + val process = Runtime.getRuntime().exec("xdg-open ${file.absolutePath}").onExit().join() + val startedInstallation = process.exitValue() == 0 && process.children().count() > 0 + if (!startedInstallation) { + Log.e(TAG, "Error starting installation: ${process.inputReader().use { it.readLines().joinToString("\n") }}${process.errorStream.use { String(it.readAllBytes()) }}") + // Failed to start installation. show directory with the file for manual installation + desktopOpenDir(file.parentFile) + } else { + AlertManager.shared.showAlertMsg( + title = generalGetString(MR.strings.app_check_for_updates_installed_successfully_title), + text = generalGetString(MR.strings.app_check_for_updates_installed_successfully_desc) + ) + file.delete() + } } } desktopPlatform.isWindows() -> { diff --git a/plans/2026-05-16-desktop-updater-fixes.md b/plans/2026-05-16-desktop-updater-fixes.md new file mode 100644 index 0000000000..40dbefd11d --- /dev/null +++ b/plans/2026-05-16-desktop-updater-fixes.md @@ -0,0 +1,100 @@ +# Desktop In-App Updater Fixes + +## Problem Statement + +The desktop in-app updater (`apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/helpers/AppUpdater.kt`) silently or visibly fails on three of the four supported desktop platforms: + +1. **Windows**: no update dialog ever appears for any Windows user, regardless of how out-of-date the running version is. +2. **AppImage (x86_64)**: the update dialog appears and the download succeeds, but clicking "Install update" opens the new AppImage in an archive viewer ("Archive format not recognized") instead of installing it. The running AppImage is never replaced. +3. **AppImage (aarch64)**: no update dialog ever appears for any aarch64 AppImage user. + +The desktop installer flow on macOS and the `.deb` flow on Debian-derivative Linux are not affected and remain unchanged. + +## Root Causes + +### 1. Windows — `which dpkg` IOException swallowed + +`chooseGitHubReleaseAssets` (AppUpdater.kt) invokes `Runtime.getRuntime().exec("which dpkg")` unconditionally to detect Debian-derivative systems. On Windows there is no `which.exe`; `CreateProcess` returns error 2 and the JVM throws `IOException: Cannot run program "which"` synchronously from `Runtime.exec`. The exception propagates up through `chooseGitHubReleaseAssets` into `checkForUpdate`'s outer `try { ... } catch (e: Exception) { Log.e(...) }`, which logs to stderr and returns. The user-facing alert is never built. + +The `.deb` probe was correct in intent but executed too eagerly: it has no business running on a non-Linux platform. + +### 2. AppImage — `xdg-open` is the wrong operation + +The Linux branch of `installAppUpdate` calls `xdg-open `. An AppImage is not "installable" in the package-manager sense — it is a self-contained executable that lives at the path stored in the `$APPIMAGE` environment variable (set by the AppImage runtime). On most desktop environments, `xdg-open` resolves the `.AppImage` MIME type to an archive handler (file-roller, ark, engrampa). The handler attempts to read the AppImage as a `.iso`/squashfs archive and fails with "Archive format not recognized". Even when it succeeds, it does not replace the running AppImage — the next launch still runs the old binary. + +The existing code had no awareness of `$APPIMAGE` at install time. The `GitHubAsset.isAppImage` field hints at an earlier abandoned attempt at AppImage-specific handling. + +### 3. aarch64 AppImage — leading space in asset name + +`Platform.desktop.kt` declares: + +```kotlin +LINUX_AARCH64("so", unixConfigPath, unixDataPath, " simplex-desktop-aarch64.AppImage"), +``` + +The `githubAssetName` literal has a leading space character. The actual release asset published by `.github/workflows/build.yml` is `simplex-desktop-aarch64.AppImage` (no space — verified against the live GitHub releases API). The exact-name filter in `chooseGitHubReleaseAssets` (`release.assets.filter { it.name == desktopPlatform.githubAssetName }`) never matches, the asset list is empty, and `checkForUpdate` returns at the "No assets to download for current system" branch without ever showing a dialog. Same silent-failure pattern as the Windows bug, single arch in blast radius. + +## Solution Summary + +Three small, independent commits — one per root cause. None of them changes shared logic; each touches one line (Windows, aarch64) or one branch of the install dispatch (AppImage). + +### Fix 1 — Gate the `dpkg` probe on Linux + +```kotlin +// AppUpdater.kt: chooseGitHubReleaseAssets +} else if (desktopPlatform.isLinux() && !isRunningFromAppImage() + && Runtime.getRuntime().exec("which dpkg").onExit().join().exitValue() == 0) { +``` + +Single conjunct (`desktopPlatform.isLinux() &&`) added at the start of the `else if`. Boolean short-circuit ensures `Runtime.exec` is never reached on non-Linux. The added gate matches the actual semantic intent: `.deb` is a Linux-only package format. Both the Windows IOException and the (theoretical) macOS misbehavior of the `which` probe are eliminated. + +### Fix 2 — AppImage-aware install path + +In `installAppUpdate`'s Linux branch, read `System.getenv("APPIMAGE")`: + +- If non-null, the running app is an AppImage at that path. Replacing it crash-safely takes two steps, because an atomic file replacement is only possible *within a single filesystem* (POSIX `rename(2)`), and the download lives in the temp dir — usually a different filesystem (tmpfs) from where `$APPIMAGE` lives: + 1. `Files.copy` the downloaded file to a staging file (`..update`) in the target's *own* directory. This is the unavoidable cross-filesystem transfer; it is not atomic, but it writes only a sidecar, never the live `$APPIMAGE`. + 2. Mark the staging file executable, then `Files.move` it onto `$APPIMAGE` with `ATOMIC_MOVE`. Because staging now shares the target's filesystem, this is a real atomic `rename(2)`: the live file flips from old to new in one indivisible step, never partially written. + + A direct `Files.move(downloaded, target, REPLACE_EXISTING)` is **not** sufficient — across filesystems it copies bytes straight onto the live `$APPIMAGE`, which is neither atomic nor crash-safe (an interrupted copy destroys the user's installed app). `ATOMIC_MOVE` on a cross-filesystem move throws `AtomicMoveNotSupportedException`. Staging on the target's filesystem first is what makes the atomic move possible. On Linux the kernel keeps the running process's open file descriptors valid across the rename: the running app continues to function until the user restarts, at which point the new binary is used. +- If null, fall back to the existing `xdg-open` path (used for `.deb` install on Debian, which is the only remaining caller of this path after Fix 2). + +On any exception (permission denied if the AppImage lives in `/opt/`, target read-only, etc.) the catch deletes the staging file and falls back to `desktopOpenDir(file.parentFile)` — the same fallback the original `xdg-open` path used. + +### Fix 3 — Remove leading space from `LINUX_AARCH64` asset name + +```kotlin +LINUX_AARCH64("so", unixConfigPath, unixDataPath, "simplex-desktop-aarch64.AppImage"), +``` + +Single character removed. The asset name now matches what `make-appimage-linux.sh` produces and what GitHub releases publish. + +## Why three commits, not one + +Each fix has a different blast radius, a different fix size, and (potentially) a different review path. Three focused commits let a reviewer judge each one in isolation: + +- Windows fix: 1 line, gates a side-effecting `Runtime.exec` on a platform check that the surrounding code already establishes. +- AppImage install: ~35 lines, introduces new file-system operations (`Files.copy` to a staging file, then `Files.move` with `ATOMIC_MOVE`). +- aarch64 fix: 1 character, fixes a typo in a string literal. + +Bundling them as a single commit would force a reviewer to verify all three at once and would obscure `git blame` on the AppImage install logic, which is the only one of the three that introduces meaningful new behavior. + +## Out of scope + +The following were identified during the audit (`apps/multiplatform/app-updater-audit.md`) but deliberately deferred to keep this PR focused: + +- `msiexec /i ${file.absolutePath}` uses the single-string `Runtime.exec` overload that tokenizes on whitespace; paths containing spaces (uncommon on Windows but possible) break the install. +- Download failures (network, TLS, disk-full, GitHub error) are caught but only logged; the user sees nothing. +- `process.children().count() > 0` in the Linux `xdg-open` path is racy and arguably wrong. +- No SHA256 / signature verification on the downloaded artifact — the updater installs whatever GitHub serves. +- 24h delay with no retry / backoff on transient network errors. +- macOS install hardcodes `/Applications/SimpleX.app`. + +Each is documented with `file:line` references in the audit; none affects the three platforms this PR fixes. + +## Test plan + +- **Windows**: built x86_64 MSI via the fork CI workflow [`build-windows-msi.yml`](https://github.com/Narasimha-sc/simplex-chat/actions/runs/25958413517), installed in a Windows VM as version 6.5.1 (intentionally lowered to trigger the check against current stable 6.5.2). Settings → Check for updates → Stable: dialog appeared as expected. +- **AppImage x86_64**: built locally (host build, GHC 9.6.3, gradle createDistributable, appimagetool), installed and ran on Linux. Settings → Check for updates → Stable: dialog appeared, Download landed file at `/tmp/simplex/simplex-desktop-x86_64.AppImage`, Install replaced `$APPIMAGE` in place. Verified by hashing `$APPIMAGE` before and after. +- **aarch64 AppImage**: not separately tested. Fix is a 1-character literal change verified against the live GitHub releases API (`simplex-desktop-aarch64.AppImage`, no leading space). +- **macOS**: no changes to the macOS install branch. From ff36d401ce40ec9a0d5f9073a589e656a1a57e00 Mon Sep 17 00:00:00 2001 From: Narasimha-sc <166327228+Narasimha-sc@users.noreply.github.com> Date: Mon, 25 May 2026 15:10:55 +0000 Subject: [PATCH 05/20] desktop: fix video playback hang caused by stuck preview snapshot (#6983) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * desktop: fix video playback hang caused by stuck preview snapshot Problem: clicking play on a video did nothing when an earlier video's preview generation was stuck — every subsequent VideoPlayer.play() was queued behind it on the shared playerThread. Cause: helper player reuse across previews exhausted the libavcodec h264 frame-buffer pool with --avcodec-hw=none (PR #6924), and the synchronous libvlc snapshots().get() call then hung waiting for a frame that was never decoded. Fix: drop the helper-player pool (release each helper after use) and run preview generation on a dedicated previewThread so a stuck preview can no longer block playback. * plans: add 2026-05-15-fix-video-preview-snapshot-hang.md * desktop: capture preview via callback surface, keep helper pool Follows up on the previous commit (4a964c66). The actual hang was in libvlc's synchronous snapshots().get() on a reused helper, not in the pooling itself. Replace the polling loop with a CallbackVideoSurface (the existing SkiaBitmapVideoSurface) wrapped in withTimeoutOrNull — the wait is bounded, so a non-decoding helper can't block previewThread. Restore the helper-player pool that the previous commit dropped. * plans: update 2026-05-15-fix-video-preview-snapshot-hang.md for final fix --- .../common/platform/VideoPlayer.desktop.kt | 14 +++-- ...6-05-15-fix-video-preview-snapshot-hang.md | 57 +++++++++++++++++++ 2 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 plans/2026-05-15-fix-video-preview-snapshot-hang.md diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/VideoPlayer.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/VideoPlayer.desktop.kt index 90c80d3b2a..c3b6dc3a4c 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/VideoPlayer.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/VideoPlayer.desktop.kt @@ -6,6 +6,7 @@ import androidx.compose.ui.graphics.* import chat.simplex.common.views.helpers.* import chat.simplex.res.MR import kotlinx.coroutines.* +import org.jetbrains.compose.videoplayer.SkiaBitmapVideoSurface import uk.co.caprica.vlcj.media.VideoOrientation import uk.co.caprica.vlcj.player.base.* import uk.co.caprica.vlcj.player.component.CallbackMediaPlayerComponent @@ -214,7 +215,7 @@ actual class VideoPlayer actual constructor( } } - suspend fun getBitmapFromVideo(defaultPreview: ImageBitmap?, uri: URI?, withAlertOnException: Boolean = true): VideoPlayerInterface.PreviewAndDuration = withContext(playerThread.asCoroutineDispatcher()) { + suspend fun getBitmapFromVideo(defaultPreview: ImageBitmap?, uri: URI?, withAlertOnException: Boolean = true): VideoPlayerInterface.PreviewAndDuration = withContext(previewThread.asCoroutineDispatcher()) { val mediaComponent = getOrCreateHelperPlayer() val player = mediaComponent.mediaPlayer() if (uri == null || !uri.toFile().exists()) { @@ -222,12 +223,12 @@ actual class VideoPlayer actual constructor( return@withContext VideoPlayerInterface.PreviewAndDuration(preview = defaultPreview, timestamp = 0L, duration = 0L) } + val surface = SkiaBitmapVideoSurface() + player.videoSurface().set(surface) player.media().startPaused(uri.toFile().absolutePath) - val start = System.currentTimeMillis() - var snap: BufferedImage? = null - while (snap == null && start + 1500 > System.currentTimeMillis()) { - snap = player.snapshots()?.get() - delay(50) + val snap = withTimeoutOrNull(1500L) { + while (surface.bitmap.value == null) delay(50) + surface.bitmap.value!!.toAwtImage() } val orientation = player.media().info().videoTracks().firstOrNull()?.orientation() if (orientation == null) { @@ -255,6 +256,7 @@ actual class VideoPlayer actual constructor( } val playerThread = Executors.newSingleThreadExecutor() + private val previewThread = Executors.newSingleThreadExecutor() private val playersPool: ArrayList = ArrayList() private val helperPlayersPool: ArrayList = ArrayList() diff --git a/plans/2026-05-15-fix-video-preview-snapshot-hang.md b/plans/2026-05-15-fix-video-preview-snapshot-hang.md new file mode 100644 index 0000000000..4a64d0ca43 --- /dev/null +++ b/plans/2026-05-15-fix-video-preview-snapshot-hang.md @@ -0,0 +1,57 @@ +# Desktop: video playback hangs after a preview snapshot stalls + +Branch: `nd/fix-video` · final code commit `4c7073bdc` · PR [#6983](https://github.com/simplex-chat/simplex-chat/pull/6983). + +## 1. Problem statement + +On Desktop with several videos in a chat, clicking the play button on the second (or any subsequent) video does nothing. The first video plays normally; later ones present a play button that responds to the click but never starts playback. No error dialog appears in the UI. `stderr` shows libvlc and libavcodec noise: + +``` +[h264 @ 0x...] get_buffer() failed +[h264 @ 0x...] thread_get_buffer() failed +[h264 @ 0x...] decode_slice_header error +[h264 @ 0x...] no frame! +... main video output error: Failed to grab a snapshot +``` + +The bug appeared after PR [#6924](https://github.com/simplex-chat/simplex-chat/pull/6924) (`ab2d03630`), which switched the preview helper player from the shared `vlcFactory` to a dedicated `vlcPreviewFactory` with `--avcodec-hw=none`. Hardware-accelerated decoding had previously masked the underlying fragility. Scope: Desktop only. + +## 2. Root cause + +Two compounding defects in `VideoPlayer.desktop.kt`, surfaced by `#6924`: + +### 2a. Synchronous `snapshots().get()` blocks the shared `playerThread` indefinitely + +`getBitmapFromVideo` ran inside `withContext(playerThread.asCoroutineDispatcher())` — the same single-thread executor used by `play()`/`stop()` for playback. Its loop polls vlcj's snapshot API: + +```kotlin +while (snap == null && start + 1500 > System.currentTimeMillis()) { + snap = player.snapshots()?.get() + delay(50) +} +``` + +The 1500 ms wall-clock guard only fires *between* calls. `player.snapshots()?.get()` is a synchronous JNI call that, when libvlc cannot produce a frame, waits indefinitely. While it blocks, `playerThread` is held: every queued `playerThread.execute { videoPlaying.value = start(...) }` from a subsequent `play()` click sits in the queue and never runs. + +This was confirmed by instrumented printlns: after the first video's preview entered the snapshot loop, the second video's `play()` body executed (UI thread println fires), but its lambda submitted to `playerThread.execute` produced no `lambda started` print — because `playerThread` was stuck inside the JNI call. + +### 2b. Helper-player pool reuse exhausts the software h264 buffer pool + +`getOrCreateHelperPlayer()` returns a `CallbackMediaPlayerComponent` from `helperPlayersPool`, recycling it across preview generations. With `vlcFactory` (hardware-accelerated by default), this was harmless — the GPU buffer pool was large with different lifecycle semantics. After `#6924` switched the helper to `vlcPreviewFactory` (`--avcodec-hw=none`), libavcodec frames from the previous run were not released cleanly across `stop` + `startPaused`, and the second decoder ran out of buffers (`get_buffer() failed`). The vout never produced a frame, which is the trigger for the hang in 2a. + +## 3. Solution summary + +`apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/VideoPlayer.desktop.kt` — single file, +8 / −6 lines. Helper-player pool is preserved as-is. + +1. **Replace the polling `snapshots().get()` loop with a `CallbackVideoSurface` capture wrapped in `withTimeoutOrNull`.** The existing `SkiaBitmapVideoSurface` (already used for full-screen playback rendering) is attached to the helper player before `media().startPaused(...)`. Its `RenderCallback.display()` runs as soon as libvlc decodes the first frame, populating `surface.bitmap`. `getBitmapFromVideo` polls `surface.bitmap.value` from inside `withTimeoutOrNull(1500L) { ... }`; the wait is now structurally bounded — the synchronous JNI call is gone. Frame is converted to `BufferedImage` via `ImageBitmap.toAwtImage()` for the existing orientation-correction code path. This addresses 2a directly: a helper that fails to decode (2b) no longer holds the dispatcher. + +2. **Move preview generation to a dedicated executor.** A new `previewThread = Executors.newSingleThreadExecutor()` runs `getBitmapFromVideo`. Defense in depth: even if 1500 ms of preview work overlaps with a play click, playback's `playerThread` is free to service it. + +The pool is intentionally not touched. Removing it loses the factory-warmup amortization across distinct video URIs without addressing the actual hang (which is in the synchronous snapshot API, not in player reuse). + +## 4. Alternatives considered (and rejected) + +- **Drop the helper-player pool (initial attempt, commit `4a964c661`).** Replaces every preview's helper with a fresh `CallbackMediaPlayerComponent`. Fixes the symptom by sidestepping pool reuse, but costs the factory-warmup benefit and does not address the underlying blocking JNI call — a single corrupt video could still hang preview generation indefinitely (just on a fresh helper). Superseded by the surface-capture approach. +- **Keep the pool, reset the helper between uses.** vlcj has no clean reset API; would require `media().release()` + manual re-attach. More code, fragile, doesn't address 2a. +- **Wrap `snapshots().get()` in a coroutine timeout on a separate IO thread.** `withTimeoutOrNull` cannot cancel a blocked JNI call; the IO thread leaks until libvlc returns (which may be never). +- **Revert PR #6924.** Restores the masking effect of hardware-accelerated decoding but reintroduces whatever the PR was guarding against, and leaves both 2a and 2b in place. From 12fbf61f326e15ee643c72b3202bbcd6a758a07f Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Tue, 26 May 2026 09:03:41 +0000 Subject: [PATCH 06/20] core, ui: require update for public groups (#7009) --- apps/ios/Shared/Model/AppAPITypes.swift | 1 + .../Shared/Views/NewChat/NewChatView.swift | 27 ++++++++++++++++ .../chat/simplex/common/model/SimpleXAPI.kt | 1 + .../common/views/newchat/ConnectPlan.kt | 27 ++++++++++++++++ .../commonMain/resources/MR/base/strings.xml | 2 ++ .../src/Directory/Service.hs | 1 + bots/api/TYPES.md | 4 +++ .../types/typescript/src/types.ts | 7 ++++ .../src/simplex_chat/types/_types.py | 7 +++- src/Simplex/Chat/Controller.hs | 2 ++ src/Simplex/Chat/Library/Commands.hs | 32 +++++++++++-------- src/Simplex/Chat/View.hs | 1 + 12 files changed, 97 insertions(+), 15 deletions(-) diff --git a/apps/ios/Shared/Model/AppAPITypes.swift b/apps/ios/Shared/Model/AppAPITypes.swift index b459f36c9d..a5a56174b1 100644 --- a/apps/ios/Shared/Model/AppAPITypes.swift +++ b/apps/ios/Shared/Model/AppAPITypes.swift @@ -1404,6 +1404,7 @@ enum GroupLinkPlan: Decodable, Hashable { case connectingProhibit(groupInfo_: GroupInfo?) case known(groupInfo: GroupInfo) case noRelays(groupSLinkData_: GroupShortLinkData?) + case updateRequired(groupSLinkData_: GroupShortLinkData?) } struct ChatTagData: Encodable { diff --git a/apps/ios/Shared/Views/NewChat/NewChatView.swift b/apps/ios/Shared/Views/NewChat/NewChatView.swift index 9bcc326a66..f73a2f1503 100644 --- a/apps/ios/Shared/Views/NewChat/NewChatView.swift +++ b/apps/ios/Shared/Views/NewChat/NewChatView.swift @@ -1559,6 +1559,33 @@ func planAndConnect( cleanup?() } } + case let .updateRequired(groupSLinkData_): + logger.debug("planAndConnect, .groupLink, .updateRequired") + await MainActor.run { + if let groupSLinkData = groupSLinkData_ { + showOpenChatAlert( + profileName: groupSLinkData.groupProfile.displayName, + profileFullName: groupSLinkData.groupProfile.fullName, + profileImage: + ProfileImage( + imageStr: groupSLinkData.groupProfile.image, + iconName: "person.2.circle.fill", + size: alertProfileImageSize + ), + theme: theme, + subtitle: NSLocalizedString("This group requires a newer version of the app. Please update the app to join.", comment: "alert subtitle"), + cancelTitle: NSLocalizedString("OK", comment: "alert button"), + confirmTitle: nil, + onCancel: { cleanup?() } + ) + } else { + showAlert( + NSLocalizedString("App update required", comment: "alert title"), + message: NSLocalizedString("This group requires a newer version of the app. Please update the app to join.", comment: "alert message") + ) + cleanup?() + } + } } case let .error(chatError): logger.debug("planAndConnect, .error \(chatErrorString(chatError))") diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index a31dc145a3..8f7cce21c4 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -6993,6 +6993,7 @@ sealed class GroupLinkPlan { @Serializable @SerialName("connectingProhibit") class ConnectingProhibit(val groupInfo_: GroupInfo? = null): GroupLinkPlan() @Serializable @SerialName("known") class Known(val groupInfo: GroupInfo): GroupLinkPlan() @Serializable @SerialName("noRelays") class NoRelays(val groupSLinkData_: GroupShortLinkData? = null): GroupLinkPlan() + @Serializable @SerialName("updateRequired") class UpdateRequired(val groupSLinkData_: GroupShortLinkData? = null): GroupLinkPlan() } abstract class TerminalItem { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ConnectPlan.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ConnectPlan.kt index cafad97574..87cf01403c 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ConnectPlan.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ConnectPlan.kt @@ -316,6 +316,33 @@ private suspend fun planAndConnectTask( cleanup() } } + is GroupLinkPlan.UpdateRequired -> { + Log.d(TAG, "planAndConnect, .GroupLink, .UpdateRequired") + val groupSLinkData = connectionPlan.groupLinkPlan.groupSLinkData_ + if (groupSLinkData != null) { + AlertManager.privacySensitive.showOpenChatAlert( + profileName = groupSLinkData.groupProfile.displayName, + profileFullName = groupSLinkData.groupProfile.fullName, + profileImage = { + ProfileImage( + size = alertProfileImageSize, + image = groupSLinkData.groupProfile.image, + icon = MR.images.ic_supervised_user_circle_filled + ) + }, + subtitle = generalGetString(MR.strings.group_link_requires_newer_version), + confirmText = null, + dismissText = generalGetString(MR.strings.ok), + onDismiss = { cleanup() } + ) + } else { + AlertManager.privacySensitive.showAlertMsg( + generalGetString(MR.strings.app_update_required), + generalGetString(MR.strings.group_link_requires_newer_version) + ) + cleanup() + } + } } is ConnectionPlan.Error -> { Log.d(TAG, "planAndConnect, error ${connectionPlan.chatError}") diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml index 375edecd44..5a0bc77ccf 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -196,6 +196,8 @@ This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. Channel temporarily unavailable Channel has no active relays. Please try to join later. + App update required + This group requires a newer version of the app. Please update the app to join. Connection error (AUTH) 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. Connection blocked diff --git a/apps/simplex-directory-service/src/Directory/Service.hs b/apps/simplex-directory-service/src/Directory/Service.hs index 6e414ef011..577cc99752 100644 --- a/apps/simplex-directory-service/src/Directory/Service.hs +++ b/apps/simplex-directory-service/src/Directory/Service.hs @@ -970,6 +970,7 @@ directoryServiceEvent st opts@DirectoryOpts {adminUsers, superUsers, serviceName GLPConnectingProhibit _ -> sendMessage cc ct $ "Already connecting to this " <> gt <> "." GLPConnectingConfirmReconnect -> sendMessage cc ct $ "Already connecting to this " <> gt <> "." GLPNoRelays _ -> sendMessage cc ct $ T.toTitle gt <> " has no active relays. Please try again later." + GLPUpdateRequired _ -> sendMessage cc ct $ T.toTitle gt <> " requires a newer version." GLPOwnLink _ -> sendMessage cc ct "Unexpected error. Please report it to directory admins." _ -> sendMessage cc ct "Unexpected error. Please report it to directory admins." diff --git a/bots/api/TYPES.md b/bots/api/TYPES.md index b4edb9bd22..3db6dcbcfc 100644 --- a/bots/api/TYPES.md +++ b/bots/api/TYPES.md @@ -2331,6 +2331,10 @@ NoRelays: - type: "noRelays" - groupSLinkData_: [GroupShortLinkData](#groupshortlinkdata)? +UpdateRequired: +- type: "updateRequired" +- groupSLinkData_: [GroupShortLinkData](#groupshortlinkdata)? + --- diff --git a/packages/simplex-chat-client/types/typescript/src/types.ts b/packages/simplex-chat-client/types/typescript/src/types.ts index 7e618e05c8..44949611b2 100644 --- a/packages/simplex-chat-client/types/typescript/src/types.ts +++ b/packages/simplex-chat-client/types/typescript/src/types.ts @@ -2602,6 +2602,7 @@ export type GroupLinkPlan = | GroupLinkPlan.ConnectingProhibit | GroupLinkPlan.Known | GroupLinkPlan.NoRelays + | GroupLinkPlan.UpdateRequired export namespace GroupLinkPlan { export type Tag = @@ -2611,6 +2612,7 @@ export namespace GroupLinkPlan { | "connectingProhibit" | "known" | "noRelays" + | "updateRequired" interface Interface { type: Tag @@ -2649,6 +2651,11 @@ export namespace GroupLinkPlan { type: "noRelays" groupSLinkData_?: GroupShortLinkData } + + export interface UpdateRequired extends Interface { + type: "updateRequired" + groupSLinkData_?: GroupShortLinkData + } } export interface GroupMember { diff --git a/packages/simplex-chat-python/src/simplex_chat/types/_types.py b/packages/simplex-chat-python/src/simplex_chat/types/_types.py index b2fc00a44c..409a187245 100644 --- a/packages/simplex-chat-python/src/simplex_chat/types/_types.py +++ b/packages/simplex-chat-python/src/simplex_chat/types/_types.py @@ -1854,6 +1854,10 @@ class GroupLinkPlan_noRelays(TypedDict): type: Literal["noRelays"] groupSLinkData_: NotRequired["GroupShortLinkData"] +class GroupLinkPlan_updateRequired(TypedDict): + type: Literal["updateRequired"] + groupSLinkData_: NotRequired["GroupShortLinkData"] + GroupLinkPlan = ( GroupLinkPlan_ok | GroupLinkPlan_ownLink @@ -1861,9 +1865,10 @@ GroupLinkPlan = ( | GroupLinkPlan_connectingProhibit | GroupLinkPlan_known | GroupLinkPlan_noRelays + | GroupLinkPlan_updateRequired ) -GroupLinkPlan_Tag = Literal["ok", "ownLink", "connectingConfirmReconnect", "connectingProhibit", "known", "noRelays"] +GroupLinkPlan_Tag = Literal["ok", "ownLink", "connectingConfirmReconnect", "connectingProhibit", "known", "noRelays", "updateRequired"] class GroupMember(TypedDict): groupMemberId: int # int64 diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index fa2d0af009..fe5b67f041 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -1051,6 +1051,7 @@ data GroupLinkPlan | GLPConnectingProhibit {groupInfo_ :: Maybe GroupInfo} | GLPKnown {groupInfo :: GroupInfo, groupUpdated :: BoolDef, ownerVerification :: Maybe OwnerVerification, linkOwners :: ListDef GroupLinkOwner} | GLPNoRelays {groupSLinkData_ :: Maybe GroupShortLinkData} + | GLPUpdateRequired {groupSLinkData_ :: Maybe GroupShortLinkData} deriving (Show) data GroupLinkOwner = GroupLinkOwner @@ -1096,6 +1097,7 @@ connectionPlanProceed = \case GLPOwnLink _ -> True GLPConnectingConfirmReconnect -> True GLPNoRelays _ -> False + GLPUpdateRequired _ -> False _ -> False CPError _ -> True diff --git a/src/Simplex/Chat/Library/Commands.hs b/src/Simplex/Chat/Library/Commands.hs index bb31ee26a5..8d9d882366 100644 --- a/src/Simplex/Chat/Library/Commands.hs +++ b/src/Simplex/Chat/Library/Commands.hs @@ -4120,21 +4120,25 @@ processChatCommand vr nm = \case Nothing -> do (fd, cData@(ContactLinkData _ UserContactData {direct, owners, relays})) <- getShortLinkConnReq' nm user l' groupSLinkData_ <- liftIO $ decodeLinkUserData cData - if not direct && null relays - then pure (con (linkConnReq fd), CPGroupLink (GLPNoRelays groupSLinkData_)) - else do - let FixedLinkData {linkConnReq = cReq, linkEntityId, rootKey} = fd - linkInfo = GroupShortLinkInfo {direct, groupRelays = relays, publicGroupId = B64UrlByteString <$> linkEntityId} - let profilePGId = groupSLinkData_ >>= \GroupShortLinkData {groupProfile = GroupProfile {publicGroup}} -> - fmap (\PublicGroupProfile {publicGroupId} -> publicGroupId) publicGroup - case (B64UrlByteString <$> linkEntityId, profilePGId) of - (Just entityId, Just publicGroupId) | entityId == publicGroupId -> pure () - (Nothing, Nothing) -> pure () - _ -> throwChatError CEInvalidConnReq - let ov = verifyLinkOwner rootKey owners l' sig_ - plan <- groupJoinRequestPlan user cReq (Just linkInfo) groupSLinkData_ ov - pure (con cReq, plan) + if + | not direct && unsupportedGroupType groupSLinkData_ -> pure (con (linkConnReq fd), CPGroupLink (GLPUpdateRequired groupSLinkData_)) + | not direct && null relays -> pure (con (linkConnReq fd), CPGroupLink (GLPNoRelays groupSLinkData_)) + | otherwise -> do + let FixedLinkData {linkConnReq = cReq, linkEntityId, rootKey} = fd + linkInfo = GroupShortLinkInfo {direct, groupRelays = relays, publicGroupId = B64UrlByteString <$> linkEntityId} + let profilePGId = groupSLinkData_ >>= \GroupShortLinkData {groupProfile = GroupProfile {publicGroup}} -> + fmap (\PublicGroupProfile {publicGroupId} -> publicGroupId) publicGroup + case (B64UrlByteString <$> linkEntityId, profilePGId) of + (Just entityId, Just publicGroupId) | entityId == publicGroupId -> pure () + (Nothing, Nothing) -> pure () + _ -> throwChatError CEInvalidConnReq + let ov = verifyLinkOwner rootKey owners l' sig_ + plan <- groupJoinRequestPlan user cReq (Just linkInfo) groupSLinkData_ ov + pure (con cReq, plan) where + unsupportedGroupType = \case + Just GroupShortLinkData {groupProfile = GroupProfile {publicGroup = Just PublicGroupProfile {groupType}}} -> groupType /= GTChannel + _ -> False knownLinkPlans = withFastStore $ \db -> liftIO (getGroupInfoViaUserShortLink db vr user l') >>= \case Just (cReq, g) -> pure $ Just (con cReq, CPGroupLink (GLPOwnLink g)) diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 477850d4b0..838d15245a 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -2138,6 +2138,7 @@ viewConnectionPlan ChatConfig {logLevel, testView} _connLink = \case ] knownGroup prepared = grpOrBizLink g <> ": known " <> prepared <> grpOrBiz g <> " " <> ttyGroup' g GLPNoRelays _ -> [grpLink "channel has no active relays, please try to join later"] + GLPUpdateRequired _ -> [grpLink "this group requires a newer version of the app, please upgrade"] where connecting g = [grpOrBizLink g <> ": connecting to " <> grpOrBiz g <> " " <> ttyGroup' g] grpLink = ("group link: " <>) From 68abd805d44f9b940af94a97bdcec840c77aa0e8 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Thu, 28 May 2026 08:44:43 +0100 Subject: [PATCH 07/20] rfc: namespace (#7001) * rfc: namespace * update rfc * markdown for names * record type, app "upgrade" alerts * update api types * rfc: change namespace syntax - now it is the usual namespace * update bot types * move types to simplexmq * core: refactore markdown * update simplexmq * better names * new names * update nix content hashes * fix * change valid name function * update simplexq, update valid name conditions * fixes Co-authored-by: simplex-chat-agent[bot] <287173099+simplex-chat-agent[bot]@users.noreply.github.com> * update simplexmq * fix localization * simpler * refactor * refactor * fix --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> Co-authored-by: simplex-chat-agent[bot] <287173099+simplex-chat-agent[bot]@users.noreply.github.com> --- .../Views/Chat/ChatItem/MsgContentView.swift | 11 +- .../Shared/Views/ChatList/ChatListView.swift | 17 +- .../Views/NewChat/NewChatMenuButton.swift | 17 +- .../Shared/Views/NewChat/NewChatView.swift | 69 +++-- apps/ios/SimpleXChat/ChatTypes.swift | 19 ++ .../chat/simplex/common/model/ChatModel.kt | 23 ++ .../common/views/chat/item/TextItemView.kt | 19 +- .../common/views/chatlist/ChatListView.kt | 42 ++- .../common/views/newchat/ConnectPlan.kt | 32 ++- .../common/views/newchat/NewChatSheet.kt | 48 ++-- .../common/views/newchat/NewChatView.kt | 45 +++- .../commonMain/resources/MR/base/strings.xml | 5 + bots/api/TYPES.md | 37 +++ bots/src/API/Docs/Types.hs | 6 + cabal.project | 2 +- docs/rfcs/2026-05-21-public-namespaces.md | 246 ++++++++++++++++++ .../types/typescript/src/types.ts | 25 ++ .../src/simplex_chat/types/_types.py | 17 +- scripts/nix/sha256map.nix | 2 +- src/Simplex/Chat/Library/Commands.hs | 28 +- src/Simplex/Chat/Markdown.hs | 42 ++- .../SQLite/Migrations/chat_query_plans.txt | 13 + tests/MarkdownTests.hs | 45 +++- tests/ValidNames.hs | 49 ++-- 24 files changed, 703 insertions(+), 156 deletions(-) create mode 100644 docs/rfcs/2026-05-21-public-namespaces.md diff --git a/apps/ios/Shared/Views/Chat/ChatItem/MsgContentView.swift b/apps/ios/Shared/Views/Chat/ChatItem/MsgContentView.swift index 2f4338c0af..9aaff57cc5 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/MsgContentView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/MsgContentView.swift @@ -208,7 +208,9 @@ private func handleTextTaps( var browser: Bool = false s.enumerateAttributes(in: NSRange(location: 0, length: s.length)) { attrs, range, stop in if index >= range.location && index < range.location + range.length { - if let url = attrs[linkAttrKey] as? String { + if let nameInfo = attrs[nameAttrKey] as? SimplexNameInfo { + showUnsupportedNameAlert(nameInfo) + } else if let url = attrs[linkAttrKey] as? String { linkURL = url browser = attrs[webLinkAttrKey] != nil } else if let showSecrets, let i = attrs[secretAttrKey] as? Int { @@ -251,6 +253,7 @@ private let webLinkAttrKey = NSAttributedString.Key("chat.simplex.app.webLink") private let secretAttrKey = NSAttributedString.Key("chat.simplex.app.secret") private let commandAttrKey = NSAttributedString.Key("chat.simplex.app.command") +private let nameAttrKey = NSAttributedString.Key("chat.simplex.app.name") typealias MsgTextResult = (string: NSMutableAttributedString, hasSecrets: Bool, handleTaps: Bool) @@ -424,6 +427,12 @@ func messageText( t = mentionText(memberName) } } + case let .simplexName(nameInfo): + attrs = linkAttrs() + if !preview { + attrs[nameAttrKey] = nameInfo + handleTaps = true + } case .email: attrs = linkAttrs() if !preview { diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index dc4971aafa..d90149c7dd 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -675,17 +675,18 @@ struct ChatListSearchBar: View { if ignoreSearchTextChange { ignoreSearchTextChange = false } else { - if let link = strHasSingleSimplexLink(t.trimmingCharacters(in: .whitespaces)) { // if SimpleX link is pasted, show connection dialogue + switch strConnectTarget(t.trimmingCharacters(in: .whitespaces)) { + case let .link(text, _, linkText): searchFocussed = false - if case let .simplexLink(_, linkType, _, smpHosts) = link.format { - ignoreSearchTextChange = true - searchText = simplexLinkText(linkType, smpHosts) - } + ignoreSearchTextChange = true + searchText = linkText searchShowingSimplexLink = true searchChatFilteredBySimplexLink = nil - connect(link.text) - } else { - if t != "" { // if some other text is pasted, enter search mode + connect(text) + case let .name(nameInfo): + showUnsupportedNameAlert(nameInfo) + case .none: + if t != "" { searchFocussed = true } else { ConnectProgressManager.shared.cancelConnectProgress() diff --git a/apps/ios/Shared/Views/NewChat/NewChatMenuButton.swift b/apps/ios/Shared/Views/NewChat/NewChatMenuButton.swift index 177f8761f4..f99b03086e 100644 --- a/apps/ios/Shared/Views/NewChat/NewChatMenuButton.swift +++ b/apps/ios/Shared/Views/NewChat/NewChatMenuButton.swift @@ -381,17 +381,18 @@ struct ContactsListSearchBar: View { if ignoreSearchTextChange { ignoreSearchTextChange = false } else { - if let link = strHasSingleSimplexLink(t.trimmingCharacters(in: .whitespaces)) { // if SimpleX link is pasted, show connection dialogue + switch strConnectTarget(t.trimmingCharacters(in: .whitespaces)) { + case let .link(text, _, linkText): searchFocussed = false - if case let .simplexLink(_, linkType, _, smpHosts) = link.format { - ignoreSearchTextChange = true - searchText = simplexLinkText(linkType, smpHosts) - } + ignoreSearchTextChange = true + searchText = linkText searchShowingSimplexLink = true searchChatFilteredBySimplexLink = nil - connect(link.text) - } else { - if t != "" { // if some other text is pasted, enter search mode + connect(text) + case let .name(nameInfo): + showUnsupportedNameAlert(nameInfo) + case .none: + if t != "" { searchFocussed = true } else { connectProgressManager.cancelConnectProgress() diff --git a/apps/ios/Shared/Views/NewChat/NewChatView.swift b/apps/ios/Shared/Views/NewChat/NewChatView.swift index f73a2f1503..4a7e50d7d2 100644 --- a/apps/ios/Shared/Views/NewChat/NewChatView.swift +++ b/apps/ios/Shared/Views/NewChat/NewChatView.swift @@ -663,14 +663,13 @@ private struct ConnectView: View { ZStack(alignment: .trailing) { Button { if let str = UIPasteboard.general.string { - if let link = strHasSingleSimplexLink(str.trimmingCharacters(in: .whitespaces)) { - pastedLink = link.text - // It would be good to hide it, but right now it is not clear how to release camera in CodeScanner - // https://github.com/twostraws/CodeScanner/issues/121 - // No known tricks worked (changing view ID, wrapping it in another view, etc.) - // showQRCodeScanner = false + switch strConnectTarget(str.trimmingCharacters(in: .whitespaces)) { + case let .link(text, _, _): + pastedLink = text connect(pastedLink) - } else { + case let .name(nameInfo): + showUnsupportedNameAlert(nameInfo) + case .none: alert = .newChatSomeAlert(alert: SomeAlert( alert: mkAlert(title: "Invalid link", message: "The text you pasted is not a SimpleX link."), id: "pasteLinkView: code is not a SimpleX link" @@ -866,16 +865,36 @@ func strIsSimplexLink(_ str: String) -> Bool { } } -func strHasSingleSimplexLink(_ str: String) -> FormattedText? { - if let parsedMd = parseSimpleXMarkdown(str) { - let parsedLinks = parsedMd.filter({ $0.format?.isSimplexLink ?? false }) - if parsedLinks.count == 1 { - return parsedLinks[0] - } else { - return nil - } +enum ConnectTarget { + case link(text: String, linkType: SimplexLinkType, linkText: String) + case name(SimplexNameInfo) +} + +func strConnectTarget(_ str: String) -> ConnectTarget? { + let parsedMd = parseSimpleXMarkdown(str) + let links = parsedMd?.filter { $0.format?.isSimplexLink ?? false } ?? [] + return if links.count == 1, case let .simplexLink(_, linkType, _, smpHosts) = links[0].format { + .link(text: links[0].text, linkType: linkType, linkText: simplexLinkText(linkType, smpHosts)) + } else if links.isEmpty, + case let .simplexName(nameInfo) = parsedMd?.first(where: { if case .simplexName = $0.format { true } else { false } })?.format { + .name(nameInfo) } else { - return nil + nil + } +} + +func showUnsupportedNameAlert(_ nameInfo: SimplexNameInfo) { + let upgrade = " " + NSLocalizedString("Please upgrade the app.", comment: "alert message") + if nameInfo.nameType == .contact { + showAlert( + NSLocalizedString("Unsupported contact name", comment: "alert title"), + message: NSLocalizedString("Connecting via contact name requires a newer app version.", comment: "alert message") + upgrade + ) + } else { + showAlert( + NSLocalizedString("Unsupported channel name", comment: "alert title"), + message: NSLocalizedString("Connecting via channel name requires a newer app version.", comment: "alert message") + upgrade + ) } } @@ -1295,13 +1314,21 @@ func planAndConnect( filterKnownContact: ((Contact) -> Void)? = nil, filterKnownGroup: ((GroupInfo) -> Void)? = nil ) { - if case .simplexLink(_, .relay, _, _) = strHasSingleSimplexLink(shortOrFullLink)?.format { - showAlert( - NSLocalizedString("Relay address", comment: "alert title"), - message: NSLocalizedString("This is a chat relay address, it cannot be used to connect.", comment: "alert message") - ) + switch strConnectTarget(shortOrFullLink) { + case let .name(nameInfo): + showUnsupportedNameAlert(nameInfo) cleanup?() return + case let .link(_, linkType, _): + if linkType == .relay { + showAlert( + NSLocalizedString("Relay address", comment: "alert title"), + message: NSLocalizedString("This is a chat relay address, it cannot be used to connect.", comment: "alert message") + ) + cleanup?() + return + } + case .none: break } ConnectProgressManager.shared.cancelConnectProgress() let inProgress = BoxedValue(true) diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index 594f90c4e4..7265038f38 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -5104,6 +5104,7 @@ public enum Format: Decodable, Equatable, Hashable { case uri case hyperLink(showText: String?, linkUri: String) case simplexLink(showText: String?, linkType: SimplexLinkType, simplexUri: String, smpHosts: [String]) + case simplexName(nameInfo: SimplexNameInfo) case command(commandStr: String) case mention(memberName: String) case email @@ -5138,6 +5139,24 @@ public enum SimplexLinkType: String, Decodable, Hashable { } } +public struct SimplexNameInfo: Decodable, Equatable, Hashable { + public var nameType: SimplexNameType + public var nameTLD: SimplexTLD + public var domain: String + public var subDomain: [String] +} + +public enum SimplexTLD: String, Decodable, Hashable { + case simplex + case testing + case web +} + +public enum SimplexNameType: String, Decodable, Hashable { + case publicGroup + case contact +} + public enum FormatColor: String, Decodable, Hashable { case red = "red" case green = "green" diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 3c9ece9dce..aa4b677b8a 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -4680,6 +4680,7 @@ sealed class Format { val viaHosts: String get() = "(${String.format(generalGetString(MR.strings.simplex_link_connection), smpHosts.firstOrNull() ?: "?")})" } + @Serializable @SerialName("simplexName") class SimplexName(val nameInfo: SimplexNameInfo): Format() @Serializable @SerialName("command") class Command(val commandStr: String): Format() @Serializable @SerialName("mention") class Mention(val memberName: String): Format() @Serializable @SerialName("email") class Email: Format() @@ -4697,6 +4698,7 @@ sealed class Format { is Uri -> linkStyle is HyperLink -> linkStyle is SimplexLink -> linkStyle + is SimplexName -> linkStyle is Command -> SpanStyle(color = MaterialTheme.colors.primary, fontFamily = FontFamily.Monospace) is Mention -> SpanStyle(fontWeight = FontWeight.Medium) is Email -> linkStyle @@ -4728,6 +4730,27 @@ enum class SimplexLinkType(val linkType: String) { }) } +@Serializable +data class SimplexNameInfo( + val nameType: SimplexNameType, + val nameTLD: SimplexTLD, + val domain: String, + val subDomain: List +) + +@Serializable +enum class SimplexTLD { + @SerialName("simplex") simplex, + @SerialName("testing") testing, + @SerialName("web") web +} + +@Serializable +enum class SimplexNameType { + @SerialName("publicGroup") publicGroup, + @SerialName("contact") contact +} + @Serializable enum class FormatColor(val color: String) { red("red"), diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/TextItemView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/TextItemView.kt index 3358a23e1e..c9f7d96f39 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/TextItemView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/TextItemView.kt @@ -281,6 +281,13 @@ fun MarkdownText ( } } } + is Format.SimplexName -> { + hasLinks = true + val ftStyle = Format.linkStyle + withAnnotation(tag = "SIMPLEX_NAME", annotation = i.toString()) { + withStyle(ftStyle) { append(ft.text) } + } + } is Format.Email -> { hasLinks = true val ftStyle = Format.linkStyle @@ -329,6 +336,16 @@ fun MarkdownText ( withAnnotation("WEB_URL") { a -> openBrowserAlert(a.item, uriHandler) } withAnnotation("OTHER_URL") { a -> safeOpenUri(a.item, uriHandler) } withAnnotation("SIMPLEX_URL") { a -> uriHandler.openVerifiedSimplexUri(a.item) } + withAnnotation("SIMPLEX_NAME") { a -> + val idx = a.item.toIntOrNull() + val nameInfo = (idx?.let { formattedText.getOrNull(it) }?.format as? Format.SimplexName)?.nameInfo + val (title, msg) = if (nameInfo?.nameType == SimplexNameType.contact) { + generalGetString(MR.strings.unsupported_contact_name) to generalGetString(MR.strings.contact_name_requires_newer_app_version) + } else { + generalGetString(MR.strings.unsupported_channel_name) to generalGetString(MR.strings.channel_name_requires_newer_app_version) + } + AlertManager.shared.showAlertMsg(title, "$msg ${generalGetString(MR.strings.please_upgrade_the_app)}") + } } if (hasSecrets) { withAnnotation("SECRET") { a -> @@ -343,7 +360,7 @@ fun MarkdownText ( onHover = { offset -> val hasAnnotation: (String) -> Boolean = { tag -> annotatedText.hasStringAnnotations(tag, start = offset, end = offset) } icon.value = - if (hasAnnotation("WEB_URL") || hasAnnotation("SIMPLEX_URL") || hasAnnotation("OTHER_URL") || hasAnnotation("SECRET") || hasAnnotation("COMMAND")) { + if (hasAnnotation("WEB_URL") || hasAnnotation("SIMPLEX_URL") || hasAnnotation("OTHER_URL") || hasAnnotation("SIMPLEX_NAME") || hasAnnotation("SECRET") || hasAnnotation("COMMAND")) { PointerIcon.Hand } else { PointerIcon.Text diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt index 01dcd021f7..e9dec64634 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt @@ -791,31 +791,29 @@ private fun ChatListSearchBar(listState: LazyListState, searchText: MutableState snapshotFlow { searchText.value.text } .distinctUntilChanged() .collect { - val link = strHasSingleSimplexLink(it.trim()) - if (link != null) { - // if SimpleX link is pasted, show connection dialogue - hideKeyboard(view) - if (link.format is Format.SimplexLink) { - val linkText = link.format.simplexLinkText - searchText.value = searchText.value.copy(linkText, selection = TextRange.Zero) + when (val target = strConnectTarget(it.trim())) { + is ConnectTarget.Link -> { + hideKeyboard(view) + searchText.value = searchText.value.copy(target.linkText, selection = TextRange.Zero) + searchShowingSimplexLink.value = true + searchChatFilteredBySimplexLink.value = null + connect(target.text, searchChatFilteredBySimplexLink) { searchText.value = TextFieldValue() } } - searchShowingSimplexLink.value = true - searchChatFilteredBySimplexLink.value = null - connect(link.text, searchChatFilteredBySimplexLink) { searchText.value = TextFieldValue() } - } else if (!searchShowingSimplexLink.value || it.isEmpty()) { - if (it.isNotEmpty()) { - // if some other text is pasted, enter search mode - focusRequester.requestFocus() - } else { - if (!chatModel.appOpenUrlConnecting.value) { - connectProgressManager.cancelConnectProgress() - } - if (listState.layoutInfo.totalItemsCount > 0) { - listState.scrollToItem(0) + is ConnectTarget.Name -> showUnsupportedNameAlert(target.nameInfo) + null -> if (!searchShowingSimplexLink.value || it.isEmpty()) { + if (it.isNotEmpty()) { + focusRequester.requestFocus() + } else { + if (!chatModel.appOpenUrlConnecting.value) { + connectProgressManager.cancelConnectProgress() + } + if (listState.layoutInfo.totalItemsCount > 0) { + listState.scrollToItem(0) + } } + searchShowingSimplexLink.value = false + searchChatFilteredBySimplexLink.value = null } - searchShowingSimplexLink.value = false - searchChatFilteredBySimplexLink.value = null } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ConnectPlan.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ConnectPlan.kt index 87cf01403c..9fd5dd5b4a 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ConnectPlan.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ConnectPlan.kt @@ -30,14 +30,23 @@ suspend fun planAndConnect( filterKnownContact: ((Contact) -> Unit)? = null, filterKnownGroup: ((GroupInfo) -> Unit)? = null, ): CompletableDeferred { - val link = strHasSingleSimplexLink(shortOrFullLink.trim()) - if (link?.format is Format.SimplexLink && (link.format as Format.SimplexLink).linkType == SimplexLinkType.relay) { - AlertManager.privacySensitive.showAlertMsg( - generalGetString(MR.strings.relay_address_alert_title), - generalGetString(MR.strings.relay_address_alert_message), - ) - cleanup?.invoke() - return CompletableDeferred(false) + when (val target = strConnectTarget(shortOrFullLink.trim())) { + is ConnectTarget.Name -> { + showUnsupportedNameAlert(target.nameInfo) + cleanup?.invoke() + return CompletableDeferred(false) + } + is ConnectTarget.Link -> { + if (target.linkType == SimplexLinkType.relay) { + AlertManager.privacySensitive.showAlertMsg( + generalGetString(MR.strings.relay_address_alert_title), + generalGetString(MR.strings.relay_address_alert_message), + ) + cleanup?.invoke() + return CompletableDeferred(false) + } + } + null -> {} } connectProgressManager.cancelConnectProgress() val inProgress = mutableStateOf(true) @@ -73,11 +82,8 @@ private suspend fun planAndConnectTask( if (!inProgress.value) { return completable } if (result != null) { val (connectionLink, connectionPlan) = result - val link = strHasSingleSimplexLink(shortOrFullLink.trim()) - val linkText = if (link?.format is Format.SimplexLink) - "

${link.format.simplexLinkText}" - else - "" + val target = strConnectTarget(shortOrFullLink.trim()) + val linkText = if (target is ConnectTarget.Link) "

${target.linkText}" else "" when (connectionPlan) { is ConnectionPlan.InvitationLink -> when (connectionPlan.invitationLinkPlan) { is InvitationLinkPlan.Ok -> diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt index 1eceaf4158..6f64fe5221 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt @@ -523,34 +523,32 @@ private fun ContactsSearchBar( snapshotFlow { searchText.value.text } .distinctUntilChanged() .collect { - val link = strHasSingleSimplexLink(it.trim()) - if (link != null) { - // if SimpleX link is pasted, show connection dialogue - hideKeyboard(view) - if (link.format is Format.SimplexLink) { - val linkText = link.format.simplexLinkText - searchText.value = searchText.value.copy(linkText, selection = TextRange.Zero) + when (val target = strConnectTarget(it.trim())) { + is ConnectTarget.Link -> { + hideKeyboard(view) + searchText.value = searchText.value.copy(target.linkText, selection = TextRange.Zero) + searchShowingSimplexLink.value = true + searchChatFilteredBySimplexLink.value = null + connect( + link = target.text, + searchChatFilteredBySimplexLink = searchChatFilteredBySimplexLink, + close = close, + cleanup = { searchText.value = TextFieldValue() } + ) } - searchShowingSimplexLink.value = true - searchChatFilteredBySimplexLink.value = null - connect( - link = link.text, - searchChatFilteredBySimplexLink = searchChatFilteredBySimplexLink, - close = close, - cleanup = { searchText.value = TextFieldValue() } - ) - } else if (!searchShowingSimplexLink.value || it.isEmpty()) { - if (it.isNotEmpty()) { - // if some other text is pasted, enter search mode - focusRequester.requestFocus() - } else { - connectProgressManager.cancelConnectProgress() - if (listState.layoutInfo.totalItemsCount > 0) { - listState.scrollToItem(0) + is ConnectTarget.Name -> showUnsupportedNameAlert(target.nameInfo) + null -> if (!searchShowingSimplexLink.value || it.isEmpty()) { + if (it.isNotEmpty()) { + focusRequester.requestFocus() + } else { + connectProgressManager.cancelConnectProgress() + if (listState.layoutInfo.totalItemsCount > 0) { + listState.scrollToItem(0) + } } + searchShowingSimplexLink.value = false + searchChatFilteredBySimplexLink.value = null } - searchShowingSimplexLink.value = false - searchChatFilteredBySimplexLink.value = null } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatView.kt index 72311cd7fe..b1ab8eb24e 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatView.kt @@ -671,13 +671,14 @@ private fun PasteLinkView(rhId: Long?, pastedLink: MutableState, showQRC val clipboard = LocalClipboardManager.current SectionItemView({ val str = clipboard.getText()?.text ?: return@SectionItemView - val link = strHasSingleSimplexLink(str.trim()) - if (link != null) { - pastedLink.value = link.text - showQRCodeScanner.value = false - withBGApi { connect(rhId, link.text, close) { pastedLink.value = "" } } - } else { - AlertManager.shared.showAlertMsg( + when (val target = strConnectTarget(str.trim())) { + is ConnectTarget.Link -> { + pastedLink.value = target.text + showQRCodeScanner.value = false + withBGApi { connect(rhId, target.text, close) { pastedLink.value = "" } } + } + is ConnectTarget.Name -> showUnsupportedNameAlert(target.nameInfo) + null -> AlertManager.shared.showAlertMsg( title = generalGetString(MR.strings.invalid_contact_link), text = generalGetString(MR.strings.the_text_you_pasted_is_not_a_link) ) @@ -819,12 +820,32 @@ fun strIsSimplexLink(str: String): Boolean { return parsedMd != null && parsedMd.size == 1 && parsedMd[0].format is Format.SimplexLink } -fun strHasSingleSimplexLink(str: String): FormattedText? { - val parsedMd = parseToMarkdown(str) ?: return null - val parsedLinks = parsedMd.filter { it.format?.isSimplexLink ?: false } - if (parsedLinks.size != 1) return null +sealed class ConnectTarget { + class Link(val text: String, val linkType: SimplexLinkType, val linkText: String) : ConnectTarget() + class Name(val nameInfo: SimplexNameInfo) : ConnectTarget() +} - return parsedLinks[0] +fun strConnectTarget(str: String): ConnectTarget? { + val parsedMd = parseToMarkdown(str) ?: return null + val links = parsedMd.filter { it.format?.isSimplexLink ?: false } + if (links.size == 1) { + val fmt = links[0].format as Format.SimplexLink + return ConnectTarget.Link(links[0].text, fmt.linkType, fmt.simplexLinkText) + } + if (links.isEmpty()) { + val nameInfo = parsedMd.firstNotNullOfOrNull { (it.format as? Format.SimplexName)?.nameInfo } + if (nameInfo != null) return ConnectTarget.Name(nameInfo) + } + return null +} + +fun showUnsupportedNameAlert(nameInfo: SimplexNameInfo) { + val (title, msg) = if (nameInfo.nameType == SimplexNameType.contact) { + generalGetString(MR.strings.unsupported_contact_name) to generalGetString(MR.strings.contact_name_requires_newer_app_version) + } else { + generalGetString(MR.strings.unsupported_channel_name) to generalGetString(MR.strings.channel_name_requires_newer_app_version) + } + AlertManager.shared.showAlertMsg(title, "$msg ${generalGetString(MR.strings.please_upgrade_the_app)}") } @Composable diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml index 5a0bc77ccf..cd0508f95a 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -194,6 +194,11 @@ Please check that you used the correct link or ask your contact to send you another one. Unsupported connection link This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link. + Unsupported channel name + Unsupported contact name + Connecting via channel name requires a newer app version. + Connecting via contact name requires a newer app version. + Please upgrade the app. Channel temporarily unavailable Channel has no active relays. Please try to join later. App update required diff --git a/bots/api/TYPES.md b/bots/api/TYPES.md index 3db6dcbcfc..af89c86411 100644 --- a/bots/api/TYPES.md +++ b/bots/api/TYPES.md @@ -165,6 +165,9 @@ This file is generated automatically. - [SecurityCode](#securitycode) - [SimplePreference](#simplepreference) - [SimplexLinkType](#simplexlinktype) +- [SimplexNameInfo](#simplexnameinfo) +- [SimplexNameType](#simplexnametype) +- [SimplexTLD](#simplextld) - [SndCIStatusProgress](#sndcistatusprogress) - [SndConnEvent](#sndconnevent) - [SndError](#snderror) @@ -2091,6 +2094,10 @@ SimplexLink: - simplexUri: string - smpHosts: [string] +SimplexName: +- type: "simplexName" +- nameInfo: [SimplexNameInfo](#simplexnameinfo) + Command: - type: "command" - commandStr: string @@ -3440,6 +3447,36 @@ A_QUEUE: - "relay" +--- + +## SimplexNameInfo + +**Record type**: +- nameType: [SimplexNameType](#simplexnametype) +- nameTLD: [SimplexTLD](#simplextld) +- domain: string +- subDomain: [string] + + +--- + +## SimplexNameType + +**Enum type**: +- "publicGroup" +- "contact" + + +--- + +## SimplexTLD + +**Enum type**: +- "simplex" +- "testing" +- "web" + + --- ## SndCIStatusProgress diff --git a/bots/src/API/Docs/Types.hs b/bots/src/API/Docs/Types.hs index be4a55835a..0f9e198cc1 100644 --- a/bots/src/API/Docs/Types.hs +++ b/bots/src/API/Docs/Types.hs @@ -345,6 +345,9 @@ chatTypesDocsData = (sti @SecurityCode, STRecord, "", [], "", ""), (sti @SimplePreference, STRecord, "", [], "", ""), (sti @SimplexLinkType, STEnum, "XL", [], "", ""), + (sti @SimplexNameInfo, STRecord, "", [], "", ""), + (sti @SimplexNameType, STEnum, "NT", [], "", ""), + (sti @SimplexTLD, STEnum, "TLD", [], "", ""), (sti @SMPAgentError, STUnion, "", [], "", ""), (sti @SndCIStatusProgress, STEnum, "SSP", [], "", ""), (sti @SndConnEvent, STUnion, "SCE", [], "", ""), @@ -558,6 +561,9 @@ deriving instance Generic RelayStatus deriving instance Generic ReportReason deriving instance Generic SecurityCode deriving instance Generic SimplexLinkType +deriving instance Generic SimplexNameInfo +deriving instance Generic SimplexNameType +deriving instance Generic SimplexTLD deriving instance Generic SMPAgentError deriving instance Generic SndCIStatusProgress deriving instance Generic SndConnEvent diff --git a/cabal.project b/cabal.project index 7ee797e621..728ab790c7 100644 --- a/cabal.project +++ b/cabal.project @@ -21,7 +21,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: f03cec7a58ed13a39a52886888c74bcefdb64479 + tag: e9265a7f7cb723d70b03e1b67af01f2666872a44 source-repository-package type: git diff --git a/docs/rfcs/2026-05-21-public-namespaces.md b/docs/rfcs/2026-05-21-public-namespaces.md new file mode 100644 index 0000000000..9f968945f3 --- /dev/null +++ b/docs/rfcs/2026-05-21-public-namespaces.md @@ -0,0 +1,246 @@ +# Public Namespaces for SimpleX Network + +## Motivation + +SimpleX has no user identifiers - users exchange invitation links out-of-band to connect. Short links help but are unmemorable. Public namespaces map human-readable names to SimpleX addresses. + +Names also solve censorship at two levels. A short link is controlled by one SMP router - that router can delete it. An on-chain name can't be deleted by any router. If the link is removed, the owner points the name to a new link on a different router. At the network level, links can be URL-filtered, but names resolve through SMP proxy chains - censoring a name requires controlling all resolvers the user can reach. + +DNS-based naming is vulnerable to domain seizure and requires WHOIS entries. Blockchains provide censorship-resistant globally unique names. + +## Product requirements + +### MVP + +- **Names**: TLD `.simplex` (e.g., `privacy.simplex`, `my-channel.simplex`). Subdomains: `support.acme.simplex`. In markdown, `.simplex` can be omitted: `#privacy` = `privacy.simplex`. +- **Name rules**: see [Name rules](#name-rules). +- **Two address types**: each name stores channel links (set) and contact links (set). Client uses the first; set provides forward-compatible redundancy. Either can be empty. +- **Optional metadata**: admin SimpleX address, admin email. +- **Registration**: commit-reveal to prevent frontrunning. Length-based ETH pricing. Annual renewal. Dutch auction on expiry. +- **Launch gating**: requires SimpleX test NFT. Up to 5 paid + 5 test names per holder. Test names free, auto-removed after 3 months, use `testing` namespace. +- **Reserved names**: common verticals (books, games, music, movies, news, etc.) reserved for community-operated channels managed by SimpleX Network Consortium. +- Only 7+ character names can be registered during "launch phase". +- **Resolution**: client queries two independent name servers (Ethereum light clients) via two SMP proxies. Agreement = trusted. Disagreement = warning. +- **Double resolution**: name -> short link (on-chain), short link -> connection data (existing protocol). +- **Verification**: if on-chain link matches profile address, name is verified. Manual "verify" button + optional auto-verify on profile open. +- **Markdown**: `#name` (`.simplex` implied), `#name.simplex` (explicit), `#name.testing` for test namespace. In CLI, `#` is local in group commands, global in `/c` and message bodies. +- **Search**: `#name.simplex` auto-resolves. Disable in "More privacy" settings. +- **Router role**: `names` added to `ServerRoles`. Not all routers support it. +- **Contract**: ENS fork on Ethereum mainnet. ETH payment. Upgradeable. + +### Post-MVP + +- **Multiple links**: redundant entries per name. Forward-compatible schema in MVP where practical. +- **Contact syntax**: `:name.simplex`, `:my-name.simplex`. Same namespace, different link type. MVP parser supports this syntax; resolution works; UI support is post-MVP. +- **Community Credits**: replace ETH for private registration. +- **Unicode expansion**: add scripts as user base grows. + +## Part 1: Blockchain contract + +### Overview + +ENS fork on Ethereum mainnet. Retains commit-reveal, pricing, expiry, Dutch auction. Compatible with ENS dApp. Upgradeable. + +ENS source: +- Contracts: https://github.com/ensdomains/ens-contracts +- dApp: https://github.com/ensdomains/ens-app-v3 +- JS library: https://github.com/ensdomains/ensjs + +### Contract state + +``` +Name record (ENS structure + SimpleX resolver fields): + owner : address + channelLinks : string[] + contactLinks : string[] + adminAddress : string -- optional + adminEmail : string -- optional + expiry : uint256 + isTest : bool + +Global state: + reservedNames : mapping(string => bool) + testNFT : address + registrationLimit : uint8 -- 5 + testLimit : uint8 -- 5 +``` + +There must be maps to track names by owner, but specific contract design should be based on ENS. + +### Name rules + +ENS normalization (ENSIP-15) with additional restrictions enforced in dApp (registration) and resolvers (resolution). Contract follows ENS as-is. + +Additional restrictions beyond ENSIP-15: +- No consecutive hyphens. +- No accented characters. Latin is `a-z` only (same as DNS LDH rule). +- Allowed scripts: Latin, Cyrillic, Arabic, Hebrew, Devanagari, Bengali, Thai, Greek, CJK, Hangul, Kana. Expandable as user base grows. + +### Registration flow + +1. NFT check +2. Limit check (5 paid / 5 test) +3. `commit(hash(name, owner, secret))` +4. Wait (min 1 minute) +5. `reveal(name, owner, secret)` + ETH (zero for test) +6. Validate: well-formed, not taken, not reserved, fee covered +7. Store record + +### Pricing + +Annual fees by name length: + +| Length | Fee | +|---|---| +| 7+ | base | +| 6 | 4x | +| 5 | 16x | +| 4 | 64x | +| 3 | 256x | + +Test names: free, expire after 3 months. + +### Renewal and expiry + +Annual renewal. Grace period, then Dutch auction decaying to base price. + +### Updates + +Owner can update links, admin address, admin email. Transfer follows ENS mechanics. + +### Reserved names + +List for community channels (e.g., `books`, `games`, `music`, `news`): +- Not registrable by users +- Revenue shared with network + +### Retained ENS features + +- **Resolver pattern**: registry maps name -> (owner, resolver). A SimpleX Resolver contract stores channel links, contact links, admin fields. Allows future extensibility without registry changes. +- **Multicoin address records**: BTC/ETH/XMR donation addresses per name. Subscribers see donation options from name resolution. +- **Text records**: generic key-value store for future metadata without contract upgrades. +- **Reverse resolution**: name lookup by address. Enables verification and discovery. +- **Subdomain registrar**: owner of `acme.simplex` can create `support.acme.simplex`, `sales.acme.simplex` without additional on-chain registration. + +### Removed ENS features + +- Avatar/image records. +- `.eth` TLD and ENS name imports. +- DNS name registration (DNSSEC imports). + +### Governance + +SimpleX Chat during testing and launch phases, migration to SimpleX Network Consortium. + +## Part 2: SMP protocol extension + +### New router role + +```haskell +data ServerRoles = ServerRoles + { storage :: Bool, + proxy :: Bool, + names :: Bool + } +``` + +Name-capable routers run an Ethereum light client. + +### Resolution protocol + +Uses existing SMP proxy infrastructure. Client sends queries through a proxy, not directly to name servers. + +#### Commands + +``` +Client -> Proxy -> Name Server: + RSLV + +Name Server -> Proxy -> Client: + NAME + ERR AUTH +``` + +Forwarded via `PRXY`/`PFWD`/`RRES` mechanism. + +#### Two-operator resolution + +``` +Client -> Proxy A (Op 1) -> Name Server X (Op 1) +Client -> Proxy B (Op 2) -> Name Server Y (Op 2) +``` + +Both read same Ethereum state. + +- Agree: trusted +- Disagree: warn, don't use +- One fails: retry with another server or show single result with reduced trust + +Proxy sees client IP and session, but not query. Name server sees query, not client IP or session. + +#### Name server implementation + +1. Runs Ethereum light client (e.g., Helios) tracking SNRC +2. Receives `RSLV` via SMP proxy +3. Returns record from local state + +State proofs can be added post-MVP. + +#### Configuration + +```haskell +data NamesConfig = NamesConfig + { ethereumEndpoint :: String, + snrcAddress :: EthAddress, + cacheSeconds :: Int + } +``` + +#### Versioning + +New SMP protocol version. Older routers/clients don't advertise the capability. + +### Default routers + +Default router list updated to include name-capable routers. + +## Part 3: UI integration + +### Markdown + +- `#name` or `#name.simplex` - native names (no dot = `.simplex` implied) +- `#my-name` or `#my-name.simplex` - hyphenated names +- `#sub.name.simplex` - subdomains (explicit TLD) +- `#name.testing` - test namespace +- Rendered as clickable resolve-and-connect links + +CLI: `#` = local in group commands, global in `/c` and messages. + +`:name.simplex`, `:my-name.simplex` - contact addresses (same namespace, different link type). MVP parser supports this syntax; resolution works; UI support is post-MVP. + +### Resolution flow + +1. Normalize per ENSIP-15, compute namehash +2. `RSLV` to two name servers via two proxies +3. Compare results +4. First channel link -> short link resolution -> connection data +5. Present for joining + +### Search + +`#...simplex` triggers resolution. Disable in "More privacy" settings. + +### Verification + +On-chain link matches profile address = verified. Only name owner can set on-chain links. + +- Manual: "Verify" button resolves and compares +- Auto: optional setting, resolves on profile open + +### Display + +Show name and verification status. `#` is syntax, not part of the name. + +## Open questions + +1. **Contract upgrade mechanism**: proxy pattern with timelock? Migration path for future Community Credits payment and domain name support. diff --git a/packages/simplex-chat-client/types/typescript/src/types.ts b/packages/simplex-chat-client/types/typescript/src/types.ts index 44949611b2..de86d1e790 100644 --- a/packages/simplex-chat-client/types/typescript/src/types.ts +++ b/packages/simplex-chat-client/types/typescript/src/types.ts @@ -2358,6 +2358,7 @@ export type Format = | Format.Uri | Format.HyperLink | Format.SimplexLink + | Format.SimplexName | Format.Command | Format.Mention | Format.Email @@ -2375,6 +2376,7 @@ export namespace Format { | "uri" | "hyperLink" | "simplexLink" + | "simplexName" | "command" | "mention" | "email" @@ -2431,6 +2433,11 @@ export namespace Format { smpHosts: string[] // non-empty } + export interface SimplexName extends Interface { + type: "simplexName" + nameInfo: SimplexNameInfo + } + export interface Command extends Interface { type: "command" commandStr: string @@ -3848,6 +3855,24 @@ export enum SimplexLinkType { Relay = "relay", } +export interface SimplexNameInfo { + nameType: SimplexNameType + nameTLD: SimplexTLD + domain: string + subDomain: string[] +} + +export enum SimplexNameType { + PublicGroup = "publicGroup", + Contact = "contact", +} + +export enum SimplexTLD { + Simplex = "simplex", + Testing = "testing", + Web = "web", +} + export enum SndCIStatusProgress { Partial = "partial", Complete = "complete", diff --git a/packages/simplex-chat-python/src/simplex_chat/types/_types.py b/packages/simplex-chat-python/src/simplex_chat/types/_types.py index 409a187245..3bbf82d350 100644 --- a/packages/simplex-chat-python/src/simplex_chat/types/_types.py +++ b/packages/simplex-chat-python/src/simplex_chat/types/_types.py @@ -1687,6 +1687,10 @@ class Format_simplexLink(TypedDict): simplexUri: str smpHosts: list[str] # non-empty +class Format_simplexName(TypedDict): + type: Literal["simplexName"] + nameInfo: "SimplexNameInfo" + class Format_command(TypedDict): type: Literal["command"] commandStr: str @@ -1712,13 +1716,14 @@ Format = ( | Format_uri | Format_hyperLink | Format_simplexLink + | Format_simplexName | Format_command | Format_mention | Format_email | Format_phone ) -Format_Tag = Literal["bold", "italic", "strikeThrough", "snippet", "secret", "small", "colored", "uri", "hyperLink", "simplexLink", "command", "mention", "email", "phone"] +Format_Tag = Literal["bold", "italic", "strikeThrough", "snippet", "secret", "small", "colored", "uri", "hyperLink", "simplexLink", "simplexName", "command", "mention", "email", "phone"] class FormattedText(TypedDict): format: NotRequired["Format"] @@ -2687,6 +2692,16 @@ class SimplePreference(TypedDict): SimplexLinkType = Literal["contact", "invitation", "group", "channel", "relay"] +class SimplexNameInfo(TypedDict): + nameType: "SimplexNameType" + nameTLD: "SimplexTLD" + domain: str + subDomain: list[str] + +SimplexNameType = Literal["publicGroup", "contact"] + +SimplexTLD = Literal["simplex", "testing", "web"] + SndCIStatusProgress = Literal["partial", "complete"] class SndConnEvent_switchQueue(TypedDict): diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index 8a91d35f05..0832fecb09 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."f03cec7a58ed13a39a52886888c74bcefdb64479" = "0bkd8kqgmwgfh5rwnw7s4p6mx9kwigi4jq9ljlfvzj23pslk1aq7"; + "https://github.com/simplex-chat/simplexmq.git"."e9265a7f7cb723d70b03e1b67af01f2666872a44" = "00xyzc5advpka2d2mq11f02cmcr7fa7n6mjj53symspdpx1pgfa5"; "https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d"; "https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl"; diff --git a/src/Simplex/Chat/Library/Commands.hs b/src/Simplex/Chat/Library/Commands.hs index 8d9d882366..43e31c8eef 100644 --- a/src/Simplex/Chat/Library/Commands.hs +++ b/src/Simplex/Chat/Library/Commands.hs @@ -5547,17 +5547,25 @@ mkValidName :: String -> String mkValidName = dropWhileEnd isSpace . take 50 . reverse . fst3 . foldl' addChar ("", '\NUL', 0 :: Int) where fst3 (x, _, _) = x - addChar (r, prev, punct) c = if validChar then (c' : r, c', punct') else (r, prev, punct) + addChar (r, prev, punct) c' = if validChar then (c : r, c, punct') else (r, prev, punct) where - c' = if isSpace c then ' ' else c + c = if isSpace c' then ' ' else c' + cat = generalCategory c + isPunct = case cat of + ConnectorPunctuation -> True + DashPunctuation -> True + OtherPunctuation -> True + _ -> False punct' - | isPunctuation c = punct + 1 - | isSpace c = punct + | isPunct = punct + 1 + | c == ' ' = punct | otherwise = 0 validChar - | c == '\'' = False - | prev == '\NUL' = c > ' ' && c /= '#' && c /= '@' && validFirstChar - | isSpace prev = validFirstChar || (punct == 0 && isPunctuation c) - | isPunctuation prev = validFirstChar || isSpace c || (punct < 3 && isPunctuation c) - | otherwise = validFirstChar || isSpace c || isMark c || isPunctuation c - validFirstChar = isLetter c || isNumber c || isSymbol c + | c `elem` prohibited = False + | prev == '\NUL' = c > ' ' && validFirstNameChar + | prev == ' ' = validFirstChar || (punct == 0 && isPunct) + | punct > 0 = validFirstChar || c == ' ' + | otherwise = validFirstChar || c == ' ' || isMark c || isPunct + validFirstNameChar = isLetter c || cat == DecimalNumber || cat == OtherSymbol + validFirstChar = validFirstNameChar || cat == CurrencySymbol || cat == MathSymbol + prohibited = ".,;/\\#@'\"`~" :: String diff --git a/src/Simplex/Chat/Markdown.hs b/src/Simplex/Chat/Markdown.hs index 9325de41eb..9507375527 100644 --- a/src/Simplex/Chat/Markdown.hs +++ b/src/Simplex/Chat/Markdown.hs @@ -35,11 +35,11 @@ import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) import Simplex.Chat.Types -import Simplex.Messaging.Agent.Protocol (AConnectionLink (..), ConnReqUriData (..), ConnShortLink (..), ConnectionLink (..), ConnectionRequestUri (..), ContactConnType (..), SMPQueue (..), simplexConnReqUri, simplexShortLink) +import Simplex.Messaging.Agent.Protocol (AConnectionLink (..), ConnReqUriData (..), ConnShortLink (..), ConnectionLink (..), ConnectionRequestUri (..), ContactConnType (..), SMPQueue (..), SimplexNameInfo (..), simplexConnReqUri, simplexShortLink) import Simplex.Messaging.Encoding.String import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON, fstToLower, sumTypeJSON) import Simplex.Messaging.Protocol (ProtocolServer (..)) -import Simplex.Messaging.Util (decodeJSON, safeDecodeUtf8, tshow) +import Simplex.Messaging.Util (decodeJSON, safeDecodeUtf8, tshow, (<$?>)) import System.Console.ANSI.Types import qualified Text.Email.Validate as Email import qualified URI.ByteString as U @@ -59,6 +59,7 @@ data Format -- showText is Nothing for the usual Uri without text | HyperLink {showText :: Maybe Text, linkUri :: Text} | SimplexLink {showText :: Maybe Text, linkType :: SimplexLinkType, simplexUri :: AConnectionLink, smpHosts :: NonEmpty Text} + | SimplexName {nameInfo :: SimplexNameInfo} | Command {commandStr :: Text} | Mention {memberName :: Text} | Email @@ -184,6 +185,7 @@ isLink = \case Uri -> True HyperLink {} -> True SimplexLink {} -> True + SimplexName {} -> True _ -> False hasLinks :: MarkdownList -> Bool @@ -202,9 +204,9 @@ markdownP = mconcat <$> A.many' fragmentP '_' -> formattedP '_' Italic '~' -> formattedP '~' StrikeThrough '`' -> formattedP '`' Snippet - '#' -> A.char '#' *> secretP + '#' -> A.char '#' *> (secretP <|> nameRefP '#' <|> secretFallback) '!' -> styledP <|> wordP - '@' -> mentionP <|> wordP + '@' -> (A.char '@' *> nameRefP '@') <|> mentionP <|> wordP '/' -> commandP <|> wordP '[' -> sowLinkP <|> wordP _ @@ -221,14 +223,29 @@ markdownP = mconcat <$> A.many' fragmentP unmarked $ c `T.cons` s `T.snoc` c | otherwise = markdown f s secretP :: Parser Markdown - secretP = secret <$> A.takeWhile (== '#') <*> A.takeTill (== '#') <*> A.takeWhile (== '#') - secret :: Text -> Text -> Text -> Markdown - secret b s a - | T.null a || T.null s || T.head s == ' ' || T.last s == ' ' = - unmarked $ '#' `T.cons` ss - | otherwise = markdown Secret $ T.init ss + secretP = secret <$?> ((,,) <$> A.takeWhile (== '#') <*> A.takeTill (== '#') <*> A.takeWhile1 (== '#')) + secret :: (Text, Text, Text) -> Either String Markdown + secret (b, s, a) + | T.null s || T.head s == ' ' || T.last s == ' ' = Left "not secret" + | otherwise = Right $ markdown Secret $ T.init ss where ss = b <> s <> a + secretFallback :: Parser Markdown + secretFallback = unmarked . ('#' `T.cons`) <$> A.takeTill (== ' ') + nameRefP :: Char -> Parser Markdown + nameRefP pfx = nameRef <$?> A.takeTill (== ' ') + where + nameRef word + | pfx == '@' && T.all (/= '.') name = Left "not a name" + | otherwise = mkMd <$> strDecode (encodeUtf8 full) + where + (name, punct) = splitPunctuation word + full = pfx `T.cons` name + mkMd ni + | T.null punct = md' + | otherwise = md' :|: unmarked punct + where + md' = markdown (SimplexName ni) full styledP :: Parser Markdown styledP = do f <- A.char '!' *> ((A.char '-' $> Small) <|> (colored <$> colorP)) <* A.space @@ -449,6 +466,7 @@ markdownText (FormattedText f_ t) = case f_ of Uri -> t HyperLink {} -> t SimplexLink {} -> t + SimplexName {} -> t Mention _ -> t Command _ -> t Email -> t @@ -479,7 +497,6 @@ displayNameTextP_ = (,"") <$> quoted '\'' <|> splitPunctuation <$> takeNameTill takeNameTill p = A.peekChar' >>= \c -> if refChar c then A.takeTill p else fail "invalid first character in display name" - splitPunctuation s = (T.dropWhileEnd isPunctuation s, T.takeWhileEnd isPunctuation s) quoted c = A.char c *> takeNameTill (== c) <* A.char c refChar c = c > ' ' && c /= '#' && c /= '@' && c /= '\'' @@ -490,6 +507,9 @@ commandTextP = do (keyword : _) | T.all (\c -> isAlpha c || isDigit c || c == '_') keyword -> pure (cmd, punct) _ -> fail "invalid command keyword" +splitPunctuation :: Text -> (Text, Text) +splitPunctuation s = (T.dropWhileEnd isPunctuation s, T.takeWhileEnd isPunctuation s) + -- quotes names that contain spaces or end on punctuation viewName :: Text -> Text viewName s = if T.any isSpace s || maybe False (isPunctuation . snd) (T.unsnoc s) then "'" <> s <> "'" else s diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt b/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt index 127fce8e45..a7880799db 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt +++ b/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt @@ -3969,6 +3969,15 @@ Query: Plan: SEARCH chat_items USING INDEX idx_chat_items_group_shared_msg_id (user_id=? AND group_id=? AND group_member_id=?) +Query: + UPDATE chat_items + SET item_deleted = ?, item_deleted_ts = ?, item_deleted_by_group_member_id = ?, updated_at = ? + WHERE user_id = ? AND group_id = ? AND msg_content_tag = ? AND item_deleted = ? AND item_sent = 0 + RETURNING chat_item_id + +Plan: +SEARCH chat_items USING COVERING INDEX idx_chat_items_groups_msg_content_tag_deleted (user_id=? AND group_id=? AND msg_content_tag=? AND item_deleted=? AND item_sent=?) + Query: UPDATE chat_items SET item_deleted = ?, item_deleted_ts = ?, item_deleted_by_group_member_id = ?, updated_at = ? @@ -6870,6 +6879,10 @@ Query: SELECT member_status FROM group_members WHERE local_display_name = ? Plan: SCAN group_members +Query: SELECT member_status FROM group_members WHERE member_role = 'relay' +Plan: +SCAN group_members + Query: SELECT member_xcontact_id, member_welcome_shared_msg_id FROM group_members WHERE user_id = ? AND group_id = ? AND group_member_id = ? Plan: SEARCH group_members USING INTEGER PRIMARY KEY (rowid=?) diff --git a/tests/MarkdownTests.hs b/tests/MarkdownTests.hs index a82e18f988..1db400c62a 100644 --- a/tests/MarkdownTests.hs +++ b/tests/MarkdownTests.hs @@ -10,6 +10,7 @@ import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) import Simplex.Chat.Markdown +import Simplex.Messaging.Agent.Protocol (SimplexNameInfo (..), SimplexNameType (..), SimplexTLD (..)) import Simplex.Messaging.Encoding.String import Simplex.Messaging.Util ((<$$>)) import System.Console.ANSI.Types @@ -28,6 +29,7 @@ markdownTests = do textWithPhone textWithMentions textWithCommands + textWithSimplexNames multilineMarkdownList testSanitizeUri @@ -117,7 +119,7 @@ secretText = describe "secret text" do "this is # unformatted # text" <==> "this is # unformatted # text" "this is #unformatted # text" - <==> "this is #unformatted # text" + <==> "this is " <> sname NTPublicGroup TLDSimplex "unformatted" [] "unformatted" <> " # text" "this is # unformatted# text" <==> "this is # unformatted# text" "this is ## unformatted ## text" @@ -125,9 +127,9 @@ secretText = describe "secret text" do "this is#unformatted# text" <==> "this is#unformatted# text" "this is #unformatted text" - <==> "this is #unformatted text" + <==> "this is " <> sname NTPublicGroup TLDSimplex "unformatted" [] "unformatted" <> " text" "*this* is #unformatted text" - <==> bold "this" <> " is #unformatted text" + <==> bold "this" <> " is " <> sname NTPublicGroup TLDSimplex "unformatted" [] "unformatted" <> " text" it "ignored internal markdown" do "snippet: `this is #secret_text#`" <==> "snippet: " <> markdown Snippet "this is #secret_text#" @@ -297,8 +299,8 @@ textWithEmail = describe "text with Email" do "test chat@simplex.chat." <==> "test " <> email "chat@simplex.chat" <> "." "test chat@simplex.chat..." <==> "test " <> email "chat@simplex.chat" <> "..." it "ignored as email markdown" do - "chat @simplex.chat" <==> "chat " <> mention "simplex.chat" "@simplex.chat" - "this is chat @simplex.chat" <==> "this is chat " <> mention "simplex.chat" "@simplex.chat" + "chat @simplex.chat" <==> "chat " <> sname NTContact TLDWeb "simplex.chat" [] "simplex.chat" + "this is chat @simplex.chat" <==> "this is chat " <> sname NTContact TLDWeb "simplex.chat" [] "simplex.chat" "this is chat@ simplex.chat" <==> "this is chat@ " <> uri "simplex.chat" "this is chat @ simplex.chat" <==> "this is chat @ " <> uri "simplex.chat" "*this* is chat @ simplex.chat" <==> bold "this" <> " is chat @ " <> uri "simplex.chat" @@ -378,6 +380,39 @@ uri' = FormattedText $ Just Uri command' :: Text -> Text -> FormattedText command' = FormattedText . Just . Command +sname :: SimplexNameType -> SimplexTLD -> Text -> [Text] -> Text -> Markdown +sname nt ns dom sub txt = markdown (SimplexName $ SimplexNameInfo nt ns dom sub) (pfx <> txt) + where + pfx = case nt of NTPublicGroup -> "#"; NTContact -> "@" + +textWithSimplexNames :: Spec +textWithSimplexNames = describe "text with SimpleX names" do + it "channel names - simplex namespace" do + "#privacy" <==> sname NTPublicGroup TLDSimplex "privacy" [] "privacy" + "#privacy.simplex" <==> sname NTPublicGroup TLDSimplex "privacy" [] "privacy.simplex" + "#my-channel.simplex" <==> sname NTPublicGroup TLDSimplex "my-channel" [] "my-channel.simplex" + "hello #privacy!" <==> "hello " <> sname NTPublicGroup TLDSimplex "privacy" [] "privacy" <> "!" + "see #privacy.simplex now" <==> "see " <> sname NTPublicGroup TLDSimplex "privacy" [] "privacy.simplex" <> " now" + "#123" <==> sname NTPublicGroup TLDSimplex "123" [] "123" + it "channel names - subdomains" do + "#support.acme.simplex" <==> sname NTPublicGroup TLDSimplex "acme" ["support"] "support.acme.simplex" + "#a.b.acme.simplex" <==> sname NTPublicGroup TLDSimplex "acme" ["b", "a"] "a.b.acme.simplex" + it "channel names - testing namespace" do + "#test.testing" <==> sname NTPublicGroup TLDTesting "test" [] "test.testing" + "#sub.test.testing" <==> sname NTPublicGroup TLDTesting "test" ["sub"] "sub.test.testing" + it "channel names - web domains" do + "#example.com" <==> sname NTPublicGroup TLDWeb "example.com" [] "example.com" + "#news.bbc.co.uk" <==> sname NTPublicGroup TLDWeb "news.bbc.co.uk" [] "news.bbc.co.uk" + "#123.com" <==> sname NTPublicGroup TLDWeb "123.com" [] "123.com" + it "contact names" do + "@privacy.simplex" <==> sname NTContact TLDSimplex "privacy" [] "privacy.simplex" + "@my-name.simplex" <==> sname NTContact TLDSimplex "my-name" [] "my-name.simplex" + "@alice.example.com" <==> sname NTContact TLDWeb "alice.example.com" [] "alice.example.com" + it "not parsed as names" do + "#secret#" <==> markdown Secret "secret" + "##double secret##" <==> markdown Secret "#double secret#" + "#" <==> "#" + multilineMarkdownList :: Spec multilineMarkdownList = describe "multiline markdown" do it "correct markdown" do diff --git a/tests/ValidNames.hs b/tests/ValidNames.hs index 22ac4a695d..dd8433d231 100644 --- a/tests/ValidNames.hs +++ b/tests/ValidNames.hs @@ -10,15 +10,17 @@ validNameTests = describe "valid chat names" $ do testMkValidName :: IO () testMkValidName = do mkValidName "alice" `shouldBe` "alice" + mkValidName " alice" `shouldBe` "alice" + mkValidName "?alice" `shouldBe` "alice" mkValidName "алиса" `shouldBe` "алиса" mkValidName "John Doe" `shouldBe` "John Doe" - mkValidName "J.Doe" `shouldBe` "J.Doe" - mkValidName "J. Doe" `shouldBe` "J. Doe" - mkValidName "J..Doe" `shouldBe` "J..Doe" - mkValidName "J ..Doe" `shouldBe` "J ..Doe" - mkValidName "J ... Doe" `shouldBe` "J ... Doe" - mkValidName "J .... Doe" `shouldBe` "J ... Doe" - mkValidName "J . . Doe" `shouldBe` "J . Doe" + mkValidName "J.Doe" `shouldBe` "JDoe" + mkValidName "J. Doe" `shouldBe` "J Doe" + mkValidName "J..Doe" `shouldBe` "JDoe" + mkValidName "J ..Doe" `shouldBe` "J Doe" + mkValidName "J ... Doe" `shouldBe` "J Doe" + mkValidName "J .... Doe" `shouldBe` "J Doe" + mkValidName "J . . Doe" `shouldBe` "J Doe" mkValidName "@alice" `shouldBe` "alice" mkValidName "#alice" `shouldBe` "alice" mkValidName "'alice" `shouldBe` "alice" @@ -26,17 +28,32 @@ testMkValidName = do mkValidName "alice " `shouldBe` "alice" mkValidName "John Doe" `shouldBe` "John Doe" mkValidName "'John Doe'" `shouldBe` "John Doe" - mkValidName "\"John Doe\"" `shouldBe` "John Doe\"" - mkValidName "`John Doe`" `shouldBe` "`John Doe`" - mkValidName "John \"Doe\"" `shouldBe` "John \"Doe\"" - mkValidName "John `Doe`" `shouldBe` "John `Doe`" - mkValidName "alice/bob" `shouldBe` "alice/bob" - mkValidName "alice / bob" `shouldBe` "alice / bob" - mkValidName "alice /// bob" `shouldBe` "alice /// bob" - mkValidName "alice //// bob" `shouldBe` "alice /// bob" + mkValidName "\"John Doe\"" `shouldBe` "John Doe" + mkValidName "`John Doe`" `shouldBe` "John Doe" + mkValidName "John \"Doe\"" `shouldBe` "John Doe" + mkValidName "John `Doe`" `shouldBe` "John Doe" + mkValidName "alice/bob" `shouldBe` "alicebob" + mkValidName "alice / bob" `shouldBe` "alice bob" + mkValidName "alice /// bob" `shouldBe` "alice bob" + mkValidName "alice //// bob" `shouldBe` "alice bob" mkValidName "alice >>= bob" `shouldBe` "alice >>= bob" - mkValidName "alice@example.com" `shouldBe` "alice@example.com" + mkValidName "alice@example.com" `shouldBe` "aliceexamplecom" mkValidName "alice <> bob" `shouldBe` "alice <> bob" mkValidName "alice -> bob" `shouldBe` "alice -> bob" + mkValidName "alice & bob" `shouldBe` "alice & bob" + mkValidName "alice && bob" `shouldBe` "alice & bob" + mkValidName "alice & & bob" `shouldBe` "alice & bob" + mkValidName "alice-bob" `shouldBe` "alice-bob" + mkValidName "alice--bob" `shouldBe` "alice-bob" + mkValidName "alice -- bob" `shouldBe` "alice - bob" + mkValidName "alice \\ bob" `shouldBe` "alice bob" + mkValidName "alice (bob)" `shouldBe` "alice bob" + mkValidName "alice: bob" `shouldBe` "alice: bob" + mkValidName "alice 👍" `shouldBe` "alice 👍" + mkValidName "👍" `shouldBe` "👍" + mkValidName "alice >" `shouldBe` "alice >" + mkValidName "> alice" `shouldBe` "alice" + mkValidName "123" `shouldBe` "123" + mkValidName "123 alice" `shouldBe` "123 alice" mkValidName "01234567890123456789012345678901234567890123456789extra" `shouldBe` "01234567890123456789012345678901234567890123456789" mkValidName "0123456789012345678901234567890123456789012345678 extra" `shouldBe` "0123456789012345678901234567890123456789012345678" From 39717d3327fd754590d70f004f01a7ac48868d5b Mon Sep 17 00:00:00 2001 From: sh <37271604+shumvgolove@users.noreply.github.com> Date: Fri, 29 May 2026 08:38:14 +0000 Subject: [PATCH 08/20] directory: add rtsopts (#7006) --- simplex-chat.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simplex-chat.cabal b/simplex-chat.cabal index 29436e128e..20cad947bb 100644 --- a/simplex-chat.cabal +++ b/simplex-chat.cabal @@ -500,7 +500,7 @@ executable simplex-directory-service Directory.Store.Migrate Directory.Util Paths_simplex_chat - ghc-options: -O2 -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-missing-kind-signatures -Wno-missing-deriving-strategies -Wno-monomorphism-restriction -Wno-prepositive-qualified-module -Wno-unused-packages -Wno-implicit-prelude -Wno-missing-safe-haskell-mode -Wno-missing-export-lists -Wno-partial-fields -Wcompat -Werror=incomplete-record-updates -Werror=incomplete-patterns -Werror=missing-methods -Werror=incomplete-uni-patterns -Werror=tabs -Wredundant-constraints -Wincomplete-record-updates -Wunused-type-patterns -threaded + ghc-options: -O2 -Weverything -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-missing-kind-signatures -Wno-missing-deriving-strategies -Wno-monomorphism-restriction -Wno-prepositive-qualified-module -Wno-unused-packages -Wno-implicit-prelude -Wno-missing-safe-haskell-mode -Wno-missing-export-lists -Wno-partial-fields -Wcompat -Werror=incomplete-record-updates -Werror=incomplete-patterns -Werror=missing-methods -Werror=incomplete-uni-patterns -Werror=tabs -Wredundant-constraints -Wincomplete-record-updates -Wunused-type-patterns -threaded -rtsopts build-depends: aeson ==2.2.* , async ==2.2.* From 5aace8401ce69bce497cdd1be993d50716d453a7 Mon Sep 17 00:00:00 2001 From: Narasimha-sc <166327228+Narasimha-sc@users.noreply.github.com> Date: Sat, 30 May 2026 06:33:10 +0000 Subject: [PATCH 09/20] core: fix /start remote host parser when iface name contains a space (#7025) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * core: fix /start remote host parser when iface name contains a space The iface= field used jsonP (which calls takeByteString and strict-decodes the entire remaining input as JSON). When port= followed iface=, the strict decode failed on the trailing data and the text1P fallback stopped at the first space inside the JSON-quoted interface name (e.g. "Ethernet 2"), leaving unparseable junk and producing "Failed reading: empty". Replace jsonP with a bounded quotedP that consumes only up to the closing quote, leaving port=… for the next parser. * plan: document fix for /start remote host iface-with-space parser bug --- plans/2026-05-29-fix-space-in-interface.md | 13 +++++++++++++ src/Simplex/Chat/Library/Commands.hs | 3 ++- tests/RemoteTests.hs | 10 +++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 plans/2026-05-29-fix-space-in-interface.md diff --git a/plans/2026-05-29-fix-space-in-interface.md b/plans/2026-05-29-fix-space-in-interface.md new file mode 100644 index 0000000000..e3d8ba7cc4 --- /dev/null +++ b/plans/2026-05-29-fix-space-in-interface.md @@ -0,0 +1,13 @@ +# Fix `/start remote host` parser when iface name contains a space + +## Problem + +Picking a non-default interface (e.g. Windows `Ethernet 2`) on the "Link a mobile" screen and refreshing the QR code returns `error chat: error chat commandError Failed reading: empty`. The desktop UI sends `/start remote host new addr=… iface="Ethernet 2" port=…`; the chat backend rejects it as an unparseable command. Without a workaround the user can't pin a specific local interface for the mobile-link controller. + +## Cause + +`rcCtrlAddressP` parses the iface value with `jsonP <|> text1P` (`src/Simplex/Chat/Library/Commands.hs:5549`). `jsonP` calls `A.takeByteString`, consuming *all* remaining input, then runs `eitherDecodeStrict'`. When `port=…` follows `iface=…` the strict decode fails because the JSON value `"Ethernet 2"` has trailing junk after it, so attoparsec backtracks to `text1P` (`takeTill (== ' ')`). `text1P` stops at the first space — inside the JSON quotes — leaving `2" port=12345` which nothing downstream can consume, `A.endOfInput` fails, the whole `A.choice` exhausts and surfaces attoparsec's `empty` message. With an iface name that has no space (`"lo"`) the bug is invisible: text1P swallows the full quoted token and the rest parses, but the interface name is stored with literal quotes so the iface preference silently never matches a real adapter anyway. + +## Fix + +Replace `jsonP` with a bounded `quotedP` that consumes only the bytes between `"…"` and leaves trailing fields for the next parser. `text1P` is kept as the unquoted fallback. Two-line change in `Commands.hs` plus a pure regression test in `tests/RemoteTests.hs` that asserts `parseChatCommand` of `/start remote host new addr=192.168.1.5 iface="Ethernet 2" port=12345` produces `RCCtrlAddress _ "Ethernet 2"` with port `12345`. diff --git a/src/Simplex/Chat/Library/Commands.hs b/src/Simplex/Chat/Library/Commands.hs index 43e31c8eef..4bbfa8e09d 100644 --- a/src/Simplex/Chat/Library/Commands.hs +++ b/src/Simplex/Chat/Library/Commands.hs @@ -5526,7 +5526,8 @@ chatCommandP = addressAA = AddressSettings False <$> (Just . AutoAccept <$> (" incognito=" *> onOffP <|> pure False)) <*> autoReply businessAA = " business" *> (AddressSettings True (Just $ AutoAccept False) <$> autoReply) autoReply = optional (A.space *> msgContentP) - rcCtrlAddressP = RCCtrlAddress <$> ("addr=" *> strP) <*> (" iface=" *> (jsonP <|> text1P)) + rcCtrlAddressP = RCCtrlAddress <$> ("addr=" *> strP) <*> (" iface=" *> (quotedP <|> text1P)) + quotedP = safeDecodeUtf8 <$> (A.char '"' *> A.takeTill (== '"') <* A.char '"') text1P = safeDecodeUtf8 <$> A.takeTill (== ' ') char_ = optional . A.char diff --git a/tests/RemoteTests.hs b/tests/RemoteTests.hs index 51f4324bf0..e96d531805 100644 --- a/tests/RemoteTests.hs +++ b/tests/RemoteTests.hs @@ -16,7 +16,8 @@ import qualified Data.ByteString as B import qualified Data.ByteString.Lazy.Char8 as LB import Data.List (find, isPrefixOf) import qualified Data.Map.Strict as M -import Simplex.Chat.Controller (ChatConfig (..), versionNumber) +import Simplex.Chat.Controller (ChatCommand (..), ChatConfig (..), versionNumber) +import Simplex.Chat.Library.Commands (parseChatCommand) import qualified Simplex.Chat.Controller as Controller import Simplex.Chat.Mobile.File import Simplex.Chat.Remote (remoteFilesFolder) @@ -24,6 +25,7 @@ import Simplex.Chat.Remote.Types import Simplex.Messaging.Crypto.File (CryptoFileArgs (..)) import Simplex.Messaging.Encoding.String (strEncode) import Simplex.Messaging.Util +import Simplex.RemoteControl.Types (RCCtrlAddress (..)) import System.FilePath (()) import Test.Hspec hiding (it) import UnliftIO @@ -32,6 +34,12 @@ import UnliftIO.Directory remoteTests :: SpecWith TestParams remoteTests = describe "Remote" $ do + describe "/start remote host parser" $ do + it "parses iface name with a space followed by port=" $ \_ -> + parseChatCommand "/start remote host new addr=192.168.1.5 iface=\"Ethernet 2\" port=12345" + `shouldSatisfy` \case + Right (StartRemoteHost Nothing (Just (RCCtrlAddress _ "Ethernet 2")) (Just 12345)) -> True + _ -> False xdescribe "No compression" $ aroundWith (. ((False, False),)) runRemoteTests xdescribe "Mobile offers compression" $ aroundWith (. ((True, False),)) runRemoteTests xdescribe "Desktop offers compression" $ aroundWith (. ((False, True),)) runRemoteTests From 553f98adf4d28f2e3f4aab4e065f61e8842509f2 Mon Sep 17 00:00:00 2001 From: Narasimha-sc <166327228+Narasimha-sc@users.noreply.github.com> Date: Sat, 30 May 2026 08:01:16 +0000 Subject: [PATCH 10/20] desktop: don't copy non-message items when selecting message text (#6993) * desktop: don't copy non-message items when selecting message text Selecting text across messages also copied the text of event/info items (e.g. "connected") that fell inside the selection, even though those items are never highlighted as selected. getSelectedCopiedText emitted text for every merged item between the selection bounds. Event/info items have no msgContent but a non-empty text, so as interior items their text was copied. Skip items whose content has no msgContent - only real messages are copyable. * plans: add 2026-05-20-fix-copy-non-msg-items.md --- .../common/views/chat/TextSelection.kt | 3 ++ plans/2026-05-20-fix-copy-non-msg-items.md | 50 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 plans/2026-05-20-fix-copy-non-msg-items.md diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/TextSelection.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/TextSelection.kt index d85488cefc..db31c05dce 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/TextSelection.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/TextSelection.kt @@ -217,6 +217,9 @@ class SelectionManager { val hi = maxOf(r.startIndex, r.endIndex) return (lo..hi).mapNotNull { idx -> val ci = items.getOrNull(idx)?.newest()?.item ?: return@mapNotNull null + // Only real messages are copyable. Event/info items (e.g. "connected", calls, e2ee info) + // have no msgContent and are never highlighted as selected, so they must never be copied. + if (ci.content.msgContent == null) return@mapNotNull null if (ci.meta.itemDeleted != null && (!revealedItems.contains(ci.id) || ci.isDeletedContent)) return@mapNotNull null val sel = selectedRange(range, idx) ?: return@mapNotNull null selectedItemCopiedText(ci, sel, linkMode) diff --git a/plans/2026-05-20-fix-copy-non-msg-items.md b/plans/2026-05-20-fix-copy-non-msg-items.md new file mode 100644 index 0000000000..4f0c554357 --- /dev/null +++ b/plans/2026-05-20-fix-copy-non-msg-items.md @@ -0,0 +1,50 @@ +# Desktop: text selection copies non-message event items + +Branch: `nd/fix-copy-non-msg-items` · code commit `a536452ca` · PR [#6993](https://github.com/simplex-chat/simplex-chat/pull/6993). + +## 1. Problem statement + +The Desktop "select text in messages" feature (PR [#6725](https://github.com/simplex-chat/simplex-chat/pull/6725)) lets the user drag a selection across several message bubbles and copy it. When the selection spans a chat event/info item — a "connected" event, a member "joined"/"left" event, a call event, an e2ee-info line, a feature-change line — the copied text includes that item's text, even though the item is never shown highlighted as part of the selection. + +Expected: only real message text is copied. Observed: event/info text such as "connected" is appended into the clipboard between the selected messages. + +### Privacy note + +Event/info item text is produced from localized string resources (`generalGetString`, `RcvConnEvent.text`, etc.) — it is rendered in the language the user has chosen for the app, whereas real message text is not. A user who selects and copies a long span of messages carelessly, then pastes it into another chat or app, can therefore leak their chosen interface language through the event lines mixed into the paste. For a privacy-focused messenger this is a metadata leak, not only a cosmetic bug. + +## 2. Root cause + +`SelectionManager.getSelectedCopiedText` (`TextSelection.kt`) builds the copied string by iterating every merged-item index between the selection bounds and emitting each item's text: + +```kotlin +return (lo..hi).mapNotNull { idx -> + val ci = items.getOrNull(idx)?.newest()?.item ?: return@mapNotNull null + if (ci.meta.itemDeleted != null && ...) return@mapNotNull null + val sel = selectedRange(range, idx) ?: return@mapNotNull null + selectedItemCopiedText(ci, sel, linkMode) +} +``` + +For an *interior* item in a multi-item selection, `selectedRange` returns `0 until Int.MAX_VALUE` — the whole item is treated as selected — so its text is emitted unconditionally. The only items previously skipped were deleted ones. + +Anchor/focus character tracking (`setupItemSelection`) is wired up only for real message views (`FramedItemView`, `EmojiItemView`); event/info items never register offsets and never compute a highlight range. So an event item caught between two selected messages is invisible to the highlight but fully visible to `getSelectedCopiedText`. The copy logic and the on-screen selection disagreed. + +The distinguishing property: a real message has `ci.content.msgContent != null`; every event/info `CIContent` variant returns `msgContent == null`. + +## 3. Solution summary + +`apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/TextSelection.kt` — one guard, +3 lines. + +In `getSelectedCopiedText`, skip any item whose `content.msgContent` is null, alongside the existing deleted-item filter: + +```kotlin +if (ci.content.msgContent == null) return@mapNotNull null +``` + +Only real messages now contribute copied text — which is exactly the set of items that are selectable and highlighted, so the clipboard matches the visible selection. `content.msgContent` is the existing model property used elsewhere to tell a real message apart from an event/info item. + +## 4. Alternatives considered (and rejected) + +- **Special-case only "connected" events.** Matches the literal report but leaves the identical bug for every other event/info item (joined/left, calls, e2ee info, feature changes) — same class, same language leak. +- **Make event items non-selectable / consume the drag.** Larger change to the selection gesture; event items are already non-anchorable, and the bug is purely in the copy aggregation, not in the gesture. +- **Filter at the call site (`onCopySelection`).** Duplicates the message/non-message distinction outside the one function that owns copied-text assembly; `getSelectedCopiedText` is the correct single source. From 68fc1b5d227941677afa534b5453a9d9ec8dbe72 Mon Sep 17 00:00:00 2001 From: sh <37271604+shumvgolove@users.noreply.github.com> Date: Sat, 30 May 2026 08:39:14 +0000 Subject: [PATCH 11/20] core, ui: split SimplexNameDomain out of SimplexNameInfo (#7024) * core, ui: split SimplexNameDomain out of SimplexNameInfo * core: bump simplexmq to b3f28948 (SimplexNameDomain split) * core: bump simplexmq to 4e2c9fc3 (StrEncoding split) * core: bump simplexmq to ee2ff402 (#1788 squash merge) * update sha256map.nix --- apps/ios/SimpleXChat/ChatTypes.swift | 4 ++++ .../kotlin/chat/simplex/common/model/ChatModel.kt | 5 +++++ bots/api/TYPES.md | 15 ++++++++++++--- bots/src/API/Docs/Types.hs | 2 ++ cabal.project | 2 +- .../types/typescript/src/types.ts | 8 ++++++-- .../src/simplex_chat/types/_types.py | 7 +++++-- scripts/nix/sha256map.nix | 2 +- tests/MarkdownTests.hs | 4 ++-- 9 files changed, 38 insertions(+), 11 deletions(-) diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index 7265038f38..d2e28394a2 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -5141,6 +5141,10 @@ public enum SimplexLinkType: String, Decodable, Hashable { public struct SimplexNameInfo: Decodable, Equatable, Hashable { public var nameType: SimplexNameType + public var nameDomain: SimplexNameDomain +} + +public struct SimplexNameDomain: Decodable, Equatable, Hashable { public var nameTLD: SimplexTLD public var domain: String public var subDomain: [String] diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index aa4b677b8a..4bb7ae3d6e 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -4733,6 +4733,11 @@ enum class SimplexLinkType(val linkType: String) { @Serializable data class SimplexNameInfo( val nameType: SimplexNameType, + val nameDomain: SimplexNameDomain +) + +@Serializable +data class SimplexNameDomain( val nameTLD: SimplexTLD, val domain: String, val subDomain: List diff --git a/bots/api/TYPES.md b/bots/api/TYPES.md index af89c86411..488b7c4f05 100644 --- a/bots/api/TYPES.md +++ b/bots/api/TYPES.md @@ -165,6 +165,7 @@ This file is generated automatically. - [SecurityCode](#securitycode) - [SimplePreference](#simplepreference) - [SimplexLinkType](#simplexlinktype) +- [SimplexNameDomain](#simplexnamedomain) - [SimplexNameInfo](#simplexnameinfo) - [SimplexNameType](#simplexnametype) - [SimplexTLD](#simplextld) @@ -3447,15 +3448,23 @@ A_QUEUE: - "relay" +--- + +## SimplexNameDomain + +**Record type**: +- nameTLD: [SimplexTLD](#simplextld) +- domain: string +- subDomain: [string] + + --- ## SimplexNameInfo **Record type**: - nameType: [SimplexNameType](#simplexnametype) -- nameTLD: [SimplexTLD](#simplextld) -- domain: string -- subDomain: [string] +- nameDomain: [SimplexNameDomain](#simplexnamedomain) --- diff --git a/bots/src/API/Docs/Types.hs b/bots/src/API/Docs/Types.hs index 0f9e198cc1..c759a7453c 100644 --- a/bots/src/API/Docs/Types.hs +++ b/bots/src/API/Docs/Types.hs @@ -345,6 +345,7 @@ chatTypesDocsData = (sti @SecurityCode, STRecord, "", [], "", ""), (sti @SimplePreference, STRecord, "", [], "", ""), (sti @SimplexLinkType, STEnum, "XL", [], "", ""), + (sti @SimplexNameDomain, STRecord, "", [], "", ""), (sti @SimplexNameInfo, STRecord, "", [], "", ""), (sti @SimplexNameType, STEnum, "NT", [], "", ""), (sti @SimplexTLD, STEnum, "TLD", [], "", ""), @@ -561,6 +562,7 @@ deriving instance Generic RelayStatus deriving instance Generic ReportReason deriving instance Generic SecurityCode deriving instance Generic SimplexLinkType +deriving instance Generic SimplexNameDomain deriving instance Generic SimplexNameInfo deriving instance Generic SimplexNameType deriving instance Generic SimplexTLD diff --git a/cabal.project b/cabal.project index 728ab790c7..77e4ed838b 100644 --- a/cabal.project +++ b/cabal.project @@ -21,7 +21,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: e9265a7f7cb723d70b03e1b67af01f2666872a44 + tag: ee2ff402fed4d27d31521570c910fe82e0cf116a source-repository-package type: git diff --git a/packages/simplex-chat-client/types/typescript/src/types.ts b/packages/simplex-chat-client/types/typescript/src/types.ts index de86d1e790..6a230ecf15 100644 --- a/packages/simplex-chat-client/types/typescript/src/types.ts +++ b/packages/simplex-chat-client/types/typescript/src/types.ts @@ -3855,13 +3855,17 @@ export enum SimplexLinkType { Relay = "relay", } -export interface SimplexNameInfo { - nameType: SimplexNameType +export interface SimplexNameDomain { nameTLD: SimplexTLD domain: string subDomain: string[] } +export interface SimplexNameInfo { + nameType: SimplexNameType + nameDomain: SimplexNameDomain +} + export enum SimplexNameType { PublicGroup = "publicGroup", Contact = "contact", diff --git a/packages/simplex-chat-python/src/simplex_chat/types/_types.py b/packages/simplex-chat-python/src/simplex_chat/types/_types.py index 3bbf82d350..c3a5e0c4fb 100644 --- a/packages/simplex-chat-python/src/simplex_chat/types/_types.py +++ b/packages/simplex-chat-python/src/simplex_chat/types/_types.py @@ -2692,12 +2692,15 @@ class SimplePreference(TypedDict): SimplexLinkType = Literal["contact", "invitation", "group", "channel", "relay"] -class SimplexNameInfo(TypedDict): - nameType: "SimplexNameType" +class SimplexNameDomain(TypedDict): nameTLD: "SimplexTLD" domain: str subDomain: list[str] +class SimplexNameInfo(TypedDict): + nameType: "SimplexNameType" + nameDomain: "SimplexNameDomain" + SimplexNameType = Literal["publicGroup", "contact"] SimplexTLD = Literal["simplex", "testing", "web"] diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index 0832fecb09..f3bbf90b9f 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."e9265a7f7cb723d70b03e1b67af01f2666872a44" = "00xyzc5advpka2d2mq11f02cmcr7fa7n6mjj53symspdpx1pgfa5"; + "https://github.com/simplex-chat/simplexmq.git"."ee2ff402fed4d27d31521570c910fe82e0cf116a" = "0vka1b2bbrjg4s8j3h6732kjqjbhji0l55pzggd89ginrdjln3fg"; "https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d"; "https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl"; diff --git a/tests/MarkdownTests.hs b/tests/MarkdownTests.hs index 1db400c62a..efa010ceb1 100644 --- a/tests/MarkdownTests.hs +++ b/tests/MarkdownTests.hs @@ -10,7 +10,7 @@ import Data.Text (Text) import qualified Data.Text as T import Data.Text.Encoding (encodeUtf8) import Simplex.Chat.Markdown -import Simplex.Messaging.Agent.Protocol (SimplexNameInfo (..), SimplexNameType (..), SimplexTLD (..)) +import Simplex.Messaging.Agent.Protocol (SimplexNameDomain (..), SimplexNameInfo (..), SimplexNameType (..), SimplexTLD (..)) import Simplex.Messaging.Encoding.String import Simplex.Messaging.Util ((<$$>)) import System.Console.ANSI.Types @@ -381,7 +381,7 @@ command' :: Text -> Text -> FormattedText command' = FormattedText . Just . Command sname :: SimplexNameType -> SimplexTLD -> Text -> [Text] -> Text -> Markdown -sname nt ns dom sub txt = markdown (SimplexName $ SimplexNameInfo nt ns dom sub) (pfx <> txt) +sname nt ns dom sub txt = markdown (SimplexName $ SimplexNameInfo nt (SimplexNameDomain ns dom sub)) (pfx <> txt) where pfx = case nt of NTPublicGroup -> "#"; NTContact -> "@" From 16982b61114fe57af3713b4006e32d339d454e35 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Sat, 30 May 2026 17:16:56 +0100 Subject: [PATCH 12/20] core: rename migrations (#7028) --- simplex-chat.cabal | 12 ++++++------ src/Simplex/Chat/Store/Postgres/Migrations.hs | 12 ++++++------ ..._senders.hs => M20260529_delivery_job_senders.hs} | 10 +++++----- ...ient_services.hs => M20260530_client_services.hs} | 10 +++++----- ..._removed_at.hs => M20260531_member_removed_at.hs} | 10 +++++----- .../Chat/Store/Postgres/Migrations/chat_schema.sql | 2 +- src/Simplex/Chat/Store/SQLite/Migrations.hs | 12 ++++++------ ..._senders.hs => M20260529_delivery_job_senders.hs} | 10 +++++----- ...ient_services.hs => M20260530_client_services.hs} | 10 +++++----- ..._removed_at.hs => M20260531_member_removed_at.hs} | 10 +++++----- tests/PostgresSchemaDump.hs | 2 +- tests/SchemaDump.hs | 2 +- 12 files changed, 51 insertions(+), 51 deletions(-) rename src/Simplex/Chat/Store/Postgres/Migrations/{M20260515_delivery_job_senders.hs => M20260529_delivery_job_senders.hs} (89%) rename src/Simplex/Chat/Store/Postgres/Migrations/{M20260520_client_services.hs => M20260530_client_services.hs} (57%) rename src/Simplex/Chat/Store/Postgres/Migrations/{M20260525_member_removed_at.hs => M20260531_member_removed_at.hs} (56%) rename src/Simplex/Chat/Store/SQLite/Migrations/{M20260515_delivery_job_senders.hs => M20260529_delivery_job_senders.hs} (89%) rename src/Simplex/Chat/Store/SQLite/Migrations/{M20260520_client_services.hs => M20260530_client_services.hs} (56%) rename src/Simplex/Chat/Store/SQLite/Migrations/{M20260525_member_removed_at.hs => M20260531_member_removed_at.hs} (54%) diff --git a/simplex-chat.cabal b/simplex-chat.cabal index 20cad947bb..0116ddbc56 100644 --- a/simplex-chat.cabal +++ b/simplex-chat.cabal @@ -133,9 +133,9 @@ library Simplex.Chat.Store.Postgres.Migrations.M20260429_relay_request_retries Simplex.Chat.Store.Postgres.Migrations.M20260507_relay_inactive_at Simplex.Chat.Store.Postgres.Migrations.M20260514_relay_request_group_link_index - Simplex.Chat.Store.Postgres.Migrations.M20260515_delivery_job_senders - Simplex.Chat.Store.Postgres.Migrations.M20260520_client_services - Simplex.Chat.Store.Postgres.Migrations.M20260525_member_removed_at + Simplex.Chat.Store.Postgres.Migrations.M20260529_delivery_job_senders + Simplex.Chat.Store.Postgres.Migrations.M20260530_client_services + Simplex.Chat.Store.Postgres.Migrations.M20260531_member_removed_at else exposed-modules: Simplex.Chat.Archive @@ -291,9 +291,9 @@ library Simplex.Chat.Store.SQLite.Migrations.M20260429_relay_request_retries Simplex.Chat.Store.SQLite.Migrations.M20260507_relay_inactive_at Simplex.Chat.Store.SQLite.Migrations.M20260514_relay_request_group_link_index - Simplex.Chat.Store.SQLite.Migrations.M20260515_delivery_job_senders - Simplex.Chat.Store.SQLite.Migrations.M20260520_client_services - Simplex.Chat.Store.SQLite.Migrations.M20260525_member_removed_at + Simplex.Chat.Store.SQLite.Migrations.M20260529_delivery_job_senders + Simplex.Chat.Store.SQLite.Migrations.M20260530_client_services + Simplex.Chat.Store.SQLite.Migrations.M20260531_member_removed_at other-modules: Paths_simplex_chat hs-source-dirs: diff --git a/src/Simplex/Chat/Store/Postgres/Migrations.hs b/src/Simplex/Chat/Store/Postgres/Migrations.hs index 9e6376fa2b..792865a3d7 100644 --- a/src/Simplex/Chat/Store/Postgres/Migrations.hs +++ b/src/Simplex/Chat/Store/Postgres/Migrations.hs @@ -31,9 +31,9 @@ import Simplex.Chat.Store.Postgres.Migrations.M20260403_item_viewed import Simplex.Chat.Store.Postgres.Migrations.M20260429_relay_request_retries import Simplex.Chat.Store.Postgres.Migrations.M20260507_relay_inactive_at import Simplex.Chat.Store.Postgres.Migrations.M20260514_relay_request_group_link_index -import Simplex.Chat.Store.Postgres.Migrations.M20260515_delivery_job_senders -import Simplex.Chat.Store.Postgres.Migrations.M20260520_client_services -import Simplex.Chat.Store.Postgres.Migrations.M20260525_member_removed_at +import Simplex.Chat.Store.Postgres.Migrations.M20260529_delivery_job_senders +import Simplex.Chat.Store.Postgres.Migrations.M20260530_client_services +import Simplex.Chat.Store.Postgres.Migrations.M20260531_member_removed_at import Simplex.Messaging.Agent.Store.Shared (Migration (..)) schemaMigrations :: [(String, Text, Maybe Text)] @@ -65,9 +65,9 @@ schemaMigrations = ("20260429_relay_request_retries", m20260429_relay_request_retries, Just down_m20260429_relay_request_retries), ("20260507_relay_inactive_at", m20260507_relay_inactive_at, Just down_m20260507_relay_inactive_at), ("20260514_relay_request_group_link_index", m20260514_relay_request_group_link_index, Just down_m20260514_relay_request_group_link_index), - ("20260515_delivery_job_senders", m20260515_delivery_job_senders, Just down_m20260515_delivery_job_senders), - ("20260520_client_services", m20260520_client_services, Just down_m20260520_client_services), - ("20260525_member_removed_at", m20260525_member_removed_at, Just down_m20260525_member_removed_at) + ("20260529_delivery_job_senders", m20260529_delivery_job_senders, Just down_m20260529_delivery_job_senders), + ("20260530_client_services", m20260530_client_services, Just down_m20260530_client_services), + ("20260531_member_removed_at", m20260531_member_removed_at, Just down_m20260531_member_removed_at) ] -- | The list of migrations in ascending order by date diff --git a/src/Simplex/Chat/Store/Postgres/Migrations/M20260515_delivery_job_senders.hs b/src/Simplex/Chat/Store/Postgres/Migrations/M20260529_delivery_job_senders.hs similarity index 89% rename from src/Simplex/Chat/Store/Postgres/Migrations/M20260515_delivery_job_senders.hs rename to src/Simplex/Chat/Store/Postgres/Migrations/M20260529_delivery_job_senders.hs index d082587391..660e33561f 100644 --- a/src/Simplex/Chat/Store/Postgres/Migrations/M20260515_delivery_job_senders.hs +++ b/src/Simplex/Chat/Store/Postgres/Migrations/M20260529_delivery_job_senders.hs @@ -5,13 +5,13 @@ -- NULL means [] (sender-less jobs, e.g. DJRelayRemoved). One column carries -- single- and multi-sender jobs uniformly; the per-job introduction bits live -- in group_members.member_relations_vector (MRIntroduced). -module Simplex.Chat.Store.Postgres.Migrations.M20260515_delivery_job_senders where +module Simplex.Chat.Store.Postgres.Migrations.M20260529_delivery_job_senders where import Data.Text (Text) import Text.RawString.QQ (r) -m20260515_delivery_job_senders :: Text -m20260515_delivery_job_senders = +m20260529_delivery_job_senders :: Text +m20260529_delivery_job_senders = [r| DROP INDEX idx_delivery_jobs_single_sender_group_member_id; @@ -24,8 +24,8 @@ WHERE single_sender_group_member_id IS NOT NULL; ALTER TABLE delivery_jobs DROP COLUMN single_sender_group_member_id; |] -down_m20260515_delivery_job_senders :: Text -down_m20260515_delivery_job_senders = +down_m20260529_delivery_job_senders :: Text +down_m20260529_delivery_job_senders = [r| -- Pre-up the FK was ON DELETE CASCADE, so orphan delivery_jobs cannot -- exist. After up the FK was dropped and orphans may accumulate. Drop diff --git a/src/Simplex/Chat/Store/Postgres/Migrations/M20260520_client_services.hs b/src/Simplex/Chat/Store/Postgres/Migrations/M20260530_client_services.hs similarity index 57% rename from src/Simplex/Chat/Store/Postgres/Migrations/M20260520_client_services.hs rename to src/Simplex/Chat/Store/Postgres/Migrations/M20260530_client_services.hs index af567130eb..2a37f8f4e3 100644 --- a/src/Simplex/Chat/Store/Postgres/Migrations/M20260520_client_services.hs +++ b/src/Simplex/Chat/Store/Postgres/Migrations/M20260530_client_services.hs @@ -1,19 +1,19 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} -module Simplex.Chat.Store.Postgres.Migrations.M20260520_client_services where +module Simplex.Chat.Store.Postgres.Migrations.M20260530_client_services where import Data.Text (Text) import Text.RawString.QQ (r) -m20260520_client_services :: Text -m20260520_client_services = +m20260530_client_services :: Text +m20260530_client_services = [r| ALTER TABLE users ADD COLUMN client_service SMALLINT NOT NULL DEFAULT 0; |] -down_m20260520_client_services :: Text -down_m20260520_client_services = +down_m20260530_client_services :: Text +down_m20260530_client_services = [r| ALTER TABLE users DROP COLUMN client_service; |] diff --git a/src/Simplex/Chat/Store/Postgres/Migrations/M20260525_member_removed_at.hs b/src/Simplex/Chat/Store/Postgres/Migrations/M20260531_member_removed_at.hs similarity index 56% rename from src/Simplex/Chat/Store/Postgres/Migrations/M20260525_member_removed_at.hs rename to src/Simplex/Chat/Store/Postgres/Migrations/M20260531_member_removed_at.hs index 6099751702..9dde712f0b 100644 --- a/src/Simplex/Chat/Store/Postgres/Migrations/M20260525_member_removed_at.hs +++ b/src/Simplex/Chat/Store/Postgres/Migrations/M20260531_member_removed_at.hs @@ -1,19 +1,19 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} -module Simplex.Chat.Store.Postgres.Migrations.M20260525_member_removed_at where +module Simplex.Chat.Store.Postgres.Migrations.M20260531_member_removed_at where import Data.Text (Text) import Text.RawString.QQ (r) -m20260525_member_removed_at :: Text -m20260525_member_removed_at = +m20260531_member_removed_at :: Text +m20260531_member_removed_at = [r| ALTER TABLE group_members ADD COLUMN removed_at TIMESTAMPTZ; |] -down_m20260525_member_removed_at :: Text -down_m20260525_member_removed_at = +down_m20260531_member_removed_at :: Text +down_m20260531_member_removed_at = [r| ALTER TABLE group_members DROP COLUMN removed_at; |] diff --git a/src/Simplex/Chat/Store/Postgres/Migrations/chat_schema.sql b/src/Simplex/Chat/Store/Postgres/Migrations/chat_schema.sql index cd38c3f8c2..286fc63a4c 100644 --- a/src/Simplex/Chat/Store/Postgres/Migrations/chat_schema.sql +++ b/src/Simplex/Chat/Store/Postgres/Migrations/chat_schema.sql @@ -963,7 +963,7 @@ CREATE TABLE test_chat_schema.groups ( public_member_count bigint, relay_request_retries bigint DEFAULT 0 NOT NULL, relay_request_delay bigint DEFAULT 0 NOT NULL, - relay_request_execute_at timestamp with time zone DEFAULT '1970-01-01 04:00:00+04'::timestamp with time zone NOT NULL, + relay_request_execute_at timestamp with time zone DEFAULT '1970-01-01 01:00:00+01'::timestamp with time zone NOT NULL, relay_inactive_at timestamp with time zone ); diff --git a/src/Simplex/Chat/Store/SQLite/Migrations.hs b/src/Simplex/Chat/Store/SQLite/Migrations.hs index 3430409fb8..a3c8e8eea7 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations.hs +++ b/src/Simplex/Chat/Store/SQLite/Migrations.hs @@ -154,9 +154,9 @@ import Simplex.Chat.Store.SQLite.Migrations.M20260403_item_viewed import Simplex.Chat.Store.SQLite.Migrations.M20260429_relay_request_retries import Simplex.Chat.Store.SQLite.Migrations.M20260507_relay_inactive_at import Simplex.Chat.Store.SQLite.Migrations.M20260514_relay_request_group_link_index -import Simplex.Chat.Store.SQLite.Migrations.M20260515_delivery_job_senders -import Simplex.Chat.Store.SQLite.Migrations.M20260520_client_services -import Simplex.Chat.Store.SQLite.Migrations.M20260525_member_removed_at +import Simplex.Chat.Store.SQLite.Migrations.M20260529_delivery_job_senders +import Simplex.Chat.Store.SQLite.Migrations.M20260530_client_services +import Simplex.Chat.Store.SQLite.Migrations.M20260531_member_removed_at import Simplex.Messaging.Agent.Store.Shared (Migration (..)) schemaMigrations :: [(String, Query, Maybe Query)] @@ -311,9 +311,9 @@ schemaMigrations = ("20260429_relay_request_retries", m20260429_relay_request_retries, Just down_m20260429_relay_request_retries), ("20260507_relay_inactive_at", m20260507_relay_inactive_at, Just down_m20260507_relay_inactive_at), ("20260514_relay_request_group_link_index", m20260514_relay_request_group_link_index, Just down_m20260514_relay_request_group_link_index), - ("20260515_delivery_job_senders", m20260515_delivery_job_senders, Just down_m20260515_delivery_job_senders), - ("20260520_client_services", m20260520_client_services, Just down_m20260520_client_services), - ("20260525_member_removed_at", m20260525_member_removed_at, Just down_m20260525_member_removed_at) + ("20260529_delivery_job_senders", m20260529_delivery_job_senders, Just down_m20260529_delivery_job_senders), + ("20260530_client_services", m20260530_client_services, Just down_m20260530_client_services), + ("20260531_member_removed_at", m20260531_member_removed_at, Just down_m20260531_member_removed_at) ] -- | The list of migrations in ascending order by date diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/M20260515_delivery_job_senders.hs b/src/Simplex/Chat/Store/SQLite/Migrations/M20260529_delivery_job_senders.hs similarity index 89% rename from src/Simplex/Chat/Store/SQLite/Migrations/M20260515_delivery_job_senders.hs rename to src/Simplex/Chat/Store/SQLite/Migrations/M20260529_delivery_job_senders.hs index 67a9ae31e8..9346b16128 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/M20260515_delivery_job_senders.hs +++ b/src/Simplex/Chat/Store/SQLite/Migrations/M20260529_delivery_job_senders.hs @@ -4,13 +4,13 @@ -- NULL means [] (sender-less jobs, e.g. DJRelayRemoved). One column carries -- single- and multi-sender jobs uniformly; the per-job introduction bits live -- in group_members.member_relations_vector (MRIntroduced). -module Simplex.Chat.Store.SQLite.Migrations.M20260515_delivery_job_senders where +module Simplex.Chat.Store.SQLite.Migrations.M20260529_delivery_job_senders where import Database.SQLite.Simple (Query) import Database.SQLite.Simple.QQ (sql) -m20260515_delivery_job_senders :: Query -m20260515_delivery_job_senders = +m20260529_delivery_job_senders :: Query +m20260529_delivery_job_senders = [sql| DROP INDEX idx_delivery_jobs_single_sender_group_member_id; @@ -23,8 +23,8 @@ WHERE single_sender_group_member_id IS NOT NULL; ALTER TABLE delivery_jobs DROP COLUMN single_sender_group_member_id; |] -down_m20260515_delivery_job_senders :: Query -down_m20260515_delivery_job_senders = +down_m20260529_delivery_job_senders :: Query +down_m20260529_delivery_job_senders = [sql| -- Pre-up the FK was ON DELETE CASCADE, so orphan delivery_jobs cannot -- exist. After up the FK was dropped and orphans may accumulate. Drop diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/M20260520_client_services.hs b/src/Simplex/Chat/Store/SQLite/Migrations/M20260530_client_services.hs similarity index 56% rename from src/Simplex/Chat/Store/SQLite/Migrations/M20260520_client_services.hs rename to src/Simplex/Chat/Store/SQLite/Migrations/M20260530_client_services.hs index db141d6c03..d65f8f1c67 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/M20260520_client_services.hs +++ b/src/Simplex/Chat/Store/SQLite/Migrations/M20260530_client_services.hs @@ -1,18 +1,18 @@ {-# LANGUAGE QuasiQuotes #-} -module Simplex.Chat.Store.SQLite.Migrations.M20260520_client_services where +module Simplex.Chat.Store.SQLite.Migrations.M20260530_client_services where import Database.SQLite.Simple (Query) import Database.SQLite.Simple.QQ (sql) -m20260520_client_services :: Query -m20260520_client_services = +m20260530_client_services :: Query +m20260530_client_services = [sql| ALTER TABLE users ADD COLUMN client_service INTEGER NOT NULL DEFAULT 0; |] -down_m20260520_client_services :: Query -down_m20260520_client_services = +down_m20260530_client_services :: Query +down_m20260530_client_services = [sql| ALTER TABLE users DROP COLUMN client_service; |] diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/M20260525_member_removed_at.hs b/src/Simplex/Chat/Store/SQLite/Migrations/M20260531_member_removed_at.hs similarity index 54% rename from src/Simplex/Chat/Store/SQLite/Migrations/M20260525_member_removed_at.hs rename to src/Simplex/Chat/Store/SQLite/Migrations/M20260531_member_removed_at.hs index 704950b3fb..c63e6a37f9 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/M20260525_member_removed_at.hs +++ b/src/Simplex/Chat/Store/SQLite/Migrations/M20260531_member_removed_at.hs @@ -1,18 +1,18 @@ {-# LANGUAGE QuasiQuotes #-} -module Simplex.Chat.Store.SQLite.Migrations.M20260525_member_removed_at where +module Simplex.Chat.Store.SQLite.Migrations.M20260531_member_removed_at where import Database.SQLite.Simple (Query) import Database.SQLite.Simple.QQ (sql) -m20260525_member_removed_at :: Query -m20260525_member_removed_at = +m20260531_member_removed_at :: Query +m20260531_member_removed_at = [sql| ALTER TABLE group_members ADD COLUMN removed_at TEXT; |] -down_m20260525_member_removed_at :: Query -down_m20260525_member_removed_at = +down_m20260531_member_removed_at :: Query +down_m20260531_member_removed_at = [sql| ALTER TABLE group_members DROP COLUMN removed_at; |] diff --git a/tests/PostgresSchemaDump.hs b/tests/PostgresSchemaDump.hs index 197e9a9b89..0cd79ac513 100644 --- a/tests/PostgresSchemaDump.hs +++ b/tests/PostgresSchemaDump.hs @@ -80,5 +80,5 @@ skipComparisonForDownMigrations = -- group_member_intro_id field moves "20251128_migrate_member_relations", -- on down migration single_sender_group_member_id column is re-added at the end of the table - "20260515_delivery_job_senders" + "20260529_delivery_job_senders" ] diff --git a/tests/SchemaDump.hs b/tests/SchemaDump.hs index bc74f3ec33..2336fd56dd 100644 --- a/tests/SchemaDump.hs +++ b/tests/SchemaDump.hs @@ -145,7 +145,7 @@ skipComparisonForDownMigrations = -- on down migration single_sender_group_member_id column and its index -- are re-added at the end of the table / file (ALTER TABLE ADD COLUMN -- appends; CREATE INDEX appends). - "20260515_delivery_job_senders" + "20260529_delivery_job_senders" ] getSchema :: FilePath -> FilePath -> IO String From 6538b1527045716194f28bc15eda826b34a5fd3b Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 31 May 2026 10:38:12 +0100 Subject: [PATCH 13/20] Revert "cli: fix redraw slowness (#6735)" This reverts commit b801d77c74f0b688000e0bc2194e835bd1d1965e. --- plans/cli-paste-slowness.md | 111 ----------------------------- src/Simplex/Chat/Terminal.hs | 10 +-- src/Simplex/Chat/Terminal/Input.hs | 14 ++-- 3 files changed, 8 insertions(+), 127 deletions(-) delete mode 100644 plans/cli-paste-slowness.md diff --git a/plans/cli-paste-slowness.md b/plans/cli-paste-slowness.md deleted file mode 100644 index 33255996be..0000000000 --- a/plans/cli-paste-slowness.md +++ /dev/null @@ -1,111 +0,0 @@ -# CLI terminal: event loss root cause analysis - -## Two distinct problems - -### Problem 1: Paste — TMVar capacity-1 bottleneck - -When copy-pasting text, the capacity-1 `TMVar` event channel between the keyboard input reader and the consumer loop throttles stdin reading to terminal redraw speed. - -**Root cause:** `events <- liftIO newEmptyTMVarIO` (`Platform.hsc:64`). Producer blocks on `putTMVar` after each event until consumer finishes redrawing. Consumer does a full terminal redraw per event (`Input.hs:161`). - -**Fix:** Replace `TMVar` with `TQueue` in `Platform.hsc` (6 line changes on POSIX, matching changes on Windows). Decouples producer from consumer — stdin is drained at full speed regardless of redraw speed. - -See previous analysis in git history for full details on this issue. - ---- - -### Problem 2: Heavy load — `outputQ` backpressure blocks `agentSubscriber` - -When the CLI is used as a heavy client (e.g., 1M connections), incoming chat events overwhelm the terminal display, causing cascading backpressure that blocks message acknowledgments and stalls the entire event processing pipeline. - -**This is the more severe problem.** It causes actual message loss at the protocol level, not just UI slowness. - -## Root cause: bounded `outputQ` + single-threaded `agentSubscriber` - -### The queue chain - -``` -Network (SMP/XFTP connections) - → agent internal queues - → subQ (TBQueue, capacity 1024) ← agent → chat boundary - → agentSubscriber (single-threaded) ← Commands.hs:4167 - → processAgentMessage ← Subscriber.hs:109 - → toView_ → writeTBQueue outputQ ← Controller.hs:1528, BLOCKS when full - → outputQ (TBQueue, capacity 1024) ← Chat.hs:152 - → runTerminalOutput ← Output.hs:146 - → printToTerminal (acquires termLock) ← Output.hs:298-303 - → terminal I/O (slow) -``` - -All queues are bounded `TBQueue` with default capacity 1024 (`Options.hs:226`). All writes use `writeTBQueue` which **blocks when full** — no events are dropped within the application, but backpressure cascades upstream. - -### The blocking chain under heavy load - -1. **Terminal I/O is the bottleneck.** `runTerminalOutput` (`Output.hs:146`) reads one event at a time from `outputQ`, acquires `termLock`, prints the message + redraws input, releases lock. Each iteration involves ANSI escape sequences, cursor manipulation, and `flush` syscalls. Throughput: ~hundreds of events/sec at best. - -2. **`outputQ` fills up.** With 1M connections generating events, the arrival rate far exceeds terminal display speed. The 1024-element TBQueue fills in seconds. - -3. **`toView_` blocks.** `Controller.hs:1528`: `writeTBQueue localQ (Nothing, event)` blocks when the queue is full. This call happens inside `processAgentMessage` → `processAgentMessageConn`, which runs within the `agentSubscriber` loop. - -4. **`agentSubscriber` blocks — head-of-line blocking.** `Commands.hs:4164-4167`: - ```haskell - agentSubscriber = do - q <- asks $ subQ . smpAgent - forever (atomically (readTBQueue q) >>= process) - ``` - Single-threaded. When `process` blocks on `toView_`, ALL events for ALL connections queue up behind it. Events for 1M other connections — including time-critical ACKs, keepalives, and handshakes — are stuck. - -5. **ACKs are never sent.** The message receive path (`Subscriber.hs:1537-1540`) calls `toView` BEFORE `ackMsg`: - ```haskell - -- Inside withAckMessage's action: - saveRcvChatItem' ... -- save to DB (succeeds) - toView $ CEvtNewChatItems ... -- BLOCKS here (outputQ full) - -- returns (withRcpt, shouldDelConns) - - -- After action returns (Subscriber.hs:1396-1397): - ackMsg msgMeta ... -- NEVER REACHED while toView blocks - ``` - The developers explicitly acknowledge this at `Subscriber.hs:122-123`: - > *without ACK the message delivery will be stuck* - -6. **`subQ` fills up.** The agent can't deliver events to `subQ` (also capacity 1024) because `agentSubscriber` isn't reading. Agent-level processing stalls. - -7. **Network-level failure.** Connections time out due to unprocessed keepalives and unacknowledged messages. Messages are lost at the protocol level. - -### `termLock` contention worsens the bottleneck - -`termLock` (`Output.hs:55`) is a `TMVar ()` mutex shared between: -- **Output thread** (`runTerminalOutput` → `printToTerminal`): acquires lock for each displayed message -- **Input thread** (`receiveFromTTY` → `updateInput`): acquires lock after each keystroke -- **Live prompt thread** (`blinkLivePrompt` → `updateInputView`): acquires lock every 1 second - -Under heavy load, the output thread dominates the lock (constant stream of messages). The input thread is starved — user keystrokes are delayed. This also slows the output thread itself (lock contention overhead). - -Note: `withTermLock` (`Output.hs:138-142`) is not exception-safe — no `bracket`/`finally`. If the action throws, the lock leaks and all threads deadlock. - -### Error reporting also blocks - -When `processAgentMessage` encounters an error, the error handler (`Commands.hs:4179`) calls `eToView'` → `toView_` → `writeTBQueue outputQ`. If `outputQ` is already full, even error reporting blocks. There is no escape path. - -## Impact summary - -| Load level | `outputQ` state | Effect | -|---|---|---| -| Light (few connections) | Nearly empty | No issues | -| Moderate (hundreds) | Partially filled | Occasional display lag | -| Heavy (thousands+) | Full (1024) | `toView_` blocks → `agentSubscriber` blocks → head-of-line blocking for ALL connections → ACKs delayed → message delivery stuck | -| Extreme (1M connections) | Permanently full | Cascading failure: all event processing stops, connections time out, messages lost at protocol level | - -## Fix - -The core fix: **`toView_` must never block the event processing pipeline on terminal display.** - -Options (in order of simplicity): - -1. **Make `outputQ` unbounded** — replace `TBQueue` with `TQueue` in `Chat.hs:152`. `writeTQueue` never blocks. Events accumulate in memory under heavy load but the event processing pipeline (including ACKs) is never stalled. Tradeoff: unbounded memory growth under sustained heavy load. - -2. **Non-blocking write with drop** — use `tryWriteTBQueue` in `toView_`. When `outputQ` is full, drop the display event (or a coalesced summary). ACKs and network processing proceed unblocked. Tradeoff: some events not displayed, but none lost at protocol level. - -3. **Separate ACK from display** — restructure `withAckMessage` to send ACK immediately after DB save, before `toView`. This decouples protocol correctness from display. `toView` can still block, but ACKs are always timely. Tradeoff: requires careful restructuring of the message processing path. - -4. **Increase queue capacity** — increase `tbqSize` from 1024 to a larger value. Delays the problem but doesn't fix it. Under sustained heavy load, any finite queue eventually fills. diff --git a/src/Simplex/Chat/Terminal.hs b/src/Simplex/Chat/Terminal.hs index 29299cfeae..21781229e4 100644 --- a/src/Simplex/Chat/Terminal.hs +++ b/src/Simplex/Chat/Terminal.hs @@ -8,7 +8,6 @@ module Simplex.Chat.Terminal where import Control.Monad -import Control.Monad.IO.Class (liftIO) import qualified Data.List.NonEmpty as L import Simplex.Chat (defaultChatConfig) import Simplex.Chat.Controller @@ -23,8 +22,6 @@ import Simplex.Chat.Terminal.Output import Simplex.FileTransfer.Client.Presets (defaultXFTPServers) import Simplex.Messaging.Client (NetworkConfig (..), SMPProxyFallback (..), SMPProxyMode (..), defaultNetworkConfig) import Simplex.Messaging.Util (raceAny_) -import System.Terminal (Key, Modifiers) -import UnliftIO.STM #if !defined(dbPostgres) import Control.Exception (handle, throwIO) import qualified Data.ByteArray as BA @@ -102,9 +99,4 @@ simplexChatTerminal cfg options t = run options #endif runChatTerminal :: ChatTerminal -> ChatController -> ChatOpts -> IO () -runChatTerminal ct cc opts = do - keyQ <- newTQueueIO - raceAny_ [runKeyReader ct keyQ, runTerminalInput ct cc keyQ, runTerminalOutput ct cc opts, runInputLoop ct cc] - -runKeyReader :: ChatTerminal -> TQueue (Key, Modifiers) -> IO () -runKeyReader ct q = withChatTerm ct $ forever $ getKey >>= liftIO . atomically . writeTQueue q +runChatTerminal ct cc opts = raceAny_ [runTerminalInput ct cc, runTerminalOutput ct cc opts, runInputLoop ct cc] diff --git a/src/Simplex/Chat/Terminal/Input.hs b/src/Simplex/Chat/Terminal/Input.hs index effcb7a71c..e0ee10aff9 100644 --- a/src/Simplex/Chat/Terminal/Input.hs +++ b/src/Simplex/Chat/Terminal/Input.hs @@ -152,14 +152,14 @@ sendUpdatedLiveMessage cc sentMsg LiveMessage {chatName, chatItemId} live = do let cmd = UpdateLiveMessage chatName chatItemId live $ T.pack sentMsg execChatCommand' cmd 0 `runReaderT` cc -runTerminalInput :: ChatTerminal -> ChatController -> TQueue (Key, Modifiers) -> IO () -runTerminalInput ct cc keyQ = do - updateInputView ct - receiveFromTTY keyQ cc ct +runTerminalInput :: ChatTerminal -> ChatController -> IO () +runTerminalInput ct cc = withChatTerm ct $ do + updateInput ct + receiveFromTTY cc ct -receiveFromTTY :: TQueue (Key, Modifiers) -> ChatController -> ChatTerminal -> IO () -receiveFromTTY keyQ cc@ChatController {inputQ, currentUser, currentRemoteHost, chatStore} ct@ChatTerminal {termSize, termState, liveMessageState, activeTo} = - forever $ atomically (readTQueue keyQ) >>= processKey >> updateInputView ct +receiveFromTTY :: forall m. MonadTerminal m => ChatController -> ChatTerminal -> m () +receiveFromTTY cc@ChatController {inputQ, currentUser, currentRemoteHost, chatStore} ct@ChatTerminal {termSize, termState, liveMessageState, activeTo} = + forever $ getKey >>= liftIO . processKey >> withTermLock ct (updateInput ct) where processKey :: (Key, Modifiers) -> IO () processKey key = case key of From 9bb2bec3fa00b3159bdea6ea63c439d7ab92c374 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Sun, 31 May 2026 17:12:12 +0100 Subject: [PATCH 14/20] plan: web previews for channels (#7022) * plan: web previews for channels * types for recipient side to support channel web previews and domain names * fix * migrations * update schema and api types * update schema * rename migrations * core: check member role --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --- apps/ios/SimpleXChat/ChatTypes.swift | 13 + .../chat/simplex/common/model/ChatModel.kt | 19 +- bots/api/TYPES.md | 23 + bots/src/API/Docs/Types.hs | 4 + .../types/typescript/src/types.ts | 13 + .../src/simplex_chat/types/_types.py | 11 + plans/2026-05-25-channel-web-preview.md | 596 ++++++++++++++++++ simplex-chat.cabal | 2 + src/Simplex/Chat/Library/Commands.hs | 6 +- src/Simplex/Chat/Library/Internal.hs | 3 +- src/Simplex/Chat/Library/Subscriber.hs | 12 +- src/Simplex/Chat/Operators.hs | 5 +- src/Simplex/Chat/Protocol.hs | 29 +- src/Simplex/Chat/Store/Connections.hs | 1 + src/Simplex/Chat/Store/Groups.hs | 41 +- src/Simplex/Chat/Store/Postgres/Migrations.hs | 4 +- .../M20260515_public_group_access.hs | 29 + .../Store/Postgres/Migrations/chat_schema.sql | 11 +- src/Simplex/Chat/Store/SQLite/Migrations.hs | 4 +- .../M20260515_public_group_access.hs | 28 + .../SQLite/Migrations/chat_query_plans.txt | 25 +- .../Store/SQLite/Migrations/chat_schema.sql | 8 +- src/Simplex/Chat/Store/Shared.hs | 32 +- src/Simplex/Chat/Types.hs | 13 +- tests/ChatTests/Groups.hs | 6 + 25 files changed, 894 insertions(+), 44 deletions(-) create mode 100644 plans/2026-05-25-channel-web-preview.md create mode 100644 src/Simplex/Chat/Store/Postgres/Migrations/M20260515_public_group_access.hs create mode 100644 src/Simplex/Chat/Store/SQLite/Migrations/M20260515_public_group_access.hs diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index d2e28394a2..7181ba2de0 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -2531,10 +2531,22 @@ public enum GroupType: Codable, Hashable { } } +public struct PublicGroupAccess: Codable, Hashable { + public var groupWebPage: String? + public var groupDomain: String? + public var domainWebPage: Bool = false + public var allowEmbedding: Bool = false +} + +public struct RelayCapabilities: Codable, Hashable { + public var baseWebUrl: String? +} + public struct PublicGroupProfile: Codable, Hashable { public var groupType: GroupType public var groupLink: String public var publicGroupId: String + public var publicGroupAccess: PublicGroupAccess? } public struct GroupProfile: Codable, NamedChat, Hashable { @@ -2703,6 +2715,7 @@ public struct GroupRelay: Identifiable, Decodable, Equatable, Hashable { public var userChatRelay: UserChatRelay public var relayStatus: RelayStatus public var relayLink: String? + public var relayCap: RelayCapabilities public var id: Int64 { groupRelayId } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 4bb7ae3d6e..8ecfa0fd93 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -2209,11 +2209,25 @@ object GroupTypeSerializer : KSerializer { } } +@Serializable +data class PublicGroupAccess( + val groupWebPage: String? = null, + val groupDomain: String? = null, + val domainWebPage: Boolean = false, + val allowEmbedding: Boolean = false +) + +@Serializable +data class RelayCapabilities( + val baseWebUrl: String? = null +) + @Serializable data class PublicGroupProfile( val groupType: GroupType, val groupLink: String, - val publicGroupId: String + val publicGroupId: String, + val publicGroupAccess: PublicGroupAccess? = null ) @Serializable @@ -2337,7 +2351,8 @@ data class GroupRelay( val groupMemberId: Long, val userChatRelay: UserChatRelay, val relayStatus: RelayStatus, - val relayLink: String? = null + val relayLink: String? = null, + val relayCap: RelayCapabilities ) { val id: Long get() = groupRelayId } diff --git a/bots/api/TYPES.md b/bots/api/TYPES.md index 488b7c4f05..4875079749 100644 --- a/bots/api/TYPES.md +++ b/bots/api/TYPES.md @@ -146,6 +146,7 @@ This file is generated automatically. - [Profile](#profile) - [ProxyClientError](#proxyclienterror) - [ProxyError](#proxyerror) +- [PublicGroupAccess](#publicgroupaccess) - [PublicGroupData](#publicgroupdata) - [PublicGroupProfile](#publicgroupprofile) - [RCErrorType](#rcerrortype) @@ -157,6 +158,7 @@ This file is generated automatically. - [RcvFileTransfer](#rcvfiletransfer) - [RcvGroupEvent](#rcvgroupevent) - [RcvMsgError](#rcvmsgerror) +- [RelayCapabilities](#relaycapabilities) - [RelayProfile](#relayprofile) - [RelayStatus](#relaystatus) - [ReportReason](#reportreason) @@ -2499,6 +2501,7 @@ UpdateRequired: - userChatRelay: [UserChatRelay](#userchatrelay) - relayStatus: [RelayStatus](#relaystatus) - relayLink: string? +- relayCap: [RelayCapabilities](#relaycapabilities) --- @@ -3068,6 +3071,17 @@ NO_SESSION: - type: "NO_SESSION" +--- + +## PublicGroupAccess + +**Record type**: +- groupWebPage: string? +- groupDomain: string? +- domainWebPage: bool +- allowEmbedding: bool + + --- ## PublicGroupData @@ -3084,6 +3098,7 @@ NO_SESSION: - groupType: [GroupType](#grouptype) - groupLink: string - publicGroupId: string +- publicGroupAccess: [PublicGroupAccess](#publicgroupaccess)? --- @@ -3341,6 +3356,14 @@ ParseError: - parseError: string +--- + +## RelayCapabilities + +**Record type**: +- baseWebUrl: string? + + --- ## RelayProfile diff --git a/bots/src/API/Docs/Types.hs b/bots/src/API/Docs/Types.hs index c759a7453c..8397503bbe 100644 --- a/bots/src/API/Docs/Types.hs +++ b/bots/src/API/Docs/Types.hs @@ -327,6 +327,7 @@ chatTypesDocsData = (sti @Profile, STRecord, "", [], "", ""), (sti @ProxyClientError, STUnion, "Proxy", [], "", ""), (sti @ProxyError, STUnion, "", [], "", ""), + (sti @PublicGroupAccess, STRecord, "", [], "", ""), (sti @PublicGroupData, STRecord, "", [], "", ""), (sti @PublicGroupProfile, STRecord, "", [], "", ""), (sti @RatchetSyncState, STEnum, "RS", [], "", ""), @@ -338,6 +339,7 @@ chatTypesDocsData = (sti @RcvFileTransfer, STRecord, "", [], "", ""), (sti @RcvGroupEvent, STUnion, "RGE", [], "", ""), (sti @RcvMsgError, STUnion, "RME", [], "", ""), + (sti @RelayCapabilities, STRecord, "", [], "", ""), (sti @RelayProfile, STRecord, "", [], "", ""), (sti @RelayStatus, STEnum, "RS", [], "", ""), (sti @ReportReason, STEnum' (dropPfxSfx "RR" ""), "", ["RRUnknown"], "", ""), @@ -546,6 +548,7 @@ deriving instance Generic PreparedGroup deriving instance Generic Profile deriving instance Generic ProxyClientError deriving instance Generic ProxyError +deriving instance Generic PublicGroupAccess deriving instance Generic PublicGroupData deriving instance Generic PublicGroupProfile deriving instance Generic RatchetSyncState @@ -557,6 +560,7 @@ deriving instance Generic RcvFileStatus deriving instance Generic RcvFileTransfer deriving instance Generic RcvGroupEvent deriving instance Generic RcvMsgError +deriving instance Generic RelayCapabilities deriving instance Generic RelayProfile deriving instance Generic RelayStatus deriving instance Generic ReportReason diff --git a/packages/simplex-chat-client/types/typescript/src/types.ts b/packages/simplex-chat-client/types/typescript/src/types.ts index 6a230ecf15..f0cf58de64 100644 --- a/packages/simplex-chat-client/types/typescript/src/types.ts +++ b/packages/simplex-chat-client/types/typescript/src/types.ts @@ -2776,6 +2776,7 @@ export interface GroupRelay { userChatRelay: UserChatRelay relayStatus: RelayStatus relayLink?: string + relayCap: RelayCapabilities } export type GroupRootKey = GroupRootKey.Private | GroupRootKey.Public @@ -3356,6 +3357,13 @@ export namespace ProxyError { } } +export interface PublicGroupAccess { + groupWebPage?: string + groupDomain?: string + domainWebPage: boolean + allowEmbedding: boolean +} + export interface PublicGroupData { publicMemberCount: number // int64 } @@ -3364,6 +3372,7 @@ export interface PublicGroupProfile { groupType: GroupType groupLink: string publicGroupId: string + publicGroupAccess?: PublicGroupAccess } export type RCErrorType = @@ -3752,6 +3761,10 @@ export namespace RcvMsgError { } } +export interface RelayCapabilities { + baseWebUrl?: string +} + export interface RelayProfile { displayName: string fullName: string diff --git a/packages/simplex-chat-python/src/simplex_chat/types/_types.py b/packages/simplex-chat-python/src/simplex_chat/types/_types.py index c3a5e0c4fb..08308ed2d4 100644 --- a/packages/simplex-chat-python/src/simplex_chat/types/_types.py +++ b/packages/simplex-chat-python/src/simplex_chat/types/_types.py @@ -1949,6 +1949,7 @@ class GroupRelay(TypedDict): userChatRelay: "UserChatRelay" relayStatus: "RelayStatus" relayLink: NotRequired[str] + relayCap: "RelayCapabilities" class GroupRootKey_private(TypedDict): type: Literal["private"] @@ -2356,6 +2357,12 @@ ProxyError = ProxyError_PROTOCOL | ProxyError_BROKER | ProxyError_BASIC_AUTH | P ProxyError_Tag = Literal["PROTOCOL", "BROKER", "BASIC_AUTH", "NO_SESSION"] +class PublicGroupAccess(TypedDict): + groupWebPage: NotRequired[str] + groupDomain: NotRequired[str] + domainWebPage: bool + allowEmbedding: bool + class PublicGroupData(TypedDict): publicMemberCount: int # int64 @@ -2363,6 +2370,7 @@ class PublicGroupProfile(TypedDict): groupType: "GroupType" groupLink: str publicGroupId: str + publicGroupAccess: NotRequired["PublicGroupAccess"] class RCErrorType_internal(TypedDict): type: Literal["internal"] @@ -2631,6 +2639,9 @@ RcvMsgError = RcvMsgError_dropped | RcvMsgError_parseError RcvMsgError_Tag = Literal["dropped", "parseError"] +class RelayCapabilities(TypedDict): + baseWebUrl: NotRequired[str] + class RelayProfile(TypedDict): displayName: str fullName: str diff --git a/plans/2026-05-25-channel-web-preview.md b/plans/2026-05-25-channel-web-preview.md new file mode 100644 index 0000000000..de0f02506b --- /dev/null +++ b/plans/2026-05-25-channel-web-preview.md @@ -0,0 +1,596 @@ +# Channel Web Preview + +## Context + +SimpleX channels are public - anybody with the link to join and chat relays rebroadcasting the messages can see content. To grow channels, owners need a public web preview (like Telegram's `t.me/s/channelname`) showing the last 50 messages. This lets potential subscribers browse before joining. + +The relay already stores all messages in its database. The web preview is a periodic read-and-render loop that writes JSON files served by Caddy, with CORS controlling which domains can embed the preview. + +This feature integrates with the `.simplex` namespace (ENS-based names resolving to channel links). A channel's registered domain (`groupDomain`) lives in `PublicGroupAccess` inside `PublicGroupProfile` and is disseminated with the profile. On-chain verification of the domain is deferred until RSLV resolution protocol ships. + +## Architecture + +``` +simplex-chat CLI (--relay --web-json-dir=... --web-base-url=...) + ├── Main chat loop (existing) + ├── Relay logic (existing, gated by --relay) + └── Web preview thread (new, gated by relayWebOptions) + ├── Periodic: load publishable groups → render JSON → write files + └── Regenerate Caddy CORS config → caddy reload + +Caddy (operator-configured) + ├── Serves JSON at /.json + └── Imports generated CORS config file + +Channel page (static HTML+JS, hosted by owner or on GitHub) + ├── Fetches JSON from relay(s) with fallback + └── Renders messages, shows join button +``` + +## Data Model Changes + +### 1. Extend `PublicGroupProfile` with domain and web access settings + +**File:** `src/Simplex/Chat/Types.hs` (line 796) + +Current: +```haskell +data PublicGroupProfile = PublicGroupProfile + { groupType :: GroupType, + groupLink :: ShortLinkContact, + publicGroupId :: B64UrlByteString + } +``` + +New: +```haskell +data PublicGroupAccess = PublicGroupAccess + { groupWebPage :: Maybe Text, -- channel's web page URL (adds CORS origin) + groupDomain :: Maybe Text, -- domain for this channel (must have link set in domain record in the contract) + domainWebPage :: Bool, -- show on the domain's page (e.g. simplexnetwork.org site for simplex TLD domains, or domain site for web domains) + allowEmbeding :: Bool -- allow embedding from any origin (CORS: *) + } + +data PublicGroupProfile = PublicGroupProfile + { groupType :: GroupType, + groupLink :: ShortLinkContact, + publicGroupId :: B64UrlByteString, + publicGroupAccess :: Maybe PublicGroupAccess -- NEW: web preview settings + } +``` + +`groupDomain` stores the channel's registered `.simplex` domain name or another supported TLD. It is: +- Set by the owner after registering a name on-chain +- Disseminated to all members via `GroupProfile` (nested in `publicGroup`) +- Used by `simplexnetwork.org/c/` to route to the channel's web preview (for .simplex domain) + +JSON instances: TH-derived `$(JQ.deriveJSON defaultJSON ''PublicGroupAccess)`. Existing `$(JQ.deriveJSON defaultJSON ''PublicGroupProfile)` covers the new optional field. + +**Migration (SQLite/Postgres):** separate columns, same pattern as `group_type`/`group_link`/`public_group_id`: +```sql +ALTER TABLE group_profiles ADD COLUMN group_web_page TEXT; +ALTER TABLE group_profiles ADD COLUMN group_domain TEXT; +ALTER TABLE group_profiles ADD COLUMN domain_web_page INTEGER; +ALTER TABLE group_profiles ADD COLUMN allow_embedding INTEGER; +ALTER TABLE group_profiles ADD COLUMN group_domain_verified_at TEXT; +``` + +`group_domain_verified_at` is relay-local verification state (nullable timestamp, NULL = unverified). + +**Store changes:** + +`src/Simplex/Chat/Store/Shared.hs` line 693 - new constructor alongside `toPublicGroupProfile`: +```haskell +toPublicGroupAccess :: Maybe Text -> Maybe Text -> Maybe BoolInt -> Maybe BoolInt -> Maybe PublicGroupAccess +toPublicGroupAccess groupWebPage groupDomain domainWebPage_ allowEmbeding_ + | isJust groupWebPage || isJust groupDomain || fromBI domainWebPage_ || fromBI allowEmbeding_ = + Just PublicGroupAccess {groupWebPage, groupDomain, domainWebPage = fromBI domainWebPage_, allowEmbeding = fromBI allowEmbeding_} + | otherwise = Nothing + where fromBI = maybe False unBI +``` + +Extend `toPublicGroupProfile` to accept and pass through `Maybe PublicGroupAccess`. + +`GroupInfoRow` type (line 668) gains columns for: `group_web_page`, `group_domain`, `domain_web_page`, `allow_embedding`, `group_domain_verified_at`. + +`src/Simplex/Chat/Store/Groups.hs`: +- INSERT (line 367): add all new columns +- SELECT (line 2375): add `gp.group_web_page`, `gp.group_domain`, `gp.domain_web_page`, `gp.allow_embedding`, `gp.group_domain_verified_at` +- UPDATE (line 1922): include new columns in `updateGroupProfile_` + +### 2. `RelayCapabilities` record, extend `XGrpRelayAcpt`, new `XGrpRelayCap` + +**File:** `src/Simplex/Chat/Protocol.hs` + +New record for relay capabilities (extensible for future fields): +```haskell +data RelayCapabilities = RelayCapabilities + { baseWebUrl :: Maybe Text + } +``` + +TH-derived JSON. All fields optional so old relays produce `{}` and new fields are backward compatible. + +**`XGrpRelayAcpt`** - carries capabilities at acceptance time: + +Current (line 444): `XGrpRelayAcpt :: ShortLinkContact -> ChatMsgEvent 'Json` +New: `XGrpRelayAcpt :: ShortLinkContact -> RelayCapabilities -> ChatMsgEvent 'Json` + +Parsing: `XGrpRelayAcpt_ -> XGrpRelayAcpt <$> p "relayLink" <*> (p "relayCap" <|> pure defaultRelayCap)` +Encoding: `XGrpRelayAcpt relayLink cap -> o ["relayLink" .= relayLink, "relayCap" .= cap]` +Backward compatible: old relays omit `relayCap`, parsed as default (all `Nothing`). + +**`XGrpRelayCap`** - new message for ongoing capability updates: + +```haskell +XGrpRelayCap :: RelayCapabilities -> ChatMsgEvent 'Json +``` + +Tag: `"x.grp.relay.cap"` +Parsing: `XGrpRelayCap_ -> XGrpRelayCap <$> p "relayCap"` +Encoding: `XGrpRelayCap cap -> o ["relayCap" .= cap]` + +Sent by relay to owner only when capabilities change (not periodic). Relay detects change by comparing current config against persisted state on startup. + +### 3. Store `baseWebUrl` per relay + +**File:** `src/Simplex/Chat/Operators.hs` (line 278) + +Current: +```haskell +data GroupRelay = GroupRelay + { groupRelayId :: Int64, + groupMemberId :: Int64, + userChatRelay :: UserChatRelay, + relayStatus :: RelayStatus, + relayLink :: Maybe ShortLinkContact + } +``` + +Add: `relayCap :: Maybe RelayCapabilities` + +Stored as separate columns (same pattern as `PublicGroupAccess`): +**Migration:** `ALTER TABLE group_relays ADD COLUMN base_web_url TEXT` + +`relayCap` constructed from columns: `Just RelayCapabilities {baseWebUrl}` when any capability column is non-NULL, `Nothing` otherwise. + +**Handlers in `src/Simplex/Chat/Library/Subscriber.hs`:** +- `XGrpRelayAcpt` (line 770): store `RelayCapabilities` in relay record on acceptance +- `XGrpRelayCap` (new handler): update `RelayCapabilities` in relay record; only accepted from relay members (`isRelay m`), owner receives + +**Relay-side persistence:** relay persists its current `RelayCapabilities` (derived from `RelayWebOptions`) so it can detect config changes on restart. On startup, if persisted capabilities differ from config, relay sends `XGrpRelayCap` to all group owners it serves. + +### 4. CLI options for web preview + +**File:** `src/Simplex/Chat/Options.hs` + +New record bundling all web preview options: +```haskell +data RelayWebOptions = RelayWebOptions + { webJsonDir :: FilePath, -- --web-json-dir: where to write JSON files + webBaseUrl :: Text, -- --web-base-url: public URL prefix (sent in XGrpRelayAcpt) + webCorsFile :: FilePath, -- --web-cors-file: generated Caddy CORS config path + webUpdateInterval :: Int -- --web-update-interval: seconds (default 300) + } +``` + +Add as a proper field in `CoreChatOpts`: +```haskell +data CoreChatOpts = CoreChatOpts + { ...existing..., + relayWebOptions :: Maybe RelayWebOptions + } +``` + +Parsed from CLI: when `--web-json-dir` is provided, all other `--web-*` flags are required. `Nothing` when no web preview flags are set. Only meaningful when `--relay` is also set. + +### 5. Web preview thread startup + +**File:** `src/Simplex/Chat/Core.hs` (line 74) + +Current: +```haskell +runSimplexChat ... = do + a1 <- runReaderT (startChatController True True) cc + when (chatRelay && not testView) $ askCreateRelayAddress cc u + forM_ (postStartHook chatHooks) ($ cc) + a2 <- async $ chat u cc + waitEither_ a1 a2 +``` + +Add web preview thread as a third async when config is present: +```haskell +runSimplexChat ... = do + a1 <- runReaderT (startChatController True True) cc + when (chatRelay && not testView) $ askCreateRelayAddress cc u + forM_ (postStartHook chatHooks) ($ cc) + a2 <- async $ chat u cc + case relayWebOptions coreOptions of + Nothing -> waitEither_ a1 a2 + Just webOpts -> do + a3 <- async $ webPreviewThread webOpts cc + void $ waitAnyCancel [a1, a2, a3] +``` + +## New Types for JSON Serialization + +**File:** new module `src/Simplex/Chat/Web/Preview.hs` + +### Reuse as-is (existing ToJSON instances) + +- `GroupProfile` (Types.hs:803) - channel metadata (displayName, fullName, shortDescr, description, image, publicGroup incl. groupDomain) +- `MsgContent` (Protocol.hs:689) - tagged union: MCText, MCLink, MCImage, MCVideo, etc. +- `LinkPreview` (Protocol.hs:256) - `{uri, title, description, image, content}` +- `FormattedText` / `MarkdownList` (Markdown.hs:133/139) - parsed markdown +- `QuotedMsg` / `MsgRef` (Protocol.hs:589) - quoted message context +- `MsgMentions` = `Map MemberName CIMention` (Messages.hs:264) +- `CIMention` (Messages.hs:272) - `{memberId, memberRef}` +- `CIReactionCount` (Messages.hs:338) - `{reaction, userReacted, totalReacted}` + +### New types + +```haskell +data WebFileInfo = WebFileInfo + { fileName :: String, + fileSize :: Integer + } + +data WebMemberProfile = WebMemberProfile + { memberId :: MemberId, + displayName :: Text, + image :: Maybe ImageData + } + +data WebMessage = WebMessage + { sender :: Maybe MemberId, -- Nothing for CIChannelRcv (forwarded-from-channel) + ts :: UTCTime, + content :: MsgContent, + formattedText :: Maybe MarkdownList, + file :: Maybe WebFileInfo, + quote :: Maybe QuotedMsg, + mentions :: Map MemberName CIMention, + reactions :: [CIReactionCount], + forwarded :: Maybe CIForwardedFrom, + edited :: Bool + } + +data WebChannelPreview = WebChannelPreview + { channel :: GroupProfile, -- NOTE: render loop strips groupDomain until verified + subscriberCount :: Maybe Int, + members :: [WebMemberProfile], + messages :: [WebMessage], + updatedAt :: UTCTime + } +``` + +TH-derived JSON for `WebFileInfo`, `WebMemberProfile`, `WebMessage`, `WebChannelPreview`. + +## Render Loop + +**File:** new module `src/Simplex/Chat/Web.hs` + +Pattern from directory service's `updateListingsThread_` (Service.hs:185-194). + +```haskell +webPreviewThread :: RelayWebOptions -> ChatController -> IO () +webPreviewThread opts cc = forever $ do + u_ <- readTVarIO $ currentUser cc + forM_ u_ $ \user -> do + groups <- getWebPublishGroups cc user + corsEntries <- forM groups $ \gInfo -> do + renderGroupPreview opts cc user gInfo + pure (corsEntry gInfo) + writeCorsConfig opts corsEntries + threadDelay (webUpdateInterval opts * 1_000_000) +``` + +### Loading groups + +New store function `getWebPublishGroups`: +```sql +SELECT ... FROM groups g +JOIN group_profiles gp ON g.group_profile_id = gp.group_profile_id +WHERE gp.group_web_page IS NOT NULL + AND g.user_id = ? +``` + +Returns `[GroupInfo]`. For each, call `getGroupChat` with `CPLast 50` (Store/Messages.hs:1436) to get chat items. + +### Converting CChatItem to WebMessage + +For each `CChatItem SMDRcv (ChatItem {chatDir, meta, content, mentions, formattedText, quotedItem, reactions, file})`: + +1. **Skip if:** + - `itemDeleted meta` is `Just _` + - `itemTimed meta` is `Just _` + - `content` is not `CIRcvMsgContent mc` (skip `CIRcvGroupEvent`, `CIRcvIntegrityError`, etc.) + - `mc` is `MCReport` or `MCUnknown` + +2. **Extract sender:** + - `CIGroupRcv member` -> `Just (memberId member)`, collect member into profiles array + - `CIChannelRcv` -> `Nothing` (channel-forwarded message, no individual sender) + +3. **Extract file info:** + - `file :: Maybe (CIFile 'MDRcv)` has `fileName :: String`, `fileSize :: Integer` + - Strip `fileSource`, `fileStatus`, `fileProtocol` (download metadata irrelevant for web) + +4. **Build WebMessage:** + ```haskell + WebMessage + { sender = senderMemberId + , ts = itemTs meta + , content = mc + , formattedText = formattedText + , file = (\f -> WebFileInfo (fileName f) (fileSize f)) <$> file + , quote = quotedItem -- QuotedMsg reused directly + , mentions = mentions + , reactions = reactions + , forwarded = itemForwarded meta + , edited = itemEdited meta + } + ``` + +5. **Collect unique senders** into `[WebMemberProfile]` from `GroupMember` records in `CIGroupRcv`. + +Also include `CIGroupSnd` items (relay's own sent messages, if any - unlikely but possible for admin announcements). + +### Filtering unverified domains + +Before serializing, the render loop strips `groupDomain` from the `PublicGroupAccess` included in the profile when not verified: + +```haskell +stripUnverifiedDomain :: Maybe UTCTime -> GroupProfile -> GroupProfile +stripUnverifiedDomain verifiedAt gp = case verifiedAt of + Just _ -> gp -- domain verified, include as-is + Nothing -> gp {publicGroup = clearDomain <$> publicGroup gp} + where + clearDomain pgp = pgp {publicGroupAccess = clearAccess <$> publicGroupAccess pgp} + clearAccess acc = acc {groupDomain = ""} -- or strip the access record entirely +``` + +The `group_domain_verified_at` timestamp is loaded alongside the group info. Until RSLV ships, this column is always NULL, so all domains are stripped from web export. + +`domainWebPage` in CORS config is also gated on verified domain - unverified means no domain-site origin in CORS. + +### Writing JSON + +- Serialize `WebChannelPreview` to JSON via `Data.Aeson.encode` +- Write atomically (write to temp, rename) to `/.json` +- `publicGroupId` from `PublicGroupProfile` (base64url-encoded, existing field) + +### Generating Caddy CORS config + +Write a single file with Caddy `map` directive: + +```caddy +map {path} {cors_origin} { + /.json "https://owner-domain.com" + /.json "*" + default "" +} +header /*.json Access-Control-Allow-Origin {cors_origin} +header /*.json Access-Control-Allow-Methods "GET, OPTIONS" +``` + +CORS origin derivation from `PublicGroupAccess`: +- `allowEmbeding = True` -> `*` +- `groupWebPage = Just url` -> extract origin from URL (+ domain site origin if `domainWebPage` and domain verified) +- `groupWebPage = Nothing, domainWebPage = True` -> domain site origin only (when domain is verified) +- No web page, no embedding, no domain page -> omit from config + +After writing, run `caddy reload` if file content changed (compare hash before/after). + +## Namespace Integration + +`groupDomain` ships now in the profile (inside `PublicGroupAccess`). What's deferred is on-chain verification (RSLV protocol). + +### What ships now + +1. **`groupDomain :: Text` in `PublicGroupAccess`** - owner sets the registered domain, disseminated to all members +2. **`domainWebPage :: Bool` in `PublicGroupAccess`** - flag stored but has no effect until domain is verified +3. **Relay strips `groupDomain` from web export** - no verification means domain is cleared in JSON, no domain-site CORS origin + +### What ships with RSLV + +1. **RSLV protocol** - relay queries name servers via SMP proxy to verify domain ownership +2. **`domainWebPage` becomes functional** - enables domain-site hosting (e.g. `simplexnetwork.org/c/`) for verified domains +3. **In-app resolution** - `#name` markdown (already parsed by namespace branch) resolves and connects + +### Verification flow (relay-side) + +When owner updates profile with `groupDomain`: + +1. **Trigger:** Relay receives profile update on owner's connection containing `groupDomain` field +2. **Initiate:** Relay sends `RSLV ` through SMP proxy (async, on the same owner connection context) +3. **Pending state:** `group_domain_verified_at = NULL` in DB. Web export excludes domain while pending. +4. **Resolution arrives:** `NAME ` agent event arrives on the owner's connection (continuation bound to the connection that sent the profile update) +5. **Verify:** Check if `channelLinks` in the NAME response includes this group's `groupLink` +6. **Store result:** Set `group_domain_verified_at = ` on success, leave NULL on failure +7. **Effect:** Web render loop includes domain in JSON and enables domain-site CORS only when `group_domain_verified_at IS NOT NULL` + +Re-verification: periodic (e.g. daily or on each web update cycle) to catch expired/transferred domains. Clear `group_domain_verified_at` when re-verification fails. + +### What the namespace branch already provides + +- `SimplexNameInfo {nameType, namespace, domain, subDomain}` in Markdown.hs +- `SimplexName` variant in `Format` ADT +- Parser for `#name` / `#name.simplex` / `:name.simplex` syntax +- Forward-compatibility alerts in Kotlin/Swift UI (shows "requires newer app" until resolution is implemented) + +## UI Changes (Kotlin/Swift) + +### Kotlin types + +**File:** `apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt` + +```kotlin +@Serializable +data class PublicGroupAccess( + val groupWebPage: String? = null, + val groupDomain: String? = null, + val domainWebPage: Boolean = false, + val allowEmbeding: Boolean = false +) + +// Extend existing PublicGroupProfile (currently at line 2213): +@Serializable +data class PublicGroupProfile( + val groupType: GroupType, + val groupLink: String, + val publicGroupId: String, + val publicGroupAccess: PublicGroupAccess? = null // NEW +) + +@Serializable +data class RelayCapabilities( + val baseWebUrl: String? = null +) + +// Extend existing GroupRelay: +@Serializable +data class GroupRelay( + ...existing fields..., + val relayCap: RelayCapabilities? = null // NEW +) +``` + +### Owner: Channel info page + +**File:** `GroupChatInfoView.kt` (around line 604-606) + +After existing `ChannelLinkButton(manageGroupLink)`: +```kotlin +ChannelWebPageButton(openChannelWebPage) // owner only +``` + +New nav destination opens `ChannelWebPageView`. + +### Owner: Channel web page screen + +**File:** new `apps/multiplatform/.../views/chat/group/ChannelWebPageView.kt` + +- Text field: web page URL (`groupWebPage`) +- Text field: domain (`groupDomain`) +- Toggle: allow embedding (`allowEmbeding`) +- Toggle: show on domain's page (`domainWebPage`) - stored but inert until RSLV ships +- Section: embed snippet (read-only, auto-generated from relay `baseWebUrl` values + `publicGroupId`) +- Save button -> `apiUpdateGroup` with updated `GroupProfile` + +### Subscriber: Channel info page + +In the top section (around line 607-614), after channel link QR: +```kotlin +val webPageUrl = groupInfo.groupProfile.publicGroup?.publicGroupAccess?.groupWebPage +if (webPageUrl != null) { + WebPageLinkRow(webPageUrl) // clickable, opens browser +} +``` + +## Build Configuration + +Web preview code compiles into the main `simplex-chat` library (not conditional). The thread only starts when `relayWebOptions` is set in `CoreChatOpts`. Mobile apps never set this. + +No cabal flag needed - the thread startup is gated by `Maybe RelayWebOptions` at runtime (same pattern as `chatRelay` gating relay behavior). + +## Caddy Setup (operator documentation) + +Main Caddyfile (operator writes once): +```caddy +relay.example.com { + import /etc/caddy/simplex-cors.conf + handle /preview/* { + root * /var/lib/simplex/web/preview + file_server + } +} +``` + +Relay CLI invocation: +``` +simplex-chat --relay \ + --web-json-dir /var/lib/simplex/web/preview \ + --web-base-url https://relay.example.com/preview \ + --web-cors-file /etc/caddy/simplex-cors.conf \ + --web-update-interval 300 +``` + +## Channel Page and Embed Code + +### Embed snippet (shown to owner) + +The "Channel web page" screen auto-generates this from the channel's relay `baseWebUrl` values and `publicGroupId`. Owner copies it into their page: + +```html +
+
+ +``` + +Example with real values: +```html +
+
+ +``` + +The script fetches `/a1b2c3d4.json`, renders the preview into the `div`. Tries relays in order, falls back on failure. The owner's domain must match the CORS origin configured by the relay (derived from `groupWebPage`), or `allowEmbeding` must be `True` for `*`. + +For iframe embedding (when allowed), the snippet is simpler - just an iframe pointing to the owner's hosted channel page. + +### Channel page (static JS) + +Separate repo or folder. `channel-preview.js` + minimal CSS: +- Reads config from `data-` attributes on the container div +- Fetches JSON from relays with fallback (try first, fall back to second) +- Renders: channel header (name, avatar, description, subscriber count), message list (text with FormattedText markdown, link previews, file indicators, reactions, quotes) +- Join button: `simplex://` deep link on mobile, QR code on desktop +- Reuses directory page's markdown rendering approach + +## Files to Create/Modify + +### New files +- `src/Simplex/Chat/Web/Preview.hs` - types: `WebChannelPreview`, `WebMessage`, `WebFileInfo`, `WebMemberProfile` +- `src/Simplex/Chat/Web.hs` - render loop, JSON writing, Caddy config generation +- `apps/multiplatform/.../views/chat/group/ChannelWebPageView.kt` +- `apps/ios/Shared/Views/Chat/Group/ChannelWebPageView.swift` +- Migration files (SQLite + Postgres): `group_web_page`, `group_domain`, `domain_web_page`, `allow_embedding`, `group_domain_verified_at` in group_profiles; `base_web_url` in group_relays +- Channel page static site (separate repo/folder) + +### Modified files +- `src/Simplex/Chat/Types.hs` - `PublicGroupAccess` type, extend `PublicGroupProfile` with `publicGroupAccess` +- `src/Simplex/Chat/Protocol.hs` - `RelayCapabilities` record, extend `XGrpRelayAcpt`, add `XGrpRelayCap` +- `src/Simplex/Chat/Options.hs` - `RelayWebOptions` record, `relayWebOptions :: Maybe RelayWebOptions` in `CoreChatOpts` +- `src/Simplex/Chat/Core.hs` - start web preview thread in `runSimplexChat` +- `src/Simplex/Chat/Operators.hs` - `baseWebUrl` in `GroupRelay` +- `src/Simplex/Chat/Store/Groups.hs` - read/write `PublicGroupAccess` columns; `getWebPublishGroups` +- `src/Simplex/Chat/Store/Shared.hs` - `toPublicGroupAccess`, extend `toPublicGroupProfile` and `GroupInfoRow` +- `src/Simplex/Chat/Library/Subscriber.hs` - handle `RelayCapabilities` in `XGrpRelayAcpt` and `XGrpRelayCap` +- `apps/multiplatform/.../model/ChatModel.kt` - `PublicGroupAccess`, `RelayCapabilities`, `PublicGroupProfile.publicGroupAccess`, `GroupRelay.relayCap` +- `apps/multiplatform/.../views/chat/group/GroupChatInfoView.kt` - nav link for web page +- `simplex-chat.cabal` - add `Simplex.Chat.Web.Preview`, `Simplex.Chat.Web` to exposed-modules + +## Implementation Order + +1. **Data model** - `PublicGroupAccess` in `PublicGroupProfile`, migrations (separate columns), store functions +2. **Protocol** - `RelayCapabilities`, extend `XGrpRelayAcpt`, add `XGrpRelayCap`, handlers in Subscriber.hs +3. **CLI options** - `RelayWebOptions` record, `relayWebOptions` field in `CoreChatOpts` +4. **Web types** - `WebChannelPreview`, `WebMessage`, etc. in new module +5. **Render loop** - thread startup in Core.hs, periodic JSON generation, Caddy config +6. **UI (owner)** - "Channel web page" settings screen +7. **UI (subscriber)** - web page link in channel info +8. **Channel page** - static HTML+JS template +9. **Documentation** - operator setup guide + +## Verification + +1. **Build**: `cabal build simplex-chat` with new modules compiles +2. **Unit test**: serialize `WebChannelPreview` with sample data, verify JSON matches expected structure +3. **Integration test**: create channel with `publicGroupAccess` set, run relay with `--web-json-dir`, verify JSON file appears at correct path with correct content +4. **CORS test**: verify generated config produces correct `Access-Control-Allow-Origin` for configured domains +5. **UI test**: owner can set web page URL and domain, see embed snippet; subscriber sees clickable link +6. **Channel page test**: serve static page locally against relay's JSON, verify rendering +7. **Domain stripping test**: set `groupDomain` on a channel, verify it is stripped from web export JSON (unverified, `group_domain_verified_at IS NULL`) diff --git a/simplex-chat.cabal b/simplex-chat.cabal index 6e459d6484..18fc93eede 100644 --- a/simplex-chat.cabal +++ b/simplex-chat.cabal @@ -133,6 +133,7 @@ library Simplex.Chat.Store.Postgres.Migrations.M20260429_relay_request_retries Simplex.Chat.Store.Postgres.Migrations.M20260507_relay_inactive_at Simplex.Chat.Store.Postgres.Migrations.M20260514_relay_request_group_link_index + Simplex.Chat.Store.Postgres.Migrations.M20260515_public_group_access else exposed-modules: Simplex.Chat.Archive @@ -288,6 +289,7 @@ library Simplex.Chat.Store.SQLite.Migrations.M20260429_relay_request_retries Simplex.Chat.Store.SQLite.Migrations.M20260507_relay_inactive_at Simplex.Chat.Store.SQLite.Migrations.M20260514_relay_request_group_link_index + Simplex.Chat.Store.SQLite.Migrations.M20260515_public_group_access other-modules: Paths_simplex_chat hs-source-dirs: diff --git a/src/Simplex/Chat/Library/Commands.hs b/src/Simplex/Chat/Library/Commands.hs index 4bbfa8e09d..20e9d6a0fb 100644 --- a/src/Simplex/Chat/Library/Commands.hs +++ b/src/Simplex/Chat/Library/Commands.hs @@ -2524,7 +2524,8 @@ processChatCommand vr nm = \case -- generate owner key, OwnerAuth signed by root key memberId <- MemberId <$> liftIO (encodedRandomBytes gVar 12) (memberPrivKey, ownerAuth) <- liftIO $ SL.newOwnerAuth gVar (unMemberId memberId) rootPrivKey - let groupProfile' = (groupProfile :: GroupProfile) {publicGroup = Just PublicGroupProfile {groupType = GTChannel, groupLink = sLnk, publicGroupId = B64UrlByteString entityId}} + -- TODO [channel web] pass publicGroupAccess from owner's profile + let groupProfile' = (groupProfile :: GroupProfile) {publicGroup = Just PublicGroupProfile {groupType = GTChannel, groupLink = sLnk, publicGroupId = B64UrlByteString entityId, publicGroupAccess = Nothing}} userData = encodeShortLinkData $ GroupShortLinkData {groupProfile = groupProfile', publicGroupData = Just (PublicGroupData 1)} userLinkData = UserContactLinkData UserContactData {direct = False, owners = [ownerAuth], relays = [], userData} -- create connection with prepared link (single network call) @@ -2643,8 +2644,7 @@ processChatCommand vr nm = \case Nothing -> throwChatError $ CEContactNotActive ct APIAcceptMember groupId gmId role -> withUser $ \user@User {userId} -> do (gInfo, m) <- withFastStore $ \db -> (,) <$> getGroupInfo db vr user groupId <*> getGroupMemberById db vr user gmId - -- TODO check that user's role is > role, possibly restrict role to only observer and member - assertUserGroupRole gInfo GRModerator + assertUserGroupRole gInfo $ max GRModerator role case memberStatus m of GSMemPendingApproval | memberCategory m == GCInviteeMember -> do -- only host can approve let GroupInfo {groupProfile = GroupProfile {memberAdmission}} = gInfo diff --git a/src/Simplex/Chat/Library/Internal.hs b/src/Simplex/Chat/Library/Internal.hs index c6c3f92752..31a1d60502 100644 --- a/src/Simplex/Chat/Library/Internal.hs +++ b/src/Simplex/Chat/Library/Internal.hs @@ -1048,7 +1048,8 @@ acceptRelayJoinRequestAsync cReqInvId cReqChatVRange relayLink = do - let msg = XGrpRelayAcpt relayLink + -- TODO [channel web] derive RelayCapabilities from relay config (RelayWebOptions) + let msg = XGrpRelayAcpt relayLink defaultRelayCapabilities subMode <- chatReadVar subscriptionMode vr <- chatVersionRange let chatV = vr `peerConnChatVersion` cReqChatVRange diff --git a/src/Simplex/Chat/Library/Subscriber.hs b/src/Simplex/Chat/Library/Subscriber.hs index 08ca90f2a6..478c53c763 100644 --- a/src/Simplex/Chat/Library/Subscriber.hs +++ b/src/Simplex/Chat/Library/Subscriber.hs @@ -765,9 +765,11 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = -- [async agent commands] no continuation needed, but command should be asynchronous for stability allowAgentConnectionAsync user conn' confId XOk | otherwise -> messageError "x.grp.acpt: memberId is different from expected" - XGrpRelayAcpt relayLink + XGrpRelayAcpt relayLink relayCap | memberRole' membership == GROwner && isRelay m -> do - withStore' $ \db -> setRelayLinkConfId db m confId relayLink + withStore' $ \db -> do + setRelayLinkConfId db m confId relayLink + updateRelayCapabilities db m relayCap void $ getAgentConnShortLinkAsync user CFGetRelayDataAccept (Just conn') relayLink | otherwise -> messageError "x.grp.relay.acpt: only owner can add relay" XGrpRelayReject reason @@ -1036,6 +1038,10 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = XGrpLinkMem p -> Nothing <$ xGrpLinkMem gInfo' m'' conn' p XGrpLinkAcpt acceptance role memberId -> Nothing <$ xGrpLinkAcpt gInfo' m'' acceptance role memberId msg brokerTs XGrpRelayNew rl -> fmap ctx <$> xGrpRelayNew gInfo' m'' rl + XGrpRelayCap relayCap + | memberRole' membership == GROwner && isRelay m'' -> + Nothing <$ withStore' (\db -> updateRelayCapabilities db m'' relayCap) + | otherwise -> Nothing <$ messageWarning "x.grp.relay.cap: only owner should receive relay capabilities" XGrpMemNew memInfo msgScope -> fmap ctx <$> xGrpMemNew gInfo' m'' memInfo msgScope msg brokerTs XGrpMemIntro memInfo memRestrictions_ -> Nothing <$ xGrpMemIntro gInfo' m'' memInfo memRestrictions_ XGrpMemInv memId introInv -> Nothing <$ xGrpMemInv gInfo' m'' memId introInv @@ -2601,6 +2607,8 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = xGrpLinkAcpt :: GroupInfo -> GroupMember -> GroupAcceptance -> GroupMemberRole -> MemberId -> RcvMessage -> UTCTime -> CM () xGrpLinkAcpt gInfo@GroupInfo {membership} m acceptance role memberId msg brokerTs + | memberRole' m < GRModerator || memberRole' m < role = + messageError "x.grp.link.acpt with insufficient member permissions" | sameMemberId memberId membership = processUserAccepted | otherwise = withStore' (\db -> runExceptT $ getGroupMemberByMemberId db vr user gInfo memberId) >>= \case diff --git a/src/Simplex/Chat/Operators.hs b/src/Simplex/Chat/Operators.hs index 03cc38e12a..6816a5f692 100644 --- a/src/Simplex/Chat/Operators.hs +++ b/src/Simplex/Chat/Operators.hs @@ -46,7 +46,7 @@ import Data.Time (addUTCTime) import Data.Time.Clock (UTCTime, nominalDay) import Language.Haskell.TH.Syntax (lift) import Simplex.Chat.Operators.Conditions -import Simplex.Chat.Protocol (RelayProfile (..)) +import Simplex.Chat.Protocol (RelayCapabilities (..), RelayProfile (..)) import Simplex.Chat.Types (ShortLinkContact, User) import Simplex.Chat.Types.Shared (RelayStatus) import Simplex.Messaging.Agent.Env.SQLite (ServerCfg (..), ServerRoles (..), allRoles) @@ -280,7 +280,8 @@ data GroupRelay = GroupRelay groupMemberId :: Int64, userChatRelay :: UserChatRelay, relayStatus :: RelayStatus, - relayLink :: Maybe ShortLinkContact + relayLink :: Maybe ShortLinkContact, + relayCap :: RelayCapabilities } deriving (Eq, Show) diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index f9c29e3552..71daeec635 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -262,6 +262,14 @@ data LinkContent = LCPage | LCImage | LCVideo {duration :: Maybe Int} | LCUnknow data ReportReason = RRSpam | RRContent | RRCommunity | RRProfile | RROther | RRUnknown Text deriving (Eq, Show) +data RelayCapabilities = RelayCapabilities + { baseWebUrl :: Maybe Text + } + deriving (Eq, Show) + +defaultRelayCapabilities :: RelayCapabilities +defaultRelayCapabilities = RelayCapabilities {baseWebUrl = Nothing} + $(pure []) instance FromJSON LinkContent where @@ -281,6 +289,12 @@ instance ToJSON LinkContent where $(JQ.deriveJSON defaultJSON ''LinkPreview) +$(JQ.deriveToJSON defaultJSON ''RelayCapabilities) + +instance FromJSON RelayCapabilities where + parseJSON = $(JQ.mkParseJSON defaultJSON ''RelayCapabilities) + omittedField = Just defaultRelayCapabilities + instance StrEncoding ReportReason where strEncode = \case RRSpam -> "spam" @@ -441,10 +455,11 @@ data ChatMsgEvent (e :: MsgEncoding) where XGrpLinkMem :: Profile -> ChatMsgEvent 'Json XGrpLinkAcpt :: GroupAcceptance -> GroupMemberRole -> MemberId -> ChatMsgEvent 'Json XGrpRelayInv :: GroupRelayInvitation -> ChatMsgEvent 'Json - XGrpRelayAcpt :: ShortLinkContact -> ChatMsgEvent 'Json + XGrpRelayAcpt :: ShortLinkContact -> RelayCapabilities -> ChatMsgEvent 'Json XGrpRelayTest :: ByteString -> Maybe ByteString -> ChatMsgEvent 'Json XGrpRelayNew :: ShortLinkContact -> ChatMsgEvent 'Json XGrpRelayReject :: RelayRejectionReason -> ChatMsgEvent 'Json + XGrpRelayCap :: RelayCapabilities -> ChatMsgEvent 'Json XGrpMemNew :: MemberInfo -> Maybe MsgScope -> ChatMsgEvent 'Json XGrpMemIntro :: MemberInfo -> Maybe MemberRestrictions -> ChatMsgEvent 'Json XGrpMemInv :: MemberId -> IntroInvitation -> ChatMsgEvent 'Json @@ -991,6 +1006,7 @@ data CMEventTag (e :: MsgEncoding) where XGrpRelayTest_ :: CMEventTag 'Json XGrpRelayNew_ :: CMEventTag 'Json XGrpRelayReject_ :: CMEventTag 'Json + XGrpRelayCap_ :: CMEventTag 'Json XGrpMemNew_ :: CMEventTag 'Json XGrpMemIntro_ :: CMEventTag 'Json XGrpMemInv_ :: CMEventTag 'Json @@ -1050,6 +1066,7 @@ instance MsgEncodingI e => StrEncoding (CMEventTag e) where XGrpRelayTest_ -> "x.grp.relay.test" XGrpRelayNew_ -> "x.grp.relay.new" XGrpRelayReject_ -> "x.grp.relay.reject" + XGrpRelayCap_ -> "x.grp.relay.cap" XGrpMemNew_ -> "x.grp.mem.new" XGrpMemIntro_ -> "x.grp.mem.intro" XGrpMemInv_ -> "x.grp.mem.inv" @@ -1110,6 +1127,7 @@ instance StrEncoding ACMEventTag where "x.grp.relay.test" -> XGrpRelayTest_ "x.grp.relay.new" -> XGrpRelayNew_ "x.grp.relay.reject" -> XGrpRelayReject_ + "x.grp.relay.cap" -> XGrpRelayCap_ "x.grp.mem.new" -> XGrpMemNew_ "x.grp.mem.intro" -> XGrpMemIntro_ "x.grp.mem.inv" -> XGrpMemInv_ @@ -1162,10 +1180,11 @@ toCMEventTag msg = case msg of XGrpLinkMem _ -> XGrpLinkMem_ XGrpLinkAcpt {} -> XGrpLinkAcpt_ XGrpRelayInv _ -> XGrpRelayInv_ - XGrpRelayAcpt _ -> XGrpRelayAcpt_ + XGrpRelayAcpt {} -> XGrpRelayAcpt_ XGrpRelayTest {} -> XGrpRelayTest_ XGrpRelayNew _ -> XGrpRelayNew_ XGrpRelayReject _ -> XGrpRelayReject_ + XGrpRelayCap _ -> XGrpRelayCap_ XGrpMemNew {} -> XGrpMemNew_ XGrpMemIntro _ _ -> XGrpMemIntro_ XGrpMemInv _ _ -> XGrpMemInv_ @@ -1318,7 +1337,8 @@ appJsonToCM AppMessageJson {v, msgId, event, params} = do XGrpLinkMem_ -> XGrpLinkMem <$> p "profile" XGrpLinkAcpt_ -> XGrpLinkAcpt <$> p "acceptance" <*> p "role" <*> p "memberId" XGrpRelayInv_ -> XGrpRelayInv <$> p "groupRelayInvitation" - XGrpRelayAcpt_ -> XGrpRelayAcpt <$> p "relayLink" + XGrpRelayAcpt_ -> XGrpRelayAcpt <$> p "relayLink" <*> (fromMaybe defaultRelayCapabilities <$> opt "relayCap") + XGrpRelayCap_ -> XGrpRelayCap <$> p "relayCap" XGrpRelayTest_ -> do B64UrlByteString challenge <- p "challenge" sig_ <- fmap (\(B64UrlByteString s) -> s) <$> opt "signature" @@ -1390,7 +1410,8 @@ chatToAppMessage chatMsg@ChatMessage {chatVRange, msgId, chatMsgEvent} = case en XGrpLinkMem profile -> o ["profile" .= profile] XGrpLinkAcpt acceptance role memberId -> o ["acceptance" .= acceptance, "role" .= role, "memberId" .= memberId] XGrpRelayInv groupRelayInv -> o ["groupRelayInvitation" .= groupRelayInv] - XGrpRelayAcpt relayLink -> o ["relayLink" .= relayLink] + XGrpRelayAcpt relayLink relayCap -> o ["relayLink" .= relayLink, "relayCap" .= relayCap] + XGrpRelayCap relayCap -> o ["relayCap" .= relayCap] XGrpRelayTest challenge sig_ -> o $ ("signature" .=? (B64UrlByteString <$> sig_)) ["challenge" .= B64UrlByteString challenge] diff --git a/src/Simplex/Chat/Store/Connections.hs b/src/Simplex/Chat/Store/Connections.hs index 62183a0313..60d865cb30 100644 --- a/src/Simplex/Chat/Store/Connections.hs +++ b/src/Simplex/Chat/Store/Connections.hs @@ -139,6 +139,7 @@ getConnectionEntity db vr user@User {userId, userContactId} agentConnId = do SELECT -- GroupInfo g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id, + gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission, g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id, diff --git a/src/Simplex/Chat/Store/Groups.hs b/src/Simplex/Chat/Store/Groups.hs index 9b21f0697b..e2a9d6816a 100644 --- a/src/Simplex/Chat/Store/Groups.hs +++ b/src/Simplex/Chat/Store/Groups.hs @@ -89,6 +89,7 @@ module Simplex.Chat.Store.Groups updateRelayStatusFromTo, setRelayLinkAccepted, setRelayLinkConfId, + updateRelayCapabilities, getRelayConfId, updateRelayMemberData, setGroupInProgressDone, @@ -367,10 +368,11 @@ createNewGroup db vr user@User {userId} groupProfile incognitoProfile useRelays INSERT INTO group_profiles (display_name, full_name, short_descr, description, image, group_type, group_link, public_group_id, + group_web_page, group_domain, domain_web_page, allow_embedding, user_id, preferences, member_admission, created_at, updated_at) - VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) |] - ((displayName, fullName, shortDescr, description, image, groupType_, groupLink_, publicGroupId_) + ((displayName, fullName, shortDescr, description, image, groupType_, groupLink_, publicGroupId_) :. publicGroupAccessRow publicGroup :. (userId, groupPreferences, memberAdmission, currentTs, currentTs)) profileId <- insertedRowId db DB.execute @@ -868,10 +870,11 @@ createGroup_ db userId groupProfile prepared business useRelays relayOwnStatus p INSERT INTO group_profiles (display_name, full_name, short_descr, description, image, group_type, group_link, public_group_id, + group_web_page, group_domain, domain_web_page, allow_embedding, user_id, preferences, member_admission, created_at, updated_at) - VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) |] - ((displayName, fullName, shortDescr, description, image, groupType_, groupLink_, publicGroupId_) + ((displayName, fullName, shortDescr, description, image, groupType_, groupLink_, publicGroupId_) :. publicGroupAccessRow publicGroup :. (userId, groupPreferences, memberAdmission, currentTs, currentTs)) profileId <- insertedRowId db DB.execute @@ -1343,15 +1346,16 @@ groupRelayQuery = [sql| SELECT gr.group_relay_id, gr.group_member_id, cr.chat_relay_id, cr.address, cr.display_name, cr.full_name, cr.short_descr, cr.image, cr.domains, cr.preset, cr.tested, cr.enabled, cr.deleted, - gr.relay_status, gr.relay_link + gr.relay_status, gr.relay_link, gr.base_web_url FROM group_relays gr JOIN chat_relays cr ON cr.chat_relay_id = gr.chat_relay_id |] -toGroupRelay :: (Int64, GroupMemberId, DBEntityId, ShortLinkContact, Text, Text, Maybe Text, Maybe ImageData, Text, BoolInt) :. (Maybe BoolInt, BoolInt, BoolInt, RelayStatus, Maybe ShortLinkContact) -> GroupRelay -toGroupRelay ((groupRelayId, groupMemberId, chatRelayId, address, displayName, fullName, shortDescr, image, domains, BI preset) :. (tested, BI enabled, BI deleted, relayStatus, relayLink)) = +toGroupRelay :: (Int64, GroupMemberId, DBEntityId, ShortLinkContact, Text, Text, Maybe Text, Maybe ImageData, Text, BoolInt) :. (Maybe BoolInt, BoolInt, BoolInt, RelayStatus, Maybe ShortLinkContact, Maybe Text) -> GroupRelay +toGroupRelay ((groupRelayId, groupMemberId, chatRelayId, address, displayName, fullName, shortDescr, image, domains, BI preset) :. (tested, BI enabled, BI deleted, relayStatus, relayLink, baseWebUrl)) = let userChatRelay = UserChatRelay {chatRelayId, address, relayProfile = toRelayProfile (displayName, fullName, shortDescr, image), domains = T.splitOn "," domains, preset, tested = unBI <$> tested, enabled, deleted} - in GroupRelay {groupRelayId, groupMemberId, userChatRelay, relayStatus, relayLink} + relayCap = RelayCapabilities {baseWebUrl} + in GroupRelay {groupRelayId, groupMemberId, userChatRelay, relayStatus, relayLink, relayCap} createRelayForOwner :: DB.Connection -> VersionRangeChat -> TVar ChaChaDRG -> User -> GroupInfo -> UserChatRelay -> ExceptT StoreError IO GroupMember createRelayForOwner db vr gVar user@User {userId, userContactId} GroupInfo {groupId, membership} UserChatRelay {relayProfile = RelayProfile {displayName}} = do @@ -1491,6 +1495,18 @@ setRelayLinkConfId db m confId relayLink = do |] (relayLink, currentTs, groupMemberId' m) +updateRelayCapabilities :: DB.Connection -> GroupMember -> RelayCapabilities -> IO () +updateRelayCapabilities db m RelayCapabilities {baseWebUrl} = do + currentTs <- getCurrentTime + DB.execute + db + [sql| + UPDATE group_relays + SET base_web_url = ?, updated_at = ? + WHERE group_member_id = ? + |] + (baseWebUrl, currentTs, groupMemberId' m) + getRelayConfId :: DB.Connection -> GroupMember -> ExceptT StoreError IO ConfirmationId getRelayConfId db m = ExceptT . firstRow fromOnly (SEGroupRelayNotFoundByMemberId $ groupMemberId' m) $ @@ -2327,6 +2343,7 @@ updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName, UPDATE group_profiles SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, group_type = ?, group_link = ?, + group_web_page = ?, group_domain = ?, domain_web_page = ?, allow_embedding = ?, preferences = ?, member_admission = ?, updated_at = ? WHERE group_profile_id IN ( SELECT group_profile_id @@ -2334,7 +2351,7 @@ updateGroupProfile db user@User {userId} g@GroupInfo {groupId, localDisplayName, WHERE user_id = ? AND group_id = ? ) |] - ((newName, fullName, shortDescr, description, image, groupType_, groupLink_) :. (groupPreferences, memberAdmission, currentTs, userId, groupId)) + ((newName, fullName, shortDescr, description, image, groupType_, groupLink_) :. publicGroupAccessRow publicGroup :. (groupPreferences, memberAdmission, currentTs, userId, groupId)) updateGroup_ ldn currentTs = do DB.execute db @@ -2374,14 +2391,16 @@ updateGroupProfileFromMember db user g@GroupInfo {groupId} Profile {displayName [sql| SELECT gp.display_name, gp.full_name, gp.short_descr, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id, + gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.preferences, gp.member_admission FROM group_profiles gp JOIN groups g ON gp.group_profile_id = g.group_profile_id WHERE g.group_id = ? |] (Only groupId) - toGroupProfile (displayName, fullName, shortDescr, description, image, groupType_, groupLink_, publicGroupId_, groupPreferences, memberAdmission) = - GroupProfile {displayName, fullName, shortDescr, description, image, publicGroup = toPublicGroupProfile groupType_ groupLink_ publicGroupId_, groupPreferences, memberAdmission} + toGroupProfile ((displayName, fullName, shortDescr, description, image, groupType_, groupLink_, publicGroupId_) :. accessRow :. (groupPreferences, memberAdmission)) = + let publicGroupAccess = toPublicGroupAccess accessRow + in GroupProfile {displayName, fullName, shortDescr, description, image, publicGroup = toPublicGroupProfile groupType_ groupLink_ publicGroupId_ publicGroupAccess, groupPreferences, memberAdmission} getGroupInfoByUserContactLinkConnReq :: DB.Connection -> VersionRangeChat -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe GroupInfo) getGroupInfoByUserContactLinkConnReq db vr user@User {userId} (cReqSchema1, cReqSchema2) = do diff --git a/src/Simplex/Chat/Store/Postgres/Migrations.hs b/src/Simplex/Chat/Store/Postgres/Migrations.hs index 437f16a43c..a8bb0da945 100644 --- a/src/Simplex/Chat/Store/Postgres/Migrations.hs +++ b/src/Simplex/Chat/Store/Postgres/Migrations.hs @@ -31,6 +31,7 @@ import Simplex.Chat.Store.Postgres.Migrations.M20260403_item_viewed import Simplex.Chat.Store.Postgres.Migrations.M20260429_relay_request_retries import Simplex.Chat.Store.Postgres.Migrations.M20260507_relay_inactive_at import Simplex.Chat.Store.Postgres.Migrations.M20260514_relay_request_group_link_index +import Simplex.Chat.Store.Postgres.Migrations.M20260515_public_group_access import Simplex.Messaging.Agent.Store.Shared (Migration (..)) schemaMigrations :: [(String, Text, Maybe Text)] @@ -61,7 +62,8 @@ schemaMigrations = ("20260403_item_viewed", m20260403_item_viewed, Just down_m20260403_item_viewed), ("20260429_relay_request_retries", m20260429_relay_request_retries, Just down_m20260429_relay_request_retries), ("20260507_relay_inactive_at", m20260507_relay_inactive_at, Just down_m20260507_relay_inactive_at), - ("20260514_relay_request_group_link_index", m20260514_relay_request_group_link_index, Just down_m20260514_relay_request_group_link_index) + ("20260514_relay_request_group_link_index", m20260514_relay_request_group_link_index, Just down_m20260514_relay_request_group_link_index), + ("20260515_public_group_access", m20260515_public_group_access, Just down_m20260515_public_group_access) ] -- | The list of migrations in ascending order by date diff --git a/src/Simplex/Chat/Store/Postgres/Migrations/M20260515_public_group_access.hs b/src/Simplex/Chat/Store/Postgres/Migrations/M20260515_public_group_access.hs new file mode 100644 index 0000000000..1fbb731626 --- /dev/null +++ b/src/Simplex/Chat/Store/Postgres/Migrations/M20260515_public_group_access.hs @@ -0,0 +1,29 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE QuasiQuotes #-} + +module Simplex.Chat.Store.Postgres.Migrations.M20260515_public_group_access where + +import Data.Text (Text) +import Text.RawString.QQ (r) + +m20260515_public_group_access :: Text +m20260515_public_group_access = + [r| +ALTER TABLE group_profiles ADD COLUMN group_web_page TEXT; +ALTER TABLE group_profiles ADD COLUMN group_domain TEXT; +ALTER TABLE group_profiles ADD COLUMN domain_web_page BIGINT; +ALTER TABLE group_profiles ADD COLUMN allow_embedding BIGINT; + +ALTER TABLE group_relays ADD COLUMN base_web_url TEXT; +|] + +down_m20260515_public_group_access :: Text +down_m20260515_public_group_access = + [r| +ALTER TABLE group_relays DROP COLUMN base_web_url; + +ALTER TABLE group_profiles DROP COLUMN allow_embedding; +ALTER TABLE group_profiles DROP COLUMN domain_web_page; +ALTER TABLE group_profiles DROP COLUMN group_domain; +ALTER TABLE group_profiles DROP COLUMN group_web_page; +|] diff --git a/src/Simplex/Chat/Store/Postgres/Migrations/chat_schema.sql b/src/Simplex/Chat/Store/Postgres/Migrations/chat_schema.sql index 6026049313..cc3543e8a8 100644 --- a/src/Simplex/Chat/Store/Postgres/Migrations/chat_schema.sql +++ b/src/Simplex/Chat/Store/Postgres/Migrations/chat_schema.sql @@ -849,7 +849,11 @@ CREATE TABLE test_chat_schema.group_profiles ( short_descr text, group_type text, group_link bytea, - public_group_id bytea + public_group_id bytea, + group_web_page text, + group_domain text, + domain_web_page bigint, + allow_embedding bigint ); @@ -874,7 +878,8 @@ CREATE TABLE test_chat_schema.group_relays ( relay_link bytea, conf_id bytea, created_at text DEFAULT now() NOT NULL, - updated_at text DEFAULT now() NOT NULL + updated_at text DEFAULT now() NOT NULL, + base_web_url text ); @@ -962,7 +967,7 @@ CREATE TABLE test_chat_schema.groups ( public_member_count bigint, relay_request_retries bigint DEFAULT 0 NOT NULL, relay_request_delay bigint DEFAULT 0 NOT NULL, - relay_request_execute_at timestamp with time zone DEFAULT '1970-01-01 04:00:00+04'::timestamp with time zone NOT NULL, + relay_request_execute_at timestamp with time zone DEFAULT '1970-01-01 01:00:00+01'::timestamp with time zone NOT NULL, relay_inactive_at timestamp with time zone ); diff --git a/src/Simplex/Chat/Store/SQLite/Migrations.hs b/src/Simplex/Chat/Store/SQLite/Migrations.hs index 9990ed74fd..89ef373af8 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations.hs +++ b/src/Simplex/Chat/Store/SQLite/Migrations.hs @@ -154,6 +154,7 @@ import Simplex.Chat.Store.SQLite.Migrations.M20260403_item_viewed import Simplex.Chat.Store.SQLite.Migrations.M20260429_relay_request_retries import Simplex.Chat.Store.SQLite.Migrations.M20260507_relay_inactive_at import Simplex.Chat.Store.SQLite.Migrations.M20260514_relay_request_group_link_index +import Simplex.Chat.Store.SQLite.Migrations.M20260515_public_group_access import Simplex.Messaging.Agent.Store.Shared (Migration (..)) schemaMigrations :: [(String, Query, Maybe Query)] @@ -307,7 +308,8 @@ schemaMigrations = ("20260403_item_viewed", m20260403_item_viewed, Just down_m20260403_item_viewed), ("20260429_relay_request_retries", m20260429_relay_request_retries, Just down_m20260429_relay_request_retries), ("20260507_relay_inactive_at", m20260507_relay_inactive_at, Just down_m20260507_relay_inactive_at), - ("20260514_relay_request_group_link_index", m20260514_relay_request_group_link_index, Just down_m20260514_relay_request_group_link_index) + ("20260514_relay_request_group_link_index", m20260514_relay_request_group_link_index, Just down_m20260514_relay_request_group_link_index), + ("20260515_public_group_access", m20260515_public_group_access, Just down_m20260515_public_group_access) ] -- | The list of migrations in ascending order by date diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/M20260515_public_group_access.hs b/src/Simplex/Chat/Store/SQLite/Migrations/M20260515_public_group_access.hs new file mode 100644 index 0000000000..69f1793d3b --- /dev/null +++ b/src/Simplex/Chat/Store/SQLite/Migrations/M20260515_public_group_access.hs @@ -0,0 +1,28 @@ +{-# LANGUAGE QuasiQuotes #-} + +module Simplex.Chat.Store.SQLite.Migrations.M20260515_public_group_access where + +import Database.SQLite.Simple (Query) +import Database.SQLite.Simple.QQ (sql) + +m20260515_public_group_access :: Query +m20260515_public_group_access = + [sql| +ALTER TABLE group_profiles ADD COLUMN group_web_page TEXT; +ALTER TABLE group_profiles ADD COLUMN group_domain TEXT; +ALTER TABLE group_profiles ADD COLUMN domain_web_page INTEGER; +ALTER TABLE group_profiles ADD COLUMN allow_embedding INTEGER; + +ALTER TABLE group_relays ADD COLUMN base_web_url TEXT; +|] + +down_m20260515_public_group_access :: Query +down_m20260515_public_group_access = + [sql| +ALTER TABLE group_relays DROP COLUMN base_web_url; + +ALTER TABLE group_profiles DROP COLUMN allow_embedding; +ALTER TABLE group_profiles DROP COLUMN domain_web_page; +ALTER TABLE group_profiles DROP COLUMN group_domain; +ALTER TABLE group_profiles DROP COLUMN group_web_page; +|] diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt b/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt index a7880799db..14c9226d2c 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt +++ b/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt @@ -143,6 +143,7 @@ Query: SELECT -- GroupInfo g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id, + gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission, g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id, @@ -979,6 +980,7 @@ SEARCH delivery_tasks USING COVERING INDEX idx_delivery_tasks_next (group_id=? A Query: SELECT gp.display_name, gp.full_name, gp.short_descr, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id, + gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, gp.preferences, gp.member_admission FROM group_profiles gp JOIN groups g ON gp.group_profile_id = g.group_profile_id @@ -1228,8 +1230,9 @@ Query: INSERT INTO group_profiles (display_name, full_name, short_descr, description, image, group_type, group_link, public_group_id, + group_web_page, group_domain, domain_web_page, allow_embedding, user_id, preferences, member_admission, created_at, updated_at) - VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) Plan: @@ -1752,6 +1755,7 @@ Query: UPDATE group_profiles SET display_name = ?, full_name = ?, short_descr = ?, description = ?, image = ?, group_type = ?, group_link = ?, + group_web_page = ?, group_domain = ?, domain_web_page = ?, allow_embedding = ?, preferences = ?, member_admission = ?, updated_at = ? WHERE group_profile_id IN ( SELECT group_profile_id @@ -5119,6 +5123,14 @@ SEARCH group_profiles USING INTEGER PRIMARY KEY (rowid=?) LIST SUBQUERY 1 SEARCH groups USING INTEGER PRIMARY KEY (rowid=?) +Query: + UPDATE group_relays + SET base_web_url = ?, updated_at = ? + WHERE group_member_id = ? + +Plan: +SEARCH group_relays USING INDEX idx_group_relays_group_member_id (group_member_id=?) + Query: UPDATE group_relays SET conf_id = ?, relay_link = ?, updated_at = ? @@ -5295,6 +5307,7 @@ Query: SELECT -- GroupInfo g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id, + gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission, g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id, @@ -5331,6 +5344,7 @@ Query: SELECT -- GroupInfo g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id, + gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission, g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id, @@ -5360,6 +5374,7 @@ Query: SELECT -- GroupInfo g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id, + gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission, g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id, @@ -5690,7 +5705,7 @@ SEARCH f USING INDEX idx_files_chat_item_id (chat_item_id=?) Query: SELECT gr.group_relay_id, gr.group_member_id, cr.chat_relay_id, cr.address, cr.display_name, cr.full_name, cr.short_descr, cr.image, cr.domains, cr.preset, cr.tested, cr.enabled, cr.deleted, - gr.relay_status, gr.relay_link + gr.relay_status, gr.relay_link, gr.base_web_url FROM group_relays gr JOIN chat_relays cr ON cr.chat_relay_id = gr.chat_relay_id @@ -5707,7 +5722,7 @@ SEARCH m USING INTEGER PRIMARY KEY (rowid=?) Query: SELECT gr.group_relay_id, gr.group_member_id, cr.chat_relay_id, cr.address, cr.display_name, cr.full_name, cr.short_descr, cr.image, cr.domains, cr.preset, cr.tested, cr.enabled, cr.deleted, - gr.relay_status, gr.relay_link + gr.relay_status, gr.relay_link, gr.base_web_url FROM group_relays gr JOIN chat_relays cr ON cr.chat_relay_id = gr.chat_relay_id WHERE gr.group_id = ? @@ -5718,7 +5733,7 @@ SEARCH cr USING INTEGER PRIMARY KEY (rowid=?) Query: SELECT gr.group_relay_id, gr.group_member_id, cr.chat_relay_id, cr.address, cr.display_name, cr.full_name, cr.short_descr, cr.image, cr.domains, cr.preset, cr.tested, cr.enabled, cr.deleted, - gr.relay_status, gr.relay_link + gr.relay_status, gr.relay_link, gr.base_web_url FROM group_relays gr JOIN chat_relays cr ON cr.chat_relay_id = gr.chat_relay_id WHERE gr.group_member_id = ? @@ -5729,7 +5744,7 @@ SEARCH cr USING INTEGER PRIMARY KEY (rowid=?) Query: SELECT gr.group_relay_id, gr.group_member_id, cr.chat_relay_id, cr.address, cr.display_name, cr.full_name, cr.short_descr, cr.image, cr.domains, cr.preset, cr.tested, cr.enabled, cr.deleted, - gr.relay_status, gr.relay_link + gr.relay_status, gr.relay_link, gr.base_web_url FROM group_relays gr JOIN chat_relays cr ON cr.chat_relay_id = gr.chat_relay_id WHERE gr.group_relay_id = ? diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql b/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql index 86c198670c..ccff26b38d 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql +++ b/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql @@ -125,7 +125,11 @@ CREATE TABLE group_profiles( short_descr TEXT, group_type TEXT, group_link BLOB, - public_group_id BLOB + public_group_id BLOB, + group_web_page TEXT, + group_domain TEXT, + domain_web_page INTEGER, + allow_embedding INTEGER ) STRICT; CREATE TABLE groups( group_id INTEGER PRIMARY KEY, -- local group ID @@ -778,6 +782,8 @@ CREATE TABLE group_relays( conf_id BLOB, created_at TEXT NOT NULL DEFAULT(datetime('now')), updated_at TEXT NOT NULL DEFAULT(datetime('now')) + , + base_web_url TEXT ) STRICT; CREATE INDEX contact_profiles_index ON contact_profiles( display_name, diff --git a/src/Simplex/Chat/Store/Shared.hs b/src/Simplex/Chat/Store/Shared.hs index af0958ed35..bd51b10329 100644 --- a/src/Simplex/Chat/Store/Shared.hs +++ b/src/Simplex/Chat/Store/Shared.hs @@ -665,18 +665,20 @@ type BusinessChatInfoRow = (Maybe BusinessChatType, Maybe MemberId, Maybe Member type GroupKeysRow = (Maybe C.PrivateKeyEd25519, Maybe C.PublicKeyEd25519, Maybe C.PrivateKeyEd25519) -type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe Text, Maybe ImageData, Maybe GroupType, Maybe ShortLinkContact, Maybe B64UrlByteString) :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. PreparedGroupRow :. BusinessChatInfoRow :. (BoolInt, Maybe RelayStatus, Maybe UIThemeEntityOverrides, Int64, Maybe Int64, Maybe CustomData, Maybe Int64, Int, Maybe ConnReqContact) :. GroupKeysRow :. GroupMemberRow +type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Text, Maybe Text, Maybe ImageData, Maybe GroupType, Maybe ShortLinkContact, Maybe B64UrlByteString) :. PublicGroupAccessRow :. (Maybe MsgFilter, Maybe BoolInt, BoolInt, Maybe GroupPreferences, Maybe GroupMemberAdmission) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime) :. PreparedGroupRow :. BusinessChatInfoRow :. (BoolInt, Maybe RelayStatus, Maybe UIThemeEntityOverrides, Int64, Maybe Int64, Maybe CustomData, Maybe Int64, Int, Maybe ConnReqContact) :. GroupKeysRow :. GroupMemberRow + +type PublicGroupAccessRow = (Maybe Text, Maybe Text, Maybe BoolInt, Maybe BoolInt) type GroupMemberRow = (GroupMemberId, GroupId, Int64, MemberId, VersionChat, VersionChat, GroupMemberRole, GroupMemberCategory, GroupMemberStatus, BoolInt, Maybe MemberRestrictionStatus) :. (Maybe Int64, Maybe GroupMemberId, ContactName, Maybe ContactId, ProfileId) :. ProfileRow :. (UTCTime, UTCTime) :. (Maybe UTCTime, Int64, Int64, Int64, Maybe UTCTime, Maybe C.PublicKeyEd25519, Maybe ShortLinkContact) type ProfileRow = (ProfileId, ContactName, Text, Maybe Text, Maybe ImageData, Maybe ConnLinkContact, Maybe ChatPeerType, LocalAlias, Maybe Preferences) toGroupInfo :: VersionRangeChat -> Int64 -> [ChatTagId] -> GroupInfoRow -> GroupInfo -toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName, fullName, shortDescr, localAlias, description, image, groupType_, groupLink_, publicGroupId_) :. (enableNtfs_, sendRcpts, BI favorite, groupPreferences, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. preparedGroupRow :. businessRow :. (BI useRelays, relayOwnStatus, uiThemes, currentMembers, publicMemberCount, customData, chatItemTTL, membersRequireAttention, viaGroupLinkUri) :. groupKeysRow :. userMemberRow) = +toGroupInfo vr userContactId chatTags ((groupId, localDisplayName, displayName, fullName, shortDescr, localAlias, description, image, groupType_, groupLink_, publicGroupId_) :. accessRow :. (enableNtfs_, sendRcpts, BI favorite, groupPreferences, memberAdmission) :. (createdAt, updatedAt, chatTs, userMemberProfileSentAt) :. preparedGroupRow :. businessRow :. (BI useRelays, relayOwnStatus, uiThemes, currentMembers, publicMemberCount, customData, chatItemTTL, membersRequireAttention, viaGroupLinkUri) :. groupKeysRow :. userMemberRow) = let membership = (toGroupMember userContactId userMemberRow) {memberChatVRange = vr} chatSettings = ChatSettings {enableNtfs = fromMaybe MFAll enableNtfs_, sendRcpts = unBI <$> sendRcpts, favorite} fullGroupPreferences = mergeGroupPreferences groupPreferences - publicGroup = toPublicGroupProfile groupType_ groupLink_ publicGroupId_ + publicGroup = toPublicGroupProfile groupType_ groupLink_ publicGroupId_ (toPublicGroupAccess accessRow) groupKeys = toGroupKeys publicGroupId_ groupKeysRow groupProfile = GroupProfile {displayName, fullName, shortDescr, description, image, publicGroup, groupPreferences, memberAdmission} businessChat = toBusinessChatInfo businessRow @@ -690,10 +692,25 @@ toPreparedGroup = \case Just PreparedGroup {connLinkToConnect = CCLink fullLink shortLink_, connLinkPreparedConnection, connLinkStartedConnection, welcomeSharedMsgId, requestSharedMsgId} _ -> Nothing -toPublicGroupProfile :: Maybe GroupType -> Maybe ShortLinkContact -> Maybe B64UrlByteString -> Maybe PublicGroupProfile -toPublicGroupProfile (Just groupType) (Just groupLink) (Just publicGroupId) = - Just PublicGroupProfile {groupType, groupLink, publicGroupId} -toPublicGroupProfile _ _ _ = Nothing +toPublicGroupProfile :: Maybe GroupType -> Maybe ShortLinkContact -> Maybe B64UrlByteString -> Maybe PublicGroupAccess -> Maybe PublicGroupProfile +toPublicGroupProfile (Just groupType) (Just groupLink) (Just publicGroupId) publicGroupAccess = + Just PublicGroupProfile {groupType, groupLink, publicGroupId, publicGroupAccess} +toPublicGroupProfile _ _ _ _ = Nothing + +publicGroupAccessRow :: Maybe PublicGroupProfile -> PublicGroupAccessRow +publicGroupAccessRow pgp = case pgp >>= publicGroupAccess of + Just PublicGroupAccess {groupWebPage, groupDomain, domainWebPage, allowEmbedding} -> + (groupWebPage, groupDomain, Just (BI domainWebPage), Just (BI allowEmbedding)) + Nothing -> (Nothing, Nothing, Nothing, Nothing) + +toPublicGroupAccess :: PublicGroupAccessRow -> Maybe PublicGroupAccess +toPublicGroupAccess (groupWebPage, groupDomain, domainWebPage_, allowEmbedding_) + | isJust groupWebPage || isJust groupDomain || domainWebPage || allowEmbedding = + Just PublicGroupAccess {groupWebPage, groupDomain, domainWebPage, allowEmbedding} + | otherwise = Nothing + where + domainWebPage = maybe False unBI domainWebPage_ + allowEmbedding = maybe False unBI allowEmbedding_ toGroupKeys :: Maybe B64UrlByteString -> GroupKeysRow -> Maybe GroupKeys toGroupKeys (Just publicGroupId) (rootPrivKey_, rootPubKey_, Just memberPrivKey) = @@ -760,6 +777,7 @@ groupInfoQueryFields = SELECT -- GroupInfo g.group_id, g.local_display_name, gp.display_name, gp.full_name, gp.short_descr, g.local_alias, gp.description, gp.image, gp.group_type, gp.group_link, gp.public_group_id, + gp.group_web_page, gp.group_domain, gp.domain_web_page, gp.allow_embedding, g.enable_ntfs, g.send_rcpts, g.favorite, gp.preferences, gp.member_admission, g.created_at, g.updated_at, g.chat_ts, g.user_member_profile_sent_at, g.conn_full_link_to_connect, g.conn_short_link_to_connect, g.conn_link_prepared_connection, g.conn_link_started_connection, g.welcome_shared_msg_id, g.request_shared_msg_id, diff --git a/src/Simplex/Chat/Types.hs b/src/Simplex/Chat/Types.hs index f2892898c4..189f730b67 100644 --- a/src/Simplex/Chat/Types.hs +++ b/src/Simplex/Chat/Types.hs @@ -793,10 +793,19 @@ instance FromField GroupType where fromField = fromTextField_ textDecode instance ToField GroupType where toField = toField . textEncode +data PublicGroupAccess = PublicGroupAccess + { groupWebPage :: Maybe Text, + groupDomain :: Maybe Text, + domainWebPage :: Bool, + allowEmbedding :: Bool + } + deriving (Eq, Show) + data PublicGroupProfile = PublicGroupProfile { groupType :: GroupType, groupLink :: ShortLinkContact, - publicGroupId :: B64UrlByteString -- group identity = sha256(genesis root key), immutable + publicGroupId :: B64UrlByteString, -- group identity = sha256(genesis root key), immutable + publicGroupAccess :: Maybe PublicGroupAccess } deriving (Eq, Show) @@ -2084,6 +2093,8 @@ instance ToJSON GroupType where toJSON = textToJSON toEncoding = textToEncoding +$(JQ.deriveJSON defaultJSON ''PublicGroupAccess) + $(JQ.deriveJSON defaultJSON ''PublicGroupProfile) $(JQ.deriveJSON defaultJSON ''GroupProfile) diff --git a/tests/ChatTests/Groups.hs b/tests/ChatTests/Groups.hs index e0ff178db4..82906110c6 100644 --- a/tests/ChatTests/Groups.hs +++ b/tests/ChatTests/Groups.hs @@ -3251,6 +3251,12 @@ testGLinkReviewMember = alice ##> "/_delete member chat #1 5" alice <## "bad chat command: member is pending" + -- moderator can't accept member with a role higher than their own + dan ##> "/_accept member #1 5 admin" + dan <## "#team: you have insufficient permissions for this action, the required role is admin" + dan ##> "/_accept member #1 5 owner" + dan <## "#team: you have insufficient permissions for this action, the required role is owner" + -- accept member dan ##> "/_accept member #1 5 member" concurrentlyN_ From e3b3cdf2d7fbdb95f6a8910442ef482e45e8583b Mon Sep 17 00:00:00 2001 From: Evgeny Date: Mon, 1 Jun 2026 12:15:50 +0100 Subject: [PATCH 15/20] ui: translations (#7032) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Translated using Weblate (Hungarian) Currently translated at 100.0% (2392 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/ * Translated using Weblate (Arabic) Currently translated at 100.0% (2768 of 2768 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/ * Translated using Weblate (Italian) Currently translated at 100.0% (2768 of 2768 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/ * Translated using Weblate (Italian) Currently translated at 100.0% (2392 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/it/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2768 of 2768 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (2767 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2767 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Arabic) Currently translated at 100.0% (2767 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/ * Translated using Weblate (Russian) Currently translated at 100.0% (2392 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/ru/ * Translated using Weblate (Russian) Currently translated at 99.9% (2765 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ru/ * Translated using Weblate (Russian) Currently translated at 99.9% (2765 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ru/ * Translated using Weblate (Czech) Currently translated at 91.1% (2523 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/cs/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2392 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2767 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Italian) Currently translated at 100.0% (2767 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/ * Translated using Weblate (German) Currently translated at 100.0% (2767 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/ * Translated using Weblate (German) Currently translated at 100.0% (2392 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/ * Translated using Weblate (Czech) Currently translated at 94.6% (2619 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/cs/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2392 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/ * Translated using Weblate (Arabic) Currently translated at 100.0% (2768 of 2768 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/ * Translated using Weblate (Italian) Currently translated at 100.0% (2768 of 2768 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/ * Translated using Weblate (Italian) Currently translated at 100.0% (2392 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/it/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2768 of 2768 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (2767 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2767 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Arabic) Currently translated at 100.0% (2767 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/ * Translated using Weblate (Russian) Currently translated at 100.0% (2392 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/ru/ * Translated using Weblate (Russian) Currently translated at 99.9% (2765 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ru/ * Translated using Weblate (Russian) Currently translated at 99.9% (2765 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ru/ * Translated using Weblate (Czech) Currently translated at 91.1% (2523 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/cs/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2392 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2767 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Italian) Currently translated at 100.0% (2767 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/ * Translated using Weblate (German) Currently translated at 100.0% (2767 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/ * Translated using Weblate (German) Currently translated at 100.0% (2392 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/ * Translated using Weblate (Czech) Currently translated at 94.6% (2619 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/cs/ * Translated using Weblate (Spanish) Currently translated at 100.0% (2392 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/es/ * Translated using Weblate (Spanish) Currently translated at 100.0% (2767 of 2767 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2392 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2768 of 2768 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2783 of 2783 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Italian) Currently translated at 100.0% (2783 of 2783 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (2783 of 2783 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/ * Translated using Weblate (German) Currently translated at 100.0% (2783 of 2783 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/ * Translated using Weblate (Arabic) Currently translated at 100.0% (2783 of 2783 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/ * Translated using Weblate (Turkish) Currently translated at 89.6% (2145 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/tr/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2783 of 2783 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2392 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2793 of 2793 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (German) Currently translated at 100.0% (2392 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/ * Translated using Weblate (German) Currently translated at 100.0% (2793 of 2793 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (2793 of 2793 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2795 of 2795 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (2795 of 2795 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/ * Translated using Weblate (Italian) Currently translated at 100.0% (2795 of 2795 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/ * Translated using Weblate (German) Currently translated at 100.0% (2795 of 2795 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/ * Translated using Weblate (Arabic) Currently translated at 100.0% (2795 of 2795 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/ * Translated using Weblate (Spanish) Currently translated at 100.0% (2795 of 2795 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/ * Translated using Weblate (Czech) Currently translated at 97.1% (2716 of 2795 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/cs/ * Translated using Weblate (Russian) Currently translated at 99.0% (2769 of 2795 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ru/ * Translated using Weblate (Italian) Currently translated at 100.0% (2800 of 2800 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/ * Translated using Weblate (German) Currently translated at 100.0% (2800 of 2800 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/ * Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 100.0% (2800 of 2800 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2392 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2800 of 2800 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2800 of 2800 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Arabic) Currently translated at 100.0% (2800 of 2800 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2800 of 2800 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2392 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2392 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2800 of 2800 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2392 of 2392 strings) Translation: SimpleX Chat/SimpleX Chat iOS Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (2800 of 2800 strings) Translation: SimpleX Chat/SimpleX Chat Android Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/ * process localizations --------- Co-authored-by: summoner001 Co-authored-by: jonnysemon Co-authored-by: Random Co-authored-by: 大王叫我来巡山 Co-authored-by: Skyward Copied Co-authored-by: slrslr Co-authored-by: mlanp Co-authored-by: zenobit Co-authored-by: Isaac ALejandro Lopez Co-authored-by: echoloji Co-authored-by: No name Co-authored-by: Андрей Абрамов Co-authored-by: Ghost of Sparta --- .../bg.xcloc/Localized Contents/bg.xliff | 110 +++++++-- .../cs.xcloc/Localized Contents/cs.xliff | 110 +++++++-- .../de.xcloc/Localized Contents/de.xliff | 172 +++++++++---- .../en.xcloc/Localized Contents/en.xliff | 139 +++++++++-- .../es.xcloc/Localized Contents/es.xliff | 116 +++++++-- .../fi.xcloc/Localized Contents/fi.xliff | 110 +++++++-- .../fr.xcloc/Localized Contents/fr.xliff | 110 +++++++-- .../hu.xcloc/Localized Contents/hu.xliff | 200 ++++++++++----- .../it.xcloc/Localized Contents/it.xliff | 118 +++++++-- .../ja.xcloc/Localized Contents/ja.xliff | 110 +++++++-- .../nl.xcloc/Localized Contents/nl.xliff | 110 +++++++-- .../pl.xcloc/Localized Contents/pl.xliff | 110 +++++++-- .../ru.xcloc/Localized Contents/ru.xliff | 116 +++++++-- .../th.xcloc/Localized Contents/th.xliff | 110 +++++++-- .../tr.xcloc/Localized Contents/tr.xliff | 120 +++++++-- .../uk.xcloc/Localized Contents/uk.xliff | 110 +++++++-- .../Localized Contents/zh-Hans.xliff | 110 +++++++-- .../SimpleX SE/hu.lproj/Localizable.strings | 4 +- apps/ios/bg.lproj/Localizable.strings | 2 +- apps/ios/cs.lproj/Localizable.strings | 2 +- apps/ios/de.lproj/Localizable.strings | 72 +++--- apps/ios/es.lproj/Localizable.strings | 16 +- apps/ios/fi.lproj/Localizable.strings | 2 +- apps/ios/fr.lproj/Localizable.strings | 2 +- apps/ios/hu.lproj/Localizable.strings | 96 ++++---- apps/ios/it.lproj/Localizable.strings | 18 +- apps/ios/ja.lproj/Localizable.strings | 2 +- apps/ios/nl.lproj/Localizable.strings | 2 +- apps/ios/pl.lproj/Localizable.strings | 2 +- apps/ios/ru.lproj/Localizable.strings | 16 +- apps/ios/th.lproj/Localizable.strings | 2 +- apps/ios/tr.lproj/Localizable.strings | 37 ++- apps/ios/uk.lproj/Localizable.strings | 2 +- apps/ios/zh-Hans.lproj/Localizable.strings | 2 +- .../commonMain/resources/MR/ar/strings.xml | 196 +++++++++++++-- .../commonMain/resources/MR/cs/strings.xml | 228 +++++++++++++++++- .../commonMain/resources/MR/de/strings.xml | 90 ++++--- .../commonMain/resources/MR/es/strings.xml | 31 ++- .../commonMain/resources/MR/hu/strings.xml | 120 +++++---- .../commonMain/resources/MR/it/strings.xml | 42 +++- .../commonMain/resources/MR/ru/strings.xml | 11 +- .../resources/MR/zh-rCN/strings.xml | 36 ++- 42 files changed, 2516 insertions(+), 598 deletions(-) diff --git a/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff b/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff index 71a7a427be..364cee97e5 100644 --- a/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff +++ b/apps/ios/SimpleX Localizations/bg.xcloc/Localized Contents/bg.xliff @@ -715,6 +715,10 @@ swipe action Активни връзки No comment provided by engineer. + + Add + No comment provided by engineer. + Add address to your profile, so that your SimpleX contacts can share it with other people. Profile update will be sent to your SimpleX contacts. No comment provided by engineer. @@ -739,6 +743,14 @@ swipe action Добави профил No comment provided by engineer. + + Add relays + No comment provided by engineer. + + + Add relays to restore message delivery. + No comment provided by engineer. + Add server Добави сървър @@ -784,10 +796,6 @@ swipe action Добавени сървъри за съобщения No comment provided by engineer. - - Adding relays will be supported later. - No comment provided by engineer. - Additional accent Допълнителен акцент @@ -1157,6 +1165,10 @@ swipe action Сесия на приложението No comment provided by engineer. + + App update required + alert title + App version Версия на приложението @@ -1578,6 +1590,14 @@ in your network alert button new chat action + + Cancel and delete channel + No comment provided by engineer. + + + Cancel creating channel? + alert title + Cancel migration Отмени миграцията @@ -1722,8 +1742,8 @@ alert subtitle Channel will be deleted for you - this cannot be undone! No comment provided by engineer. - - Channel will start working with %1$d of %2$d relays. Proceed? + + Channel will start working with %1$d of %2$d relays. Continue? alert message @@ -2215,6 +2235,14 @@ This is your own one-time link! Свързване с настолно устройство No comment provided by engineer. + + Connecting via channel name requires a newer app version. + alert message + + + Connecting via contact name requires a newer app version. + alert message + Connection Връзка @@ -2360,7 +2388,7 @@ This is your own one-time link! Continue Продължи - No comment provided by engineer. + alert action Contribute @@ -2780,6 +2808,10 @@ swipe action Изтрий за мен No comment provided by engineer. + + Delete from history + No comment provided by engineer. + Delete group Изтрий група @@ -3514,6 +3546,10 @@ chat item action Error adding relay alert title + + Error adding relays + alert title + Error adding server alert title @@ -3632,6 +3668,10 @@ chat item action Грешка при изтриване на базата данни alert title + + Error deleting message + alert title + Error deleting old database Грешка при изтриване на старата база данни @@ -5677,6 +5717,10 @@ The most secure encryption. Приложението няма kод за достъп Authentication unavailable + + No available relays + No comment provided by engineer. + No chat relays No comment provided by engineer. @@ -5788,6 +5832,10 @@ The most secure encryption. Няма получени или изпратени файлове No comment provided by engineer. + + No relays + No comment provided by engineer. + No servers for private message routing. servers error @@ -6373,6 +6421,10 @@ Error: %@ Please try to disable and re-enable notfications. token info + + Please upgrade the app. + alert message + Please wait for group moderators to review your request to join the group. snd group event chat item @@ -6480,10 +6532,6 @@ Error: %@ Private routing timeout alert title - - Proceed - alert action - Profile and server connections Профилни и сървърни връзки @@ -6845,6 +6893,14 @@ swipe action Relay test failed! No comment provided by engineer. + + Relay will be removed from channel - this cannot be undone! + alert message + + + Relays added: %@. + alert message + Reliability: many relays per channel. No comment provided by engineer. @@ -6885,9 +6941,9 @@ swipe action Премахване на паролата от keychain? No comment provided by engineer. - - Remove subscriber - No comment provided by engineer. + + Remove relay? + alert title Remove subscriber? @@ -8078,6 +8134,10 @@ report reason Statistics No comment provided by engineer. + + Status + No comment provided by engineer. + Stop Спри @@ -8608,10 +8668,19 @@ your contacts and groups. Тази група вече не съществува. No comment provided by engineer. + + This group requires a newer version of the app. Please update the app to join. + alert message +alert subtitle + This is a chat relay address, it cannot be used to connect. alert message + + This is the last active relay. Removing it will prevent message delivery to subscribers. + alert message + This is your link for channel %@! new chat action @@ -8920,10 +8989,18 @@ To connect, please ask your contact to create another connection link and check Непрочетено swipe action + + Unsupported channel name + alert title + Unsupported connection link conn error description + + Unsupported contact name + alert title + Up to 100 last messages are sent to new members. На новите членове се изпращат до последните 100 съобщения. @@ -9852,6 +9929,11 @@ Repeat connection request? Your network No comment provided by engineer. + + Your new channel %1$@ is connected to %2$d of %3$d relays. +If you cancel, the channel will be deleted - you can create it again. + alert message + Your preferences Вашите настройки 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 1cc44dd7cb..5ba29ec846 100644 --- a/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff +++ b/apps/ios/SimpleX Localizations/cs.xcloc/Localized Contents/cs.xliff @@ -710,6 +710,10 @@ swipe action Aktivní spojení No comment provided by engineer. + + Add + No comment provided by engineer. + Add address to your profile, so that your SimpleX contacts can share it with other people. Profile update will be sent to your SimpleX contacts. No comment provided by engineer. @@ -732,6 +736,14 @@ swipe action Přidat profil No comment provided by engineer. + + Add relays + No comment provided by engineer. + + + Add relays to restore message delivery. + No comment provided by engineer. + Add server Přidat server @@ -776,10 +788,6 @@ swipe action Přidané servery zpráv No comment provided by engineer. - - Adding relays will be supported later. - No comment provided by engineer. - Additional accent Další zbarvení @@ -1140,6 +1148,10 @@ swipe action App session No comment provided by engineer. + + App update required + alert title + App version Verze aplikace @@ -1542,6 +1554,14 @@ in your network alert button new chat action + + Cancel and delete channel + No comment provided by engineer. + + + Cancel creating channel? + alert title + Cancel migration Zrušit přesun @@ -1686,8 +1706,8 @@ alert subtitle Channel will be deleted for you - this cannot be undone! No comment provided by engineer. - - Channel will start working with %1$d of %2$d relays. Proceed? + + Channel will start working with %1$d of %2$d relays. Continue? alert message @@ -2127,6 +2147,14 @@ Toto je váš vlastní jednorázový odkaz! Connecting to desktop No comment provided by engineer. + + Connecting via channel name requires a newer app version. + alert message + + + Connecting via contact name requires a newer app version. + alert message + Connection Připojení @@ -2264,7 +2292,7 @@ Toto je váš vlastní jednorázový odkaz! Continue Pokračovat - No comment provided by engineer. + alert action Contribute @@ -2673,6 +2701,10 @@ swipe action Smazat pro mě No comment provided by engineer. + + Delete from history + No comment provided by engineer. + Delete group Smazat skupinu @@ -3387,6 +3419,10 @@ chat item action Error adding relay alert title + + Error adding relays + alert title + Error adding server alert title @@ -3504,6 +3540,10 @@ chat item action Chyba při mazání databáze alert title + + Error deleting message + alert title + Error deleting old database Chyba při mazání staré databáze @@ -5489,6 +5529,10 @@ The most secure encryption. Žádné heslo aplikace Authentication unavailable + + No available relays + No comment provided by engineer. + No chat relays No comment provided by engineer. @@ -5599,6 +5643,10 @@ The most secure encryption. Žádné přijaté ani odeslané soubory No comment provided by engineer. + + No relays + No comment provided by engineer. + No servers for private message routing. servers error @@ -6169,6 +6217,10 @@ Error: %@ Please try to disable and re-enable notfications. token info + + Please upgrade the app. + alert message + Please wait for group moderators to review your request to join the group. snd group event chat item @@ -6275,10 +6327,6 @@ Error: %@ Private routing timeout alert title - - Proceed - alert action - Profile and server connections Profil a připojení k serveru @@ -6634,6 +6682,14 @@ swipe action Relay test failed! No comment provided by engineer. + + Relay will be removed from channel - this cannot be undone! + alert message + + + Relays added: %@. + alert message + Reliability: many relays per channel. No comment provided by engineer. @@ -6674,9 +6730,9 @@ swipe action Odstranit přístupovou frázi z klíčenek? No comment provided by engineer. - - Remove subscriber - No comment provided by engineer. + + Remove relay? + alert title Remove subscriber? @@ -7845,6 +7901,10 @@ report reason Statistics No comment provided by engineer. + + Status + No comment provided by engineer. + Stop Zastavit @@ -8367,10 +8427,19 @@ your contacts and groups. Tato skupina již neexistuje. No comment provided by engineer. + + This group requires a newer version of the app. Please update the app to join. + alert message +alert subtitle + This is a chat relay address, it cannot be used to connect. alert message + + This is the last active relay. Removing it will prevent message delivery to subscribers. + alert message + This is your link for channel %@! new chat action @@ -8670,10 +8739,18 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu Nepřečtený swipe action + + Unsupported channel name + alert title + Unsupported connection link conn error description + + Unsupported contact name + alert title + Up to 100 last messages are sent to new members. No comment provided by engineer. @@ -9567,6 +9644,11 @@ Repeat connection request? Your network No comment provided by engineer. + + Your new channel %1$@ is connected to %2$d of %3$d relays. +If you cancel, the channel will be deleted - you can create it again. + alert message + Your preferences Vaše preference 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 872fafddd7..797a489c92 100644 --- a/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff +++ b/apps/ios/SimpleX Localizations/de.xcloc/Localized Contents/de.xliff @@ -622,7 +622,7 @@ time interval A separate TCP connection will be used **for each chat profile you have in the app**. - **Für jedes von Ihnen in der App genutzte Chat-Profil** wird eine separate TCP-Verbindung genutzt. + **Für jedes von Ihnen in der App genutzte Chat-Profil** wird eine separate TCP-Verbindung (und SOCKS-Berechtigung) genutzt. No comment provided by engineer. @@ -736,6 +736,10 @@ swipe action Aktive Verbindungen No comment provided by engineer. + + Add + No comment provided by engineer. + Add address to your profile, so that your SimpleX contacts can share it with other people. Profile update will be sent to your SimpleX contacts. Fügen Sie die Adresse Ihrem Profil hinzu, damit Ihre SimpleX-Kontakte sie mit anderen Personen teilen können. Es wird eine Profilaktualisierung an Ihre SimpleX-Kontakte gesendet. @@ -761,6 +765,14 @@ swipe action Profil hinzufügen No comment provided by engineer. + + Add relays + No comment provided by engineer. + + + Add relays to restore message delivery. + No comment provided by engineer. + Add server Server hinzufügen @@ -806,11 +818,6 @@ swipe action Nachrichtenserver hinzugefügt No comment provided by engineer. - - Adding relays will be supported later. - Das Hinzufügen von Relais wird zu einem späteren Zeitpunkt unterstützt. - No comment provided by engineer. - Additional accent Erste Akzentfarbe @@ -963,7 +970,7 @@ swipe action All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays. - Alle Ihre Kontakte, Unterhaltungen und Dateien werden sicher verschlüsselt und in Daten-Paketen auf die konfigurierten XTFP-Relais hochgeladen. + Alle Ihre Kontakte, Unterhaltungen und Dateien werden sicher verschlüsselt und in Daten-Paketen auf die konfigurierten XTFP-Router hochgeladen. No comment provided by engineer. @@ -1123,12 +1130,12 @@ swipe action Always use private routing. - Sie nutzen immer privates Routing. + Immer privates Routing nutzen. No comment provided by engineer. Always use relay - Über ein Relais verbinden + Immer über einen Router verbinden No comment provided by engineer. @@ -1186,6 +1193,10 @@ swipe action App-Sitzung No comment provided by engineer. + + App update required + alert title + App version App Version @@ -1198,7 +1209,7 @@ swipe action Appearance - Erscheinungsbild + Darstellung No comment provided by engineer. @@ -1615,6 +1626,14 @@ in Ihrem Netzwerk alert button new chat action + + Cancel and delete channel + No comment provided by engineer. + + + Cancel creating channel? + alert title + Cancel migration Migration abbrechen @@ -1772,9 +1791,8 @@ alert subtitle Der Kanal wird für Sie gelöscht. Dies kann nicht rückgängig gemacht werden! No comment provided by engineer. - - Channel will start working with %1$d of %2$d relays. Proceed? - Der Kanal wird mit %1$d von %2$d Relais gestartet. Fortfahren? + + Channel will start working with %1$d of %2$d relays. Continue? alert message @@ -1864,7 +1882,7 @@ alert subtitle Chat profile - Benutzerprofil + Chat-Profil No comment provided by engineer. @@ -2070,7 +2088,7 @@ chat toolbar Conditions accepted on: %@. - Die Nutzungsbedingungen wurden akzeptiert am: %@. + Die Nutzungsbedingungen wurden am %@ akzeptiert. No comment provided by engineer. @@ -2095,12 +2113,12 @@ chat toolbar Conditions will be accepted on: %@. - Die Nutzungsbedingungen werden akzeptiert am: %@. + Die Nutzungsbedingungen werden am %@ akzeptiert. No comment provided by engineer. Conditions will be automatically accepted for enabled operators on: %@. - Die Nutzungsbedingungen der aktivierten Betreiber werden automatisch akzeptiert am: %@. + Die Nutzungsbedingungen der aktivierten Betreiber werden am %@ automatisch akzeptiert. No comment provided by engineer. @@ -2278,6 +2296,14 @@ Das ist Ihr eigener Einmal-Link! Mit dem Desktop verbinden No comment provided by engineer. + + Connecting via channel name requires a newer app version. + alert message + + + Connecting via contact name requires a newer app version. + alert message + Connection Verbindung @@ -2433,7 +2459,7 @@ Das ist Ihr eigener Einmal-Link! Continue Weiter - No comment provided by engineer. + alert action Contribute @@ -2879,6 +2905,10 @@ swipe action Nur bei mir löschen No comment provided by engineer. + + Delete from history + No comment provided by engineer. + Delete group Gruppe löschen @@ -3242,7 +3272,7 @@ alert button Do NOT use private routing. - Sie nutzen KEIN privates Routing. + KEIN privates Routing nutzen. No comment provided by engineer. @@ -3262,7 +3292,7 @@ alert button Do not use credentials with proxy. - Verwenden Sie keine Anmeldeinformationen mit einem Proxy. + Keine Anmeldeinformationen mit einem Proxy verwenden. No comment provided by engineer. @@ -3666,6 +3696,10 @@ chat item action Fehler beim Hinzufügen des Relais alert title + + Error adding relays + alert title + Error adding server Fehler beim Hinzufügen des Servers @@ -3796,6 +3830,10 @@ chat item action Fehler beim Löschen der Datenbank alert title + + Error deleting message + alert title + Error deleting old database Fehler beim Löschen der alten Datenbank @@ -5895,12 +5933,12 @@ wer mit wem kommuniziert New SOCKS credentials will be used every time you start the app. - Jedes Mal wenn Sie die App starten, werden neue SOCKS-Anmeldeinformationen genutzt + Bei jedem Neustart der App, werden neue SOCKS-Anmeldeinformationen genutzt. No comment provided by engineer. New SOCKS credentials will be used for each server. - Für jeden Server werden neue SOCKS-Anmeldeinformationen genutzt + Für jeden Server werden neue SOCKS-Anmeldeinformationen genutzt. No comment provided by engineer. @@ -6005,6 +6043,10 @@ Die sicherste Verschlüsselung. Kein App-Passwort Authentication unavailable + + No available relays + No comment provided by engineer. + No chat relays Keine Chat-Relais @@ -6130,9 +6172,13 @@ Die sicherste Verschlüsselung. Keine herunter- oder hochgeladenen Dateien No comment provided by engineer. + + No relays + No comment provided by engineer. + No servers for private message routing. - Keine Server für privates Nachrichten-Routing. + Keine Router für privates Nachrichten-Routing. servers error @@ -6778,6 +6824,10 @@ Fehler: %@ Bitte versuchen Sie, die Benachrichtigungen zu deaktivieren und wieder zu aktivieren. token info + + Please upgrade the app. + alert message + Please wait for group moderators to review your request to join the group. Bitte warten Sie auf die Überprüfung Ihrer Anfrage durch die Gruppen-Moderatoren, um der Gruppe beitreten zu können. @@ -6855,7 +6905,7 @@ Fehler: %@ Privacy: for owners and subscribers. - Privatsphäre: für Besitzer und Abonnenten. + Privatsphäre: Für Eigentümer und Abonnenten. No comment provided by engineer. @@ -6903,11 +6953,6 @@ Fehler: %@ Zeitüberschreitung der privaten Routing-Sitzung alert title - - Proceed - Fortfahren - alert action - Profile and server connections Profil und Serververbindungen @@ -7011,7 +7056,7 @@ Fehler: %@ Protect your IP address from the messaging relays chosen by your contacts. Enable in *Network & servers* settings. - Schützen Sie Ihre IP-Adresse vor den Nachrichten-Relais, die Ihre Kontakte ausgewählt haben. + Schützen Sie Ihre IP-Adresse vor den Nachrichten-Routern, die Ihre Kontakte ausgewählt haben. Aktivieren Sie es in den *Netzwerk & Server* Einstellungen. No comment provided by engineer. @@ -7294,7 +7339,7 @@ swipe action Relay server protects your IP address, but it can observe the duration of the call. - Relais-Server schützen Ihre IP-Adresse, aber sie können die Anrufdauer erfassen. + Relais-Server schützen Ihre IP-Adresse, können aber die Anrufdauer erfassen. No comment provided by engineer. @@ -7302,9 +7347,17 @@ swipe action Relais-Test fehlgeschlagen! No comment provided by engineer. + + Relay will be removed from channel - this cannot be undone! + alert message + + + Relays added: %@. + alert message + Reliability: many relays per channel. - Zuverlässigkeit: mehrere Relais pro Kanal. + Zuverlässigkeit: Mehrere Relais pro Kanal. No comment provided by engineer. @@ -7347,10 +7400,9 @@ swipe action Passwort aus dem Schlüsselbund entfernen? No comment provided by engineer. - - Remove subscriber - Abonnent entfernen - No comment provided by engineer. + + Remove relay? + alert title Remove subscriber? @@ -7850,7 +7902,7 @@ chat item action Security: owners hold channel keys. - Sicherheit: Eigentümer besitzen die Kanalschlüssel. + Sicherheit: Nur die Eigentümer des Kanals besitzen die Schlüssel. No comment provided by engineer. @@ -7945,12 +7997,12 @@ chat item action Send messages directly when IP address is protected and your or destination server does not support private routing. - Nachrichten werden direkt versendet, wenn die IP-Adresse geschützt ist, und Ihr oder der Zielserver kein privates Routing unterstützt. + Nachrichten werden direkt versendet, wenn die IP-Adresse geschützt ist, und Ihr oder der Ziel-Server kein privates Routing unterstützt. No comment provided by engineer. Send messages directly when your or destination server does not support private routing. - Nachrichten werden direkt versendet, wenn Ihr oder der Zielserver kein privates Routing unterstützt. + Nachrichten werden direkt versendet, wenn Ihr oder der Ziel-Server kein privates Routing unterstützt. No comment provided by engineer. @@ -8150,7 +8202,7 @@ chat item action Server requires authorization to connect to relay, check password. - Der Server erfordert eine Autorisierung, um eine Verbindung zum Relais herzustellen. Bitte Passwort überprüfen. + Der Server erfordert eine Autorisierung, um eine Verbindung zum Router herzustellen. Bitte Passwort überprüfen. relay test error @@ -8366,7 +8418,7 @@ chat item action Share relay address - Relais-Adresse teilen + Router-Adresse teilen No comment provided by engineer. @@ -8667,6 +8719,10 @@ report reason Statistiken No comment provided by engineer. + + Status + No comment provided by engineer. + Stop Beenden @@ -9254,11 +9310,20 @@ in dem Sie Ihre Kontakte und Gruppen besitzen. Diese Gruppe existiert nicht mehr. No comment provided by engineer. + + This group requires a newer version of the app. Please update the app to join. + alert message +alert subtitle + This is a chat relay address, it cannot be used to connect. Dies ist eine Chat‑Relais-Adresse, welche nicht zum Verbinden verwendet werden kann. alert message + + This is the last active relay. Removing it will prevent message delivery to subscribers. + alert message + This is your link for channel %@! Dies ist Ihr Link für den Kanal %@! @@ -9336,7 +9401,7 @@ in dem Sie Ihre Kontakte und Gruppen besitzen. To protect your IP address, private routing uses your SMP servers to deliver messages. - Zum Schutz Ihrer IP-Adresse, wird für die Nachrichten-Auslieferung privates Routing über Ihre konfigurierten SMP-Server genutzt. + Zum Schutz Ihrer IP-Adresse, wird für die Nachrichten-Auslieferung privates Routing über Ihre konfigurierten SMP-Router genutzt. No comment provided by engineer. @@ -9593,11 +9658,19 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Ungelesen swipe action + + Unsupported channel name + alert title + Unsupported connection link Verbindungs-Link wird nicht unterstützt conn error description + + Unsupported contact name + alert title + Up to 100 last messages are sent to new members. Bis zu 100 der letzten Nachrichten werden an neue Mitglieder gesendet. @@ -9785,12 +9858,12 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Use private routing with unknown servers when IP address is not protected. - Sie nutzen privates Routing mit unbekannten Servern, wenn Ihre IP-Adresse nicht geschützt ist. + Bei unbekannten Servern privates Routing nutzen, wenn Ihre IP-Adresse nicht geschützt ist. No comment provided by engineer. Use private routing with unknown servers. - Sie nutzen privates Routing mit unbekannten Servern. + Bei unbekannten Servern privates Routing nutzen. No comment provided by engineer. @@ -10120,7 +10193,7 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s Without Tor or VPN, your IP address will be visible to these XFTP relays: %@. - Ohne Tor- oder VPN-Nutzung wird Ihre IP-Adresse für diese XFTP-Relais sichtbar sein: %@. + Ohne Tor- oder VPN-Nutzung wird Ihre IP-Adresse für diese XFTP-Router sichtbar sein: %@. alert message @@ -10237,7 +10310,7 @@ Verbindungsanfrage wiederholen? You can change it in Appearance settings. - Kann von Ihnen in den Erscheinungsbild-Einstellungen geändert werden. + Sie können dies in den Einstellungen unter „Darstellung“ ändern. No comment provided by engineer. @@ -10606,6 +10679,11 @@ Verbindungsanfrage wiederholen? Ihr Netzwerk No comment provided by engineer. + + Your new channel %1$@ is connected to %2$d of %3$d relays. +If you cancel, the channel will be deleted - you can create it again. + alert message + Your preferences Ihre Präferenzen @@ -11659,7 +11737,7 @@ Zuletzt empfangene Nachricht: %2$@ unknown servers - Unbekannte Relais + Unbekannte Server 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 5e95cf39cc..c108dcc904 100644 --- a/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff +++ b/apps/ios/SimpleX Localizations/en.xcloc/Localized Contents/en.xliff @@ -736,6 +736,11 @@ swipe action Active connections No comment provided by engineer. + + Add + Add + No comment provided by engineer. + Add address to your profile, so that your SimpleX contacts can share it with other people. Profile update will be sent to your SimpleX contacts. Add address to your profile, so that your SimpleX contacts can share it with other people. Profile update will be sent to your SimpleX contacts. @@ -761,6 +766,16 @@ swipe action Add profile No comment provided by engineer. + + Add relays + Add relays + No comment provided by engineer. + + + Add relays to restore message delivery. + Add relays to restore message delivery. + No comment provided by engineer. + Add server Add server @@ -806,11 +821,6 @@ swipe action Added message servers No comment provided by engineer. - - Adding relays will be supported later. - Adding relays will be supported later. - No comment provided by engineer. - Additional accent Additional accent @@ -1186,6 +1196,11 @@ swipe action App session No comment provided by engineer. + + App update required + App update required + alert title + App version App version @@ -1615,6 +1630,16 @@ in your network alert button new chat action + + Cancel and delete channel + Cancel and delete channel + No comment provided by engineer. + + + Cancel creating channel? + Cancel creating channel? + alert title + Cancel migration Cancel migration @@ -1772,9 +1797,9 @@ alert subtitle Channel will be deleted for you - this cannot be undone! No comment provided by engineer. - - Channel will start working with %1$d of %2$d relays. Proceed? - Channel will start working with %1$d of %2$d relays. Proceed? + + Channel will start working with %1$d of %2$d relays. Continue? + Channel will start working with %1$d of %2$d relays. Continue? alert message @@ -2278,6 +2303,16 @@ This is your own one-time link! Connecting to desktop No comment provided by engineer. + + Connecting via channel name requires a newer app version. + Connecting via channel name requires a newer app version. + alert message + + + Connecting via contact name requires a newer app version. + Connecting via contact name requires a newer app version. + alert message + Connection Connection @@ -2433,7 +2468,7 @@ This is your own one-time link! Continue Continue - No comment provided by engineer. + alert action Contribute @@ -2879,6 +2914,11 @@ swipe action Delete for me No comment provided by engineer. + + Delete from history + Delete from history + No comment provided by engineer. + Delete group Delete group @@ -3666,6 +3706,11 @@ chat item action Error adding relay alert title + + Error adding relays + Error adding relays + alert title + Error adding server Error adding server @@ -3796,6 +3841,11 @@ chat item action Error deleting database alert title + + Error deleting message + Error deleting message + alert title + Error deleting old database Error deleting old database @@ -6005,6 +6055,11 @@ The most secure encryption. No app password Authentication unavailable + + No available relays + No available relays + No comment provided by engineer. + No chat relays No chat relays @@ -6130,6 +6185,11 @@ The most secure encryption. No received or sent files No comment provided by engineer. + + No relays + No relays + No comment provided by engineer. + No servers for private message routing. No servers for private message routing. @@ -6778,6 +6838,11 @@ Error: %@ Please try to disable and re-enable notfications. token info + + Please upgrade the app. + Please upgrade the app. + alert message + Please wait for group moderators to review your request to join the group. Please wait for group moderators to review your request to join the group. @@ -6903,11 +6968,6 @@ Error: %@ Private routing timeout alert title - - Proceed - Proceed - alert action - Profile and server connections Profile and server connections @@ -7302,6 +7362,16 @@ swipe action Relay test failed! No comment provided by engineer. + + Relay will be removed from channel - this cannot be undone! + Relay will be removed from channel - this cannot be undone! + alert message + + + Relays added: %@. + Relays added: %@. + alert message + Reliability: many relays per channel. Reliability: many relays per channel. @@ -7347,10 +7417,10 @@ swipe action Remove passphrase from keychain? No comment provided by engineer. - - Remove subscriber - Remove subscriber - No comment provided by engineer. + + Remove relay? + Remove relay? + alert title Remove subscriber? @@ -8667,6 +8737,11 @@ report reason Statistics No comment provided by engineer. + + Status + Status + No comment provided by engineer. + Stop Stop @@ -9254,11 +9329,22 @@ your contacts and groups. This group no longer exists. No comment provided by engineer. + + This group requires a newer version of the app. Please update the app to join. + This group requires a newer version of the app. Please update the app to join. + alert message +alert subtitle + This is a chat relay address, it cannot be used to connect. This is a chat relay address, it cannot be used to connect. alert message + + This is the last active relay. Removing it will prevent message delivery to subscribers. + This is the last active relay. Removing it will prevent message delivery to subscribers. + alert message + This is your link for channel %@! This is your link for channel %@! @@ -9593,11 +9679,21 @@ To connect, please ask your contact to create another connection link and check Unread swipe action + + Unsupported channel name + Unsupported channel name + alert title + Unsupported connection link Unsupported connection link conn error description + + Unsupported contact name + Unsupported contact name + alert title + Up to 100 last messages are sent to new members. Up to 100 last messages are sent to new members. @@ -10606,6 +10702,13 @@ Repeat connection request? Your network No comment provided by engineer. + + Your new channel %1$@ is connected to %2$d of %3$d relays. +If you cancel, the channel will be deleted - you can create it again. + Your new channel %1$@ is connected to %2$d of %3$d relays. +If you cancel, the channel will be deleted - you can create it again. + alert message + Your preferences Your preferences 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 43d3895cc4..d93e692a63 100644 --- a/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff +++ b/apps/ios/SimpleX Localizations/es.xcloc/Localized Contents/es.xliff @@ -7,7 +7,7 @@ (can be copied) - (puede copiarse) + (puede ser copiado) No comment provided by engineer. @@ -736,6 +736,10 @@ swipe action Conexiones activas No comment provided by engineer. + + Add + No comment provided by engineer. + Add address to your profile, so that your SimpleX contacts can share it with other people. Profile update will be sent to your SimpleX contacts. Añade la dirección a tu perfil para que tus contactos SimpleX puedan compartirla con otros. La actualización del perfil se enviará a tus contactos SimpleX. @@ -761,6 +765,14 @@ swipe action Añadir perfil No comment provided by engineer. + + Add relays + No comment provided by engineer. + + + Add relays to restore message delivery. + No comment provided by engineer. + Add server Añadir servidor @@ -806,11 +818,6 @@ swipe action Servidores de mensajes añadidos No comment provided by engineer. - - Adding relays will be supported later. - Añadir servidores estará disponible en una versión posterior. - No comment provided by engineer. - Additional accent Acento adicional @@ -1186,6 +1193,10 @@ swipe action por sesión No comment provided by engineer. + + App update required + alert title + App version Versión de la aplicación @@ -1615,6 +1626,14 @@ en tu red alert button new chat action + + Cancel and delete channel + No comment provided by engineer. + + + Cancel creating channel? + alert title + Cancel migration Cancelar migración @@ -1772,9 +1791,8 @@ alert subtitle El canal será eliminado para tí. ¡No puede deshacerse! No comment provided by engineer. - - Channel will start working with %1$d of %2$d relays. Proceed? - El canal comenzará a funcionar con %1$d de %2$d servidores. ¿Continuar? + + Channel will start working with %1$d of %2$d relays. Continue? alert message @@ -2278,6 +2296,14 @@ This is your own one-time link! Conectando con ordenador No comment provided by engineer. + + Connecting via channel name requires a newer app version. + alert message + + + Connecting via contact name requires a newer app version. + alert message + Connection Conexión @@ -2433,7 +2459,7 @@ This is your own one-time link! Continue Continuar - No comment provided by engineer. + alert action Contribute @@ -2879,6 +2905,10 @@ swipe action Eliminar para mí No comment provided by engineer. + + Delete from history + No comment provided by engineer. + Delete group Eliminar grupo @@ -3666,6 +3696,10 @@ chat item action Error al añadir el servidor alert title + + Error adding relays + alert title + Error adding server Error al añadir servidor @@ -3796,6 +3830,10 @@ chat item action Error al eliminar base de datos alert title + + Error deleting message + alert title + Error deleting old database Error al eliminar base de datos antigua @@ -6005,6 +6043,10 @@ El cifrado más seguro. Sin contraseña de la aplicación Authentication unavailable + + No available relays + No comment provided by engineer. + No chat relays Sin servidores de chat @@ -6130,6 +6172,10 @@ El cifrado más seguro. Sin archivos recibidos o enviados No comment provided by engineer. + + No relays + No comment provided by engineer. + No servers for private message routing. Sin servidores para enrutamiento privado. @@ -6778,6 +6824,10 @@ Error: %@ Por favor, intenta desactivar y reactivar las notificaciones. token info + + Please upgrade the app. + alert message + Please wait for group moderators to review your request to join the group. Por favor, espera a que tu solicitud sea revisada por los moderadores del grupo. @@ -6903,11 +6953,6 @@ Error: %@ Timeout enrutamiento privado alert title - - Proceed - Continuar - alert action - Profile and server connections Eliminar perfil y conexiones @@ -7302,6 +7347,14 @@ swipe action ¡El test del servidor ha fallado! No comment provided by engineer. + + Relay will be removed from channel - this cannot be undone! + alert message + + + Relays added: %@. + alert message + Reliability: many relays per channel. Fiabilidad: muchos servidores por canal. @@ -7347,10 +7400,9 @@ swipe action ¿Eliminar contraseña de Keychain? No comment provided by engineer. - - Remove subscriber - Eliminar suscriptor - No comment provided by engineer. + + Remove relay? + alert title Remove subscriber? @@ -8667,6 +8719,10 @@ report reason Estadísticas No comment provided by engineer. + + Status + No comment provided by engineer. + Stop Parar @@ -9254,11 +9310,20 @@ y los contactos son tuyos. Este grupo ya no existe. No comment provided by engineer. + + This group requires a newer version of the app. Please update the app to join. + alert message +alert subtitle + This is a chat relay address, it cannot be used to connect. Esto es una dirección de servidor, no puede usarse para conectar. alert message + + This is the last active relay. Removing it will prevent message delivery to subscribers. + alert message + This is your link for channel %@! Este es tu enlace para el canal %@! @@ -9593,11 +9658,19 @@ Para conectarte pide a tu contacto que cree otro enlace y comprueba la conexión No leído swipe action + + Unsupported channel name + alert title + Unsupported connection link Enlace de conexión no compatible conn error description + + Unsupported contact name + alert title + Up to 100 last messages are sent to new members. Hasta 100 últimos mensajes son enviados a los miembros nuevos. @@ -10606,6 +10679,11 @@ Repeat connection request? Tu red No comment provided by engineer. + + Your new channel %1$@ is connected to %2$d of %3$d relays. +If you cancel, the channel will be deleted - you can create it again. + alert message + Your preferences Mis preferencias diff --git a/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff b/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff index 892f686bd2..5656516b7d 100644 --- a/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff +++ b/apps/ios/SimpleX Localizations/fi.xcloc/Localized Contents/fi.xliff @@ -667,6 +667,10 @@ swipe action Active connections No comment provided by engineer. + + Add + No comment provided by engineer. + Add address to your profile, so that your SimpleX contacts can share it with other people. Profile update will be sent to your SimpleX contacts. No comment provided by engineer. @@ -688,6 +692,14 @@ swipe action Lisää profiili No comment provided by engineer. + + Add relays + No comment provided by engineer. + + + Add relays to restore message delivery. + No comment provided by engineer. + Add server Lisää palvelin @@ -728,10 +740,6 @@ swipe action Added message servers No comment provided by engineer. - - Adding relays will be supported later. - No comment provided by engineer. - Additional accent No comment provided by engineer. @@ -1069,6 +1077,10 @@ swipe action App session No comment provided by engineer. + + App update required + alert title + App version Sovellusversio @@ -1442,6 +1454,14 @@ in your network alert button new chat action + + Cancel and delete channel + No comment provided by engineer. + + + Cancel creating channel? + alert title + Cancel migration No comment provided by engineer. @@ -1580,8 +1600,8 @@ alert subtitle Channel will be deleted for you - this cannot be undone! No comment provided by engineer. - - Channel will start working with %1$d of %2$d relays. Proceed? + + Channel will start working with %1$d of %2$d relays. Continue? alert message @@ -2014,6 +2034,14 @@ This is your own one-time link! Connecting to desktop No comment provided by engineer. + + Connecting via channel name requires a newer app version. + alert message + + + Connecting via contact name requires a newer app version. + alert message + Connection Yhteys @@ -2151,7 +2179,7 @@ This is your own one-time link! Continue Jatka - No comment provided by engineer. + alert action Contribute @@ -2560,6 +2588,10 @@ swipe action Poista minulta No comment provided by engineer. + + Delete from history + No comment provided by engineer. + Delete group Poista ryhmä @@ -3273,6 +3305,10 @@ chat item action Error adding relay alert title + + Error adding relays + alert title + Error adding server alert title @@ -3389,6 +3425,10 @@ chat item action Virhe tietokannan poistamisessa alert title + + Error deleting message + alert title + Error deleting old database Virhe vanhan tietokannan poistamisessa @@ -5372,6 +5412,10 @@ The most secure encryption. Ei sovelluksen salasanaa Authentication unavailable + + No available relays + No comment provided by engineer. + No chat relays No comment provided by engineer. @@ -5482,6 +5526,10 @@ The most secure encryption. Ei vastaanotettuja tai lähetettyjä tiedostoja No comment provided by engineer. + + No relays + No comment provided by engineer. + No servers for private message routing. servers error @@ -6049,6 +6097,10 @@ Error: %@ Please try to disable and re-enable notfications. token info + + Please upgrade the app. + alert message + Please wait for group moderators to review your request to join the group. snd group event chat item @@ -6155,10 +6207,6 @@ Error: %@ Private routing timeout alert title - - Proceed - alert action - Profile and server connections Profiili- ja palvelinyhteydet @@ -6514,6 +6562,14 @@ swipe action Relay test failed! No comment provided by engineer. + + Relay will be removed from channel - this cannot be undone! + alert message + + + Relays added: %@. + alert message + Reliability: many relays per channel. No comment provided by engineer. @@ -6554,9 +6610,9 @@ swipe action Poista tunnuslause avainnipusta? No comment provided by engineer. - - Remove subscriber - No comment provided by engineer. + + Remove relay? + alert title Remove subscriber? @@ -7723,6 +7779,10 @@ report reason Statistics No comment provided by engineer. + + Status + No comment provided by engineer. + Stop Lopeta @@ -8242,10 +8302,19 @@ your contacts and groups. Tätä ryhmää ei enää ole olemassa. No comment provided by engineer. + + This group requires a newer version of the app. Please update the app to join. + alert message +alert subtitle + This is a chat relay address, it cannot be used to connect. alert message + + This is the last active relay. Removing it will prevent message delivery to subscribers. + alert message + This is your link for channel %@! new chat action @@ -8544,10 +8613,18 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja Lukematon swipe action + + Unsupported channel name + alert title + Unsupported connection link conn error description + + Unsupported contact name + alert title + Up to 100 last messages are sent to new members. No comment provided by engineer. @@ -9439,6 +9516,11 @@ Repeat connection request? Your network No comment provided by engineer. + + Your new channel %1$@ is connected to %2$d of %3$d relays. +If you cancel, the channel will be deleted - you can create it again. + alert message + Your preferences Asetuksesi 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 be6a766ca1..3ea0859d76 100644 --- a/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff +++ b/apps/ios/SimpleX Localizations/fr.xcloc/Localized Contents/fr.xliff @@ -715,6 +715,10 @@ swipe action Connections actives No comment provided by engineer. + + Add + No comment provided by engineer. + Add address to your profile, so that your SimpleX contacts can share it with other people. Profile update will be sent to your SimpleX contacts. No comment provided by engineer. @@ -739,6 +743,14 @@ swipe action Ajouter un profil No comment provided by engineer. + + Add relays + No comment provided by engineer. + + + Add relays to restore message delivery. + No comment provided by engineer. + Add server Ajouter un serveur @@ -784,10 +796,6 @@ swipe action Ajout de serveurs de messages No comment provided by engineer. - - Adding relays will be supported later. - No comment provided by engineer. - Additional accent Accent additionnel @@ -1156,6 +1164,10 @@ swipe action Session de l'app No comment provided by engineer. + + App update required + alert title + App version Version de l'app @@ -1571,6 +1583,14 @@ in your network alert button new chat action + + Cancel and delete channel + No comment provided by engineer. + + + Cancel creating channel? + alert title + Cancel migration Annuler le transfert @@ -1715,8 +1735,8 @@ alert subtitle Channel will be deleted for you - this cannot be undone! No comment provided by engineer. - - Channel will start working with %1$d of %2$d relays. Proceed? + + Channel will start working with %1$d of %2$d relays. Continue? alert message @@ -2203,6 +2223,14 @@ Il s'agit de votre propre lien unique ! Connexion au bureau No comment provided by engineer. + + Connecting via channel name requires a newer app version. + alert message + + + Connecting via contact name requires a newer app version. + alert message + Connection Connexion @@ -2355,7 +2383,7 @@ Il s'agit de votre propre lien unique ! Continue Continuer - No comment provided by engineer. + alert action Contribute @@ -2791,6 +2819,10 @@ swipe action Supprimer pour moi No comment provided by engineer. + + Delete from history + No comment provided by engineer. + Delete group Supprimer le groupe @@ -3558,6 +3590,10 @@ chat item action Error adding relay alert title + + Error adding relays + alert title + Error adding server Erreur lors de l'ajout du serveur @@ -3683,6 +3719,10 @@ chat item action Erreur lors de la suppression de la base de données alert title + + Error deleting message + alert title + Error deleting old database Erreur lors de la suppression de l'ancienne base de données @@ -5816,6 +5856,10 @@ The most secure encryption. Pas de mot de passe pour l'app Authentication unavailable + + No available relays + No comment provided by engineer. + No chat relays No comment provided by engineer. @@ -5933,6 +5977,10 @@ The most secure encryption. Aucun fichier reçu ou envoyé No comment provided by engineer. + + No relays + No comment provided by engineer. + No servers for private message routing. Pas de serveurs pour le routage privé des messages. @@ -6541,6 +6589,10 @@ Erreur : %@ Please try to disable and re-enable notfications. token info + + Please upgrade the app. + alert message + Please wait for group moderators to review your request to join the group. snd group event chat item @@ -6656,10 +6708,6 @@ Erreur : %@ Private routing timeout alert title - - Proceed - alert action - Profile and server connections Profil et connexions au serveur @@ -7038,6 +7086,14 @@ swipe action Relay test failed! No comment provided by engineer. + + Relay will be removed from channel - this cannot be undone! + alert message + + + Relays added: %@. + alert message + Reliability: many relays per channel. No comment provided by engineer. @@ -7080,9 +7136,9 @@ swipe action Supprimer la phrase secrète de la keychain ? No comment provided by engineer. - - Remove subscriber - No comment provided by engineer. + + Remove relay? + alert title Remove subscriber? @@ -8339,6 +8395,10 @@ report reason Statistiques No comment provided by engineer. + + Status + No comment provided by engineer. + Stop Arrêter @@ -8889,10 +8949,19 @@ your contacts and groups. Ce groupe n'existe plus. No comment provided by engineer. + + This group requires a newer version of the app. Please update the app to join. + alert message +alert subtitle + This is a chat relay address, it cannot be used to connect. alert message + + This is the last active relay. Removing it will prevent message delivery to subscribers. + alert message + This is your link for channel %@! new chat action @@ -9215,10 +9284,18 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien Non lu swipe action + + Unsupported channel name + alert title + Unsupported connection link conn error description + + Unsupported contact name + alert title + Up to 100 last messages are sent to new members. Les 100 derniers messages sont envoyés aux nouveaux membres. @@ -10185,6 +10262,11 @@ Répéter la demande de connexion ? Your network No comment provided by engineer. + + Your new channel %1$@ is connected to %2$d of %3$d relays. +If you cancel, the channel will be deleted - you can create it again. + alert message + Your preferences Vos préférences diff --git a/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff b/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff index 7723cabdcb..129436ecb0 100644 --- a/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff +++ b/apps/ios/SimpleX Localizations/hu.xcloc/Localized Contents/hu.xliff @@ -257,12 +257,12 @@ channel relay bar %1$d/%2$d relays connected - %1$d/%2$d átjátszó kapcsolódva + %1$d/%2$d átjátszó kapcsolódott channel subscriber relay bar progress %1$d/%2$d relays connected, %3$d errors - %1$d/%2$d átjátszó kapcsolódva, %3$d hiba + %1$d/%2$d átjátszó kapcsolódott, %3$d hiba channel subscriber relay bar @@ -412,17 +412,17 @@ channel relay bar **Create 1-time link**: to create and share a new invitation link. - **Partner hozzáadása:** új meghívási hivatkozás létrehozásához, vagy egy kapott hivatkozáson keresztül történő kapcsolódáshoz. + **Partner hozzáadása**: új meghívási hivatkozás létrehozásához, vagy egy kapott hivatkozáson keresztül történő kapcsolódáshoz. No comment provided by engineer. **Create group**: to create a new group. - **Csoport létrehozása:** új csoport létrehozásához. + **Csoport létrehozása**: új csoport létrehozásához. No comment provided by engineer. **More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata. - **Privátabb:** 20 percenként ellenőrzi az új üzeneteket. Az eszköztoken meg lesz osztva a SimpleX Chat kiszolgálóval, de az nem, hogy hány partnere vagy üzenete van. + **Privátabb**: 20 percenként ellenőrzi az új üzeneteket. Az eszköztoken meg lesz osztva a SimpleX Chat kiszolgálóval, de az nem, hogy hány partnere vagy üzenete van. No comment provided by engineer. @@ -432,17 +432,17 @@ channel relay bar **Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection. - **Megjegyzés:** ha két eszközön is ugyanazt az adatbázist használja, akkor biztonsági védelemként megszakítja a partnereitől érkező üzenetek visszafejtését. + **Megjegyzés**: ha két eszközön is ugyanazt az adatbázist használja, akkor biztonsági védelemként megszakítja a partnereitől érkező üzenetek visszafejtését. No comment provided by engineer. **Please note**: you will NOT be able to recover or change passphrase if you lose it. - **Megjegyzés:** NEM fogja tudni helyreállítani vagy módosítani a jelmondatot abban az esetben, ha elveszíti. + **Megjegyzés**: NEM fogja tudni helyreállítani vagy módosítani a jelmondatot abban az esetben, ha elveszíti. No comment provided by engineer. **Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from. - **Megjegyzés:** az eszköztoken és az értesítések el lesznek küldve a SimpleX Chat értesítési kiszolgálóra, kivéve az üzenet tartalma, mérete vagy az, hogy kitől származik. + **Megjegyzés**: az eszköztoken és az értesítések el lesznek küldve a SimpleX Chat értesítési kiszolgálóra, kivéve az üzenet tartalma, mérete vagy az, hogy kitől származik. No comment provided by engineer. @@ -457,12 +457,12 @@ channel relay bar **Warning**: Instant push notifications require passphrase saved in Keychain. - **Figyelmeztetés:** Az azonnali leküldéses értesítésekhez a kulcstartóban tárolt jelmondat megadása szükséges. + **Figyelmeztetés**: Az azonnali leküldéses értesítésekhez a kulcstartóban tárolt jelmondat megadása szükséges. No comment provided by engineer. **Warning**: the archive will be removed. - **Figyelmeztetés:** az archívum el lesz távolítva. + **Figyelmeztetés**: az archívum el lesz távolítva. No comment provided by engineer. @@ -629,7 +629,7 @@ time interval 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. **Az összes partneréhez és csoporttaghoz** külön TCP-kapcsolat lesz használva. -**Megjegyzés:** ha sok kapcsolata van, akkor az akkumulátor-használat és az adatforgalom jelentősen megnövekedhet, és néhány kapcsolódási kísérlet sikertelen lehet. +**Megjegyzés**: ha sok kapcsolata van, akkor az akkumulátor-használat és az adatforgalom jelentősen megnövekedhet, és néhány kapcsolódási kísérlet sikertelen lehet. No comment provided by engineer. @@ -718,7 +718,7 @@ swipe action Acknowledged - Visszaigazolt + Visszaigazolva No comment provided by engineer. @@ -736,6 +736,10 @@ swipe action Aktív kapcsolatok száma No comment provided by engineer. + + Add + No comment provided by engineer. + Add address to your profile, so that your SimpleX contacts can share it with other people. Profile update will be sent to your SimpleX contacts. Cím hozzáadása a profilhoz, hogy a SimpleX partnerei megoszthassák másokkal. A profilfrissítés el lesz küldve a SimpleX partnerei számára. @@ -761,6 +765,14 @@ swipe action Profil hozzáadása No comment provided by engineer. + + Add relays + No comment provided by engineer. + + + Add relays to restore message delivery. + No comment provided by engineer. + Add server Kiszolgáló hozzáadása @@ -806,11 +818,6 @@ swipe action Hozzáadott üzenetkiszolgálók No comment provided by engineer. - - Adding relays will be supported later. - Az átjátszók hozzáadása később lesz támogatott. - No comment provided by engineer. - Additional accent További kiemelőszín @@ -1123,12 +1130,12 @@ swipe action Always use private routing. - Mindig legyen használva privát útválasztás. + Privát útválasztás használata minden esetben. No comment provided by engineer. Always use relay - Mindig legyen használva átjátszó + Átjátszó használata minden esetben No comment provided by engineer. @@ -1186,6 +1193,10 @@ swipe action Alkalmazás munkamenete No comment provided by engineer. + + App update required + alert title + App version Alkalmazás verziója @@ -1360,7 +1371,7 @@ a saját hálózatában Be free in your network. - Legyen szabad a saját hálózatában. + Váljon szabaddá a saját hálózatában. No comment provided by engineer. @@ -1470,7 +1481,7 @@ a saját hálózatában Blocked by admin - Letiltva az adminisztrátor által + Az adminisztrátor letiltotta No comment provided by engineer. @@ -1615,6 +1626,14 @@ a saját hálózatában alert button new chat action + + Cancel and delete channel + No comment provided by engineer. + + + Cancel creating channel? + alert title + Cancel migration Átköltöztetés visszavonása @@ -1754,7 +1773,7 @@ alert subtitle Channel profile was changed. If you save it, the updated profile will be sent to channel subscribers. - A csatornaprofil módosult. Ha menti, akkor a frissített profil el lesz küldve a csatorna feliratkozóinak. + Csatornaprofil módosítva. Ha menti, akkor a frissített profil el lesz küldve a csatorna feliratkozóinak. alert message @@ -1772,9 +1791,8 @@ alert subtitle A csatorna törölve lesz az Ön számára – ez a művelet nem vonható vissza! No comment provided by engineer. - - Channel will start working with %1$d of %2$d relays. Proceed? - A csatorna %2$d átjátszóból %1$d használatával kezd el működni. Folytatja? + + Channel will start working with %1$d of %2$d relays. Continue? alert message @@ -2278,6 +2296,14 @@ Ez a saját egyszer használható meghívója! Társítás számítógéppel No comment provided by engineer. + + Connecting via channel name requires a newer app version. + alert message + + + Connecting via contact name requires a newer app version. + alert message + Connection Kapcsolat @@ -2433,7 +2459,7 @@ Ez a saját egyszer használható meghívója! Continue Folytatás - No comment provided by engineer. + alert action Contribute @@ -2701,7 +2727,7 @@ Ez a saját egyszer használható meghívója! Database passphrase is different from saved in the keychain. - Az adatbázis jelmondata nem egyezik a kulcstartóba mentettől. + Az adatbázis jelmondata eltér a kulcstartóban tárolttól. No comment provided by engineer. @@ -2879,6 +2905,10 @@ swipe action Csak nálam No comment provided by engineer. + + Delete from history + No comment provided by engineer. + Delete group Csoport törlése @@ -3242,7 +3272,7 @@ alert button Do NOT use private routing. - NE legyen használva privát útválasztás. + Privát útválasztás használatának elkerülése. No comment provided by engineer. @@ -3533,7 +3563,7 @@ chat item action Encrypted message: keychain error - Titkosított üzenet: kulcstartó hiba + Titkosított üzenet: kulcstartóhiba notification @@ -3653,7 +3683,7 @@ chat item action Error accepting member - Hiba a tag befogadásakor + Hiba történt a tag befogadásakor alert title @@ -3663,7 +3693,11 @@ chat item action Error adding relay - Hiba az átjátszó hozzáadásakor + Hiba történt az átjátszó hozzáadásakor + alert title + + + Error adding relays alert title @@ -3683,7 +3717,7 @@ chat item action Error changing chat profile - Hiba a csevegési profil módosításakor + Hiba történt a csevegési profil módosításakor alert title @@ -3728,7 +3762,7 @@ chat item action Error creating channel - Hiba a csatorna létrehozásakor + Hiba történt a csatorna létrehozásakor alert title @@ -3773,7 +3807,7 @@ chat item action Error deleting chat - Hiba a csevegés törlésekor + Hiba történt a csevegés törlésekor alert title @@ -3796,6 +3830,10 @@ chat item action Hiba történt az adatbázis törlésekor alert title + + Error deleting message + alert title + Error deleting old database Hiba történt a régi adatbázis törlésekor @@ -3913,7 +3951,7 @@ chat item action Error saving channel profile - Hiba a csatornaprofil mentésekor + Hiba történt a csatornaprofil mentésekor No comment provided by engineer. @@ -3973,7 +4011,7 @@ chat item action Error setting auto-accept - Hiba az automatikus elfogadás beállításakor + Hiba történt az automatikus elfogadás beállításakor No comment provided by engineer. @@ -3983,7 +4021,7 @@ chat item action Error sharing channel - Hiba a csatorna megosztásakor + Hiba történt a csatorna megosztásakor alert title @@ -4611,7 +4649,7 @@ Hiba: %2$@ Group profile was changed. If you save it, the updated profile will be sent to group members. - Csoportprofil módosítva. Ha menti, akkor a frissített profil el lesz küldve a csoporttagoknak. + Csoportprofil módosítva. Ha menti, akkor a frissített profil el lesz küldve a csoport tagjainak. alert message @@ -4726,7 +4764,7 @@ Hiba: %2$@ How to use your servers - Hogyan használja a saját kiszolgálóit + Útmutató a saját kiszolgálók használatához No comment provided by engineer. @@ -4938,7 +4976,7 @@ További fejlesztések hamarosan! Install SimpleX Chat for terminal - A SimpleX Chat terminálhoz telepítése + SimpleX Chat telepítése a terminálhoz No comment provided by engineer. @@ -5273,7 +5311,7 @@ Ez a saját hivatkozása a(z) %@ nevű csoporthoz! Let someone connect to you - Hagyja, hogy valaki elérje Önt + Legyen elérhető mások számára No comment provided by engineer. @@ -6005,6 +6043,10 @@ A legbiztonságosabb titkosítás. Nincs alkalmazás jelszó Authentication unavailable + + No available relays + No comment provided by engineer. + No chat relays Nincsenek csevegési átjátszók @@ -6130,6 +6172,10 @@ A legbiztonságosabb titkosítás. Nincsenek fogadott vagy küldött fájlok No comment provided by engineer. + + No relays + No comment provided by engineer. + No servers for private message routing. Nincsenek kiszolgálók a privát üzenet-útválasztáshoz. @@ -6778,6 +6824,10 @@ Hiba: %@ Próbálja meg letiltani és újra engedélyezni az értesítéseket. token info + + Please upgrade the app. + alert message + Please wait for group moderators to review your request to join the group. Várja meg, amíg a csoport moderátorai áttekintik a csoporthoz való csatlakozási kérését. @@ -6855,7 +6905,7 @@ Hiba: %@ Privacy: for owners and subscribers. - Adatvédelem: tulajdonosok és előfizetők számára. + Adatvédelem: tulajdonosok és feliratkozók számára. No comment provided by engineer. @@ -6903,11 +6953,6 @@ Hiba: %@ Privát útválasztás időtúllépése alert title - - Proceed - Folytatás - alert action - Profile and server connections Profil és kiszolgálókapcsolatok @@ -7289,12 +7334,12 @@ swipe action Relay server is only used if necessary. Another party can observe your IP address. - Az átjátszó csak szükség esetén lesz használva. Egy másik fél megfigyelheti az IP-címét. + Az átjátszó kiszolgáló csak szükség esetén lesz használva. Egy másik fél megfigyelheti az IP-címét. No comment provided by engineer. Relay server protects your IP address, but it can observe the duration of the call. - Az átjátszó megvédi az IP-címét, de megfigyelheti a hívás időtartamát. + Az átjátszó kiszolgáló megvédi az IP-címét, de megfigyelheti a hívás időtartamát. No comment provided by engineer. @@ -7302,6 +7347,14 @@ swipe action Nem sikerült tesztelni az átjátszót! No comment provided by engineer. + + Relay will be removed from channel - this cannot be undone! + alert message + + + Relays added: %@. + alert message + Reliability: many relays per channel. Megbízhatóság: több átjátszó is használható csatornánként. @@ -7347,10 +7400,9 @@ swipe action Eltávolítja a jelmondatot a kulcstartóból? No comment provided by engineer. - - Remove subscriber - Feliratkozó eltávolítása - No comment provided by engineer. + + Remove relay? + alert title Remove subscriber? @@ -8667,6 +8719,10 @@ report reason Statisztikák No comment provided by engineer. + + Status + No comment provided by engineer. + Stop Megállítás @@ -8971,7 +9027,7 @@ Az átjátszó címe ennek az átjátszónak a beállítására szolgált a csat Test failed at step %@. - A teszt a(z) %@ lépésnél sikertelen volt. + A teszt a(z) %@. lépésnél sikertelen volt. relay test failure server test failure @@ -9254,11 +9310,20 @@ a saját kapcsolatait és csoportjait. Ez a csoport már nem létezik. No comment provided by engineer. + + This group requires a newer version of the app. Please update the app to join. + alert message +alert subtitle + This is a chat relay address, it cannot be used to connect. Ez egy csevegési átjátszó címe, nem használható kapcsolódásra. alert message + + This is the last active relay. Removing it will prevent message delivery to subscribers. + alert message + This is your link for channel %@! Ez a saját hivatkozása a(z) %@ nevű csatornához! @@ -9593,11 +9658,19 @@ A kapcsolódáshoz kérje meg a partnerét, hogy hozzon létre egy másik kapcso Olvasatlan swipe action + + Unsupported channel name + alert title + Unsupported connection link Nem támogatott kapcsolattartási hivatkozás conn error description + + Unsupported contact name + alert title + Up to 100 last messages are sent to new members. Legfeljebb az utolsó 100 üzenet lesz elküldve az új tagok számára. @@ -10175,7 +10248,7 @@ A kapcsolódáshoz kérje meg a partnerét, hogy hozzon létre egy másik kapcso You are already connected with %@. - Ön már kapcsolódva van vele: %@. + Ön már kapcsolatban van vele: %@. No comment provided by engineer. @@ -10606,6 +10679,11 @@ Megismétli a kapcsolódási kérést? Saját hálózat No comment provided by engineer. + + Your new channel %1$@ is connected to %2$d of %3$d relays. +If you cancel, the channel will be deleted - you can create it again. + alert message + Your preferences Beállítások @@ -10815,7 +10893,7 @@ Az átjátszók hozzáférhetnek a csatornaüzenetekhez. blocked by admin - letiltva az adminisztrátor által + az adminisztrátor letiltotta blocked chat item marked deleted chat item preview text @@ -11570,7 +11648,7 @@ time to disappear reviewed by admins - áttekintve a moderátorok által + a moderátorok áttekintették No comment provided by engineer. @@ -11674,12 +11752,12 @@ utoljára fogadott üzenet: %2$@ updated channel profile - frissített csatornaprofil + frissítette a csatorna profilját rcv group event chat item updated group profile - frissítette a csoportprofilt + frissítette a csoport profilját rcv group event chat item @@ -12019,7 +12097,7 @@ utoljára fogadott üzenet: %2$@ Database passphrase is different from saved in the keychain. - Az adatbázis jelmondata nem egyezik a kulcstartóba mentettől. + Az adatbázis jelmondata eltér a kulcstartóban tárolttól. No comment provided by engineer. @@ -12139,7 +12217,7 @@ utoljára fogadott üzenet: %2$@ Wait - Várjon + Várakozás 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 4d9fce7b4f..469da88ce2 100644 --- a/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff +++ b/apps/ios/SimpleX Localizations/it.xcloc/Localized Contents/it.xliff @@ -736,6 +736,10 @@ swipe action Connessioni attive No comment provided by engineer. + + Add + No comment provided by engineer. + Add address to your profile, so that your SimpleX contacts can share it with other people. Profile update will be sent to your SimpleX contacts. Aggiungi l'indirizzo al tuo profilo, in modo che i tuoi contatti di SimpleX possano condividerlo con altre persone. L'aggiornamento del profilo verrà inviato ai tuoi contatti di SimpleX. @@ -761,6 +765,14 @@ swipe action Aggiungi profilo No comment provided by engineer. + + Add relays + No comment provided by engineer. + + + Add relays to restore message delivery. + No comment provided by engineer. + Add server Aggiungi server @@ -806,11 +818,6 @@ swipe action Server dei messaggi aggiunti No comment provided by engineer. - - Adding relays will be supported later. - L'aggiunta di relay verrà supportata prossimamente. - No comment provided by engineer. - Additional accent Principale aggiuntivo @@ -1186,6 +1193,10 @@ swipe action Sessione dell'app No comment provided by engineer. + + App update required + alert title + App version Versione dell'app @@ -1615,6 +1626,14 @@ nella tua rete alert button new chat action + + Cancel and delete channel + No comment provided by engineer. + + + Cancel creating channel? + alert title + Cancel migration Annulla migrazione @@ -1772,9 +1791,8 @@ alert subtitle Il canale verrà eliminato per te, non è reversibile! No comment provided by engineer. - - Channel will start working with %1$d of %2$d relays. Proceed? - Il canale sarà operativo con %1$d di %2$d relay. Procedere? + + Channel will start working with %1$d of %2$d relays. Continue? alert message @@ -2278,6 +2296,14 @@ Questo è il tuo link una tantum! Connessione al desktop No comment provided by engineer. + + Connecting via channel name requires a newer app version. + alert message + + + Connecting via contact name requires a newer app version. + alert message + Connection Connessione @@ -2433,7 +2459,7 @@ Questo è il tuo link una tantum! Continue Continua - No comment provided by engineer. + alert action Contribute @@ -2879,6 +2905,10 @@ swipe action Elimina per me No comment provided by engineer. + + Delete from history + No comment provided by engineer. + Delete group Elimina gruppo @@ -3666,6 +3696,10 @@ chat item action Errore di aggiunta del relay alert title + + Error adding relays + alert title + Error adding server Errore di aggiunta del server @@ -3796,6 +3830,10 @@ chat item action Errore nell'eliminazione del database alert title + + Error deleting message + alert title + Error deleting old database Errore nell'eliminazione del database vecchio @@ -6005,6 +6043,10 @@ La crittografia più sicura. Nessuna password dell'app Authentication unavailable + + No available relays + No comment provided by engineer. + No chat relays Nessun relay di chat @@ -6130,6 +6172,10 @@ La crittografia più sicura. Nessun file ricevuto o inviato No comment provided by engineer. + + No relays + No comment provided by engineer. + No servers for private message routing. Nessun server per l'instradamento dei messaggi privati. @@ -6463,12 +6509,12 @@ alert button Open new channel - Apri un canale nuovo + Apri il nuovo canale new chat action Open new chat - Apri una chat nuova + Apri la nuova chat new chat action @@ -6778,6 +6824,10 @@ Errore: %@ Prova a disattivare e riattivare le notifiche. token info + + Please upgrade the app. + alert message + Please wait for group moderators to review your request to join the group. Attendi che i moderatori del gruppo revisionino la tua richiesta di entrare nel gruppo. @@ -6903,11 +6953,6 @@ Errore: %@ Scadenza dell'instradamento privato alert title - - Proceed - Procedi - alert action - Profile and server connections Profilo e connessioni al server @@ -7302,6 +7347,14 @@ swipe action Prova del relay fallita! No comment provided by engineer. + + Relay will be removed from channel - this cannot be undone! + alert message + + + Relays added: %@. + alert message + Reliability: many relays per channel. Affidabilità: relay multipli per canale. @@ -7347,10 +7400,9 @@ swipe action Rimuovere la password dal portachiavi? No comment provided by engineer. - - Remove subscriber - Rimuovi iscritto - No comment provided by engineer. + + Remove relay? + alert title Remove subscriber? @@ -8667,6 +8719,10 @@ report reason Statistiche No comment provided by engineer. + + Status + No comment provided by engineer. + Stop Ferma @@ -9254,11 +9310,20 @@ i tuoi contatti e i tuoi gruppi. Questo gruppo non esiste più. No comment provided by engineer. + + This group requires a newer version of the app. Please update the app to join. + alert message +alert subtitle + This is a chat relay address, it cannot be used to connect. Questo è un indirizzo di relay di chat, non può essere usato per connettersi. alert message + + This is the last active relay. Removing it will prevent message delivery to subscribers. + alert message + This is your link for channel %@! Questo è il tuo link per il canale %@! @@ -9593,11 +9658,19 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e Non letto swipe action + + Unsupported channel name + alert title + Unsupported connection link Link di connessione non supportato conn error description + + Unsupported contact name + alert title + Up to 100 last messages are sent to new members. Vengono inviati ai nuovi membri fino a 100 ultimi messaggi. @@ -10606,6 +10679,11 @@ Ripetere la richiesta di connessione? La tua rete No comment provided by engineer. + + Your new channel %1$@ is connected to %2$d of %3$d relays. +If you cancel, the channel will be deleted - you can create it again. + alert message + Your preferences Le tue preferenze diff --git a/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff b/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff index 0d3a7a9088..13396b13a4 100644 --- a/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff +++ b/apps/ios/SimpleX Localizations/ja.xcloc/Localized Contents/ja.xliff @@ -711,6 +711,10 @@ swipe action アクティブな接続 No comment provided by engineer. + + Add + No comment provided by engineer. + Add address to your profile, so that your SimpleX contacts can share it with other people. Profile update will be sent to your SimpleX contacts. No comment provided by engineer. @@ -734,6 +738,14 @@ swipe action プロフィールを追加 No comment provided by engineer. + + Add relays + No comment provided by engineer. + + + Add relays to restore message delivery. + No comment provided by engineer. + Add server サーバを追加 @@ -778,10 +790,6 @@ swipe action 追加されたメッセージサーバー No comment provided by engineer. - - Adding relays will be supported later. - No comment provided by engineer. - Additional accent No comment provided by engineer. @@ -1135,6 +1143,10 @@ swipe action App session No comment provided by engineer. + + App update required + alert title + App version アプリのバージョン @@ -1515,6 +1527,14 @@ in your network alert button new chat action + + Cancel and delete channel + No comment provided by engineer. + + + Cancel creating channel? + alert title + Cancel migration 移行を中止する @@ -1655,8 +1675,8 @@ alert subtitle Channel will be deleted for you - this cannot be undone! No comment provided by engineer. - - Channel will start working with %1$d of %2$d relays. Proceed? + + Channel will start working with %1$d of %2$d relays. Continue? alert message @@ -2107,6 +2127,14 @@ This is your own one-time link! デスクトップに接続中 No comment provided by engineer. + + Connecting via channel name requires a newer app version. + alert message + + + Connecting via contact name requires a newer app version. + alert message + Connection 接続 @@ -2246,7 +2274,7 @@ This is your own one-time link! Continue 続ける - No comment provided by engineer. + alert action Contribute @@ -2658,6 +2686,10 @@ swipe action 自分側で削除 No comment provided by engineer. + + Delete from history + No comment provided by engineer. + Delete group グループを削除 @@ -3374,6 +3406,10 @@ chat item action Error adding relay alert title + + Error adding relays + alert title + Error adding server alert title @@ -3491,6 +3527,10 @@ chat item action データベースの削除にエラー発生 alert title + + Error deleting message + alert title + Error deleting old database 古いデータベースを削除にエラー発生 @@ -5475,6 +5515,10 @@ The most secure encryption. アプリのパスワードはありません Authentication unavailable + + No available relays + No comment provided by engineer. + No chat relays No comment provided by engineer. @@ -5585,6 +5629,10 @@ The most secure encryption. 送受信済みのファイルがありません No comment provided by engineer. + + No relays + No comment provided by engineer. + No servers for private message routing. servers error @@ -6153,6 +6201,10 @@ Error: %@ Please try to disable and re-enable notfications. token info + + Please upgrade the app. + alert message + Please wait for group moderators to review your request to join the group. snd group event chat item @@ -6260,10 +6312,6 @@ Error: %@ Private routing timeout alert title - - Proceed - alert action - Profile and server connections プロフィールとサーバ接続 @@ -6618,6 +6666,14 @@ swipe action Relay test failed! No comment provided by engineer. + + Relay will be removed from channel - this cannot be undone! + alert message + + + Relays added: %@. + alert message + Reliability: many relays per channel. No comment provided by engineer. @@ -6658,9 +6714,9 @@ swipe action キーチェーンからパスフレーズを削除しますか? No comment provided by engineer. - - Remove subscriber - No comment provided by engineer. + + Remove relay? + alert title Remove subscriber? @@ -7821,6 +7877,10 @@ report reason Statistics No comment provided by engineer. + + Status + No comment provided by engineer. + Stop 停止 @@ -8339,10 +8399,19 @@ your contacts and groups. このグループはもう存在しません。 No comment provided by engineer. + + This group requires a newer version of the app. Please update the app to join. + alert message +alert subtitle + This is a chat relay address, it cannot be used to connect. alert message + + This is the last active relay. Removing it will prevent message delivery to subscribers. + alert message + This is your link for channel %@! new chat action @@ -8641,10 +8710,18 @@ To connect, please ask your contact to create another connection link and check 未読 swipe action + + Unsupported channel name + alert title + Unsupported connection link conn error description + + Unsupported contact name + alert title + Up to 100 last messages are sent to new members. No comment provided by engineer. @@ -9537,6 +9614,11 @@ Repeat connection request? Your network No comment provided by engineer. + + Your new channel %1$@ is connected to %2$d of %3$d relays. +If you cancel, the channel will be deleted - you can create it again. + alert message + Your preferences あなたの設定 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 3bf4a6f197..9f1818fba9 100644 --- a/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff +++ b/apps/ios/SimpleX Localizations/nl.xcloc/Localized Contents/nl.xliff @@ -714,6 +714,10 @@ swipe action Actieve verbindingen No comment provided by engineer. + + Add + No comment provided by engineer. + Add address to your profile, so that your SimpleX contacts can share it with other people. Profile update will be sent to your SimpleX contacts. No comment provided by engineer. @@ -737,6 +741,14 @@ swipe action Profiel toevoegen No comment provided by engineer. + + Add relays + No comment provided by engineer. + + + Add relays to restore message delivery. + No comment provided by engineer. + Add server Server toevoegen @@ -782,10 +794,6 @@ swipe action Berichtservers toegevoegd No comment provided by engineer. - - Adding relays will be supported later. - No comment provided by engineer. - Additional accent Extra accent @@ -1153,6 +1161,10 @@ swipe action Appsessie No comment provided by engineer. + + App update required + alert title + App version App versie @@ -1568,6 +1580,14 @@ in your network alert button new chat action + + Cancel and delete channel + No comment provided by engineer. + + + Cancel creating channel? + alert title + Cancel migration Migratie annuleren @@ -1712,8 +1732,8 @@ alert subtitle Channel will be deleted for you - this cannot be undone! No comment provided by engineer. - - Channel will start working with %1$d of %2$d relays. Proceed? + + Channel will start working with %1$d of %2$d relays. Continue? alert message @@ -2203,6 +2223,14 @@ Dit is uw eigen eenmalige link! Verbinding maken met desktop No comment provided by engineer. + + Connecting via channel name requires a newer app version. + alert message + + + Connecting via contact name requires a newer app version. + alert message + Connection Verbinding @@ -2355,7 +2383,7 @@ Dit is uw eigen eenmalige link! Continue Doorgaan - No comment provided by engineer. + alert action Contribute @@ -2792,6 +2820,10 @@ swipe action Verwijder voor mij No comment provided by engineer. + + Delete from history + No comment provided by engineer. + Delete group Groep verwijderen @@ -3560,6 +3592,10 @@ chat item action Error adding relay alert title + + Error adding relays + alert title + Error adding server Fout bij toevoegen server @@ -3686,6 +3722,10 @@ chat item action Fout bij het verwijderen van de database alert title + + Error deleting message + alert title + Error deleting old database Fout bij het verwijderen van de oude database @@ -5845,6 +5885,10 @@ The most secure encryption. Geen app wachtwoord Authentication unavailable + + No available relays + No comment provided by engineer. + No chat relays No comment provided by engineer. @@ -5967,6 +6011,10 @@ The most secure encryption. Geen ontvangen of verzonden bestanden No comment provided by engineer. + + No relays + No comment provided by engineer. + No servers for private message routing. Geen servers voor het routeren van privéberichten. @@ -6585,6 +6633,10 @@ Fout: %@ Probeer meldingen uit en weer in te schakelen. token info + + Please upgrade the app. + alert message + Please wait for group moderators to review your request to join the group. Wacht totdat de moderators van de groep uw verzoek tot lidmaatschap van de groep hebben beoordeeld. @@ -6705,10 +6757,6 @@ Fout: %@ Private routing timeout alert title - - Proceed - alert action - Profile and server connections Profiel- en serververbindingen @@ -7092,6 +7140,14 @@ swipe action Relay test failed! No comment provided by engineer. + + Relay will be removed from channel - this cannot be undone! + alert message + + + Relays added: %@. + alert message + Reliability: many relays per channel. No comment provided by engineer. @@ -7134,9 +7190,9 @@ swipe action Wachtwoord van de keychain verwijderen? No comment provided by engineer. - - Remove subscriber - No comment provided by engineer. + + Remove relay? + alert title Remove subscriber? @@ -8415,6 +8471,10 @@ report reason Statistieken No comment provided by engineer. + + Status + No comment provided by engineer. + Stop Stop @@ -8969,10 +9029,19 @@ your contacts and groups. Deze groep bestaat niet meer. No comment provided by engineer. + + This group requires a newer version of the app. Please update the app to join. + alert message +alert subtitle + This is a chat relay address, it cannot be used to connect. alert message + + This is the last active relay. Removing it will prevent message delivery to subscribers. + alert message + This is your link for channel %@! new chat action @@ -9298,11 +9367,19 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak Ongelezen swipe action + + Unsupported channel name + alert title + Unsupported connection link Niet-ondersteunde verbindingslink conn error description + + Unsupported contact name + alert title + Up to 100 last messages are sent to new members. Er worden maximaal 100 laatste berichten naar nieuwe leden verzonden. @@ -10275,6 +10352,11 @@ Verbindingsverzoek herhalen? Your network No comment provided by engineer. + + Your new channel %1$@ is connected to %2$d of %3$d relays. +If you cancel, the channel will be deleted - you can create it again. + alert message + Your preferences Jouw voorkeuren diff --git a/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff index b232aa84af..2644708927 100644 --- a/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff +++ b/apps/ios/SimpleX Localizations/pl.xcloc/Localized Contents/pl.xliff @@ -715,6 +715,10 @@ swipe action Aktywne połączenia No comment provided by engineer. + + Add + No comment provided by engineer. + Add address to your profile, so that your SimpleX contacts can share it with other people. Profile update will be sent to your SimpleX contacts. No comment provided by engineer. @@ -739,6 +743,14 @@ swipe action Dodaj profil No comment provided by engineer. + + Add relays + No comment provided by engineer. + + + Add relays to restore message delivery. + No comment provided by engineer. + Add server Dodaj serwer @@ -784,10 +796,6 @@ swipe action Dodano serwery wiadomości No comment provided by engineer. - - Adding relays will be supported later. - No comment provided by engineer. - Additional accent Dodatkowy akcent @@ -1158,6 +1166,10 @@ swipe action Sesja aplikacji No comment provided by engineer. + + App update required + alert title + App version Wersja aplikacji @@ -1582,6 +1594,14 @@ in your network alert button new chat action + + Cancel and delete channel + No comment provided by engineer. + + + Cancel creating channel? + alert title + Cancel migration Anuluj migrację @@ -1726,8 +1746,8 @@ alert subtitle Channel will be deleted for you - this cannot be undone! No comment provided by engineer. - - Channel will start working with %1$d of %2$d relays. Proceed? + + Channel will start working with %1$d of %2$d relays. Continue? alert message @@ -2219,6 +2239,14 @@ To jest twój jednorazowy link! Łączenie z komputerem No comment provided by engineer. + + Connecting via channel name requires a newer app version. + alert message + + + Connecting via contact name requires a newer app version. + alert message + Connection Połączenie @@ -2373,7 +2401,7 @@ To jest twój jednorazowy link! Continue Kontynuuj - No comment provided by engineer. + alert action Contribute @@ -2811,6 +2839,10 @@ swipe action Usuń dla mnie No comment provided by engineer. + + Delete from history + No comment provided by engineer. + Delete group Usuń grupę @@ -3585,6 +3617,10 @@ chat item action Error adding relay alert title + + Error adding relays + alert title + Error adding server Błąd podczas dodawania serwera @@ -3714,6 +3750,10 @@ chat item action Błąd usuwania bazy danych alert title + + Error deleting message + alert title + Error deleting old database Błąd usuwania starej bazy danych @@ -5895,6 +5935,10 @@ The most secure encryption. Brak hasła aplikacji Authentication unavailable + + No available relays + No comment provided by engineer. + No chat relays No comment provided by engineer. @@ -6018,6 +6062,10 @@ The most secure encryption. Brak odebranych lub wysłanych plików No comment provided by engineer. + + No relays + No comment provided by engineer. + No servers for private message routing. Brak serwerów prywatnej sesji routingu. @@ -6648,6 +6696,10 @@ Błąd: %@ Spróbuj wyłączyć, a następnie ponownie włączyć powiadomienia. token info + + Please upgrade the app. + alert message + Please wait for group moderators to review your request to join the group. Poczekaj, aż moderatorzy grupy rozpatrzą Twoją prośbę o dołączenie do grupy. @@ -6769,10 +6821,6 @@ Błąd: %@ Limit czasu routingu prywatnego alert title - - Proceed - alert action - Profile and server connections Profil i połączenia z serwerem @@ -7157,6 +7205,14 @@ swipe action Relay test failed! No comment provided by engineer. + + Relay will be removed from channel - this cannot be undone! + alert message + + + Relays added: %@. + alert message + Reliability: many relays per channel. No comment provided by engineer. @@ -7201,9 +7257,9 @@ swipe action Usunąć hasło z pęku kluczy? No comment provided by engineer. - - Remove subscriber - No comment provided by engineer. + + Remove relay? + alert title Remove subscriber? @@ -8501,6 +8557,10 @@ report reason Statystyki No comment provided by engineer. + + Status + No comment provided by engineer. + Stop Zatrzymaj @@ -9065,10 +9125,19 @@ your contacts and groups. Ta grupa już nie istnieje. No comment provided by engineer. + + This group requires a newer version of the app. Please update the app to join. + alert message +alert subtitle + This is a chat relay address, it cannot be used to connect. alert message + + This is the last active relay. Removing it will prevent message delivery to subscribers. + alert message + This is your link for channel %@! new chat action @@ -9399,11 +9468,19 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc Nieprzeczytane swipe action + + Unsupported channel name + alert title + Unsupported connection link Nieobsługiwane łącze połączenia conn error description + + Unsupported contact name + alert title + Up to 100 last messages are sent to new members. Do nowych członków wysyłanych jest do 100 ostatnich wiadomości. @@ -10394,6 +10471,11 @@ Powtórzyć prośbę połączenia? Your network No comment provided by engineer. + + Your new channel %1$@ is connected to %2$d of %3$d relays. +If you cancel, the channel will be deleted - you can create it again. + alert message + Your preferences Twoje preferencje 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 a438327ba1..a3971c0325 100644 --- a/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff +++ b/apps/ios/SimpleX Localizations/ru.xcloc/Localized Contents/ru.xliff @@ -736,6 +736,10 @@ swipe action Активные соединения No comment provided by engineer. + + Add + No comment provided by engineer. + Add address to your profile, so that your SimpleX contacts can share it with other people. Profile update will be sent to your SimpleX contacts. Добавьте адрес в свой профиль, чтобы Ваши SimpleX контакты могли поделиться им. Профиль будет отправлен Вашим SimpleX контактам. @@ -761,6 +765,14 @@ swipe action Добавить профиль No comment provided by engineer. + + Add relays + No comment provided by engineer. + + + Add relays to restore message delivery. + No comment provided by engineer. + Add server Добавить сервер @@ -806,11 +818,6 @@ swipe action Дополнительные серверы сообщений No comment provided by engineer. - - Adding relays will be supported later. - Добавление релеев будет поддерживаться позже. - No comment provided by engineer. - Additional accent Дополнительный акцент @@ -1186,6 +1193,10 @@ swipe action Сессия приложения No comment provided by engineer. + + App update required + alert title + App version Версия приложения @@ -1615,6 +1626,14 @@ in your network alert button new chat action + + Cancel and delete channel + No comment provided by engineer. + + + Cancel creating channel? + alert title + Cancel migration Отменить миграцию @@ -1772,9 +1791,8 @@ alert subtitle Канал будет удалён для Вас - это нельзя отменить! No comment provided by engineer. - - Channel will start working with %1$d of %2$d relays. Proceed? - Канал начнёт работу с %1$d из %2$d релеев. Продолжить? + + Channel will start working with %1$d of %2$d relays. Continue? alert message @@ -2278,6 +2296,14 @@ This is your own one-time link! Подключение к компьютеру No comment provided by engineer. + + Connecting via channel name requires a newer app version. + alert message + + + Connecting via contact name requires a newer app version. + alert message + Connection Соединение @@ -2433,7 +2459,7 @@ This is your own one-time link! Continue Продолжить - No comment provided by engineer. + alert action Contribute @@ -2879,6 +2905,10 @@ swipe action Удалить для меня No comment provided by engineer. + + Delete from history + No comment provided by engineer. + Delete group Удалить группу @@ -3666,6 +3696,10 @@ chat item action Ошибка добавления релея alert title + + Error adding relays + alert title + Error adding server Ошибка добавления сервера @@ -3796,6 +3830,10 @@ chat item action Ошибка при удалении данных чата alert title + + Error deleting message + alert title + Error deleting old database Ошибка при удалении предыдущей версии данных чата @@ -6004,6 +6042,10 @@ The most secure encryption. Нет кода доступа Authentication unavailable + + No available relays + No comment provided by engineer. + No chat relays Нет чат-релеев @@ -6129,6 +6171,10 @@ The most secure encryption. Нет полученных или отправленных файлов No comment provided by engineer. + + No relays + No comment provided by engineer. + No servers for private message routing. Нет серверов для доставки сообщений. @@ -6777,6 +6823,10 @@ Error: %@ Попробуйте выключить и снова включить уведомления. token info + + Please upgrade the app. + alert message + Please wait for group moderators to review your request to join the group. Пожалуйста, подождите, пока модераторы группы рассмотрят ваш запрос на вступление. @@ -6902,11 +6952,6 @@ Error: %@ Таймаут конфиденциальной доставки alert title - - Proceed - Продолжить - alert action - Profile and server connections Профиль и соединения на сервере @@ -7301,6 +7346,14 @@ swipe action Тест релея не пройден! No comment provided by engineer. + + Relay will be removed from channel - this cannot be undone! + alert message + + + Relays added: %@. + alert message + Reliability: many relays per channel. Надёжность: несколько релеев на каждый канал. @@ -7346,10 +7399,9 @@ swipe action Удалить пароль из Keychain? No comment provided by engineer. - - Remove subscriber - Удалить подписчика - No comment provided by engineer. + + Remove relay? + alert title Remove subscriber? @@ -8666,6 +8718,10 @@ report reason Статистика No comment provided by engineer. + + Status + No comment provided by engineer. + Stop Остановить @@ -9253,11 +9309,20 @@ your contacts and groups. Эта группа больше не существует. No comment provided by engineer. + + This group requires a newer version of the app. Please update the app to join. + alert message +alert subtitle + This is a chat relay address, it cannot be used to connect. Это адрес чат-релея, с ним нельзя соединиться. alert message + + This is the last active relay. Removing it will prevent message delivery to subscribers. + alert message + This is your link for channel %@! Это ваша ссылка на канал %@! @@ -9592,11 +9657,19 @@ To connect, please ask your contact to create another connection link and check Не прочитано swipe action + + Unsupported channel name + alert title + Unsupported connection link Ссылка не поддерживается conn error description + + Unsupported contact name + alert title + Up to 100 last messages are sent to new members. До 100 последних сообщений отправляются новым членам. @@ -10605,6 +10678,11 @@ Repeat connection request? Ваша сеть No comment provided by engineer. + + Your new channel %1$@ is connected to %2$d of %3$d relays. +If you cancel, the channel will be deleted - you can create it again. + alert message + Your preferences Ваши предпочтения @@ -11673,7 +11751,7 @@ last received msg: %2$@ updated channel profile - обновлён профиль канала + обновил профиль канала rcv group event chat item diff --git a/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff b/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff index 04c51bbf43..cd2e30977d 100644 --- a/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff +++ b/apps/ios/SimpleX Localizations/th.xcloc/Localized Contents/th.xliff @@ -659,6 +659,10 @@ swipe action Active connections No comment provided by engineer. + + Add + No comment provided by engineer. + Add address to your profile, so that your SimpleX contacts can share it with other people. Profile update will be sent to your SimpleX contacts. No comment provided by engineer. @@ -680,6 +684,14 @@ swipe action เพิ่มโปรไฟล์ No comment provided by engineer. + + Add relays + No comment provided by engineer. + + + Add relays to restore message delivery. + No comment provided by engineer. + Add server เพิ่มเซิร์ฟเวอร์ @@ -720,10 +732,6 @@ swipe action Added message servers No comment provided by engineer. - - Adding relays will be supported later. - No comment provided by engineer. - Additional accent No comment provided by engineer. @@ -1061,6 +1069,10 @@ swipe action App session No comment provided by engineer. + + App update required + alert title + App version เวอร์ชันแอป @@ -1434,6 +1446,14 @@ in your network alert button new chat action + + Cancel and delete channel + No comment provided by engineer. + + + Cancel creating channel? + alert title + Cancel migration No comment provided by engineer. @@ -1572,8 +1592,8 @@ alert subtitle Channel will be deleted for you - this cannot be undone! No comment provided by engineer. - - Channel will start working with %1$d of %2$d relays. Proceed? + + Channel will start working with %1$d of %2$d relays. Continue? alert message @@ -2005,6 +2025,14 @@ This is your own one-time link! Connecting to desktop No comment provided by engineer. + + Connecting via channel name requires a newer app version. + alert message + + + Connecting via contact name requires a newer app version. + alert message + Connection การเชื่อมต่อ @@ -2142,7 +2170,7 @@ This is your own one-time link! Continue ดำเนินการต่อ - No comment provided by engineer. + alert action Contribute @@ -2549,6 +2577,10 @@ swipe action ลบให้ฉัน No comment provided by engineer. + + Delete from history + No comment provided by engineer. + Delete group ลบกลุ่ม @@ -3259,6 +3291,10 @@ chat item action Error adding relay alert title + + Error adding relays + alert title + Error adding server alert title @@ -3374,6 +3410,10 @@ chat item action เกิดข้อผิดพลาดในการลบฐานข้อมูล alert title + + Error deleting message + alert title + Error deleting old database เกิดข้อผิดพลาดในการลบฐานข้อมูลเก่า @@ -5354,6 +5394,10 @@ The most secure encryption. ไม่มีรหัสผ่านสำหรับแอป Authentication unavailable + + No available relays + No comment provided by engineer. + No chat relays No comment provided by engineer. @@ -5463,6 +5507,10 @@ The most secure encryption. ไม่มีไฟล์ที่ได้รับหรือส่ง No comment provided by engineer. + + No relays + No comment provided by engineer. + No servers for private message routing. servers error @@ -6028,6 +6076,10 @@ Error: %@ Please try to disable and re-enable notfications. token info + + Please upgrade the app. + alert message + Please wait for group moderators to review your request to join the group. snd group event chat item @@ -6134,10 +6186,6 @@ Error: %@ Private routing timeout alert title - - Proceed - alert action - Profile and server connections การเชื่อมต่อโปรไฟล์และเซิร์ฟเวอร์ @@ -6491,6 +6539,14 @@ swipe action Relay test failed! No comment provided by engineer. + + Relay will be removed from channel - this cannot be undone! + alert message + + + Relays added: %@. + alert message + Reliability: many relays per channel. No comment provided by engineer. @@ -6531,9 +6587,9 @@ swipe action ลบรหัสผ่านออกจาก keychain หรือไม่? No comment provided by engineer. - - Remove subscriber - No comment provided by engineer. + + Remove relay? + alert title Remove subscriber? @@ -7696,6 +7752,10 @@ report reason Statistics No comment provided by engineer. + + Status + No comment provided by engineer. + Stop หยุด @@ -8214,10 +8274,19 @@ your contacts and groups. ไม่มีกลุ่มนี้แล้ว No comment provided by engineer. + + This group requires a newer version of the app. Please update the app to join. + alert message +alert subtitle + This is a chat relay address, it cannot be used to connect. alert message + + This is the last active relay. Removing it will prevent message delivery to subscribers. + alert message + This is your link for channel %@! new chat action @@ -8516,10 +8585,18 @@ To connect, please ask your contact to create another connection link and check เปลี่ยนเป็นยังไม่ได้อ่าน swipe action + + Unsupported channel name + alert title + Unsupported connection link conn error description + + Unsupported contact name + alert title + Up to 100 last messages are sent to new members. No comment provided by engineer. @@ -9408,6 +9485,11 @@ Repeat connection request? Your network No comment provided by engineer. + + Your new channel %1$@ is connected to %2$d of %3$d relays. +If you cancel, the channel will be deleted - you can create it again. + alert message + Your preferences การตั้งค่าของคุณ diff --git a/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff b/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff index 90d537d06c..1189b53e3c 100644 --- a/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff +++ b/apps/ios/SimpleX Localizations/tr.xcloc/Localized Contents/tr.xliff @@ -187,16 +187,19 @@ %d relays failed + %d aktarıcı başarısız oldu channel relay bar channel subscriber relay bar %d relays not active + %d aktarıcı etkin değil channel relay bar channel subscriber relay bar %d relays removed + %d aktarıcı kaldırıldı channel relay bar channel subscriber relay bar @@ -217,10 +220,12 @@ channel subscriber relay bar %d subscriber + %d abone channel subscriber count %d subscribers + %d abone channel subscriber count @@ -230,24 +235,29 @@ channel subscriber relay bar %1$d/%2$d relays active + %1$d/%2$d aktarıcı etkin channel creation progress channel relay bar progress %1$d/%2$d relays active, %3$d errors + %1$d/%2$d aktarıcı etkin, %3$d hata channel relay bar %1$d/%2$d relays active, %3$d failed + %1$d/%2$d aktarıcı etkin, %3$d başarısız channel creation progress with errors channel relay bar %1$d/%2$d relays active, %3$d removed + %1$d/%2$d aktarıcı etkin, %3$d kaldırıldı channel relay bar %1$d/%2$d relays connected + %1$d/%2$d aktarıcı bağlı channel subscriber relay bar progress @@ -715,6 +725,10 @@ swipe action Aktif bağlantılar No comment provided by engineer. + + Add + No comment provided by engineer. + Add address to your profile, so that your SimpleX contacts can share it with other people. Profile update will be sent to your SimpleX contacts. No comment provided by engineer. @@ -739,6 +753,14 @@ swipe action Profil ekle No comment provided by engineer. + + Add relays + No comment provided by engineer. + + + Add relays to restore message delivery. + No comment provided by engineer. + Add server Sunucu ekle @@ -784,10 +806,6 @@ swipe action Mesaj sunucuları eklendi No comment provided by engineer. - - Adding relays will be supported later. - No comment provided by engineer. - Additional accent Ek ana renk @@ -1157,6 +1175,10 @@ swipe action Uygulama oturumu No comment provided by engineer. + + App update required + alert title + App version Uygulama sürümü @@ -1578,6 +1600,14 @@ in your network alert button new chat action + + Cancel and delete channel + No comment provided by engineer. + + + Cancel creating channel? + alert title + Cancel migration Taşımayı iptal et @@ -1722,8 +1752,8 @@ alert subtitle Channel will be deleted for you - this cannot be undone! No comment provided by engineer. - - Channel will start working with %1$d of %2$d relays. Proceed? + + Channel will start working with %1$d of %2$d relays. Continue? alert message @@ -2215,6 +2245,14 @@ Bu senin kendi tek kullanımlık bağlantın! Bilgisayara bağlanıyor No comment provided by engineer. + + Connecting via channel name requires a newer app version. + alert message + + + Connecting via contact name requires a newer app version. + alert message + Connection Bağlantı @@ -2368,7 +2406,7 @@ Bu senin kendi tek kullanımlık bağlantın! Continue Devam et - No comment provided by engineer. + alert action Contribute @@ -2806,6 +2844,10 @@ swipe action Benden sil No comment provided by engineer. + + Delete from history + No comment provided by engineer. + Delete group Grubu sil @@ -3578,6 +3620,10 @@ chat item action Error adding relay alert title + + Error adding relays + alert title + Error adding server Sunucu eklenirken hata oluştu @@ -3706,6 +3752,10 @@ chat item action Veritabanı silinirken hata oluştu alert title + + Error deleting message + alert title + Error deleting old database Eski veritabanı silinirken hata oluştu @@ -5877,6 +5927,10 @@ The most secure encryption. Uygulama şifresi yok Authentication unavailable + + No available relays + No comment provided by engineer. + No chat relays No comment provided by engineer. @@ -6000,6 +6054,10 @@ The most secure encryption. Hiç alınmış veya gönderilmiş dosya yok No comment provided by engineer. + + No relays + No comment provided by engineer. + No servers for private message routing. Özel mesaj yönlendirmesi için hiç sunucu yok. @@ -6628,6 +6686,10 @@ Hata: %@ Lütfen bildirimleri devre dışı bırakmayı ve yeniden etkinleştirmeyi deneyin. token info + + Please upgrade the app. + alert message + Please wait for group moderators to review your request to join the group. Lütfen grup moderatörlerinin gruba katılma isteğinizi incelemesini bekleyin. @@ -6749,10 +6811,6 @@ Hata: %@ Özel yönlendirme zaman aşımı alert title - - Proceed - alert action - Profile and server connections Profil ve sunucu bağlantıları @@ -7137,6 +7195,14 @@ swipe action Relay test failed! No comment provided by engineer. + + Relay will be removed from channel - this cannot be undone! + alert message + + + Relays added: %@. + alert message + Reliability: many relays per channel. No comment provided by engineer. @@ -7180,9 +7246,9 @@ swipe action Anahtar Zinciri'ndeki parola silinsin mi? No comment provided by engineer. - - Remove subscriber - No comment provided by engineer. + + Remove relay? + alert title Remove subscriber? @@ -8475,6 +8541,10 @@ report reason İstatistikler No comment provided by engineer. + + Status + No comment provided by engineer. + Stop Dur @@ -9036,10 +9106,19 @@ your contacts and groups. Bu grup artık mevcut değildir. No comment provided by engineer. + + This group requires a newer version of the app. Please update the app to join. + alert message +alert subtitle + This is a chat relay address, it cannot be used to connect. alert message + + This is the last active relay. Removing it will prevent message delivery to subscribers. + alert message + This is your link for channel %@! new chat action @@ -9369,11 +9448,19 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste Okunmamış swipe action + + Unsupported channel name + alert title + Unsupported connection link Desteklenmeyen bağlantı bağlantısı conn error description + + Unsupported contact name + alert title + Up to 100 last messages are sent to new members. Yeni üyelere 100e kadar en son mesajlar gönderildi. @@ -10359,6 +10446,11 @@ Bağlantı isteği tekrarlansın mı? Your network No comment provided by engineer. + + Your new channel %1$@ is connected to %2$d of %3$d relays. +If you cancel, the channel will be deleted - you can create it again. + alert message + Your preferences Tercihleriniz diff --git a/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff b/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff index c20b26e029..49f9e21eda 100644 --- a/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff +++ b/apps/ios/SimpleX Localizations/uk.xcloc/Localized Contents/uk.xliff @@ -715,6 +715,10 @@ swipe action Активні з'єднання No comment provided by engineer. + + Add + No comment provided by engineer. + Add address to your profile, so that your SimpleX contacts can share it with other people. Profile update will be sent to your SimpleX contacts. No comment provided by engineer. @@ -739,6 +743,14 @@ swipe action Додати профіль No comment provided by engineer. + + Add relays + No comment provided by engineer. + + + Add relays to restore message delivery. + No comment provided by engineer. + Add server Додати сервер @@ -784,10 +796,6 @@ swipe action Додано сервери повідомлень No comment provided by engineer. - - Adding relays will be supported later. - No comment provided by engineer. - Additional accent Додатковий акцент @@ -1155,6 +1163,10 @@ swipe action Сесія програми No comment provided by engineer. + + App update required + alert title + App version Версія програми @@ -1574,6 +1586,14 @@ in your network alert button new chat action + + Cancel and delete channel + No comment provided by engineer. + + + Cancel creating channel? + alert title + Cancel migration Скасувати міграцію @@ -1718,8 +1738,8 @@ alert subtitle Channel will be deleted for you - this cannot be undone! No comment provided by engineer. - - Channel will start working with %1$d of %2$d relays. Proceed? + + Channel will start working with %1$d of %2$d relays. Continue? alert message @@ -2211,6 +2231,14 @@ This is your own one-time link! Підключення до ПК No comment provided by engineer. + + Connecting via channel name requires a newer app version. + alert message + + + Connecting via contact name requires a newer app version. + alert message + Connection Підключення @@ -2363,7 +2391,7 @@ This is your own one-time link! Continue Продовжуйте - No comment provided by engineer. + alert action Contribute @@ -2801,6 +2829,10 @@ swipe action Видалити для мене No comment provided by engineer. + + Delete from history + No comment provided by engineer. + Delete group Видалити групу @@ -3572,6 +3604,10 @@ chat item action Error adding relay alert title + + Error adding relays + alert title + Error adding server Помилка додавання сервера @@ -3700,6 +3736,10 @@ chat item action Помилка видалення бази даних alert title + + Error deleting message + alert title + Error deleting old database Помилка видалення старої бази даних @@ -5867,6 +5907,10 @@ The most secure encryption. Немає пароля програми Authentication unavailable + + No available relays + No comment provided by engineer. + No chat relays No comment provided by engineer. @@ -5990,6 +6034,10 @@ The most secure encryption. Немає отриманих або відправлених файлів No comment provided by engineer. + + No relays + No comment provided by engineer. + No servers for private message routing. Немає серверів для маршрутизації приватних повідомлень. @@ -6613,6 +6661,10 @@ Error: %@ Будь ласка, спробуйте вимкнути та знову увімкнути сповіщення. token info + + Please upgrade the app. + alert message + Please wait for group moderators to review your request to join the group. Будь ласка, зачекайте, поки модератори групи розглянуть ваш запит на приєднання до групи. @@ -6734,10 +6786,6 @@ Error: %@ Тайм-аут приватної маршрутизації alert title - - Proceed - alert action - Profile and server connections З'єднання профілю та сервера @@ -7122,6 +7170,14 @@ swipe action Relay test failed! No comment provided by engineer. + + Relay will be removed from channel - this cannot be undone! + alert message + + + Relays added: %@. + alert message + Reliability: many relays per channel. No comment provided by engineer. @@ -7164,9 +7220,9 @@ swipe action Видалити парольну фразу з брелока? No comment provided by engineer. - - Remove subscriber - No comment provided by engineer. + + Remove relay? + alert title Remove subscriber? @@ -8459,6 +8515,10 @@ report reason Статистика No comment provided by engineer. + + Status + No comment provided by engineer. + Stop Зупинити @@ -9019,10 +9079,19 @@ your contacts and groups. Цієї групи більше не існує. No comment provided by engineer. + + This group requires a newer version of the app. Please update the app to join. + alert message +alert subtitle + This is a chat relay address, it cannot be used to connect. alert message + + This is the last active relay. Removing it will prevent message delivery to subscribers. + alert message + This is your link for channel %@! new chat action @@ -9350,11 +9419,19 @@ To connect, please ask your contact to create another connection link and check Непрочитане swipe action + + Unsupported channel name + alert title + Unsupported connection link Несумісне посилання для підключення conn error description + + Unsupported contact name + alert title + Up to 100 last messages are sent to new members. Новим користувачам надсилається до 100 останніх повідомлень. @@ -10340,6 +10417,11 @@ Repeat connection request? Your network No comment provided by engineer. + + Your new channel %1$@ is connected to %2$d of %3$d relays. +If you cancel, the channel will be deleted - you can create it again. + alert message + Your preferences Ваші уподобання 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 51cbb94bda..8823f17204 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 @@ -715,6 +715,10 @@ swipe action 活动连接 No comment provided by engineer. + + Add + No comment provided by engineer. + Add address to your profile, so that your SimpleX contacts can share it with other people. Profile update will be sent to your SimpleX contacts. No comment provided by engineer. @@ -739,6 +743,14 @@ swipe action 添加个人资料 No comment provided by engineer. + + Add relays + No comment provided by engineer. + + + Add relays to restore message delivery. + No comment provided by engineer. + Add server 添加服务器 @@ -784,10 +796,6 @@ swipe action 已添加消息服务器 No comment provided by engineer. - - Adding relays will be supported later. - No comment provided by engineer. - Additional accent 附加重音 @@ -1158,6 +1166,10 @@ swipe action 应用会话 No comment provided by engineer. + + App update required + alert title + App version 应用程序版本 @@ -1582,6 +1594,14 @@ in your network alert button new chat action + + Cancel and delete channel + No comment provided by engineer. + + + Cancel creating channel? + alert title + Cancel migration 取消迁移 @@ -1726,8 +1746,8 @@ alert subtitle Channel will be deleted for you - this cannot be undone! No comment provided by engineer. - - Channel will start working with %1$d of %2$d relays. Proceed? + + Channel will start working with %1$d of %2$d relays. Continue? alert message @@ -2219,6 +2239,14 @@ This is your own one-time link! 正连接到桌面 No comment provided by engineer. + + Connecting via channel name requires a newer app version. + alert message + + + Connecting via contact name requires a newer app version. + alert message + Connection 连接 @@ -2371,7 +2399,7 @@ This is your own one-time link! Continue 继续 - No comment provided by engineer. + alert action Contribute @@ -2809,6 +2837,10 @@ swipe action 为我删除 No comment provided by engineer. + + Delete from history + No comment provided by engineer. + Delete group 删除群组 @@ -3582,6 +3614,10 @@ chat item action Error adding relay alert title + + Error adding relays + alert title + Error adding server 添加服务器出错 @@ -3710,6 +3746,10 @@ chat item action 删除数据库错误 alert title + + Error deleting message + alert title + Error deleting old database 删除旧数据库错误 @@ -5889,6 +5929,10 @@ The most secure encryption. 没有应用程序密码 Authentication unavailable + + No available relays + No comment provided by engineer. + No chat relays No comment provided by engineer. @@ -6012,6 +6056,10 @@ The most secure encryption. 未收到或发送文件 No comment provided by engineer. + + No relays + No comment provided by engineer. + No servers for private message routing. 无私密消息路由服务器。 @@ -6642,6 +6690,10 @@ Error: %@ 请尝试禁用并重新启用通知。 token info + + Please upgrade the app. + alert message + Please wait for group moderators to review your request to join the group. 请等待群的协管审核你加入该群的请求。 @@ -6763,10 +6815,6 @@ Error: %@ 私密路由超时 alert title - - Proceed - alert action - Profile and server connections 资料和服务器连接 @@ -7150,6 +7198,14 @@ swipe action Relay test failed! No comment provided by engineer. + + Relay will be removed from channel - this cannot be undone! + alert message + + + Relays added: %@. + alert message + Reliability: many relays per channel. No comment provided by engineer. @@ -7194,9 +7250,9 @@ swipe action 从钥匙串中删除密码? No comment provided by engineer. - - Remove subscriber - No comment provided by engineer. + + Remove relay? + alert title Remove subscriber? @@ -8493,6 +8549,10 @@ report reason 统计 No comment provided by engineer. + + Status + No comment provided by engineer. + Stop 停止 @@ -9054,10 +9114,19 @@ your contacts and groups. 该群组已不存在。 No comment provided by engineer. + + This group requires a newer version of the app. Please update the app to join. + alert message +alert subtitle + This is a chat relay address, it cannot be used to connect. alert message + + This is the last active relay. Removing it will prevent message delivery to subscribers. + alert message + This is your link for channel %@! new chat action @@ -9387,11 +9456,19 @@ To connect, please ask your contact to create another connection link and check 未读 swipe action + + Unsupported channel name + alert title + Unsupported connection link 不支持的连接链接 conn error description + + Unsupported contact name + alert title + Up to 100 last messages are sent to new members. 给新成员发送了最多 100 条历史消息。 @@ -10379,6 +10456,11 @@ Repeat connection request? Your network No comment provided by engineer. + + Your new channel %1$@ is connected to %2$d of %3$d relays. +If you cancel, the channel will be deleted - you can create it again. + alert message + Your preferences 您的偏好设置 diff --git a/apps/ios/SimpleX SE/hu.lproj/Localizable.strings b/apps/ios/SimpleX SE/hu.lproj/Localizable.strings index dfb7a302b9..3aad39c5d1 100644 --- a/apps/ios/SimpleX SE/hu.lproj/Localizable.strings +++ b/apps/ios/SimpleX SE/hu.lproj/Localizable.strings @@ -29,7 +29,7 @@ "Database error" = "Adatbázishiba"; /* No comment provided by engineer. */ -"Database passphrase is different from saved in the keychain." = "Az adatbázis jelmondata nem egyezik a kulcstartóba mentettől."; +"Database passphrase is different from saved in the keychain." = "Az adatbázis jelmondata eltér a kulcstartóban tárolttól."; /* No comment provided by engineer. */ "Database passphrase is required to open chat." = "A csevegés megnyitásához adja meg az adatbázis jelmondatát."; @@ -101,7 +101,7 @@ "Unsupported format" = "Nem támogatott formátum"; /* No comment provided by engineer. */ -"Wait" = "Várjon"; +"Wait" = "Várakozás"; /* No comment provided by engineer. */ "Wrong database passphrase" = "Érvénytelen adatbázis-jelmondat"; diff --git a/apps/ios/bg.lproj/Localizable.strings b/apps/ios/bg.lproj/Localizable.strings index 17e11d1020..ec869e05b4 100644 --- a/apps/ios/bg.lproj/Localizable.strings +++ b/apps/ios/bg.lproj/Localizable.strings @@ -1364,7 +1364,7 @@ server test step */ /* No comment provided by engineer. */ "Contacts can mark messages for deletion; you will be able to view them." = "Контактите могат да маркират съобщения за изтриване; ще можете да ги разглеждате."; -/* No comment provided by engineer. */ +/* alert action */ "Continue" = "Продължи"; /* No comment provided by engineer. */ diff --git a/apps/ios/cs.lproj/Localizable.strings b/apps/ios/cs.lproj/Localizable.strings index f7e90e0c88..fc4b3f0fc6 100644 --- a/apps/ios/cs.lproj/Localizable.strings +++ b/apps/ios/cs.lproj/Localizable.strings @@ -1056,7 +1056,7 @@ server test step */ /* No comment provided by engineer. */ "Contacts can mark messages for deletion; you will be able to view them." = "Kontakty mohou označit zprávy ke smazání; vy je budete moci zobrazit."; -/* No comment provided by engineer. */ +/* alert action */ "Continue" = "Pokračovat"; /* No comment provided by engineer. */ diff --git a/apps/ios/de.lproj/Localizable.strings b/apps/ios/de.lproj/Localizable.strings index d5978b48dc..2c4e37791b 100644 --- a/apps/ios/de.lproj/Localizable.strings +++ b/apps/ios/de.lproj/Localizable.strings @@ -373,7 +373,7 @@ time interval */ "A new random profile will be shared." = "Es wird ein neues Zufallsprofil geteilt."; /* No comment provided by engineer. */ -"A separate TCP connection will be used **for each chat profile you have in the app**." = "**Für jedes von Ihnen in der App genutzte Chat-Profil** wird eine separate TCP-Verbindung genutzt."; +"A separate TCP connection will be used **for each chat profile you have in the app**." = "**Für jedes von Ihnen in der App genutzte Chat-Profil** wird eine separate TCP-Verbindung (und SOCKS-Berechtigung) genutzt."; /* No comment provided by engineer. */ "A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail." = "**Für jeden Kontakt und jedes Gruppenmitglied** wird eine separate TCP-Verbindung genutzt.\n**Bitte beachten Sie**: Wenn Sie viele Verbindungen haben, kann der Batterieverbrauch und die Datennutzung wesentlich höher sein und einige Verbindungen können scheitern."; @@ -505,9 +505,6 @@ swipe action */ /* No comment provided by engineer. */ "Added message servers" = "Nachrichtenserver hinzugefügt"; -/* No comment provided by engineer. */ -"Adding relays will be supported later." = "Das Hinzufügen von Relais wird zu einem späteren Zeitpunkt unterstützt."; - /* No comment provided by engineer. */ "Additional accent" = "Erste Akzentfarbe"; @@ -617,7 +614,7 @@ swipe action */ "All your contacts will remain connected. Profile update will be sent to your contacts." = "Alle Ihre Kontakte bleiben verbunden. Es wird eine Profilaktualisierung an Ihre Kontakte gesendet."; /* No comment provided by engineer. */ -"All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays." = "Alle Ihre Kontakte, Unterhaltungen und Dateien werden sicher verschlüsselt und in Daten-Paketen auf die konfigurierten XTFP-Relais hochgeladen."; +"All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays." = "Alle Ihre Kontakte, Unterhaltungen und Dateien werden sicher verschlüsselt und in Daten-Paketen auf die konfigurierten XTFP-Router hochgeladen."; /* No comment provided by engineer. */ "Allow" = "Erlauben"; @@ -716,10 +713,10 @@ swipe action */ "always" = "Immer"; /* No comment provided by engineer. */ -"Always use private routing." = "Sie nutzen immer privates Routing."; +"Always use private routing." = "Immer privates Routing nutzen."; /* No comment provided by engineer. */ -"Always use relay" = "Über ein Relais verbinden"; +"Always use relay" = "Immer über einen Router verbinden"; /* No comment provided by engineer. */ "An empty chat profile with the provided name is created, and the app opens as usual." = "Es wurde ein leeres Chat-Profil mit dem eingegebenen Namen erstellt und die App öffnet wie gewohnt."; @@ -764,7 +761,7 @@ swipe action */ "App version: v%@" = "App Version: v%@"; /* No comment provided by engineer. */ -"Appearance" = "Erscheinungsbild"; +"Appearance" = "Darstellung"; /* No comment provided by engineer. */ "Apply" = "Anwenden"; @@ -1182,9 +1179,6 @@ alert subtitle */ /* No comment provided by engineer. */ "Channel will be deleted for you - this cannot be undone!" = "Der Kanal wird für Sie gelöscht. Dies kann nicht rückgängig gemacht werden!"; -/* alert message */ -"Channel will start working with %d of %d relays. Proceed?" = "Der Kanal wird mit %1$d von %2$d Relais gestartet. Fortfahren?"; - /* No comment provided by engineer. */ "Channels" = "Kanäle"; @@ -1237,7 +1231,7 @@ alert subtitle */ "Chat preferences were changed." = "Die Chat-Präferenzen wurden geändert."; /* No comment provided by engineer. */ -"Chat profile" = "Benutzerprofil"; +"Chat profile" = "Chat-Profil"; /* No comment provided by engineer. */ "Chat relay" = "Chat-Relais"; @@ -1367,7 +1361,7 @@ chat toolbar */ "Completed" = "Abgeschlossen"; /* No comment provided by engineer. */ -"Conditions accepted on: %@." = "Die Nutzungsbedingungen wurden akzeptiert am: %@."; +"Conditions accepted on: %@." = "Die Nutzungsbedingungen wurden am %@ akzeptiert."; /* No comment provided by engineer. */ "Conditions are accepted for the operator(s): **%@**." = "Die Nutzungsbedingungen der/des Betreiber(s) werden akzeptiert: **%@**."; @@ -1382,10 +1376,10 @@ chat toolbar */ "Conditions will be accepted for the operator(s): **%@**." = "Die Nutzungsbedingungen der/des Betreiber(s) werden akzeptiert: **%@**."; /* No comment provided by engineer. */ -"Conditions will be accepted on: %@." = "Die Nutzungsbedingungen werden akzeptiert am: %@."; +"Conditions will be accepted on: %@." = "Die Nutzungsbedingungen werden am %@ akzeptiert."; /* No comment provided by engineer. */ -"Conditions will be automatically accepted for enabled operators on: %@." = "Die Nutzungsbedingungen der aktivierten Betreiber werden automatisch akzeptiert am: %@."; +"Conditions will be automatically accepted for enabled operators on: %@." = "Die Nutzungsbedingungen der aktivierten Betreiber werden am %@ automatisch akzeptiert."; /* No comment provided by engineer. */ "Configure ICE servers" = "ICE-Server konfigurieren"; @@ -1634,7 +1628,7 @@ server test step */ /* blocking reason */ "Content violates conditions of use" = "Inhalt verletzt Nutzungsbedingungen"; -/* No comment provided by engineer. */ +/* alert action */ "Continue" = "Weiter"; /* No comment provided by engineer. */ @@ -2169,10 +2163,10 @@ alert button */ "Do NOT send messages directly, even if your or destination server does not support private routing." = "Nachrichten werden nicht direkt versendet, selbst wenn Ihr oder der Zielserver kein privates Routing unterstützt."; /* No comment provided by engineer. */ -"Do not use credentials with proxy." = "Verwenden Sie keine Anmeldeinformationen mit einem Proxy."; +"Do not use credentials with proxy." = "Keine Anmeldeinformationen mit einem Proxy verwenden."; /* No comment provided by engineer. */ -"Do NOT use private routing." = "Sie nutzen KEIN privates Routing."; +"Do NOT use private routing." = "KEIN privates Routing nutzen."; /* No comment provided by engineer. */ "Do NOT use SimpleX for emergency calls." = "SimpleX NICHT für Notrufe nutzen."; @@ -3975,10 +3969,10 @@ servers warning */ "New server" = "Neuer Server"; /* No comment provided by engineer. */ -"New SOCKS credentials will be used every time you start the app." = "Jedes Mal wenn Sie die App starten, werden neue SOCKS-Anmeldeinformationen genutzt"; +"New SOCKS credentials will be used every time you start the app." = "Bei jedem Neustart der App, werden neue SOCKS-Anmeldeinformationen genutzt."; /* No comment provided by engineer. */ -"New SOCKS credentials will be used for each server." = "Für jeden Server werden neue SOCKS-Anmeldeinformationen genutzt"; +"New SOCKS credentials will be used for each server." = "Für jeden Server werden neue SOCKS-Anmeldeinformationen genutzt."; /* pref value */ "no" = "Nein"; @@ -4074,7 +4068,7 @@ servers warning */ "No received or sent files" = "Keine herunter- oder hochgeladenen Dateien"; /* servers error */ -"No servers for private message routing." = "Keine Server für privates Nachrichten-Routing."; +"No servers for private message routing." = "Keine Router für privates Nachrichten-Routing."; /* servers error */ "No servers to receive files." = "Keine Server für das Herunterladen von Dateien."; @@ -4548,7 +4542,7 @@ alert button */ "Privacy policy and conditions of use." = "Datenschutz- und Nutzungsbedingungen."; /* No comment provided by engineer. */ -"Privacy: for owners and subscribers." = "Privatsphäre: für Besitzer und Abonnenten."; +"Privacy: for owners and subscribers." = "Privatsphäre: Für Eigentümer und Abonnenten."; /* No comment provided by engineer. */ "Private and secure messaging." = "Private und sichere Kommunikation."; @@ -4577,9 +4571,6 @@ alert button */ /* alert title */ "Private routing timeout" = "Zeitüberschreitung der privaten Routing-Sitzung"; -/* alert action */ -"Proceed" = "Fortfahren"; - /* No comment provided by engineer. */ "Profile and server connections" = "Profil und Serververbindungen"; @@ -4644,7 +4635,7 @@ alert button */ "Protect your chat profiles with a password!" = "Ihre Chat-Profile mit einem Passwort schützen!"; /* No comment provided by engineer. */ -"Protect your IP address from the messaging relays chosen by your contacts.\nEnable in *Network & servers* settings." = "Schützen Sie Ihre IP-Adresse vor den Nachrichten-Relais, die Ihre Kontakte ausgewählt haben.\nAktivieren Sie es in den *Netzwerk & Server* Einstellungen."; +"Protect your IP address from the messaging relays chosen by your contacts.\nEnable in *Network & servers* settings." = "Schützen Sie Ihre IP-Adresse vor den Nachrichten-Routern, die Ihre Kontakte ausgewählt haben.\nAktivieren Sie es in den *Netzwerk & Server* Einstellungen."; /* No comment provided by engineer. */ "Protocol background timeout" = "Protokoll Hintergrund-Zeitüberschreitung"; @@ -4829,13 +4820,13 @@ swipe action */ "Relay server is only used if necessary. Another party can observe your IP address." = "Relais-Server werden nur genutzt, wenn sie benötigt werden. Ihre IP-Adresse kann von Anderen erfasst werden."; /* No comment provided by engineer. */ -"Relay server protects your IP address, but it can observe the duration of the call." = "Relais-Server schützen Ihre IP-Adresse, aber sie können die Anrufdauer erfassen."; +"Relay server protects your IP address, but it can observe the duration of the call." = "Relais-Server schützen Ihre IP-Adresse, können aber die Anrufdauer erfassen."; /* No comment provided by engineer. */ "Relay test failed!" = "Relais-Test fehlgeschlagen!"; /* No comment provided by engineer. */ -"Reliability: many relays per channel." = "Zuverlässigkeit: mehrere Relais pro Kanal."; +"Reliability: many relays per channel." = "Zuverlässigkeit: Mehrere Relais pro Kanal."; /* alert action */ "Remove" = "Entfernen"; @@ -4861,9 +4852,6 @@ swipe action */ /* No comment provided by engineer. */ "Remove passphrase from keychain?" = "Passwort aus dem Schlüsselbund entfernen?"; -/* No comment provided by engineer. */ -"Remove subscriber" = "Abonnent entfernen"; - /* alert title */ "Remove subscriber?" = "Abonnent entfernen?"; @@ -5223,7 +5211,7 @@ chat item action */ "security code changed" = "Sicherheitscode wurde geändert"; /* No comment provided by engineer. */ -"Security: owners hold channel keys." = "Sicherheit: Eigentümer besitzen die Kanalschlüssel."; +"Security: owners hold channel keys." = "Sicherheit: Nur die Eigentümer des Kanals besitzen die Schlüssel."; /* chat item action */ "Select" = "Auswählen"; @@ -5280,10 +5268,10 @@ chat item action */ "Send message to enable calls." = "Nachricht senden, um Anrufe zu aktivieren."; /* No comment provided by engineer. */ -"Send messages directly when IP address is protected and your or destination server does not support private routing." = "Nachrichten werden direkt versendet, wenn die IP-Adresse geschützt ist, und Ihr oder der Zielserver kein privates Routing unterstützt."; +"Send messages directly when IP address is protected and your or destination server does not support private routing." = "Nachrichten werden direkt versendet, wenn die IP-Adresse geschützt ist, und Ihr oder der Ziel-Server kein privates Routing unterstützt."; /* No comment provided by engineer. */ -"Send messages directly when your or destination server does not support private routing." = "Nachrichten werden direkt versendet, wenn Ihr oder der Zielserver kein privates Routing unterstützt."; +"Send messages directly when your or destination server does not support private routing." = "Nachrichten werden direkt versendet, wenn Ihr oder der Ziel-Server kein privates Routing unterstützt."; /* No comment provided by engineer. */ "Send notifications" = "Benachrichtigungen senden"; @@ -5406,7 +5394,7 @@ chat item action */ "server queue info: %@\n\nlast received msg: %@" = "Server-Warteschlangen-Information: %1$@\n\nZuletzt empfangene Nachricht: %2$@"; /* relay test error */ -"Server requires authorization to connect to relay, check password." = "Der Server erfordert eine Autorisierung, um eine Verbindung zum Relais herzustellen. Bitte Passwort überprüfen."; +"Server requires authorization to connect to relay, check password." = "Der Server erfordert eine Autorisierung, um eine Verbindung zum Router herzustellen. Bitte Passwort überprüfen."; /* server test error */ "Server requires authorization to create queues, check password." = "Der Server erfordert zum Erstellen von Warteschlangen eine Autorisierung. Bitte überprüfen Sie das Passwort."; @@ -5539,7 +5527,7 @@ chat item action */ "Share profile" = "Profil teilen"; /* No comment provided by engineer. */ -"Share relay address" = "Relais-Adresse teilen"; +"Share relay address" = "Router-Adresse teilen"; /* No comment provided by engineer. */ "Share SimpleX address on social media." = "Die SimpleX-Adresse auf sozialen Medien teilen."; @@ -6141,7 +6129,7 @@ server test failure */ "To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "Um Ihre Informationen zu schützen, schalten Sie die SimpleX-Sperre ein.\nSie werden aufgefordert, die Authentifizierung abzuschließen, bevor diese Funktion aktiviert wird."; /* No comment provided by engineer. */ -"To protect your IP address, private routing uses your SMP servers to deliver messages." = "Zum Schutz Ihrer IP-Adresse, wird für die Nachrichten-Auslieferung privates Routing über Ihre konfigurierten SMP-Server genutzt."; +"To protect your IP address, private routing uses your SMP servers to deliver messages." = "Zum Schutz Ihrer IP-Adresse, wird für die Nachrichten-Auslieferung privates Routing über Ihre konfigurierten SMP-Router genutzt."; /* No comment provided by engineer. */ "To protect your privacy, SimpleX uses separate IDs for each of your contacts." = "Zum Schutz Ihrer Privatsphäre verwendet SimpleX an Stelle von Benutzerkennungen, die von allen anderen Plattformen verwendet werden, Kennungen für Nachrichtenwarteschlangen, die für jeden Ihrer Kontakte individuell sind."; @@ -6270,7 +6258,7 @@ server test failure */ "Unknown error" = "Unbekannter Fehler"; /* No comment provided by engineer. */ -"unknown servers" = "Unbekannte Relais"; +"unknown servers" = "Unbekannte Server"; /* alert title */ "Unknown servers!" = "Unbekannte Server!"; @@ -6417,10 +6405,10 @@ server test failure */ "Use only local notifications?" = "Nur lokale Benachrichtigungen nutzen?"; /* No comment provided by engineer. */ -"Use private routing with unknown servers when IP address is not protected." = "Sie nutzen privates Routing mit unbekannten Servern, wenn Ihre IP-Adresse nicht geschützt ist."; +"Use private routing with unknown servers when IP address is not protected." = "Bei unbekannten Servern privates Routing nutzen, wenn Ihre IP-Adresse nicht geschützt ist."; /* No comment provided by engineer. */ -"Use private routing with unknown servers." = "Sie nutzen privates Routing mit unbekannten Servern."; +"Use private routing with unknown servers." = "Bei unbekannten Servern privates Routing nutzen."; /* No comment provided by engineer. */ "Use relay" = "Relais verwenden"; @@ -6672,7 +6660,7 @@ server test failure */ "Without Tor or VPN, your IP address will be visible to file servers." = "Ohne Tor- oder VPN-Nutzung wird Ihre IP-Adresse für Datei-Server sichtbar sein."; /* alert message */ -"Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." = "Ohne Tor- oder VPN-Nutzung wird Ihre IP-Adresse für diese XFTP-Relais sichtbar sein: %@."; +"Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." = "Ohne Tor- oder VPN-Nutzung wird Ihre IP-Adresse für diese XFTP-Router sichtbar sein: %@."; /* No comment provided by engineer. */ "Wrong database passphrase" = "Falsches Datenbank-Passwort"; @@ -6759,7 +6747,7 @@ server test failure */ "You can accept calls from lock screen, without device and app authentication." = "Sie können Anrufe ohne Geräte- und App-Authentifizierung vom Sperrbildschirm aus annehmen."; /* No comment provided by engineer. */ -"You can change it in Appearance settings." = "Kann von Ihnen in den Erscheinungsbild-Einstellungen geändert werden."; +"You can change it in Appearance settings." = "Sie können dies in den Einstellungen unter „Darstellung“ ändern."; /* No comment provided by engineer. */ "You can configure servers via settings." = "Sie können die Server über die Einstellungen konfigurieren."; diff --git a/apps/ios/es.lproj/Localizable.strings b/apps/ios/es.lproj/Localizable.strings index 49826ff7f6..cf03ae6dbf 100644 --- a/apps/ios/es.lproj/Localizable.strings +++ b/apps/ios/es.lproj/Localizable.strings @@ -1,5 +1,5 @@ /* No comment provided by engineer. */ -" (can be copied)" = " (puede copiarse)"; +" (can be copied)" = " (puede ser copiado)"; /* No comment provided by engineer. */ "_italic_" = "\\_italic_"; @@ -505,9 +505,6 @@ swipe action */ /* No comment provided by engineer. */ "Added message servers" = "Servidores de mensajes añadidos"; -/* No comment provided by engineer. */ -"Adding relays will be supported later." = "Añadir servidores estará disponible en una versión posterior."; - /* No comment provided by engineer. */ "Additional accent" = "Acento adicional"; @@ -1182,9 +1179,6 @@ alert subtitle */ /* No comment provided by engineer. */ "Channel will be deleted for you - this cannot be undone!" = "El canal será eliminado para tí. ¡No puede deshacerse!"; -/* alert message */ -"Channel will start working with %d of %d relays. Proceed?" = "El canal comenzará a funcionar con %1$d de %2$d servidores. ¿Continuar?"; - /* No comment provided by engineer. */ "Channels" = "Canales"; @@ -1634,7 +1628,7 @@ server test step */ /* blocking reason */ "Content violates conditions of use" = "El contenido viola las condiciones de uso"; -/* No comment provided by engineer. */ +/* alert action */ "Continue" = "Continuar"; /* No comment provided by engineer. */ @@ -4577,9 +4571,6 @@ alert button */ /* alert title */ "Private routing timeout" = "Timeout enrutamiento privado"; -/* alert action */ -"Proceed" = "Continuar"; - /* No comment provided by engineer. */ "Profile and server connections" = "Eliminar perfil y conexiones"; @@ -4861,9 +4852,6 @@ swipe action */ /* No comment provided by engineer. */ "Remove passphrase from keychain?" = "¿Eliminar contraseña de Keychain?"; -/* No comment provided by engineer. */ -"Remove subscriber" = "Eliminar suscriptor"; - /* alert title */ "Remove subscriber?" = "¿Eliminar suscriptor?"; diff --git a/apps/ios/fi.lproj/Localizable.strings b/apps/ios/fi.lproj/Localizable.strings index b75323054a..3b1bd6523c 100644 --- a/apps/ios/fi.lproj/Localizable.strings +++ b/apps/ios/fi.lproj/Localizable.strings @@ -732,7 +732,7 @@ server test step */ /* No comment provided by engineer. */ "Contacts can mark messages for deletion; you will be able to view them." = "Kontaktit voivat merkitä viestit poistettaviksi; voit katsella niitä."; -/* No comment provided by engineer. */ +/* alert action */ "Continue" = "Jatka"; /* No comment provided by engineer. */ diff --git a/apps/ios/fr.lproj/Localizable.strings b/apps/ios/fr.lproj/Localizable.strings index 329490f34b..91cd6f3078 100644 --- a/apps/ios/fr.lproj/Localizable.strings +++ b/apps/ios/fr.lproj/Localizable.strings @@ -1357,7 +1357,7 @@ server test step */ /* blocking reason */ "Content violates conditions of use" = "Le contenu enfreint les conditions d'utilisation"; -/* No comment provided by engineer. */ +/* alert action */ "Continue" = "Continuer"; /* No comment provided by engineer. */ diff --git a/apps/ios/hu.lproj/Localizable.strings b/apps/ios/hu.lproj/Localizable.strings index 623d79433c..029fb9edd5 100644 --- a/apps/ios/hu.lproj/Localizable.strings +++ b/apps/ios/hu.lproj/Localizable.strings @@ -38,10 +38,10 @@ "[Send us email](mailto:chat@simplex.chat)" = "[Küldjön nekünk e-mailt](mailto:chat@simplex.chat)"; /* No comment provided by engineer. */ -"**Create 1-time link**: to create and share a new invitation link." = "**Partner hozzáadása:** új meghívási hivatkozás létrehozásához, vagy egy kapott hivatkozáson keresztül történő kapcsolódáshoz."; +"**Create 1-time link**: to create and share a new invitation link." = "**Partner hozzáadása**: új meghívási hivatkozás létrehozásához, vagy egy kapott hivatkozáson keresztül történő kapcsolódáshoz."; /* No comment provided by engineer. */ -"**Create group**: to create a new group." = "**Csoport létrehozása:** új csoport létrehozásához."; +"**Create group**: to create a new group." = "**Csoport létrehozása**: új csoport létrehozásához."; /* No comment provided by engineer. */ "**e2e encrypted** audio call" = "**végpontok között titkosított** hanghívás"; @@ -50,19 +50,19 @@ "**e2e encrypted** video call" = "**végpontok között titkosított** videóhívás"; /* No comment provided by engineer. */ -"**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." = "**Privátabb:** 20 percenként ellenőrzi az új üzeneteket. Az eszköztoken meg lesz osztva a SimpleX Chat kiszolgálóval, de az nem, hogy hány partnere vagy üzenete van."; +"**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." = "**Privátabb**: 20 percenként ellenőrzi az új üzeneteket. Az eszköztoken meg lesz osztva a SimpleX Chat kiszolgálóval, de az nem, hogy hány partnere vagy üzenete van."; /* No comment provided by engineer. */ "**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." = "**A legprivátabb**: Az alkalmazás nem használja a SimpleX Chat push-kiszolgálóját. Az alkalmazás a háttérben ellenőrzi az üzeneteket, amikor a rendszer ezt lehetővé teszi, attól függően, hogy Ön milyen gyakran használja az alkalmazást."; /* No comment provided by engineer. */ -"**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." = "**Megjegyzés:** ha két eszközön is ugyanazt az adatbázist használja, akkor biztonsági védelemként megszakítja a partnereitől érkező üzenetek visszafejtését."; +"**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." = "**Megjegyzés**: ha két eszközön is ugyanazt az adatbázist használja, akkor biztonsági védelemként megszakítja a partnereitől érkező üzenetek visszafejtését."; /* No comment provided by engineer. */ -"**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Megjegyzés:** NEM fogja tudni helyreállítani vagy módosítani a jelmondatot abban az esetben, ha elveszíti."; +"**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Megjegyzés**: NEM fogja tudni helyreállítani vagy módosítani a jelmondatot abban az esetben, ha elveszíti."; /* No comment provided by engineer. */ -"**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." = "**Megjegyzés:** az eszköztoken és az értesítések el lesznek küldve a SimpleX Chat értesítési kiszolgálóra, kivéve az üzenet tartalma, mérete vagy az, hogy kitől származik."; +"**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." = "**Megjegyzés**: az eszköztoken és az értesítések el lesznek küldve a SimpleX Chat értesítési kiszolgálóra, kivéve az üzenet tartalma, mérete vagy az, hogy kitől származik."; /* No comment provided by engineer. */ "**Scan / Paste link**: to connect via a link you received." = "**Hivatkozás beolvasása / beillesztése**: egy kapott hivatkozáson keresztüli kapcsolódáshoz."; @@ -71,10 +71,10 @@ "**Test relay** to retrieve its name." = "**Átjátszó tesztelése** a nevének lekéréséhez."; /* No comment provided by engineer. */ -"**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Figyelmeztetés:** Az azonnali leküldéses értesítésekhez a kulcstartóban tárolt jelmondat megadása szükséges."; +"**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Figyelmeztetés**: Az azonnali leküldéses értesítésekhez a kulcstartóban tárolt jelmondat megadása szükséges."; /* No comment provided by engineer. */ -"**Warning**: the archive will be removed." = "**Figyelmeztetés:** az archívum el lesz távolítva."; +"**Warning**: the archive will be removed." = "**Figyelmeztetés**: az archívum el lesz távolítva."; /* No comment provided by engineer. */ "*bold*" = "\\*félkövér*"; @@ -226,10 +226,10 @@ channel relay bar */ "%d/%d relays active, %d removed" = "%1$d/%2$d átjátszó aktív, %3$d eltávolítva"; /* channel subscriber relay bar progress */ -"%d/%d relays connected" = "%1$d/%2$d átjátszó kapcsolódva"; +"%d/%d relays connected" = "%1$d/%2$d átjátszó kapcsolódott"; /* channel subscriber relay bar */ -"%d/%d relays connected, %d errors" = "%1$d/%2$d átjátszó kapcsolódva, %3$d hiba"; +"%d/%d relays connected, %d errors" = "%1$d/%2$d átjátszó kapcsolódott, %3$d hiba"; /* channel subscriber relay bar */ "%d/%d relays connected, %d failed" = "%1$d/%2$d átjátszó kapcsolódott, %3$d átjátszóhoz nem sikerült kapcsolódni"; @@ -376,7 +376,7 @@ time interval */ "A separate TCP connection will be used **for each chat profile you have in the app**." = "**Az összes csevegési profiljához az alkalmazásban** külön TCP-kapcsolat lesz használva."; /* No comment provided by engineer. */ -"A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail." = "**Az összes partneréhez és csoporttaghoz** külön TCP-kapcsolat lesz használva.\n**Megjegyzés:** ha sok kapcsolata van, akkor az akkumulátor-használat és az adatforgalom jelentősen megnövekedhet, és néhány kapcsolódási kísérlet sikertelen lehet."; +"A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail." = "**Az összes partneréhez és csoporttaghoz** külön TCP-kapcsolat lesz használva.\n**Megjegyzés**: ha sok kapcsolata van, akkor az akkumulátor-használat és az adatforgalom jelentősen megnövekedhet, és néhány kapcsolódási kísérlet sikertelen lehet."; /* No comment provided by engineer. */ "Abort" = "Megszakítás"; @@ -449,7 +449,7 @@ swipe action */ "accepted you" = "befogadta Önt"; /* No comment provided by engineer. */ -"Acknowledged" = "Visszaigazolt"; +"Acknowledged" = "Visszaigazolva"; /* No comment provided by engineer. */ "Acknowledgement errors" = "Visszaigazolási hibák"; @@ -505,9 +505,6 @@ swipe action */ /* No comment provided by engineer. */ "Added message servers" = "Hozzáadott üzenetkiszolgálók"; -/* No comment provided by engineer. */ -"Adding relays will be supported later." = "Az átjátszók hozzáadása később lesz támogatott."; - /* No comment provided by engineer. */ "Additional accent" = "További kiemelőszín"; @@ -716,10 +713,10 @@ swipe action */ "always" = "mindig"; /* No comment provided by engineer. */ -"Always use private routing." = "Mindig legyen használva privát útválasztás."; +"Always use private routing." = "Privát útválasztás használata minden esetben."; /* No comment provided by engineer. */ -"Always use relay" = "Mindig legyen használva átjátszó"; +"Always use relay" = "Átjátszó használata minden esetben"; /* No comment provided by engineer. */ "An empty chat profile with the provided name is created, and the app opens as usual." = "Egy üres csevegési profil lesz létrehozva a megadott névvel, és az alkalmazás a szokásos módon megnyílik."; @@ -878,7 +875,7 @@ swipe action */ "Be free\nin your network" = "Váljon szabaddá\na saját hálózatában"; /* No comment provided by engineer. */ -"Be free in your network." = "Legyen szabad a saját hálózatában."; +"Be free in your network." = "Váljon szabaddá a saját hálózatában."; /* No comment provided by engineer. */ "Because we destroyed the power to know who you are. So that your power can never be taken." = "Mert felszámoltuk a lehetőségét is annak, hogy megtudjuk, Ön kicsoda. Így az önrendelkezése soha nem kerülhet idegen kezekbe."; @@ -951,10 +948,10 @@ swipe action */ /* blocked chat item marked deleted chat item preview text */ -"blocked by admin" = "letiltva az adminisztrátor által"; +"blocked by admin" = "az adminisztrátor letiltotta"; /* No comment provided by engineer. */ -"Blocked by admin" = "Letiltva az adminisztrátor által"; +"Blocked by admin" = "Az adminisztrátor letiltotta"; /* No comment provided by engineer. */ "Blur for better privacy." = "Elhomályosítás a jobb adatvédelemért."; @@ -1171,7 +1168,7 @@ alert subtitle */ "channel profile updated" = "csatornaprofil frissítve"; /* alert message */ -"Channel profile was changed. If you save it, the updated profile will be sent to channel subscribers." = "A csatornaprofil módosult. Ha menti, akkor a frissített profil el lesz küldve a csatorna feliratkozóinak."; +"Channel profile was changed. If you save it, the updated profile will be sent to channel subscribers." = "Csatornaprofil módosítva. Ha menti, akkor a frissített profil el lesz küldve a csatorna feliratkozóinak."; /* alert title */ "Channel temporarily unavailable" = "A csatorna ideiglenesen nem érhető el"; @@ -1182,9 +1179,6 @@ alert subtitle */ /* No comment provided by engineer. */ "Channel will be deleted for you - this cannot be undone!" = "A csatorna törölve lesz az Ön számára – ez a művelet nem vonható vissza!"; -/* alert message */ -"Channel will start working with %d of %d relays. Proceed?" = "A csatorna %2$d átjátszóból %1$d használatával kezd el működni. Folytatja?"; - /* No comment provided by engineer. */ "Channels" = "Csatornák"; @@ -1634,7 +1628,7 @@ server test step */ /* blocking reason */ "Content violates conditions of use" = "A tartalom sérti a használati feltételeket"; -/* No comment provided by engineer. */ +/* alert action */ "Continue" = "Folytatás"; /* No comment provided by engineer. */ @@ -1800,7 +1794,7 @@ server test step */ "Database passphrase & export" = "Adatbázis-jelmondat és -exportálás"; /* No comment provided by engineer. */ -"Database passphrase is different from saved in the keychain." = "Az adatbázis jelmondata nem egyezik a kulcstartóba mentettől."; +"Database passphrase is different from saved in the keychain." = "Az adatbázis jelmondata eltér a kulcstartóban tárolttól."; /* No comment provided by engineer. */ "Database passphrase is required to open chat." = "A csevegés megnyitásához adja meg az adatbázis jelmondatát."; @@ -2172,7 +2166,7 @@ alert button */ "Do not use credentials with proxy." = "Ne használja a hitelesítési adatokat proxyval."; /* No comment provided by engineer. */ -"Do NOT use private routing." = "NE legyen használva privát útválasztás."; +"Do NOT use private routing." = "Privát útválasztás használatának elkerülése."; /* No comment provided by engineer. */ "Do NOT use SimpleX for emergency calls." = "NE használja a SimpleXet segélyhívásokhoz."; @@ -2356,7 +2350,7 @@ chat item action */ "Encrypted message: database migration error" = "Titkosított üzenet: adatbázis-átköltöztetési hiba"; /* notification */ -"Encrypted message: keychain error" = "Titkosított üzenet: kulcstartó hiba"; +"Encrypted message: keychain error" = "Titkosított üzenet: kulcstartóhiba"; /* notification */ "Encrypted message: no passphrase" = "Titkosított üzenet: nincs jelmondat"; @@ -2461,13 +2455,13 @@ chat item action */ "Error accepting contact request" = "Hiba történt a partneri kapcsolatkérés elfogadásakor"; /* alert title */ -"Error accepting member" = "Hiba a tag befogadásakor"; +"Error accepting member" = "Hiba történt a tag befogadásakor"; /* No comment provided by engineer. */ "Error adding member(s)" = "Hiba történt a tag(ok) hozzáadásakor"; /* alert title */ -"Error adding relay" = "Hiba az átjátszó hozzáadásakor"; +"Error adding relay" = "Hiba történt az átjátszó hozzáadásakor"; /* alert title */ "Error adding server" = "Hiba történt a kiszolgáló hozzáadásakor"; @@ -2479,7 +2473,7 @@ chat item action */ "Error changing address" = "Hiba történt a cím módosításakor"; /* alert title */ -"Error changing chat profile" = "Hiba a csevegési profil módosításakor"; +"Error changing chat profile" = "Hiba történt a csevegési profil módosításakor"; /* No comment provided by engineer. */ "Error changing connection profile" = "Hiba történt a kapcsolati profilra való váltáskor"; @@ -2506,7 +2500,7 @@ chat item action */ "Error creating address" = "Hiba történt a cím létrehozásakor"; /* alert title */ -"Error creating channel" = "Hiba a csatorna létrehozásakor"; +"Error creating channel" = "Hiba történt a csatorna létrehozásakor"; /* No comment provided by engineer. */ "Error creating group" = "Hiba történt a csoport létrehozásakor"; @@ -2533,7 +2527,7 @@ chat item action */ "Error decrypting file" = "Hiba történt a fájl visszafejtésekor"; /* alert title */ -"Error deleting chat" = "Hiba a csevegés törlésekor"; +"Error deleting chat" = "Hiba történt a csevegés törlésekor"; /* alert title */ "Error deleting chat database" = "Hiba történt a csevegési adatbázis törlésekor"; @@ -2614,7 +2608,7 @@ chat item action */ "Error resetting statistics" = "Hiba történt a statisztikák visszaállításakor"; /* No comment provided by engineer. */ -"Error saving channel profile" = "Hiba a csatornaprofil mentésekor"; +"Error saving channel profile" = "Hiba történt a csatornaprofil mentésekor"; /* alert title */ "Error saving chat list" = "Hiba történt a csevegési lista mentésekor"; @@ -2653,13 +2647,13 @@ chat item action */ "Error sending message" = "Hiba történt az üzenet elküldésekor"; /* No comment provided by engineer. */ -"Error setting auto-accept" = "Hiba az automatikus elfogadás beállításakor"; +"Error setting auto-accept" = "Hiba történt az automatikus elfogadás beállításakor"; /* No comment provided by engineer. */ "Error setting delivery receipts!" = "Hiba történt a kézbesítési jelentések beállításakor!"; /* alert title */ -"Error sharing channel" = "Hiba a csatorna megosztásakor"; +"Error sharing channel" = "Hiba történt a csatorna megosztásakor"; /* No comment provided by engineer. */ "Error starting chat" = "Hiba történt a csevegés elindításakor"; @@ -3057,7 +3051,7 @@ servers warning */ "group profile updated" = "csoportprofil frissítve"; /* alert message */ -"Group profile was changed. If you save it, the updated profile will be sent to group members." = "Csoportprofil módosítva. Ha menti, akkor a frissített profil el lesz küldve a csoporttagoknak."; +"Group profile was changed. If you save it, the updated profile will be sent to group members." = "Csoportprofil módosítva. Ha menti, akkor a frissített profil el lesz küldve a csoport tagjainak."; /* No comment provided by engineer. */ "Group welcome message" = "A csoport üdvözlőüzenete"; @@ -3129,7 +3123,7 @@ servers warning */ "How to use it" = "Használati útmutató"; /* No comment provided by engineer. */ -"How to use your servers" = "Hogyan használja a saját kiszolgálóit"; +"How to use your servers" = "Útmutató a saját kiszolgálók használatához"; /* No comment provided by engineer. */ "Hungarian interface" = "Magyar kezelőfelület"; @@ -3267,7 +3261,7 @@ servers warning */ "Initial role" = "Kezdeti szerepkör"; /* No comment provided by engineer. */ -"Install SimpleX Chat for terminal" = "A SimpleX Chat terminálhoz telepítése"; +"Install SimpleX Chat for terminal" = "SimpleX Chat telepítése a terminálhoz"; /* No comment provided by engineer. */ "Instant" = "Azonnali"; @@ -3501,7 +3495,7 @@ servers warning */ "Less traffic on mobile networks." = "Kevesebb adatforgalom a mobilhálózatokon."; /* No comment provided by engineer. */ -"Let someone connect to you" = "Hagyja, hogy valaki elérje Önt"; +"Let someone connect to you" = "Legyen elérhető mások számára"; /* email subject */ "Let's talk in SimpleX Chat" = "Beszélgessünk a SimpleX Chatben"; @@ -4548,7 +4542,7 @@ alert button */ "Privacy policy and conditions of use." = "Adatvédelmi szabályzat és felhasználási feltételek."; /* No comment provided by engineer. */ -"Privacy: for owners and subscribers." = "Adatvédelem: tulajdonosok és előfizetők számára."; +"Privacy: for owners and subscribers." = "Adatvédelem: tulajdonosok és feliratkozók számára."; /* No comment provided by engineer. */ "Private and secure messaging." = "Privát és biztonságos üzenetváltás."; @@ -4577,9 +4571,6 @@ alert button */ /* alert title */ "Private routing timeout" = "Privát útválasztás időtúllépése"; -/* alert action */ -"Proceed" = "Folytatás"; - /* No comment provided by engineer. */ "Profile and server connections" = "Profil és kiszolgálókapcsolatok"; @@ -4826,10 +4817,10 @@ swipe action */ "Relay results:" = "Átjátszóeredmények:"; /* No comment provided by engineer. */ -"Relay server is only used if necessary. Another party can observe your IP address." = "Az átjátszó csak szükség esetén lesz használva. Egy másik fél megfigyelheti az IP-címét."; +"Relay server is only used if necessary. Another party can observe your IP address." = "Az átjátszó kiszolgáló csak szükség esetén lesz használva. Egy másik fél megfigyelheti az IP-címét."; /* No comment provided by engineer. */ -"Relay server protects your IP address, but it can observe the duration of the call." = "Az átjátszó megvédi az IP-címét, de megfigyelheti a hívás időtartamát."; +"Relay server protects your IP address, but it can observe the duration of the call." = "Az átjátszó kiszolgáló megvédi az IP-címét, de megfigyelheti a hívás időtartamát."; /* No comment provided by engineer. */ "Relay test failed!" = "Nem sikerült tesztelni az átjátszót!"; @@ -4861,9 +4852,6 @@ swipe action */ /* No comment provided by engineer. */ "Remove passphrase from keychain?" = "Eltávolítja a jelmondatot a kulcstartóból?"; -/* No comment provided by engineer. */ -"Remove subscriber" = "Feliratkozó eltávolítása"; - /* alert title */ "Remove subscriber?" = "Eltávolítja a feliratkozót?"; @@ -5030,7 +5018,7 @@ swipe action */ "Review members before admitting (\"knocking\")." = "Tagok áttekintése a befogadás előtt (kopogtatás)."; /* No comment provided by engineer. */ -"reviewed by admins" = "áttekintve a moderátorok által"; +"reviewed by admins" = "a moderátorok áttekintették"; /* No comment provided by engineer. */ "Revoke" = "Visszavonás"; @@ -5919,7 +5907,7 @@ report reason */ /* relay test failure server test failure */ -"Test failed at step %@." = "A teszt a(z) %@ lépésnél sikertelen volt."; +"Test failed at step %@." = "A teszt a(z) %@. lépésnél sikertelen volt."; /* No comment provided by engineer. */ "Test notifications" = "Értesítések tesztelése"; @@ -6327,13 +6315,13 @@ server test failure */ "Update settings?" = "Frissíti a beállításokat?"; /* rcv group event chat item */ -"updated channel profile" = "frissített csatornaprofil"; +"updated channel profile" = "frissítette a csatorna profilját"; /* No comment provided by engineer. */ "Updated conditions" = "Frissített feltételek"; /* rcv group event chat item */ -"updated group profile" = "frissítette a csoportprofilt"; +"updated group profile" = "frissítette a csoport profilját"; /* profile update event chat item */ "updated profile" = "frissítette a profilját"; @@ -6714,7 +6702,7 @@ server test failure */ "You are already connected to %@." = "Ön már kapcsolódott a következőhöz: %@."; /* No comment provided by engineer. */ -"You are already connected with %@." = "Ön már kapcsolódva van vele: %@."; +"You are already connected with %@." = "Ön már kapcsolatban van vele: %@."; /* new chat sheet message */ "You are already connecting to %@." = "A kapcsolódás már folyamatban van a következőhöz: %@."; diff --git a/apps/ios/it.lproj/Localizable.strings b/apps/ios/it.lproj/Localizable.strings index 96b117eeca..c882eb662c 100644 --- a/apps/ios/it.lproj/Localizable.strings +++ b/apps/ios/it.lproj/Localizable.strings @@ -505,9 +505,6 @@ swipe action */ /* No comment provided by engineer. */ "Added message servers" = "Server dei messaggi aggiunti"; -/* No comment provided by engineer. */ -"Adding relays will be supported later." = "L'aggiunta di relay verrà supportata prossimamente."; - /* No comment provided by engineer. */ "Additional accent" = "Principale aggiuntivo"; @@ -1182,9 +1179,6 @@ alert subtitle */ /* No comment provided by engineer. */ "Channel will be deleted for you - this cannot be undone!" = "Il canale verrà eliminato per te, non è reversibile!"; -/* alert message */ -"Channel will start working with %d of %d relays. Proceed?" = "Il canale sarà operativo con %1$d di %2$d relay. Procedere?"; - /* No comment provided by engineer. */ "Channels" = "Canali"; @@ -1634,7 +1628,7 @@ server test step */ /* blocking reason */ "Content violates conditions of use" = "Il contenuto viola le condizioni di utilizzo"; -/* No comment provided by engineer. */ +/* alert action */ "Continue" = "Continua"; /* No comment provided by engineer. */ @@ -4293,10 +4287,10 @@ alert button */ "Open migration to another device" = "Apri migrazione ad un altro dispositivo"; /* new chat action */ -"Open new channel" = "Apri un canale nuovo"; +"Open new channel" = "Apri il nuovo canale"; /* new chat action */ -"Open new chat" = "Apri una chat nuova"; +"Open new chat" = "Apri la nuova chat"; /* new chat action */ "Open new group" = "Apri il nuovo gruppo"; @@ -4577,9 +4571,6 @@ alert button */ /* alert title */ "Private routing timeout" = "Scadenza dell'instradamento privato"; -/* alert action */ -"Proceed" = "Procedi"; - /* No comment provided by engineer. */ "Profile and server connections" = "Profilo e connessioni al server"; @@ -4861,9 +4852,6 @@ swipe action */ /* No comment provided by engineer. */ "Remove passphrase from keychain?" = "Rimuovere la password dal portachiavi?"; -/* No comment provided by engineer. */ -"Remove subscriber" = "Rimuovi iscritto"; - /* alert title */ "Remove subscriber?" = "Rimuovere l'iscritto?"; diff --git a/apps/ios/ja.lproj/Localizable.strings b/apps/ios/ja.lproj/Localizable.strings index b14777244f..35d8732e3f 100644 --- a/apps/ios/ja.lproj/Localizable.strings +++ b/apps/ios/ja.lproj/Localizable.strings @@ -1005,7 +1005,7 @@ server test step */ /* No comment provided by engineer. */ "Contacts can mark messages for deletion; you will be able to view them." = "連絡先はメッセージを削除対象とすることができます。あなたには閲覧可能です。"; -/* No comment provided by engineer. */ +/* alert action */ "Continue" = "続ける"; /* No comment provided by engineer. */ diff --git a/apps/ios/nl.lproj/Localizable.strings b/apps/ios/nl.lproj/Localizable.strings index e12e255488..407665bbec 100644 --- a/apps/ios/nl.lproj/Localizable.strings +++ b/apps/ios/nl.lproj/Localizable.strings @@ -1382,7 +1382,7 @@ server test step */ /* blocking reason */ "Content violates conditions of use" = "Inhoud schendt de gebruiksvoorwaarden"; -/* No comment provided by engineer. */ +/* alert action */ "Continue" = "Doorgaan"; /* No comment provided by engineer. */ diff --git a/apps/ios/pl.lproj/Localizable.strings b/apps/ios/pl.lproj/Localizable.strings index e2e46590c9..8905300160 100644 --- a/apps/ios/pl.lproj/Localizable.strings +++ b/apps/ios/pl.lproj/Localizable.strings @@ -1439,7 +1439,7 @@ server test step */ /* blocking reason */ "Content violates conditions of use" = "Treść narusza warunki użytkowania"; -/* No comment provided by engineer. */ +/* alert action */ "Continue" = "Kontynuuj"; /* No comment provided by engineer. */ diff --git a/apps/ios/ru.lproj/Localizable.strings b/apps/ios/ru.lproj/Localizable.strings index c2b01228a2..9f79b5dea0 100644 --- a/apps/ios/ru.lproj/Localizable.strings +++ b/apps/ios/ru.lproj/Localizable.strings @@ -505,9 +505,6 @@ swipe action */ /* No comment provided by engineer. */ "Added message servers" = "Дополнительные серверы сообщений"; -/* No comment provided by engineer. */ -"Adding relays will be supported later." = "Добавление релеев будет поддерживаться позже."; - /* No comment provided by engineer. */ "Additional accent" = "Дополнительный акцент"; @@ -1182,9 +1179,6 @@ alert subtitle */ /* No comment provided by engineer. */ "Channel will be deleted for you - this cannot be undone!" = "Канал будет удалён для Вас - это нельзя отменить!"; -/* alert message */ -"Channel will start working with %d of %d relays. Proceed?" = "Канал начнёт работу с %1$d из %2$d релеев. Продолжить?"; - /* No comment provided by engineer. */ "Channels" = "Каналы"; @@ -1634,7 +1628,7 @@ server test step */ /* blocking reason */ "Content violates conditions of use" = "Содержание нарушает условия использования"; -/* No comment provided by engineer. */ +/* alert action */ "Continue" = "Продолжить"; /* No comment provided by engineer. */ @@ -4577,9 +4571,6 @@ alert button */ /* alert title */ "Private routing timeout" = "Таймаут конфиденциальной доставки"; -/* alert action */ -"Proceed" = "Продолжить"; - /* No comment provided by engineer. */ "Profile and server connections" = "Профиль и соединения на сервере"; @@ -4861,9 +4852,6 @@ swipe action */ /* No comment provided by engineer. */ "Remove passphrase from keychain?" = "Удалить пароль из Keychain?"; -/* No comment provided by engineer. */ -"Remove subscriber" = "Удалить подписчика"; - /* alert title */ "Remove subscriber?" = "Удалить подписчика?"; @@ -6327,7 +6315,7 @@ server test failure */ "Update settings?" = "Обновить настройки?"; /* rcv group event chat item */ -"updated channel profile" = "обновлён профиль канала"; +"updated channel profile" = "обновил профиль канала"; /* No comment provided by engineer. */ "Updated conditions" = "Обновлённые условия"; diff --git a/apps/ios/th.lproj/Localizable.strings b/apps/ios/th.lproj/Localizable.strings index 652737b4ca..cc3abea189 100644 --- a/apps/ios/th.lproj/Localizable.strings +++ b/apps/ios/th.lproj/Localizable.strings @@ -705,7 +705,7 @@ server test step */ /* No comment provided by engineer. */ "Contacts can mark messages for deletion; you will be able to view them." = "ผู้ติดต่อสามารถทําเครื่องหมายข้อความเพื่อลบได้ คุณจะสามารถดูได้"; -/* No comment provided by engineer. */ +/* alert action */ "Continue" = "ดำเนินการต่อ"; /* No comment provided by engineer. */ diff --git a/apps/ios/tr.lproj/Localizable.strings b/apps/ios/tr.lproj/Localizable.strings index fb3bf39168..e06989afee 100644 --- a/apps/ios/tr.lproj/Localizable.strings +++ b/apps/ios/tr.lproj/Localizable.strings @@ -169,6 +169,18 @@ /* time interval */ "%d months" = "%d ay"; +/* channel relay bar +channel subscriber relay bar */ +"%d relays failed" = "%d aktarıcı başarısız oldu"; + +/* channel relay bar +channel subscriber relay bar */ +"%d relays not active" = "%d aktarıcı etkin değil"; + +/* channel relay bar +channel subscriber relay bar */ +"%d relays removed" = "%d aktarıcı kaldırıldı"; + /* time interval */ "%d sec" = "%d saniye"; @@ -178,9 +190,32 @@ /* integrity error chat item */ "%d skipped message(s)" = "%d okunmamış mesaj(lar)"; +/* channel subscriber count */ +"%d subscriber" = "%d abone"; + +/* channel subscriber count */ +"%d subscribers" = "%d abone"; + /* time interval */ "%d weeks" = "%d hafta"; +/* channel creation progress +channel relay bar progress */ +"%d/%d relays active" = "%1$d/%2$d aktarıcı etkin"; + +/* channel relay bar */ +"%d/%d relays active, %d errors" = "%1$d/%2$d aktarıcı etkin, %3$d hata"; + +/* channel creation progress with errors +channel relay bar */ +"%d/%d relays active, %d failed" = "%1$d/%2$d aktarıcı etkin, %3$d başarısız"; + +/* channel relay bar */ +"%d/%d relays active, %d removed" = "%1$d/%2$d aktarıcı etkin, %3$d kaldırıldı"; + +/* channel subscriber relay bar progress */ +"%d/%d relays connected" = "%1$d/%2$d aktarıcı bağlı"; + /* No comment provided by engineer. */ "%lld" = "%lld"; @@ -1424,7 +1459,7 @@ server test step */ /* blocking reason */ "Content violates conditions of use" = "İçerik kullanım koşullarını ihlal ediyor"; -/* No comment provided by engineer. */ +/* alert action */ "Continue" = "Devam et"; /* No comment provided by engineer. */ diff --git a/apps/ios/uk.lproj/Localizable.strings b/apps/ios/uk.lproj/Localizable.strings index a0d9490b00..4a21eb4ae8 100644 --- a/apps/ios/uk.lproj/Localizable.strings +++ b/apps/ios/uk.lproj/Localizable.strings @@ -1409,7 +1409,7 @@ server test step */ /* blocking reason */ "Content violates conditions of use" = "Вміст порушує умови використання"; -/* No comment provided by engineer. */ +/* alert action */ "Continue" = "Продовжуйте"; /* No comment provided by engineer. */ diff --git a/apps/ios/zh-Hans.lproj/Localizable.strings b/apps/ios/zh-Hans.lproj/Localizable.strings index 3893351fdd..13be5125ea 100644 --- a/apps/ios/zh-Hans.lproj/Localizable.strings +++ b/apps/ios/zh-Hans.lproj/Localizable.strings @@ -1433,7 +1433,7 @@ server test step */ /* blocking reason */ "Content violates conditions of use" = "内容违反使用条款"; -/* No comment provided by engineer. */ +/* alert action */ "Continue" = "继续"; /* No comment provided by engineer. */ diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml index 95ec53287a..bd9c5d6881 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/ar/strings.xml @@ -60,7 +60,7 @@ السماح لجهات اتصالك بالاتصال بك. السماح بردود الفعل على الرسائل. يتم مسح جميع البيانات عند إدخالها. - سيتم استخدام Android Keystore لتخزين عبارة المرور بشكل آمن بعد إعادة تشغيل التطبيق أو تغيير عبارة المرور - سيسمح بتلقي الإشعارات. + سيتم استخدام Android Keystore لتخزين عبارة المرور بشكل آمن بعد إعادة تشغيل التطبيق أو تغيير عبارة المرور - سيسمح بإستلام الإشعارات. السماح لجهات اتصالك بإرسال رسائل تختفي. اسمح بالرسائل الصوتية فقط إذا سمحت جهة اتصالك بذلك. رمز مرور التطبيق @@ -76,7 +76,7 @@ عن عنوان SimpleX بناء التطبيق: %s المظهر - أضف عنوانًا إلى ملف تعريفك، حتى تتمكن جهات اتصالك من مشاركته مع أشخاص آخرين. سيتم إرسال تحديث ملف التعريف إلى جهات اتصالك. + أضف عنوانًا إلى ملف تعريفك، حتى تتمكن جهات اتصالك على SimpleX من مشاركته مع أشخاص آخرين. سيتم إرسال تحديث ملف التعريف إلى جهات اتصالك على SimpleX. ستبقى جميع جهات اتصالك متصلة. سيتم إرسال تحديث ملف التعريف إلى جهات اتصالك. رمز التطبيق عنوان @@ -525,7 +525,7 @@ فوري المضيف إخفاء - السماح بذلك في مربع الحوار التالي لتلقي الإشعارات على الفور.]]> + السماح بذلك في مربع الحوار التالي لاستلام الإشعارات على الفور.]]> ردًا على إشعارات فورية خوادم ICE (واحد لكل سطر) @@ -728,7 +728,7 @@ إيصالات تسليم الرسائل! دقائق شهور - - توصيل رسائل أكثر استقرارًا.\n- مجموعات أفضل قليلاً.\n- و اكثر! + - تسليم رسائل أكثر استقرارًا.\n- مجموعات أفضل قليلاً.\n- و اكثر! حالة الشبكة كتم ردود الفعل الرسائل ممنوعة. @@ -878,7 +878,7 @@ يرى المُستلمون التحديثات أثناء كتابتها. استلمت، ممنوع حفظ - سيتم إرسال تحديث ملف التعريف إلى جهات اتصالك. + سيتم إرسال تحديث ملف التعريف إلى جهات اتصالك على SimpleX. حفظ وإشعار جهات الاتصال حفظ وتحديث ملف تعريف المجموعة عدد البينج @@ -971,7 +971,7 @@ تدمير ذاتي مسح الرمز أرسل أسئلة وأفكار - مشاركة العنوان مع جهات الاتصال؟ + مشاركة العنوان مع جهات اتصال SimpleX؟ شارك العنوان حفظ رسالة الترحيب؟ احفظ الخوادم @@ -1060,7 +1060,7 @@ التوقف عن إرسال الملف؟ عنوان SimpleX استخدم مضيفي .onion إلى "لا" إذا كان وسيط SOCKS لا يدعمها.]]> - مشاركة مع جهات الاتصال + شارك مع جهات اتصال SimpleX إيقاف التشغيل؟ إعدادات وسيط SOCKS إيقاف التشغيل @@ -1306,7 +1306,7 @@ الإيصالات مُعطَّلة %s: %s تضم هذه المجموعة أكثر من %1$d عضو، ولا يتم إرسال إيصالات التسليم. - التوصيل + التسليم مُعطَّل تعطيل الإيصالات للمجموعات؟ فعّل (الاحتفاظ بتجاوزات المجموعة) @@ -1323,7 +1323,7 @@ افتح إعدادات التطبيق لا يمكن تشغيل SimpleX في الخلفية. ستستلم الإشعارات فقط عندما يكون التطبيق قيد التشغيل. سيتم مشاركة ملف تعريف عشوائي جديد. - ألصق الرابط المُستلَم للتواصل مع جهة اتصالك… + ألصِق الرابط المُستلَم للتواصل مع جهة اتصالك… ستتم مشاركة ملفك التعريفي %1$s. قد يغلق التطبيق بعد دقيقة واحدة في الخلفية. سماح @@ -1348,7 +1348,7 @@ افتح مجلد قاعدة البيانات سيتم تخزين عبارة المرور في الإعدادات كنص عادي بعد تغييرها أو إعادة تشغيل التطبيق. عبارة المرور مخزنة في الإعدادات كنص عادي. - يُرجى الملاحظة: يتم توصيل مُرحلات الرسائل والملفات عبر وسيط SOCKS. تستخدم المكالمات وإرسال معاينات الروابط الاتصال المباشر.]]> + يُرجى الملاحظة: يتم تسليم مُرحلات الرسائل والملفات عبر وسيط SOCKS. تستخدم المكالمات الاتصال المباشر.]]> عَمِّ الملفات المحلية عمِّ الملفات والوسائط المخزنة تطبيق سطح المكتب الجديد! @@ -1843,7 +1843,7 @@ العضو غير نشط رسالة مُحوّلة لا يوجد اتصال مباشر حتى الآن، الرسالة مُحوّلة بواسطة المُدير. - امسح / ألصِق الرابط + ألصِق رابط / امسح خوادم SMP المهيأة خوادم SMP أخرى خوادم XFTP المهيأة @@ -1936,7 +1936,7 @@ أعِد توصيل كافة الخوادم المتصلة لفرض تسليم الرسالة. يستخدم حركة مرور إضافية. خوادم موّكلة تلقي الأخطاء - تلقى الإجمالي + استلام الإجمالي أعِد توصيل جميع الخوادم أعِد توصيل الخوادم؟ عنوان الخادم @@ -2125,7 +2125,7 @@ %s.]]> شروط الاستخدام للتوجيه الخاص - لتلقي + لاستلام استخدم للملفات اعرض الشروط %s.]]> @@ -2159,12 +2159,12 @@ لا يمكن تحميل نص الشروط الحالية، يمكنك مراجعة الشروط عبر هذا الرابط: خطأ في قبول الشروط خطأ في حفظ الخوادم - على سبيل المثال، إذا تلقى أحد جهات اتصالك رسائل عبر خادم SimpleX Chat، فسيقوم تطبيقك بتسليمها عبر خادم Flux. + على سبيل المثال، إذا استلم أحد جهات اتصالك رسائل عبر خادم SimpleX Chat، فسيقوم تطبيقك بتسليمها عبر خادم Flux. لا يوجد خوادم لتوجيه الرسائل الخاصة. لا يوجد خوادم رسائل. - لا يوجد خوادم لاستقبال الملفات. + لا يوجد خوادم لاستلام الملفات. لا توجد رسالة - لا يوجد خوادم لاستقبال الرسائل. + لا يوجد خوادم لاستلام الرسائل. - فتح الدردشة عند أول رسالة غير مقروءة.\n- الانتقال إلى الرسائل المقتبسة. يمكنك تعيين اسم الاتصال، لتذكر الأشخاص الذين تمت مشاركة الرابط معهم. راجع لاحقًا @@ -2197,7 +2197,7 @@ احذف الدردشة الدردشة موجودة بالفعل! حذف الدردشة؟ - %1$s.]]> + %1$s.]]> أو استورد ملف الأرشيف لا توجد خدمة خلفية الإشعارات والبطارية @@ -2350,8 +2350,8 @@ لا يمكن قراءة عبارة المرور في Keystore. قد يكون هذا قد حدث بعد تحديث النظام غير متوافق مع التطبيق. إذا لم يكن الأمر كذلك، فيُرجى التواصل مع المطوِّرين. موافقة الانتظار سياسة الخصوصية وشروط الاستخدام. - لا يمكن الوصول إلى الدردشات الخاصة والمجموعات وجهات اتصالك لمشغلي الخادم. - باستخدام SimpleX Chat، توافق على:\n- إرسال المحتوى القانوني فقط في المجموعات العامة.\n- احترام المستخدمين الآخرين – لا سبام. + يلتزم المشغلون بما يلي:\n- الاستقلالية\n- تقليل استخدام البيانات الوصفية\n- تشغيل برمجيات مفتوحة المصدر وموثقة + أنت تلتزم بـ:\n- المحتوى القانوني فقط في المجموعات العامة\n- احترام المستخدمين الآخرين — لا للرسائل المزعجة اقبل يتطلب هذا الرابط إصدار تطبيق أحدث. يُرجى ترقية التطبيق أو اطلب من جهة اتصالك إرسال رابط متوافق. رابط كامل @@ -2564,7 +2564,7 @@ حُدِّث ملف تعريف القناة ستُحذف القناة لجميع المشتركين - لا يمكن التراجع عن هذا الإجراء! ستُحذف القناة من عِندك - لا يمكن التراجع عن هذا الإجراء! - ستبدأ القناة بالعمل مع %1$d من أصل %2$d من المُرحلات. أتود المتابعة؟ + ستبدأ القناة بالعمل مع %1$d من أصل %2$d مُرحلات. أتود الاستمرار؟ مُرحل الدردشة مُرحلات الدردشة مُرحلات الدردشة توجّه الرسائل في القنوات التي تنشئها. @@ -2654,4 +2654,160 @@ عنوان مُرحلك اسم مُرحلك ستتوقف عن تلقي الرسائل من هذه القناة، وسيتم الاحتفاظ بسجل الدردشة. + %1$d/%2$d مِن المُرحلات نشطة، و%3$d أخطاء + %1$d/%2$d مِن المُرحلات نشطة، وأُزيل %3$d + %1$d/%2$d مِن المُرحلات متصلة، وفشل %3$d + %1$d/%2$d مِن المُرحلات متصلة، وأُزيل %3$d + فشل %1$d مُرحلات + %1$d مُرحلات غير نشطة + %1$d مُرحلات أُزيلت + أضف مُرحلات لاستعادة تسليم الرسائل. + رابط اتصال لشخص واحد + اسمح للأعضاء بالدردشة مع المُدراء. + اسمح بإرسال رسائل مباشرة للمشتركين. + اسمح للمشتركين بالدردشة مع المُدراء. + فشلت كل المُرحلات + أُزيلت كل المُرحلات + لأننا دمرنا القدرة على معرفة من أنت. لكي لا تُسلب قوتك أبدًا. + كن حُرًا\nفي شبكتك + كن حُرًا في شبكتك. + الشريط السفلي + عنوان العمل التجاري + تعذّر الإذاعة + القناة لا تحتوي على محطات تقوية نشطة. يُرجى محاولة الانضمام لاحقًا. + رابط القناة + تفضلات القناة + القنوات + القناة غير متوفرة مؤقتًا + الدردشة مع المُدراء محظورة. + الدردشة مع المُدراء في القنوات العامة لا تتوفر فيها ميزة التعمية بين الطرفين (E2E) - لا تستخدمها إلا مع مُرحلات دردشة موثوقة. + عُطّل الدردشة مع الأعضاء + الدردشة مع المُدراء + اتصل عبر رابط أو رمز QR + عنوان التواصل + أنشئ رابطك + أنشئ رابطك العام + تُمنع الرسائل المباشرة بين المشتركين. + عطّل + لا ترسل السجل للمشتركين الجدد. + أصبح أسهل أن تدعي أصحابك 👋 + فعّل + فعّل + فعّل الدردشة مع المُدراء؟ + فعّل معاينة الروابط؟ + أدخل اسم ملف التعريف… + خطأ + خطأ في مشاركة القناة + لكي يتمكن أي شخص من الوصول إليك + (مِن المالك) + ابدأ + رابط المجموعة + لا يُرسل السجل للمشتركين الجدد. + غير نشط + ادعُ شخصٍ ما بشكل خاص + اجعل شخصًا ما يتواصل معك + سيتم طلب معاينة الرابط عبر وسيط SOCKS. قد لا يزال البحث عن نظام أسماء النطاقات (DNS) يتم محليًا من خلال محلّل DNS الخاص بك. + تحققَ من توقيع الرابط. + يمكن للأعضاء الدردشة مع المُدراء. + ليست مُعمّاة تمامًا بين الطرفين. يمكن لمُرحلات الدردشة رؤية هذه الرسائل.]]> + رحّل + التزامات الشبكة + خطأ في الشبكة + لا يمكن لموجّهات الشبكة معرفة مَن يتحدث مع مَن + رابط جديد لمرة واحدة + لا حساب. لا هاتف. لا بريد إلكتروني. لا هوية. التعمية الأكثر أمانًا. + لا مُرحلات نشطة + لم يتتبع أحد محادثاتك. ولم يرسم أحد خريطة للأماكن التي زرتها. لم تكن الخصوصية مجرد ميزة، بل كانت أسلوب حياة. + تنظيم غير ربحي + ليس مجرد قفل أفضل على باب شخص آخر. ولا صاحب عقار ألطف يحترم خصوصيتك، لكنه لا يزال يحتفظ بسجل لجميع الزوار. أنت لست ضيفًا. أنت في بيتك. لا يمكن لأي ملك أن يدخله — فأنت صاحب السيادة. + رابط لمرة واحدة + يمكن لمالكي القنوات فقط تغيير تفضيلات القناة. + على جوّالك، وليس على الخوادم. + افتح رابط خارجي؟ + - الموافقة على إرسال معاينات الروابط.\n- استخدام وسيط SOCKS في حال تفعيله.\n- منع التصيد الاحتيالي عبر الروابط التشعبية.\n- إزالة تتبُع الروابط. + أو أظهر رمز QR شخصيًا أو عبر مكالمة فيديو. + أو استخدم رمز QR هذا - اطبعه أو اعرضه عبر الإنترنت. + الملكية: يمكنك تشغيل المُرحلات الخاصة بك. + الخصوصية: للمالكين والمشتركين. + مُراسلة خاصة وآمنة. + منع الدردشات مع المُدراء. + منع إرسال رسائل مباشرة للمشتركين. + قنوات عامة - تحدث بحرية 🚀 + نتائج المُرحل: + الموثوقية: عِدّة مُرحلات لكل قناة. + أُزيل بواسطة المُشغل + روابط ويب آمنة + الأمن: المالكون يمتلكون مفاتيح القنوات. + قد يؤدي إرسال معاينة للرابط إلى كشف عنوان IP الخاص بك للموقع الإلكتروني. يمكنك تغيير هذا الإعداد لاحقًا من إعدادات الخصوصية. + أرسل الرابط عبر أي تطبيق مُراسلة - فهو آمن. واطلب منه لصقه في SimpleX. + أرسل ما يصل إلى آخر 100 رسالة للمشتركين الجدد. + أعِدّ الإشعارات + أعِدّ أجهزة التوجيه + شارك القناة… + شارك عبر الدردشة + ⚠️ فشل التحقق من التوقيع: %s. + (موقّع) + بلاغات المشترك + يمكن للمشتركين إضافة ردود الفعل على الرسائل. + يمكن للمشتركين الدردشة مع المُدراء. + يمكن للمشتركين حذف الرسائل المُرسلة نهائيًا. (24 ساعة) + يمكن للمشتركين الإبلاغ عن الرسائل للمشرفين. + يمكن للمشتركين إرسال رسائل مباشرة. + يمكن للمشتركين إرسال رسائل تختفي. + يمكن للمشتركين إرسال الملفات والوسائط. + يمكن للمشتركين إرسال روابط SimpleX. + يمكن للمشتركين إرسال رسائل صوتية. + تحدث مع شخصٍ ما + انقر للفتح + وصل الاتصال إلى الحد الأقصى للرسائل غير المُسلَّمة + أول شبكة تمتلك\nفيها جهات اتصالك ومجموعاتك. + ’ثم انتقلنا إلى الإنترنت، وطلبت كل منصة جزءًا منك؛ اسمك، ورقمك، وأصدقاءك. وقبلنا بأن ثمن التحدث مع الآخرين هو السماح لشخص ما بمعرفة مَن نتحدث إليهم. وكل جيل، سواء من الناس أو التقنية، عاش الأمر على هذا النحو؛ الهاتف، والبريد الإلكتروني، وتطبيقات المراسلة، ووسائل التواصل الاجتماعي. وبدا أن هذه هي الطريقة الوحيدة الممكنة. + أقدم حرية إنسانية — وهي التحدث إلى شخص آخر دون مراقبة — مبنية على بنية تحتية لا يمكنها خيانتها. + هناك طريقة أخرى. شبكة بلا أرقام هواتف، ولا أسماء مستخدمين، ولا حسابات، ولا هويات مستخدمين من أي نوع. شبكة تربط الناس وتنقل الرسائل المُعمّاة دون معرفة مَن المتصل. + لضمان استمرارية شبكة SimpleX. + الشريط العلوي + يُرسل ما يصل إلى آخر 100 رسالة للمشتركين الجدد. + استخدم هذا العنوان في ملف تعريفك على مواقع التواصل الاجتماعي أو موقعك الإلكتروني أو في توقيع بريدك الإلكتروني. + في انتظار قيام مالك القناة بإضافة المُرحلات. + جعلنا عملية الاتصال أكثر بساطة للمستخدمين الجدد. + لماذا بُنيا SimpleX. + محادثاتك ملك لك، تمامًا كما كان الحال دائمًا قبل ظهور الإنترنت. الشبكة ليست مكانًا تزوره، بل هي مكان تصنعه وتمتلكه؛ ولا يمكن لأحد أن يسلبه منك، سواء جعلته خاصًا أو عامًا. + شبكتك + ملف تعريفك + عنوانك العام + لقد وُلدت دون حساب تعريفي. + قناتك الجديدة %1$s متصلة بـ %2$d من أصل %3$d مُرحلات. إذا ألغيت، ستُحذف القناة - يمكنك إنشاؤها مرة أخرى. + أضف + أضف مُرحل + أضف مُرحلات + ألغِ واحذف القناة + %d مُرحل/ات مُحدّدة + خطأ في إضافة المُرحلات + لا مُرحلات متاحة + لا مُرحلات + لا مُرحلات مُحدّدة + المُرحلات المُضافة: %1$s. + سيُزيل المُرحل من القناة - لا يمكن التراجع عن هذا الإجراء! + أُزيل + أزِل المُرحل + إزالة المُرحل؟ + حدّد المُرحلات + هذا هو آخر مُرحل نشط. إزالته ستمنع تسليم الرسائل للمشتركين. + أغلِق التطبيق + خطأ في حذف الرسالة + من السجل + إذا اخترت أغلِق، فلن تُستلم الرسائل.\nيمكنك تغيير ذلك لاحقًا من إعدادات المظهر. + أبقِ SimpleX يعمل في الخلفية لاستلام الرسائل. + صغّر إلى اللوحة + تصغير إلى اللوحة؟ + صغّر إلى اللوحة عند إغلاق النافذة + أنهِ SimpleX + أظهر SimpleX + SimpleX + SimpleX — %d غير مقروءة + قد تكون هناك نُسخة أخرى من التطبيق قيد التشغيل أو لم تُغلق بشكل صحيح. أتريد البدء على أي حال؟ + التطبيق يعمل بالفعل + رُفض + رُفض بواسطة مُشغل المُرحل + الحالة diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/cs/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/cs/strings.xml index df4907885c..c727cc1cb0 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/cs/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/cs/strings.xml @@ -1537,7 +1537,7 @@ Chyba vytváření zprávy Chyba odstranění soukromých poznámek Smazat soukromé poznámky? - Všechny zprávy budou smazány - nemůže být zvráceno! + Všechny zprávy budou smazány - nelze zvrátit! Možnosti vývojáře blokováno %s kontakt %1$s změnen na %2$s @@ -1556,8 +1556,7 @@ PC má nepodporovanou verzi. Ujistěte se, že používáte stejnou verzi na obou zařízeních PC má chybný kód pozvánky Neplatné jméno! - Databáze migrace běží. -\nMůže to trvat několik minut. + Probíhá migrace databáze. \nMůže to trvat několik minut. člen %1$s změněn na %2$s blokováno Blokováno adminem @@ -1589,7 +1588,7 @@ %s byl odpojen]]> Chyba otevření prohlížeče odstraněn profilový obrázek - nastavit novou kontaktní adresu + nastavil novou kontaktní adresu nastavil nový profilový obrázek Uložené zprávy Pomalá funkce @@ -2247,9 +2246,9 @@ Nahráno Ano Tuto akci nelze zrušit - zprávy odeslané a přijaté v tomto chatu dříve než vybraná, budou smazány. - Statistiky serverů budou obnoveny - nemůže být vráceno! + Statistiky serverů budou obnoveny - nelze vrátit! Odešlete soukromý report - Pomozte administrátorům moderovat své skupiny. + Pomozte správcům moderovat jejich skupiny. Rychlejší mazání skupin. Od %s. Můžete zmínit až %1$s členů ve zprávě! @@ -2532,7 +2531,7 @@ Filtr Obrázky Odkazy - Zprávy člena budou smazány - nemůže být zrušeno! + Zprávy člena budou smazány - nelze zrušit! bez předplatného Odebrat a smazat zprávy Hledat soubory @@ -2556,4 +2555,219 @@ Nejstarší lidská svoboda - mluvit s druhým člověkem, aniž by byl sledován - postavena na infrastruktuře, která ji nemůže zradit. Protože jsme zničili sílu vědět, kdo jste. Aby vám vaši moc nikdo nemohl vzít. Buďte svobodní ve své síti. + %1$d/%2$d relé aktivních + %1$d/%2$d relé aktivních, %3$d chyb + %1$d/%2$d relé aktivních, %3$d selhalo + %1$d/%2$d relé aktivních, %3$d odstraněno + %1$d/%2$d relé připojeno + %1$d/%2$d relé připojeno, %3$d chyb + %1$d/%2$d relé připojeno, %3$d selhalo + %1$d/%2$d relé připojeno, %3$d odstraněno + %1$d relé selhalo + %1$d relé neaktivních + %1$d relé odstraněno + %1$d odběratel + %1$d odběratelů + přijaté + Přidávání relé bude podporováno později. + Odkaz pro připojení jedné osoby + Povolit členům chat se správci. + Kanál + Kanál + Celý název kanálu: + Kanál nemá aktivní relé.\nProsím, zkuste se připojit později. + Odkaz kanálu + Odkaz kanálu + Členové kanálu + Název kanálu + Vlastnosti kanálu + kanál + Profil Kanálu je uložen na zařízeních odběratelů a na chat relé. + profil kanálu aktualizován + Kanály + Kanál dočasně nedostupný + Kanál bude smazán pro všechny odběratele - nelze zrušit! + Kanál bude pro vás odstraněn - nelze zvrátit! + Kanál začne pracovat s %1$d z %2$d relé. Pokračovat? + Chat relé + Chat relé + Chat relé + Chat relé + Chat relé předávají zprávy v kanálech, které vytvoříte. + Chat relé předávají zprávy na kanál odběratelů. + Chat se správci zakázán. + Chat se správci ve veřejných kanálech nemají šifrování E2E - používejte pouze s důvěryhodnými chat relé. + Chat se členy je zakázán + Chat se správci + Zkontrolujte relé adresu a zkuste to znovu. + Zkontrolujte jméno relé a zkuste to znovu. + Nastavit relé + Připojit + připojeno + připojuji + Připojení přes odkaz nebo QR kód + Vytvořit veřejný kanál + Vytvořit veřejný kanál + Vytvořit veřejný kanál (BETA) + Vytvořte odkaz + Vytvořte si veřejnou adresu + Vytvářím kanál + %d událostí kanálu + Dekódovací odkaz + Smazat kanál + Smazat kanál? + smazán + smazaný kanál + Smazat relé + Přímé zprávy mezi odběrateli jsou zakázány. + Zakázat + Neposílat historii novým odběratelům. + Snadněji pozvěte své přátele 👋 + Upravit profil kanálu + Povolit + Povolit + Povolte alespoň jedno chat relé pro vytvoření kanálu. + Povolit chat se správci? + Povolit náhledy odkazů? + Zadejte název profilu… + Zadejte jméno relé… + Chyba + Chyba přidávání relé + Chyba vytváření kanálu + Chyba otevírání kanálu + chyba: %s + Chyba ukládání profilu kanálu + Chyba sdílení kanálu + selhalo + selhalo + Pro spojení s kýmkoli + (od majitele) + Získat odkaz + Začít + Odkaz skupiny + Historie není odesílána novým odběratelům. + neaktivní + Špatná relé adresa! + Neplatné jméno relé! + pozván + Pozvat soukromě + Připojit ke kanálu + Opustit kanál + Opustit kanál? + Přidat + Přidat relé + Přidat relé + Povolit odběratelům odesílání přímých zpráv. + Povolit odběratelům chat se správci. + Všechna relé selhala + Všechny relé odebrány + Buďte volní\nve vaší síti + Blokovat uživatele všem? + Spodní lišta + Vysílání + Test relé pro načtení jeho jména.]]> + Obchodní adresa + Zrušit a odstranit kanál + Zrušit vytvoření kanálu? + nemůže vysílat + Zavřít aplikaci + Adresa kontaktu + %d relé vybráno + zahozeno (%1$d pokusů) + Chyba přidávání relé + Chyba odstranění zprávy + Z historie + Pokud vyberete Zavřít, zprávy nebudou přijímány.\nMůžete to změnit později v nastavení Vzhledu. + Nechat SimpleX běžet na pozadí pro přijímání zpráv. + Umožněte ostatním se s vámi spojit + Odkaz + Náhled odkazu bude vyžádán prostřednictvím SOCKS proxy. DNS vyhledávání se stále může uskutečnit lokálně pomocí vašeho DNS resolveru. + Podpis odkazu ověřen. + Členové mohou chatovat se správci. + Chyba zprávy + end-to-end nešifrované. Chat relé může vidět tyto zprávy.]]> + Přenést + Skrýt do oznamovací oblasti + Skrýt do oznamovací oblasti? + Skrýt do oznamovací oblasti při zavření okna + Chyba sítě + nové + Nový jednorázový odkaz + Nové chat relé + Žádné aktivní relé + Žádné dostupné relé + Žádné chat relé + Není povoleno žádné relé. + Žádné relé + Relé nevybráno + Ne všechny relé připojeny + Jednorázový odkaz + Pouze majitelé kanálu mohou měnit nastavení. + Na vašem telefonu, ne na serverech. + Otevřít kanál + Otevřít externí odkaz? + Otevřít nový kanál + Nebo ukažte QR osobně nebo prostřednictvím videohovoru. + Nebo použijte tento QR kód - tisk nebo zobrazit online. + MAJITEL + Majitelé + Vlastnictví: můžete provozovat vlastní relé. + Soukromí: pro majitele a odběratele. + Soukromé a bezpečné zasílání zpráv. + Zakázat chat se správci. + Zakázat odesílání přímých zpráv odběratelům. + Veřejné kanály - mluvte volně 🚀 + Opustit SimpleX + relé + RELÉ + Relé adresa + Relé adresa + Připojení relé selhalo + Relé odkaz + Relé výsledky: + Relé přidáno: %1$s. + Relé test selhal! + odstraněno + odstraněno operátorem + Odstranit relé + Odstranit relé? + Odstranit odběratele + Odstranit odběratele? + Bezpečné odkazy + Uložit a informovat odběratele kanálu + Uložit profil kanálu + Vybrat relé + Varování serveru + Nastavit oznamování + Nastavit routery + Sdílet kanál… + Sdílet adresu relé + Sdílet pomocí chatu + Zobrazit SimpleX + ⚠️ Ověření podpisu selhalo: %s. + (podepsán) + SimpleX + SimpleX - %d nepřečteno + ODBĚRATEL + Hlášení odběratelů + Odběratelé + Odběratelé mohou přidávat reakce na zprávy. + Odběratelé mohou psát správcům. + Odběratelé mohou nevratně mazat odeslané zprávy. (24 hodin) + Odběratelé mohou nahlásit zprávy moderátorům. + Odběratelé mohou posílat přímé zprávy. + Odběratelé mohou odesílat mizející zprávy. + Odběratelé mohou posílat soubory a média. + Odběratelé mohou odesílat SimpleX odkazy. + Odběratelé mohou posílat hlasové zprávy. + Odběratelé použijí relé odkaz pro připojení ke kanálu.\nRelé adresa byla použita pro nastavení tohoto kanálu. + Odběratelé budou odebráni z kanálu - Nelze zvrátit! + Promluvte si s někým + Klepněte na Přidat kanál + Klepněte pro otevření + Test selhal v kroku %s. + Test relé + Aplikace odstranila tuto zprávu po %1$d pokusech o přijetí. + Připojení dosáhlo limitu nedoručených zpráv + První síť, kde vy vlastníte\nvaše kontakty a skupiny. diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml index 8700ade74e..42049da403 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml @@ -404,7 +404,7 @@ Onion-Hosts werden nicht verwendet. Für die Verbindung werden Onion-Hosts benötigt. \nBitte beachten Sie: Ohne .onion-Adresse können Sie keine Verbindung mit den Servern herstellen. - Erscheinungsbild + Darstellung Adresse erstellen Adresse löschen? @@ -495,14 +495,14 @@ Audio- & Videoanrufe Ihre Anrufe - Immer über ein Relais verbinden + Immer über einen Router verbinden Anrufe auf Sperrbildschirm: Akzeptieren Anzeigen Deaktivieren Ihre ICE-Server WebRTC-ICE-Server - Relais-Server schützen Ihre IP-Adresse, aber sie können die Anrufdauer erfassen. + Relais-Server schützen Ihre IP-Adresse, können aber die Anrufdauer erfassen. Relais-Server werden nur genutzt, wenn sie benötigt werden. Ihre IP-Adresse kann von Anderen erfasst werden. Öffnen Sie SimpleX Chat, um den Anruf anzunehmen. @@ -1437,7 +1437,7 @@ Datenbank-Ordner öffnen Das Passwort wird in Klartext in den Einstellungen gespeichert, nachdem Sie es geändert oder die App neu gestartet haben. Das Passwort wurde in Klartext in den Einstellungen gespeichert. - Bitte beachten Sie: Die Nachrichten- und Datei-Relais sind per SOCKS-Proxy verbunden. Anrufe nutzen eine direkte Verbindung.]]> + Bitte beachten Sie: Die Nachrichten- und Datei-Router sind per SOCKS-Proxy verbunden. Anrufe nutzen eine direkte Verbindung.]]> Lokale Dateien verschlüsseln Öffnen Gespeicherte Dateien & Medien verschlüsseln @@ -1702,7 +1702,7 @@ Link-Details werden heruntergeladen Archiv wird heruntergeladen Anwenden - Alle Ihre Kontakte, Unterhaltungen und Dateien werden sicher verschlüsselt und in Daten-Paketen auf die konfigurierten XTFP-Relais hochgeladen. + Alle Ihre Kontakte, Unterhaltungen und Dateien werden sicher verschlüsselt und in Daten-Paketen auf die konfigurierten XTFP-Router hochgeladen. Archivieren und Hochladen Warnung: Das Archiv wird gelöscht.]]> Überprüfen Sie Ihre Internetverbindung und probieren Sie es nochmals @@ -1840,8 +1840,8 @@ Nie Unbekannte Server Ungeschützt - Sie nutzen privates Routing mit unbekannten Servern. - Sie nutzen KEIN privates Routing. + Bei unbekannten Servern privates Routing nutzen. + KEIN privates Routing nutzen. Modus für das Nachrichten-Routing Ja Nein @@ -1849,20 +1849,19 @@ Fallback für das Nachrichten-Routing Nachrichtenstatus anzeigen Herabstufung erlauben - Sie nutzen immer privates Routing. + Immer privates Routing nutzen. Nachrichten werden nicht direkt versendet, selbst wenn Ihr oder der Ziel-Server kein privates Routing unterstützt. PRIVATES NACHRICHTEN-ROUTING Nachrichten werden direkt versendet, wenn die IP-Adresse geschützt ist, und Ihr oder der Ziel-Server kein privates Routing unterstützt. Nachrichten werden direkt versendet, wenn Ihr oder der Ziel-Server kein privates Routing unterstützt. - Zum Schutz Ihrer IP-Adresse, wird für die Nachrichten-Auslieferung privates Routing über Ihre konfigurierten SMP-Server genutzt. - Sie nutzen privates Routing mit unbekannten Servern, wenn Ihre IP-Adresse nicht geschützt ist. + Zum Schutz Ihrer IP-Adresse, wird für die Nachrichten-Auslieferung privates Routing über Ihre konfigurierten SMP-Router genutzt. + Bei unbekannten Servern privates Routing nutzen, wenn Ihre IP-Adresse nicht geschützt ist. IP-Adresse schützen DATEIEN Die App wird bei unbekannten Datei-Servern nach einer Download-Bestätigung fragen (außer bei .onion oder wenn ein SOCKS-Proxy aktiviert ist). Unbekannte Server! Ohne Tor- oder VPN-Nutzung wird Ihre IP-Adresse für Datei-Server sichtbar sein. - Ohne Tor- oder VPN-Nutzung wird Ihre IP-Adresse für diese XFTP-Relais sichtbar sein: -\n%1$s. + Ohne Tor- oder VPN-Nutzung wird Ihre IP-Adresse für diese XFTP-Router sichtbar sein: \n%1$s. Profil-Design Schwarz Farbvariante @@ -1903,8 +1902,7 @@ Gestalten Sie Ihre Chats unterschiedlich! Neue Chat-Designs Privates Nachrichten-Routing 🚀 - Schützen Sie Ihre IP-Adresse vor den Nachrichten-Relais , die Ihr Kontakt ausgewählt hat. -\nAktivieren Sie es in den *Netzwerk & Server* Einstellungen. + Schützen Sie Ihre IP-Adresse vor den Nachrichten-Routern, die Ihre Kontakte ausgewählt haben.\nAktivieren Sie es in den *Netzwerk & Server* Einstellungen. Dateien sicher herunterladen Mit reduziertem Akkuverbrauch. Keine Information @@ -2107,7 +2105,7 @@ Erstellen Laden Sie neue Versionen von GitHub herunter. Direkt aus der Chat-Liste abspielen. - Kann von Ihnen in den Erscheinungsbild-Einstellungen geändert werden. + Sie können dies in den Einstellungen unter „Darstellung“ ändern. Kontakte für spätere Chats archivieren. Ihre IP-Adresse und Verbindungen werden geschützt. Löschen Sie bis zu 20 Nachrichten auf einmal. @@ -2135,15 +2133,15 @@ Chat-Profil auswählen Archiv entfernen? Ihre Anmeldeinformationen können unverschlüsselt versendet werden. - Verwenden Sie keine Anmeldeinformationen mit einem Proxy. + Keine Anmeldeinformationen mit einem Proxy verwenden. Ihre Verbindung wurde auf %s verschoben, aber während des Profil-Wechsels trat ein Fehler auf. Stellen Sie sicher, dass die Proxy-Konfiguration richtig ist. Fehler beim Speichern des Proxys Passwort Proxy-Authentifizierung - Verwenden Sie für jede Verbindung unterschiedliche Proxy-Anmeldeinformationen. - Verwenden Sie für jedes Profil unterschiedliche Proxy-Anmeldeinformationen. - Verwenden Sie zufällige Anmeldeinformationen + Für jede Verbindung unterschiedliche Proxy-Anmeldeinformationen verwenden. + Für jedes Profil unterschiedliche Proxy-Anmeldeinformationen verwenden. + Zufällige Anmeldeinformationen verwenden Benutzername %1$d Datei-Fehler:\n%2$s %1$d Datei(en) wird/werden immer noch heruntergeladen. @@ -2179,7 +2177,7 @@ Die SimpleX-Protokolle wurden von Trail of Bits überprüft. Während des Anrufs zwischen Audio und Video wechseln Das Chat-Profil für Einmal-Einladungen wechseln - Jedes Mal wenn Sie die App starten, werden neue SOCKS-Anmeldeinformationen genutzt + Bei jedem Neustart der App, werden neue SOCKS-Anmeldeinformationen genutzt Verbesserte Sicherheit ✅ Verbesserte Nachrichten-Datumsinformation Verbesserte Nutzer-Erfahrung @@ -2215,7 +2213,7 @@ Aktualisieren Voreingestellte Server Nutzungsbedingungen einsehen - Die Nutzungsbedingungen der aktivierten Betreiber werden automatisch akzeptiert am: %s. + Die Nutzungsbedingungen der aktivierten Betreiber werden am %s automatisch akzeptiert. Ihre Server Betreiber %s Server @@ -2258,7 +2256,7 @@ nur mit einem Kontakt genutzt werden - teilen Sie ihn nur persönlich oder über einen beliebigen Messenger.]]> %s.]]> %s.]]> - Die Nutzungsbedingungen wurden akzeptiert am: %s + Die Nutzungsbedingungen wurden am %s akzeptiert. %s.]]> %s zu nutzen, müssen Sie dessen Nutzungsbedingungen akzeptieren.]]> Fehler beim Akzeptieren der Nutzungsbedingungen @@ -2274,7 +2272,7 @@ Zum Schutz vor dem Austausch Ihres Links können Sie die Sicherheitscodes Ihrer Kontakte vergleichen. %s.]]> %s.]]> - Die Nutzungsbedingungen wurden akzeptiert am: %s. + Die Nutzungsbedingungen wurden am %s akzeptiert. Der Text der aktuellen Nutzungsbedingungen konnte nicht geladen werden. Sie können die Nutzungsbedingungen unter diesem Link einsehen: Ferngesteuerte Mobiltelefone Oder importieren Sie eine Archiv-Datei @@ -2380,7 +2378,7 @@ %d Meldungen Mitglieder-Meldungen Meldungen - Inhalt verletzt Nutzungsbedingungen + Inhalt verletzt die Nutzungsbedingungen Spam Verbindung blockiert Die Datei wird vom Serverbetreiber blockiert:\n%1$s. @@ -2708,7 +2706,7 @@ Relais-Test fehlgeschlagen! Abonnent entfernen Abonnent entfernen? - Der Server erfordert eine Autorisierung, um eine Verbindung zum Relais herzustellen. Bitte Passwort überprüfen. + Der Server erfordert eine Autorisierung, um eine Verbindung zum Router herzustellen. Bitte Passwort überprüfen. Serverwarnung Relais-Adresse teilen ABONNENT @@ -2757,7 +2755,7 @@ %1$d Relais fehlgeschlagen %1$d Relais nicht aktiv %1$d Relais entfernt - Das Hinzufügen von Relais wird zu einem späteren Zeitpunkt unterstützt. + Relais hinzufügen, um die Nachrichtenübermittlung wiederherzustellen. Alle Relais fehlgeschlagen Alle Relais entfernt Broadcast nicht möglich @@ -2805,11 +2803,11 @@ Oder den QR‑Code persönlich oder per Videoanruf zeigen. Oder diesen QR‑Code verwenden – ausgedruckt oder online. Volle Kontrolle: Sie können Ihre eigenen Relais betreiben. - Privatsphäre: für Besitzer und Abonnenten. + Privatsphäre: Für Eigentümer und Abonnenten. Öffentliche Kanäle – frei sprechen 🚀 - Zuverlässigkeit: mehrere Relais pro Kanal. + Zuverlässigkeit: Mehrere Relais pro Kanal. Sichere Web-Links - Sicherheit: Eigentümer besitzen die Kanalschlüssel. + Sicherheit: Nur die Eigentümer des Kanals besitzen die Schlüssel. Den Link über einen beliebigen Messenger versenden – es ist sicher. Bitte in SimpleX einfügen. Mit Jemandem sprechen Für ein dauerhaftes SimpleX-Netzwerk. @@ -2873,4 +2871,38 @@ Untere Leiste Die Linkvorschau wird über einen SOCKS-Proxy angefordert. DNS-Abfragen können dennoch lokal über Ihren DNS-Resolver erfolgen. Obere Leiste + Ihr neuer Kanal %1$s ist mit %2$d von %3$d Relais verbunden.\nBei Abbruch, wird der Kanal gelöscht. Sie können ihn später neu erstellen. + Hinzufügen + Relais hinzufügen + Relais hinzufügen + Kanal abbrechen und löschen + %d Relais ausgewählt + Fehler beim Hinzufügen von Relais + Keine verfügbaren Relais + Keine Relais + Keine Relais ausgewählt + %1$s Relais hinzugefügt. + Relais wird aus dem Kanal entfernt. Dies kann nicht rückgängig gemacht werden! + Entfernt + Relais entfernen + Relais entfernen? + Relais auswählen + Dies ist das letzte aktive Relais. Wenn Sie es entfernen, können keine Nachrichten mehr an Abonnenten zugestellt werden. + App schließen + Wenn Sie \"Schließen\" auswählen, werden keine Nachrichten mehr empfangen.\nSie können dies später in den Einstellungen unter \"Darstellung\" ändern. + SimpleX im Hintergrund weiter ausführen, um Nachrichten zu empfangen. + In den Infobereich minimieren + In den Infobereich minimieren? + Beim Schließen des Fensters in den Infobereich minimieren + SimpleX beenden + SimpleX anzeigen + SimpleX + SimpleX — %d ungelesen + Fehler beim Löschen der Nachricht + Aus dem Nachrichtenverlauf + App ist bereits gestartet + App ist eventuell schon gestartet oder wurde nicht richtig beendet. Trotzdem starten? + Abgelehnt + Status + Vom Relais-Betreiber abgelehnt diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml index 7088c54d9b..8be594f9e2 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml @@ -2708,7 +2708,7 @@ %1$d servidores han fallado %1$d servidores inactivos %1$d servidores eliminados - Añadir servidores estará disponible en una versión posterior. + Añadir servidores pare retomar el envío. Enlace para un solo contacto Permitir que los miembros chateen con administradores. Permitir que los suscriptores chateen con administradores. @@ -2798,4 +2798,33 @@ Menú inferior Las previsualizaciones de enlaces se solicitan a través del proxy SOCKS. Las peticiónes DNS aún pueden usar el DNS local del sistema. Menú superior + Tu nuevo canal %1$s está conectado a %2$d de %3$d servidores.\nSi cancelas, el canal será eliminado. Puedes crearlo de nuevo. + Añadir + Añadir servidor + Añadir servidores + Cancelar y eliminar el canal + Cerrar aplicación + %d servidor(es) seleccionados + Error al añaidr servidores + Error al eliminar mensaje + Del historial + Si eliges Cerrar, los mensajes no serán recibidos.\nPuedes cambiarlo más tarde desde el menú Apariencia. + Mantener Simplex en segundo plano para recibir mensajes. + Minimizar + Minimizar? + Minimizar al cerrar la ventana + Sin servidores disponibles + Sin servidores + Sin servidores seleccionados + Salir de SimpleX + Servidores añadidos %1$s. + El servidor será eliminado del canal. ¡No puede deshacerse! + eliminado + Eliminar servidor + ¿Eliminar el servidor? + Seleccionar servidores + Ver SimpleX + SimpleX + SimpleX — %d no leído + Este es el último servidor activo. Si lo eliminas los mensajes no llegarán a los suscriptores. diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml index 2df64ae590..31b8a89dc7 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/hu/strings.xml @@ -38,14 +38,14 @@ %s visszavonva Előre beállított kiszolgálók hozzáadása A hívások kezdeményezése le van tiltva. - Az összes partneréhez és csoporttaghoz külön TCP-kapcsolat (és SOCKS-hitelesítési adat) lesz használva.\nMegjegyzés: ha sok kapcsolata van, akkor az akkumulátor-használat és az adatforgalom jelentősen megnövekedhet, és néhány kapcsolódási kísérlet sikertelen lehet.]]> + Az összes partneréhez és csoporttaghoz külön TCP-kapcsolat (és SOCKS-hitelesítési adat) lesz használva.\nMegjegyzés: ha sok kapcsolata van, akkor az akkumulátor-használat és az adatforgalom jelentősen megnövekedhet, és néhány kapcsolódási kísérlet sikertelen lehet.]]> hivatkozáselőnézet visszavonása Az összes csevegési profiljához az alkalmazásban külön TCP-kapcsolat (és SOCKS-hitelesítési adat) lesz használva.]]> Mindkét fél küldhet eltűnő üzeneteket. Az Android Keystore-t a jelmondat biztonságos tárolására használják – lehetővé teszi az értesítési szolgáltatás működését. Hibás az üzenet kivonata Háttér - Megjegyzés: az üzenet- és a fájlátjátszók SOCKS proxyn keresztül kapcsolódnak. A hívások pedig közvetlen kapcsolatot használnak.]]> + Megjegyzés: az üzenet- és a fájlátjátszók SOCKS proxyn keresztül kapcsolódnak. A hívások pedig közvetlen kapcsolatot használnak.]]> Alkalmazásadatok biztonsági mentése Az adatbázis előkészítése sikertelen Az összes partnerével továbbra is kapcsolatban marad. A profilfrissítés el lesz küldve a partnerei számára. @@ -91,7 +91,7 @@ Nem lehet meghívni a partnert! hibás az üzenet azonosítója Partneri kapcsolatkérések automatikus elfogadása - Megjegyzés: NEM fogja tudni helyreállítani vagy módosítani a jelmondatot abban az esetben, ha elveszíti.]]> + Megjegyzés: NEM fogja tudni helyreállítani vagy módosítani a jelmondatot abban az esetben, ha elveszíti.]]> hívás… További másodlagos szín Hozzáadás egy másik eszközhöz @@ -114,7 +114,7 @@ Az alkalmazásjelkód helyettesítve lesz egy önmegsemmisítő jelkóddal. Arab, bolgár, finn, héber, thai és ukrán – köszönet a felhasználóknak és a Weblate-nek. Engedélyezi a hangüzeneteket? - Mindig legyen használva átjátszó + Átjátszó használata minden esetben mindig A hívás már véget ért! Engedélyezés @@ -168,7 +168,7 @@ hanghívás (végpontok között NEM titkosított) letiltva Módosítja az adatbázis jelmondatát? - kapcsolódva + kapcsolódott Jelkód módosítása a következőre módosította %s szerepkörét: „%s” Fogadási cím módosítása @@ -182,9 +182,9 @@ Kapcsolódás kapcsolódott Társított hordozható eszköz - kapcsolódva + kapcsolódott Szerepkör módosítása - Kapcsolódva + Kapcsolódott Hitelesítési adat megerősítése Módosítja a fogadási címet? módosította a címet az Ön számára @@ -1022,7 +1022,7 @@ Kapott hivatkozás beillesztése Menti a kiszolgálókat? A SimpleX Chat biztonsága a Trail of Bits által lett auditálva. - frissítette a csoportprofilt + frissítette a csoport profilját SIMPLEX CHAT TÁMOGATÁSA SimpleX Chat szolgáltatás Ön megfigyelő @@ -1295,7 +1295,7 @@ Nem sikerült ellenőrizni; próbálja meg újra. Az üzenet az összes tag számára moderáltként lesz megjelölve. Értesítések fogadásához adja meg az adatbázis jelmondatát - A teszt a(z) %s lépésnél sikertelen volt. + A teszt a(z) %s. lépésnél sikertelen volt. Az alkalmazás elindításához vagy 30 másodpercnyi háttérben töltött idő után, az alkalmazáshoz való visszatéréshez hitelesítésre lesz szükség. Az üzenet az összes tag számára törölve lesz. A videó nem dekódolható. Próbálja ki egy másik videóval, vagy lépjen kapcsolatba a fejlesztőkkel. @@ -1571,8 +1571,8 @@ feloldotta %s letiltását Ön feloldotta %s letiltását letiltva - letiltva az adminisztrátor által - Letiltva az adminisztrátor által + az adminisztrátor letiltotta + Az adminisztrátor letiltotta letiltotta őt: %s Letiltás Az összes tag számára letiltja a tagot? @@ -1649,14 +1649,14 @@ Átköltöztetés ide Eszköz átköltöztetése Átköltöztetés egy másik eszközre - Figyelmeztetés: az archívum törölve lesz.]]> + Figyelmeztetés: az archívum törölve lesz.]]> Átköltöztetés egy másik eszközről Kvantumbiztos titkosítás Megpróbálhatja még egyszer. Átköltöztetés kész Átköltöztetés egy másik eszközre QR-kód használatával. Átköltöztetés - Megjegyzés: ha két eszközön is ugyanazt az adatbázist használja, akkor biztonsági védelemként megszakítja a partnereitől érkező üzenetek visszafejtését.]]> + Megjegyzés: ha két eszközön is ugyanazt az adatbázist használja, akkor biztonsági védelemként megszakítja a partnereitől érkező üzenetek visszafejtését.]]> Megpróbálhatja még egyszer. Érvénytelen hivatkozás végpontok közötti kvantumbiztos titkosítás @@ -1720,11 +1720,11 @@ Profilkép alakzata Négyzet, kör vagy bármi a kettő között. Célkiszolgáló-hiba: %1$s - Továbbítókiszolgáló: %1$s\nHiba: %2$s + Továbbító kiszolgáló: %1$s\nHiba: %2$s Hálózati problémák – az üzenet többszöri elküldési kísérlet után lejárt. A kiszolgáló verziója nem kompatibilis a hálózati beállításokkal. Érvénytelen kulcs vagy ismeretlen kapcsolat – valószínűleg ez a kapcsolat törlődött. - Továbbítókiszolgáló: %1$s\nCélkiszolgáló-hiba: %2$s + Továbbító kiszolgáló: %1$s\nCélkiszolgáló-hiba: %2$s Hiba: %1$s Kapacitás túllépés – a címzett nem kapta meg a korábban elküldött üzeneteket. Üzenetkézbesítési figyelmeztetés @@ -1738,10 +1738,10 @@ Nem Nem védett Igen - NE legyen használva privát útválasztás. + Privát útválasztás használatának elkerülése. Privát útválasztás Privát útválasztás használata az ismeretlen kiszolgálókhoz. - Mindig legyen használva privát útválasztás. + Privát útválasztás használata minden esetben. Üzenet-útválasztási mód Közvetlen üzenetküldés, ha az IP-cím védett és a saját kiszolgálója vagy a célkiszolgáló nem támogatja a privát útválasztást. Közvetlen üzenetküldés, ha a saját kiszolgálója vagy a célkiszolgáló nem támogatja a privát útválasztást. @@ -1874,7 +1874,7 @@ Kiszolgáló-beállítások megnyitása Kiszolgáló címe Feltöltési hibák - Visszaigazolt + Visszaigazolva Visszaigazolási hibák kísérletek Törölt töredékek @@ -1926,7 +1926,7 @@ Ezen verzió kihagyása Ha értesítést szeretne kapni az új kiadásokról, kapcsolja be a stabil vagy béta verziók időszakos ellenőrzését. Új verzió érhető el: %s - A frissítés letöltése megszakítva + Frissítésletöltés visszavonva Béta Letiltás Letiltva @@ -2194,7 +2194,7 @@ A tagok közötti közvetlen üzenetek le vannak tiltva. Üzleti csevegések Saját ügyfeleinek adatvédelme. - %1$s.]]> + %1$s.]]> A csevegés már létezik! Csökkentse az üzenet méretét, és küldje el újra. Üzenetek ellenőrzése 10 percenként @@ -2270,7 +2270,7 @@ Tagok jelentései 1 jelentés Jelentések - %s által archivált jelentés + %s archiválta a jelentést %d jelentés Kéretlen tartalom A tartalom sérti a használati feltételeket @@ -2350,7 +2350,7 @@ Kikapcsolva Előre beállított kiszolgálók A 443-as TCP-port használata kizárólag az előre beállított kiszolgálókhoz. - Hiba a tag befogadásakor + Hiba történt a tag befogadásakor %d csevegés a tagokkal %d üzenet 1 csevegés egy taggal @@ -2382,7 +2382,7 @@ Ön befogadta ezt a tagot Befogadás tagként befogadta őt: %1$s - áttekintve a moderátorok által + a moderátorok áttekintették nem lehet üzeneteket küldeni partner letiltva csoport törölve @@ -2407,7 +2407,7 @@ Hiba történt a partneri kapcsolatkérés elutasításakor Hiba történt a csevegés megnyitásakor Hiba történt a csoport megnyitásakor - Hiba a profil módosításakor + Hiba történt a profil módosításakor Megnyitás a csatlakozáshoz Megnyitás a kapcsolódáshoz Megnyitás az elfogadáshoz @@ -2496,7 +2496,7 @@ Teljes hivatkozás megnyitása Nyomonkövetési paraméterek eltávolítása a hivatkozásokból SimpleX-átjátszó címe - Hiba a csevegés olvasottként való megjelölésekor + Hiba történt a csevegés olvasottként való megjelölésekor A célkiszolgáló címében szereplő ujjlenyomat nem egyezik a tanúsítvánnyal: %1$s. A továbbító kiszolgáló címében szereplő ujjlenyomat nem egyezik a tanúsítvánnyal: %1$s. A kiszolgáló címében szereplő ujjlenyomat nem egyezik a tanúsítvánnyal: %1$s. @@ -2597,7 +2597,7 @@ Ellenőrizze az átjátszó nevét, és próbálja újra. Érvénytelen az átjátszó címe! Ellenőrizze az átjátszó címét, és próbálja újra. - Hiba az átjátszó hozzáadásakor + Hiba történt az átjátszó hozzáadásakor Csevegési átjátszók A csevegési átjátszók továbbítják az üzeneteket az Ön által létrehozott csatornákban. Csevegési átjátszók @@ -2605,8 +2605,8 @@ A csevegési átjátszók továbbítják az üzeneteket a csatorna feliratkozóinak. %1$d/%2$d átjátszó aktív, %3$d sikertelen %1$d/%2$d átjátszó aktív - %1$d/%2$d átjátszó kapcsolódva, %3$d hiba - %1$d/%2$d átjátszó kapcsolódva + %1$d/%2$d átjátszó kapcsolódott, %3$d hiba + %1$d/%2$d átjátszó kapcsolódott ÁTJÁTSZÓ Átjátszóhivatkozás Átjátszó címe @@ -2616,7 +2616,7 @@ Ön ezen az átjátszóhivatkozáson keresztül kapcsolódott a csatornához. Feliratkozó eltávolítása Az összes feliratkozó számára letiltja a feliratkozót? - Hiba a csatorna létrehozásakor + Hiba történt a csatorna létrehozásakor Visszavonja a csatorna létrehozását? Engedélyezzen legalább egy csevegési átjátszót a csatorna létrehozásához. A(z) %1$s nevű profilja meg lesz osztva a csatorna átjátszóival és feliratkozóival.\nAz átjátszók hozzáférhetnek a csatornaüzenetekhez. @@ -2627,7 +2627,7 @@ Átjátszó címe Ez egy csevegési átjátszó címe, nem használható kapcsolódásra. %1$s nevű csatornához!]]> - Hiba a csatorna megnyitásakor + Hiba történt a csatorna megnyitásakor Az összes feliratkozó számára feloldja a feliratkozó letiltását? Átjátszó tesztelése a nevének lekéréséhez.]]> Csatorna teljes neve: @@ -2636,11 +2636,11 @@ %d csatornaesemény törölt csatorna hiba: %s - Hiba a csatornaprofil mentésekor + Hiba történt a csatornaprofil mentésekor Üzenethiba Mentés és a csatorna feliratkozóinak értesítése Csatornaprofil mentése - frissített csatornaprofil + frissítette a csatorna profilját Az alkalmazás %1$d sikertelen letöltési kísérlet után eltávolította ezt az üzenetet. eltávolítva (%1$d kísérlet) A csatorna ideiglenesen nem érhető el @@ -2658,12 +2658,12 @@ %1$d/%2$d átjátszó aktív, %3$d hiba %1$d/%2$d átjátszó kapcsolódott, %3$d átjátszóhoz nem sikerült kapcsolódni %1$d/%2$d átjátszó kapcsolódott, %3$d eltávolítva - Az átjátszók hozzáadása később lesz támogatott. + Átjátszók hozzáadása az üzenetküldés helyreállításához. Várakozás a csatorna tulajdonosára az átjátszók hozzáadásához. Üzleti cím Csatornahivatkozás Kapcsolattartási cím - Hiba a csatorna megosztásakor + Hiba történt a csatorna megosztásakor (a tulajdonostól) Csoporthivatkozás Hivatkozás aláírása ellenőrizve. @@ -2692,7 +2692,7 @@ Saját hivatkozás létrehozása Bárki számára, aki el szeretné érni Önt Partner meghívása privátban - Hagyja, hogy valaki elérje Önt + Legyen elérhető mások számára Vagy mutassa meg a QR-kódot személyesen vagy videóhíváson keresztül. Vagy használja ezt a QR-kódot – nyomtassa ki vagy mutassa meg online. Küldje el a hivatkozást bármilyen üzenetváltó alkalmazáson keresztül – ez egy biztonságos módszer – és kérje meg a partnerét, hogy illessze be a SimpleX alkalmazásba. @@ -2702,7 +2702,7 @@ Nonprofit irányítás - Hivatkozások előnézetének küldése.\n- SOCKS proxy használata, ha engedélyezve van.\n- Hiperhivatkozásokon keresztüli adathalászat megakadályozása.\n- Hivatkozások nyomonkövetési paramétereinek eltávolítása. Tulajdonjog: saját átjátszókat üzemeltethet. - Adatvédelem: tulajdonosok és előfizetők számára. + Adatvédelem: tulajdonosok és feliratkozók számára. Nyilvános csatornák – mondja el szabadon a véleményét 🚀 Megbízhatóság: több átjátszó is használható csatornánként. Biztonságos webhivatkozások @@ -2711,13 +2711,13 @@ Az új felhasználók számára egyszerűbbé tettük a kapcsolatok létrehozását. Fiók nélkül születtünk. Senki sem követte nyomon a beszélgetéseinket. Senki sem készített térképet arról, hogy merre jártunk. A magánéletünk nem csak egy funkció volt, hanem az életmódunk. - Aztán felléptünk az internetre, és minden platform kért belőlünk egy darabot - nevet, telefonszámot, baráti kapcsolatokat. Elfogadtuk, hogy a kommunikáció ára az, hogy mások megtudják, hogy kivel beszélünk. Minden generáció, az emberek és a technológia is eddig így működött - telefon, e-mail, üzenetküldő programok, közösségi média. Úgy tűnt, ez az egyetlen lehetséges mód. + Aztán felléptünk az internetre, és minden platform kért belőlünk egy darabot– nevet, telefonszámot, baráti kapcsolatokat. Elfogadtuk, hogy a kommunikáció ára az, hogy mások megtudják, hogy kivel beszélünk. Minden generáció, az emberek és a technológia is eddig így működött – telefon, e-mail, üzenetküldő programok, közösségi média. Úgy tűnt, ez az egyetlen lehetséges mód. De van egy másik lehetőség is. Egy hálózat, amelyben nincsenek telefonszámok. Nincsenek felhasználónevek. Nincsenek fiókok. Nincsenek semmiféle felhasználói azonosítók. Egy hálózat, amely összeköti az embereket és titkosított üzeneteket továbbít, anélkül, hogy tudná, ki csatlakozik hozzá. - Nem egy jobb zár mások ajtaján. Nem egy kedvesebb házmester, aki tiszteletben tartja az Ön magánéletét, de mégis nyilvántartást vezet minden látogatójáról. Ön itt nem csak egy vendég. Ön itt otthon van. Nincs az a hatalom, amely beléphetne ide - Ön itt szuverén. + Nem egy jobb zár mások ajtaján. Nem egy kedvesebb házmester, aki tiszteletben tartja az Ön magánéletét, de mégis nyilvántartást vezet minden látogatójáról. Ön itt nem csak egy vendég. Ön itt otthon van. Nincs az a hatalom, amely beléphetne ide – Ön itt szuverén. A beszélgetései Önhöz tartoznak, ahogy az internet megjelenése előtt is mindig így volt. A hálózat nem egy hely, amelyet meglátogat. Ez egy olyan hely, amelyet Ön hoz létre saját magának. És senki sem veheti el Öntől, függetlenül attól, hogy privát vagy nyilvános. - A legrégebbi emberi szabadság - beszélgetni az emberekkel, anélkül, hogy mások megfigyelnének - olyan infrastruktúrán alapul, amely nem tudja elárulni. + A legrégebbi emberi szabadság – beszélgetni az emberekkel, anélkül, hogy mások megfigyelnének – olyan infrastruktúrán alapul, amely nem tudja elárulni. Mert felszámoltuk a lehetőségét is annak, hogy megtudjuk, Ön kicsoda. Így az önrendelkezése soha nem kerülhet idegen kezekbe. - Legyen szabad a saját hálózatában. + Váljon szabaddá a saját hálózatában. Feliratkozók jelentései A közvetlen üzenetek küldése a feliratkozók között engedélyezve van. @@ -2737,7 +2737,7 @@ Az előzmények nem lesznek elküldve az új feliratkozók számára. A csevegés az adminisztrátorokkal engedélyezve van a tagok számára. A csevegés az adminisztrátorokkal engedélyezve van a feliratkozók számára. - Váljon szabaddá\na saját hálózatában. + Váljon szabaddá\na saját hálózatában A csevegés az adminisztrátorokkal le van tiltva. A nyilvános csatornákban az adminisztrátorokkal való csevegések nem rendelkeznek végpontok közötti titkosítással – csak megbízható csevegési átjátszókkal használja őket. A csevegés a tagokkal le van tiltva @@ -2758,12 +2758,46 @@ A csevegés az adminisztrátorokkal le van tiltva. Értesítések beállítása Útválasztók beállítása - A feliratkozók cseveghetnek az adminisztrátorokkal + A feliratkozók cseveghetnek az adminisztrátorokkal. Miért jött létre a SimpleX? Saját hálózat - Profil létrehozása + Saját profil létrehozása Az első hálózat, ahol Ön birtokolja\na saját kapcsolatait és csoportjait. Alsó sáv A hivatkozások előnézetét SOCKS proxyn keresztül kéri le a kliens. A DNS-lekérdezés viszont továbbra is történhet helyi szinten, a saját DNS-kiszolgálón keresztül. Felső sáv + Az új %1$s nevű csatornája %3$d átjátszóból %2$d átjátszóhoz kapcsolódik.\nHa visszavonja, akkor a csatorna törlődni fog – de később újra létrehozhatja. + Visszavonás és a csatorna törlése + Hozzáadás + Átjátszó hozzáadása + Átjátszók hozzáadása + %d átjátszó kiválasztva + Hiba történt az átjátszók hozzáadásakor + Nincsenek elérhető átjátszók + Nincsenek átjátszók + Nincsenek átjátszók kiválasztva + Átjátszók hozzáadva: %1$s. + Az átjátszó el lesz távolítva a csatornából – ez a művelet nem vonható vissza! + eltávolítva + Átjátszó eltávolítása + Eltávolítja az átjátszót? + Átjátszók kiválasztása + Ez az utolsó aktív átjátszó. Ha eltávolítja, akkor azzal megakadályozza az üzenetek eljuttatását a feliratkozóknak. + Alkalmazás bezárása + Ha a bezárás mellett dönt, az üzenetek nem fognak megérkezni.\nEzt később a „Megjelenés” beállításaiban módosíthatja. + Hagyja a SimpleX-et a háttérben futni az üzenetek fogadásához. + Kilépés a SimpleXből + SimpleX megjelenítése + SimpleX + SimpleX – %d olvasatlan üzenet + Kicsinyítés az értesítési területre + Biztosan kicsinyíteni szeretné az értesítési területre? + Kicsinyítés az értesítési területre az ablak bezárásakor + Hiba történt az üzenet törlésekor + Az előzményekből + Állapot + elutasítva + az átjátszó üzemeltetője elutasította + Az alkalmazás már fut + Lehet, hogy egy másik alkalmazáspéldány fut, vagy nem zárult be megfelelően. Így is elindítja? diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml index adce58e804..6642553f2e 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml @@ -2448,7 +2448,7 @@ Errore nel rifiuto della richiesta di contatto Entra nel gruppo Apri la chat - Apri una chat nuova + Apri la nuova chat Apri il nuovo gruppo Apri per accettare Apri per connettere @@ -2578,7 +2578,7 @@ Nome del canale Il canale verrà eliminato per tutti gli iscritti, non è reversibile! Il canale verrà eliminato per te, non è reversibile! - Il canale sarà operativo con %1$d di %2$d relay. Procedere? + Il canale sarà operativo con %1$d di %2$d relay. Continuare? Relay di chat Relay di chat Relay di chat @@ -2619,7 +2619,7 @@ Nessun relay di chat attivato. Non tutti i relay sono connessi Apri canale - Apri un canale nuovo + Apri il nuovo canale PROPRIETARIO Proprietari Indirizzo relay preimpostato @@ -2686,7 +2686,7 @@ %1$d relay falliti %1$d relay non attivi %1$d relay rimossi - L\'aggiunta di relay verrà supportata prossimamente. + Aggiungi relay per ripristinare la consegna dei messaggi. Tutti i relay falliti Tutti i relay rimossi impossibile trasmettere @@ -2802,4 +2802,38 @@ Barra inferiore L\'anteprima del link verrà richiesta via proxy SOCKS. La ricerca DNS può ancora accadere localmente tramite il tuo risolutore DNS. Barra superiore + Il tuo nuovo canale %1$s è connesso a %2$d di %3$d relay.\nSe annulli, il canale verrà eliminato. Potrai crearlo di nuovo. + Aggiungi + Aggiungi relay + Aggiungi relay + Annulla ed elimina il canale + %d relay selezionato/i + Errore di aggiunta dei relay + Nessun relay disponibile + Nessun relay + Nessun relay selezionato + Relay aggiunti: %1$s. + Il relay verrà rimosso dal canale, non è reversibile! + rimosso + Rimuovi relay + Rimuovere il relay? + Seleziona i relay + Questo è l\'ultimo relay attivo. La sua rimozione impedirà la consegna dei messaggi agli iscritti. + Chiudi l\'app + Errore di eliminazione del messaggio + Dalla cronologia + Se scegli Chiudi, i messaggi non verranno ricevuti.\nPuoi cambiarlo più tardi nelle impostazioni di Aspetto. + Tieni SimpleX attivo in secondo piano per ricevere i messaggi. + Riduci nell\'area delle notifiche + Ridurre nell\'area delle notifiche? + Riduci nell\'area delle notifiche alla chiusura della finestra + Esci da SimpleX + Mostra SimpleX + SimpleX + SimpleX — %d non letto/i + Un\'altra istanza dell\'app potrebbe essere in esecuzione o non si è chiusa correttamente. Avviare comunque? + L\'app è già in esecuzione + rifiutato + rifiutato dall\'operatore del relay + Stato diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/ru/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/ru/strings.xml index b5bbf47973..b1d505e644 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/ru/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/ru/strings.xml @@ -2207,7 +2207,7 @@ Посмотреть условия %s.]]> Условия будут автоматически приняты для включенных операторов: %s. - Условия приняты: %s. + Условия приняты: %s Вебсайт %s.]]> %s.]]> @@ -2799,7 +2799,7 @@ Приложение удалило это сообщение после %1$d попыток его получить. Если Вы присоединились к каналам или создали их, они перестанут работать навсегда. Вы перестанете получать сообщения из этого канала. История чата сохранится. - обновлён профиль канала + обновил профиль канала ошибка ОШИБКА СОЕДИНЕНИЯ Чат с админами @@ -2858,7 +2858,7 @@ ошибка новый Все релеи недоступны - Добавление релеев будет поддерживаться позже. + Добавить релеи чтобы восстановить доставку сообщений. Ожидает, когда владелец канала добавит релеи. через %1$s Подписчики используют ссылку релея для подключения к каналу.\nАдрес релея был использован для настройки этого релея для канала. @@ -2874,4 +2874,9 @@ Нижнее меню Картинка ссылки будет загружена через SOCKS-прокси. DNS-запрос может быть локальным через Ваш резолвер. Верхнее меню + Добавить + Добавить релей + Добавить релеи + Отменить и удалить канал + Закрыть приложение diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml index 1392d7b42b..0901494460 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml @@ -2670,7 +2670,7 @@ %1$d 个中继失灵 %1$d 个中继不活跃 删除了 %1$d 个中继 - 目前不支持添加中继。 + 添加恢复消息传输的中继。 所有中继均失灵 删除了所有中继 无法广播 @@ -2785,4 +2785,38 @@ 底部栏 将通过 SOCKS5 代理请求链接预览。DNS 查询仍可能通过你的 DNS 解析器在本地发生。 顶部栏 + 你的新频道 %1$s 已连接到 %3$d 个中继中的 %2$d 个中继。\n如果取消,频道将被删除 —— 你可以再次创建它。 + 这是上次活跃的中继。删除它会阻止给订阅者传送消息。 + 添加 + 添加中继 + 添加中继 + 取消并删除频道 + 选中了 %d 个中继 + 添加中继出错 + 无可用中继 + 无中继 + 未选中中继 + 已添加的中继:%1$s。 + 将从频道删除中继 — 这无法撤销! + 已删除 + 删除中继 + 删除中继? + 选择中继 + 如果选择关闭将不会接收消息。\n可以之后在外观设置中更改。 + 保持 SimpleX 在后台运行以接收消息。 + 最小化到托盘 + 最小化到托盘? + 关闭窗口时最小化到托盘 + 退出 SimpleX + 显示 SimpleX + SimpleX + SimpleX — %d 则未读 + 关闭应用 + 删除消息出错 + 来自历史记录 + 另一个应用实例可能正在运行或没有正确退出。仍要启动? + 应用正在运行 + 被拒绝 + 被中继运营方拒绝 + 状态 From 5ac3e71d973749478f6b85c1b5a31f9e313ffaaa Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 1 Jun 2026 13:19:51 +0100 Subject: [PATCH 16/20] core: 6.5.4.0 (simplexmq 6.5.3.0) --- cabal.project | 2 +- scripts/nix/sha256map.nix | 2 +- simplex-chat.cabal | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cabal.project b/cabal.project index 77e4ed838b..3e32dfcd5e 100644 --- a/cabal.project +++ b/cabal.project @@ -21,7 +21,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: ee2ff402fed4d27d31521570c910fe82e0cf116a + tag: b981dcb70b8c99dc3733a99b6c1dc0d0ef83a3f7 source-repository-package type: git diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index f3bbf90b9f..3610906390 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."ee2ff402fed4d27d31521570c910fe82e0cf116a" = "0vka1b2bbrjg4s8j3h6732kjqjbhji0l55pzggd89ginrdjln3fg"; + "https://github.com/simplex-chat/simplexmq.git"."b981dcb70b8c99dc3733a99b6c1dc0d0ef83a3f7" = "0wpri01w30rd3wwzw630yngnj9fmyb7rschl3ic1cjd926vpg9b7"; "https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d"; "https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl"; diff --git a/simplex-chat.cabal b/simplex-chat.cabal index 18fc93eede..4990dae942 100644 --- a/simplex-chat.cabal +++ b/simplex-chat.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: simplex-chat -version: 6.5.3.0 +version: 6.5.4.0 category: Web, System, Services, Cryptography homepage: https://github.com/simplex-chat/simplex-chat#readme author: simplex.chat From e4ef8ef101e5113a702db1fc3fdca267c5c39c9c Mon Sep 17 00:00:00 2001 From: sh <37271604+shumvgolove@users.noreply.github.com> Date: Mon, 1 Jun 2026 12:20:29 +0000 Subject: [PATCH 17/20] simplex-chat-python: add BotCommand.params field (#7034) --- .../examples/squaring_bot.py | 24 +++++++- .../src/simplex_chat/bot.py | 52 ++++++++++++++++-- .../tests/test_bot_registration.py | 55 +++++++++++++++++++ 3 files changed, 126 insertions(+), 5 deletions(-) diff --git a/packages/simplex-chat-python/examples/squaring_bot.py b/packages/simplex-chat-python/examples/squaring_bot.py index 296b51347e..4d062ad718 100644 --- a/packages/simplex-chat-python/examples/squaring_bot.py +++ b/packages/simplex-chat-python/examples/squaring_bot.py @@ -26,7 +26,15 @@ bot = Bot( profile=BotProfile(display_name="Squaring bot"), db=SqliteDb(file_prefix="./squaring_bot"), welcome="Send me a number, I'll square it.", - commands=[BotCommand(keyword="help", label="Show help")], + commands=[ + # `params=None` (default): the client SENDS `/help` immediately + # when the user taps it in the commands menu. + BotCommand(keyword="help", label="Show help"), + # `params=""`: the client PASTES `/square ` + # into the input box and positions the cursor at the end. The + # user replaces `` with the actual number and sends. + BotCommand(keyword="square", label="Square a number", params=""), + ], ) NUMBER_RE = re.compile(r"^-?\d+(\.\d+)?$") @@ -48,5 +56,19 @@ async def help_cmd(msg: Message, _cmd: ParsedCommand) -> None: await msg.reply("Send a number, I'll square it.") +@bot.on_command("square") +async def square_cmd(msg: Message, cmd: ParsedCommand) -> None: + """Demonstrates the `params` flow: `cmd.args` is the trimmed text + AFTER `/square`. When the user tapped the menu entry above, the + client pasted `/square ` and the user replaced + `` with the actual value before sending.""" + try: + n = float(cmd.args) + except ValueError: + await msg.reply(f"Usage: /square (got {cmd.args!r})") + return + await msg.reply(f"{n} * {n} = {n * n}") + + if __name__ == "__main__": bot.run() diff --git a/packages/simplex-chat-python/src/simplex_chat/bot.py b/packages/simplex-chat-python/src/simplex_chat/bot.py index fb511e2818..4e385493b2 100644 --- a/packages/simplex-chat-python/src/simplex_chat/bot.py +++ b/packages/simplex-chat-python/src/simplex_chat/bot.py @@ -33,8 +33,38 @@ from .types import T @dataclass(slots=True) class BotCommand: + """One entry in the bot's advertised slash-command list (wire-side + `groupPreferences.commands` or profile `preferences.commands`). + + `keyword` and `label` are required: `keyword` is what the user + types after `/`; `label` is the human-readable description shown + next to the keyword in the SimpleX client's commands menu. + + `params` is an optional placeholder string that controls how the + client behaves when the user taps the command in the menu: + + * `params=None` (default) — the client SENDS `/` + immediately on tap; no input-box detour. Use this for + zero-argument commands (`/help`, `/ping`) where the action is + unambiguous. + + * `params=""` — the client PASTES `/ ` + into the input box and positions the cursor at the end. The + user edits the placeholder and sends. Use this for commands + that take a required argument (`/review `, + `/order `) so the user sees the expected shape + without having to remember it. + + Mirrors `CBCCommand` in the Haskell core + (`Simplex.Chat.Types.Preferences`) and the wire TypedDict + `ChatBotCommand_command`. Both SimpleX clients (Android/Kotlin + and iOS/Swift) implement the paste-vs-send branch on the + `params` field; see `CommandsMenuView.{kt,swift}` for the + reference UI behaviour. + """ keyword: str label: str + params: str | None = None class Bot(Client): @@ -145,10 +175,24 @@ class Bot(Client): "files": {"allow": "yes" if self._allow_files else "no"}, } if self._commands: - prefs["commands"] = [ - {"type": "command", "keyword": c.keyword, "label": c.label} - for c in self._commands - ] + cmds: list[T.ChatBotCommand] = [] + for c in self._commands: + entry: T.ChatBotCommand_command = { + "type": "command", + "keyword": c.keyword, + "label": c.label, + } + # `params` is `NotRequired[str]` on the wire; omit the + # key entirely when None so the Haskell parser sees + # `Nothing` rather than `Just ""`. The two have + # different client semantics: `Nothing` (`params=None`) + # triggers an immediate send on tap; `Just ""` would + # paste `/ ` (with a trailing space) into the + # input box, which is rarely what the operator wants. + if c.params is not None: + entry["params"] = c.params + cmds.append(entry) + prefs["commands"] = cmds p["preferences"] = prefs p["peerType"] = "bot" return p diff --git a/packages/simplex-chat-python/tests/test_bot_registration.py b/packages/simplex-chat-python/tests/test_bot_registration.py index f6f245c344..06837bdc07 100644 --- a/packages/simplex-chat-python/tests/test_bot_registration.py +++ b/packages/simplex-chat-python/tests/test_bot_registration.py @@ -86,8 +86,63 @@ def test_bot_profile_to_wire_with_commands(): ) cmds = bot._profile_to_wire().get("preferences", {}).get("commands") or [] assert len(cmds) == 2 + # `params` defaults to None and must be ABSENT from the wire dict + # (not present as `null`/`""`) so the Haskell parser sees + # `Nothing` and the SimpleX client sends the bare `/keyword` on + # tap rather than pasting a trailing-space placeholder. assert cmds[0] == {"type": "command", "keyword": "ping", "label": "Ping bot"} assert cmds[1] == {"type": "command", "keyword": "help", "label": "Show help"} + assert "params" not in cmds[0] + assert "params" not in cmds[1] + + +def test_bot_command_params_emits_on_wire(): + """When `BotCommand.params` is set, the wire dict carries it as + `params: `. The SimpleX client (verified against + `CommandsMenuView.kt:153-161` and `CommandsMenuView.swift:117-128` + in simplex-chat 6.5) then pastes `/ ` into the + input box on tap, positions the cursor at the end, and lets the + user edit before sending. Use this for commands that take a + required argument (`/review `).""" + bot = Bot( + profile=BotProfile(display_name="x"), + db=SqliteDb(file_prefix="/tmp/test"), + commands=[ + BotCommand(keyword="review", label="Review PR", params=""), + BotCommand(keyword="order", label="Place order", params=""), + ], + ) + cmds = bot._profile_to_wire().get("preferences", {}).get("commands") or [] + assert cmds[0] == { + "type": "command", + "keyword": "review", + "label": "Review PR", + "params": "", + } + assert cmds[1] == { + "type": "command", + "keyword": "order", + "label": "Place order", + "params": "", + } + + +def test_bot_command_distinguishes_none_from_empty_params(): + """`params=None` (immediate send) and `params=""` (paste with + trailing space) are semantically different on the client side. + Verify the wire form preserves the distinction: None → key + absent; empty string → key present with empty value.""" + bot = Bot( + profile=BotProfile(display_name="x"), + db=SqliteDb(file_prefix="/tmp/test"), + commands=[ + BotCommand(keyword="send", label="Send", params=None), + BotCommand(keyword="paste", label="Paste", params=""), + ], + ) + cmds = bot._profile_to_wire().get("preferences", {}).get("commands") or [] + assert "params" not in cmds[0] + assert cmds[1].get("params") == "" def test_client_profile_to_wire_has_no_bot_extras(): From 83f4f6cd382f0262905f8b3832f15d897052902a Mon Sep 17 00:00:00 2001 From: Evgeny Date: Mon, 1 Jun 2026 21:33:35 +0100 Subject: [PATCH 18/20] core: rename field in protocol (#7038) * core: rename field in protocol * update bot apis --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --- apps/ios/SimpleXChat/ChatTypes.swift | 2 +- .../chat/simplex/common/model/ChatModel.kt | 2 +- bots/api/TYPES.md | 2 +- .../types/typescript/src/types.ts | 2 +- .../src/simplex_chat/types/_types.py | 2 +- plans/2026-05-25-channel-web-preview.md | 20 +++++++++---------- src/Simplex/Chat/Protocol.hs | 4 ++-- src/Simplex/Chat/Store/Groups.hs | 8 ++++---- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index 7181ba2de0..5e0c302720 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -2539,7 +2539,7 @@ public struct PublicGroupAccess: Codable, Hashable { } public struct RelayCapabilities: Codable, Hashable { - public var baseWebUrl: String? + public var webDomain: String? } public struct PublicGroupProfile: Codable, Hashable { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 8ecfa0fd93..11c0f9e7f6 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -2219,7 +2219,7 @@ data class PublicGroupAccess( @Serializable data class RelayCapabilities( - val baseWebUrl: String? = null + val webDomain: String? = null ) @Serializable diff --git a/bots/api/TYPES.md b/bots/api/TYPES.md index 4875079749..a87bcae5e4 100644 --- a/bots/api/TYPES.md +++ b/bots/api/TYPES.md @@ -3361,7 +3361,7 @@ ParseError: ## RelayCapabilities **Record type**: -- baseWebUrl: string? +- webDomain: string? --- diff --git a/packages/simplex-chat-client/types/typescript/src/types.ts b/packages/simplex-chat-client/types/typescript/src/types.ts index f0cf58de64..5e671169de 100644 --- a/packages/simplex-chat-client/types/typescript/src/types.ts +++ b/packages/simplex-chat-client/types/typescript/src/types.ts @@ -3762,7 +3762,7 @@ export namespace RcvMsgError { } export interface RelayCapabilities { - baseWebUrl?: string + webDomain?: string } export interface RelayProfile { diff --git a/packages/simplex-chat-python/src/simplex_chat/types/_types.py b/packages/simplex-chat-python/src/simplex_chat/types/_types.py index 08308ed2d4..66ba77c062 100644 --- a/packages/simplex-chat-python/src/simplex_chat/types/_types.py +++ b/packages/simplex-chat-python/src/simplex_chat/types/_types.py @@ -2640,7 +2640,7 @@ RcvMsgError = RcvMsgError_dropped | RcvMsgError_parseError RcvMsgError_Tag = Literal["dropped", "parseError"] class RelayCapabilities(TypedDict): - baseWebUrl: NotRequired[str] + webDomain: NotRequired[str] class RelayProfile(TypedDict): displayName: str diff --git a/plans/2026-05-25-channel-web-preview.md b/plans/2026-05-25-channel-web-preview.md index de0f02506b..561d3948e7 100644 --- a/plans/2026-05-25-channel-web-preview.md +++ b/plans/2026-05-25-channel-web-preview.md @@ -19,7 +19,7 @@ simplex-chat CLI (--relay --web-json-dir=... --web-base-url=...) └── Regenerate Caddy CORS config → caddy reload Caddy (operator-configured) - ├── Serves JSON at /.json + ├── Serves JSON at https:///group/.json └── Imports generated CORS config file Channel page (static HTML+JS, hosted by owner or on GitHub) @@ -105,7 +105,7 @@ Extend `toPublicGroupProfile` to accept and pass through `Maybe PublicGroupAcces New record for relay capabilities (extensible for future fields): ```haskell data RelayCapabilities = RelayCapabilities - { baseWebUrl :: Maybe Text + { webDomain :: Maybe Text } ``` @@ -132,7 +132,7 @@ Encoding: `XGrpRelayCap cap -> o ["relayCap" .= cap]` Sent by relay to owner only when capabilities change (not periodic). Relay detects change by comparing current config against persisted state on startup. -### 3. Store `baseWebUrl` per relay +### 3. Store `webDomain` per relay **File:** `src/Simplex/Chat/Operators.hs` (line 278) @@ -152,7 +152,7 @@ Add: `relayCap :: Maybe RelayCapabilities` Stored as separate columns (same pattern as `PublicGroupAccess`): **Migration:** `ALTER TABLE group_relays ADD COLUMN base_web_url TEXT` -`relayCap` constructed from columns: `Just RelayCapabilities {baseWebUrl}` when any capability column is non-NULL, `Nothing` otherwise. +`relayCap` constructed from columns: `Just RelayCapabilities {webDomain}` when any capability column is non-NULL, `Nothing` otherwise. **Handlers in `src/Simplex/Chat/Library/Subscriber.hs`:** - `XGrpRelayAcpt` (line 770): store `RelayCapabilities` in relay record on acceptance @@ -168,7 +168,7 @@ New record bundling all web preview options: ```haskell data RelayWebOptions = RelayWebOptions { webJsonDir :: FilePath, -- --web-json-dir: where to write JSON files - webBaseUrl :: Text, -- --web-base-url: public URL prefix (sent in XGrpRelayAcpt) + webDomain :: Text, -- --web-base-url: public URL prefix (sent in XGrpRelayAcpt) webCorsFile :: FilePath, -- --web-cors-file: generated Caddy CORS config path webUpdateInterval :: Int -- --web-update-interval: seconds (default 300) } @@ -443,7 +443,7 @@ data class PublicGroupProfile( @Serializable data class RelayCapabilities( - val baseWebUrl: String? = null + val webDomain: String? = null ) // Extend existing GroupRelay: @@ -473,7 +473,7 @@ New nav destination opens `ChannelWebPageView`. - Text field: domain (`groupDomain`) - Toggle: allow embedding (`allowEmbeding`) - Toggle: show on domain's page (`domainWebPage`) - stored but inert until RSLV ships -- Section: embed snippet (read-only, auto-generated from relay `baseWebUrl` values + `publicGroupId`) +- Section: embed snippet (read-only, auto-generated from relay `webDomain` values + `publicGroupId`) - Save button -> `apiUpdateGroup` with updated `GroupProfile` ### Subscriber: Channel info page @@ -518,12 +518,12 @@ simplex-chat --relay \ ### Embed snippet (shown to owner) -The "Channel web page" screen auto-generates this from the channel's relay `baseWebUrl` values and `publicGroupId`. Owner copies it into their page: +The "Channel web page" screen auto-generates this from the channel's relay `webDomain` values and `publicGroupId`. Owner copies it into their page: ```html
+ data-relays=",">
``` @@ -565,7 +565,7 @@ Separate repo or folder. `channel-preview.js` + minimal CSS: - `src/Simplex/Chat/Protocol.hs` - `RelayCapabilities` record, extend `XGrpRelayAcpt`, add `XGrpRelayCap` - `src/Simplex/Chat/Options.hs` - `RelayWebOptions` record, `relayWebOptions :: Maybe RelayWebOptions` in `CoreChatOpts` - `src/Simplex/Chat/Core.hs` - start web preview thread in `runSimplexChat` -- `src/Simplex/Chat/Operators.hs` - `baseWebUrl` in `GroupRelay` +- `src/Simplex/Chat/Operators.hs` - `webDomain` in `GroupRelay` - `src/Simplex/Chat/Store/Groups.hs` - read/write `PublicGroupAccess` columns; `getWebPublishGroups` - `src/Simplex/Chat/Store/Shared.hs` - `toPublicGroupAccess`, extend `toPublicGroupProfile` and `GroupInfoRow` - `src/Simplex/Chat/Library/Subscriber.hs` - handle `RelayCapabilities` in `XGrpRelayAcpt` and `XGrpRelayCap` diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index 71daeec635..9b8f6da766 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -263,12 +263,12 @@ data ReportReason = RRSpam | RRContent | RRCommunity | RRProfile | RROther | RRU deriving (Eq, Show) data RelayCapabilities = RelayCapabilities - { baseWebUrl :: Maybe Text + { webDomain :: Maybe Text } deriving (Eq, Show) defaultRelayCapabilities :: RelayCapabilities -defaultRelayCapabilities = RelayCapabilities {baseWebUrl = Nothing} +defaultRelayCapabilities = RelayCapabilities {webDomain = Nothing} $(pure []) diff --git a/src/Simplex/Chat/Store/Groups.hs b/src/Simplex/Chat/Store/Groups.hs index e2a9d6816a..8c207d99a7 100644 --- a/src/Simplex/Chat/Store/Groups.hs +++ b/src/Simplex/Chat/Store/Groups.hs @@ -1352,9 +1352,9 @@ groupRelayQuery = |] toGroupRelay :: (Int64, GroupMemberId, DBEntityId, ShortLinkContact, Text, Text, Maybe Text, Maybe ImageData, Text, BoolInt) :. (Maybe BoolInt, BoolInt, BoolInt, RelayStatus, Maybe ShortLinkContact, Maybe Text) -> GroupRelay -toGroupRelay ((groupRelayId, groupMemberId, chatRelayId, address, displayName, fullName, shortDescr, image, domains, BI preset) :. (tested, BI enabled, BI deleted, relayStatus, relayLink, baseWebUrl)) = +toGroupRelay ((groupRelayId, groupMemberId, chatRelayId, address, displayName, fullName, shortDescr, image, domains, BI preset) :. (tested, BI enabled, BI deleted, relayStatus, relayLink, webDomain)) = let userChatRelay = UserChatRelay {chatRelayId, address, relayProfile = toRelayProfile (displayName, fullName, shortDescr, image), domains = T.splitOn "," domains, preset, tested = unBI <$> tested, enabled, deleted} - relayCap = RelayCapabilities {baseWebUrl} + relayCap = RelayCapabilities {webDomain} in GroupRelay {groupRelayId, groupMemberId, userChatRelay, relayStatus, relayLink, relayCap} createRelayForOwner :: DB.Connection -> VersionRangeChat -> TVar ChaChaDRG -> User -> GroupInfo -> UserChatRelay -> ExceptT StoreError IO GroupMember @@ -1496,7 +1496,7 @@ setRelayLinkConfId db m confId relayLink = do (relayLink, currentTs, groupMemberId' m) updateRelayCapabilities :: DB.Connection -> GroupMember -> RelayCapabilities -> IO () -updateRelayCapabilities db m RelayCapabilities {baseWebUrl} = do +updateRelayCapabilities db m RelayCapabilities {webDomain} = do currentTs <- getCurrentTime DB.execute db @@ -1505,7 +1505,7 @@ updateRelayCapabilities db m RelayCapabilities {baseWebUrl} = do SET base_web_url = ?, updated_at = ? WHERE group_member_id = ? |] - (baseWebUrl, currentTs, groupMemberId' m) + (webDomain, currentTs, groupMemberId' m) getRelayConfId :: DB.Connection -> GroupMember -> ExceptT StoreError IO ConfirmationId getRelayConfId db m = From 7725b068334e73e815fefd0e82ecef91871d87c0 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 1 Jun 2026 21:34:48 +0100 Subject: [PATCH 19/20] 6.5.4.1 --- simplex-chat.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simplex-chat.cabal b/simplex-chat.cabal index 4990dae942..f3612c88cf 100644 --- a/simplex-chat.cabal +++ b/simplex-chat.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: simplex-chat -version: 6.5.4.0 +version: 6.5.4.1 category: Web, System, Services, Cryptography homepage: https://github.com/simplex-chat/simplex-chat#readme author: simplex.chat From cfd25b8c73432c398f8176ea7747c7c6409a42ba Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 1 Jun 2026 22:54:38 +0100 Subject: [PATCH 20/20] ios: update library --- apps/ios/SimpleX.xcodeproj/project.pbxproj | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index 3d274bcc85..5bc1de821c 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -183,8 +183,8 @@ 64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */; }; 64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829982D54AEED006B9E89 /* libgmp.a */; }; 64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829992D54AEEE006B9E89 /* libffi.a */; }; - 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy-ghc9.6.3.a */; }; - 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy.a */; }; + 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.4.1-4Ybrr1jdwOoLdJnIO0aOG7-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.4.1-4Ybrr1jdwOoLdJnIO0aOG7-ghc9.6.3.a */; }; + 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.5.4.1-4Ybrr1jdwOoLdJnIO0aOG7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.4.1-4Ybrr1jdwOoLdJnIO0aOG7.a */; }; 64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */; }; 64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */; }; 64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */; }; @@ -561,8 +561,8 @@ 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTimePicker.swift; sourceTree = ""; }; 64C829982D54AEED006B9E89 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; 64C829992D54AEEE006B9E89 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; - 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy-ghc9.6.3.a"; sourceTree = ""; }; - 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy.a"; sourceTree = ""; }; + 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.4.1-4Ybrr1jdwOoLdJnIO0aOG7-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.5.4.1-4Ybrr1jdwOoLdJnIO0aOG7-ghc9.6.3.a"; sourceTree = ""; }; + 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.4.1-4Ybrr1jdwOoLdJnIO0aOG7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.5.4.1-4Ybrr1jdwOoLdJnIO0aOG7.a"; sourceTree = ""; }; 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = ""; }; 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressView.swift; sourceTree = ""; }; 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressLearnMore.swift; sourceTree = ""; }; @@ -731,8 +731,8 @@ 64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */, 64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */, 64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */, - 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy-ghc9.6.3.a in Frameworks */, - 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy.a in Frameworks */, + 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.4.1-4Ybrr1jdwOoLdJnIO0aOG7-ghc9.6.3.a in Frameworks */, + 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.5.4.1-4Ybrr1jdwOoLdJnIO0aOG7.a in Frameworks */, CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -818,8 +818,8 @@ 64C829992D54AEEE006B9E89 /* libffi.a */, 64C829982D54AEED006B9E89 /* libgmp.a */, 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */, - 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy-ghc9.6.3.a */, - 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.3.0-GwIsQfwCbNXBEEtsO80Uxy.a */, + 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.4.1-4Ybrr1jdwOoLdJnIO0aOG7-ghc9.6.3.a */, + 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.5.4.1-4Ybrr1jdwOoLdJnIO0aOG7.a */, ); path = Libraries; sourceTree = "";