From eadebccb4f68bffc492a5e1f4076c9d9951b4082 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Mon, 12 Oct 2026 22:59:42 +0100 Subject: [PATCH 01/21] nodejs: send error events to bot (#7691) * nodejs: send error events to bot * version --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> From 4b6ac88c6e808f5d22fe5fcb6937b55d91b2bc98 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Tue, 13 Oct 2026 08:40:32 +0100 Subject: [PATCH 02/21] 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 ccbdaadc5c..a138b24e4e 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -193,8 +193,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-7.1.0.10-1nt2m8GM0NE8NSirknFyt8-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-7.1.0.10-1nt2m8GM0NE8NSirknFyt8-ghc9.6.3.a */; }; - 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-7.1.0.10-1nt2m8GM0NE8NSirknFyt8.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-7.1.0.10-1nt2m8GM0NE8NSirknFyt8.a */; }; + 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-7.1.0.11-LxOKYAQgszM3XevVjyMHum-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-7.1.0.11-LxOKYAQgszM3XevVjyMHum-ghc9.6.3.a */; }; + 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-7.1.0.11-LxOKYAQgszM3XevVjyMHum.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-7.1.0.11-LxOKYAQgszM3XevVjyMHum.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 */; }; @@ -586,8 +586,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-7.1.0.10-1nt2m8GM0NE8NSirknFyt8-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-7.1.0.10-1nt2m8GM0NE8NSirknFyt8-ghc9.6.3.a"; sourceTree = ""; }; - 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-7.1.0.10-1nt2m8GM0NE8NSirknFyt8.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-7.1.0.10-1nt2m8GM0NE8NSirknFyt8.a"; sourceTree = ""; }; + 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-7.1.0.11-LxOKYAQgszM3XevVjyMHum-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-7.1.0.11-LxOKYAQgszM3XevVjyMHum-ghc9.6.3.a"; sourceTree = ""; }; + 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-7.1.0.11-LxOKYAQgszM3XevVjyMHum.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-7.1.0.11-LxOKYAQgszM3XevVjyMHum.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 = ""; }; @@ -760,8 +760,8 @@ 64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */, 64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */, 64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */, - 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-7.1.0.10-1nt2m8GM0NE8NSirknFyt8-ghc9.6.3.a in Frameworks */, - 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-7.1.0.10-1nt2m8GM0NE8NSirknFyt8.a in Frameworks */, + 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-7.1.0.11-LxOKYAQgszM3XevVjyMHum-ghc9.6.3.a in Frameworks */, + 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-7.1.0.11-LxOKYAQgszM3XevVjyMHum.a in Frameworks */, CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -848,8 +848,8 @@ 64C829992D54AEEE006B9E89 /* libffi.a */, 64C829982D54AEED006B9E89 /* libgmp.a */, 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */, - 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-7.1.0.10-1nt2m8GM0NE8NSirknFyt8-ghc9.6.3.a */, - 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-7.1.0.10-1nt2m8GM0NE8NSirknFyt8.a */, + 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-7.1.0.11-LxOKYAQgszM3XevVjyMHum-ghc9.6.3.a */, + 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-7.1.0.11-LxOKYAQgszM3XevVjyMHum.a */, ); path = Libraries; sourceTree = ""; From d673a6b02136018e49f0796da74e089ad49a0446 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Tue, 13 Oct 2026 08:42:59 +0100 Subject: [PATCH 03/21] ui: show unicode characters in address as unicode markers (#7698) * ui: show unicode characters in address as unicode markers * defence in depth for links in messages (they are non-clickable from parser) --------- Current code does not have this problem, as markdown parser rejects such links, and they are not clickable - it is needed for new markdown file viewer - this way unicode links in markdown show with unicode sequences instead of look-alike letters. --- .../Views/Chat/ChatItem/CILinkView.swift | 26 ++++++++++++++++--- .../Views/Chat/ChatItem/MsgContentView.swift | 1 + .../ios/Shared/Views/Helpers/ShareSheet.swift | 2 +- .../common/views/chat/item/TextItemView.kt | 12 ++++++--- .../simplex/common/views/helpers/Utils.kt | 18 +++++++++++-- 5 files changed, 49 insertions(+), 10 deletions(-) diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift index 4eb187bcac..3433115ceb 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift @@ -52,7 +52,7 @@ struct CILinkView: View { func openBrowserAlert(uri: String) { let (url, err) = sanitizeUri(uri) if let url { - let uriStr = url.uri.absoluteString + let uriStr = displayedUri(url.uri.absoluteString) showAlert( NSLocalizedString("Open link?", comment: "alert title"), message: uriStr.count > 160 ? "\(uriStr.prefix(160))…" : uriStr, @@ -90,9 +90,9 @@ func openBrowserAlert(uri: String) { func showInvalidLinkAlert(_ uri: String, error: String? = nil) { let message = if let error, !error.isEmpty { - error + "\n" + uri + error + "\n" + displayedUri(uri) } else { - uri + displayedUri(uri) } showAlert( NSLocalizedString("Invalid link", comment: "alert title"), @@ -101,6 +101,26 @@ func showInvalidLinkAlert(_ uri: String, error: String? = nil) { ) } +func unicodeMarker(_ s: Unicode.Scalar) -> String { + String(format: "[U+%04X]", s.value) +} + +func displayedUri(_ uri: String) -> String { + let authority = uri.range(of: "://").map { scheme in + scheme.upperBound ..< (uri[scheme.upperBound...].firstIndex(where: { "/?#".contains($0) }) ?? uri.endIndex) + } + var shown = "" + for i in uri.unicodeScalars.indices { + let s = uri.unicodeScalars[i] + if s.properties.generalCategory == .format || (!s.isASCII && authority?.contains(i) == true) { + shown += unicodeMarker(s) + } else { + shown.unicodeScalars.append(s) + } + } + return shown +} + func sanitizeUri(_ s: String) -> (url: (uri: URL, sanitizedUri: URL?)?, error: String?) { let parsed = parseSanitizeUri(s, safe: false) return if let uri = URL(string: s), let uriInfo = parsed?.uriInfo { diff --git a/apps/ios/Shared/Views/Chat/ChatItem/MsgContentView.swift b/apps/ios/Shared/Views/Chat/ChatItem/MsgContentView.swift index c9d8858fa1..f95e31369e 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/MsgContentView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/MsgContentView.swift @@ -490,6 +490,7 @@ func messageText( attrs[webLinkAttrKey] = true handleTaps = true } + t = displayedUri(t) case let .hyperLink(text, uri): attrs = linkAttrs() if let text { t = text } diff --git a/apps/ios/Shared/Views/Helpers/ShareSheet.swift b/apps/ios/Shared/Views/Helpers/ShareSheet.swift index 56e437e5c8..ebe74b2a50 100644 --- a/apps/ios/Shared/Views/Helpers/ShareSheet.swift +++ b/apps/ios/Shared/Views/Helpers/ShareSheet.swift @@ -98,7 +98,7 @@ func openExternalLink(_ url: URL) { } else { showAlert( title: NSLocalizedString("Open external link?", comment: "alert title"), - message: s, + message: displayedUri(s), buttonTitle: NSLocalizedString("Open", comment: "alert button"), buttonAction: { UIApplication.shared.open(url) }, cancelButton: true 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 da64862c03..c198451464 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 @@ -96,6 +96,7 @@ fun itemSegmentDisplayText(ft: FormattedText, ci: ChatItem, linkMode: SimplexLin } else if (mention != null) mentionText(ft.format.memberName) else ft.text } + is Format.Uri -> displayedUri(ft.text) is Format.HyperLink -> ft.format.showText ?: ft.text is Format.SimplexLink -> { val t = ft.format.showText @@ -283,7 +284,7 @@ fun MarkdownText ( val s = ft.text val link = if (s.startsWith("http://") || s.startsWith("https://")) s else "https://$s" withAnnotation(tag = "WEB_URL", annotation = link) { - withStyle(ftStyle) { append(ft.text) } + withStyle(ftStyle) { append(displayedUri(ft.text)) } } } is Format.HyperLink -> { @@ -535,19 +536,22 @@ fun openBrowserAlert(uri: String, uriHandler: UriHandler) { if (res == null) { showInvalidLinkAlert(uri, err) } else { - val message = if (uri.count() > 160) uri.substring(0, 159) + "…" else uri + val shownUri = displayedUri(uri) + val message = if (shownUri.count() > 160) shownUri.substring(0, 159) + "…" else shownUri val sanitizedUri = res.second if (sanitizedUri == null) { AlertManager.shared.showAlertDialog( generalGetString(MR.strings.privacy_chat_list_open_web_link_question), message, confirmText = generalGetString(MR.strings.open_verb), - onConfirm = { safeOpenUri(uri, uriHandler) } + onConfirm = { safeOpenUri(uri, uriHandler) }, + parseHtml = false ) } else { AlertManager.shared.showAlertDialogButtonsColumn( generalGetString(MR.strings.privacy_chat_list_open_web_link_question), message, + parseHtml = false, buttons = { Column { SectionItemView({ @@ -585,7 +589,7 @@ fun safeOpenUri(uri: String, uriHandler: UriHandler) { } fun showInvalidLinkAlert(uri: String, error: String? = null) { - val message = if (error.isNullOrEmpty()) { uri } else { error + "\n" + uri } + val message = if (error.isNullOrEmpty()) { displayedUri(uri) } else { error + "\n" + displayedUri(uri) } AlertManager.shared.showAlertMsg(generalGetString(MR.strings.error_parsing_uri_title), message) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt index dfe33e3595..6b69a3820f 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt @@ -617,13 +617,27 @@ fun UriHandler.openExternalLink(uri: String) { } else { AlertManager.shared.showAlertDialog( title = generalGetString(MR.strings.open_external_link_title), - text = uri, + text = displayedUri(uri), confirmText = generalGetString(MR.strings.open_verb), - onConfirm = { uriHandler.openUriCatching(uri) } + onConfirm = { uriHandler.openUriCatching(uri) }, + parseHtml = false ) } } +val invisibleChar = Regex("\\p{Cf}") + +private val nonAsciiChar = Regex("[^\\x00-\\x7F]") + +fun unicodeMarker(c: String): String = "[U+" + Integer.toHexString(c.codePointAt(0)).uppercase().padStart(4, '0') + "]" + +fun displayedUri(uri: String): String { + val authorityStart = uri.indexOf("://").takeIf { it >= 0 }?.plus(3) ?: uri.length + val authorityEnd = uri.indexOfAny(charArrayOf('/', '?', '#'), authorityStart).takeIf { it >= 0 } ?: uri.length + val authority = uri.substring(authorityStart, authorityEnd).replace(nonAsciiChar) { unicodeMarker(it.value) } + return uri.replaceRange(authorityStart, authorityEnd, authority).replace(invisibleChar) { unicodeMarker(it.value) } +} + fun IntSize.Companion.Saver(): Saver = Saver( save = { it.width to it.height }, restore = { IntSize(it.first, it.second) } From 1bd7c95006a82a92e8d19d233d0d37e72ae8ba24 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Tue, 13 Oct 2026 08:53:04 +0100 Subject: [PATCH 04/21] ui: additional check for plain domains (#7699) Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --- apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift | 7 +++---- .../kotlin/chat/simplex/common/views/helpers/Utils.kt | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift index 3433115ceb..42259361b7 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CILinkView.swift @@ -106,13 +106,12 @@ func unicodeMarker(_ s: Unicode.Scalar) -> String { } func displayedUri(_ uri: String) -> String { - let authority = uri.range(of: "://").map { scheme in - scheme.upperBound ..< (uri[scheme.upperBound...].firstIndex(where: { "/?#".contains($0) }) ?? uri.endIndex) - } + let authorityStart = uri.range(of: "://")?.upperBound ?? uri.startIndex + let authority = authorityStart ..< (uri[authorityStart...].firstIndex(where: { "/?#".contains($0) }) ?? uri.endIndex) var shown = "" for i in uri.unicodeScalars.indices { let s = uri.unicodeScalars[i] - if s.properties.generalCategory == .format || (!s.isASCII && authority?.contains(i) == true) { + if s.properties.generalCategory == .format || (!s.isASCII && authority.contains(i)) { shown += unicodeMarker(s) } else { shown.unicodeScalars.append(s) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt index 6b69a3820f..81a64cb527 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/Utils.kt @@ -632,7 +632,7 @@ private val nonAsciiChar = Regex("[^\\x00-\\x7F]") fun unicodeMarker(c: String): String = "[U+" + Integer.toHexString(c.codePointAt(0)).uppercase().padStart(4, '0') + "]" fun displayedUri(uri: String): String { - val authorityStart = uri.indexOf("://").takeIf { it >= 0 }?.plus(3) ?: uri.length + val authorityStart = uri.indexOf("://").takeIf { it >= 0 }?.plus(3) ?: 0 val authorityEnd = uri.indexOfAny(charArrayOf('/', '?', '#'), authorityStart).takeIf { it >= 0 } ?: uri.length val authority = uri.substring(authorityStart, authorityEnd).replace(nonAsciiChar) { unicodeMarker(it.value) } return uri.replaceRange(authorityStart, authorityEnd, authority).replace(invisibleChar) { unicodeMarker(it.value) } From 7d870c6ff515d97ed93b81d2964ffd9f310d82f4 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Tue, 13 Oct 2026 11:24:14 +0100 Subject: [PATCH 05/21] ui: open file after tap when it is received (#7700) * ui: open file after tap when it is received * fix ios * simplify * simplify * better --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --- apps/ios/Shared/Model/ChatModel.swift | 17 +++++ apps/ios/Shared/Model/SimpleXAPI.swift | 3 + .../Views/Chat/ChatItem/CIFileView.swift | 7 +- .../Views/Chat/ChatItem/CIImageView.swift | 1 + .../Views/Chat/ChatItem/CIVideoView.swift | 20 ++++++ .../Views/Chat/ChatItem/CIVoiceView.swift | 5 ++ .../chat/simplex/common/model/ChatModel.kt | 15 +++++ .../common/views/chat/item/CIFileView.kt | 24 +++++-- .../common/views/chat/item/CIImageView.kt | 10 +++ .../common/views/chat/item/CIVideoView.kt | 66 +++++++++++-------- .../common/views/chat/item/CIVoiceView.kt | 3 +- 11 files changed, 132 insertions(+), 39 deletions(-) diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index 7c1e8e65ab..1ee79e1c4f 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -474,10 +474,27 @@ final class ChatModel: ObservableObject { // another in its place; only the badge alert shows regardless. Set while rendering, so not published. var chatListBanner: ChatListBanner? + var fileToOpen: (fileId: Int64, chatId: String?, open: (CIFile) -> Void)? + func bannerSlotFree(for banner: ChatListBanner) -> Bool { chatListBanner == nil || chatListBanner == banner } + func openWhenReceived(_ file: CIFile, open: @escaping (CIFile) -> Void) { + fileToOpen = (fileId: file.fileId, chatId: chatId, open: open) + } + + func receivedFileToOpen(_ file: CIFile) -> Bool { + if let f = fileToOpen { f.fileId == file.fileId && f.chatId == chatId } else { false } + } + + func openReceivedFile(_ file: CIFile) { + if let f = fileToOpen, receivedFileToOpen(file) { + fileToOpen = nil + f.open(file) + } + } + static let shared = ChatModel() let im = ItemsModel.shared diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index d04ccf5b03..3ec9c34f8e 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -2873,6 +2873,9 @@ func processReceivedMsg(_ res: ChatEvent) async { await chatItemSimpleUpdate(user, aChatItem) case let .rcvFileComplete(user, aChatItem): await chatItemSimpleUpdate(user, aChatItem) + if active(user), let file = aChatItem.chatItem.file, m.receivedFileToOpen(file) { + await MainActor.run { m.openReceivedFile(file) } + } case let .rcvFileSndCancelled(user, aChatItem, _): await chatItemSimpleUpdate(user, aChatItem) Task { cleanupFile(aChatItem) } diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift index 8bb76ca155..59ab1c24e0 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift @@ -26,7 +26,7 @@ struct CIFileView: View { var body: some View { if smallViewSize != nil { fileIndicator() - .simultaneousGesture(TapGesture().onEnded(fileAction)) + .simultaneousGesture(TapGesture().onEnded { fileAction(file) }) } else { // reserve exact space for the overlaid meta (timestamp + all icons), rendered transparently - matches MsgContentView let encrypted: Bool? = if let fileSource = file?.fileSource { fileSource.cryptoArgs != nil } else { nil } @@ -56,7 +56,7 @@ struct CIFileView: View { .padding(.bottom, 6) .padding(.leading, 10) .padding(.trailing, 12) - .simultaneousGesture(TapGesture().onEnded(fileAction)) + .simultaneousGesture(TapGesture().onEnded { fileAction(file) }) .disabled(!itemInteractive) } } @@ -85,7 +85,7 @@ struct CIFileView: View { return false } - private func fileAction() { + private func fileAction(_ file: CIFile?) { logger.debug("CIFileView fileAction") if let file = file { switch (file.fileStatus) { @@ -93,6 +93,7 @@ struct CIFileView: View { if let prohibited = file.fileProhibited { showProhibitedFileAlert(file, prohibited) } else { + m.openWhenReceived(file) { fileAction($0) } Task { logger.debug("CIFileView fileAction - in .rcvInvitation, .rcvAborted, in Task") if let user = m.currentUser { diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift index fb6ccb1ea3..469fc5a3fc 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIImageView.swift @@ -54,6 +54,7 @@ struct CIImageView: View { if let prohibited = file.fileProhibited { showProhibitedFileAlert(file, prohibited) } else { + m.openWhenReceived(file) { _ in showFullScreenImage = true } Task { if let user = m.currentUser { await receiveFile(user: user, fileId: file.fileId) diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift index 0df17dfe3d..339d119362 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIVideoView.swift @@ -120,6 +120,25 @@ struct CIVideoView: View { } } + private func playReceived(_ file: CIFile) { + setupPlayer(file) + if urlDecrypted == nil { + decrypt(file: file, completed: playVideo) + } else { + playVideo() + } + } + + private func playVideo() { + if smallView { + showFullScreenPlayer = urlDecrypted != nil + } else if let urlDecrypted { + m.stopPreviousRecPlay = urlDecrypted + videoPlaying = true + player?.play() + } + } + private func setupPlayer(_ file: CIFile?) { let newUrl = getLoadedVideo(file) if newUrl == url { @@ -424,6 +443,7 @@ struct CIVideoView: View { if let prohibited = file.fileProhibited { showProhibitedFileAlert(file, prohibited) } else { + m.openWhenReceived(file, open: playReceived) Task { if let user = m.currentUser { await receiveFile(user, file.fileId, false, false) diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIVoiceView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIVoiceView.swift index 64fa2a4dd3..6c89a5f788 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIVoiceView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIVoiceView.swift @@ -303,6 +303,11 @@ struct VoiceMessagePlayer: View { private func downloadButton(_ recordingFile: CIFile, _ icon: String) -> some View { playPauseIcon(icon, theme.colors.primary) .simultaneousGesture(TapGesture().onEnded { + chatModel.openWhenReceived(recordingFile) { file in + if let recordingSource = getLoadedFileSource(file) { + startPlayback(recordingSource) + } + } Task { if let user = chatModel.currentUser { await receiveFile(user: user, fileId: recordingFile.fileId) 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 c33a077ae9..1afea4541b 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 @@ -132,6 +132,8 @@ object BadgeModel { enum class ChatListBanner { BadgeExpired, BadgeIssueFailed, BadgePitch, GetStake } +data class FileToOpen(val fileId: Long, val inChatList: Boolean) + /* * Without this annotation an animation from ChatList to ChatView has 1 frame per the whole animation. Don't delete it * */ @@ -205,8 +207,21 @@ object ChatModel { // another in its place; only the badge alert shows regardless. Set while rendering, so not a state. var chatListBanner: ChatListBanner? = null + var fileToOpen: FileToOpen? = null + fun bannerSlotFree(banner: ChatListBanner): Boolean = chatListBanner == null || chatListBanner == banner + fun openWhenReceived(file: CIFile, inChatList: Boolean) { + fileToOpen = FileToOpen(file.fileId, inChatList) + } + + fun receivedFileToOpen(file: CIFile, inChatList: Boolean): Boolean { + val f = fileToOpen + if (f == null || f.fileId != file.fileId || f.inChatList != inChatList || !file.loaded) return false + fileToOpen = null + return !inChatList || appPlatform.isDesktop || chatId.value == null + } + // preferences val notificationPreviewMode by lazy { mutableStateOf( diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt index a3c2b63f8f..1523e2ff22 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt @@ -80,6 +80,7 @@ fun CIFileView( if (prohibited != null) { showProhibitedFileAlert(file, prohibited) } else { + chatModel.openWhenReceived(file, smallView) receiveFile(file.fileId) } } @@ -188,16 +189,18 @@ fun CIFileView( val encrypted = file.fileSource.cryptoArgs != null SaveOrOpenFileMenu(showOpenSaveMenu, encrypted, ext, File(loadedFilePath).toURI(), file.fileSource, saveFile = { fileAction() }) } + val onClick: () -> Unit = { + if (appPlatform.isAndroid && loadedFilePath != null) { + showOpenSaveMenu.value = true + } else { + fileAction() + } + } + if (file != null) OpenWhenReceivedEffect(file, smallView, onClick) Row( Modifier .combinedClickable( - onClick = { - if (appPlatform.isAndroid && loadedFilePath != null) { - showOpenSaveMenu.value = true - } else { - fileAction() - } - }, + onClick = onClick, onLongClick = { showMenu.value = true } ) .padding(if (smallView) PaddingValues() else PaddingValues(top = 4.sp.toDp(), bottom = 6.sp.toDp(), start = 6.sp.toDp(), end = 12.sp.toDp())), @@ -239,6 +242,13 @@ fun CIFileView( // the core decides whether a received file is above the size the sender's badge allows fun fileSizeValid(file: CIFile): Boolean = file.fileProhibited == null +@Composable +fun OpenWhenReceivedEffect(file: CIFile, inChatList: Boolean, open: () -> Unit) { + KeyChangeEffect(file.loaded) { + if (chatModel.receivedFileToOpen(file, inChatList)) open() + } +} + fun showProhibitedFileAlert(file: CIFile, prohibited: FileProhibited) { val badgeIssue = when (prohibited.badgeStatus) { null, BadgeStatus.Active -> "" diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.kt index d5ea44126b..a49884beeb 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.kt @@ -173,6 +173,15 @@ fun CIImageView( return null } + val view = LocalMultiplatformView() + if (file != null) { + OpenWhenReceivedEffect(file, smallView) { + hideKeyboard(view) + ModalManager.fullscreen.showCustomModal(animated = false) { close -> + ImageFullScreenView(imageProvider, close) + } + } + } Box( Modifier.layoutId(CHAT_IMAGE_LAYOUT_ID) .then( @@ -222,6 +231,7 @@ fun CIImageView( if (file.fileProhibited != null) { showProhibitedFileAlert(file, file.fileProhibited) } else { + chatModel.openWhenReceived(file, smallView) receiveFile(file.fileId) } file.fileStatus is CIFileStatus.RcvAccepted -> diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIVideoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIVideoView.kt index 21f406db18..62e1b9f737 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIVideoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIVideoView.kt @@ -70,6 +70,8 @@ fun CIVideoView( } } val f = filePath.value + val autoPlay = remember { mutableStateOf(false) } + if (file != null) OpenWhenReceivedEffect(file, smallView) { autoPlay.value = true } if (file != null && f != null) { val view = LocalMultiplatformView() val openFullscreen = { @@ -80,7 +82,6 @@ fun CIVideoView( } val uri = remember(filePath) { getAppFileUri(f.substringAfterLast(File.separator)) } - val autoPlay = remember { mutableStateOf(false) } val uriDecrypted = remember(filePath) { mutableStateOf(if (file.fileSource?.cryptoArgs == null) uri else file.fileSource.decryptedGet()) } val decrypted = uriDecrypted.value if (decrypted != null && smallView) { @@ -98,7 +99,7 @@ fun CIVideoView( if (file != null) { when (file.fileStatus) { CIFileStatus.RcvInvitation, CIFileStatus.RcvAborted -> - receiveFileIfValidSize(file, receiveFile) + receiveFileIfValidSize(file, smallView, receiveFile) CIFileStatus.RcvAccepted -> when (file.fileProtocol) { FileProtocol.XFTP -> @@ -128,7 +129,7 @@ fun CIVideoView( DurationProgress(file, remember { mutableStateOf(false) }, remember { mutableStateOf(duration * 1000L) }, remember { mutableStateOf(0L) }/*, soundEnabled*/) } if (showDownloadButton(file?.fileStatus) && !blurred.value && file != null) { - PlayButton(error = false, sizeMultiplier, { showMenu.value = true }) { receiveFileIfValidSize(file, receiveFile) } + PlayButton(error = false, sizeMultiplier, { showMenu.value = true }) { receiveFileIfValidSize(file, smallView, receiveFile) } } } } @@ -156,22 +157,26 @@ private fun VideoViewEncrypted( ) { var decryptionInProgress by rememberSaveable(file.fileName) { mutableStateOf(false) } val onLongClick = { showMenu.value = true } + val play: () -> Unit = { + decryptionInProgress = true + withBGApi { + try { + uriUnencrypted.value = file.fileSource?.decryptedGetOrCreate() + autoPlay.value = uriUnencrypted.value != null + } finally { + decryptionInProgress = false + } + } + } + LaunchedEffect(autoPlay.value) { + if (autoPlay.value) play() + } Box { VideoPreviewImageView(defaultPreview, smallView = false, blurred = blurred, if (decryptionInProgress) {{}} else openFullscreen, onLongClick) if (decryptionInProgress) { VideoDecryptionProgress(1f, onLongClick = onLongClick) } else if (!blurred.value) { - PlayButton(false, 1f, onLongClick = onLongClick) { - decryptionInProgress = true - withBGApi { - try { - uriUnencrypted.value = file.fileSource?.decryptedGetOrCreate() - autoPlay.value = uriUnencrypted.value != null - } finally { - decryptionInProgress = false - } - } - } + PlayButton(false, 1f, onLongClick = onLongClick, onClick = play) } DurationProgress(file, remember { mutableStateOf(false) }, remember { mutableStateOf(defaultDuration) }, remember { mutableStateOf(0L) }) } @@ -190,22 +195,26 @@ private fun SmallVideoViewEncrypted( ) { var decryptionInProgress by rememberSaveable(file.fileName) { mutableStateOf(false) } val onLongClick = { showMenu.value = true } + val play: () -> Unit = { + decryptionInProgress = true + withBGApi { + try { + uriUnencrypted.value = file.fileSource?.decryptedGetOrCreate() + autoPlay.value = uriUnencrypted.value != null + } finally { + decryptionInProgress = false + } + } + } + LaunchedEffect(autoPlay.value) { + if (autoPlay.value) play() + } Box { VideoPreviewImageView(defaultPreview, smallView = true, blurred = blurred, onClick = if (decryptionInProgress) {{}} else openFullscreen, onLongClick = onLongClick) if (decryptionInProgress) { VideoDecryptionProgress(sizeMultiplier, onLongClick = onLongClick) } else if (!file.showStatusIconInSmallView && !blurHidesMedia(true, blurred)) { - PlayButton(false, sizeMultiplier, onLongClick = onLongClick) { - decryptionInProgress = true - withBGApi { - try { - uriUnencrypted.value = file.fileSource?.decryptedGetOrCreate() - autoPlay.value = uriUnencrypted.value != null - } finally { - decryptionInProgress = false - } - } - } + PlayButton(false, sizeMultiplier, onLongClick = onLongClick, onClick = play) } } } @@ -240,7 +249,7 @@ private fun SmallVideoView( PlayButton(brokenVideo, sizeMultiplier, onLongClick = {}, onClick = openFullscreen) } } - LaunchedEffect(uri) { + LaunchedEffect(uri, autoPlay.value) { if (autoPlay.value) openFullscreen() } } @@ -272,7 +281,7 @@ private fun VideoView( player.stop() } val showPreview = remember { derivedStateOf { !videoPlaying.value || progress.value == 0L } } - LaunchedEffect(uri) { + LaunchedEffect(uri, autoPlay.value) { if (autoPlay.value) play() } // Drop autoPlay only when show preview changes to prevent blinking of the view @@ -566,10 +575,11 @@ private fun fileStatusIcon(file: CIFile?, smallView: Boolean) { private fun showDownloadButton(status: CIFileStatus?): Boolean = status is CIFileStatus.RcvInvitation || status is CIFileStatus.RcvAborted -private fun receiveFileIfValidSize(file: CIFile, receiveFile: (Long) -> Unit) { +private fun receiveFileIfValidSize(file: CIFile, smallView: Boolean, receiveFile: (Long) -> Unit) { if (file.fileProhibited != null) { showProhibitedFileAlert(file, file.fileProhibited) } else { + chatModel.openWhenReceived(file, smallView) receiveFile(file.fileId) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIVoiceView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIVoiceView.kt index 3b3c835e0a..5aeefd9668 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIVoiceView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIVoiceView.kt @@ -87,7 +87,7 @@ fun CIVoiceView( durationText(time / 1000) } } - VoiceLayout(file, ci, text, audioPlaying, progress, duration, brokenAudio, sent, hasText, timedMessagesTTL, showViaProxy, showTimestamp, sizeMultiplier, play, pause, longClick, receiveFile) { + VoiceLayout(file, ci, text, audioPlaying, progress, duration, brokenAudio, sent, hasText, timedMessagesTTL, showViaProxy, showTimestamp, sizeMultiplier, play, pause, longClick, { chatModel.openWhenReceived(file, smallView); receiveFile(it) }) { AudioPlayer.seekTo(it, progress, fileSource.value?.filePath) } if (smallView) { @@ -95,6 +95,7 @@ fun CIVoiceView( AudioPlayer.stop() } } + OpenWhenReceivedEffect(file, smallView, play) } else if (smallView) { VoiceMsgIndicator(null, false, sent, hasText, null, null, false, sizeMultiplier, {}, {}, longClick, receiveFile) } else { From 2f4a443dda2600278446fd082f73275de2f13ad1 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Tue, 13 Oct 2026 14:38:37 +0100 Subject: [PATCH 06/21] ui: markdown viewer (#7702) * ui: markdown viewer * simplify * fixes * plan * ios sheet, center modal on desktop --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --- .../Views/Chat/ChatItem/CIFileView.swift | 14 +- .../Chat/ChatItem/MarkdownFileView.swift | 215 ++++++++++++ .../NetworkAndServers/ConditionsWebView.swift | 37 +- .../Views/UserSettings/SettingsView.swift | 2 + apps/ios/SimpleX.xcodeproj/project.pbxproj | 4 + apps/ios/spec/client/chat-view.md | 6 + apps/multiplatform/common/build.gradle.kts | 4 +- .../simplex/common/platform/Images.android.kt | 9 + .../common/views/helpers/Utils.android.kt | 3 +- .../kotlin/chat/simplex/common/App.kt | 5 +- .../chat/simplex/common/model/SimpleXAPI.kt | 2 + .../chat/simplex/common/platform/Images.kt | 1 + .../common/views/chat/item/CIFileView.kt | 4 +- .../views/chat/item/MarkdownFileView.kt | 284 ++++++++++++++++ .../views/chatlist/ChatListNavLinkView.kt | 2 +- .../simplex/common/views/helpers/ModalView.kt | 5 +- .../networkAndServers/OperatorView.kt | 33 +- .../moko-resources/base/strings.xml | 3 + .../simplex/common/platform/Images.desktop.kt | 9 + .../views/chat/item/CIImageView.desktop.kt | 2 +- apps/multiplatform/product/README.md | 4 +- apps/multiplatform/spec/client/chat-view.md | 1 + apps/multiplatform/spec/client/navigation.md | 6 +- plans/2026-10-11-markdown-file-viewer.md | 319 ++++++++++++++++++ 24 files changed, 941 insertions(+), 33 deletions(-) create mode 100644 apps/ios/Shared/Views/Chat/ChatItem/MarkdownFileView.swift create mode 100644 apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/MarkdownFileView.kt create mode 100644 plans/2026-10-11-markdown-file-viewer.md diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift index 59ab1c24e0..5c65a310b5 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIFileView.swift @@ -118,7 +118,7 @@ struct CIFileView: View { case .rcvComplete: logger.debug("CIFileView fileAction - in .rcvComplete") if let fileSource = getLoadedFileSource(file) { - saveCryptoFile(fileSource) + openFile(file, fileSource) } case let .rcvError(rcvFileError): logger.debug("CIFileView fileAction - in .rcvError") @@ -129,12 +129,12 @@ struct CIFileView: View { case .sndStored: logger.debug("CIFileView fileAction - in .sndStored") if file.fileProtocol == .local, let fileSource = getLoadedFileSource(file) { - saveCryptoFile(fileSource) + openFile(file, fileSource) } case .sndComplete: logger.debug("CIFileView fileAction - in .sndComplete") if let fileSource = getLoadedFileSource(file) { - saveCryptoFile(fileSource) + openFile(file, fileSource) } case let .sndError(sndFileError): logger.debug("CIFileView fileAction - in .sndError") @@ -147,6 +147,14 @@ struct CIFileView: View { } } + private func openFile(_ file: CIFile, _ fileSource: CryptoFile) { + if isMarkdownFile(file) { + showMarkdownFile(file) + } else { + saveCryptoFile(fileSource) + } + } + @ViewBuilder private func fileIndicator() -> some View { if let file = file { switch file.fileStatus { diff --git a/apps/ios/Shared/Views/Chat/ChatItem/MarkdownFileView.swift b/apps/ios/Shared/Views/Chat/ChatItem/MarkdownFileView.swift new file mode 100644 index 0000000000..aa7e827d6b --- /dev/null +++ b/apps/ios/Shared/Views/Chat/ChatItem/MarkdownFileView.swift @@ -0,0 +1,215 @@ +import SwiftUI +import SimpleXChat +import Ink + +private let MAX_MARKDOWN_VIEW_SIZE: Int64 = 1_048_576 +private let MAX_MARKDOWN_LINE_LENGTH = 15_000 +private let MAX_MARKDOWN_NESTING = 16 +private let MAX_MARKDOWN_LINK_NESTING = 4 +private let MAX_MARKDOWN_TABLE_ROWS = 200 +private let MAX_MARKDOWN_IMAGE_DIMENSION: CGFloat = 4320 +private let MAX_MARKDOWN_IMAGE_PIXELS: CGFloat = 2160 * 4320 + +func isMarkdownFile(_ file: CIFile) -> Bool { + let ext = (file.fileName as NSString).pathExtension.lowercased() + return file.fileSize <= MAX_MARKDOWN_VIEW_SIZE && (ext == "md" || ext == "markdown") +} + +func showMarkdownFile(_ file: CIFile) { + guard let fileSource = getLoadedFileSource(file) else { return } + Task { + do { + let data = try getFileData(getAppFilePath(fileSource.filePath), fileSource.cryptoArgs) + let text = decodedText(data) + let lines = text.components(separatedBy: "\n") + let html = withinMarkdownLimits(lines) + ? MarkdownParser(modifiers: [Modifier(target: .html) { escapedHTML($0.markdown) }, imagesWithinBudget()]).html(from: text) + : nil + let shownLines = lines.flatMap(chunkedLine) + let hasBidiControls = text.unicodeScalars.contains(where: isBidiControl) + await MainActor.run { + showAppSheet { + MarkdownFileView(file: file, fileSource: fileSource, lines: shownLines, html: html, hasBidiControls: hasBidiControls) + } + } + } catch { + await MainActor.run { + showAlert(NSLocalizedString("Error reading file", comment: "alert title"), message: error.localizedDescription) + } + } + } +} + +private func decodedText(_ data: Data) -> String { + var text = String.UnicodeScalarView() + var parser = Unicode.UTF8.ForwardParser() + var bytes = data.makeIterator() + while true { + switch parser.parseScalar(from: &bytes) { + case let .valid(encoded): text.append(replacingControlChar(Unicode.UTF8.decode(encoded))) + case let .error(length): text.append(contentsOf: repeatElement("?" as Unicode.Scalar, count: length)) + case .emptyInput: return String(text).replacingOccurrences(of: "\r\n", with: "\n").replacingOccurrences(of: "\r", with: "\n") + } + } +} + +private func replacingControlChar(_ s: Unicode.Scalar) -> Unicode.Scalar { + s.value < 0x20 && s != "\t" && s != "\n" && s != "\r" ? "?" : s +} + +private func withinMarkdownLimits(_ lines: [String]) -> Bool { + var tableRows = 0 + var linkNesting = 0 + for line in lines { + tableRows = line.contains("|") ? tableRows + 1 : 0 + if line.count > MAX_MARKDOWN_LINE_LENGTH || nestingLevel(line) > MAX_MARKDOWN_NESTING || tableRows > MAX_MARKDOWN_TABLE_ROWS || !linkNestingWithinLimit(line, &linkNesting) { + return false + } + } + return true +} + +private func linkNestingWithinLimit(_ line: String, _ nesting: inout Int) -> Bool { + if line.isEmpty { nesting = 0 } + for c in line { + if c == "[" { + nesting += 1 + if nesting > MAX_MARKDOWN_LINK_NESTING { return false } + } else if c == "]" && nesting > 0 { + nesting -= 1 + } + } + return true +} + +private func nestingLevel(_ line: String) -> Int { + let content = line.drop(while: { $0 == " " || $0 == "\t" || $0 == ">" }) + let prefix = line[.." }.count + let listItem = content.first.map { "-*+".contains($0) || $0.isNumber } ?? false + return quotes + (listItem ? (prefix.count - quotes) / 2 : 0) +} + +private func chunkedLine(_ line: String) -> [String] { + let chars = Array(line) + return stride(from: 0, to: max(chars.count, 1), by: MAX_MARKDOWN_LINE_LENGTH).map { + String(chars[$0 ..< min($0 + MAX_MARKDOWN_LINE_LENGTH, chars.count)]) + } +} + +private func escapedHTML(_ s: Substring) -> String { + s.replacingOccurrences(of: "&", with: "&") + .replacingOccurrences(of: "<", with: "<") + .replacingOccurrences(of: ">", with: ">") +} + +private func imagesWithinBudget() -> Modifier { + var pixels = MAX_MARKDOWN_IMAGE_PIXELS + return Modifier(target: .images) { image in + let src = image.html.dropFirst("" + } +} + +private func isBidiControl(_ s: Unicode.Scalar) -> Bool { + [0x061C, 0x200E, 0x200F].contains(s.value) || (0x202A...0x202E).contains(s.value) || (0x2066...0x2069).contains(s.value) +} + +private func withInvisibleCharMarkers(_ line: String) -> Text { + var text = Text(verbatim: "") + var plain = "" + for s in line.unicodeScalars { + if s.properties.generalCategory == .format { + text = text + Text(verbatim: plain) + Text(verbatim: unicodeMarker(s)).foregroundColor(.orange) + plain = "" + } else { + plain.unicodeScalars.append(s) + } + } + return text + Text(verbatim: plain) +} + +private func openMarkdownFileLink(_ url: URL) { + let uri = url.absoluteString + let parsed = parseSimpleXMarkdown(uri) + let format = parsed?.count == 1 ? parsed?.first?.format : nil + switch format { + case let .simplexLink(_, _, simplexUri, _)?: + if let simplexUrl = URL(string: simplexUri) { + ChatModel.shared.appOpenUrl = simplexUrl + } else { + showInvalidLinkAlert(uri) + } + case .uri? where uri.hasPrefix("https://") || uri.hasPrefix("http://"): + openBrowserAlert(uri: uri) + default: + showInvalidLinkAlert(uri) + } +} + +struct MarkdownFileView: View { + @AppStorage(DEFAULT_MARKDOWN_FILE_TEXT_VIEW) private var textView = false + let file: CIFile + let fileSource: CryptoFile + let lines: [String] + let html: String? + let hasBidiControls: Bool + + var body: some View { + NavigationView { + VStack(spacing: 0) { + if hasBidiControls { + Text("This file has text direction characters. The text can be shown in a different order than it is written.") + .foregroundColor(.orange) + .padding() + } + if let html, !textView { + ConditionsWebView(html: html, openLink: openMarkdownFileLink) + .padding(.horizontal) + } else { + ScrollView { + LazyVStack(alignment: .leading, spacing: 0) { + ForEach(lines.indices, id: \.self) { i in + withInvisibleCharMarkers(lines[i]) + .font(.system(.body, design: .monospaced)) + .textSelection(.enabled) + } + } + .padding() + } + } + } + .modifier(ThemedBackground()) + .navigationTitle(file.fileName) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItemGroup(placement: .navigationBarTrailing) { + if html != nil { + Button { + textView.toggle() + } label: { + Image(systemName: textView ? "doc.richtext" : "chevron.left.forwardslash.chevron.right") + } + } + Button { + saveCryptoFile(fileSource) + } label: { + Image(systemName: "square.and.arrow.up") + } + } + } + } + .navigationViewStyle(.stack) + } +} diff --git a/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ConditionsWebView.swift b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ConditionsWebView.swift index 5abbbf8d2e..6a05851c63 100644 --- a/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ConditionsWebView.swift +++ b/apps/ios/Shared/Views/UserSettings/NetworkAndServers/ConditionsWebView.swift @@ -12,11 +12,15 @@ import WebKit struct ConditionsWebView: UIViewRepresentable { @State var html: String + var openLink: (URL) -> Void = openExternalLink @EnvironmentObject var theme: AppTheme @State var pageLoaded = false func makeUIView(context: Context) -> WKWebView { - let view = WKWebView() + let config = WKWebViewConfiguration() + config.defaultWebpagePreferences.allowsContentJavaScript = false + config.websiteDataStore = .nonPersistent() + let view = WKWebView(frame: .zero, configuration: config) view.backgroundColor = .clear view.isOpaque = false view.navigationDelegate = context.coordinator @@ -35,8 +39,9 @@ struct ConditionsWebView: UIViewRepresentable { } private func loadPage(_ webView: WKWebView) { + let nonce = UUID().uuidString let styles = """ - """ - let head = "\(styles)" + let csp = "" + let head = "\(csp)\(styles)" webView.loadHTMLString(head + html, baseURL: nil) DispatchQueue.main.async { pageLoaded = true @@ -58,10 +77,16 @@ struct ConditionsWebView: UIViewRepresentable { } func makeCoordinator() -> Cordinator { - Cordinator() + Cordinator(openLink: openLink) } class Cordinator: NSObject, WKNavigationDelegate { + let openLink: (URL) -> Void + + init(openLink: @escaping (URL) -> Void) { + self.openLink = openLink + } + func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { @@ -71,9 +96,9 @@ struct ConditionsWebView: UIViewRepresentable { switch navigationAction.navigationType { case .linkActivated: decisionHandler(.cancel) - openExternalLink(url) + openLink(url) default: - decisionHandler(.allow) + decisionHandler(url.scheme == "about" ? .allow : .cancel) } } } diff --git a/apps/ios/Shared/Views/UserSettings/SettingsView.swift b/apps/ios/Shared/Views/UserSettings/SettingsView.swift index 6cf9b5abe6..f7042b0c39 100644 --- a/apps/ios/Shared/Views/UserSettings/SettingsView.swift +++ b/apps/ios/Shared/Views/UserSettings/SettingsView.swift @@ -84,6 +84,7 @@ let DEFAULT_SHOW_DELETE_CONVERSATION_NOTICE = "showDeleteConversationNotice" let DEFAULT_SHOW_DELETE_CONTACT_NOTICE = "showDeleteContactNotice" let DEFAULT_SHOW_SENT_VIA_RPOXY = "showSentViaProxy" let DEFAULT_SHOW_SUBSCRIPTION_PERCENTAGE = "showSubscriptionPercentage" +let DEFAULT_MARKDOWN_FILE_TEXT_VIEW = "markdownFileTextView" let DEFAULT_CURRENT_THEME = "currentTheme" let DEFAULT_SYSTEM_DARK_THEME = "systemDarkTheme" @@ -145,6 +146,7 @@ let appDefaults: [String: Any] = [ DEFAULT_SHOW_DELETE_CONTACT_NOTICE: true, DEFAULT_SHOW_SENT_VIA_RPOXY: false, DEFAULT_SHOW_SUBSCRIPTION_PERCENTAGE: false, + DEFAULT_MARKDOWN_FILE_TEXT_VIEW: false, ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN: AppSettingsLockScreenCalls.show.rawValue, DEFAULT_THEME_OVERRIDES: "{}", diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index a138b24e4e..e9cfcab5bc 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -168,6 +168,7 @@ 647C9E293061362A0032110E /* GetStakeBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647C9E283061362A0032110E /* GetStakeBanner.swift */; }; 647F090E288EA27B00644C40 /* GroupMemberInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647F090D288EA27B00644C40 /* GroupMemberInfoView.swift */; }; 648010AB281ADD15009009B9 /* CIFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648010AA281ADD15009009B9 /* CIFileView.swift */; }; + 6F3B2A1C2E8D4F0A9B7C1D21 /* MarkdownFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3B2A1C2E8D4F0A9B7C1D22 /* MarkdownFileView.swift */; }; 648679AB2BC96A74006456E7 /* ChatItemForwardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */; }; 6495D7042F48CFC50060512B /* ChannelMembersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6495D7032F48CFC50060512B /* ChannelMembersView.swift */; }; 6495D7062F48CFFD0060512B /* ChannelRelaysView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6495D7052F48CFFD0060512B /* ChannelRelaysView.swift */; }; @@ -560,6 +561,7 @@ 647C9E283061362A0032110E /* GetStakeBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetStakeBanner.swift; sourceTree = ""; }; 647F090D288EA27B00644C40 /* GroupMemberInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupMemberInfoView.swift; sourceTree = ""; }; 648010AA281ADD15009009B9 /* CIFileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIFileView.swift; sourceTree = ""; }; + 6F3B2A1C2E8D4F0A9B7C1D22 /* MarkdownFileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownFileView.swift; sourceTree = ""; }; 648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemForwardingView.swift; sourceTree = ""; }; 6493D667280ED77F007A76FB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 6495D7032F48CFC50060512B /* ChannelMembersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelMembersView.swift; sourceTree = ""; }; @@ -1123,6 +1125,7 @@ 649BCDA12805D6EF00C3A862 /* CIImageView.swift */, 5C10D88928F187F300E58BF0 /* FullScreenMediaView.swift */, 648010AA281ADD15009009B9 /* CIFileView.swift */, + 6F3B2A1C2E8D4F0A9B7C1D22 /* MarkdownFileView.swift */, 644EFFDF292CFD7F00525D5B /* CIVoiceView.swift */, 3CDBCF4727FF621E00354CDD /* CILinkView.swift */, 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */, @@ -1600,6 +1603,7 @@ 5CB634A829E437960066AD6B /* PasscodeEntry.swift in Sources */, 5CFA59C42860BC6200863A68 /* MigrateToAppGroupView.swift in Sources */, 648010AB281ADD15009009B9 /* CIFileView.swift in Sources */, + 6F3B2A1C2E8D4F0A9B7C1D21 /* MarkdownFileView.swift in Sources */, 644EFFE2292D089800525D5B /* FramedCIVoiceView.swift in Sources */, 5C4B3B0A285FB130003915F2 /* DatabaseView.swift in Sources */, 5CB2084F28DA4B4800D024EC /* RTCServers.swift in Sources */, diff --git a/apps/ios/spec/client/chat-view.md b/apps/ios/spec/client/chat-view.md index c509662c9e..b63d336c34 100644 --- a/apps/ios/spec/client/chat-view.md +++ b/apps/ios/spec/client/chat-view.md @@ -238,6 +238,12 @@ Renders voice messages with waveform visualization, play/pause control, and dura Renders file attachments with filename, size, and download/open actions. Shows transfer progress during upload/download. +### MarkdownFileView + +**File**: [`Shared/Views/Chat/ChatItem/MarkdownFileView.swift`](../../Shared/Views/Chat/ChatItem/MarkdownFileView.swift) + +Sheet with a viewer of Markdown files up to `MAX_MARKDOWN_VIEW_SIZE` (1 MB), formatted or as text, with Save. Opened by a tap on the file item, or when reception completes after the tap that started it. + ### [CILinkView](../../Shared/Views/Chat/ChatItem/CILinkView.swift#L14) **File**: [`Shared/Views/Chat/ChatItem/CILinkView.swift`](../../Shared/Views/Chat/ChatItem/CILinkView.swift) diff --git a/apps/multiplatform/common/build.gradle.kts b/apps/multiplatform/common/build.gradle.kts index 0ff63fa8de..e9503bc6ce 100644 --- a/apps/multiplatform/common/build.gradle.kts +++ b/apps/multiplatform/common/build.gradle.kts @@ -77,8 +77,8 @@ kotlin { api("dev.icerock.moko:resources-compose:0.27.1") // Markdown - implementation("com.mikepenz:multiplatform-markdown-renderer:0.27.0") - implementation("com.mikepenz:multiplatform-markdown-renderer-m2:0.27.0") + implementation("com.mikepenz:multiplatform-markdown-renderer:0.34.0") + implementation("com.mikepenz:multiplatform-markdown-renderer-m2:0.34.0") } } val commonTest by getting { diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Images.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Images.android.kt index 1a3703822d..df2e6ccadc 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Images.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Images.android.kt @@ -13,6 +13,7 @@ import androidx.core.graphics.scale import boofcv.android.ConvertBitmap import boofcv.struct.image.GrayU8 import chat.simplex.common.R +import chat.simplex.common.views.helpers.decodeSampledBitmapFromByteArray import chat.simplex.common.views.helpers.errorBitmap import chat.simplex.common.views.helpers.getFileName import java.io.ByteArrayOutputStream @@ -41,6 +42,14 @@ actual fun base64ToBitmap(base64ImageString: String): ImageBitmap { } } +actual fun decodeBase64Image(base64: String, width: Int): ImageBitmap? = + try { + decodeSampledBitmapFromByteArray(Base64.decode(base64, Base64.DEFAULT), width, width, MAX_IMAGE_DIMENSION).asImageBitmap() + } catch (e: Exception) { + Log.e(TAG, "decodeBase64Image error: $e") + null + } + actual fun resizeImageToStrSize(image: ImageBitmap, maxDataSize: Long): String { var img = image var str = compressImageStr(img) diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/helpers/Utils.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/helpers/Utils.android.kt index 371bd7676b..7b2f53d365 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/helpers/Utils.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/helpers/Utils.android.kt @@ -194,11 +194,12 @@ actual suspend fun getLoadedImage(file: CIFile?): Pair? } // https://developer.android.com/topic/performance/graphics/load-bitmap#load-bitmap -private fun decodeSampledBitmapFromByteArray(data: ByteArray, reqWidth: Int, reqHeight: Int): Bitmap { +internal fun decodeSampledBitmapFromByteArray(data: ByteArray, reqWidth: Int, reqHeight: Int, maxDimension: Int = Int.MAX_VALUE): Bitmap { // First decode with inJustDecodeBounds=true to check dimensions return BitmapFactory.Options().run { inJustDecodeBounds = true BitmapFactory.decodeByteArray(data, 0, data.size, this) + if (outWidth > maxDimension || outHeight > maxDimension) throw IOException("Image dimensions exceed limit") // Calculate inSampleSize inSampleSize = calculateInSampleSize(this, reqWidth, reqHeight) // Decode bitmap with inSampleSize set diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt index 656911443a..ce1152de6a 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt @@ -415,7 +415,10 @@ fun CenterPartOfScreen() { } } } - else -> ChatView(chatsCtx = chatModel.chatsContext, currentChatId) {} + else -> { + ChatView(chatsCtx = chatModel.chatsContext, currentChatId) {} + ModalManager.center.showInView() + } } } 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 14bb9304d8..faf27d3ceb 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 @@ -142,6 +142,7 @@ class AppPreferences { val appearanceBarsBlurRadius = mkIntPreference(SHARED_PREFS_APPEARANCE_BARS_BLUR_RADIUS, if (deviceSupportsBlur) 50 else 0) val experimentalCalls = mkBoolPreference(SHARED_PREFS_EXPERIMENTAL_CALLS, false) val showUnreadAndFavorites = mkBoolPreference(SHARED_PREFS_SHOW_UNREAD_AND_FAVORITES, false) + val markdownFileTextView = mkBoolPreference(SHARED_PREFS_MARKDOWN_FILE_TEXT_VIEW, false) val chatArchiveName = mkStrPreference(SHARED_PREFS_CHAT_ARCHIVE_NAME, null) val chatArchiveTime = mkDatePreference(SHARED_PREFS_CHAT_ARCHIVE_TIME, null) val chatLastStart = mkDatePreference(SHARED_PREFS_CHAT_LAST_START, null) @@ -429,6 +430,7 @@ class AppPreferences { const val SHARED_PREFS_PRIVACY_FULL_BACKUP = "FullBackup" private const val SHARED_PREFS_EXPERIMENTAL_CALLS = "ExperimentalCalls" private const val SHARED_PREFS_SHOW_UNREAD_AND_FAVORITES = "ShowUnreadAndFavorites" + private const val SHARED_PREFS_MARKDOWN_FILE_TEXT_VIEW = "MarkdownFileTextView" private const val SHARED_PREFS_CHAT_ARCHIVE_NAME = "ChatArchiveName" private const val SHARED_PREFS_CHAT_ARCHIVE_TIME = "ChatArchiveTime" private const val SHARED_PREFS_APP_LANGUAGE = "AppLanguage" diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Images.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Images.kt index d7a241d5ee..47cc034bfb 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Images.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Images.kt @@ -15,6 +15,7 @@ import java.io.InputStream import java.net.URI expect fun base64ToBitmap(base64ImageString: String): ImageBitmap +expect fun decodeBase64Image(base64: String, width: Int): ImageBitmap? expect fun resizeImageToStrSize(image: ImageBitmap, maxDataSize: Long): String expect fun resizeImageToDataSize(image: ImageBitmap, usePng: Boolean, maxDataSize: Long): ByteArrayOutputStream expect fun cropToSquare(image: ImageBitmap): ImageBitmap diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt index 1523e2ff22..42cbff3a2f 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIFileView.kt @@ -106,6 +106,8 @@ fun CIFileView( showFileErrorAlert(file.fileStatus.sndFileError) file.fileStatus is CIFileStatus.SndWarning -> showFileErrorAlert(file.fileStatus.sndFileError, temporary = true) + isMarkdownFile(file) && file.loaded -> + showMarkdownFile(file) file.forwardingAllowed() -> { withLongRunningApi(slow = 600_000) { var filePath = getLoadedFilePath(file) @@ -184,7 +186,7 @@ fun CIFileView( val showOpenSaveMenu = rememberSaveable(file?.fileId) { mutableStateOf(false) } val ext = file?.fileSource?.filePath?.substringAfterLast(".")?.takeIf { it.isNotBlank() } - val loadedFilePath = if (appPlatform.isAndroid && file?.fileSource != null) getLoadedFilePath(file) else null + val loadedFilePath = if (appPlatform.isAndroid && file?.fileSource != null && !isMarkdownFile(file)) getLoadedFilePath(file) else null if (loadedFilePath != null && file?.fileSource != null) { val encrypted = file.fileSource.cryptoArgs != null SaveOrOpenFileMenu(showOpenSaveMenu, encrypted, ext, File(loadedFilePath).toURI(), file.fileSource, saveFile = { fileAction() }) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/MarkdownFileView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/MarkdownFileView.kt new file mode 100644 index 0000000000..189e69a925 --- /dev/null +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/MarkdownFileView.kt @@ -0,0 +1,284 @@ +package chat.simplex.common.views.chat.item + +import SectionBottomSpacer +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.text.selection.SelectionContainer +import androidx.compose.material.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.painter.BitmapPainter +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.platform.UriHandler +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import chat.simplex.common.model.* +import chat.simplex.common.model.ChatController.appPrefs +import chat.simplex.common.platform.* +import chat.simplex.common.ui.theme.DEFAULT_PADDING +import chat.simplex.common.ui.theme.WarningOrange +import chat.simplex.common.views.chat.topPaddingToContent +import chat.simplex.common.views.helpers.* +import chat.simplex.common.views.usersettings.networkAndServers.documentMarkdownStyle +import chat.simplex.res.* +import com.mikepenz.markdown.compose.MarkdownElement +import com.mikepenz.markdown.m2.Markdown +import com.mikepenz.markdown.model.ImageData +import com.mikepenz.markdown.model.ImageTransformer +import com.mikepenz.markdown.model.State +import com.mikepenz.markdown.model.markdownAnnotator +import com.mikepenz.markdown.model.parseMarkdownFlow +import dev.icerock.moko.resources.compose.painterResource +import dev.icerock.moko.resources.compose.stringResource +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.withContext +import org.intellij.markdown.MarkdownElementTypes +import org.intellij.markdown.ast.ASTNode +import org.intellij.markdown.ast.getTextInNode +import org.intellij.markdown.flavours.gfm.GFMElementTypes +import java.io.ByteArrayInputStream +import java.io.File +import java.net.URI +import java.nio.ByteBuffer +import java.nio.charset.CodingErrorAction + +private const val DATA_IMAGE = "data:image/" +private const val MAX_MARKDOWN_VIEW_SIZE: Long = 1_048_576 +private const val MAX_MARKDOWN_LINE_LENGTH = 15_000 +private const val MAX_MARKDOWN_DEPTH = 32 +private const val MAX_MARKDOWN_TABLE_ROWS = 200 +private const val MAX_MARKDOWN_IMAGE_PIXELS = 2160L * 4320 + +private val controlChar = Regex("[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]") + +private val bidiControlChar = Regex("[\\u061C\\u200E\\u200F\\u202A-\\u202E\\u2066-\\u2069]") + +private val invisibleCharMarkerStyle = SpanStyle(color = WarningOrange) + +private class MarkdownDocument(val lines: List, val formatted: State.Success?, val hasBidiControls: Boolean) + +fun isMarkdownFile(file: CIFile): Boolean = + file.fileSize <= MAX_MARKDOWN_VIEW_SIZE && file.fileName.substringAfterLast('.', "").lowercase() in listOf("md", "markdown") + +fun showMarkdownFile(file: CIFile) { + withLongRunningApi(slow = 600_000) { + var filePath = getLoadedFilePath(file) + if (chatModel.connectedToRemote() && filePath == null) { + file.loadRemoteFile(true) + filePath = getLoadedFilePath(file) + } + if (filePath == null) { + AlertManager.shared.showAlertMsg(MR.strings.file_not_found) + return@withLongRunningApi + } + val bytes = try { + val cryptoArgs = file.fileSource?.cryptoArgs + if (cryptoArgs != null) readCryptoFile(filePath, cryptoArgs) else File(filePath).readBytes() + } catch (e: Exception) { + AlertManager.shared.showAlertMsg(generalGetString(MR.strings.error), e.message) + return@withLongRunningApi + } + val text = decodedText(bytes) + val lines = text.lines() + val formatted = if (lines.all { it.length <= MAX_MARKDOWN_LINE_LENGTH }) formattedWithinLimits(text) else null + val document = MarkdownDocument( + lines.flatMap { line -> line.chunked(MAX_MARKDOWN_LINE_LENGTH).ifEmpty { listOf("") } }, + formatted, + bidiControlChar.containsMatchIn(text) + ) + ModalManager.center.showModal(endButtons = { + if (formatted != null) MarkdownViewSwitch() + MarkdownFileSaveButton(file, bytes) + }) { + MarkdownFileView(file.fileName, document) + } + } +} + +private fun decodedText(bytes: ByteArray): String = + Charsets.UTF_8.newDecoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE) + .replaceWith("?") + .decode(ByteBuffer.wrap(bytes)) + .toString() + .replace(controlChar, "?") + +private suspend fun formattedWithinLimits(text: String): State.Success? = + (parseMarkdownFlow(text).first { it !is State.Loading } as? State.Success)?.takeIf { treeWithinLimits(it.node) } + +private fun treeWithinLimits(root: ASTNode): Boolean { + val nodes = ArrayDeque(listOf(root to 1)) + while (nodes.isNotEmpty()) { + val (node, depth) = nodes.removeLast() + if (depth > MAX_MARKDOWN_DEPTH) return false + if (node.type == GFMElementTypes.TABLE && node.children.count { it.type == GFMElementTypes.ROW } > MAX_MARKDOWN_TABLE_ROWS) return false + node.children.forEach { nodes.addLast(it to depth + 1) } + } + return true +} + +@Composable +private fun MarkdownViewSwitch() { + val textView = remember { appPrefs.markdownFileTextView.state } + IconButton({ appPrefs.markdownFileTextView.set(!textView.value) }) { + Icon( + painterResource(if (textView.value) MR.images.ic_article else MR.images.ic_code), + stringResource(if (textView.value) MR.strings.markdown_formatted_view else MR.strings.markdown_text_view), + tint = MaterialTheme.colors.primary + ) + } +} + +@Composable +private fun MarkdownFileSaveButton(file: CIFile, bytes: ByteArray) { + val saveFileLauncher = rememberFileChooserLauncher(false, bytes) { to: URI? -> + if (to != null) copyBytesToFile(ByteArrayInputStream(bytes), to) {} + } + IconButton({ withLongRunningApi { saveFileLauncher.launch(file.fileName) } }) { + Icon(painterResource(MR.images.ic_download), stringResource(MR.strings.save_verb), tint = MaterialTheme.colors.primary) + } +} + +@Composable +private fun MarkdownFileView(fileName: String, document: MarkdownDocument) { + val textView = remember { appPrefs.markdownFileTextView.state } + val formatted = document.formatted + if (textView.value || formatted == null) { + SelectionContainer { + MarkdownFileColumn(fileName, document.hasBidiControls) { + items(document.lines) { line -> + Text(remember(line) { withInvisibleCharMarkers(line) }, Modifier.padding(horizontal = DEFAULT_PADDING), fontFamily = FontFamily.Monospace) + } + } + } + } else { + FormattedMarkdown(fileName, formatted, document.hasBidiControls) + } +} + +private fun withInvisibleCharMarkers(line: String): AnnotatedString = buildAnnotatedString { + var start = 0 + invisibleChar.findAll(line).forEach { + append(line.substring(start, it.range.first)) + withStyle(invisibleCharMarkerStyle) { append(unicodeMarker(it.value)) } + start = it.range.last + 1 + } + append(line.substring(start)) +} + +@Composable +private fun FormattedMarkdown(fileName: String, formatted: State.Success, hasBidiControls: Boolean) { + val style = documentMarkdownStyle(h1 = MaterialTheme.typography.h2.copy(fontSize = 26.sp, fontWeight = FontWeight.Bold)) + val codeSpanStyle = style.typography.inlineCode.toSpanStyle().copy(background = style.colors.inlineCodeBackground) + val annotator = remember(codeSpanStyle) { + markdownAnnotator { content, child -> + when (child.type) { + MarkdownElementTypes.CODE_SPAN -> { + withStyle(codeSpanStyle) { append(" ${child.getTextInNode(content).trim('`')} ") } + true + } + MarkdownElementTypes.IMAGE -> + if (imageLink(content, child)?.startsWith(DATA_IMAGE) == true) { + false + } else { + append(child.getTextInNode(content).toString()) + true + } + else -> false + } + } + } + val imageTransformer = remember { DataImageTransformer() } + val parentUriHandler = LocalUriHandler.current + CompositionLocalProvider(LocalUriHandler provides remember { markdownFileUriHandler(parentUriHandler) }) { + Markdown( + formatted, + colors = style.colors, + typography = style.typography, + padding = style.padding, + imageTransformer = imageTransformer, + annotator = annotator, + components = style.components, + success = { state, components, _ -> + MarkdownFileColumn(fileName, hasBidiControls) { + items(state.node.children) { node -> + Column(Modifier.padding(horizontal = DEFAULT_PADDING)) { + MarkdownElement(node, components, state.content, skipLinkDefinition = state.linksLookedUp) + } + } + } + } + ) + } +} + +@Composable +private fun MarkdownFileColumn(fileName: String, hasBidiControls: Boolean, content: LazyListScope.() -> Unit) { + val oneHandUI = remember { appPrefs.oneHandUI.state } + LazyColumnWithScrollBar( + contentPadding = PaddingValues( + top = if (oneHandUI.value) WindowInsets.statusBars.asPaddingValues().calculateTopPadding() + DEFAULT_PADDING + 5.dp else topPaddingToContent(false) + ) + ) { + item { AppBarTitle(fileName) } + if (hasBidiControls) { + item { + Text(stringResource(MR.strings.markdown_file_bidi_warning), Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING), color = WarningOrange) + } + } + content() + item { SectionBottomSpacer() } + } +} + +private fun imageLink(content: String, image: ASTNode): String? = + image.children.firstOrNull { it.type == MarkdownElementTypes.INLINE_LINK } + ?.children?.firstOrNull { it.type == MarkdownElementTypes.LINK_DESTINATION } + ?.getTextInNode(content)?.toString() + +private fun markdownFileUriHandler(parentUriHandler: UriHandler): UriHandler = object: UriHandler { + override fun openUri(uri: String) { + val format = parseToMarkdown(uri)?.singleOrNull()?.format + when { + format is Format.SimplexLink -> parentUriHandler.openVerifiedSimplexUri(format.simplexUri) + format is Format.Uri && (uri.startsWith("https://") || uri.startsWith("http://")) -> openBrowserAlert(uri, parentUriHandler) + else -> showInvalidLinkAlert(uri) + } + } +} + +private class DataImageTransformer : ImageTransformer { + private var pixels = MAX_MARKDOWN_IMAGE_PIXELS + private val images = mutableMapOf() + + @Composable + override fun transform(link: String): ImageData? { + val width = with(LocalDensity.current) { LocalWindowWidth().roundToPx() } + val bitmap by produceState(null, link) { + value = withContext(Dispatchers.Default) { decodedImage(link, width) } + } + return remember(bitmap) { bitmap?.let { ImageData(BitmapPainter(it)) } } + } + + private fun decodedImage(link: String, width: Int): ImageBitmap? = synchronized(this) { + if (link !in images) images[link] = imageWithinBudget(link, width) + images[link] + } + + private fun imageWithinBudget(link: String, width: Int): ImageBitmap? = + link.takeIf { it.startsWith(DATA_IMAGE) } + ?.let { decodeBase64Image(it.substringAfter("base64,"), width) } + ?.takeIf { it.width.toLong() * it.height <= pixels } + ?.also { pixels -= it.width.toLong() * it.height } +} diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt index 489ebb8c39..32e0befa3f 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt @@ -586,7 +586,7 @@ fun ContactConnectionMenuItems(rhId: Long?, chatInfo: ChatInfo.ContactConnection onClick = { ModalManager.center.closeModals() ModalManager.end.closeModals() - ModalManager.center.showModalCloseable(settings = true, showClose = appPlatform.isAndroid, cardScreen = true) { close -> + ModalManager.center.showModalCloseable(settings = true, cardScreen = true) { close -> ContactConnectionInfoView(chatModel, rhId, chatInfo.contactConnection.connLinkInv, chatInfo.contactConnection, true, close) } showMenu.value = false diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ModalView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ModalView.kt index 02c0b45de4..b13c321f70 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ModalView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ModalView.kt @@ -10,7 +10,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import chat.simplex.common.model.ChatController.appPrefs -import chat.simplex.common.model.ChatModel import chat.simplex.common.platform.* import LocalCardScreen import chat.simplex.common.ui.theme.* @@ -142,9 +141,7 @@ class ModalManager(private val placement: ModalPlacement? = null) { modalViews.add(ModalViewHolder(id, anim, data, modal)) _modalCount.value = modalViews.size - toRemove.size - if (placement == ModalPlacement.CENTER) { - ChatModel.chatId.value = null - } else if (placement == ModalPlacement.END) { + if (placement == ModalPlacement.END) { desktopExpandWindowToWidth(DEFAULT_START_MODAL_WIDTH * sqrt(appPrefs.fontScale.get()) + DEFAULT_MIN_CENTER_MODAL_WIDTH + DEFAULT_END_MODAL_WIDTH * sqrt(appPrefs.fontScale.get())) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/OperatorView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/OperatorView.kt index 1e46091ea3..20a182551c 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/OperatorView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/networkAndServers/OperatorView.kt @@ -33,10 +33,14 @@ import chat.simplex.common.views.helpers.* import chat.simplex.common.views.onboarding.* import chat.simplex.res.* import com.mikepenz.markdown.compose.Markdown +import com.mikepenz.markdown.compose.components.MarkdownComponents import com.mikepenz.markdown.compose.components.markdownComponents import com.mikepenz.markdown.compose.elements.MarkdownHeader import com.mikepenz.markdown.m2.markdownColor import com.mikepenz.markdown.m2.markdownTypography +import com.mikepenz.markdown.model.MarkdownColors +import com.mikepenz.markdown.model.MarkdownPadding +import com.mikepenz.markdown.model.MarkdownTypography import com.mikepenz.markdown.model.markdownPadding import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource @@ -771,22 +775,36 @@ expect fun ConditionsBox(modifier: Modifier, scrollState: ScrollState, content: @Composable private fun ConditionsMarkdown(text: String) { + val style = documentMarkdownStyle() Markdown(text, - markdownColor(linkText = MaterialTheme.colors.primary), + style.colors, + style.typography, + Modifier.padding(8.dp), + // using CommonMarkFlavourDescriptor instead of GFMFlavourDescriptor because it shows `https://simplex.chat/` (link inside backticks) incorrectly + flavour = CommonMarkFlavourDescriptor(), + components = style.components, + padding = style.padding + ) +} + +class DocumentMarkdownStyle(val colors: MarkdownColors, val typography: MarkdownTypography, val components: MarkdownComponents, val padding: MarkdownPadding) + +@Composable +fun documentMarkdownStyle(h1: TextStyle = MaterialTheme.typography.body1): DocumentMarkdownStyle = + DocumentMarkdownStyle( + markdownColor(), markdownTypography( - h1 = MaterialTheme.typography.body1, + h1 = h1, h2 = MaterialTheme.typography.h3.copy(fontSize = 22.sp, fontWeight = FontWeight.Bold), h3 = MaterialTheme.typography.h4.copy(fontWeight = FontWeight.Bold), h4 = MaterialTheme.typography.h5.copy(fontSize = 16.sp, fontWeight = FontWeight.Bold), h5 = MaterialTheme.typography.h6.copy(fontWeight = FontWeight.Bold), link = MaterialTheme.typography.body1.copy( + color = MaterialTheme.colors.primary, textDecoration = TextDecoration.Underline ) ), - Modifier.padding(8.dp), - // using CommonMarkFlavourDescriptor instead of GFMFlavourDescriptor because it shows `https://simplex.chat/` (link inside backticks) incorrectly - flavour = CommonMarkFlavourDescriptor(), - components = markdownComponents( + markdownComponents( heading2 = { Spacer(Modifier.height(10.dp)) MarkdownHeader(it.content, it.node, it.typography.h2) @@ -803,9 +821,8 @@ private fun ConditionsMarkdown(text: String) { Spacer(Modifier.height(4.dp)) }, ), - padding = markdownPadding(block = 4.dp) + markdownPadding(block = 4.dp) ) -} @Composable private fun ConditionsLinkView(conditionsLink: String) { diff --git a/apps/multiplatform/common/src/commonMain/moko-resources/base/strings.xml b/apps/multiplatform/common/src/commonMain/moko-resources/base/strings.xml index 93bf4f5838..326be2b05d 100644 --- a/apps/multiplatform/common/src/commonMain/moko-resources/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/moko-resources/base/strings.xml @@ -687,6 +687,9 @@ File saved File not found Error saving file + Formatted view + Text view + This file has text direction characters. The text can be shown in a different order than it is written. Loading the file Please, wait while the file is being loaded from the linked mobile File error diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Images.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Images.desktop.kt index 9c6139b493..925698154e 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Images.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Images.desktop.kt @@ -5,6 +5,7 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntSize import boofcv.io.image.ConvertBufferedImage import boofcv.struct.image.GrayU8 +import chat.simplex.common.views.helpers.decodeBoundedBufferedImage import chat.simplex.res.* import org.jetbrains.skia.Image import java.awt.RenderingHints @@ -54,6 +55,14 @@ actual fun base64ToBitmap(base64ImageString: String): ImageBitmap { } } +actual fun decodeBase64Image(base64: String, width: Int): ImageBitmap? = + try { + decodeBoundedBufferedImage(Base64.getMimeDecoder().decode(base64), width).toComposeImageBitmap() + } catch (e: Throwable) { + Log.e(TAG, "decodeBase64Image error: $e") + null + } + actual fun resizeImageToStrSize(image: ImageBitmap, maxDataSize: Long): String { var img = image var str = compressImageStr(img) diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.desktop.kt index 98ffa7c8a4..dd76e86c81 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.desktop.kt @@ -23,7 +23,7 @@ actual fun SimpleAndAnimatedImageView( // still image. A full screen modal is shown beside the chat rather than in place of it, so this item keeps // composing under one and would otherwise decode where nobody can see it. val frame = if (smallView) imageBitmap - else rememberAnimatedImage(data, imageBitmap) { blurred.value || ModalManager.fullscreen.hasModalsOpen() } + else rememberAnimatedImage(data, imageBitmap) { blurred.value || ModalManager.fullscreen.hasModalsOpen() || ModalManager.center.hasModalsOpen() } ImageView(BitmapPainter(frame)) { if (getLoadedFilePath(file) != null) { ModalManager.fullscreen.showCustomModal(animated = false) { close -> diff --git a/apps/multiplatform/product/README.md b/apps/multiplatform/product/README.md index 5dc606a50e..3dff974366 100644 --- a/apps/multiplatform/product/README.md +++ b/apps/multiplatform/product/README.md @@ -307,7 +307,7 @@ UserPicker | | OR (when no chat selected): | GroupProfileView | | UserPicker (overlay) | "No selected chat" | AddGroupMembersView | | - profile switcher | | MemberAdmission | -| - quick settings | OR (when modal open): | VerifyCodeView | +| - quick settings | Over them (when modal open): | VerifyCodeView | | | ModalManager.center content | SettingsView subtabs | | ModalManager.start | (settings, new chat, etc.) | | | - secondary modals | | ModalManager.end | @@ -316,7 +316,7 @@ UserPicker ModalManager Placement (Desktop): - ModalManager.start -> left panel overlay (settings subviews) - - ModalManager.center -> center panel (replaces chat, used when chatId is null) + - ModalManager.center -> center panel (drawn over the chat or the placeholder) - ModalManager.end -> right panel (detail/info views) - ModalManager.fullscreen -> full window overlay (onboarding, auth, call) diff --git a/apps/multiplatform/spec/client/chat-view.md b/apps/multiplatform/spec/client/chat-view.md index adf8ad0224..3bad2e09c4 100644 --- a/apps/multiplatform/spec/client/chat-view.md +++ b/apps/multiplatform/spec/client/chat-view.md @@ -313,6 +313,7 @@ Key sections: group profile, group link, member list with roles, group preferenc | `EmojiItemView.kt` | Large emoji-only message | | `FramedItemView.kt` | Message bubble frame (quoted item, text, media) | | `ImageFullScreenView.kt` | Fullscreen image gallery | +| `MarkdownFileView.kt` | Markdown file viewer, formatted or as text | | `IntegrityErrorItemView.kt` | Message integrity error | | `MarkedDeletedItemView.kt` | Marked-as-deleted / moderated message | | `TextItemView.kt` | Plain text message with markdown | diff --git a/apps/multiplatform/spec/client/navigation.md b/apps/multiplatform/spec/client/navigation.md index 96dd58052c..d0340e8d23 100644 --- a/apps/multiplatform/spec/client/navigation.md +++ b/apps/multiplatform/spec/client/navigation.md @@ -178,7 +178,7 @@ fun DesktopScreen(userPickerState: MutableStateFlow) |---|---|---| | **Left** | `DEFAULT_START_MODAL_WIDTH * fontSizeSqrtMultiplier` (fixed) | `StartPartOfScreen` (ChatListView) + `UserPicker` overlay | | **Left overlay** | Same as left column | `ModalManager.start` modals + `SwitchingUsersView` | -| **Center** | `min = DEFAULT_MIN_CENTER_MODAL_WIDTH`, `weight = 1f` (flexible) | `CenterPartOfScreen` (ChatView or "no selected chat" placeholder, or `ModalManager.center`) | +| **Center** | `min = DEFAULT_MIN_CENTER_MODAL_WIDTH`, `weight = 1f` (flexible) | `CenterPartOfScreen` (ChatView with `ModalManager.center` over it; without a chat: `ModalManager.center` or "no selected chat" placeholder) | | **Right** | `max = DEFAULT_END_MODAL_WIDTH * fontSizeSqrtMultiplier` (flexible, 0 when empty) | `ModalManager.end` (ChatInfoView, GroupChatInfoView, ChatItemInfoView, etc.) | ### Column Separators @@ -196,7 +196,7 @@ When the UserPicker is visible or a start modal is open (but no center modal), a - When `chatId` is null and no center modals: shows "No selected chat" placeholder. - When `chatId` is null and center modals open: shows `ModalManager.center`. -- When `chatId` is set: shows `ChatView`. +- When `chatId` is set: shows `ChatView`, and `ModalManager.center` over it. - Automatically closes center modals when a chat is selected. ### StartPartOfScreen @@ -223,7 +223,7 @@ class ModalManager(private val placement: ModalPlacement?) | Zone | Android Behavior | Desktop Behavior | |---|---|---| | `start` | Shared (same as all others) | Left column overlay, slides from start | -| `center` | Shared | Center column overlay, replaces ChatView | +| `center` | Shared | Center column overlay, drawn over ChatView | | `end` | Shared | Right column, slides from end | | `fullscreen` | Shared | Fullscreen overlay | diff --git a/plans/2026-10-11-markdown-file-viewer.md b/plans/2026-10-11-markdown-file-viewer.md new file mode 100644 index 0000000000..c5152ea485 --- /dev/null +++ b/plans/2026-10-11-markdown-file-viewer.md @@ -0,0 +1,319 @@ +# Markdown file viewer: implementation plan + +Markdown files from chats are viewed in the Android, desktop and iOS apps, formatted or as text. + +## 1. Files that qualify + +By name and size only: + +``` +name file.fileName ends with .md or .markdown, case-insensitive +size file.fileSize <= MAX_MARKDOWN_VIEW_SIZE +``` + +``` +MAX_MARKDOWN_VIEW_SIZE 1_048_576 +Kotlin views/chat/item/MarkdownFileView.kt, private +iOS Shared/Views/Chat/ChatItem/MarkdownFileView.swift, private +``` + +Function, in each viewer file: + +``` +Kotlin fun isMarkdownFile(file: CIFile): Boolean +iOS func isMarkdownFile(_ file: CIFile) -> Bool +``` + +Other files, and Markdown files over the limit: tap actions unchanged. + +## 2. Tap and long press + +Tap on a qualifying file, every platform: + +``` +file status action +received or sent, file loaded viewer +desktop with a linked mobile loadRemoteFile, then viewer +RcvInvitation, RcvAborted reception started; viewer shown on completion, in the same place +other statuses as now: waiting alerts, error alerts +``` + +Long press: the existing Save item of the item menu (`ChatItemView.kt`, `ChatView.swift`). + +Places: + +``` +Kotlin CIFileView.kt, onClick and fileAction +iOS CIFileView.swift, fileAction +``` + +- Android: "Open with" of `SaveOrOpenFileMenu` is not shown for qualifying files; "Share" in the item menu stays. +- Present locally: `getLoadedFilePath(file)` (Kotlin), `getLoadedFileSource(file)` (iOS), as for other files. + +## 3. Open on completion + +In master (#7700), for images, voice messages, video and files. The viewer adds only the Markdown case to the file tap action. + +A tap that starts reception registers the file. On completion the tap action of the received file is performed: + +``` +image full screen view +voice playback +video playback; locally encrypted: decrypted first; chat list preview: full screen player +file Markdown: viewer + other: share sheet (iOS), open/save menu (Android), save dialog (desktop) +``` + +Kotlin, chat model: + +``` +fileToOpen file ID of the last tapped file + inChatList: tap in a chat list preview +openWhenReceived(file, inChatList) sets fileToOpen +receivedFileToOpen(file, inChatList) false unless fileToOpen has file.fileId and inChatList, and the file is loaded + then fileToOpen cleared + chat view: true + chat list preview: true on desktop, or when no chat is open +``` + +Item views: `OpenWhenReceivedEffect(file, inChatList, open)` (`CIFileView.kt`), on a change of `file.loaded`; in `CIImageView.kt`, `CIVoiceView.kt`, `CIVideoView.kt`, `CIFileView.kt`. + +iOS, chat model: + +``` +fileToOpen file ID of the last tapped file + chat ID at the tap; nil in the chat list + open callback of the tapped view +openWhenReceived(file, open) sets fileToOpen +receivedFileToOpen(file) fileToOpen has file.fileId, and the open chat is the chat at the tap +openReceivedFile(file) receivedFileToOpen rechecked; fileToOpen cleared; open(file) +``` + +Event `rcvFileComplete` (`SimpleXAPI.swift`): for the active user, when `receivedFileToOpen(file)`: `openReceivedFile(file)` on the main actor. + +`fileToOpen` is replaced by a later tap. + +## 4. Reading in memory + +The file is read and decoded off the main thread, before the viewer is shown. + +``` +Kotlin withLongRunningApi + encrypted: readCryptoFile(path, cryptoArgs) (CryptoFile.kt:36) + plain: File(path).readBytes() + text: UTF-8 CharsetDecoder, REPLACE with "?" + parsed: parseMarkdownFlow (renderer), once, before the viewer is shown +iOS Task + getFileData(path, cryptoArgs) (SimpleXChat/ImageUtils.swift:41) + text: Unicode.UTF8.ForwardParser, "?" for each invalid byte + html: MarkdownParser with an .html modifier, raw HTML escaped +``` + +Text decoding, as the core safeDecodeUtf8 (simplexmq Util.hs:383-386): + +``` +invalid UTF-8 "?" for each invalid byte +C0 controls U+0000-U+001F replaced with "?", except tab, line feed, carriage return +line endings CRLF and CR are line breaks: Kotlin text.lines(); iOS: replaced with LF before parsing +``` + +Limits, checked before rendering; over a limit: text view only, the switch hidden: + +``` +line length 15 000 characters +nesting Kotlin: 32 levels of the parsed tree; iOS: 16 levels estimated from line prefixes +link nesting iOS: 4 open square brackets, counted until an empty line +table rows 200 +parse error Kotlin: text view only +``` + +Text view: lines longer than 15 000 characters are split. + +- `readCryptoFile`: core `chatReadFile` (`Mobile/File.hs:87`); the file is decrypted by `CF.readFile` into memory. +- Bytes, text and HTML are held only in memory, in the viewer's state. +- Read error: alert with the error text; the viewer is not shown. +- File absent: "File not found" alert (Kotlin); no action (iOS), as for other files. + +## 5. Viewer + +``` +presentation Android: full screen, like the image viewer + desktop: over the conversation + iOS: sheet +toolbar back (Kotlin) + switch between the formatted and the text view + save +title file name +preference view chosen last; default formatted +``` + +### 5.1. Kotlin + +Renderer: `com.mikepenz:multiplatform-markdown-renderer` and `-m2`, 0.27.0 → 0.34.0 (`common/build.gradle.kts:80-81`). + +``` +0.34.0 dependencies kotlin-stdlib 2.1.20, org.jetbrains:markdown 0.7.3, kotlinx-collections-immutable 0.3.8 +compose compileOnly; built with Compose 1.8.0, the app uses 1.8.2 +app Kotlin 2.1.20, Compose Multiplatform 1.8.2 +added table component, LazyMarkdownSuccess, success lambda of Markdown() +``` + +New file `views/chat/item/MarkdownFileView.kt`: + +``` +isMarkdownFile(file) +showMarkdownFile(file) loadRemoteFile if needed, read, ModalManager.center.showModal +MarkdownFileView formatted or text view by appPrefs.markdownFileTextView +``` + +Formatted view: + +``` +renderer m2 Markdown(state), GFM flavour, the parsed state from showMarkdownFile +styles documentMarkdownStyle(h1) (OperatorView.kt), shared with the operator conditions + h1: typography h2, 26 sp, bold +lazy success lambda: LazyColumnWithScrollBar, title item, one item per top-level node: MarkdownElement in a Column +links LocalUriHandler: markdownFileUriHandler +code spans annotator: source text between the backticks; inlineCode style, inlineCodeBackground +images data:image URIs: DataImageTransformer, decoded off the main thread by decodeBase64Image + other image links: the annotator appends the source text; nothing is fetched +tables default table component +``` + +Links, classified by the core markdown parser (`parseToMarkdown`), as message links: + +``` +SimplexLink openVerifiedSimplexUri +Uri, http or https openBrowserAlert +other showInvalidLinkAlert +``` + +Displayed link targets (`displayedUri`, Kotlin and iOS; also in message link alerts and the external-link alert): + +``` +format characters (Cf) [U+XXXX] +non-ASCII in the authority [U+XXXX] +HTML not parsed (parseHtml = false) +``` + +Images: + +``` +decode decodeBase64Image(base64, window width): power-of-two subsampling, both sides kept at or above the window width + Android: decodeSampledBitmapFromByteArray, 4320 px per side + desktop: decodeBoundedBufferedImage, 16384 px per side, 4320 × 4320 decoded pixels +budget 2160 × 4320 decoded pixels per document; images over the budget are not shown +cache per document, in the transformer; the desktop base64BitmapCache is not used +``` + +Text view: `LazyColumnWithScrollBar`, title item, one monospace `Text` per line, inside `SelectionContainer`; format characters shown as `[U+XXXX]` markers. + +Bidi controls (U+061C, U+200E, U+200F, U+202A-U+202E, U+2066-U+2069): warning under the title, both views. + +Toolbar end buttons: + +``` +switch ic_code in the formatted view, ic_article in the text view; sets appPrefs.markdownFileTextView; hidden over the limits +save ic_download; rememberFileChooserLauncher; copyBytesToFile(ByteArrayInputStream(bytes)) (Files.kt:65) +``` + +`OperatorView.kt`: + +``` +markdownColor(linkText = primary) markdownColor(); link colour moved to the link text style (linkText deprecated in 0.34.0) +ConditionsMarkdown styles moved to documentMarkdownStyle(h1): DocumentMarkdownStyle (colors, typography, components, padding) + h1: body1 by default, as in the conditions +``` + +`SimpleXAPI.kt`: `markdownFileTextView = mkBoolPreference(SHARED_PREFS_MARKDOWN_FILE_TEXT_VIEW, false)`. + +Centre placement on desktop: modals are drawn over the open conversation. + +``` +ModalView.kt showCustomModal: chatId is not changed +App.kt CenterPartOfScreen: ModalManager.center.showInView() after ChatView +ChatListNavLinkView.kt "Set contact name": back button on desktop +``` + +`moko-resources/base/strings.xml`: + +``` +markdown_formatted_view Formatted view +markdown_text_view Text view +markdown_file_bidi_warning This file has text direction characters. The text can be shown in a different order than it is written. +``` + +### 5.2. iOS + +New file `Shared/Views/Chat/ChatItem/MarkdownFileView.swift`, added to the SimpleX target in `project.pbxproj`. + +``` +isMarkdownFile(file) +showMarkdownFile(file) read, parse, showAppSheet +MarkdownFileView NavigationView; toolbar: switch, save +``` + +``` +formatted ConditionsWebView(html:openLink:), Ink 0.6.0, raw HTML escaped by an .html modifier +text ScrollView, LazyVStack, one monospace Text per line, text selection, format characters as [U+XXXX] +switch @AppStorage(DEFAULT_MARKDOWN_FILE_TEXT_VIEW); hidden over the limits +save saveCryptoFile(fileSource) (CIFileView.swift) +close swipe down +links openMarkdownFileLink: parseSimpleXMarkdown; simplexLink: appOpenUrl; uri, http or https: openBrowserAlert; other: showInvalidLinkAlert +``` + +`SettingsView.swift`: `DEFAULT_MARKDOWN_FILE_TEXT_VIEW = "markdownFileTextView"`, default false. + +`ConditionsWebView.swift`, shared with the operator conditions: + +``` +configuration allowsContentJavaScript = false; websiteDataStore = .nonPersistent() +head Content-Security-Policy: default-src 'none'; style-src 'nonce-'; img-src data:; base-uri 'none'; form-action 'none' +style the app's style block with the nonce; table, th, td borders; pre overflow-x auto; img max-width 100% +navigation linkActivated: openLink (openExternalLink by default) + other navigation: allowed for about: URLs only +``` + +- Ink 0.6.0 applies `.html` modifiers to block and inline HTML fragments (`HTMLConvertible.swift`); the viewer's modifier escapes `&`, `<`, `>`; code spans and blocks are escaped by Ink itself. + +Images, an `.images` modifier (`imagesWithinBudget`): + +``` +data:image, base64 shown as , rebuilt from the source only + 4320 px per side; 2160 × 4320 pixels per document; size read by UIImage(data:) +over a limit not shown +other data URIs not shown +other image links the source text; nothing is fetched +``` + +## 6. Size limit + +1 MB. +4 MB: formatted views render lazily (Kotlin: one lazy item per top-level node; iOS: WKWebView); text views render one lazy item per line. +A single top-level block (one table, code block or paragraph) is still laid out at once. + +## 7. Checks + +1. Plain and encrypted files: formatted and text views on Android, desktop and iOS. +2. Desktop linked to a mobile: file loaded from the mobile, then shown. +3. File over 1 MB: tap action as before. +4. File not received: one tap; viewer shown on completion in the same chat; not shown after leaving the chat. +5. Same for images, voice messages, video and other files, each with its tap action. +6. Tap in a chat list preview: opened on completion while the chat list is shown; Android, iOS: not opened after a chat is opened; desktop: opened while a chat is open. +7. Not opened after a reception error, cancel or abort. Kotlin: not opened after the item scrolled out of view. +8. Invalid UTF-8 and control characters: shown as "?". +9. Lines over 15 000 characters, 33 tree levels (Kotlin), 17 nesting levels (iOS), 5 nested square brackets (iOS), 201 table rows: text view only, long lines split. +10. Bidi controls: warning; format characters as [U+XXXX] in the text view. +11. Data images: decoded to the window width (Kotlin); images over 4320 px per side (Android, iOS) or beyond 2160 × 4320 pixels per document not shown. +12. Links: SimpleX links to the connect flow; http(s) links to the "Open web link?" alert; other schemes and relative links: "Invalid link"; targets with invisible or non-ASCII host characters shown with [U+XXXX]. +13. View switch kept after closing the viewer and after a restart. +14. Save: the file at the chosen destination equals the original. +15. Link inside backticks: shown as code. +16. Remote image: not loaded, the source text shown; data URI image: shown. +17. iOS inline HTML with ` +{% if not noEvents %} + +{% endif %} + + + Invest in SimpleX Chat @@ -18,7 +30,7 @@ eleventyExcludeFromCollections: true - + From c07d28681f41c9c52509617a58c9861f2ba82e64 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Wed, 14 Oct 2026 21:42:51 +0100 Subject: [PATCH 11/21] website: update cf links and page (#7722) * website: update cf links and page * noopener --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --- ...5.8-private-message-routing-chat-themes.md | 2 +- blog/20260919-simplex-supporter-badges.md | 4 +- website/langs/ar.json | 16 ++-- website/langs/cs.json | 16 ++-- website/langs/de.json | 16 ++-- website/langs/en.json | 18 ++--- website/langs/es.json | 16 ++-- website/langs/fa.json | 16 ++-- website/langs/fi.json | 16 ++-- website/langs/fr.json | 16 ++-- website/langs/he.json | 16 ++-- website/langs/hu.json | 16 ++-- website/langs/id.json | 16 ++-- website/langs/it.json | 16 ++-- website/langs/ja.json | 16 ++-- website/langs/nl.json | 16 ++-- website/langs/pl.json | 16 ++-- website/langs/pt_BR.json | 16 ++-- website/langs/ru.json | 16 ++-- website/langs/tr.json | 16 ++-- website/langs/uk.json | 16 ++-- website/langs/zh_Hans.json | 16 ++-- website/langs/zh_Hant.json | 12 +-- .../src/_includes/blog_previews/20221108.html | 2 +- .../src/_includes/blog_previews/20221206.html | 8 +- website/src/_includes/contact_page.html | 12 +-- website/src/_includes/footer.html | 38 ++++----- website/src/_includes/hero.html | 20 ++--- website/src/_includes/layouts/token.html | 6 +- website/src/_includes/navbar.html | 12 +-- .../src/_includes/sections/join_simplex.html | 8 +- website/src/crowdfunding.html | 2 +- website/src/directory.html | 2 +- website/src/fdroid.html | 2 +- website/src/file.html | 8 +- website/src/index.html | 78 +++++++++---------- website/src/js/channel-preview.jsc | 10 +-- website/src/js/directory.jsc | 1 + website/src/js/simplex-lib.jsc | 4 +- website/src/livestream.html | 2 +- website/src/messaging.html | 2 +- 41 files changed, 277 insertions(+), 280 deletions(-) diff --git a/blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.md b/blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.md index e06f7c2084..9bd342a146 100644 --- a/blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.md +++ b/blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.md @@ -110,7 +110,7 @@ We [wrote previously](https://github.com/simplex-chat/simplexmq/blob/master/rfcs While we cannot require the operators of self-hosted and private servers to disclose any information about them (apart from which server code they use - this is the requirement of the AGPLv3 license to share this information with users connecting to the server), as we add other server operators to the app, it is important for the users to have all important information about these operators and servers location. -This server release adds server information page where all this information can be made available to the users. For example, this is the information about one of the servers preset in the app. +This server release adds server information page where all this information can be made available to the users. For example, this is the information about one of the servers preset in the app. The updated server guide also includes [the instruction](../docs/SERVER.md#) about how to host this page for your server. It is generated as a static page when the server starts. We recommend using Caddy webserver to serve it. diff --git a/blog/20260919-simplex-supporter-badges.md b/blog/20260919-simplex-supporter-badges.md index 7aa2b2e4ac..50e2ebcb4b 100644 --- a/blog/20260919-simplex-supporter-badges.md +++ b/blog/20260919-simplex-supporter-badges.md @@ -11,7 +11,7 @@ permalink: "/blog/20260919-simplex-supporter-badges.html" **Published:** Sep 19, 2026 -You can now support SimpleX Chat and get a supporter badge, larger files and longer file storage — from v7.1 beta[^beta]. Watch how to buy a badge. +You can now support SimpleX Chat and get a supporter badge, larger files and longer file storage — from v7.1 beta[^beta]. Watch how to buy a badge. ## A paid feature that cannot identify you @@ -28,7 +28,7 @@ Read more about badges in the [whitepaper](https://github.com/simplex-chat/simpl ## How to get a badge - + Buy a code on [simplex.chat/badges](https://simplex.chat/badges/), paying by card, Bitcoin or Monero, and redeem it in the app: open Settings, tap **Supporter perks**, and enter the code. The badge appears on your profile. The badge does not renew by itself, and no account is created. diff --git a/website/langs/ar.json b/website/langs/ar.json index 3f5c220f19..39d72effc2 100644 --- a/website/langs/ar.json +++ b/website/langs/ar.json @@ -82,7 +82,7 @@ "hero-overlay-card-1-p-5": "الأجهزة العميلة فقط هي التي تخزن ملفات تعريف المستخدمين، جهات الاتصال والمجموعات؛ يتم إرسال الرسائل بتعمية ثنائية الطبقة بين الطرفين.", "hero-overlay-card-2-p-2": "يمكنهم بعد ذلك ربط هذه المعلومات بالشبكات الاجتماعية العامة الحالية، وتحديد بعض الهويات الحقيقية.", "simplex-network-overlay-card-1-li-3": "P2P لا يحل مشكلة هجوم الوسيط (MITM)، ومعظم التطبيقات الحالية لا تستخدم خارج النطاق رسائل للتبادل الأولي للمفاتيح. يستخدم SimpleX رسائل خارج النطاق أو، في بعض الحالات، اتصالات آمنة وموثوق بها موجودة مسبقًا لتبادل المفاتيح الأولي.", - "privacy-matters-overlay-card-1-p-3": "تستخدم بعض الشركات المالية والتأمين رسوماً بيانية اجتماعية لتحديد أسعار الفائدة وأقساط التأمين. عادةً ما تجعل الأشخاص ذوي الدخل المنخفض يدفعون أكثر — أو كما يسمى \"علاوة الفقر\".", + "privacy-matters-overlay-card-1-p-3": "تستخدم بعض الشركات المالية والتأمين رسوماً بيانية اجتماعية لتحديد أسعار الفائدة وأقساط التأمين. عادةً ما تجعل الأشخاص ذوي الدخل المنخفض يدفعون أكثر — أو كما يسمى \"علاوة الفقر\".", "privacy-matters-overlay-card-1-p-2": "يعرف تجار التجزئة عبر الإنترنت أن الأشخاص ذوي الدخل المنخفض هم أكثر عرضة لإجراء عمليات شراء عاجلة، لذلك قد يفرضون أسعارًا أعلى أو يزيلون الخصومات.", "simplex-private-6-title": "تبديل
خارج النطاق", "simplex-private-9-title": "قوائم انتظار
أحادية الاتجاه", @@ -90,7 +90,7 @@ "simplex-private-card-1-point-1": "بروتوكول Double-ratchet —
رسائل OTR مع السرية المستمرة واستعادة الاختراق.", "simplex-private-card-1-point-2": "NaCL cryptobox في كل قائمة انتظار لمنع ارتباط حركة مرور البيانات بين قوائم انتظار الرسائل في حالة اختراق TLS.", "simplex-private-card-2-point-1": "طبقة إضافية من تعمية الخادم للتسليم إلى المُستلم، لمنع الارتباط بين حركة مرور بيانات الخادم المُستلمة والمُرسلة في حالة اختراق TLS.", - "simplex-private-card-4-point-2": "لاستخدام SimpleX عبر تور، يُرجى تثبيت تطبيق Orbot وتمكّين وكيل SOCKS5 (أو VPN على iOS ).", + "simplex-private-card-4-point-2": "لاستخدام SimpleX عبر تور، يُرجى تثبيت تطبيق Orbot وتمكّين وكيل SOCKS5 (أو VPN على iOS ).", "simplex-private-card-5-point-2": "يجعل الرسائل ذات الأحجام المختلفة تبدو متشابهة للخوادم ومراقبي الشبكة.", "simplex-private-card-6-point-1": "العديد من شبكات التواصل عرضة لهجمات الوسيط (MITM) من قِبل الخوادم أو موفري الشبكات.", "simplex-private-card-7-point-1": "لضمان سلامة الرسائل يتم ترقيمها بالتسلسل وتضمين تجزئة الرسالة السابقة.", @@ -103,7 +103,7 @@ "hero-overlay-card-1-p-2": "لتسليم الرسائل، بدلاً من معرّفات المُستخدم المُستخدمة من قِبل جميع الشبكات الأخرى، يستخدم SimpleX معرّفات زوجية مجهولة مؤقتة لقوائم الرسائل، منفصلة لكل اتصال من اتصالاتك — ولا توجد معرّفات طويلة الأجل.", "simplex-network-overlay-card-1-p-1": "بروتوكولات المُراسلة والتطبيقات P2P بها مشاكل مختلفة تجعلها أقل موثوقية من SimpleX وأكثر تعقيدًا في التحليل و عرضة لعدة أنواع من الهجمات.", "hero-overlay-card-2-p-1": "عندما يكون لدى المستخدمين هويات ثابتة، حتى لو كان هذا مجرد رقم عشوائي، مثل معرّف الجلسة، فهناك خطر يتمثل في أن الموفر أو المهاجم يمكنه مراقبة كيفية اتصال المستخدمين وعدد الرسائل التي يرسلونها.", - "hero-overlay-card-1-p-6": "اقرأ المزيد في SimpleX whitepaper.", + "hero-overlay-card-1-p-6": "اقرأ المزيد في SimpleX whitepaper.", "hero-overlay-card-2-p-3": "حتى مع معظم التطبيقات الخاصة التي تستخدم خدمات Tor v3، إذا تحدثت إلى جهتي اتصال مختلفتين عبر نفس الملف الشخصي، فيمكنهما إثبات أنهما متصلان بنفس الشخص.", "hero-overlay-card-2-p-4": "يحمي SimpleX من هذه الهجمات من خلال عدم وجود أي معرّفات مستخدم في تصميمه. وإذا كنت تستخدم وضع التخفي، فسيكون لديك اسم عرض مختلف لكل جهة اتصال، مع تجنب أي بيانات مشتركة بينهما.", "simplex-network-overlay-card-1-li-6": "قد تكون شبكات P2P عرضة DRDoS attack، عندما يتمكّن العملاء من إعادة بث حركة مرور البيانات وتضخيمها، مما يؤدي إلى رفض الخدمة على مستوى الشبكة. يقوم عملاء SimpleX فقط بترحيل حركة مرور البيانات من اتصال معروف ولا يمكن للمهاجم استخدامها لتضخيم حركة مرور البيانات في الشبكة بالكامل.", @@ -119,17 +119,17 @@ "simplex-unique-overlay-card-3-p-2": "يتم الاحتفاظ بالرسائل المُعمَّاة بين الطرفين مؤقتًا على خوادم ترحيل SimpleX حتى يتم استلامها، ثُمَّ تُحذف نهائيًا.", "simplex-unique-overlay-card-3-p-4": "لا توجد معرفّات أو نص مُعَمَّى مشترك بين حركة مرور بيانات الخادم المُرسلة والمُستلمة — إذا كان أي شخص يراقب ذلك، فلن يتمكّن بسهولة من تحديد من يتواصل مع من، حتى لو اختُرق TLS.", "simplex-unique-card-1-p-1": "يحمي SimpleX خصوصية ملف تعريفك، جهات اتصالك والبيانات الوصفية، ويخفيه عن خوادم شبكة SimpleX وأي مراقبين.", - "privacy-matters-overlay-card-2-p-1": "منذ وقت ليس ببعيد، لاحظنا أن الانتخابات الرئيسية يتم التلاعب بها بواسطة شركة استشارية ذات سمعة طيبة التي استخدمت الرسوم البيانية الاجتماعية لتشويه نظرتنا للعالم الحقيقي والتلاعب بأصواتنا.", + "privacy-matters-overlay-card-2-p-1": "منذ وقت ليس ببعيد، لاحظنا أن الانتخابات الرئيسية يتم التلاعب بها بواسطة شركة استشارية ذات سمعة طيبة التي استخدمت الرسوم البيانية الاجتماعية لتشويه نظرتنا للعالم الحقيقي والتلاعب بأصواتنا.", "privacy-matters-overlay-card-2-p-2": "لكي تكون موضوعيًا وتتخذ قرارات مستقلة، عليك التحكم في مساحة معلوماتك. لا يمكن تحقيق ذلك إلا باستخدام شبكة اتصال خاصة لا يمكنها الوصول إلى حسابك على مواقع التواصل الاجتماعي.", "privacy-matters-overlay-card-2-p-3": "SimpleX هي أول شبكة لا تحتوي على أي معرّفات مستخدم من حيث التصميم، وبهذه الطريقة تحمي مخطط اتصالاتك بشكل أفضل من أي بديل معروف.", - "privacy-matters-overlay-card-3-p-2": "واحدة من أكثر القصص إثارة للصدمة هي تجربة محمدو ولد صلاحي الموصوفة في مذكراته والموضحة في فيلم موريتاني. تم وضعه في معتقل غوانتانامو بدون محاكمة، وتعرض للتعذيب هناك لمدة 15 عامًا بعد مكالمة هاتفية مع قريبه في أفغانستان، للاشتباه في تورطه في هجمات 11 سبتمبر، على الرغم من أنه عاش في ألمانيا طوال السنوات العشر الماضية.", - "privacy-matters-overlay-card-3-p-3": "يتم القبض على الأشخاص العاديين بسبب ما يشاركونه عبر الإنترنت، حتى عبر حساباتهم \"المجهولة\"، وحتى في البلدان الديمقراطية.", + "privacy-matters-overlay-card-3-p-2": "واحدة من أكثر القصص إثارة للصدمة هي تجربة محمدو ولد صلاحي الموصوفة في مذكراته والموضحة في فيلم موريتاني. تم وضعه في معتقل غوانتانامو بدون محاكمة، وتعرض للتعذيب هناك لمدة 15 عامًا بعد مكالمة هاتفية مع قريبه في أفغانستان، للاشتباه في تورطه في هجمات 11 سبتمبر، على الرغم من أنه عاش في ألمانيا طوال السنوات العشر الماضية.", + "privacy-matters-overlay-card-3-p-3": "يتم القبض على الأشخاص العاديين بسبب ما يشاركونه عبر الإنترنت، حتى عبر حساباتهم \"المجهولة\"، وحتى في البلدان الديمقراطية.", "simplex-unique-overlay-card-1-p-1": "على عكس شبكات المُراسلة الأخرى، لا يحتوي SimpleX على معرّفات مخصصة للمستخدمين. لا يعتمد على أرقام الهواتف أو العناوين المستندة إلى النطاقات (مثل البريد الإلكتروني أو XMPP)، أسماء المستخدمين، المفاتيح العامة أو حتى الأرقام العشوائية لتحديد مستخدميها — مُشغلي خادم SimpleX لا يعرفون عدد الأشخاص الذين يستخدمون خوادمهم.", "simplex-unique-overlay-card-1-p-2": "لتسليم الرسائل، يستخدم العناوين المزدوجة المجهولة لقوائم انتظار الرسائل أحادية الاتجاه، منفصلة عن الرسائل المُستلمة والمُرسلة، عادةً عبر خوادم مختلفة.", "simplex-unique-overlay-card-3-p-1": "يخزن SimpleX Chat جميع بيانات المستخدم على أجهزة العميل فقط باستخدام تنسيق قاعدة بيانات محمولة مُعمَّاة يمكّن تصديرها ونقلها إلى أي جهاز مدعوم.", "simplex-unique-overlay-card-3-p-3": "على عكس خوادم الشبكات الاتحادية (البريد الإلكتروني أو XMPP أو Matrix)، لا تقوم خوادم SimpleX بتخزين حسابات المستخدمين، فهي تقوم فقط بترحيل الرسائل، مما يحمي خصوصية كلا الطرفين.", "simplex-unique-overlay-card-4-p-1": "يمكنك استخدام SimpleX مع خوادمك والاستمرار في التواصل مع الأشخاص الذين يستخدمون الخوادم المُهيأة مسبقًا في التطبيقات.", - "simplex-unique-overlay-card-4-p-3": "إذا كنت تفكر في التطوير شبكة SimpleX، على سبيل المثال، بوت الدردشة لمستخدمي تطبيق SimpleX، أو دمج مكتبة SimpleX Chat في تطبيقات الأجهزة المحمولة، من فضلك تواصل معي لأي نصيحة والدعم.", + "simplex-unique-overlay-card-4-p-3": "إذا كنت تفكر في التطوير شبكة SimpleX، على سبيل المثال، بوت الدردشة لمستخدمي تطبيق SimpleX، أو دمج مكتبة SimpleX Chat في تطبيقات الأجهزة المحمولة، من فضلك تواصل معي لأي نصيحة والدعم.", "donate-here-to-help-us": "تبرّع هنا لمساعدتنا", "sign-up-to-receive-our-updates": "اشترك للحصول على آخر مستجداتنا", "enter-your-email-address": "أدخل عنوان بريدك الإلكتروني", @@ -209,7 +209,7 @@ "simplex-network-2-desc": "لا تقوم خوادم الترحيل SimpleX بتخزين ملفات تعريف المستخدمين وجهات الاتصال والرسائل التي تم تسليمها، ولا تتصل ببعضها البعض، ولا يوجد دليل خوادم.", "comparison-point-1-text": "يتطلب هوية عالمية", "protocol-3-text": "بروتوكولات P2P", - "simplex-unique-overlay-card-4-p-2": "تستخدم شبكة بروتوكول مفتوح وتوفرSDK لإنشاء روبوتات دردشة، مما يسمح بتنفيذ الخدمات التي يمكن للمستخدمين التفاعل معها عبر تطبيقات SimpleX Chat — التي تتطلع حقًا لمعرفة خدمات SimpleX التي ستبنيها.", + "simplex-unique-overlay-card-4-p-2": "تستخدم شبكة بروتوكول مفتوح وتوفرSDK لإنشاء روبوتات دردشة، مما يسمح بتنفيذ الخدمات التي يمكن للمستخدمين التفاعل معها عبر تطبيقات SimpleX Chat — التي تتطلع حقًا لمعرفة خدمات SimpleX التي ستبنيها.", "guide-dropdown-1": "بداية سريعة", "guide-dropdown-2": "إرسال الرسائل", "guide-dropdown-3": "مجموعات سرية", diff --git a/website/langs/cs.json b/website/langs/cs.json index 90bad228aa..0df72eeab9 100644 --- a/website/langs/cs.json +++ b/website/langs/cs.json @@ -1,7 +1,7 @@ { "simplex-private-card-10-point-2": "Umožňuje doručovat zprávy bez identifikátoru uživatelských profilů, což poskytuje lepší soukromí metadat než alternativy.", "simplex-unique-4-overlay-1-title": "Plně decentralizované — uživatelé vlastní síť SimpleX", - "hero-overlay-card-1-p-6": "Více v SimpleX whitepaper.", + "hero-overlay-card-1-p-6": "Více v SimpleX whitepaper.", "hero-overlay-card-1-p-2": "K doručování zpráv používá SimpleX namísto ID uživatelů používaných všemi ostatními sítěmi, dočasné anonymní párové identifikátory front zpráv, oddělené pro každé z vašich připojení — neexistují žádné dlouhodobé identifikátory.", "hero-overlay-card-1-p-3": "Definujete, které servery se mají používat k přijímání zpráv, vašich kontaktů — servery, které používáte k odesílání zpráv. Každá konverzace bude pravděpodobně používat dva různé servery.", "hero-overlay-card-2-p-3": "I v těch nejsoukromějších aplikacích, které používají služby Tor v3, pokud mluvíte se dvěma různými kontakty prostřednictvím stejného profilu, může být prokázáno, že jsou spojeni se stejnou osobou.", @@ -89,11 +89,11 @@ "simplex-network-overlay-card-1-li-3": "P2P neřeší problém MITM útoku a většina existujících implementací nepoužívá out-of-band zprávy pro počáteční výměnu klíčů. SimpleX používá out-of-band zprávy nebo v některých případech již existující zabezpečená a důvěryhodná připojení pro počáteční výměnu klíčů.", "simplex-network-overlay-card-1-li-4": "Implementace P2P mohou být některými poskytovateli internetu blokovány (například BitTorrent). SimpleX je transportně nezávislý — může pracovat přes standardní webové protokoly, např. WebSockety.", "privacy-matters-overlay-card-1-p-1": "Mnoho velkých společností používá informace o tom, s kým jste ve spojení, k odhadu vašich příjmů, prodeji produktů, které ve skutečnosti nepotřebujete, a ke stanovení cen.", - "privacy-matters-overlay-card-1-p-3": "Některé finanční a pojišťovací společnosti používají sociální grafy k určení úrokových sazeb a pojistného. Často přiměje lidi s nižšími příjmy platit více —, je to známé jako \"prémie za chudobu\".", - "privacy-matters-overlay-card-2-p-1": "Není to tak dávno, co jsme pozorovali, jak velké volby zmanipulovala renomovaná poradenská společnost, která používala naše sociální grafy ke zkreslení našeho pohledu na skutečný svět a manipulovala s našimi hlasy.", + "privacy-matters-overlay-card-1-p-3": "Některé finanční a pojišťovací společnosti používají sociální grafy k určení úrokových sazeb a pojistného. Často přiměje lidi s nižšími příjmy platit více —, je to známé jako \"prémie za chudobu\".", + "privacy-matters-overlay-card-2-p-1": "Není to tak dávno, co jsme pozorovali, jak velké volby zmanipulovala renomovaná poradenská společnost, která používala naše sociální grafy ke zkreslení našeho pohledu na skutečný svět a manipulovala s našimi hlasy.", "privacy-matters-overlay-card-2-p-3": "SimpleX je první síť, která nemá žádné uživatelské identifikátory záměrně, a tímto způsobem chrání váš graf připojení lépe než jakákoli známá alternativa.", "privacy-matters-overlay-card-3-p-1": "Každý by se měl starat o soukromí a bezpečnost své komunikace — neškodné rozhovory vás mohou vystavit nebezpečí, i když nemáte co skrývat.", - "privacy-matters-overlay-card-3-p-3": "Obyčejní lidé jsou zatčeni za to, co sdílejí online, dokonce i prostřednictvím svých \"anonymních\" účtů, i v demokratických zemích.", + "privacy-matters-overlay-card-3-p-3": "Obyčejní lidé jsou zatčeni za to, co sdílejí online, dokonce i prostřednictvím svých \"anonymních\" účtů, i v demokratických zemích.", "privacy-matters-overlay-card-3-p-4": "Nestačí používat end-to-end šifrovaný messenger, všichni bychom měli používat messengery, které chrání soukromí našich osobních sítí — s kým jsme spojeni.", "simplex-unique-overlay-card-1-p-3": "Tento design chrání soukromí těch, s kým komunikujete, skrývá ho před servery sítě SimpleX a před jakýmikoli pozorovateli. Pro skrytí své IP adresy před servery, můžete se připojit k serverům SimpleX přes Tor.", "simplex-unique-overlay-card-2-p-1": "Protože na síti SimpleX nemáte žádný identifikátor, nikdo vás nemůže kontaktovat pokud nenasdílíte jednorázovou nebo dočasnou uživatelskou adresu, jako QR kód nebo odkaz.", @@ -101,7 +101,7 @@ "simplex-unique-overlay-card-3-p-2": "End-to-end šifrované zprávy jsou dočasně uchovávány na přenosových serverech SimpleX, dokud nejsou přijaty, poté jsou trvale odstraněny.", "simplex-unique-overlay-card-3-p-3": "Na rozdíl od serverů federovaných sítí (e-mail, XMPP nebo Matrix) servery SimpleX neukládají uživatelské účty, pouze předávají zprávy, čímž chrání soukromí obou stran.", "simplex-unique-overlay-card-4-p-1": "Můžete použít SimpleX se svými vlastními servery a přesto komunikovat s lidmi, kteří používají přednastavené servery v aplikacích.", - "simplex-unique-overlay-card-4-p-3": "Pokud uvažujete o vývoji pro SimpleX síť, například chat bot pro uživatele aplikace SimpleX nebo integraci knihovny SimpleX chat do Vasí mobilní aplikace, prosím buďte ve spojení pro jakoukoli radu a podporu.", + "simplex-unique-overlay-card-4-p-3": "Pokud uvažujete o vývoji pro SimpleX síť, například chat bot pro uživatele aplikace SimpleX nebo integraci knihovny SimpleX chat do Vasí mobilní aplikace, prosím buďte ve spojení pro jakoukoli radu a podporu.", "simplex-unique-card-1-p-1": "SimpleX chrání soukromí vašeho profilu, kontaktů a metadat a skrývá je před servery SimpleX sítě a jakýmikoli pozorovateli.", "simplex-unique-card-1-p-2": "Na rozdíl od jakékoli jiné existující síti pro zasílání zpráv nemá SimpleX žádné identifikátory přiřazené uživatelům — ani náhodná čísla.", "simplex-unique-card-3-p-1": "SimpleX Chat ukládá všechna uživatelská data pouze na klientských zařízeních pomocí přenosného šifrovaného databázového formátu, který lze exportovat a přenést na jakékoli podporované zařízení.", @@ -181,14 +181,14 @@ "privacy-matters-overlay-card-1-p-4": "SimpleX síť chrání soukromí vašich připojení lépe než jakákoli jiná alternativa a plně zabraňuje tomu, aby byl váš sociální graf dostupný všem společnostem nebo organizacím. I když lidé používají servery přednastavené v aplikaci SimpleX Chat, operátoři serverů neznají počet uživatelů ani jejich připojení.", "privacy-matters-overlay-card-2-p-2": "Chcete-li být objektivní a činit nezávislá rozhodnutí, musíte mít svůj informační prostor pod kontrolou. Je to možné pouze v případě, že používáte soukromou komunikační síť, která nemá přístup k vašemu sociálnímu grafu.", "simplex-unique-overlay-card-1-p-2": "K doručování zpráv SimpleX používá párové anonymní adresy jednosměrných front zpráv, oddělených pro přijaté a odeslané zprávy, obvykle přes různé servery.", - "privacy-matters-overlay-card-3-p-2": "Jedním z nejvíce šokujících příběhů je zkušenost Mohamedoua Oulda Salahiho popsaná v jeho pamětech a zobrazená v Mauritánském filmu. Byl umístěn do tábora na Guantánamu bez soudu a byl tam 15 let mučen po telefonátu svému příbuznému v Afghánistánu pro podezření z účasti na útocích z 11. září, i když předchozích 10 let žil v Německu.", + "privacy-matters-overlay-card-3-p-2": "Jedním z nejvíce šokujících příběhů je zkušenost Mohamedoua Oulda Salahiho popsaná v jeho pamětech a zobrazená v Mauritánském filmu. Byl umístěn do tábora na Guantánamu bez soudu a byl tam 15 let mučen po telefonátu svému příbuznému v Afghánistánu pro podezření z účasti na útocích z 11. září, i když předchozích 10 let žil v Německu.", "simplex-unique-overlay-card-1-p-1": "Na rozdíl od jiných sítí pro zasílání zpráv nemá SimpleX žádné identifikátory přiřazené uživatelům. Nespoléhá na telefonní čísla, adresy založené na doméně (jako e-mail nebo XMPP), uživatelská jména, veřejné klíče nebo dokonce náhodná čísla k identifikaci svých uživatelů — Operátoři SimpleX serverů neví, kolik lidí používá jejich servery.", "invitation-hero-header": "Byl vám zaslán odkaz pro připojení na SimpleX Chat", "simplex-unique-overlay-card-3-p-1": "SimpleX Chat ukládá všechna uživatelská data pouze na klientských zařízeních pomocí přenosného šifrovaného databázového formátu, který lze exportovat a přenést na jakékoli podporované zařízení.", "contact-hero-p-1": "Veřejné klíče a adresa fronty zpráv v tomto odkazu NEJSOU při zobrazení této stránky odesílány přes síť — jsou obsaženy ve fragmentu kontrolního součtu adresy URL odkazu.", "simplex-unique-overlay-card-3-p-4": "Mezi odeslaným a přijatým provozem serveru nejsou žádné společné identifikátory ani šifrovaný text — pokud to někdo pozoruje, nemůže snadno určit, kdo s kým komunikuje, i když je TLS kompromitován.", "simplex-unique-card-2-p-1": "Protože na SimpleX síti nemáte žádný identifikátor nebo pevnou adresu, nikdo vás nemůže kontaktovat pokud nenasdílíte jednorázovou nebo dočasnou uživatelskou adresu, jako QR kód nebo odkaz.", - "simplex-unique-overlay-card-4-p-2": "SimpleX síť používá otevřený protokol a poskytuje SDK k vytváření chat botů, což umožní implementaci služeb, díky nimž boudou moci uživatelé komunikovat prostřednictvím aplikací SimpleX Chat — Opravdu se těšíme, jaké služby SimpleX vytvoříte.", + "simplex-unique-overlay-card-4-p-2": "SimpleX síť používá otevřený protokol a poskytuje SDK k vytváření chat botů, což umožní implementaci služeb, díky nimž boudou moci uživatelé komunikovat prostřednictvím aplikací SimpleX Chat — Opravdu se těšíme, jaké služby SimpleX vytvoříte.", "simplex-network": "SimpleX síť", "simplex-explained-tab-2-p-1": "Pro každé připojení používáte dvě samostatné fronty zasílání zpráv k odesílání a přijímání zpráv prostřednictvím různých serverů.", "simplex-explained-tab-1-p-1": "Můžete vytvářet kontakty a skupiny a vést obousměrné konverzace, stejně jako v jakémkoli jiném messengeru.", @@ -201,7 +201,7 @@ "simplex-private-8-title": "Smíchání zpráv
ke snížení korelace", "simplex-unique-1-overlay-1-title": "Úplné soukromí vaší identity, profilu, kontaktů a metadat", "simplex-private-card-2-point-1": "Další vrstva šifrování serveru pro doručení příjemci, aby se zabránilo korelaci mezi přijatým a odeslaným serverovým provozem, pokud je protokol TLS ohrožen.", - "simplex-private-card-4-point-2": "Pro použití SimpleX přes Tor prosím nainstalujte Orbot aplikaci a povolte SOCKS5 proxy (nebo VPN na iOS).", + "simplex-private-card-4-point-2": "Pro použití SimpleX přes Tor prosím nainstalujte Orbot aplikaci a povolte SOCKS5 proxy (nebo VPN na iOS).", "simplex-private-card-5-point-1": "SimpleX používá odsazení obsahu pro každou šifrovací vrstvu, aby zmařil útoky na velikost zprávy.", "simplex-private-card-6-point-2": "Aby se tomu zabránilo, aplikace SimpleX předávají jednorázové klíče mimo pásmo, když sdílíte adresu jako odkaz nebo QR kód.", "simplex-private-card-8-point-1": "Servery SimpleX fungují jako smíšené uzly s nízkou latencí — příchozí a odchozí zprávy mají různá pořadí.", diff --git a/website/langs/de.json b/website/langs/de.json index ac35879b3a..34cecb8e4d 100644 --- a/website/langs/de.json +++ b/website/langs/de.json @@ -62,7 +62,7 @@ "simplex-private-card-3-point-2": "Server-Fingerabdrücke und Kanalbindung verhindern MITM- und Replay-Angriffe.", "simplex-private-card-3-point-3": "Die Wiederaufnahme von Verbindungen ist deaktiviert, um Sitzungs-Angriffe zu verhindern.", "simplex-private-card-4-point-1": "Um Ihre IP-Adresse zu schützen, können Sie per Tor oder irgendeinem anderen Transportschichten-Netzwerk auf Server zugreifen.", - "simplex-private-card-4-point-2": "Um SimpleX per Tor zu nutzen, installieren Sie unter Android bitte die Orbot-App und aktivieren Sie den SOCKS5-Proxy oder unter iOS per VPN.", + "simplex-private-card-4-point-2": "Um SimpleX per Tor zu nutzen, installieren Sie unter Android bitte die Orbot-App und aktivieren Sie den SOCKS5-Proxy oder unter iOS per VPN.", "simplex-private-card-5-point-1": "SimpleX nutzt Inhalteauffüllung für jede Verschlüsselungs-Schicht, um Angriffe auf die Nachrichtengröße zu vereiteln.", "simplex-private-card-5-point-2": "Erzeugt Nachrichten mit unterschiedlichen Größen, die für Server und Netzwerk-Beobachter identisch aussehen.", "simplex-private-card-6-point-1": "Viele Kommunikations-Netzwerke sind für MITM-Angriffe durch Server oder Netzwerk-Anbieter anfällig.", @@ -93,7 +93,7 @@ "simplex-private-card-8-point-1": "Die SimpleX-Server arbeiten als Mix-Knoten mit geringer Verzögerung — eingehende und ausgehende Nachrichten haben eine unterschiedliche Reihenfolge.", "hero-overlay-card-1-p-3": "Sie definieren, welche(n) Server Sie für den Empfang von Nachrichten nutzen. Ihre Kontakte — nutzen diese Server, um ihnen Nachrichten darüber zu senden. Jede Konversation nutzt üblicherweise also zwei unterschiedliche Server.", "hero-overlay-card-1-p-5": "Die Benutzer-Profile, Kontakte und Gruppen werden nur auf den Endgerät des Nutzers gespeichert. Die Nachrichten werden mit einer 2-Schichten-Ende-zu-Ende-Verschlüsselung versendet.", - "hero-overlay-card-1-p-6": "Lesen Sie mehr darüber im SimpleX Whitepaper.", + "hero-overlay-card-1-p-6": "Lesen Sie mehr darüber im SimpleX Whitepaper.", "hero-overlay-card-2-p-2": "Sie können diese Informationen mit bestehenden öffentlichen sozialen Netzwerken korrelieren und damit wahre Identitäten herausfinden.", "hero-overlay-card-2-p-3": "Wenn Sie sich mit zwei unterschiedlichen Kontakten über dasselbe Profil unterhalten, können sie, selbst bei sehr auf Privatsphäre bedachten Apps, die Tor-v3-Dienste nutzen, feststellen, dass diese Kontakte mit derselben Person verbunden sind.", "hero-overlay-card-1-p-2": "Um Nachrichten auszuliefern, nutzt SimpleX statt Benutzerkennungen wie alle anderen Netzwerke nur temporäre, anonyme und paarweise Kennungen für Nachrichten-Warteschlangen, die für jede Ihrer Verbindungen unterschiedlich sind — es gibt keinerlei Langzeit-Kennungen.", @@ -106,7 +106,7 @@ "simplex-network-overlay-card-1-li-6": "P2P-Netzwerke können anfällig für DRDoS-Angriffe sein, wenn die Clients den Datenverkehr erneut senden und verstärken können, was zu einem netzwerkweiten Denial-of-Service führt. SimpleX-Clients leiten nur Datenverkehr von bekannten Verbindungen weiter und können von einem Angreifer nicht dazu verwendet werden, den Datenverkehr im gesamten Netzwerk zu verstärken.", "privacy-matters-overlay-card-1-p-1": "Viele große Unternehmen nutzen Informationen, mit wem Sie in Verbindung stehen, um Ihr Einkommen zu schätzen, Ihnen Produkte zu verkaufen, die Sie nicht wirklich benötigen und um die Preise zu bestimmen.", "privacy-matters-overlay-card-1-p-2": "Online-Händler wissen, dass Menschen mit geringerem Einkommen eher dringende Einkäufe tätigen, sodass sie möglicherweise höhere Preise verlangen können oder Rabatte streichen.", - "privacy-matters-overlay-card-1-p-3": "Einige Finanz- und Versicherungsunternehmen verwenden soziale Graphen, um Zinssätze und Prämien zu ermitteln. Menschen mit niedrigerem Einkommen zahlen so häufig mehr — dies ist als \"Armutsprämie\" bekannt.", + "privacy-matters-overlay-card-1-p-3": "Einige Finanz- und Versicherungsunternehmen verwenden soziale Graphen, um Zinssätze und Prämien zu ermitteln. Menschen mit niedrigerem Einkommen zahlen so häufig mehr — dies ist als \"Armutsprämie\" bekannt.", "privacy-matters-overlay-card-2-p-2": "Um objektiv zu sein und unabhängige Entscheidungen treffen zu können, müssen Sie die Kontrolle über Ihren Informationsraum haben. Dies ist nur möglich, wenn Sie ein privates Kommunikations-Netzwerk verwenden, welches keinen Zugriff auf Ihren sozialen Graphen hat.", "privacy-matters-overlay-card-2-p-3": "SimpleX ist das erste Netzwerk, das konzeptionell ohne Benutzerkennungen auskommt und dadurch Ihren Verbindungsgraphen besser schützt als jede bekannte Alternative.", "privacy-matters-overlay-card-3-p-1": "Jede Person sollte sich um ihre Privatsphäre und die Sicherheit ihrer Kommunikation kümmern — Harmlose Gespräche könnten Sie in Gefahr bringen, selbst wenn Sie nichts zu verbergen haben.", @@ -176,8 +176,8 @@ "comparison-section-list-point-7": "P2P-Netzwerke haben entweder eine zentrale Verwaltung oder das gesamte Netzwerk kann kompromittiert werden", "see-here": "Siehe hier", "simplex-unique-card-4-p-1": "Das SimpleX-Netzwerk ist vollständig dezentralisiert und unabhängig von Kryptowährungen oder anderen Netzwerken außer dem Internet.", - "privacy-matters-overlay-card-2-p-1": "Vor nicht allzu langer Zeit beobachteten wir, wie große Wahlen von einem angesehenen Beratungsunternehmen manipuliert wurden, welches unsere sozialen Graphen nutzte, um unsere Sicht auf die reale Welt zu verzerren und unsere Stimmen zu manipulieren.", - "privacy-matters-overlay-card-3-p-2": "Eine der schockierendsten Geschichten ist die Erfahrung von Mohamedou Ould Salahi, welche in seinen Memoiren beschrieben und im Film „The Mauritanian“ gezeigt wird. Er kam nach einem Anruf bei seinen Verwandten in Afghanistan und ohne Gerichtsverfahren in das Guantanamo-Lager und wurde dort einige Jahre lang gefoltert, weil er verdächtigt wurde, an den 9/11-Angriffen beteiligt gewesen zu sein, obwohl er die vorhergehenden 10 Jahre in Deutschland gelebt hatte.", + "privacy-matters-overlay-card-2-p-1": "Vor nicht allzu langer Zeit beobachteten wir, wie große Wahlen von einem angesehenen Beratungsunternehmen manipuliert wurden, welches unsere sozialen Graphen nutzte, um unsere Sicht auf die reale Welt zu verzerren und unsere Stimmen zu manipulieren.", + "privacy-matters-overlay-card-3-p-2": "Eine der schockierendsten Geschichten ist die Erfahrung von Mohamedou Ould Salahi, welche in seinen Memoiren beschrieben und im Film „The Mauritanian“ gezeigt wird. Er kam nach einem Anruf bei seinen Verwandten in Afghanistan und ohne Gerichtsverfahren in das Guantanamo-Lager und wurde dort einige Jahre lang gefoltert, weil er verdächtigt wurde, an den 9/11-Angriffen beteiligt gewesen zu sein, obwohl er die vorhergehenden 10 Jahre in Deutschland gelebt hatte.", "simplex-unique-overlay-card-1-p-1": "Im Gegensatz zu anderen Nachrichten-Netzwerken weist SimpleX den Benutzern keine Kennungen zu. Es verlässt sich nicht auf Telefonnummern, domänenbasierte Adressen (wie E-Mail oder XMPP), Benutzernamen, öffentliche Schlüssel oder Zufallszahlen, um seine Benutzer zu identifizieren — selbst SimpleX Server-Betreiber wissen nicht, wie viele Personen deren Server verwenden.", "simplex-unique-overlay-card-1-p-2": "Um Nachrichten auszuliefern nutzt SimpleX paarweise anonyme Adressen aus unidirektionalen Nachrichten-Warteschlangen, die für empfangene und gesendete Nachrichten separiert sind und gewöhnlich über verschiedene Server gesendet werden.", "simplex-network-overlay-card-1-li-5": "Alle bekannten P2P-Netzwerke können anfällig für Sybil-Angriffe sein, da jeder Knoten ermittelbar ist und das Netzwerk als Ganzes funktioniert. Bekannte Maßnahmen zur Verhinderung erfordern entweder eine zentralisierte Komponente oder einen teuren Ausführungsnachweis. Das SimpleX-Netzwerk bietet keine Ermittlung der Server, ist fragmentiert und arbeitet mit mehreren isolierten Subnetzwerken, wodurch netzwerkweite Angriffe unmöglich werden.", @@ -197,14 +197,14 @@ "comparison-section-list-point-5": "Die Privatsphäre-Metadaten des Nutzers werden nicht geschützt", "simplex-network-overlay-card-1-li-1": "P2P-Netzwerke vertrauen auf Varianten von DHT, um Nachrichten zu routen. DHT-Designs müssen zwischen Zustellungsgarantie und Latenz ausgleichen. Verglichen mit P2P bietet SimpleX sowohl eine bessere Zustellungsgarantie als auch eine niedrigere Latenz, weil eine Nachricht redundant und parallel über mehrere Server gesendet werden kann, wobei die durch den Empfänger ausgewählten Server genutzt werden. In P2P-Netzwerken werden Nachrichten sequentiell über O(log N)-Knoten gesendet, wobei die Knoten durch einen Algorithmus ausgewählt werden.", "simplex-unique-overlay-card-3-p-4": "Zwischen dem gesendeten und dem empfangenen Serververkehr gibt es keine gemeinsamen Kennungen oder Chiffriertexte — sodass ein Beobachter nicht ohne weiteres feststellen kann, wer mit wem kommuniziert, selbst wenn TLS kompromittiert wurde.", - "simplex-unique-overlay-card-4-p-3": "Falls Sie Interesse daran haben, aktiv bei der Entwicklung des SimpleX-Netzwerks mitzuhelfen, z.B. einen Chatbot für SimpleX App-Nutzer zu entwickeln oder die Integration der SimpleX Chat-Bibliothek in mobile Apps voranzutreiben, kontaktieren Sie uns bitte für eine weitere Beratung und Unterstützung.", + "simplex-unique-overlay-card-4-p-3": "Falls Sie Interesse daran haben, aktiv bei der Entwicklung des SimpleX-Netzwerks mitzuhelfen, z.B. einen Chatbot für SimpleX App-Nutzer zu entwickeln oder die Integration der SimpleX Chat-Bibliothek in mobile Apps voranzutreiben, kontaktieren Sie uns bitte für eine weitere Beratung und Unterstützung.", "privacy-matters-overlay-card-1-p-4": "Das SimpleX-Netzwerk schützt die Privatsphäre Ihrer Verbindungen besser als jede Alternative und verhindert vollständig, dass Ihr sozialer Graph für Unternehmen oder Organisationen einsehbar wird. Selbst wenn Anwender die in der SimpleX Chat-App vorkonfigurierten Server verwenden, kennen die Server-Betreiber die Anzahl der Benutzer oder deren Verbindungen nicht.", "contact-hero-header": "Sie haben eine Adresse zur Verbindung mit SimpleX Chat erhalten", "invitation-hero-header": "Sie haben einen Einmal-Link zur Verbindung mit SimpleX Chat erhalten", - "privacy-matters-overlay-card-3-p-3": "Selbst in demokratischen Ländern werden normale Menschen, auch unter Nutzung ihrer „anonymen“ Benutzerkonten, für das, was sie online teilen, verhaftet.", + "privacy-matters-overlay-card-3-p-3": "Selbst in demokratischen Ländern werden normale Menschen, auch unter Nutzung ihrer „anonymen“ Benutzerkonten, für das, was sie online teilen, verhaftet.", "simplex-unique-overlay-card-3-p-1": "SimpleX Chat speichert alle Benutzerdaten ausschließlich auf den Endgeräten in einem portablen und verschlüsselten Datenbankformat, welches exportiert und auf jedes unterstützte Gerät übertragen werden kann.", "simplex-unique-overlay-card-2-p-2": "Auch wenn die optionale Benutzeradresse zum Versenden von Spam-Kontaktanfragen verwendet werden kann, können Sie sie ändern oder ganz löschen, ohne dass Ihre Verbindungen verloren gehen.", - "simplex-unique-overlay-card-4-p-2": "Das SimpleX-Netzwerk verwendet ein offenes Protokoll und bietet ein SDK zur Erstellung von Chatbots an. Dies ermöglicht die Erstellung von Diensten, mit denen Nutzer über SimpleX Chat-Apps interagieren können — wir freuen uns sehr darauf zu sehen, welche SimpleX-Dienste Sie entwickeln werden.", + "simplex-unique-overlay-card-4-p-2": "Das SimpleX-Netzwerk verwendet ein offenes Protokoll und bietet ein SDK zur Erstellung von Chatbots an. Dies ermöglicht die Erstellung von Diensten, mit denen Nutzer über SimpleX Chat-Apps interagieren können — wir freuen uns sehr darauf zu sehen, welche SimpleX-Dienste Sie entwickeln werden.", "simplex-unique-card-4-p-2": "Sie können SimpleX mit eigenen Servern oder mit den von uns zur Verfügung gestellten Servern verwenden — und sich trotzdem mit jedem Benutzer verbinden.", "why-simplex-is-unique": "Warum ist SimpleX einmalig", "contact-hero-p-1": "Die öffentlichen Schlüssel und die Adresse der Nachrichtenwarteschlange in diesem Link werden NICHT über das Netzwerk gesendet, wenn Sie diese Seite aufrufen — sie sind in dem Hash-Fragment der Link-URL enthalten.", diff --git a/website/langs/en.json b/website/langs/en.json index 24dde29b39..6138b34c8c 100644 --- a/website/langs/en.json +++ b/website/langs/en.json @@ -66,7 +66,7 @@ "simplex-private-card-3-point-2": "Server fingerprint and channel binding prevent MITM and replay attacks.", "simplex-private-card-3-point-3": "Connection resumption is disabled to prevent session attacks.", "simplex-private-card-4-point-1": "To protect your IP address you can access the servers via Tor or some other transport overlay network.", - "simplex-private-card-4-point-2": "To use SimpleX via Tor please install Orbot app and enable SOCKS5 proxy (or VPN on iOS).", + "simplex-private-card-4-point-2": "To use SimpleX via Tor please install Orbot app and enable SOCKS5 proxy (or VPN on iOS).", "simplex-private-card-5-point-1": "SimpleX uses content padding for each encryption layer to frustrate message size attacks.", "simplex-private-card-5-point-2": "It makes messages of different sizes look the same to the servers and network observers.", "simplex-private-card-6-point-1": "Many communication networks are vulnerable to MITM attacks by servers or network providers.", @@ -100,7 +100,7 @@ "hero-overlay-card-1-p-3": "You define which server(s) to use to receive the messages, your contacts — the servers you use to send the messages to them. Every conversation is likely to use two different servers.", "hero-overlay-card-1-p-4": "This design prevents leaking any users' metadata on the application level. To further improve privacy and protect your IP address you can connect to messaging servers via Tor.", "hero-overlay-card-1-p-5": "Only client devices store user profiles, contacts and groups; the messages are sent with 2-layer end-to-end encryption.", - "hero-overlay-card-1-p-6": "Read more in SimpleX whitepaper.", + "hero-overlay-card-1-p-6": "Read more in SimpleX whitepaper.", "hero-overlay-card-2-p-1": "When users have persistent identities, even if this is just a random number, like a Session ID, there is a risk that the provider or an attacker can observe how the users are connected and how many messages they send.", "hero-overlay-card-2-p-2": "They could then correlate this information with the existing public social networks, and determine some real identities.", "hero-overlay-card-2-p-3": "Even with the most private apps that use Tor v3 services, if you talk to two different contacts via the same profile they can prove that they are connected to the same person.", @@ -117,14 +117,14 @@ "simplex-network-overlay-card-1-li-6": "P2P networks may be vulnerable to DRDoS attack, when the clients can rebroadcast and amplify traffic, resulting in network-wide denial of service. SimpleX clients only relay traffic from known connections and cannot be used by an attacker to amplify the traffic in the whole network.", "privacy-matters-overlay-card-1-p-1": "Many large companies use information about who you are connected with to estimate your income, sell you the products you don't really need, and to determine the prices.", "privacy-matters-overlay-card-1-p-2": "Online retailers know that people with lower incomes are more likely to make urgent purchases, so they may charge higher prices or remove discounts.", - "privacy-matters-overlay-card-1-p-3": "Some financial and insurance companies use social graphs to determine interest rates and premiums. It often makes people with lower incomes pay more — it is known as \"poverty premium\".", + "privacy-matters-overlay-card-1-p-3": "Some financial and insurance companies use social graphs to determine interest rates and premiums. It often makes people with lower incomes pay more — it is known as \"poverty premium\".", "privacy-matters-overlay-card-1-p-4": "SimpleX network protects the privacy of your connections better than any alternative, fully preventing your social graph from becoming available to any companies or organizations. Even when people use servers preconfigured in SimpleX Chat apps, server operators do not know the number of users or their connections.", - "privacy-matters-overlay-card-2-p-1": "Not so long ago we observed the major elections being manipulated by a reputable consulting company that used our social graphs to distort our view of the real world and manipulate our votes.", + "privacy-matters-overlay-card-2-p-1": "Not so long ago we observed the major elections being manipulated by a reputable consulting company that used our social graphs to distort our view of the real world and manipulate our votes.", "privacy-matters-overlay-card-2-p-2": "To be objective and to make independent decisions you need to be in control of your information space. It is only possible if you use a private communication network that does not have access to your social graph.", "privacy-matters-overlay-card-2-p-3": "SimpleX is the first network that doesn't have any user identifiers by design, in this way protecting your connections graph better than any known alternative.", "privacy-matters-overlay-card-3-p-1": "Everyone should care about privacy and security of their communications — harmless conversations can put you in danger, even if you have nothing to hide.", - "privacy-matters-overlay-card-3-p-2": "One of the most shocking stories is the experience of Mohamedou Ould Salahi described in his memoir and shown in The Mauritanian movie. He was put into Guantanamo camp, without trial, and was tortured there for 15 years after a phone call to his relative in Afghanistan, under suspicion of being involved in 9/11 attacks, even though he lived in Germany for the previous 10 years.", - "privacy-matters-overlay-card-3-p-3": "Ordinary people get arrested for what they share online, even via their \"anonymous\" accounts, even in democratic countries.", + "privacy-matters-overlay-card-3-p-2": "One of the most shocking stories is the experience of Mohamedou Ould Salahi described in his memoir and shown in The Mauritanian movie. He was put into Guantanamo camp, without trial, and was tortured there for 15 years after a phone call to his relative in Afghanistan, under suspicion of being involved in 9/11 attacks, even though he lived in Germany for the previous 10 years.", + "privacy-matters-overlay-card-3-p-3": "Ordinary people get arrested for what they share online, even via their \"anonymous\" accounts, even in democratic countries.", "privacy-matters-overlay-card-3-p-4": "It is not enough to use an end-to-end encrypted messenger, we all should use the messengers that protect the privacy of our personal networks — who we are connected with.", "simplex-unique-overlay-card-1-p-1": "Unlike other messaging networks, SimpleX has no identifiers assigned to the users. It does not rely on phone numbers, domain-based addresses (like email or XMPP), usernames, public keys or even random numbers to identify its users — SimpleX server operators don't know how many people use their servers.", "simplex-unique-overlay-card-1-p-2": "To deliver messages SimpleX uses pairwise anonymous addresses of unidirectional message queues, separate for received and sent messages, usually via different servers.", @@ -136,8 +136,8 @@ "simplex-unique-overlay-card-3-p-3": "Unlike federated networks servers (email, XMPP or Matrix), SimpleX servers don't store user accounts, they only relay messages, protecting the privacy of both parties.", "simplex-unique-overlay-card-3-p-4": "There are no identifiers or ciphertext in common between sent and received server traffic — if anybody is observing it, they cannot easily determine who communicates with whom, even if TLS is compromised.", "simplex-unique-overlay-card-4-p-1": "You can use SimpleX with your own servers and still communicate with people who use the servers preconfigured in the apps.", - "simplex-unique-overlay-card-4-p-2": "SimpleX network uses an open protocol and provides SDK to create chat bots, allowing implementation of services that users can interact with via SimpleX Chat apps — we're really looking forward to seeing what SimpleX services you will build.", - "simplex-unique-overlay-card-4-p-3": "If you are considering developing for the SimpleX network, for example, the chat bot for SimpleX app users, or the integration of the SimpleX Chat library into your mobile apps, please get in touch for any advice and support.", + "simplex-unique-overlay-card-4-p-2": "SimpleX network uses an open protocol and provides SDK to create chat bots, allowing implementation of services that users can interact with via SimpleX Chat apps — we're really looking forward to seeing what SimpleX services you will build.", + "simplex-unique-overlay-card-4-p-3": "If you are considering developing for the SimpleX network, for example, the chat bot for SimpleX app users, or the integration of the SimpleX Chat library into your mobile apps, please get in touch for any advice and support.", "simplex-unique-card-1-p-1": "SimpleX protects the privacy of your profile, contacts and metadata, hiding it from SimpleX network servers and any observers.", "simplex-unique-card-1-p-2": "Unlike any other existing messaging network, SimpleX has no identifiers assigned to the users — not even random numbers.", "simplex-unique-card-2-p-1": "Because you have no identifier or fixed address on the SimpleX network, nobody can contact you unless you share a one-time or temporary user address, as a QR code or a link.", @@ -291,7 +291,7 @@ "index-token-p2": "This will cover infrastructure, software development and network governance.", "index-token-cta": "Learn more about Community Credits", "index-token-p2-cf": "And the users can now invest in SimpleX Chat — the equity crowdfunding is live!", - "index-token-cta-cf": "Get a stake in SimpleX Chat", + "index-token-cta-cf": "Invest in SimpleX Chat", "index-roadmap-h2": "SimpleX Roadmap to Free Internet", "index-roadmap-now": "Now", "index-roadmap-1": "2026", diff --git a/website/langs/es.json b/website/langs/es.json index c5d4c5291e..22b69cfbeb 100644 --- a/website/langs/es.json +++ b/website/langs/es.json @@ -32,7 +32,7 @@ "hero-p-1": "Las demás aplicaciones usan ID de usuario: Signal, Matrix, Session, Briar, Jami, Cwtch, etc.
SimpleX no los tiene, ni siquiera usa números aleatorios.
Esto mejora radicalmente tu privacidad.", "hero-2-header-desc": "El vídeo muestra cómo te conectas con tus amistades a través del código QR de un solo uso, en persona o a través de videollamada. También puedes conectarte compartiendo un enlace de invitación.", "feature-7-title": "Almacenamiento portable y cifrado, podrás transferir tu perfil a otro dispositivo", - "simplex-private-card-4-point-2": "Para usar SimpleX a través de Tor, instala la aplicación Orbot y activa el proxy SOCKS5 (o VPN en iOS).", + "simplex-private-card-4-point-2": "Para usar SimpleX a través de Tor, instala la aplicación Orbot y activa el proxy SOCKS5 (o VPN en iOS).", "simplex-private-card-3-point-1": "Para las conexiones cliente servidor se usan exclusivamente el protocolo TLS 1.2/1.3 con algoritmos robustos.", "simplex-private-card-4-point-1": "Para proteger tu dirección IP, puedes acceder a los servidores a través Tor u otra red superpuesta de transporte.", "simplex-private-card-5-point-2": "Esto hace que mensajes de distintos tamaños parezcan iguales desde el punto de vista de servidores y observadores de red.", @@ -84,7 +84,7 @@ "simplex-unique-4-title": "La red SimpleX te pertenece", "hero-overlay-card-1-p-1": "Muchos usuarios se preguntan: si SimpleX no tiene identificadores de usuario, ¿cómo puede saber dónde entregar los mensajes?", "hero-overlay-card-1-p-4": "Este diseño evita que se filtren metadatos de usuario a nivel de aplicación. Para mejorar aún más la privacidad y proteger tu dirección IP, puedes conectarte a los servidores de mensajería a través de la red Tor.", - "hero-overlay-card-1-p-6": "Encontrarás más información en el SimpleX whitepaper.", + "hero-overlay-card-1-p-6": "Encontrarás más información en el SimpleX whitepaper.", "hero-overlay-card-2-p-1": "Cuando los usuarios asumen identidades persistentes, aunque sólo se trate de un número aleatorio como un identificador de sesión, existe el riesgo de que el proveedor de red o un atacante puedan observar cómo se conectan y la cantidad de mensajes que envían.", "hero-overlay-card-2-p-2": "A continuación podrían correlacionar esta información con las redes sociales públicas existentes y averiguar las identidades reales de los usuarios.", "hero-overlay-card-2-p-3": "Incluso con las aplicaciones más privadas que usan los servicios de Tor v3, si hablas con dos contactos a través de un mismo perfil se puede probar que estos están conectados con la misma persona.", @@ -105,7 +105,7 @@ "simplex-network-overlay-card-1-li-3": "P2P no resuelve el problema del ataque MITM , y la mayoría de implementaciones existentes no usan mensajes fuera de banda para el intercambio de claves inicial. SimpleX usa mensajes fuera de banda, o en algunos casos conexiones seguras y de confianza preexistentes para el intercambio inicial de claves.", "simplex-network-overlay-card-1-li-4": "Algunos proveedores de Internet pueden bloquear las aplicaciones P2P (como BitTorrent). SimpleX es independiente del transporte. Puede trabajar con protocolos web estándar, por ejemplo WebSockets.", "privacy-matters-overlay-card-1-p-4": "La red SimpleX protege la privacidad de tus conexiones mejor que cualquier alternativa, impidiendo por completo que tu gráfica social esté disponible para empresas u organizaciones. Incluso cuando se emplean los servidores configurados en SimpleX Chat, los operadores de servidores desconocen el número de usuarios o sus conexiones.", - "privacy-matters-overlay-card-2-p-1": "Hace poco vimos cómo las principales elecciones eran manipuladas por una reputada compañía consultora que usaba nuestras gráficas sociales para distorsionar nuestra visión del mundo real y manipular nuestros votos.", + "privacy-matters-overlay-card-2-p-1": "Hace poco vimos cómo las principales elecciones eran manipuladas por una reputada compañía consultora que usaba nuestras gráficas sociales para distorsionar nuestra visión del mundo real y manipular nuestros votos.", "privacy-matters-overlay-card-2-p-2": "Para ser objetivo y tomar decisiones independientes necesitas tener el controlar de tu espacio de información. Sólo es posible si usas una red de comunicaciónes privada que no tenga acceso a tu gráfica social.", "privacy-matters-overlay-card-2-p-3": "SimpleX es la primera red que por diseño no tiene identificadores de usuario, protegiendo así tu gráfica de conexiones mejor que cualquier alternativa conocida.", "privacy-matters-overlay-card-3-p-1": "Todos deberían preocuparse por la privacidad y la seguridad de sus comunicaciones. Las conversaciones inofensivas pueden ponerte en peligro incluso aunque no tengas nada que ocultar.", @@ -119,10 +119,10 @@ "simplex-network-overlay-card-1-li-6": "Las redes P2P pueden ser vulnerables al ataque DRDoS, en el que los clientes retransmiten y amplifican el tráfico provocando el bloqueo del servicio en la red. Los clientes SimpleX sólo transmiten el tráfico desde conexiones conocidas y no pueden ser usados por un atacante para amplificar el tráfico en toda la red.", "privacy-matters-overlay-card-1-p-1": "Las grandes empresas usan la información relativa a tus contactos para calcular tus ingresos, venderte productos que realmente no necesitas y para determinar sus precios.", "privacy-matters-overlay-card-1-p-2": "Los minoristas en línea saben que las personas con menos ingresos son más propensas a hacer compras urgentes, por lo que pueden cobrar precios más altos o eliminar descuentos.", - "privacy-matters-overlay-card-1-p-3": "Algunas compañías financieras y de seguros usan las gráficas sociales para determinar primas y tipos de interés. Eso genera a menudo un mayor desembolso a personas con menores ingresos, conocido como \"prima de pobreza\".", - "privacy-matters-overlay-card-3-p-2": "Una de las historias más estremecedoras es la experiencia de Mohamedou Ould Salahi descrita en sus memorias y plasmada en la película The Mauritanian. Fue internado en la prisión militar de Guantánamo sin juicio previo, donde fue torturado durante 15 años tras una llamada telefónica a su pariente en Afganistán, bajo la sospecha de estar implicado en los atentados del 11-S a pesar de que vivía en Alemania desde hacía 10 años.", + "privacy-matters-overlay-card-1-p-3": "Algunas compañías financieras y de seguros usan las gráficas sociales para determinar primas y tipos de interés. Eso genera a menudo un mayor desembolso a personas con menores ingresos, conocido como \"prima de pobreza\".", + "privacy-matters-overlay-card-3-p-2": "Una de las historias más estremecedoras es la experiencia de Mohamedou Ould Salahi descrita en sus memorias y plasmada en la película The Mauritanian. Fue internado en la prisión militar de Guantánamo sin juicio previo, donde fue torturado durante 15 años tras una llamada telefónica a su pariente en Afganistán, bajo la sospecha de estar implicado en los atentados del 11-S a pesar de que vivía en Alemania desde hacía 10 años.", "privacy-matters-overlay-card-3-p-4": "No basta con usar mensajería cifrada de extremo a extremo, todos deberíamos usar las aplicaciones de mensajería que protejan la privacidad de nuestras redes personales y con quién nos comunicamos.", - "privacy-matters-overlay-card-3-p-3": "Personas corrientes son detenidas por lo que comparten en Internet, incluso a través de sus cuentas \"anónimas\" e incluso en países democráticos.", + "privacy-matters-overlay-card-3-p-3": "Personas corrientes son detenidas por lo que comparten en Internet, incluso a través de sus cuentas \"anónimas\" e incluso en países democráticos.", "simplex-unique-overlay-card-1-p-2": "Para la entrega de mensajes, SimpleX usa direcciones anónimas por pares de colas de mensajes unidireccionales separadas para mensajes salientes y entrantes a través de servidores diferentes.", "simplex-unique-overlay-card-3-p-1": "SimpleX Chat almacena todos los datos de usuario únicamente en los dispositivos cliente usando un formato cifrado y portable de la base de datos, la cual puede ser exportada y transferida a cualquier dispositivo compatible.", "simplex-unique-overlay-card-3-p-3": "A diferencia de los servidores de redes federadas (correo electrónico, XMPP o Matrix), los servidores SimpleX no almacenan cuentas de usuario, sólo retransmiten mensajes, protegiendo así la privacidad de ambas partes.", @@ -185,8 +185,8 @@ "comparison-section-list-point-6": "A pesar de que las redes P2P son distribuidas, no son federadas. Funcionan como una única red", "comparison-section-list-point-7": "Las redes P2P, o bien disponen de una autoridad central, o bien la red completa podría verse comprometida", "see-here": "ver aquí", - "simplex-unique-overlay-card-4-p-2": "La red SimpleX usa un protocolo abierto y proporciona el SDK para crear chatbots, permitiendo implementar servicios con los que los usuarios interactúen con las aplicaciones SimpleX Chat — esperamos ver con interés los servicios SimpleX que creará usted.", - "simplex-unique-overlay-card-4-p-3": "Si estás considerando desarrollar para la red SimpleX, por ejemplo un chatbot para los usuarios de SimpleX o la integración de las librerías SimpleX Chat en tus aplicaciones móviles, por favor ponte en contacto para consejos y soporte.", + "simplex-unique-overlay-card-4-p-2": "La red SimpleX usa un protocolo abierto y proporciona el SDK para crear chatbots, permitiendo implementar servicios con los que los usuarios interactúen con las aplicaciones SimpleX Chat — esperamos ver con interés los servicios SimpleX que creará usted.", + "simplex-unique-overlay-card-4-p-3": "Si estás considerando desarrollar para la red SimpleX, por ejemplo un chatbot para los usuarios de SimpleX o la integración de las librerías SimpleX Chat en tus aplicaciones móviles, por favor ponte en contacto para consejos y soporte.", "simplex-unique-card-1-p-2": "A diferencia de cualquier otra red de mensajería existente, SimpleX no tiene identificadores asignados a los usuarios, ni siquiera números aleatorios.", "simplex-unique-card-2-p-1": "Al no tener identificadores o dirección permanente en la red SimpleX, nadie puede ponerse en contacto contigo salvo que compartas una dirección de usuario de un solo uso o una dirección temporal en forma de enlace o código QR.", "simplex-unique-card-4-p-1": "La red SimpleX está completamente descentralizada y es independiente de cualquier criptodivisa o de cualquier red salvo Internet.", diff --git a/website/langs/fa.json b/website/langs/fa.json index 0afd6a8f83..5a369c86e2 100644 --- a/website/langs/fa.json +++ b/website/langs/fa.json @@ -63,14 +63,14 @@ "simplex-network-overlay-card-1-li-6": "شبکه‌های P2P ممکن است در معرض حمله DRDoS قرار بگیرند، زمانی که کلاینت‌ها می‌توانند ترافیک را دوباره پخش و تقویت کنند که منجر به عدم دسترسی به خدمات در سطح شبکه می‌شود. کلاینت‌های SimpleX تنها ترافیک را از اتصالات شناخته‌شده منتقل می‌کنند و نمی‌توانند توسط یک مهاجم برای تقویت ترافیک در کل شبکه استفاده شوند.", "privacy-matters-overlay-card-1-p-1": "بسیاری از شرکت‌های بزرگ از اطلاعات مربوط به اینکه با چه کسانی ارتباط دارید، برای تخمین درآمد شما، فروش محصولات غیرضروری به شما و تعیین قیمت‌ها استفاده می‌کنند.", "privacy-matters-overlay-card-1-p-2": "فروشگاه‌های آنلاین می‌دانند که افراد با درآمد پایین‌تر بیشتر احتمال دارد خریدهای فوری انجام دهند، بنابراین ممکن است قیمت‌های بالاتری تعیین کنند یا تخفیف‌ها را حذف کنند.", - "privacy-matters-overlay-card-1-p-3": "برخی از شرکت‌های مالی و بیمه از نمودارهای اجتماعی برای تعیین نرخ‌های بهره و حق بیمه‌ها استفاده می‌کنند. این اغلب باعث می‌شود که افراد با درآمد پایین‌تر بیشتر پرداخت کنند — که به آن 'حق بیمه فقر' گفته می‌شود.", + "privacy-matters-overlay-card-1-p-3": "برخی از شرکت‌های مالی و بیمه از نمودارهای اجتماعی برای تعیین نرخ‌های بهره و حق بیمه‌ها استفاده می‌کنند. این اغلب باعث می‌شود که افراد با درآمد پایین‌تر بیشتر پرداخت کنند — که به آن 'حق بیمه فقر' گفته می‌شود.", "privacy-matters-overlay-card-1-p-4": "شبکه SimpleX حریم خصوصی ارتباطات شما را بهتر از هر گزینه دیگری محافظت می‌کند و به‌طور کامل از در دسترس قرار گرفتن نمودار اجتماعی شما برای هر شرکت یا سازمانی جلوگیری می‌کند. حتی زمانی که افراد از سرورهای پیش‌پیکربندی‌شده در برنامه‌های چت SimpleX استفاده می‌کنند، اپراتورهای سرور از تعداد کاربران یا ارتباطات آن‌ها اطلاعی ندارند.", - "privacy-matters-overlay-card-2-p-1": "مدتی پیش، ما شاهد دستکاری در انتخابات بزرگ توسط یک شرکت مشاوره معتبر بودیم که از نمودارهای اجتماعی ما برای تحریف دیدگاه‌مان نسبت به دنیای واقعی و دستکاری در رأی‌گیری‌مان استفاده کرد.", + "privacy-matters-overlay-card-2-p-1": "مدتی پیش، ما شاهد دستکاری در انتخابات بزرگ توسط یک شرکت مشاوره معتبر بودیم که از نمودارهای اجتماعی ما برای تحریف دیدگاه‌مان نسبت به دنیای واقعی و دستکاری در رأی‌گیری‌مان استفاده کرد.", "privacy-matters-overlay-card-2-p-2": "برای اینکه عینی باشید و تصمیمات مستقل بگیرید، باید کنترل فضای اطلاعات خود را در دست داشته باشید. این تنها در صورتی ممکن است که از یک شبکه ارتباطی خصوصی استفاده کنید که به نمودار اجتماعی شما دسترسی نداشته باشد.", "privacy-matters-overlay-card-2-p-3": "SimpleX اولین شبکه‌ای است که به‌طور ذاتی هیچ شناسه کاربری ندارد و به این ترتیب نمودار ارتباطات شما را بهتر از هر گزینه شناخته‌شده دیگری محافظت می‌کند.", "privacy-matters-overlay-card-3-p-1": "همه باید به حریم خصوصی و امنیت ارتباطات خود اهمیت دهند — گفتگوهای بی‌ضرر می‌توانند شما را در خطر قرار دهند، حتی اگر چیزی برای پنهان کردن نداشته باشید.", - "privacy-matters-overlay-card-3-p-2": "یکی از شگفت‌انگیزترین داستان‌ها، تجربه محمدو ولد صلاحی است که در یادداشت‌هایش توصیف شده و در فیلم \"موریتانی\" به نمایش درآمده است. او بدون محاکمه به اردوگاه گوانتانامو منتقل شد و به مدت ۱۵ سال تحت شکنجه قرار گرفت. این اتفاق پس از یک تماس تلفنی با یکی از بستگانش در افغانستان رخ داد. او به ظن ارتباط با حملات ۱۱ سپتامبر، بدون محاکمه به اردوگاه گوانتانامو منتقل شد.", - "privacy-matters-overlay-card-3-p-3": "افراد عادی به خاطر آنچه که به‌صورت آنلاین به اشتراک می‌گذارند، حتی از طریق حساب‌های 'ناشناس' خود، حتی در کشورهای دموکراتیک دستگیر می‌شوند.", + "privacy-matters-overlay-card-3-p-2": "یکی از شگفت‌انگیزترین داستان‌ها، تجربه محمدو ولد صلاحی است که در یادداشت‌هایش توصیف شده و در فیلم \"موریتانی\" به نمایش درآمده است. او بدون محاکمه به اردوگاه گوانتانامو منتقل شد و به مدت ۱۵ سال تحت شکنجه قرار گرفت. این اتفاق پس از یک تماس تلفنی با یکی از بستگانش در افغانستان رخ داد. او به ظن ارتباط با حملات ۱۱ سپتامبر، بدون محاکمه به اردوگاه گوانتانامو منتقل شد.", + "privacy-matters-overlay-card-3-p-3": "افراد عادی به خاطر آنچه که به‌صورت آنلاین به اشتراک می‌گذارند، حتی از طریق حساب‌های 'ناشناس' خود، حتی در کشورهای دموکراتیک دستگیر می‌شوند.", "privacy-matters-overlay-card-3-p-4": "استفاده از یک پیام‌رسان رمزنگاری شده انتها به انتها کافی نیست؛ همه ما باید از پیام‌رسان‌هایی استفاده کنیم که حریم خصوصی شبکه‌های شخصی ما را محافظت می‌کنند — اینکه با چه کسانی ارتباط داریم.", "simplex-unique-overlay-card-1-p-1": "برخلاف سایر شبکه‌های پیام‌رسان، SimpleX هیچ شناسه‌ای به کاربران اختصاص نمی‌دهد. این شبکه به شماره‌های تلفن، آدرس‌های مبتنی بر دامنه (مانند ایمیل یا XMPP)، نام‌های کاربری، کلیدهای عمومی یا حتی شماره‌های تصادفی برای شناسایی کاربران خود تکیه نمی‌کند — اپراتورهای سرور SimpleX نمی‌دانند چند نفر از سرورهای آن‌ها استفاده می‌کنند.", "simplex-unique-overlay-card-1-p-2": "برای ارسال پیام‌ها، SimpleX از آدرس‌های ناشناس جفتی در صف‌های پیام یک‌طرفه استفاده می‌کند که برای پیام‌های دریافتی و ارسال‌شده جداگانه هستند و معمولاً از طریق سرورهای مختلف انجام می‌شود.", @@ -82,8 +82,8 @@ "simplex-unique-overlay-card-3-p-3": "برخلاف سرورهای شبکه‌های فدرال (ایمیل، XMPP یا Matrix)، سرورهای SimpleX حساب‌های کاربری را ذخیره نمی‌کنند و تنها پیام‌ها را منتقل می‌کنند، که حریم خصوصی هر دو طرف را محافظت می‌کند.", "simplex-unique-overlay-card-3-p-4": "هیچ شناسه یا متن رمز مشترکی بین ترافیک سرور ارسال‌شده و دریافتی وجود ندارد — اگر کسی در حال نظارت باشد، نمی‌تواند به‌راحتی تشخیص دهد که چه کسی با چه کسی ارتباط برقرار می‌کند، حتی اگر TLS به خطر بیفتد.", "simplex-unique-overlay-card-4-p-1": "شما می‌توانید از SimpleX با سرورهای خود استفاده کنید و همچنان با افرادی که از سرورهای پیش‌پیکربندی‌شده در برنامه‌ها استفاده می‌کنند، ارتباط برقرار کنید.", - "simplex-unique-overlay-card-4-p-2": "شبکه SimpleX از یک پروتکل باز استفاده می‌کند و SDK برای ایجاد ربات‌های چت ارائه می‌دهد، که امکان پیاده‌سازی خدماتی را فراهم می‌کند که کاربران می‌توانند از طریق برنامه‌های چت SimpleX با آن‌ها تعامل داشته باشند — ما واقعاً منتظر هستیم ببینیم چه خدماتی را با SimpleX ایجاد خواهید کرد.", - "simplex-unique-overlay-card-4-p-3": "اگر به توسعه برای شبکه SimpleX فکر می‌کنید، مثلاً ربات چت برای کاربران برنامه SimpleX یا ادغام کتابخانه چت SimpleX در برنامه‌های موبایل خود، لطفاً برای هرگونه مشاوره و پشتیبانی با ما تماس بگیرید.", + "simplex-unique-overlay-card-4-p-2": "شبکه SimpleX از یک پروتکل باز استفاده می‌کند و SDK برای ایجاد ربات‌های چت ارائه می‌دهد، که امکان پیاده‌سازی خدماتی را فراهم می‌کند که کاربران می‌توانند از طریق برنامه‌های چت SimpleX با آن‌ها تعامل داشته باشند — ما واقعاً منتظر هستیم ببینیم چه خدماتی را با SimpleX ایجاد خواهید کرد.", + "simplex-unique-overlay-card-4-p-3": "اگر به توسعه برای شبکه SimpleX فکر می‌کنید، مثلاً ربات چت برای کاربران برنامه SimpleX یا ادغام کتابخانه چت SimpleX در برنامه‌های موبایل خود، لطفاً برای هرگونه مشاوره و پشتیبانی با ما تماس بگیرید.", "simplex-unique-card-1-p-1": "SimpleX حریم خصوصی پروفایل، مخاطبین و فراداده‌های شما را محافظت می‌کند و آن‌ها را از سرورهای شبکه SimpleX و هر ناظر دیگری پنهان می‌سازد.", "simplex-unique-card-1-p-2": "برخلاف هر شبکه پیام‌رسان دیگری، SimpleX هیچ شناسه‌ای به کاربران اختصاص نمی‌دهد — حتی شماره‌های تصادفی نیز وجود ندارد.", "simplex-unique-card-2-p-1": "از آنجا که شما هیچ شناسه یا آدرس ثابتی در شبکه SimpleX ندارید، هیچ‌کس نمی‌تواند با شما تماس بگیرد مگر اینکه یک آدرس کاربری یک‌بار مصرف یا موقتی را به‌عنوان کد QR یا لینک به اشتراک بگذارید.", @@ -214,7 +214,7 @@ "simplex-private-card-3-point-2": "اثر انگشت سرور و اتصال کانال از حملات MITM و حملات replay جلوگیری می‌کنند.", "simplex-private-card-3-point-3": "برای جلوگیری از حملات نشست، از سرگیری اتصال غیرفعال شده است.", "simplex-private-card-4-point-1": "برای محافظت از آدرس IP خود، می‌توانید از طریق Tor یا برخی دیگر از شبکه‌های حمل و نقل پوششی به سرورها دسترسی پیدا کنید.", - "simplex-private-card-4-point-2": "برای استفاده از SimpleX از طریق Tor، لطفاً برنامه Orbot را نصب کرده و پروکسی SOCKS5 (یا VPN در iOS) را فعال کنید.", + "simplex-private-card-4-point-2": "برای استفاده از SimpleX از طریق Tor، لطفاً برنامه Orbot را نصب کرده و پروکسی SOCKS5 (یا VPN در iOS) را فعال کنید.", "simplex-private-card-5-point-1": "SimpleX از padding محتوا برای هر لایه رمزنگاری استفاده می‌کند تا از حملات اندازه پیام جلوگیری کند.", "simplex-private-card-5-point-2": "این کار باعث می‌شود که پیام‌های با اندازه‌های مختلف برای سرورها و ناظران شبکه یکسان به نظر برسند.", "simplex-private-card-6-point-1": "بسیاری از شبکه‌های ارتباطی در برابر حملات MITM توسط سرورها یا ارائه‌دهندگان شبکه آسیب‌پذیر هستند.", @@ -248,7 +248,7 @@ "hero-overlay-card-1-p-3": "شما سرورهایی را که برای ارسال پیام‌ها به مخاطبین خود استفاده می‌کنید مشخص می‌کنید و همچنین از کدام سرور(ها) برای دریافت پیام‌ها بهره می‌برید. هر مکالمه احتمالاً از دو سرور مختلف استفاده می‌کند.", "hero-overlay-card-1-p-4": "این طراحی از نشت هرگونه فراداده کاربران در سطح برنامه جلوگیری می‌کند. برای بهبود بیشتر حریم خصوصی و محافظت از آدرس IP خود، می‌توانید از طریق Tor به سرورهای پیام‌رسانی متصل شوید.", "hero-overlay-card-1-p-5": "فقط دستگاه‌های کلاینت پروفایل‌های کاربری، مخاطبین و گروه‌ها را ذخیره می‌کنند؛ پیام‌ها با رمزنگاری انتها به انتهای دو لایه ارسال می‌شوند.", - "hero-overlay-card-1-p-6": "برای اطلاعات بیشتر به وایت پیپر SimpleX مراجعه کنید.", + "hero-overlay-card-1-p-6": "برای اطلاعات بیشتر به وایت پیپر SimpleX مراجعه کنید.", "hero-overlay-card-2-p-1": "زمانی که کاربران هویت‌های پایدار دارند، حتی اگر این فقط یک شماره تصادفی مانند شناسه جلسه باشد، خطر این وجود دارد که ارائه‌دهنده یا یک مهاجم بتوانند مشاهده کنند که کاربران چگونه متصل شده‌اند و چند پیام ارسال می‌کنند.", "hero-overlay-card-2-p-2": "سپس آن‌ها می‌توانند این اطلاعات را با شبکه‌های اجتماعی عمومی موجود همبسته کرده و برخی هویت‌های واقعی را شناسایی کنند.", "hero-overlay-card-2-p-3": "حتی با خصوصی‌ترین برنامه‌ها که از خدمات Tor v3 استفاده می‌کنند، اگر شما با دو مخاطب مختلف از طریق یک پروفایل یکسان صحبت کنید، آن‌ها می‌توانند ثابت کنند که به یک شخص واحد متصل هستند.", diff --git a/website/langs/fi.json b/website/langs/fi.json index 3a138aa7c4..363697b8fb 100644 --- a/website/langs/fi.json +++ b/website/langs/fi.json @@ -37,7 +37,7 @@ "simplex-private-9-title": "Yksisuuntaisia
viestijonoja", "simplex-private-10-title": "Väliaikaiset nimettömät parittaiset tunnisteet", "simplex-private-card-3-point-1": "Vain TLS 1.2/1.3 vahvoilla algoritmeilla käytetään asiakas-palvelin-yhteyksiin.", - "simplex-private-card-4-point-2": "Käyttääksesi SimpleX:ää Torin kautta, asenna Orbot-sovellus ja ota käyttöön SOCKS5-välityspalvelin (tai VPN iOS:lla).", + "simplex-private-card-4-point-2": "Käyttääksesi SimpleX:ää Torin kautta, asenna Orbot-sovellus ja ota käyttöön SOCKS5-välityspalvelin (tai VPN iOS:lla).", "simplex-private-card-7-point-1": "Viestien eheyden varmistamiseksi ne numeroituvat peräkkäin ja sisältävät edellisen viestin tiivisteen.", "simplex-private-card-7-point-2": "Jos viestiä lisätään, poistetaan tai muutetaan, vastaanottaja saa ilmoituksen.", "simplex-private-card-8-point-1": "SimpleX-palvelimet toimivat matalan viiveen sekoitussolmuina — saapuvilla ja lähtevillä viesteillä on erilainen järjestys.", @@ -152,7 +152,7 @@ "simplex-unique-4-overlay-1-title": "Täysin hajautettu — käyttäjät omistavat SimpleX-verkon", "hero-overlay-card-1-p-1": "Monet käyttäjät ovat kysyneet: jos SimpleX ei käytä käyttäjätunnisteita, miten se tietää minne viestit toimitetaan?", "hero-overlay-card-1-p-3": "Määrität, minkä palvelimen(t) valitset viestien vastaanottamiseen, sekä kontaktisi — palvelimet, joita käytät viestien lähettämiseen heille. Jokainen keskustelu käyttää todennäköisesti kahta eri palvelinta.", - "hero-overlay-card-1-p-6": "Lue lisää SimpleX whitepaperista.", + "hero-overlay-card-1-p-6": "Lue lisää SimpleX whitepaperista.", "hero-overlay-card-1-p-2": "Viestien toimittamiseen SimpleX ei käytä muiden alustojen käyttäjätunnuksia, vaan sen sijaan se käyttää väliaikaisia nimettömiä parittaisia tunnisteita viestijonoille, jotka ovat erillisiä jokaiselle yhteydelle — pitkäaikaisia tunnisteita ei ole.", "hero-overlay-card-2-p-3": "Jopa yksityisimmillä sovelluksilla, jotka käyttävät Tor v3 -palveluja, jos puhut kahdelle eri yhteyshenkilölle saman profiilin kautta, he voivat todistaa olevansa yhteydessä samaan henkilöön.", "hero-overlay-card-2-p-4": "SimpleX suojaa näitä hyökkäyksiä vastaan, sillä siinä ei ole käyttäjätunnuksia toteutuksessaan. Ja jos käytät Incognito-tilaa, sinulla on eri näyttönimi jokaiselle yhteyshenkilölle, mikä estää yhteisten tietojen jakamisen heidän välillään.", @@ -163,14 +163,14 @@ "simplex-network-overlay-card-1-li-3": "P2P ei ratkaise välikäden hyökkäys -ongelmaa, ja useimmat olemassa olevat toteutukset eivät käytä kanavan ulkopuolisia viestejä alkuperäiseen avaimenvaihtoon. SimpleX käyttää kanavan ulkopuolisia viestejä tai, joissakin tapauksissa, jo valmiiksi turvallisia ja luotettuja yhteyksiä alkuperäiseen avaimenvaihtoon.", "simplex-network-overlay-card-1-li-4": "P2P-toteutukset voivat estyä joidenkin Internet-palveluntarjoajien (kuten BitTorrent) toimesta. SimpleX on kuljetusprotokollasta riippumaton - se voi toimia yleisten verkkoprotokollien kautta, kuten esimerkiksi WebSockets.", "simplex-network-overlay-card-1-li-5": "Kaikki tunnetut P2P-verkot saattavat olla alttiita Sybil-hyökkäykselle, koska jokainen solmu on löydettävissä, ja verkko toimii kokonaisuutena. Tunnetut keinot sen lieventämiseksi vaativat joko keskitetyn komponentin tai kalliin työn todistuksen. SimpleX-verkolla ei ole palvelimen löydettävyyttä, se on fragmentoitunut ja toimii useina eristettyinä aliverkkoina, mikä tekee verkko-laajuisista hyökkäyksistä mahdottomia.", - "privacy-matters-overlay-card-1-p-3": "Jotkut rahoitus- ja vakuutusyhtiöt käyttävät sosiaalisia verkostoja määrittääkseen korkoja ja vakuutusmaksuja. Se tekee usein alhaisempiin tuloihin kuuluvien ihmisten maksavan enemmän — sitä kutsutaan 'köyhyyslisäksi'.", + "privacy-matters-overlay-card-1-p-3": "Jotkut rahoitus- ja vakuutusyhtiöt käyttävät sosiaalisia verkostoja määrittääkseen korkoja ja vakuutusmaksuja. Se tekee usein alhaisempiin tuloihin kuuluvien ihmisten maksavan enemmän — sitä kutsutaan 'köyhyyslisäksi'.", "privacy-matters-overlay-card-1-p-4": "SimpleX-alusta suojaa yhteyksiesi yksityisyyttä paremmin kuin mikään vaihtoehto, estäen täysin yhteysverkkosi tulemisen saataville mille tahansa yrityksille tai organisaatioille. Vaikka ihmiset käyttävät SimpleX Chatin tarjoamia palvelimia, emme tiedä käyttäjien määrää tai heidän yhteyksiään.", "privacy-matters-overlay-card-2-p-3": "SimpleX on ensimmäinen alusta, jolla ei ole mitään käyttäjätunnisteita suunnittelussaan, suojaten siten yhteyksesi verkkoa paremmin kuin mikään tunnettu vaihtoehto.", - "privacy-matters-overlay-card-2-p-1": "Ei niin kauan sitten huomasimme merkittävien vaalien olevan manipuloitavissa kunnioitetun konsulttiyrityksen toimesta, joka käytti sosiaalisia verkostoja vääristämään käsitystämme todellisesta maailmasta ja manipuloimaan ääniämme.", + "privacy-matters-overlay-card-2-p-1": "Ei niin kauan sitten huomasimme merkittävien vaalien olevan manipuloitavissa kunnioitetun konsulttiyrityksen toimesta, joka käytti sosiaalisia verkostoja vääristämään käsitystämme todellisesta maailmasta ja manipuloimaan ääniämme.", "privacy-matters-overlay-card-3-p-1": "Kaikkien pitäisi välittää viestinnän yksityisyydestä ja turvallisuudesta — harmittomat keskustelut voivat asettaa sinut vaaraan, vaikka sinulla ei olisi mitään piilotettavaa.", "privacy-matters-overlay-card-2-p-2": "Ollaksesi objektiivinen ja tehdäksesi itsenäisiä päätöksiä, sinun on hallittava tietotilaasi. Se on mahdollista vain, jos käytät yksityistä viestintäalustaa, jolla ei ole pääsyä sosiaaliseen verkostoosi.", - "privacy-matters-overlay-card-3-p-2": "Yksi järkyttävimmistä tarinoista on Mohamedou Ould Salahi'n kokemus, joka on kuvattu hänen muistelmissaan ja esitetty The Mauritanian -elokuvassa. Hänet laitettiin Guantanamo-leirille ilman oikeudenkäyntiä ja häntä kidutettiin siellä 15 vuotta puhelun jälkeen sukulaiselleen Afganistanissa, epäiltynä osallisuudesta 9/11-iskuihin, vaikka hän oli asunut Saksassa edelliset 10 vuotta.", - "privacy-matters-overlay-card-3-p-3": "Tavalliset ihmiset pidätetään siitä, mitä he jakavat verkossa, jopa 'anonyymien' tiliensä kautta, jopa demokraattisissa maissa.", + "privacy-matters-overlay-card-3-p-2": "Yksi järkyttävimmistä tarinoista on Mohamedou Ould Salahi'n kokemus, joka on kuvattu hänen muistelmissaan ja esitetty The Mauritanian -elokuvassa. Hänet laitettiin Guantanamo-leirille ilman oikeudenkäyntiä ja häntä kidutettiin siellä 15 vuotta puhelun jälkeen sukulaiselleen Afganistanissa, epäiltynä osallisuudesta 9/11-iskuihin, vaikka hän oli asunut Saksassa edelliset 10 vuotta.", + "privacy-matters-overlay-card-3-p-3": "Tavalliset ihmiset pidätetään siitä, mitä he jakavat verkossa, jopa 'anonyymien' tiliensä kautta, jopa demokraattisissa maissa.", "privacy-matters-overlay-card-3-p-4": "Ei riitä, että käytät päästä päähän salattua viestintäsovellusta, meidän kaikkien pitäisi käyttää viestintäsovelluksia, jotka suojelevat henkilökohtaisten verkostojemme yksityisyyttä — keiden kanssa olemme yhteydessä.", "simplex-unique-overlay-card-1-p-1": "Toisin kuin muut viestintäalustat, SimpleX:llä ei ole mitään tunnisteita käyttäjille. Se ei luota puhelinnumeroihin, verkkotunnuksiin perustuviin osoitteisiin (kuten sähköposti tai XMPP), käyttäjänimiin, julkisiin avaimiin tai edes satunnaisiin numeroihin tunnistaakseen käyttäjänsä — emme tiedä kuinka monta ihmistä käyttää SimpleX-palvelimiamme.", "simplex-unique-overlay-card-1-p-2": "Viestien toimittamiseksi SimpleX käyttää parittaisia nimettömiä osoitteita kaksisuuntaisille viestijonoille, jotka ovat erilliset vastaanotetuille ja lähetetyille viesteille, yleensä eri palvelimien kautta.", @@ -179,9 +179,9 @@ "simplex-unique-overlay-card-2-p-2": "Jopa valinnaisen käyttäjäosoitteen kanssa, vaikka sitä voitaisiin käyttää roskapostiyhteyspyyntöjen lähettämiseen, voit vaihtaa sen tai poistaa sen kokonaan menettämättä mitään yhteyksiäsi.", "simplex-unique-overlay-card-3-p-3": "Toisin kuin liitettyjen verkkojen palvelimet (sähköposti, XMPP tai Matrix), SimpleX-palvelimet eivät tallenna käyttäjätilejä, ne vain välittävät viestejä, suojaten molempien osapuolien yksityisyyttä.", "simplex-unique-overlay-card-3-p-4": "Lähetetyssä ja vastaanotetussa palvelimen liikenteessä ei ole yhteisiä tunnisteita tai salaustekstiä — jos joku havaitsee sen, he eivät voi helposti selvittää kuka kommunikoi kenen kanssa, vaikka TLS olisi vaarantunut.", - "simplex-unique-overlay-card-4-p-3": "Jos harkitset kehittämistä SimpleX-alustalle, esimerkiksi chat-botin luomista SimpleX-sovelluksen käyttäjille tai SimpleX Chat -kirjaston integrointia mobiilisovelluksiisi, ole hyvä ja ota yhteyttä saadaksesi neuvoja ja tukea.", + "simplex-unique-overlay-card-4-p-3": "Jos harkitset kehittämistä SimpleX-alustalle, esimerkiksi chat-botin luomista SimpleX-sovelluksen käyttäjille tai SimpleX Chat -kirjaston integrointia mobiilisovelluksiisi, ole hyvä ja ota yhteyttä saadaksesi neuvoja ja tukea.", "simplex-unique-overlay-card-4-p-1": "Voit käyttää SimpleX:ää omien palvelimiesi kanssa ja silti kommunikoida ihmisten kanssa, jotka käyttävät meille tarjottuja valmiiksi määritettyjä palvelimia.", - "simplex-unique-overlay-card-4-p-2": "SimpleX-alusta käyttää avoimeen protokollaan ja tarjoaa SDK:n chatbotien luomiseen, mahdollistaen palvelujen toteuttamisen, joiden kanssa käyttäjät voivat olla vuorovaikutuksessa SimpleX Chat -sovellusten kautta — odotamme innolla nähdä, millaisia SimpleX-palveluja voit rakentaa.", + "simplex-unique-overlay-card-4-p-2": "SimpleX-alusta käyttää avoimeen protokollaan ja tarjoaa SDK:n chatbotien luomiseen, mahdollistaen palvelujen toteuttamisen, joiden kanssa käyttäjät voivat olla vuorovaikutuksessa SimpleX Chat -sovellusten kautta — odotamme innolla nähdä, millaisia SimpleX-palveluja voit rakentaa.", "simplex-unique-card-3-p-1": "SimpleX tallentaa kaikki käyttäjätiedot asiakaslaitteille siirrettävässä salatussa tietokannan muodossa — se voidaan siirtää toiseen laitteeseen.", "simplex-unique-card-3-p-2": "Päästä päähän salatut viestit säilytetään väliaikaisesti SimpleX-releay-palvelimilla, kunnes ne vastaanotetaan, minkä jälkeen ne poistetaan pysyvästi.", "we-invite-you-to-join-the-conversation": "Kutsumme sinut mukaan keskusteluun", diff --git a/website/langs/fr.json b/website/langs/fr.json index bd91c36e22..124c7ddc67 100644 --- a/website/langs/fr.json +++ b/website/langs/fr.json @@ -61,7 +61,7 @@ "simplex-private-card-3-point-2": "L'empreinte du serveur et la fixation des canaux empêchent les attaques MITM et les attaques par rejeu.", "simplex-private-card-3-point-3": "La reprise de connexion est désactivée pour empêcher les attaques de session.", "simplex-private-card-4-point-1": "Pour protéger votre adresse IP, vous pouvez accéder aux serveurs via Tor ou un autre réseau de transport superposé.", - "simplex-private-card-4-point-2": "Pour utiliser SimpleX via Tor, veuillez installer l'application Orbot et activez le proxy SOCKS5 (ou le mode VPN sur iOS).", + "simplex-private-card-4-point-2": "Pour utiliser SimpleX via Tor, veuillez installer l'application Orbot et activez le proxy SOCKS5 (ou le mode VPN sur iOS).", "simplex-private-card-5-point-1": "SimpleX utilise une couverture de contenu pour chaque couche de chiffrement afin de résister aux attaques ciblant la taille des messages.", "simplex-private-card-5-point-2": "Cela rend les messages de différentes tailles identiques pour les serveurs et les observateurs du réseau.", "simplex-private-card-6-point-1": "De nombreux réseaux de communication sont vulnérables aux attaques MITM par des serveurs ou des fournisseurs de réseau.", @@ -95,7 +95,7 @@ "hero-overlay-card-1-p-3": "Vous définissez le ou les serveurs que vous souhaitez utiliser pour recevoir les messages et vos contacts — les serveurs que vous utilisez pour leur envoyer des messages. Chaque conversation est susceptible d'utiliser deux serveurs différents.", "hero-overlay-card-1-p-4": "Cette méthode empêche la fuite de métadonnées des utilisateurs au niveau de l'application. Pour améliorer encore la protection de votre vie privée et protéger votre adresse IP, vous pouvez vous connecter aux serveurs de messagerie via Tor.", "hero-overlay-card-1-p-5": "Seuls les appareils clients stockent les profils des utilisateurs, les contacts et les groupes ; les messages sont envoyés avec un chiffrement de bout en bout à deux couches.", - "hero-overlay-card-1-p-6": "En savoir plus sur le livre blanc de SimpleX.", + "hero-overlay-card-1-p-6": "En savoir plus sur le livre blanc de SimpleX.", "hero-overlay-card-2-p-1": "Lorsque les utilisateurs ont des identités persistantes, même s'il ne s'agit que d'un nombre aléatoire, comme un ID de session, il y a un risque que le fournisseur ou un attaquant puisse observer comment les utilisateurs sont connectés et combien de messages ils envoient.", "hero-overlay-card-2-p-2": "Ils pourraient ensuite corréler ces informations avec les réseaux sociaux publics existants, et déterminer de véritables identités.", "hero-overlay-card-2-p-3": "Même avec les applications les plus privées qui utilisent les services Tor v3, si vous parlez à deux contacts différents via le même profil, ils peuvent prouver qu'ils sont connectés à la même personne.", @@ -109,14 +109,14 @@ "simplex-network-overlay-card-1-li-6": "Les réseaux P2P peuvent être vulnérables aux attaques DRDoS, lorsque les clients peuvent retransmettre et amplifier le trafic, entraînant un déni de service à l’échelle du réseau. Les clients SimpleX ne relaient le trafic que depuis des connexions connues et ne peuvent pas être utilisés par un attaquant pour amplifier le trafic sur l’ensemble du réseau.", "privacy-matters-overlay-card-1-p-1": "De nombreuses grandes entreprises utilisent les informations sur vos relations pour estimer vos revenus, vous vendre des produits dont vous n’avez pas vraiment besoin et déterminer les prix.", "privacy-matters-overlay-card-1-p-2": "Les vendeurs en ligne savent que les personnes à faible revenu sont plus susceptibles d'effectuer des achats urgents. Ils peuvent donc pratiquer des prix plus élevés ou supprimer des remises.", - "privacy-matters-overlay-card-1-p-3": "Certaines entreprises financières et d’assurance utilisent les graphes sociaux pour déterminer les taux d’intérêt et les primes d’assurance. Cela conduit souvent les personnes aux revenus modestes à payer davantage — on parle de « prime de pauvreté ».", + "privacy-matters-overlay-card-1-p-3": "Certaines entreprises financières et d’assurance utilisent les graphes sociaux pour déterminer les taux d’intérêt et les primes d’assurance. Cela conduit souvent les personnes aux revenus modestes à payer davantage — on parle de « prime de pauvreté ».", "privacy-matters-overlay-card-1-p-4": "Le réseau SimpleX protège mieux la confidentialité de vos connexions que toute autre solution, empêchant totalement que votre graphe social soit accessible à des entreprises ou des organisations. Même lorsque les utilisateurs utilisent les serveurs préconfigurés dans les applications SimpleX Chat, les opérateurs des serveurs ne connaissent ni le nombre d’utilisateurs ni leurs connexions.", - "privacy-matters-overlay-card-2-p-1": "Il n’y a pas si longtemps, nous avons vu des élections majeures être manipulées par une société de conseil réputée qui a utilisé nos graphes sociaux pour fausser notre perception du monde réel et manipuler nos votes.", + "privacy-matters-overlay-card-2-p-1": "Il n’y a pas si longtemps, nous avons vu des élections majeures être manipulées par une société de conseil réputée qui a utilisé nos graphes sociaux pour fausser notre perception du monde réel et manipuler nos votes.", "privacy-matters-overlay-card-2-p-2": "Pour être objectif et prendre des décisions indépendantes, vous devez garder le contrôle de votre espace informationnel. Cela n’est possible que si vous utilisez un réseau de communication privé qui n’a pas accès à votre graphe social.", "privacy-matters-overlay-card-2-p-3": "SimpleX est le premier réseau qui ne possède aucun identifiant utilisateur par conception, protégeant ainsi votre graphe de connexions mieux que toute autre solution connue.", "privacy-matters-overlay-card-3-p-1": "Tout le monde devrait se soucier de la confidentialité et de la sécurité de ses communications — des conversations lambda peuvent vous mettre en danger, même si vous n'avez rien à cacher.", - "privacy-matters-overlay-card-3-p-2": "L’une des histoires les plus choquantes est celle de Mohamedou Ould Salahi, relatée dans ses mémoires et dans le film Désigné coupable (The Mauritanian). Il a été détenu au camp de Guantánamo, sans procès, et y a été torturé pendant 15 ans après un appel téléphonique à un proche en Afghanistan, étant soupçonné d’être impliqué dans les attentats du 11 septembre, alors qu’il vivait en Allemagne depuis 10 ans.", - "privacy-matters-overlay-card-3-p-3": "Des personnes ordinaires sont arrêtées pour ce qu’elles partagent en ligne, même via leurs comptes « anonymes », même dans les pays démocratiques.", + "privacy-matters-overlay-card-3-p-2": "L’une des histoires les plus choquantes est celle de Mohamedou Ould Salahi, relatée dans ses mémoires et dans le film Désigné coupable (The Mauritanian). Il a été détenu au camp de Guantánamo, sans procès, et y a été torturé pendant 15 ans après un appel téléphonique à un proche en Afghanistan, étant soupçonné d’être impliqué dans les attentats du 11 septembre, alors qu’il vivait en Allemagne depuis 10 ans.", + "privacy-matters-overlay-card-3-p-3": "Des personnes ordinaires sont arrêtées pour ce qu’elles partagent en ligne, même via leurs comptes « anonymes », même dans les pays démocratiques.", "privacy-matters-overlay-card-3-p-4": "Il ne suffit pas d'utiliser une messagerie chiffrée de bout en bout, nous devrions tous utiliser des messageries qui protègent la vie privée de nos réseaux personnels — les personnes avec lesquelles nous sommes connectés.", "simplex-unique-overlay-card-1-p-1": "Contrairement aux autres réseaux de messagerie, SimpleX n’attribue aucun identifiant aux utilisateurs. Il ne repose ni sur les numéros de téléphone, ni sur les adresses basées sur des domaines (comme les adresses e-mail ou XMPP), ni sur les noms d’utilisateur, les clés publiques ou même les nombres aléatoires pour identifier ses utilisateurs — les opérateurs des serveurs SimpleX ne savent pas combien de personnes utilisent leurs serveurs.", "simplex-unique-overlay-card-1-p-2": "Pour acheminer les messages, SimpleX utilise des adresses anonymes distinctes pour chaque interlocuteur de files de messages unidirectionnelles, séparées pour les messages reçus et envoyés, généralement via des serveurs différents.", @@ -128,8 +128,8 @@ "simplex-unique-overlay-card-3-p-3": "Contrairement aux serveurs de réseaux fédérés (e-mail, XMPP ou Matrix), les serveurs SimpleX ne stockent pas les comptes d'utilisateurs, ils ne font que relayer les messages, protégeant ainsi la vie privée des deux parties.", "simplex-unique-overlay-card-3-p-4": "Il n'y a pas d'identifiant ou de texte chiffré en commun entre le trafic serveur envoyé et reçu - si quelqu'un l'observe, il ne peut pas facilement déterminer qui communique avec qui, même si le TLS est compromis.", "simplex-unique-overlay-card-4-p-1": "Vous pouvez utiliser SimpleX avec vos propres serveurs et continuer à communiquer avec les personnes qui utilisent les serveurs préconfigurés dans les applications.", - "simplex-unique-overlay-card-4-p-2": "Le réseau SimpleX utilise un protocole ouvert et fournit un SDK pour créer des chatbots, permettant de mettre en œuvre des services avec lesquels les utilisateurs peuvent interagir via les applications SimpleX Chat — nous avons hâte de voir quels services SimpleX vous allez créer.", - "simplex-unique-overlay-card-4-p-3": "Si vous envisagez de développer pour le réseau SimpleX, par exemple un chatbot pour les utilisateurs de l’application SimpleX ou d’intégrer la bibliothèque SimpleX Chat à vos applications mobiles, n’hésitez pas à nous contacter pour obtenir des conseils et de l’aide.", + "simplex-unique-overlay-card-4-p-2": "Le réseau SimpleX utilise un protocole ouvert et fournit un SDK pour créer des chatbots, permettant de mettre en œuvre des services avec lesquels les utilisateurs peuvent interagir via les applications SimpleX Chat — nous avons hâte de voir quels services SimpleX vous allez créer.", + "simplex-unique-overlay-card-4-p-3": "Si vous envisagez de développer pour le réseau SimpleX, par exemple un chatbot pour les utilisateurs de l’application SimpleX ou d’intégrer la bibliothèque SimpleX Chat à vos applications mobiles, n’hésitez pas à nous contacter pour obtenir des conseils et de l’aide.", "simplex-unique-card-1-p-1": "SimpleX protège la confidentialité de votre profil, de vos contacts et de vos métadonnées, en les cachant des serveurs du réseau SimpleX et de tout observateur.", "simplex-unique-card-1-p-2": "Contrairement à tout autre réseau de messagerie existant, SimpleX n'a aucun identifiant attribué aux utilisateurs — pas même de nombres aléatoires.", "simplex-unique-card-2-p-1": "Parce que vous n'avez pas d'identifiant ou d'adresse fixe sur le réseau SimpleX, personne ne peut vous contacter sauf si vous partagez une adresse d'utilisateur unique ou temporaire, comme un code QR ou un lien.", diff --git a/website/langs/he.json b/website/langs/he.json index 11dc4a0b4d..8e1fd0aaf5 100644 --- a/website/langs/he.json +++ b/website/langs/he.json @@ -73,7 +73,7 @@ "simplex-private-5-title": "שכבות מרובות של
ריפוד תוכן", "simplex-private-card-3-point-3": "חידוש החיבור מושבת כדי למנוע התקפות הפעלה.", "simplex-private-card-4-point-1": "כדי להגן על כתובת ה-IP שלכם, אתם יכולים לגשת לשרתים דרך Tor או רשת שכבת-על אחרת של תעבורה.", - "simplex-private-card-4-point-2": "כדי להשתמש ב-SimpleX דרך Tor, התקן את אפליקציית Orbot והפעל את SOCKS5 proxy (או VPN ב-iOS).", + "simplex-private-card-4-point-2": "כדי להשתמש ב-SimpleX דרך Tor, התקן את אפליקציית Orbot והפעל את SOCKS5 proxy (או VPN ב-iOS).", "simplex-private-card-7-point-2": "אם הודעה כלשהי תתווסף, תוסר או תשתנה, הנמען יקבל התראה.", "simplex-private-card-9-point-1": "כל תור הודעות מעביר הודעות בכיוון אחד, עם כתובות השליחה והקבלה השונות.", "privacy-matters-1-overlay-1-title": "פרטיות חוסכת לכם כסף", @@ -99,13 +99,13 @@ "privacy-matters-overlay-card-1-p-2": "קמעונאים מקוונים יודעים שאנשים עם הכנסה נמוכה יותר נוטים יותר לבצע רכישות דחופות, ולכן הם עשויים לגבות מחירים גבוהים יותר או להסיר הנחות.", "hero-overlay-card-1-p-2": "כדי להעביר הודעות, במקום מזהי משתמש המשמשים את כל הרשתות האחרות, SimpleX משתמש במזהים זמניים אנונימיים של תורי הודעות, נפרדים לכל אחד מהחיבורים שלך - אין מזהים ארוכי טווח.", "hero-overlay-card-2-p-2": "לאחר מכן הם יוכלו לקשר מידע זה עם הרשתות החברתיות הציבוריות הקיימות, ולקבוע כמה זהויות אמיתיות.", - "privacy-matters-overlay-card-1-p-3": "חלק מחברות פיננסיות וביטוח משתמשות בגרפים חברתיים כדי לקבוע שיעורי ריבית ופרמיות. לעתים קרובות זה גורם לאנשים עם הכנסה נמוכה יותר לשלם יותר — זה ידוע בתור 'פרמיית עוני'.", + "privacy-matters-overlay-card-1-p-3": "חלק מחברות פיננסיות וביטוח משתמשות בגרפים חברתיים כדי לקבוע שיעורי ריבית ופרמיות. לעתים קרובות זה גורם לאנשים עם הכנסה נמוכה יותר לשלם יותר — זה ידוע בתור 'פרמיית עוני'.", "hero-overlay-card-1-p-4": "עיצוב זה מונע דליפה כלשהי של משתמשים' מטא נתונים ברמת האפליקציה. כדי לשפר עוד יותר את הפרטיות ולהגן על כתובת ה-IP שלך, תוכל להתחבר לשרתי הודעות באמצעות Tor.", "hero-overlay-card-2-p-3": "אפילו עם האפליקציות הפרטיות ביותר המשתמשות בשירותי Tor v3, אם אתה מדבר עם שני אנשי קשר שונים דרך אותו פרופיל הם יכולים להוכיח שהם מחוברים לאותו אדם.", "hero-overlay-card-3-p-1": "Trail of Bits היא חברת ייעוץ מובילה בתחום אבטחה וטכנולוגיה שלקוחותיה כוללים ביג טק, סוכנויות ממשלתיות ופרויקטי בלוקצ'יין גדולים.", "hero-overlay-card-1-p-1": "משתמשים רבים שאלו: אם ל-SimpleX אין מזהי משתמש, איך הוא יכול לדעת לאן להעביר הודעות?", "simplex-network-overlay-card-1-li-2": "לעיצוב של SimpleX, בניגוד לרוב רשתות ה-P2P, אין מזהי משתמש גלובלי מכל סוג שהוא, אפילו זמני, והוא משתמש רק במזהים זמניים זוגיים, המספקים אנונימיות טובה יותר והגנה על מטא נתונים.", - "hero-overlay-card-1-p-6": "קרא עוד בסקירה הטכנית של SimpleX.", + "hero-overlay-card-1-p-6": "קרא עוד בסקירה הטכנית של SimpleX.", "simplex-network-overlay-card-1-p-1": "לפרוטוקולי הודעות ואפליקציות P2P יש בעיות שונות שהופכות אותן לפחות אמינות מ-SimpleX, מורכבות יותר לניתוח, ופגיעות למספר סוגי התקפות.", "hero-overlay-card-1-p-3": "אתה מגדיר באילו שרתים להשתמש כדי לקבל את ההודעות, אנשי הקשר שלך — השרתים שבהם אתה משתמש כדי לשלוח את ההודעות אליהם. סביר שכל שיחה תהשתמש בשני שרתים שונים.", "copy-the-command-below-text": "העתיקו את הפקודה למטה והשתמש בה בצ'אט:", @@ -113,7 +113,7 @@ "scan-the-qr-code-with-the-simplex-chat-app": "סרקו את קוד ה-QR באפליקציית SimpleX Chat", "simplex-unique-card-3-p-2": "ההודעות המוצפנות מקצה לקצה מוחזקות באופן זמני בשרתי ממסר של SimpleX עד שמתקבלות, ואז הן נמחקות לצמיתות.", "simplex-unique-overlay-card-3-p-3": "שלא כמו שרתי רשת מאוחדים (דוא\"ל, XMPP או Matrix), שרתי SimpleX אינם מאחסנים חשבונות משתמשים, הם רק מעבירים הודעות, ומגנים על הפרטיות של שני הצדדים.", - "privacy-matters-overlay-card-3-p-2": "אחד הסיפורים המזעזעים ביותר הוא החוויה של מוחמד אולד סלאחי המתוארת בספר הזיכרונות שלו ומוצגת בסרט \"המאוריטני\". הוא הוכנס למתקן המעצר בגואנטנמו, ללא משפט, ועונה שם במשך 15 שנים לאחר שיחת טלפון לקרוב משפחתו באפגניסטן, בחשד שהיה מעורב בפיגועים ב-11 בספטמבר, למרות שחי בגרמניה בעשר השנים הקודמות.", + "privacy-matters-overlay-card-3-p-2": "אחד הסיפורים המזעזעים ביותר הוא החוויה של מוחמד אולד סלאחי המתוארת בספר הזיכרונות שלו ומוצגת בסרט \"המאוריטני\". הוא הוכנס למתקן המעצר בגואנטנמו, ללא משפט, ועונה שם במשך 15 שנים לאחר שיחת טלפון לקרוב משפחתו באפגניסטן, בחשד שהיה מעורב בפיגועים ב-11 בספטמבר, למרות שחי בגרמניה בעשר השנים הקודמות.", "simplex-network-2-desc": "שרתי ממסר SimpleX אינם מאחסנים פרופילי משתמש, אנשי קשר והודעות שנמסרו, אינם מתחברים זה לזה, ואין ספריית שרתים.", "installing-simplex-chat-to-terminal": "התקנת SimpleX Chat למסוף שורת הפקודה", "use-this-command": "השתמשו בפקודה הזו:", @@ -160,13 +160,13 @@ "simplex-unique-overlay-card-3-p-4": "אין מזהים או טקסט מוצפן משותף בין תעבורת שרת שנשלחה ומתקבלת — אם מישהו צופה בזה, הם לא יכולים לקבוע בקלות מי מתקשר עם מי, גם אם ה-TLS נפרץ.", "get-simplex": "הורידו את אפליקציית שולחן העבודה SimpleX", "privacy-matters-overlay-card-3-p-1": "לכולם צריך להיות אכפת מהפרטיות והאבטחה של התקשורת שלהם — שיחות לא מזיקות עלולות להעמיד אתכם בסכנה, גם אם אין לכם מה להסתיר.", - "simplex-unique-overlay-card-4-p-3": "אם אתם שוקלים לפתח עבור רשת SimpleX, למשל, את הצ'אט בוט עבור משתמשי אפליקציית SimpleX, או שילוב של ספריית SimpleX Chat באפליקציות לנייד שלכם, אנא צרו קשר לכל עצה ותמיכה.", + "simplex-unique-overlay-card-4-p-3": "אם אתם שוקלים לפתח עבור רשת SimpleX, למשל, את הצ'אט בוט עבור משתמשי אפליקציית SimpleX, או שילוב של ספריית SimpleX Chat באפליקציות לנייד שלכם, אנא צרו קשר לכל עצה ותמיכה.", "simplex-network-3-header": "רשת SimpleX", "simplex-unique-card-2-p-1": "מכיוון שאין לך מזהה או כתובת קבועה ברשת SimpleX, אף אחד לא יכול ליצור איתך קשר אלא אם כן אתה משתף כתובת משתמש חד-פעמית או זמנית, כקוד QR או קישור.", "join-the-REDDIT-community": "הצטרפו לקהילת REDDIT", - "privacy-matters-overlay-card-3-p-3": "אנשים רגילים נעצרים על מה שהם משתפים באינטרנט, אפילו דרך החשבונות ה'אנונימיים' שלהם, אפילו במדינות דמוקרטיות.", + "privacy-matters-overlay-card-3-p-3": "אנשים רגילים נעצרים על מה שהם משתפים באינטרנט, אפילו דרך החשבונות ה'אנונימיים' שלהם, אפילו במדינות דמוקרטיות.", "simplex-unique-overlay-card-3-p-2": "ההודעות המוצפנות מקצה לקצה מוחזקות באופן זמני בשרתי ממסר של SimpleX עד שמתקבלות, ואז הן נמחקות לצמיתות.", - "simplex-unique-overlay-card-4-p-2": "רשת SimpleX משתמשת בפרוטוקול פתוח ומספקת SDK ליצירת צ'אט בוטים, המאפשר הטמעה של שירותים שמשתמשים יכולים לתקשר איתם באמצעות אפליקציות SimpleX Chat — אנחנו ממש מצפים לראות אילו שירותי SimpleX אתם יכולים לבנות.", + "simplex-unique-overlay-card-4-p-2": "רשת SimpleX משתמשת בפרוטוקול פתוח ומספקת SDK ליצירת צ'אט בוטים, המאפשר הטמעה של שירותים שמשתמשים יכולים לתקשר איתם באמצעות אפליקציות SimpleX Chat — אנחנו ממש מצפים לראות אילו שירותי SimpleX אתם יכולים לבנות.", "contact-hero-p-2": "עדיין לא הורדתם את ה-SimpleX Chat?", "why-simplex-is-unique": "מדוע SimpleX הוא ייחודי", "simplex-network-section-header": "רשת SimpleX", @@ -174,7 +174,7 @@ "simplex-network-1-overlay-linktext": "בעיות של רשתות P2P", "protocol-2-text": "XMPP, Matrix", "simplex-network-overlay-card-1-li-4": "יישומי P2P עלולים להיחסם על ידי ספקי אינטרנט מסוימים (כמו BitTorrent). SimpleX אינו תלוי בתעבורה - הוא יכול לעבוד על פרוטוקולי אינטרנט סטנדרטיים, למשל WebSockets.", - "privacy-matters-overlay-card-2-p-1": "לפני זמן לא רב ראינו את הבחירות הראשיות שעברו מניפולציות על ידי חברת ייעוץ מכובדת שהשתמשה בגרפים החברתיים שלנו כדי לעוות את השקפתנו על העולם האמיתי ולתמרן את ההצבעות שלנו.", + "privacy-matters-overlay-card-2-p-1": "לפני זמן לא רב ראינו את הבחירות הראשיות שעברו מניפולציות על ידי חברת ייעוץ מכובדת שהשתמשה בגרפים החברתיים שלנו כדי לעוות את השקפתנו על העולם האמיתי ולתמרן את ההצבעות שלנו.", "privacy-matters-overlay-card-2-p-3": "SimpleX היא הרשת הראשונה שאין לה מזהי משתמש מטבעה, ובכך מגינה על גרף החיבורים שלך טוב יותר מכל חלופה מוכרת.", "learn-more": "למדו עוד", "scan-qr-code-from-mobile-app": "סרקו קוד QR באפליקציה לנייד", diff --git a/website/langs/hu.json b/website/langs/hu.json index 80a5c614e1..1b3a4f5869 100644 --- a/website/langs/hu.json +++ b/website/langs/hu.json @@ -90,7 +90,7 @@ "hero-overlay-card-1-p-2": "Az üzenetek kézbesítéséhez az összes többi hálózat által használt felhasználói azonosítók helyett a SimpleX az üzenetek várólistába rendezéséhez ideiglenes, névtelen, páros azonosítókat használ, külön-külön minden egyes kapcsolathoz — nincsenek hosszú távú azonosítók.", "hero-overlay-card-1-p-4": "Ez a kialakítás megakadályozza a felhasználók metaadatainak kiszivárgását az alkalmazás szintjén. Az adatvédelem további javítása és az IP-cím védelme érdekében az üzenetküldő kiszolgálókhoz Tor hálózaton keresztül is kapcsolódhat.", "hero-overlay-card-1-p-5": "Csak a kliensek tárolják a felhasználói profilokat, partnereket és csoportokat; az üzenetek küldése kétrétegű végpontok közötti titkosítással történik.", - "hero-overlay-card-1-p-6": "További leírást a SimpleX ismertetőben olvashat.", + "hero-overlay-card-1-p-6": "További leírást a SimpleX ismertetőben olvashat.", "hero-overlay-card-2-p-1": "Ha a felhasználók állandó azonosítóval rendelkeznek, még akkor is, ha ez csak egy véletlenszerű szám, például egy munkamenet-azonosító, fennáll annak a veszélye, hogy a szolgáltató vagy egy támadó megfigyelheti, azt hogy hogyan kapcsolódnak a felhasználók egymáshoz, és hány üzenetet küldenek egymásnak.", "hero-overlay-card-2-p-2": "Ezt az információt aztán összefüggésbe hozhatják a meglévő nyilvános közösségi hálózatokkal, és meghatározhatnak néhány valódi személyazonosságot.", "hero-overlay-card-2-p-3": "Még a Tor v3 szolgáltatásokat használó, legprivátabb alkalmazások esetében is, ha két különböző partnerével beszél ugyanazon a profilon keresztül, bizonyítani tudják, hogy ugyanahhoz a személyhez kapcsolódnak.", @@ -104,14 +104,14 @@ "simplex-network-overlay-card-1-li-5": "Minden ismert P2P-hálózat sebezhető Sybil támadással, mert minden egyes csomópont felderíthető, és a hálózat egészként működik. A támadások enyhítésére szolgáló ismert intézkedés lehet egy központi kiszolgáló (például: tracker), vagy egy drága tanúsítvány. A SimpleX hálózat nem ismeri fel a kiszolgálókat, töredezett és több elszigetelt alhálózatként működik, ami lehetetlenné teszi az egész hálózatra kiterjedő támadásokat.", "privacy-matters-overlay-card-1-p-1": "Sok nagyvállalat arra használja fel a felhasználóival kapcsolatban álló személyek adatait, hogy megbecsülje a jövedelmi helyzetüket és olyan termékeket kínáljon fel, amelyekre valójában nincs is szükségük, valamint hogy meghatározza az árakat.", "privacy-matters-overlay-card-1-p-2": "Az online kiskereskedők tudják, hogy az alacsonyabb jövedelműek nagyobb valószínűséggel vásárolnak azonnal, ezért magasabb árakat számíthatnak fel, vagy eltörölhetik a kedvezményeket.", - "privacy-matters-overlay-card-1-p-3": "Egyes pénzügyi és biztosítótársaságok szociális grafikonokat használnak a kamatlábak és a díjak meghatározásához. Ez gyakran arra készteti az alacsonyabb jövedelmű embereket, hogy többet fizessenek — ez az úgynevezett „szegénységi prémium”.", + "privacy-matters-overlay-card-1-p-3": "Egyes pénzügyi és biztosítótársaságok szociális grafikonokat használnak a kamatlábak és a díjak meghatározásához. Ez gyakran arra készteti az alacsonyabb jövedelmű embereket, hogy többet fizessenek — ez az úgynevezett „szegénységi prémium”.", "privacy-matters-overlay-card-1-p-4": "A SimpleX hálózat minden alternatívánál jobban védi a kapcsolatai adatait, teljes mértékben megakadályozva, hogy az ismeretségi-hálója bármilyen vállalat vagy szervezet számára elérhetővé váljon. Még ha az emberek a SimpleX Chat által előre beállított kiszolgálókat is használják, sem az alkalmazások, sem a kiszolgálók üzemeltetői nem ismerik, sem felhasználók számát, sem a kapcsolataikat.", - "privacy-matters-overlay-card-2-p-1": "Nem is olyan régen megfigyelhettük, hogy a nagy választásokat manipulálta egy neves tanácsadó cég, amely az ismeretségi-háló segítségével eltorzította a valós világról alkotott képünket, és manipulálta a szavazatainkat.", + "privacy-matters-overlay-card-2-p-1": "Nem is olyan régen megfigyelhettük, hogy a nagy választásokat manipulálta egy neves tanácsadó cég, amely az ismeretségi-háló segítségével eltorzította a valós világról alkotott képünket, és manipulálta a szavazatainkat.", "privacy-matters-overlay-card-2-p-2": "Ahhoz, hogy objektív legyen és független döntéseket tudjon hozni, az információs terét is kézben kell tartania. Ez csak akkor lehetséges, ha egy privát kommunikációs hálózatot használ, amely nem fér hozzá az ismeretségi hálójához.", "privacy-matters-overlay-card-2-p-3": "A SimpleX az első olyan hálózat, amely eleve nem rendelkezik felhasználói azonosítókkal, így jobban védi az ismeretségi-hálóját, mint bármely ismert alternatíva.", "privacy-matters-overlay-card-3-p-1": "Mindenkinek törődnie kell a magánélet és a kommunikáció biztonságával — az ártalmatlan beszélgetések veszélybe sodorhatják, még akkor is, ha nincs semmi rejtegetnivalója.", - "privacy-matters-overlay-card-3-p-2": "Az egyik legmegdöbbentőbb a Mohamedou Ould Salahi memoárjában leírt és az „A mauritániai” c. filmben bemutatott történet. Őt bírósági tárgyalás nélkül a guantánamói táborba zárták, és ott kínozták 15 éven át, miután egy afganisztáni rokonát telefonon felhívta, akit azzal gyanúsítottak a hatóságok, hogy köze van a 9/11-es merényletekhez, holott Salahi az előző 10 évben Németországban élt.", - "privacy-matters-overlay-card-3-p-3": "Átlagos embereket letartóztatnak azért, amit online megosztanak, még „névtelen” fiókjaikon keresztül is, még demokratikus országokban is.", + "privacy-matters-overlay-card-3-p-2": "Az egyik legmegdöbbentőbb a Mohamedou Ould Salahi memoárjában leírt és az „A mauritániai” c. filmben bemutatott történet. Őt bírósági tárgyalás nélkül a guantánamói táborba zárták, és ott kínozták 15 éven át, miután egy afganisztáni rokonát telefonon felhívta, akit azzal gyanúsítottak a hatóságok, hogy köze van a 9/11-es merényletekhez, holott Salahi az előző 10 évben Németországban élt.", + "privacy-matters-overlay-card-3-p-3": "Átlagos embereket letartóztatnak azért, amit online megosztanak, még „névtelen” fiókjaikon keresztül is, még demokratikus országokban is.", "privacy-matters-overlay-card-3-p-4": "Nem elég csak egy végpontok között titkosított üzenetváltó alkalmazást használnunk, mindannyiunknak olyan üzenetváltó alkalmazásokat kell használnunk, amelyek védik a személyes partnereink magánéletét — akikkel kapcsolatban állunk.", "simplex-unique-overlay-card-1-p-1": "Más üzenetküldő hálózatoktól eltérően a SimpleX nem rendel azonosítókat a felhasználókhoz. Nem támaszkodik telefonszámokra, tartomány-alapú címekre (mint az e-mail, XMPP vagy a Matrix), felhasználónevekre, nyilvános kulcsokra vagy akár véletlenszerű számokra a felhasználók azonosításához — a SimpleX kiszolgálók üzemeltetői nem tudják, hogy hányan használják a kiszolgálóikat.", "simplex-unique-overlay-card-1-p-2": "Az üzenetek kézbesítéséhez a SimpleX egyirányú várólistákat használ az üzenetekhez, páronkénti, névtelen címekkel, külön a fogadott és külön az elküldött üzenetekhez, általában különböző kiszolgálókon keresztül.", @@ -123,8 +123,8 @@ "simplex-unique-overlay-card-3-p-3": "A föderált hálózatok kiszolgálóitól (e-mail, XMPP vagy Matrix) eltérően a SimpleX kiszolgálók nem tárolják a felhasználói fiókokat, csak továbbítják az üzeneteket, így védve mindkét fél magánéletét.", "simplex-unique-overlay-card-3-p-4": "A küldött és a fogadott kiszolgálóforgalom között nincsenek közös azonosítók vagy titkosított szövegek — ha bárki megfigyeli, nem tudja könnyen megállapítani, hogy ki kivel kommunikál, még akkor sem, ha a TLS-t kompromittálják.", "simplex-unique-overlay-card-4-p-1": "Használhatja a SimpleXet a saját kiszolgálóival, és továbbra is kommunikálhat azokkal, akik az előre beállított kiszolgálókat használják az alkalmazásban.", - "simplex-unique-overlay-card-4-p-2": "A SimpleX hálózat nyitott protokollt használ és SDK-t biztosít a csevegési botok létrehozásához, lehetővé téve olyan szolgáltatások megvalósítását, amelyekkel a felhasználók a SimpleX Chat alkalmazásokon keresztül léphetnek kapcsolatba — mi már nagyon várjuk, hogy milyen SimpleX szolgáltatásokat készítenek a lelkes közreműködők.", - "simplex-unique-overlay-card-4-p-3": "Ha a SimpleX hálózatra való fejlesztést fontolgatja, például a SimpleX alkalmazások felhasználóinak szánt csevegési botot, vagy a SimpleX Chat könyvtárbotjának integrálását más mobilalkalmazásba, lépjen velünk kapcsolatba, ha bármilyen tanácsot vagy támogatást szeretne kapni.", + "simplex-unique-overlay-card-4-p-2": "A SimpleX hálózat nyitott protokollt használ és SDK-t biztosít a csevegési botok létrehozásához, lehetővé téve olyan szolgáltatások megvalósítását, amelyekkel a felhasználók a SimpleX Chat alkalmazásokon keresztül léphetnek kapcsolatba — mi már nagyon várjuk, hogy milyen SimpleX szolgáltatásokat készítenek a lelkes közreműködők.", + "simplex-unique-overlay-card-4-p-3": "Ha a SimpleX hálózatra való fejlesztést fontolgatja, például a SimpleX alkalmazások felhasználóinak szánt csevegési botot, vagy a SimpleX Chat könyvtárbotjának integrálását más mobilalkalmazásba, lépjen velünk kapcsolatba, ha bármilyen tanácsot vagy támogatást szeretne kapni.", "simplex-unique-card-1-p-1": "A SimpleX megvédi a profilhoz tartozó partnereket és azok metaadatait is, elrejtve azokat a SimpleX hálózat kiszolgálói és a megfigyelők elől.", "simplex-unique-card-1-p-2": "Minden más létező üzenetküldő hálózattól eltérően a SimpleX nem rendelkezik a felhasználókhoz rendelt azonosítókkal — még véletlenszerű számokkal sem.", "simplex-unique-card-2-p-1": "Mivel a SimpleX hálózaton senkinek sincs azonosítója vagy állandó címe, ezért senki sem tud kapcsolatba lépni a felhasználókkal, hacsak nem osztanak meg egy egyszeri vagy ideiglenes felhasználói címet, például QR-kódot vagy hivatkozást.", @@ -224,7 +224,7 @@ "contact-hero-header": "Ez egy meghívó a SimpleX Chaten való beszélgetéshez", "invitation-hero-header": "Kapott egy egyszer használható meghívót a SimpleX Chaten való beszélgetéshez", "simplex-network-overlay-card-1-li-4": "A P2P-megvalósításokat egyes internetszolgáltatók blokkolhatják (mint például a BitTorrent). A SimpleX átvitel-független — a szabványos webes protokollokon, például WebSocketsen keresztül is működik.", - "simplex-private-card-4-point-2": "A SimpleX, Tor hálózaton keresztüli használatához telepítse az Orbot alkalmazást és engedélyezze a SOCKS5 proxyt (vagy a VPN-t az iOS-ban).", + "simplex-private-card-4-point-2": "A SimpleX, Tor hálózaton keresztüli használatához telepítse az Orbot alkalmazást és engedélyezze a SOCKS5 proxyt (vagy a VPN-t az iOS-ban).", "simplex-private-card-5-point-1": "A SimpleX minden titkosítási réteghez tartalomkitöltést használ az üzenetméretre irányuló támadások meghiúsítása érdekében.", "simplex-private-card-5-point-2": "A kiszolgálók és a hálózatot megfigyelők számára a különböző méretű üzenetek egyformának tűnnek.", "privacy-matters-1-title": "Hirdetés és árdiszkrimináció", diff --git a/website/langs/id.json b/website/langs/id.json index 2811873b46..b1134bcaff 100644 --- a/website/langs/id.json +++ b/website/langs/id.json @@ -126,7 +126,7 @@ "simplex-private-card-3-point-1": "Hanya TLS 1.2/1.3 dengan algoritma kuat yang digunakan untuk koneksi client-server.", "simplex-private-card-3-point-2": "Sidikjari server dan pengikatan saluran mencegah serangan MITM secara berulang.", "simplex-private-card-3-point-3": "Pemulihan koneksi dinonaktifkan untuk mencegah serangan sesi.", - "simplex-private-card-4-point-2": "Untuk menggunakan SimpleX melalui Tor, silakan instal aplikasi Orbot dan aktifkan proksi SOCKS5 (atau VPN di iOS).", + "simplex-private-card-4-point-2": "Untuk menggunakan SimpleX melalui Tor, silakan instal aplikasi Orbot dan aktifkan proksi SOCKS5 (atau VPN di iOS).", "simplex-private-card-5-point-1": "SimpleX menggunakan pengunci konten untuk setiap lapisan enkripsi guna menggagalkan serangan pesan.", "simplex-private-card-5-point-2": "Ini membuat pesan dengan ukuran berbeda terlihat sama bagi server dan pengamat jaringan.", "simplex-private-card-6-point-1": "Banyak jaringan komunikasi yang rentan terhadap serangan MITM oleh server atau penyedia jaringan.", @@ -145,7 +145,7 @@ "simplex-private-card-1-point-2": "Kotak kripto NaCL di setiap antrean untuk mencegah korelasi lalu lintas antara antrean pesan jika TLS disusupi.", "simplex-private-card-2-point-1": "Lapisan enkripsi server tambahan untuk pengiriman ke penerima, untuk mencegah korelasi antara lalu lintas server yang diterima dan dikirim jika TLS disusupi.", "hero-overlay-card-1-p-5": "Hanya perangkat klien yang menyimpan profil pengguna, kontak, dan grup; pesan dikirim dengan enkripsi end-to-end 2 lapis.", - "hero-overlay-card-1-p-6": "Selengkapnya di whitepaper SimpleX.", + "hero-overlay-card-1-p-6": "Selengkapnya di whitepaper SimpleX.", "hero-overlay-card-2-p-1": "Bila pengguna memiliki identitas persisten, meskipun ini hanya angka acak, seperti ID Sesi, ada risiko bahwa penyedia atau penyerang dapat mengamati bagaimana pengguna terhubung dan berapa banyak pesan yang mereka kirim.", "hero-overlay-card-2-p-2": "Mereka kemudian dapat menghubungkan informasi ini dengan jaringan sosial publik yang ada, dan menentukan beberapa identitas sebenarnya.", "hero-overlay-card-2-p-3": "Bahkan dengan aplikasi paling pribadi yang menggunakan layanan Tor v3, jika Anda berbicara dengan dua kontak berbeda melalui profil yang sama, mereka dapat membuktikan bahwa mereka terhubung dengan orang yang sama.", @@ -236,14 +236,14 @@ "simplex-network-overlay-card-1-li-6": "Jaringan P2P mungkin rentan terhadap serangan DRDoS, ketika klien dapat menyiarkan ulang dan memperkuat lalu lintas, yang mengakibatkan penolakan layanan di seluruh jaringan. Klien SimpleX hanya meneruskan lalu lintas dari koneksi yang diketahui dan tidak dapat digunakan oleh penyerang untuk memperkuat lalu lintas di seluruh jaringan.", "privacy-matters-overlay-card-1-p-1": "Banyak perusahaan besar menggunakan informasi mengenai siapa yang terhubung dengan Anda untuk memperkirakan pendapatan Anda, menjual produk yang sebenarnya tidak Anda butuhkan, dan menentukan harga.", "privacy-matters-overlay-card-1-p-2": "Pengecer daring mengetahui bahwa orang-orang berpenghasilan rendah cenderung melakukan pembelian mendesak, sehingga mereka mungkin mengenakan harga lebih tinggi atau menghapus diskon.", - "privacy-matters-overlay-card-1-p-3": "Beberapa perusahaan keuangan dan asuransi menggunakan grafik sosial untuk menentukan suku bunga dan premium. Hal ini seringkali membuat orang berpenghasilan rendah membayar lebih banyak — dikenal sebagai 'premium kemelaratan'.", + "privacy-matters-overlay-card-1-p-3": "Beberapa perusahaan keuangan dan asuransi menggunakan grafik sosial untuk menentukan suku bunga dan premium. Hal ini seringkali membuat orang berpenghasilan rendah membayar lebih banyak — dikenal sebagai 'premium kemelaratan'.", "privacy-matters-overlay-card-1-p-4": "Jaringan SimpleX melindungi privasi koneksi Anda lebih baik daripada alternatif lainnya, sepenuhnya mencegah grafik sosial Anda diakses oleh perusahaan atau organisasi mana pun. Bahkan ketika orang menggunakan server yang telah dikonfigurasi sebelumnya di aplikasi SimpleX Chat, operator server tidak mengetahui jumlah pengguna atau koneksi mereka.", - "privacy-matters-overlay-card-2-p-1": "Belum lama ini kita menyaksikan pemilu besar dimanipulasi oleh sebuah perusahaan konsultan terkemuka yang menggunakan grafik sosial kita untuk mendistorsi pandangan kita terhadap dunia nyata dan memanipulasi suara kita.", + "privacy-matters-overlay-card-2-p-1": "Belum lama ini kita menyaksikan pemilu besar dimanipulasi oleh sebuah perusahaan konsultan terkemuka yang menggunakan grafik sosial kita untuk mendistorsi pandangan kita terhadap dunia nyata dan memanipulasi suara kita.", "privacy-matters-overlay-card-2-p-2": "Agar objektif dan dapat membuat keputusan yang independen, Anda perlu mengendalikan ruang informasi Anda. Hal ini hanya mungkin jika Anda menggunakan jaringan komunikasi pribadi yang tidak memiliki akses ke grafik sosial Anda.", "privacy-matters-overlay-card-2-p-3": "SimpleX adalah jaringan pertama yang tidak memiliki ID pengguna apa pun secara desain, dengan cara ini melindungi grafik koneksi Anda lebih baik daripada alternatif yang diketahui.", "privacy-matters-overlay-card-3-p-1": "Setiap orang harus peduli dengan privasi dan keamanan komunikasi mereka — percakapan yang tidak berbahaya dapat membahayakan Anda, bahkan jika Anda tidak menyembunyikan apa pun.", - "privacy-matters-overlay-card-3-p-2": "Salah satu kisah paling mengejutkan adalah pengalaman Mohamedou Ould Salahi yang diceritakan dalam memoarnya dan ditampilkan dalam film The Mauritanian. Ia dimasukkan ke kamp Guantanamo tanpa diadili, dan disiksa di sana selama 15 tahun setelah menelepon kerabatnya di Afghanistan, karena dicurigai terlibat dalam serangan 9/11, meskipun ia pernah tinggal di Jerman selama 10 tahun sebelumnya.", - "privacy-matters-overlay-card-3-p-3": "Orang biasa ditangkap karena apa yang mereka bagikan secara daring, bahkan melalui akun 'anonim' mereka, bahkan di negara demokrasi.", + "privacy-matters-overlay-card-3-p-2": "Salah satu kisah paling mengejutkan adalah pengalaman Mohamedou Ould Salahi yang diceritakan dalam memoarnya dan ditampilkan dalam film The Mauritanian. Ia dimasukkan ke kamp Guantanamo tanpa diadili, dan disiksa di sana selama 15 tahun setelah menelepon kerabatnya di Afghanistan, karena dicurigai terlibat dalam serangan 9/11, meskipun ia pernah tinggal di Jerman selama 10 tahun sebelumnya.", + "privacy-matters-overlay-card-3-p-3": "Orang biasa ditangkap karena apa yang mereka bagikan secara daring, bahkan melalui akun 'anonim' mereka, bahkan di negara demokrasi.", "privacy-matters-overlay-card-3-p-4": "Tidaklah cukup jika hanya menggunakan layanan pesan terenkripsi end-to-end, kita semua harus menggunakan layanan pesan yang melindungi privasi jaringan pribadi kita — dengan siapa kita terhubung.", "simplex-unique-overlay-card-1-p-1": "Berbeda dengan jaringan perpesanan lainnya, SimpleX tidak memiliki ID tetap kepada pengguna. SimpleX tidak bergantung pada nomor telepon, alamat berbasis domain (seperti email atau XMPP), nama pengguna, kunci publik, atau bahkan angka acak untuk mengidentifikasi penggunanya — operator server SimpleX tidak tahu berapa banyak orang yang menggunakan server mereka.", "simplex-unique-overlay-card-1-p-2": "Untuk menyampaikan pesan, SimpleX menggunakan alamat anonim penghubung dari antrean pesan searah, terpisah untuk pesan yang diterima dan dikirim, biasanya melalui server yang berbeda.", @@ -255,8 +255,8 @@ "simplex-unique-overlay-card-3-p-3": "Tidak seperti server jaringan terfederasi (email, XMPP atau Matrix), server SimpleX tidak menyimpan akun pengguna, mereka hanya menyampaikan pesan, melindungi privasi kedua belah pihak.", "simplex-unique-overlay-card-3-p-4": "Tidak ada pengenal atau teks sandi yang sama antara lalu lintas server yang dikirim dan diterima — jika ada orang yang mengamatinya, mereka tidak dapat dengan mudah menentukan siapa yang berkomunikasi dengan siapa, bahkan jika TLS disusupi.", "simplex-unique-overlay-card-4-p-1": "Anda dapat menggunakan SimpleX dengan server Anda sendiri dan tetap berkomunikasi dengan orang yang menggunakan server yang dikonfigurasikan sebelumnya dalam aplikasi.", - "simplex-unique-overlay-card-4-p-2": "Jaringan SimpleX menggunakan protokol terbuka dan menyediakan SDK untuk membuat bot obrolan, yang memungkinkan penerapan layanan yang dapat berinteraksi dengan pengguna melalui aplikasi SimpleX Chat — kami benar-benar menantikan untuk melihat layanan SimpleX apa yang akan Anda buat.", - "simplex-unique-overlay-card-4-p-3": "Jika Anda mempertimbangkan pengembangan untuk jaringan SimpleX, misalnya, bot obrolan untuk pengguna aplikasi SimpleX, atau integrasi pustaka Obrolan SimpleX ke dalam aplikasi seluler Anda, silakan hubungi kami untuk saran dan dukungan apa pun.", + "simplex-unique-overlay-card-4-p-2": "Jaringan SimpleX menggunakan protokol terbuka dan menyediakan SDK untuk membuat bot obrolan, yang memungkinkan penerapan layanan yang dapat berinteraksi dengan pengguna melalui aplikasi SimpleX Chat — kami benar-benar menantikan untuk melihat layanan SimpleX apa yang akan Anda buat.", + "simplex-unique-overlay-card-4-p-3": "Jika Anda mempertimbangkan pengembangan untuk jaringan SimpleX, misalnya, bot obrolan untuk pengguna aplikasi SimpleX, atau integrasi pustaka Obrolan SimpleX ke dalam aplikasi seluler Anda, silakan hubungi kami untuk saran dan dukungan apa pun.", "simplex-unique-card-1-p-1": "SimpleX melindungi privasi profil, kontak, dan metadata Anda, menyembunyikannya dari server jaringan SimpleX dan pengamat mana pun.", "get-simplex": "Dapatkan aplikasi desktop SimpleX", "index-hero-h1": "
Bebaslah
", diff --git a/website/langs/it.json b/website/langs/it.json index a482d23c03..649637b8e2 100644 --- a/website/langs/it.json +++ b/website/langs/it.json @@ -57,7 +57,7 @@ "hero-overlay-card-1-p-1": "Molti utenti hanno chiesto: se SimpleX non ha identificatori utente, come può sapere dove recapitare i messaggi? ", "hero-overlay-card-1-p-3": "Sei tu a definire quale/i server usare per ricevere i messaggi, i tuoi contatti i server che usi per inviare loro i messaggi. È probabile che ogni conversazione utilizzi due server diversi.", "hero-overlay-card-1-p-5": "Solo i dispositivi client conservano profili utente, contatti e gruppi; i messaggi vengono inviati con crittografia end-to-end a 2 livelli.", - "hero-overlay-card-1-p-6": "Maggiori informazioni nel libro bianco di SimpleX.", + "hero-overlay-card-1-p-6": "Maggiori informazioni nel libro bianco di SimpleX.", "hero-overlay-card-2-p-2": "Potrebbero quindi correlare queste informazioni con i social network pubblici esistenti e determinare alcune identità reali.", "hero-overlay-card-2-p-4": "SimpleX protegge da questi attacchi non avendo alcun ID utente per design. Inoltre, se usi la modalità di modalità in incognito, il tuo nome mostrato sarà diverso per ogni contatto, evitando dati condivisi tra di loro.", "simplex-network-overlay-card-1-li-2": "Il design di SimpleX, a differenza della maggior parte delle reti P2P, non ha identificatori utente globali di alcun tipo, nemmeno temporanei, e usa solo identificatori temporanei a coppie, garantendo una maggiore protezione dell'anonimato e dei metadati.", @@ -67,14 +67,14 @@ "privacy-matters-overlay-card-1-p-4": "La rete di SimpleX protegge la privacy delle tue connessioni meglio di qualsiasi alternativa, impedendo completamente che il tuo grafico sociale diventi disponibile a qualsiasi azienda o organizzazione. Anche quando le persone usano i server preconfigurati in SimpleX Chat, gli operatori dei server non conoscono il numero di utenti o le loro connessioni.", "privacy-matters-overlay-card-2-p-3": "SimpleX è la prima rete che non ha alcun identificatore utente per design, proteggendo così il tuo grafico delle connessioni meglio di qualsiasi alternativa conosciuta.", "privacy-matters-overlay-card-3-p-1": "Tutti dovrebbero preoccuparsi della privacy e della sicurezza delle proprie comunicazioni — conversazioni innocue possono metterti in pericolo, anche se non hai nulla da nascondere.", - "privacy-matters-overlay-card-3-p-3": "Le persone comuni vengono arrestate per ciò che condividono online, anche tramite i loro account \"anonimi\", anche nei Paesi democratici.", + "privacy-matters-overlay-card-3-p-3": "Le persone comuni vengono arrestate per ciò che condividono online, anche tramite i loro account \"anonimi\", anche nei Paesi democratici.", "simplex-unique-overlay-card-1-p-3": "Questo design protegge la privacy di chi stai comunicando, nascondendola ai server della rete SimpleX e a qualsiasi osservatore. Per nascondere il tuo indirizzo IP ai server, puoi connetterti ai server SimpleX tramite Tor.", "simplex-unique-overlay-card-2-p-1": "Poiché non hai alcun identificatore sulla rete SimpleX, nessuno può contattarti a meno che tu non condivida un indirizzo utente una tantum o temporaneo, come un codice QR o un link.", "simplex-unique-overlay-card-2-p-2": "Anche l'indirizzo utente opzionale, che può essere usato per inviare richieste di contatto spam, è possibile modificarlo o eliminarlo completamente senza perdere alcuna connessione.", "simplex-unique-overlay-card-3-p-1": "SimpleX Chat conserva tutti i dati utente solo sui dispositivi client usando un formato trasferibile di database crittografato che può essere esportato e trasferito su qualsiasi dispositivo supportato.", "simplex-unique-overlay-card-3-p-2": "I messaggi crittografati end-to-end vengono conservati temporaneamente sui server di inoltro SimpleX fino alla ricezione, quindi vengono eliminati definitivamente.", "simplex-unique-overlay-card-4-p-1": "Puoi usare SimpleX con i tuoi server personali e continuare a comunicare con le persone che usano i server preconfigurati nelle app.", - "simplex-unique-overlay-card-4-p-3": "Se stai pensando di sviluppare per la rete SimpleX, ad esempio, il chat bot per gli utenti dell'app SimpleX o l'integrazione della libreria SimpleX Chat nelle tue app mobili, contattaci per qualsiasi consiglio e supporto.", + "simplex-unique-overlay-card-4-p-3": "Se stai pensando di sviluppare per la rete SimpleX, ad esempio, il chat bot per gli utenti dell'app SimpleX o l'integrazione della libreria SimpleX Chat nelle tue app mobili, contattaci per qualsiasi consiglio e supporto.", "simplex-unique-card-1-p-1": "SimpleX protegge la privacy del tuo profilo, contatti e metadati, nascondendoli ai server della rete SimpleX e ad eventuali osservatori.", "simplex-unique-card-1-p-2": "A differenza di qualsiasi altra rete di messaggistica esistente, SimpleX non ha identificatori assegnati agli utenti — nemmeno numeri casuali.", "simplex-unique-card-2-p-1": "Poiché non hai un identificatore o un indirizzo fisso sulla rete SimpleX, nessuno può contattarti a meno che tu non condivida un indirizzo utente una tantum o temporaneo, come codice un QR o un link.", @@ -155,7 +155,7 @@ "developers": "Sviluppatori", "simplex-explained-tab-1-p-1": "Puoi creare contatti e gruppi, ed avere conversazioni bidirezionali, come in qualsiasi altro messenger.", "feature-2-title": "Immagini, video e file
crittografati E2E", - "simplex-private-card-4-point-2": "Per usare SimpleX tramite Tor, installa l'app Orbot e attiva il proxy SOCKS5 (o VPN su iOS).", + "simplex-private-card-4-point-2": "Per usare SimpleX tramite Tor, installa l'app Orbot e attiva il proxy SOCKS5 (o VPN su iOS).", "reference": "Riferimenti", "simplex-explained-tab-3-text": "3. Cosa vedono i server", "chat-bot-example": "Esempio di chat bot", @@ -180,10 +180,10 @@ "privacy-matters-overlay-card-1-p-1": "Molte grandi aziende usano le informazioni sulle persone con cui sei connesso per stimare il tuo reddito, venderti i prodotti di cui non hai realmente bisogno e determinare i prezzi.", "simplex-network-overlay-card-1-li-5": "Tutte le reti P2P conosciute possono essere vulnerabili all'attacco di Sybil, perché ogni nodo è rilevabile e la rete opera come un tutt'uno. Le misure note per mitigarlo richiedono un componente centralizzato o proof-of-work costosi. La rete di SimpleX non ha la possibilità di scoprire i server, è frammentata e opera come più sottoreti isolate, rendendo impossibili gli attacchi a livello di rete.", "privacy-matters-overlay-card-1-p-2": "I rivenditori online sanno che le persone con redditi più bassi sono più propense a fare acquisti urgenti, quindi possono applicare prezzi più alti o rimuovere sconti.", - "privacy-matters-overlay-card-1-p-3": "Alcune società finanziarie e assicurative usano grafici sociali per determinare i tassi di interesse e i premi. Spesso ciò fa pagare di più le persone con redditi più bassi — è noto come \"premio di povertà\".", - "privacy-matters-overlay-card-2-p-1": "Non molto tempo fa abbiamo assistito alla manipolazione delle principali elezioni da una rispettabile società di consulenza che ha usato i nostri grafici sociali per distorcere la nostra visione del mondo reale e manipolare i nostri voti.", + "privacy-matters-overlay-card-1-p-3": "Alcune società finanziarie e assicurative usano grafici sociali per determinare i tassi di interesse e i premi. Spesso ciò fa pagare di più le persone con redditi più bassi — è noto come \"premio di povertà\".", + "privacy-matters-overlay-card-2-p-1": "Non molto tempo fa abbiamo assistito alla manipolazione delle principali elezioni da una rispettabile società di consulenza che ha usato i nostri grafici sociali per distorcere la nostra visione del mondo reale e manipolare i nostri voti.", "privacy-matters-overlay-card-2-p-2": "Per essere obiettivi e prendere decisioni indipendenti devi avere il controllo del tuo spazio informativo. È possibile solo se usi una rete di comunicazione privata che non ha accesso al tuo grafico sociale.", - "privacy-matters-overlay-card-3-p-2": "Una delle storie più scioccanti è l'esperienza di Mohamedou Ould Salahi descritta nel suo libro di memorie e mostrata nel film The Mauritanian. È stato rinchiuso nel campo di Guantánamo, senza processo, e lì è stato torturato per 15 anni dopo una telefonata a un suo parente in Afghanistan, sospettato di essere coinvolto negli attacchi dell'11/9, nonostante avesse vissuto in Germania per i precedenti 10 anni.", + "privacy-matters-overlay-card-3-p-2": "Una delle storie più scioccanti è l'esperienza di Mohamedou Ould Salahi descritta nel suo libro di memorie e mostrata nel film The Mauritanian. È stato rinchiuso nel campo di Guantánamo, senza processo, e lì è stato torturato per 15 anni dopo una telefonata a un suo parente in Afghanistan, sospettato di essere coinvolto negli attacchi dell'11/9, nonostante avesse vissuto in Germania per i precedenti 10 anni.", "join-us-on-GitHub": "Unisciti a noi su GitHub", "simplex-chat-for-the-terminal": "SimpleX Chat per il terminale", "privacy-matters-overlay-card-3-p-4": "Non è sufficiente usare un messenger crittografato end-to-end, tutti dovremmo usare i messenger che proteggono la privacy delle nostre reti personali — con chi siamo connessi.", @@ -195,7 +195,7 @@ "join-the-REDDIT-community": "Unisciti alla comunità di REDDIT", "sign-up-to-receive-our-updates": "Iscriviti per ricevere i nostri aggiornamenti", "simplex-unique-overlay-card-3-p-4": "Non ci sono identificatori o testi cifrati in comune tra il traffico del server inviato e quello ricevuto — se qualcuno lo osserva, non può determinare facilmente chi comunica con chi, anche se il TLS è compromesso.", - "simplex-unique-overlay-card-4-p-2": "La rete di SimpleX usa un protocollo aperto e fornisce un SDK per creare chat bot, consentendo l'implementazione di servizi con cui gli utenti possono interagire tramite le app SimpleX Chat — siamo impazienti di vedere quali servizi SimpleX creerai.", + "simplex-unique-overlay-card-4-p-2": "La rete di SimpleX usa un protocollo aperto e fornisce un SDK per creare chat bot, consentendo l'implementazione di servizi con cui gli utenti possono interagire tramite le app SimpleX Chat — siamo impazienti di vedere quali servizi SimpleX creerai.", "simplex-unique-card-4-p-1": "La rete SimpleX è completamente decentralizzata e indipendente da qualsiasi criptovaluta o altra rete, ad eccezione di internet.", "donate-here-to-help-us": "Dona qui per aiutarci", "to-make-a-connection": "Per stabilire una connessione:", diff --git a/website/langs/ja.json b/website/langs/ja.json index 087a18ac6a..4c850853e4 100644 --- a/website/langs/ja.json +++ b/website/langs/ja.json @@ -24,8 +24,8 @@ "guide-dropdown-8": "アプリ設定", "menu": "メニュー", "simplex-unique-card-1-p-1": "SimpleXは、SimpleXネットワークのサーバやその他の観察者から隠すことで、あなたのプロフィール、連絡先やメタデータのプライバシーを守ります。", - "simplex-unique-overlay-card-4-p-3": "例えば、SimpleXアプリユーザへのチャットボットやSimpleX Chatライブラリーの携帯アプリへの統合など、SimpleXネットワークに関する開発を検討してくださっているようでしたら、どのようなアドバイスや支援のことでもご連絡ください 。", - "simplex-unique-overlay-card-4-p-2": "SimpleXネットワークは、SimpleX Chatアプリを介してユーザが交流するサービスを実装させつつオープンプロトコルを使い、チャットボットを作成するためにSDKを提供します—私たちはあなた達がどのようなSimpleXのサービスを築くか本当に楽しみです。", + "simplex-unique-overlay-card-4-p-3": "例えば、SimpleXアプリユーザへのチャットボットやSimpleX Chatライブラリーの携帯アプリへの統合など、SimpleXネットワークに関する開発を検討してくださっているようでしたら、どのようなアドバイスや支援のことでもご連絡ください 。", + "simplex-unique-overlay-card-4-p-2": "SimpleXネットワークは、SimpleX Chatアプリを介してユーザが交流するサービスを実装させつつオープンプロトコルを使い、チャットボットを作成するためにSDKを提供します—私たちはあなた達がどのようなSimpleXのサービスを築くか本当に楽しみです。", "simplex-unique-overlay-card-4-p-1": "あなたが、自分自身のサーバでSimpleXを使っても、アプリで予め設定されたサーバを使う方々と連絡を取ることができます。", "reference": "リファレンス", "simplex-explained-tab-1-text": "1. ユーザーが経験すること", @@ -64,7 +64,7 @@ "feature-7-title": "ポータブルな暗号化データベース — プロファイルを別のデバイスに移動する", "no-federated": "いいえ - 連合型", "simplex-unique-overlay-card-3-p-3": "電子メール、XMPP、Matrixなどの連携ネットワークサーバーとは異なり、SimpleXサーバーはユーザーアカウントを保存せず、メッセージの中継のみを行い、双方のプライバシーを保護します。", - "privacy-matters-overlay-card-3-p-2": "最も衝撃的な話の 1 つは、Mohamedou Ould Salahiの経験であり、彼の回顧録に記述され、『モーリタニア映画』で紹介されています。彼はアフガニスタンの親戚に電話をかけた後、アメリカ同時多発テロへの関与の疑いで拘束されました。 彼は裁判も受けずにグアンタナモ収容所に入れられ、そこで15年間拷問を受けました。", + "privacy-matters-overlay-card-3-p-2": "最も衝撃的な話の 1 つは、Mohamedou Ould Salahiの経験であり、彼の回顧録に記述され、『モーリタニア映画』で紹介されています。彼はアフガニスタンの親戚に電話をかけた後、アメリカ同時多発テロへの関与の疑いで拘束されました。 彼は裁判も受けずにグアンタナモ収容所に入れられ、そこで15年間拷問を受けました。", "signing-key-fingerprint": "署名キーのフィンガープリント (SHA-256)", "simplex-network-2-desc": "SimpleX リレー サーバーは、ユーザー プロファイル、連絡先、配信されたメッセージを保存せず、相互に接続せず、サーバー ディレクトリもありません。", "docs-dropdown-5": "ホストXFTPサーバー", @@ -109,7 +109,7 @@ "no": "いいえ", "contact-hero-header": "SimpleX Chatで接続するためのアドレスを受信しました", "feature-8-title": "シークレット モード —
SimpleX Chat に固有の", - "simplex-private-card-4-point-2": "Tor経由でSimpleXを使用するには、Orbotアプリをインストールし、SOCKS5プロキシを有効にしてください(iOSの場合はVPN)。", + "simplex-private-card-4-point-2": "Tor経由でSimpleXを使用するには、Orbotアプリをインストールし、SOCKS5プロキシを有効にしてください(iOSの場合はVPN)。", "contact-hero-subheader": "スマホ・タブレットのSimpleX ChatアプリでQRコードを読み取ってください。", "simplex-unique-2-overlay-1-title": "スパムと悪用からの最高の保護", "simplex-private-6-title": "帯域外の
鍵交換", @@ -128,7 +128,7 @@ "comparison-point-1-text": "グローバル ID が必要", "comparison-section-list-point-5": "ユーザーのメタデータのプライバシーを保護しない", "hero-overlay-card-2-p-2": "その後、この情報を既存の公開ソーシャル ネットワークと関連付けて、本当の身元を特定することができます。", - "privacy-matters-overlay-card-1-p-3": "一部の金融会社や保険会社は、ソーシャル グラフを使用して金利や保険料を決定しています。 多くの場合、収入の低い人にはより多くの料金を支払わなければなりません。 —これは、\"「貧困プレミアム」\" として知られています。", + "privacy-matters-overlay-card-1-p-3": "一部の金融会社や保険会社は、ソーシャル グラフを使用して金利や保険料を決定しています。 多くの場合、収入の低い人にはより多くの料金を支払わなければなりません。 —これは、\"「貧困プレミアム」\" として知られています。", "comparison-point-3-text": "DNS への依存", "yes": "はい", "docs-dropdown-6": "WebRTC サーバー", @@ -190,7 +190,7 @@ "feature-6-title": "E2E暗号化された
音声通話とビデオ通話", "simplex-network-overlay-card-1-li-2": "SimpleX 設計は、ほとんどの P2P ネットワークとは異なり、一時的であってもいかなる種類のグローバル ユーザー識別子も持たず、一時的なペアごとの識別子のみを使用するため、より優れた匿名性とメタデータ保護が提供されます。", "simplex-unique-4-title": "SimpleX ネットワークを所有", - "privacy-matters-overlay-card-3-p-3": "一般の人が、たとえ「匿名」アカウント経由であっても、オンラインで共有した内容で逮捕されます。それは、たとえ民主主義の国であったとしても同じです。", + "privacy-matters-overlay-card-3-p-3": "一般の人が、たとえ「匿名」アカウント経由であっても、オンラインで共有した内容で逮捕されます。それは、たとえ民主主義の国であったとしても同じです。", "simplex-unique-overlay-card-3-p-2": "エンドツーエンドで暗号化されたメッセージは、SimpleXのリレーサーバーで受信するまで一時的に保持され、その後永久に削除されます。", "simplex-private-card-7-point-1": "整合性を保証するために、メッセージには連続した番号が付けられ、前のメッセージのハッシュが含まれます。", "contact-hero-p-2": "SimpleX Chat をまだダウンロードしていませんか?", @@ -209,7 +209,7 @@ "hero-overlay-2-title": "ユーザー ID がプライバシーに悪影響を与えるのはなぜですか?", "docs-dropdown-4": "ホストSMPサーバー", "feature-4-title": "E2E暗号化された音声メッセージ", - "privacy-matters-overlay-card-2-p-1": "つい最近まで、主要な選挙が 有名なコンサルティング会社によって操作されていました。 ソーシャルグラフは私たちの現実世界の見方を歪め、投票を操作しています。", + "privacy-matters-overlay-card-2-p-1": "つい最近まで、主要な選挙が 有名なコンサルティング会社によって操作されていました。 ソーシャルグラフは私たちの現実世界の見方を歪め、投票を操作しています。", "privacy-matters-overlay-card-2-p-3": "SimpleX は、設計上ユーザー識別子を持たない最初のネットワークであり、この方法で既知の代替手段よりも接続グラフを保護します。", "learn-more": "さらに詳しく", "simplex-private-8-title": "メッセージのミキシング
相関性を減らす", @@ -233,7 +233,7 @@ "simplex-unique-overlay-card-1-p-3": "この設計により、通信相手のプライバシーが保護され、SimpleX ネットワークサーバや監視者からプライバシーが隠されます。 IP アドレスをサーバから隠すには、Tor 経由で SimpleX サーバーに接続します。", "simplex-private-7-title": "メッセージの整合性
検証", "privacy-matters-overlay-card-1-p-4": "SimpleXネットワークは、他のどのプラットフォームよりも接続のプライバシーを保護し、ソーシャル グラフが企業や組織に利用されることを完全に防ぎます。 SimpleX Chatアプリに予め設定されたサーバを利用している場合でも、サーバオペレータはユーザーの数や接続数を知ることはできません。", - "hero-overlay-card-1-p-6": "詳細については、SimpleX ホワイトペーパーをご覧ください。", + "hero-overlay-card-1-p-6": "詳細については、SimpleX ホワイトペーパーをご覧ください。", "simplex-network-overlay-card-1-p-1": "P2P メッセージング プロトコルとアプリには、SimpleX よりも信頼性が低く、分析がより複雑になるさまざまな問題があり、また、いくつかの種類の攻撃に対して脆弱です。", "simplex-network-overlay-card-1-li-1": "P2P ネットワークは、メッセージをルーティングするために DHT の一部の変種に依存します。 DHT の設計では、配信保証と遅延のバランスを取る必要があります。 SimpleX は、受信者が選択したサーバーを使用して、メッセージを複数のサーバーを介して並行して冗長的に渡すことができるため、P2P よりも優れた配信保証と低い遅延の両方を備えています。 P2P ネットワークでは、メッセージはアルゴリズムによって選択されたノードを使用して、O(log N) 個のノードを順番に通過します。", "privacy-matters-section-label": "メッセージアプリがあなたのデータにアクセスできないようにしてください!", diff --git a/website/langs/nl.json b/website/langs/nl.json index aebab52842..a7d3741e27 100644 --- a/website/langs/nl.json +++ b/website/langs/nl.json @@ -70,7 +70,7 @@ "simplex-private-6-title": "Out-of-band
sleuteluitwisseling", "simplex-private-7-title": "Bericht integriteit
verificatie", "simplex-private-card-4-point-1": "Om uw IP-adres te beschermen kunt u via Tor of een ander transport overlay netwerk toegang krijgen tot de servers.", - "simplex-private-card-4-point-2": "Installeer de Orbot-app en schakel de SOCKS5-proxy in (of VPN op iOS ) om SimpleX via Tor te gebruiken.", + "simplex-private-card-4-point-2": "Installeer de Orbot-app en schakel de SOCKS5-proxy in (of VPN op iOS ) om SimpleX via Tor te gebruiken.", "simplex-private-card-6-point-2": "Om te voorkomen dat SimpleX apps eenmalige sleutels out-of-band doorgeven, wanneer u een adres deelt als een link of een QR-code.", "simplex-private-card-7-point-1": "Om de integriteit te garanderen, zijn de berichten opeenvolgend genummerd en bevatten ze de hash van het vorige bericht.", "simplex-private-card-9-point-1": "Elke berichten wachtrij geeft berichten in één richting door, met verschillende verzend en ontvangst adressen.", @@ -96,7 +96,7 @@ "hero-overlay-card-2-p-1": "Wanneer gebruikers een blijvende identiteit hebben, zelfs als dit slechts een willekeurig nummer is, zoals een sessie ID, bestaat het risico dat de provider of een aanvaller kan zien hoe de gebruikers zijn verbonden en hoeveel berichten ze verzenden.", "hero-overlay-card-1-p-4": "Dit ontwerp voorkomt het lekken van eventuele gebruikersgegevens en metadata op applicatie niveau. Om de privacy verder te verbeteren en uw IP-adres te beschermen kunt u via Tor verbinding maken met berichten servers.", "hero-overlay-card-1-p-5": "Alleen client apparaten slaan gebruikers profielen, contacten en groepen op; de berichten worden verzonden met 2-laags end-to-end versleuteling.", - "hero-overlay-card-1-p-6": "Lees meer in SimpleX-whitepaper.", + "hero-overlay-card-1-p-6": "Lees meer in SimpleX-whitepaper.", "hero-overlay-card-2-p-2": "Ze zouden deze informatie vervolgens kunnen correleren met de bestaande openbare sociale netwerken en een aantal echte identiteiten kunnen bepalen.", "privacy-matters-3-overlay-1-linkText": "Privacy beschermt uw vrijheid", "simplex-unique-2-overlay-1-title": "De beste bescherming tegen spam en misbruik", @@ -178,14 +178,14 @@ "simplex-network-overlay-card-1-li-6": "P2P netwerken kunnen kwetsbaar zijn voor DRDoS-aanvallen, wanneer de clients het verkeer opnieuw kunnen uitzenden en versterken, wat resulteert in netwerkbrede denial of service. SimpleX clients sturen alleen verkeer van een bekende verbinding door en kunnen niet door een aanvaller worden gebruikt om het verkeer in het hele netwerk te versterken.", "privacy-matters-overlay-card-1-p-1": "Veel grote bedrijven gebruiken informatie over met wie u verbonden bent om uw inkomen te schatten, u de producten te verkopen die u niet echt nodig heeft en om de prijzen te bepalen.", "privacy-matters-overlay-card-1-p-2": "Webwinkels weten dat mensen met lagere inkomens eerder geneigd zijn om dringende aankopen te doen, dus kunnen ze hogere prijzen vragen of kortingen intrekken.", - "privacy-matters-overlay-card-1-p-3": "Sommige financiële en verzekeringsmaatschappijen gebruiken sociale grafieken om rentetarieven en premies te bepalen. Het zorgt er vaak voor dat mensen met lagere inkomens meer betalen — het staat bekend als 'armoedepremie'.", + "privacy-matters-overlay-card-1-p-3": "Sommige financiële en verzekeringsmaatschappijen gebruiken sociale grafieken om rentetarieven en premies te bepalen. Het zorgt er vaak voor dat mensen met lagere inkomens meer betalen — het staat bekend als 'armoedepremie'.", "privacy-matters-overlay-card-1-p-4": "SimpleX Network beschermt de privacy van uw verbindingen beter dan welk alternatief dan ook en voorkomt dat uw sociale netwerk beschikbaar wordt voor bedrijven of organisaties. Zelfs wanneer gebruikers servers gebruiken die vooraf zijn geconfigureerd in SimpleX Chat-apps, weten serverbeheerders niet hoeveel gebruikers er zijn of hoeveel verbindingen ze hebben.", "privacy-matters-overlay-card-2-p-2": "Om objectief te zijn en onafhankelijke beslissingen te nemen, moet je de controle hebben over je informatieruimte. Dit is alleen mogelijk als je gebruikmaakt van een privécommunicatienetwerk dat geen toegang heeft tot je sociale media-account.", "privacy-matters-overlay-card-2-p-3": "SimpleX is het eerste netwerk dat standaard geen gebruikers-ID's gebruikt. Hierdoor is uw verbindingsgrafiek beter beschermd dan elk ander bekend alternatief.", "privacy-matters-overlay-card-3-p-1": "Iedereen zou zich zorgen moeten maken over de privacy en veiligheid van hun communicatie — onschuldige gesprekken kunnen je in gevaar brengen, zelfs als je niets te verbergen hebt.", "simplex-unique-overlay-card-1-p-1": "In tegenstelling tot andere berichtennetwerken heeft SimpleX geen identificatiegegevens toegewezen aan de gebruikers. Het maakt geen gebruik van telefoonnummers, domeingebaseerde adressen (zoals e-mail of XMPP), gebruikersnamen, openbare sleutels of zelfs willekeurige getallen om gebruikers te identificeren – SimpleX-serverbeheerders weten niet hoeveel mensen hun servers gebruiken.", - "privacy-matters-overlay-card-3-p-2": "Een van de meest schokkende verhalen is de ervaring van Mohamedou Ould Salahi beschreven in zijn memoires en getoond in The Mauritanian movie. Hij werd zonder proces in het Guantanamo kamp geplaatst en werd daar 15 jaar lang gemarteld na een telefoontje naar zijn familielid in Afghanistan, op verdenking van betrokkenheid bij aanslagen van 9/11, ook al woonde hij de afgelopen 10 jaar in Duitsland.", - "privacy-matters-overlay-card-3-p-3": "Gewone mensen worden gearresteerd voor wat ze online delen, zelfs via hun 'anonieme' accounts, zelfs in democratische landen.", + "privacy-matters-overlay-card-3-p-2": "Een van de meest schokkende verhalen is de ervaring van Mohamedou Ould Salahi beschreven in zijn memoires en getoond in The Mauritanian movie. Hij werd zonder proces in het Guantanamo kamp geplaatst en werd daar 15 jaar lang gemarteld na een telefoontje naar zijn familielid in Afghanistan, op verdenking van betrokkenheid bij aanslagen van 9/11, ook al woonde hij de afgelopen 10 jaar in Duitsland.", + "privacy-matters-overlay-card-3-p-3": "Gewone mensen worden gearresteerd voor wat ze online delen, zelfs via hun 'anonieme' accounts, zelfs in democratische landen.", "privacy-matters-overlay-card-3-p-4": "Het is niet genoeg om een end-to-end versleutelde messenger te gebruiken, we zouden allemaal de messengers moeten gebruiken die de privacy van onze persoonlijke netwerken beschermen — met wie we verbonden zijn.", "simplex-unique-overlay-card-1-p-2": "Om berichten af te leveren gebruikt SimpleX paarsgewijze anonieme adressen van unidirectionele berichten wachtrijen, gescheiden voor ontvangen en verzonden berichten, meestal via verschillende servers.", "simplex-unique-overlay-card-1-p-3": "Dit ontwerp beschermt de privacy van uw communicatiepartner door deze te verbergen voor SimpleX-netwerkservers en andere buitenstaanders. Om uw IP-adres voor de servers te verbergen, kunt u via Tor verbinding maken met SimpleX-servers.", @@ -195,16 +195,16 @@ "simplex-unique-overlay-card-3-p-2": "De end-to-end versleutelde berichten worden tijdelijk op SimpleX relayservers bewaard totdat ze zijn ontvangen, waarna ze permanent worden verwijderd.", "simplex-unique-overlay-card-3-p-3": "In tegenstelling tot gefedereerde netwerkservers (e-mail, XMPP of Matrix), slaan SimpleX servers geen gebruikers account op, ze sturen alleen berichten door, waardoor de privacy van beide partijen wordt beschermd.", "simplex-unique-overlay-card-4-p-1": "U kunt SimpleX gebruiken met uw eigen servers en toch communiceren met mensen die de servers gebruiken die vooraf in de apps zijn geconfigureerd.", - "simplex-unique-overlay-card-4-p-3": "Als u overweegt om voor het SimpleX-netwerk te ontwikkelen, bijvoorbeeld de chatbot voor SimpleX-appgebruikers of de integratie van de SimpleX Chat-bibliotheek in uw mobiele apps, neem dan contact op voor advies en ondersteuning.", + "simplex-unique-overlay-card-4-p-3": "Als u overweegt om voor het SimpleX-netwerk te ontwikkelen, bijvoorbeeld de chatbot voor SimpleX-appgebruikers of de integratie van de SimpleX Chat-bibliotheek in uw mobiele apps, neem dan contact op voor advies en ondersteuning.", "simplex-unique-card-1-p-1": "SimpleX beschermt de privacy van uw profiel, contacten en metagegevens door deze te verbergen voor de netwerkservers van SimpleX en andere buitenstaanders.", "enter-your-email-address": "Voer uw e-mail adres in", "get-simplex": "Verkrijg SimpleX desktop app", "tap-to-close": "Tik om te sluiten", "contact-hero-header": "Je hebt een adres ontvangen om verbinding te maken met SimpleX Chat", "invitation-hero-header": "Je hebt een eenmalige link ontvangen om verbinding te maken met SimpleX Chat", - "privacy-matters-overlay-card-2-p-1": "Nog niet zo lang geleden zagen we dat de grote verkiezingen werden gemanipuleerd door een gerenommeerd adviesbureau dat onze sociale grafieken gebruikte om ons beeld van de echte wereld te vervormen en onze stemmen te manipuleren.", + "privacy-matters-overlay-card-2-p-1": "Nog niet zo lang geleden zagen we dat de grote verkiezingen werden gemanipuleerd door een gerenommeerd adviesbureau dat onze sociale grafieken gebruikte om ons beeld van de echte wereld te vervormen en onze stemmen te manipuleren.", "simplex-unique-overlay-card-3-p-4": "Er zijn geen identifiers of gecodeerde tekst tussen verzonden en ontvangen serververkeer — als iemand het waarneemt, kunnen ze niet gemakkelijk bepalen wie met wie communiceert, zelfs als TLS is gecompromitteerd.", - "simplex-unique-overlay-card-4-p-2": "SimpleX Network maakt gebruik van een open protocol en biedt SDK om chatbots te maken. Dit maakt de implementatie van services mogelijk waarmee gebruikers via SimpleX Chat-apps kunnen communiceren. We zijn erg benieuwd welke SimpleX-services jullie gaan bouwen.", + "simplex-unique-overlay-card-4-p-2": "SimpleX Network maakt gebruik van een open protocol en biedt SDK om chatbots te maken. Dit maakt de implementatie van services mogelijk waarmee gebruikers via SimpleX Chat-apps kunnen communiceren. We zijn erg benieuwd welke SimpleX-services jullie gaan bouwen.", "simplex-unique-card-1-p-2": "In tegenstelling tot andere bestaande berichtennetwerken worden bij SimpleX geen identificatiegegevens aan de gebruikers toegewezen – zelfs geen willekeurige getallen.", "comparison-section-list-point-1": "Meestal op basis van een telefoonnummer, in sommige gevallen op gebruikersnamen", "comparison-section-list-point-2": "Op DNS gebaseerde adressen", diff --git a/website/langs/pl.json b/website/langs/pl.json index 21a3576cb8..a760dc34cf 100644 --- a/website/langs/pl.json +++ b/website/langs/pl.json @@ -68,7 +68,7 @@ "simplex-private-card-6-point-2": "Aby temu zapobiec aplikacje SimpleX przekazują klucze jednorazowe out-of-band, gdy udostępniasz adres jako link lub kod QR.", "simplex-private-card-7-point-1": "Aby zagwarantować integralność, wiadomości są kolejno numerowane i zawierają hash poprzedniej wiadomości.", "simplex-private-card-7-point-2": "Jeśli jakaś wiadomość zostanie dodana, usunięta lub zmieniona, odbiorca zostanie o tym powiadomiony.", - "simplex-private-card-4-point-2": "Aby użyć SimpleX przez Tor zainstaluj aplikację Orbot na android i włącz proxy SOCKS5 (lub VPN na iOS).", + "simplex-private-card-4-point-2": "Aby użyć SimpleX przez Tor zainstaluj aplikację Orbot na android i włącz proxy SOCKS5 (lub VPN na iOS).", "simplex-private-card-5-point-1": "SimpleX używa paddingu treści dla każdej warstwy szyfrowania, aby udaremnić ataki na rozmiar wiadomości.", "simplex-private-card-5-point-2": "Dzięki temu wiadomości o różnych rozmiarach wyglądają tak samo dla serwerów i obserwatorów sieci.", "simplex-private-card-8-point-1": "Serwery SimpleX działają jak węzły mieszania o niskim opóźnieniu — wiadomości przychodzące i wychodzące mają inną kolejność.", @@ -115,23 +115,23 @@ "hero-overlay-card-1-p-1": "Wielu użytkowników pytało: jeśli SimpleX nie ma identyfikatorów użytkowników, skąd może wiedzieć, gdzie dostarczyć wiadomości?", "hero-overlay-card-1-p-2": "Aby dostarczyć wiadomości, zamiast identyfikatorów użytkownika używanych przez wszystkie inne sieci, SimpleX używa tymczasowych anonimowych identyfikatorów kolejek wiadomości, oddzielnych dla każdego z twoich połączeń — nie ma żadnych długoterminowych identyfikatorów.", "simplex-network-overlay-card-1-li-1": "Sieci P2P opierają się na pewnym wariancie DHT do przekazywania wiadomości. DHT muszą równoważyć gwarancję dostarczenia i opóźnienie. SimpleX ma zarówno lepszą gwarancję dostarczenia, jak i mniejsze opóźnienia niż P2P, ponieważ wiadomość może być nadmiarowo przepuszczona przez kilka serwerów równolegle, z wykorzystaniem serwerów wybranych przez odbiorcę. W sieciach P2P wiadomość jest przepuszczana przez , O(log N) węzłów sekwencyjnie, z wykorzystaniem węzłów wybranych przez algorytm.", - "privacy-matters-overlay-card-3-p-3": "Zwykli ludzie są aresztowani za to, co udostępniają w sieci, nawet za pośrednictwem swoich \"anonimowych\" kont, nawet w krajach demokratycznych.", - "hero-overlay-card-1-p-6": "Więcej w SimpleX whitepaper.", + "privacy-matters-overlay-card-3-p-3": "Zwykli ludzie są aresztowani za to, co udostępniają w sieci, nawet za pośrednictwem swoich \"anonimowych\" kont, nawet w krajach demokratycznych.", + "hero-overlay-card-1-p-6": "Więcej w SimpleX whitepaper.", "hero-overlay-card-2-p-1": "Gdy użytkownicy mają trwałe tożsamości, nawet jeśli jest to tylko losowa liczba, jak ID sesji, istnieje ryzyko, że dostawca lub atakujący może obserwować, jak użytkownicy są połączeni i ile wiadomości wysyłają.", "hero-overlay-card-2-p-2": "Następnie mogliby skorelować te informacje z istniejącymi publicznymi sieciami społecznymi i określić niektóre prawdziwe tożsamości.", "hero-overlay-card-2-p-4": "SimpleX chroni przed tymi atakami, nie mając w swojej konstrukcji żadnych identyfikatorów użytkowników. A jeśli używasz trybu Incognito, będziesz miał inną nazwę wyświetlania dla każdego kontaktu, unikając wszelkich wspólnych danych między nimi.", "simplex-network-overlay-card-1-p-1": "Protokoły P2P i aplikacje do przesyłania wiadomości mają różne problemy, które czynią je mniej niezawodnymi niż SimpleX, bardziej skomplikowanymi w analizie i podatnymi na kilka rodzajów ataków.", "simplex-network-overlay-card-1-li-2": "Projekt SimpleX, w przeciwieństwie do większości sieci P2P, nie posiada żadnych globalnych identyfikatorów użytkowników, nawet tymczasowych, a jedynie używa tymczasowych identyfikatorów parami, zapewniając lepszą anonimowość i ochronę metadanych.", "simplex-network-overlay-card-1-li-4": "Implementacje P2P mogą być blokowane przez niektórych dostawców Internetu (np. BitTorrent). SimpleX jest agnostykiem transportowym — może działać przez standardowe protokoły internetowe, np. WebSockets.", - "privacy-matters-overlay-card-1-p-3": "Niektóre firmy finansowe i ubezpieczeniowe wykorzystują wykresy społeczne do określania stóp procentowych i składek. Często powoduje to, że osoby o niższych dochodach płacą więcej — jest to znane jako \"premia za ubóstwo\".", + "privacy-matters-overlay-card-1-p-3": "Niektóre firmy finansowe i ubezpieczeniowe wykorzystują wykresy społeczne do określania stóp procentowych i składek. Często powoduje to, że osoby o niższych dochodach płacą więcej — jest to znane jako \"premia za ubóstwo\".", "privacy-matters-overlay-card-1-p-4": "Sieć SimpleX chroni prywatność Twoich połączeń lepiej niż jakakolwiek alternatywa, w pełni zapobiegając udostępnieniu Twojego wykresu społecznego jakimkolwiek firmom lub organizacjom. Nawet gdy ludzie korzystają z serwerów prekonfigurowanych przez SimpleX Chat, aplikacje i operatorzy serwerów nie znają liczby użytkowników ani ich połączeń.", "privacy-matters-overlay-card-1-p-2": "Sprzedawcy internetowi wiedzą, że osoby o niższych dochodach częściej dokonują pilnych zakupów, więc mogą naliczać wyższe ceny lub usuwać rabaty.", "privacy-matters-overlay-card-2-p-2": "Aby być obiektywnym i podejmować niezależne decyzje musisz mieć kontrolę nad swoją przestrzenią informacyjną. Jest to możliwe tylko wtedy, gdy korzystasz z prywatnej sieci komunikacyjnej, która nie ma dostępu do Twojego wykresu społecznego.", "privacy-matters-overlay-card-2-p-3": "SimpleX to pierwsza sieć, która z założenia nie posiada żadnych identyfikatorów użytkowników, w ten sposób chroniąc Twój graf połączeń lepiej niż jakakolwiek znana alternatywa.", "simplex-network-overlay-card-1-li-5": "Wszystkie znane sieci P2P mogą być podatne na atak Sybil, ponieważ każdy węzeł jest możliwy do odkrycia, a sieć działa jako całość. Znane środki łagodzące wymagają albo scentralizowanego komponentu, albo kosztownego proof of work. SimpleX nie ma możliwości odkrywania serwerów, jest pofragmentowana i działa jako wiele odizolowanych podsieci, co uniemożliwia ataki na całą sieć.", - "privacy-matters-overlay-card-2-p-1": "Jeszcze nie tak dawno obserwowaliśmy, jak główne wybory były manipulowane przez szacowną firmę konsultingową, która wykorzystywała nasze wykresy społeczne do zniekształcania naszego spojrzenia na świat rzeczywisty i manipulowania naszymi głosami.", + "privacy-matters-overlay-card-2-p-1": "Jeszcze nie tak dawno obserwowaliśmy, jak główne wybory były manipulowane przez szacowną firmę konsultingową, która wykorzystywała nasze wykresy społeczne do zniekształcania naszego spojrzenia na świat rzeczywisty i manipulowania naszymi głosami.", "privacy-matters-overlay-card-3-p-1": "Każdy powinien dbać o prywatność i bezpieczeństwo swojej komunikacji — nieszkodliwe rozmowy mogą narazić Cię na niebezpieczeństwo, nawet jeśli nie masz nic do ukrycia.", - "privacy-matters-overlay-card-3-p-2": "Jedną z najbardziej wstrząsających historii jest doświadczenie Mohamedou Ould Salahi opisane w jego pamiętniku i pokazane w filmie Mauretańczyk (2021). Został on umieszczony w obozie Guantanamo, bez procesu, i był tam torturowany przez 15 lat po telefonie do swojego krewnego w Afganistanie, pod zarzutem udziału w atakach 9/11, mimo że przez poprzednie 10 lat mieszkał w Niemczech.", + "privacy-matters-overlay-card-3-p-2": "Jedną z najbardziej wstrząsających historii jest doświadczenie Mohamedou Ould Salahi opisane w jego pamiętniku i pokazane w filmie Mauretańczyk (2021). Został on umieszczony w obozie Guantanamo, bez procesu, i był tam torturowany przez 15 lat po telefonie do swojego krewnego w Afganistanie, pod zarzutem udziału w atakach 9/11, mimo że przez poprzednie 10 lat mieszkał w Niemczech.", "privacy-matters-overlay-card-3-p-4": "Nie wystarczy używać komunikatora szyfrowanego end-to-end, wszyscy powinniśmy używać komunikatorów, które chronią prywatność naszych sieci osobistych — z kim jesteśmy połączeni.", "simplex-unique-overlay-card-1-p-1": "W przeciwieństwie do innych sieci komunikacyjnych, SimpleX ma brak identyfikatorów przypisanych do użytkowników. Nie polega na numerach telefonów, adresach opartych na domenie (jak e-mail lub XMPP), nazwach użytkowników, kluczach publicznych czy nawet losowych liczbach, aby zidentyfikować swoich użytkowników — operatorzy serwerów SimpleX nie wiedzą, ile osób korzysta z ich serwerów.", "hero-overlay-card-2-p-3": "Nawet w przypadku najbardziej prywatnych aplikacji, które korzystają z trzeciej wersji usług Tor, jeśli rozmawiasz z dwoma różnymi kontaktami za pośrednictwem tego samego profilu, mogą one udowodnić, że są połączone z tą samą osobą.", @@ -149,9 +149,9 @@ "simplex-unique-overlay-card-3-p-3": "W przeciwieństwie do serwerów sieci federacyjnych (e-mail, XMPP czy Matrix), serwery SimpleX nie przechowują kont użytkowników, a jedynie przekazują wiadomości, chroniąc prywatność obu stron.", "simplex-unique-overlay-card-4-p-1": "Możesz używać SimpleX z własnymi serwerami i nadal komunikować się z osobami, które korzystają z prekonfigurowanych w aplikacji serwerów.", "donate-here-to-help-us": "Przekaż darowiznę tutaj, aby nam pomóc", - "simplex-unique-overlay-card-4-p-3": "Jeśli rozważasz rozwój dla sieci SimpleX, na przykład czatbotu dla użytkowników aplikacji SimpleX lub integrację biblioteki SimpleX Chat w swoich aplikacjach mobilnych, prosimy o kontakt dla wszelkich porad i wsparcia.", + "simplex-unique-overlay-card-4-p-3": "Jeśli rozważasz rozwój dla sieci SimpleX, na przykład czatbotu dla użytkowników aplikacji SimpleX lub integrację biblioteki SimpleX Chat w swoich aplikacjach mobilnych, prosimy o kontakt dla wszelkich porad i wsparcia.", "simplex-unique-card-1-p-1": "SimpleX chroni prywatność Twojego profilu, kontaktów i metadanych, ukrywając je przed serwerami sieci SimpleX i wszelkimi obserwatorami.", - "simplex-unique-overlay-card-4-p-2": "Sieć SimpleX wykorzystuje otwarty protokół i udostępnia SDK do tworzenia chatbotów, umożliwiając implementację usług, z którymi użytkownicy mogą wchodzić w interakcje za pośrednictwem aplikacji SimpleX Chat — naprawdę nie możemy się doczekać, aby zobaczyć, jakie usługi SimpleX zbudujesz.", + "simplex-unique-overlay-card-4-p-2": "Sieć SimpleX wykorzystuje otwarty protokół i udostępnia SDK do tworzenia chatbotów, umożliwiając implementację usług, z którymi użytkownicy mogą wchodzić w interakcje za pośrednictwem aplikacji SimpleX Chat — naprawdę nie możemy się doczekać, aby zobaczyć, jakie usługi SimpleX zbudujesz.", "simplex-unique-card-1-p-2": "W przeciwieństwie do każdej innej istniejącej sieci komunikacyjnej, SimpleX nie ma żadnych identyfikatorów przypisanych do użytkowników — nawet losowych liczb.", "tap-the-connect-button-in-the-app": "Stuknij przycisk \"połącz się\" w aplikacji", "join-the-REDDIT-community": "Dołącz do społeczności REDDIT", diff --git a/website/langs/pt_BR.json b/website/langs/pt_BR.json index 52848e5cea..4b663e75ec 100644 --- a/website/langs/pt_BR.json +++ b/website/langs/pt_BR.json @@ -46,7 +46,7 @@ "simplex-private-3-title": "Transporte TLS
seguro e autenticado", "simplex-private-card-2-point-1": "Camada adicional de criptografia de servidor para entrega ao destinatário, para evitar a correlação entre o tráfego de servidor recebido e enviado se o TLS for comprometido.", "simplex-private-card-4-point-1": "Para proteger seu endereço IP, você pode acessar os servidores por meio do Tor ou de alguma outra rede de sobreposição de transporte.", - "simplex-private-card-4-point-2": "Para usar o SimpleX via Tor, instale o aplicativo Orbot e ative o proxy SOCKS5 (ou VPN no iOS).", + "simplex-private-card-4-point-2": "Para usar o SimpleX via Tor, instale o aplicativo Orbot e ative o proxy SOCKS5 (ou VPN no iOS).", "simplex-private-card-7-point-2": "Se alguma mensagem for adicionada, removida ou alterada, o destinatário será alertado.", "simplex-private-card-10-point-1": "O SimpleX usa endereços anônimos temporários em pares e credenciais para cada contato de usuário ou membro de grupo.", "privacy-matters-1-overlay-1-title": "A privacidade economiza seu dinheiro", @@ -68,13 +68,13 @@ "privacy-matters-overlay-card-2-p-2": "Para ser objetivo e tomar decisões independentes, você precisa ter o controle do seu espaço de informações. Isso só é possível se você usar uma rede de comunicação privada que não tenha acesso ao seu gráfico social.", "privacy-matters-overlay-card-3-p-1": "Todos devem se preocupar com a privacidade e a segurança de suas comunicações — conversas inofensivas podem colocá-lo em perigo, mesmo que você não tenha nada a esconder.", "simplex-unique-overlay-card-3-p-1": "O SimpleX Chat armazena todos os dados do usuário somente em dispositivos clientes usando um formato de banco de dados criptografado portátil que pode ser exportado e transferido para qualquer dispositivo compatível.", - "privacy-matters-overlay-card-3-p-2": "Uma das histórias mais chocantes é a experiência de Mohamedou Ould Salahi, descrita em seu livro de memórias e mostrada no filme O Mauritano. Ele foi colocado no campo de Guantánamo, sem julgamento, e lá foi torturado por 15 anos após um telefonema para seu parente no Afeganistão, sob suspeita de estar envolvido nos ataques de 11 de setembro, embora tenha vivido na Alemanha nos 10 anos anteriores.", + "privacy-matters-overlay-card-3-p-2": "Uma das histórias mais chocantes é a experiência de Mohamedou Ould Salahi, descrita em seu livro de memórias e mostrada no filme O Mauritano. Ele foi colocado no campo de Guantánamo, sem julgamento, e lá foi torturado por 15 anos após um telefonema para seu parente no Afeganistão, sob suspeita de estar envolvido nos ataques de 11 de setembro, embora tenha vivido na Alemanha nos 10 anos anteriores.", "simplex-unique-overlay-card-1-p-1": "Ao contrário de outras redes de mensagens, o SimpleX não tem identificadores atribuídos aos usuários. Ele não depende de números de telefone, endereços baseados em domínio (como email ou XMPP), nomes de usuário, chaves públicas ou mesmo números aleatórios para identificar seus usuários — Os operadores dos servidores não sabem quantas pessoas usam os servidores SimpleX.", "simplex-private-card-1-point-1": "Protocolo de dupla catraca —
mensagens OTR com Sigilo de Encaminhamento Perfeito (Perfect Forward Secrecy) e recuperação de invasão.", "simplex-private-8-title": "Mistura de mensagens
para reduzir a correlação", - "simplex-unique-overlay-card-4-p-2": "A rede SimpleX usa um protocolo aberto e fornece um SDK para criar bots de chat, permitindo a implementação de serviços com os quais os usuários podem interagir por meio dos aplicativos SimpleX Chat — estamos' realmente ansiosos para ver quais serviços SimpleX você pode criar.", + "simplex-unique-overlay-card-4-p-2": "A rede SimpleX usa um protocolo aberto e fornece um SDK para criar bots de chat, permitindo a implementação de serviços com os quais os usuários podem interagir por meio dos aplicativos SimpleX Chat — estamos' realmente ansiosos para ver quais serviços SimpleX você pode criar.", "simplex-unique-overlay-card-3-p-4": "Não há identificadores ou texto cifrado em comum entre o tráfego de servidor enviado e recebido — se alguém estiver observando, não poderá determinar facilmente quem se comunica com quem, mesmo que o TLS esteja comprometido.", - "simplex-unique-overlay-card-4-p-3": "Se estiver pensando em desenvolver para a rede SimpleX, por exemplo, o bot de chat para os usuários do aplicativo SimpleX ou a integração da biblioteca SimpleX Chat em seus aplicativos móveis, entre em contato para qualquer orientação e suporte.", + "simplex-unique-overlay-card-4-p-3": "Se estiver pensando em desenvolver para a rede SimpleX, por exemplo, o bot de chat para os usuários do aplicativo SimpleX ou a integração da biblioteca SimpleX Chat em seus aplicativos móveis, entre em contato para qualquer orientação e suporte.", "simplex-unique-card-2-p-1": "Como você não tem um identificador ou endereço fixo na rede SimpleX, ninguém pode entrar em contato com você, a menos que você compartilhe um endereço de usuário único ou temporário, como um QR code ou um link.", "simplex-unique-card-3-p-1": "O SimpleX armazena todos os dados do usuário nos dispositivos clientes em um formato de banco de dados criptografado portátil — que pode ser transferido para outro dispositivo.", "hide-info": "Ocultar informações", @@ -116,17 +116,17 @@ "simplex-unique-4-overlay-1-title": "Totalmente descentralizado — os usuários são proprietários da rede SimpleX", "hero-overlay-card-1-p-3": "Você define o(s) servidor(es) a ser(em) usado(s) para receber as mensagens, seus contatos — os servidores que você usa para enviar as mensagens a eles. É provável que cada conversa use dois servidores diferentes.", "hero-overlay-card-1-p-4": "Esse design evita o vazamento de metadados de qualquer usuário' no nível do aplicativo. Para aumentar ainda mais a privacidade e proteger seu endereço IP, você pode se conectar aos servidores de mensagens via Tor.", - "hero-overlay-card-1-p-6": "Leia mais no informativo técnico do SimpleX.", + "hero-overlay-card-1-p-6": "Leia mais no informativo técnico do SimpleX.", "hero-overlay-card-2-p-2": "Eles poderiam então correlacionar essas informações com as redes sociais públicas existentes e determinar algumas identidades reais.", "hero-overlay-card-2-p-4": "O SimpleX se protege contra esses ataques por não ter nenhum ID de usuário em seu design. E, se você usar o modo Anônimo, terá um nome de exibição diferente para cada contato, evitando qualquer compartilhamento de dados entre eles.", "simplex-network-overlay-card-1-p-1": "Os protocolos e aplicativos de mensagens P2P têm vários problemas que os tornam menos confiáveis do que o SimpleX, mais complexos de analisar e vulneráveis a vários tipos de ataque.", "simplex-network-overlay-card-1-li-2": "O design do SimpleX, ao contrário da maioria das redes P2P, não tem identificadores de usuário globais de qualquer tipo, mesmo temporários, e usa apenas identificadores temporários em pares, proporcionando melhor anonimato e proteção de metadados.", "simplex-network-overlay-card-1-li-3": "O P2P não resolve o problema do ataque MITM, e a maioria das implementações existentes não usa mensagens fora de banda para a troca de chaves inicial. O SimpleX usa mensagens fora de banda ou, em alguns casos, conexões pré-existentes seguras e confiáveis para a troca de chaves inicial.", "simplex-network-overlay-card-1-li-6": "As redes P2P podem ser vulneráveis a ataques DRDoS, quando os clientes podem retransmitir e amplificar o tráfego, resultando em uma negação de serviço em toda a rede. Os clientes SimpleX apenas retransmitem o tráfego de uma conexão conhecida e não podem ser usados por um invasor para amplificar o tráfego em toda a rede.", - "privacy-matters-overlay-card-1-p-3": "Algumas empresas financeiras e de seguros usam gráficos sociais para determinar taxas de juros e prêmios. Isso geralmente faz com que as pessoas com renda mais baixa paguem mais — isso é conhecido como \"prêmio de pobreza\".", - "privacy-matters-overlay-card-2-p-1": "Não faz muito tempo que observamos as eleições sendo manipuladas por uma empresa de consultoria respeitável que usou nossos gráficos sociais para distorcer nossa visão do mundo real e manipular nossos votos.", + "privacy-matters-overlay-card-1-p-3": "Algumas empresas financeiras e de seguros usam gráficos sociais para determinar taxas de juros e prêmios. Isso geralmente faz com que as pessoas com renda mais baixa paguem mais — isso é conhecido como \"prêmio de pobreza\".", + "privacy-matters-overlay-card-2-p-1": "Não faz muito tempo que observamos as eleições sendo manipuladas por uma empresa de consultoria respeitável que usou nossos gráficos sociais para distorcer nossa visão do mundo real e manipular nossos votos.", "privacy-matters-overlay-card-2-p-3": "O SimpleX é a primeira rede que não tem nenhum identificador de usuário por design, protegendo assim seu gráfico de conexões melhor do que qualquer alternativa conhecida.", - "privacy-matters-overlay-card-3-p-3": "Pessoas comuns são presas pelo que compartilham online, mesmo por meio de suas contas \"anônimas\", mesmo em países democráticos.", + "privacy-matters-overlay-card-3-p-3": "Pessoas comuns são presas pelo que compartilham online, mesmo por meio de suas contas \"anônimas\", mesmo em países democráticos.", "privacy-matters-overlay-card-3-p-4": "Não basta usar um mensageiro criptografado de ponta-a-ponta, todos nós devemos usar os mensageiros que protegem a privacidade de nossas redes pessoais — com quem estamos conectados.", "simplex-unique-overlay-card-1-p-2": "Para entregar mensagens, o SimpleX usa endereços anônimos em pares de filas de mensagens unidirecionais, separadas para mensagens recebidas e enviadas, geralmente por meio de servidores diferentes.", "simplex-unique-overlay-card-1-p-3": "Esse design protege a privacidade com quem você está se comunicando, ocultando-a dos servidores da rede SimpleX e de quaisquer observadores. Para ocultar seu endereço IP dos servidores, você pode se conectar aos servidores do SimpleX via Tor.", diff --git a/website/langs/ru.json b/website/langs/ru.json index 9e714e59ff..a8bdfa8725 100644 --- a/website/langs/ru.json +++ b/website/langs/ru.json @@ -14,7 +14,7 @@ "hero-2-header": "Установите конфиденциальное соединение", "simplex-unique-overlay-card-3-p-3": "В отличие от серверов федеративных сетей (электронной почты, XMPP или Matrix), серверы SimpleX не хранят учетные записи пользователей, они только ретранслируют сообщения, защищая конфиденциальность обеих сторон.", "hero-subheader": "Первый мессенджер
без идентификаторов пользователей", - "privacy-matters-overlay-card-3-p-2": "Одна из самых шокирующих историй — это опыт Слахи, Мохаммеда Ульда, описанный в его мемуарах и показанный в фильме Мавританец. Он был помещен в лагерь Гуантанамо без суда и следствия и подвергался там пыткам в течение 15 лет после телефонного звонка своему родственнику в Афганистане, из-за подозрения в причастности к терактам 11 сентября, хотя предыдущие 10 лет он жил в Германии.", + "privacy-matters-overlay-card-3-p-2": "Одна из самых шокирующих историй — это опыт Слахи, Мохаммеда Ульда, описанный в его мемуарах и показанный в фильме Мавританец. Он был помещен в лагерь Гуантанамо без суда и следствия и подвергался там пыткам в течение 15 лет после телефонного звонка своему родственнику в Афганистане, из-за подозрения в причастности к терактам 11 сентября, хотя предыдущие 10 лет он жил в Германии.", "signing-key-fingerprint": "Идентификатор ключа подписи (SHA-256)", "simplex-network-2-desc": "Серверные узлы SimpleX НЕ хранят профили пользователей, контакты и доставленные сообщения, НЕ подключаются друг к другу, и НЕ имеют каталога серверов.", "simplex-privacy": "Конфиденциальность SimpleX", @@ -73,7 +73,7 @@ "contact-hero-header": "Вы получили адрес контакта для соединения в SimpleX Chat", "feature-8-title": "Режим инкогнито —
уникальный для SimpleX Chat", "why-simplex": "Почему SimpleX", - "simplex-private-card-4-point-2": "Чтобы использовать SimpleX через сеть Tor, пожалуйста, установите приложение Orbot и включите прокси (или режим VPN на iOS).", + "simplex-private-card-4-point-2": "Чтобы использовать SimpleX через сеть Tor, пожалуйста, установите приложение Orbot и включите прокси (или режим VPN на iOS).", "contact-hero-subheader": "Отсканируйте QR-код с помощью приложения SimpleX Chat на Вашем телефоне или планшете.", "simplex-unique-2-overlay-1-title": "Лучшая защита от спама и злоупотреблений", "simplex-private-6-title": "Обмен ключами
вне протокола", @@ -95,7 +95,7 @@ "comparison-point-1-text": "Требуется глобальный идентификатор", "comparison-section-list-point-5": "Не защищает конфиденциальность пользовательских метаданных", "hero-overlay-card-2-p-2": "Эта информация может быть сопоставлена с социальными сетями, чтобы определить реальные личности пользователей.", - "privacy-matters-overlay-card-1-p-3": "Некоторые финансовые и страховые компании используют социальные сети для определения процентных ставок и премий. Это часто заставляет людей с более низкими доходами платить больше — это известно как \"наценка за бедность\".", + "privacy-matters-overlay-card-1-p-3": "Некоторые финансовые и страховые компании используют социальные сети для определения процентных ставок и премий. Это часто заставляет людей с более низкими доходами платить больше — это известно как \"наценка за бедность\".", "comparison-point-3-text": "Зависимость от DNS", "yes": "Да", "docs-dropdown-6": "Сервера WebRTC", @@ -157,7 +157,7 @@ "get-simplex": "Скачать SimpleX для компьютера", "privacy-matters-overlay-card-3-p-1": "Каждый должен заботиться о конфиденциальности и безопасности своих коммуникаций — безобидные разговоры могут подвергнуть Вас опасности, например за Ваши политические взгляды, даже если кажется, что Вам \"нечего скрывать\".", "simplex-unique-2-title": "Вы защищены от
спама и злоупотреблений", - "simplex-unique-overlay-card-4-p-3": "Если Вы рассматриваете возможность разработки для сети SimpleX, например, чат-бота для пользователей SimpleX или интеграции библиотеки SimpleX Chat в Ваше мобильное приложение, пожалуйста, обращайтесь за любыми советами и поддержкой.", + "simplex-unique-overlay-card-4-p-3": "Если Вы рассматриваете возможность разработки для сети SimpleX, например, чат-бота для пользователей SimpleX или интеграции библиотеки SimpleX Chat в Ваше мобильное приложение, пожалуйста, обращайтесь за любыми советами и поддержкой.", "comparison-section-list-point-2": "Адреса на основе DNS", "stable-and-beta-versions-built-by-developers": "Стабильные и бета-версии, созданные разработчиками", "simplex-network-3-header": "Сеть SimpleX", @@ -180,11 +180,11 @@ "hero-p-1": "Другие приложения используют ID пользователей: Signal, Matrix, Session, Briar, Jami, Cwtch и т. п.
SimpleX не использует, даже случайных цифр.
Это значительно повышает Вашу конфиденциальность.", "simplex-network-overlay-card-1-li-2": "В отличие от многих P2P сетей, SimpleX спроектирован так, чтобы не нуждаться в глобальных идентификаторах пользователей, даже временных, используя только временные попарные идентификаторы, обеспечивая лучшую анонимность и защиту метаданных пользователей.", "simplex-unique-4-title": "Вы владеете
сетью SimpleX", - "privacy-matters-overlay-card-3-p-3": "Обычных людей арестовывают за то, чем они делятся в Интернете, даже через свои \"анонимные\" аккаунты, даже в демократических странах.", + "privacy-matters-overlay-card-3-p-3": "Обычных людей арестовывают за то, чем они делятся в Интернете, даже через свои \"анонимные\" аккаунты, даже в демократических странах.", "simplex-unique-overlay-card-3-p-2": "Сквозные зашифрованные сообщения временно хранятся на серверах SimpleX до получения, после чего они удаляются безвозвратно.", "blog": "Блог", "simplex-private-card-7-point-1": "Для обеспечения неизменности, сообщения нумеруются по порядку и содержат хеш предыдущего сообщения.", - "simplex-unique-overlay-card-4-p-2": "Сеть SimpleX использует открытый протокол и предоставляет SDK для создания чат-ботов, позволяя внедрять сервисы, с которыми пользователи могут взаимодействовать через приложение SimpleX Chat — мы с нетерпением ждем сервисы SimpleX, которые Вы создадите.", + "simplex-unique-overlay-card-4-p-2": "Сеть SimpleX использует открытый протокол и предоставляет SDK для создания чат-ботов, позволяя внедрять сервисы, с которыми пользователи могут взаимодействовать через приложение SimpleX Chat — мы с нетерпением ждем сервисы SimpleX, которые Вы создадите.", "simplex-explained-tab-1-p-1": "Вы можете создавать контакты и группы, а также вести двусторонние беседы, как и в любом другом мессенджере.", "contact-hero-p-2": "Еще не скачали SimpleX Chat?", "why-simplex-is-unique": "Почему SimpleX уникален", @@ -203,7 +203,7 @@ "simplex-explained-tab-2-text": "2. Как это работает", "docs-dropdown-4": "Свой SMP Сервер", "feature-4-title": "Голосовые сообщения с E2E-шифрованием", - "privacy-matters-overlay-card-2-p-1": "Не так давно мы наблюдали, как авторитетная консалтинговая компания манипулировала крупными выборами, используя наши контакты в социальных сетях, чтобы изменять наши мнения и манипулировать нашими голосами.", + "privacy-matters-overlay-card-2-p-1": "Не так давно мы наблюдали, как авторитетная консалтинговая компания манипулировала крупными выборами, используя наши контакты в социальных сетях, чтобы изменять наши мнения и манипулировать нашими голосами.", "privacy-matters-overlay-card-2-p-3": "SimpleX — это первая сеть, которая не имеет никаких идентификаторов пользователей, таким образом защищая Ваши контакты лучше, чем любая известная альтернатива.", "learn-more": "Узнать больше", "donate": "Пожертвовать", @@ -234,7 +234,7 @@ "developers": "Разработчики", "simplex-private-7-title": "Проверка неизменности
сообщений", "privacy-matters-overlay-card-1-p-4": "Сеть SimpleX обеспечивает более высокий уровень конфиденциальности ваших связей, чем любое другое решение, полностью предотвращая доступ компаний и организаций к вашему социальному графу. Даже если пользователи используют серверы, предварительно настроенные в приложениях SimpleX Chat, операторы этих серверов не знают ни количества пользователей, ни их связей.", - "hero-overlay-card-1-p-6": "Подробнее читайте в техническом описании SimpleX.", + "hero-overlay-card-1-p-6": "Подробнее читайте в техническом описании SimpleX.", "simplex-network-overlay-card-1-p-1": "Протоколы и приложения для обмена сообщениями P2P имеют различные проблемы, которые делают их менее надежными, чем SimpleX, более сложными для анализа и уязвимыми для нескольких типов атак.", "terms-and-privacy-policy": "Политика Конфиденциальности", "about-and-contact-us": "O нас и наши контакты", diff --git a/website/langs/tr.json b/website/langs/tr.json index de607f689a..216e03b43c 100644 --- a/website/langs/tr.json +++ b/website/langs/tr.json @@ -67,7 +67,7 @@ "simplex-private-card-3-point-2": "Sunucu parmak izleri ve kanal bağlama, MITM ve tekrar saldırılarını önler.", "simplex-private-card-3-point-3": "Oturum saldırılarını önlemek için bağlantı devamı devre dışı bırakılmıştır.", "simplex-private-card-4-point-1": "IP adresinizi korumak için Tor veya başka bir taşıma katmanı ağı üzerinden sunuculara erişebilirsiniz.", - "simplex-private-card-4-point-2": "Android'de Tor ile SimpleX kullanmak için Orbot uygulamasını yükleyin ve SOCKS5 proxy'yi etkinleştirin veya iOS'ta VPN kullanın.", + "simplex-private-card-4-point-2": "Android'de Tor ile SimpleX kullanmak için Orbot uygulamasını yükleyin ve SOCKS5 proxy'yi etkinleştirin veya iOS'ta VPN kullanın.", "simplex-private-card-5-point-1": "SimpleX, mesaj boyutu saldırılarını önlemek için her şifreleme katmanında içerik doldurma kullanır.", "simplex-private-card-5-point-2": "Sunucu ve ağ gözlemcileri için aynı görünen farklı boyutlarda mesajlar üretir.", "simplex-private-card-6-point-1": "Birçok iletişim ağı, sunucu veya ağ sağlayıcıları tarafından yapılan MITM saldırılarına karşı savunmasızdır.", @@ -101,7 +101,7 @@ "hero-overlay-card-1-p-3": "Hangi sunucuları mesaj almak için kullanacağınızı siz belirlersiniz. Kişileriniz — bu sunucuları size mesaj göndermek için kullanır. Her konuşma genellikle iki farklı sunucu kullanır.", "hero-overlay-card-1-p-4": "Bu tasarım, uygulama düzeyinde kullanıcı meta verilerinin sızmasını önler. Mesaj sunucularına Tor üzerinden bağlanarak gizliliğinizi ve IP adresinizi koruyabilirsiniz.", "hero-overlay-card-1-p-5": "Kullanıcı profilleri, kişiler ve gruplar yalnızca kullanıcının cihazında saklanır. Mesajlar, iki katmanlı uçtan uca şifreleme ile gönderilir.", - "hero-overlay-card-1-p-6": "Daha fazla bilgi için SimpleX teknik dokümanını okuyun.", + "hero-overlay-card-1-p-6": "Daha fazla bilgi için SimpleX teknik dokümanını okuyun.", "hero-overlay-card-2-p-1": "Kullanıcıların kalıcı kimlikleri olduğunda, bu bir oturum kimliği gibi rastgele bir sayı olsa bile, sağlayıcıların veya saldırganların kullanıcıların birbirleriyle nasıl bağlantılı olduğunu ve kaç mesaj gönderdiklerini tespit etme riski vardır.", "hero-overlay-card-2-p-2": "Bu bilgiler, mevcut halka açık sosyal ağlarla ilişkilendirilebilir ve gerçek kimlikler ortaya çıkarılabilir.", "hero-overlay-card-2-p-3": "Aynı profil üzerinden iki farklı kişiyle sohbet ederseniz, en gizlilik odaklı uygulamalarda bile (ör. Tor v3 hizmetleri kullananlar) bu kişiler, aynı kişiyle bağlantılı olduklarını anlayabilir.", @@ -118,14 +118,14 @@ "simplex-network-overlay-card-1-li-6": "P2P ağları, istemciler trafiği tekrar gönderip güçlendirebildiğinde DRDoS saldırılarına karşı savunmasız olabilir ve bu da tüm ağda hizmet reddine yol açabilir. SimpleX istemcileri yalnızca bilinen bağlantılardan gelen trafiği iletir ve bir saldırgan tarafından ağ genelinde trafiği güçlendirmek için kullanılamaz.", "privacy-matters-overlay-card-1-p-1": "Birçok büyük şirket, kiminle bağlantılı olduğunuz bilgisini gelir seviyenizi tahmin etmek, size gerçekten ihtiyacınız olmayan ürünleri satmak ve fiyatları belirlemek için kullanır.", "privacy-matters-overlay-card-1-p-2": "Çevrimiçi satıcılar, daha düşük gelire sahip kişilerin daha acil alışverişler yaptığını bilir ve bu nedenle daha yüksek fiyatlar talep edebilir veya indirimleri kaldırabilir.", - "privacy-matters-overlay-card-1-p-3": "Bazı finans ve sigorta şirketleri, faiz oranlarını ve primleri belirlemek için sosyal grafikleri kullanır. Düşük gelirliler genellikle daha fazla öder — bu, \"yoksulluk primi\" olarak bilinir.", + "privacy-matters-overlay-card-1-p-3": "Bazı finans ve sigorta şirketleri, faiz oranlarını ve primleri belirlemek için sosyal grafikleri kullanır. Düşük gelirliler genellikle daha fazla öder — bu, \"yoksulluk primi\" olarak bilinir.", "privacy-matters-overlay-card-1-p-4": "SimpleX ağı, bağlantılarınızın gizliliğini diğer tüm alternatiflerden daha iyi korur ve sosyal grafiğinizin şirketler veya kuruluşlar tarafından erişilmesini tamamen engeller. Kullanıcılar SimpleX Chat uygulamasının önceden yapılandırılmış sunucularını kullansa bile, sunucu sahipleri kullanıcı sayısını veya bağlantılarını bilmez.", - "privacy-matters-overlay-card-2-p-1": "Çok da uzun olmayan bir süre önce, büyük seçimlerin saygın bir danışmanlık şirketi tarafından sosyal grafiklerimiz kullanılarak manipüle edildiğine tanık olduk; bu, gerçek dünyaya bakışımızı çarpıttı ve oylarımızı etkiledi.", + "privacy-matters-overlay-card-2-p-1": "Çok da uzun olmayan bir süre önce, büyük seçimlerin saygın bir danışmanlık şirketi tarafından sosyal grafiklerimiz kullanılarak manipüle edildiğine tanık olduk; bu, gerçek dünyaya bakışımızı çarpıttı ve oylarımızı etkiledi.", "privacy-matters-overlay-card-2-p-2": "Tarafsız olmak ve bağımsız kararlar verebilmek için bilgi alanınız üzerinde kontrol sahibi olmalısınız. Bu, yalnızca sosyal grafiğinize erişimi olmayan özel bir iletişim ağı kullanıyorsanız mümkündür.", "privacy-matters-overlay-card-2-p-3": "SimpleX, tasarımı gereği hiçbir kullanıcı kimliği olmayan ilk ağdır ve bağlantı grafiğinizi diğer tüm bilinen alternatiflerden daha iyi korur.", "privacy-matters-overlay-card-3-p-1": "Herkes gizliliğine ve iletişiminin güvenliğine önem vermelidir — zararsız sohbetler bile, saklayacak bir şeyiniz olmasa da sizi tehlikeye atabilir.", - "privacy-matters-overlay-card-3-p-2": "En çarpıcı hikayelerden biri, Mohamedou Ould Slahi'nin yaşadıklarıdır; anılarını yazdığı kitapta ve \"The Mauritanian\" filminde anlatılır. Afganistan'daki akrabalarını aradıktan sonra, Almanya'da 10 yıl yaşamasına rağmen, Guantanamo'ya gönderildi ve yıllarca yargılanmadan işkence gördü çünkü 11 Eylül saldırılarına karıştığından şüpheleniliyordu.", - "privacy-matters-overlay-card-3-p-3": "Normal insanlar bile, çevrimiçi olarak paylaştıkları şeyler nedeniyle, hatta \"anonim\" hesaplarını kullansalar bile, demokratik ülkelerde bile tutuklanabiliyor.", + "privacy-matters-overlay-card-3-p-2": "En çarpıcı hikayelerden biri, Mohamedou Ould Slahi'nin yaşadıklarıdır; anılarını yazdığı kitapta ve \"The Mauritanian\" filminde anlatılır. Afganistan'daki akrabalarını aradıktan sonra, Almanya'da 10 yıl yaşamasına rağmen, Guantanamo'ya gönderildi ve yıllarca yargılanmadan işkence gördü çünkü 11 Eylül saldırılarına karıştığından şüpheleniliyordu.", + "privacy-matters-overlay-card-3-p-3": "Normal insanlar bile, çevrimiçi olarak paylaştıkları şeyler nedeniyle, hatta \"anonim\" hesaplarını kullansalar bile, demokratik ülkelerde bile tutuklanabiliyor.", "privacy-matters-overlay-card-3-p-4": "Sadece uçtan uca şifreli bir mesajlaşma uygulaması kullanmak yeterli değildir. Hepimiz, bağlı olduğumuz kişisel ağlarımızın gizliliğini koruyan bir mesajlaşma uygulaması kullanmalıyız.", "simplex-unique-overlay-card-1-p-1": "Diğer mesajlaşma ağlarının aksine, SimpleX kullanıcılara hiçbir kimlik atamaz. Kullanıcıları tanımlamak için telefon numaralarına, alan adı tabanlı adreslere (e-posta veya XMPP gibi), kullanıcı adlarına, genel anahtarlara veya rastgele sayılara dayanmaz — SimpleX sunucu sahipleri bile sunucularını kaç kişinin kullandığını bilmez.", "simplex-unique-overlay-card-1-p-2": "SimpleX, mesajları iletmek için ikili anonim adresler kullanır; bunlar tek yönlü mesaj kuyruklarından oluşur ve alınan ve gönderilen mesajlar için ayrıdır, genellikle farklı sunucular üzerinden iletilir.", @@ -137,8 +137,8 @@ "simplex-unique-overlay-card-3-p-3": "Federasyonlu ağ sunucularının (ör. e-posta, XMPP veya Matrix) aksine, SimpleX sunucuları kullanıcı hesabı saklamaz, yalnızca mesajları iletir ve böylece tarafların gizliliği korunur.", "simplex-unique-overlay-card-3-p-4": "Gönderilen ve alınan sunucu trafiği arasında ortak bir kimlik veya şifreli metin yoktur — bu nedenle, bir gözlemci TLS ele geçirilse bile kimin kiminle iletişim kurduğunu kolayca tespit edemez.", "simplex-unique-overlay-card-4-p-1": "Kendi sunucularınızla SimpleX kullanabilirsiniz ve yine de uygulamanın önceden yapılandırılmış sunucularını kullanan kişilerle iletişim kurabilirsiniz.", - "simplex-unique-overlay-card-4-p-2": "SimpleX ağı açık bir protokol kullanır ve sohbet botları geliştirmek için bir SDK sunar. Bu sayede kullanıcıların SimpleX Chat uygulamalarıyla etkileşime geçebileceği servisler oluşturabilirsiniz — hangi SimpleX servislerini geliştireceğinizi merakla bekliyoruz.", - "simplex-unique-overlay-card-4-p-3": "SimpleX ağının geliştirilmesine aktif olarak katkıda bulunmak, örneğin SimpleX uygulama kullanıcıları için bir sohbet botu geliştirmek veya SimpleX Chat kütüphanesinin mobil uygulamalara entegrasyonunu ilerletmek isterseniz, bizimle iletişime geçin ve destek alın.", + "simplex-unique-overlay-card-4-p-2": "SimpleX ağı açık bir protokol kullanır ve sohbet botları geliştirmek için bir SDK sunar. Bu sayede kullanıcıların SimpleX Chat uygulamalarıyla etkileşime geçebileceği servisler oluşturabilirsiniz — hangi SimpleX servislerini geliştireceğinizi merakla bekliyoruz.", + "simplex-unique-overlay-card-4-p-3": "SimpleX ağının geliştirilmesine aktif olarak katkıda bulunmak, örneğin SimpleX uygulama kullanıcıları için bir sohbet botu geliştirmek veya SimpleX Chat kütüphanesinin mobil uygulamalara entegrasyonunu ilerletmek isterseniz, bizimle iletişime geçin ve destek alın.", "simplex-unique-card-1-p-1": "SimpleX, profilinizin, kişilerin ve meta verilerinizin gizliliğini korur ve bunları SimpleX ağ sunucularından ve diğer olası gözlemcilerden gizler.", "simplex-unique-card-1-p-2": "Mevcut tüm diğer mesajlaşma ağlarının aksine, SimpleX kullanıcılarına hiçbir kimlik atamaz — rastgele sayılar bile yoktur.", "simplex-unique-card-2-p-1": "SimpleX ağında kimliğiniz veya sabit bir adresiniz olmadığı için, kimse size ulaşamaz; yalnızca tek kullanımlık veya geçici bir kullanıcı adresini QR kodu veya bağlantı olarak paylaşırsanız iletişim kurabilir.", diff --git a/website/langs/uk.json b/website/langs/uk.json index 1848b5ce96..d681517200 100644 --- a/website/langs/uk.json +++ b/website/langs/uk.json @@ -13,13 +13,13 @@ "simplex-network-overlay-card-1-li-5": "Всі відомі P2P-мережі можуть бути вразливими до атаки Sybil, оскільки кожен вузол є відкритим, і мережа працює як ціле. Відомі заходи для пом'якшення цієї атаки вимагають або централізованого компонента, або дорогого доказу роботи. Мережа SimpleX не має виявності сервера, вона роздроблена і працює як кілька ізольованих підмереж, що робить неможливими атаки на всю мережу.", "simplex-network-overlay-card-1-li-6": "P2P-мережі можуть бути вразливими до атаки DRDoS, коли клієнти можуть ребродкастити та збільшувати трафік, що призводить до відмови в обслуговуванні на рівні всієї мережі. Клієнти SimpleX лише пересилають трафік від відомого підключення і не можуть бути використані зловмисником для збільшення трафіку в усій мережі.", "privacy-matters-overlay-card-1-p-2": "Інтернет-роздрібники знають, що люди з низьким доходом частіше роблять термінові покупки, тому вони можуть встановлювати вищі ціни або скасовувати знижки.", - "privacy-matters-overlay-card-1-p-3": "Деякі фінансові та страхові компанії використовують соціальні графи для визначення ставок та страхових премій. Це часто змушує людей з низькими доходами платити більше — це відомо як 'поширений преміум'.", + "privacy-matters-overlay-card-1-p-3": "Деякі фінансові та страхові компанії використовують соціальні графи для визначення ставок та страхових премій. Це часто змушує людей з низькими доходами платити більше — це відомо як 'поширений преміум'.", "privacy-matters-overlay-card-1-p-4": "Мережа SimpleX краще захищає конфіденційність ваших з'єднань, ніж будь-яка інша альтернатива, повністю запобігаючи тому, щоб ваш соціальний граф став доступним для будь-яких компаній чи організацій. Навіть коли люди використовують сервери, попередньо налаштовані в додатках SimpleX Chat, оператори серверів не знають кількості користувачів або їхніх з'єднань.", - "privacy-matters-overlay-card-2-p-1": "Не так давно ми спостерігали, як великі вибори маніпулювалися поважною консалтинговою компанією, яка використовувала наші соціальні графи для спотворення нашого уявлення про реальний світ та маніпулювання нашими голосами.", + "privacy-matters-overlay-card-2-p-1": "Не так давно ми спостерігали, як великі вибори маніпулювалися поважною консалтинговою компанією, яка використовувала наші соціальні графи для спотворення нашого уявлення про реальний світ та маніпулювання нашими голосами.", "privacy-matters-overlay-card-2-p-2": "Щоб бути об'єктивним та приймати незалежні рішення, вам потрібно контролювати ваш інформаційний простір. Це можливо лише за умови використання приватної мережі для спілкування, яка не має доступу до вашого соціального графа.", "privacy-matters-overlay-card-2-p-3": "SimpleX - це перша мережа, яка не має жодних ідентифікаторів користувачів за своїм дизайном, таким чином, захищаючи ваш графік з'єднань краще, ніж будь-яка відома альтернатива.", "privacy-matters-overlay-card-3-p-1": "Кожен повинен турбуватися про конфіденційність та безпеку свого спілкування — безпечні розмови можуть поставити вас під загрозу, навіть якщо у вас немає чого приховувати.", - "privacy-matters-overlay-card-3-p-2": "Однією з найшокуючих історій є досвід Мохамеду Ульд Слахі, описаний в його мемуарах і показаний у фільмі \"Мавританець\". Його посадили в табір Гуантанамо без суду і мукили там 15 років після телефонного дзвінка йому родичеві в Афганістані, під підозрою в причетності до атак 11 вересня, навіть не дивлячись на те, що він проживав у Німеччині протягом попередніх 10 років.", + "privacy-matters-overlay-card-3-p-2": "Однією з найшокуючих історій є досвід Мохамеду Ульд Слахі, описаний в його мемуарах і показаний у фільмі \"Мавританець\". Його посадили в табір Гуантанамо без суду і мукили там 15 років після телефонного дзвінка йому родичеві в Афганістані, під підозрою в причетності до атак 11 вересня, навіть не дивлячись на те, що він проживав у Німеччині протягом попередніх 10 років.", "privacy-matters-overlay-card-3-p-4": "Недостатньо використовувати зашифрований від кінця до кінця месенджер; ми всі повинні використовувати месенджери, які захищають конфіденційність наших особистих мереж — з ким ми з'єднані.", "simplex-unique-overlay-card-1-p-1": "На відміну від інших мереж для обміну повідомленнями, у SimpleX немає ідентифікаторів, призначених користувачам. Він не покладається на номери телефонів, адреси на основі доменів (як електронна пошта або XMPP), імена користувачів, публічні ключі або навіть випадкові числа для ідентифікації своїх користувачів — оператори серверів SimpleX не знають, скільки людей використовують їхні сервери.", "simplex-unique-overlay-card-3-p-3": "На відміну від федеративних мереж серверів (електронна пошта, XMPP або Matrix), сервери SimpleX не зберігають облікові записи користувачів, вони лише ретранслюють повідомлення, захищаючи приватність обох сторін.", @@ -118,7 +118,7 @@ "simplex-private-card-3-point-1": "Використовується лише TLS 1.2/1.3 із сильними алгоритмами для з'єднань клієнт-сервер.", "simplex-private-card-3-point-2": "Відбиток сервера та зв'язування каналу запобігають MITM та атакам повтору.", "simplex-private-card-4-point-1": "Для захисту вашої IP-адреси ви можете отримати доступ до серверів через Tor чи іншу транспортну оверлейну мережу.", - "simplex-private-card-4-point-2": "Для використання SimpleX через Tor, будь ласка, встановіть додаток Orbot та активуйте SOCKS5-проксі (або VPN на iOS).", + "simplex-private-card-4-point-2": "Для використання SimpleX через Tor, будь ласка, встановіть додаток Orbot та активуйте SOCKS5-проксі (або VPN на iOS).", "simplex-private-card-5-point-1": "SimpleX використовує наповнення вмісту для кожного шару шифрування для ускладнення атак за розміром повідомлень.", "simplex-private-card-5-point-2": "Це забезпечує, що повідомлення різних розмірів виглядають однаково для серверів та спостерігачів мережі.", "simplex-private-card-6-point-1": "Багато комунікаційних мереж вразливі до MITM-атак серверів чи постачальників мережі.", @@ -148,7 +148,7 @@ "hero-overlay-card-1-p-2": "Для доставки повідомлень, замість ідентифікаторів користувачів, які використовують усі інші мережі, SimpleX використовує тимчасові анонімні парні ідентифікатори черг повідомлень, окремі для кожного вашого підключення — тут немає довгострокових ідентифікаторів.", "hero-overlay-card-1-p-3": "Ви визначаєте, який(кі) сервер(и) використовувати для отримання повідомлень, ваші контакти — сервери, які ви використовуєте для відправки повідомлень їм. Кожна розмова, ймовірно, використовує два різних сервери.", "hero-overlay-card-1-p-4": "Цей дизайн запобігає витоку метаданих будь-яких користувачів на рівні додатка. Для подальшого покращення конфіденційності та захисту вашої IP-адреси ви можете підключитися до серверів обміну повідомленнями через Tor.", - "hero-overlay-card-1-p-6": "Докладніше читайте у білетному запису SimpleX.", + "hero-overlay-card-1-p-6": "Докладніше читайте у білетному запису SimpleX.", "hero-overlay-card-2-p-2": "Вони можуть потім корелювати цю інформацію з існуючими громадськими соціальними мережами та визначати деякі реальні ідентифікатори.", "hero-overlay-card-2-p-3": "Навіть з найбільш приватними додатками, які використовують служби Tor v3, якщо ви розмовляєте з двома різними контактами через той самий профіль, вони можуть довести, що вони підключені до однієї й тієї ж особи.", "hero-overlay-card-2-p-4": "SimpleX захищає від цих атак, не маючи жодних ідентифікаторів користувачів в своєму дизайні. І, якщо ви використовуєте режим інкогніто, у вас буде різне відображення для кожного контакту, уникнення будь-яких спільних даних між ними.", @@ -156,7 +156,7 @@ "simplex-network-overlay-card-1-li-1": "P2P-мережі покладаються на якусь варіацію розподіленої хеш-таблиці (DHT) для маршрутизації повідомлень. Дизайни DHT повинні балансувати гарантію доставки та затримку. У SimpleX є як краща гарантія доставки, так і менша затримка порівняно із P2P, оскільки повідомлення може передаватися паралельно кількома серверами, використовуючи сервери, обрані отримувачем. У P2P-мережах повідомлення проходить через вузли O(log N) послідовно, використовуючи вузли, обрані алгоритмом.", "simplex-network-overlay-card-1-li-2": "У дизайні SimpleX, на відміну від більшості P2P-мереж, немає жодних глобальних ідентифікаторів користувачів будь-якого виду, навіть тимчасових, та використовуються лише тимчасові парні ідентифікатори, що забезпечує кращу анонімність та захист метаданих.", "privacy-matters-overlay-card-1-p-1": "Багато великих компаній використовують інформацію про те, з ким ви з'єднані, щоб оцінити ваш дохід, продавати вам продукти, які вам дійсно не потрібні, і визначати ціни.", - "privacy-matters-overlay-card-3-p-3": "Звичайних людей арештовують за те, що вони публікують онлайн, навіть через свої 'анонімні' облікові записи, навіть у демократичних країнах.", + "privacy-matters-overlay-card-3-p-3": "Звичайних людей арештовують за те, що вони публікують онлайн, навіть через свої 'анонімні' облікові записи, навіть у демократичних країнах.", "simplex-unique-overlay-card-1-p-2": "Для доставки повідомлень SimpleX використовує парні анонімні адреси однобічних черг повідомлень, окремо для отриманих та відправлених повідомлень, зазвичай через різні сервери.", "simplex-unique-overlay-card-1-p-3": "Цей дизайн захищає конфіденційність того, з ким ви спілкуєтесь, приховуючи це від серверів мережі SimpleX та від будь-яких спостерігачів. Щоб приховати вашу IP-адресу від серверів, ви можете підключатися до серверів SimpleX через Tor.", "simplex-unique-overlay-card-2-p-1": "Оскільки у вас немає ідентифікатора в мережі SimpleX, ніхто не може зв'язатися з вами, якщо ви не поділитеся одноразовою або тимчасовою адресою користувача, як QR-кодом або посиланням.", @@ -165,8 +165,8 @@ "simplex-unique-overlay-card-3-p-2": "Зашифровані повідомлення від кінця до кінця тимчасово зберігаються на ретрансляційних серверах SimpleX до їх отримання, після чого вони назавжди видаляються.", "simplex-unique-overlay-card-3-p-4": "Між надісланим і отриманим серверним трафіком немає спільних ідентифікаторів чи шифрованого тексту — якщо хтось його спостерігає, він не може легко визначити, хто спілкується з ким, навіть якщо TLS скомпрометовано.", "simplex-unique-overlay-card-4-p-1": "Ви можете використовувати SimpleX зі своїми власними серверами і при цьому спілкуватися з людьми, які використовують сервери, попередньо налаштовані в додатках.", - "simplex-unique-overlay-card-4-p-2": "Мережа SimpleX використовує відкритий протокол і надає SDK для створення чат-ботів, що дозволяє реалізувати сервіси, з якими користувачі можуть взаємодіяти через додатки SimpleX Chat — ми справді чекаємо, щоб побачити, які сервіси SimpleX ви створите.", - "simplex-unique-overlay-card-4-p-3": "Якщо ви плануєте розробляти для мережі SimpleX, наприклад, чат-бота для користувачів додатка SimpleX або інтеграцію бібліотеки SimpleX Chat у ваші мобільні додатки, будь ласка, зв'яжіться з нами для отримання порад та підтримки.", + "simplex-unique-overlay-card-4-p-2": "Мережа SimpleX використовує відкритий протокол і надає SDK для створення чат-ботів, що дозволяє реалізувати сервіси, з якими користувачі можуть взаємодіяти через додатки SimpleX Chat — ми справді чекаємо, щоб побачити, які сервіси SimpleX ви створите.", + "simplex-unique-overlay-card-4-p-3": "Якщо ви плануєте розробляти для мережі SimpleX, наприклад, чат-бота для користувачів додатка SimpleX або інтеграцію бібліотеки SimpleX Chat у ваші мобільні додатки, будь ласка, зв'яжіться з нами для отримання порад та підтримки.", "simplex-unique-card-1-p-1": "SimpleX захищає конфіденційність вашого профілю, контактів та метаданих, приховуючи їх від серверів мережі SimpleX та будь-яких спостерігачів.", "simplex-unique-card-1-p-2": "На відміну від будь-якої іншої існуючої мережі для обміну повідомленнями, у SimpleX немає ідентифікаторів, призначених користувачам — не навіть випадкових чисел.", "simplex-unique-card-2-p-1": "Оскільки у вас немає ідентифікатора або фіксованої адреси в мережі SimpleX, ніхто не може зв'язатися з вами, якщо ви не поділитеся одноразовою або тимчасовою адресою користувача, як QR-кодом або посиланням.", diff --git a/website/langs/zh_Hans.json b/website/langs/zh_Hans.json index 2ead3da487..45cee06f5a 100644 --- a/website/langs/zh_Hans.json +++ b/website/langs/zh_Hans.json @@ -86,7 +86,7 @@ "simplex-private-card-3-point-1": "客户端与服务器之间的连接只使用强加密的 TLS 1.2/1.3。", "privacy-matters-3-overlay-1-title": "隐私保您自由", "privacy-matters-3-overlay-1-linkText": "隐私保您自由", - "simplex-private-card-4-point-2": "要通过 Tor 使用 SimpleX,请安装 Orbot 应用并启用 SOCKS5 代理(在 iOS上需要使用 VPN 模式)。", + "simplex-private-card-4-point-2": "要通过 Tor 使用 SimpleX,请安装 Orbot 应用并启用 SOCKS5 代理(在 iOS上需要使用 VPN 模式)。", "simplex-unique-1-overlay-1-title": "您的身份、个人资料、联系人和元数据的完整隐私", "simplex-unique-2-title": "您可以免受垃圾消息和平台滥用的侵害", "simplex-unique-1-title": "您有完整的隐私", @@ -110,7 +110,7 @@ "hero-overlay-card-1-p-4": "此设计可防止在应用程序级别泄漏任何用户元数据。 为了进一步改善隐私并保护您的 IP 地址,您可以通过 Tor 连接到消息服务器。", "hero-overlay-card-1-p-5": "只有客户端设备存储用户配置文件、联系人和群组; 消息均使用两层端到端加密发送。", "simplex-network-overlay-card-1-p-1": "P2P 消息传递协议和应用程序存在各种问题,使得它们不如 SimpleX 可靠,分析起来更复杂,并且 容易受到多种类型的攻击。", - "hero-overlay-card-1-p-6": "阅读 SimpleX 白皮书了解更多信息。", + "hero-overlay-card-1-p-6": "阅读 SimpleX 白皮书了解更多信息。", "hero-overlay-card-2-p-2": "然后他们可以将这些信息与现有的公共社交网络相关联,并确定一些真实身份。", "hero-overlay-card-2-p-1": "当用户具有持久身份时,即使这只是一个随机数(如会话 ID),提供商或攻击者也有可能观察到用户如何连接以及他们发送了多少消息。", "hero-overlay-card-2-p-3": "即使是使用 Tor v3 服务的最私密的应用程序,如果您通过同一个人资料与两个不同的联系人交谈,他们也可以证明他们与同一个人有联系。", @@ -121,14 +121,14 @@ "simplex-network-overlay-card-1-li-5": "所有已知的 P2P 网络都可能受到Sybil 攻击,因为每个节点都是可发现的,并且网络作为一个整体运行。 已知的缓解措施不是需要一个中心化的组件就是需要昂贵的工作量证明。而 SimpleX 网络没有服务器可发现性,它是碎片化的并且作为多个隔离的子网运行,这样全网络范围的攻击便无从实现。", "simplex-network-overlay-card-1-li-6": "P2P 网络可能受到 分布式反射拒绝服务攻击 。客户端有能力重新广播和放大流量,从而导致整个网络范围内的服务中断。 SimpleX 客户端仅中继来自已知连接的流量,因此不能被攻击者用来放大整个网络的流量。", "privacy-matters-overlay-card-1-p-2": "在线零售商知道收入较低的人更有可能在紧急情况下购买商品,因此他们可能会收取更高的价格或取消折扣。", - "privacy-matters-overlay-card-1-p-3": "一些金融和保险公司使用社交图谱来确定利率和保费。 它通常会让收入较低的人支付更多—它被称为“贫困溢价”。", + "privacy-matters-overlay-card-1-p-3": "一些金融和保险公司使用社交图谱来确定利率和保费。 它通常会让收入较低的人支付更多—它被称为“贫困溢价”。", "privacy-matters-overlay-card-1-p-4": "SimpleX 网络比任何替代方案都能更好地保护您人际关系层面的隐私,彻底防止您的社交图谱被任何公司或组织使用。 即使人们使用 SimpleX Chat 应用预配置的服务器,服务器运营方也不知道用户数量或他们的连接数。", - "privacy-matters-overlay-card-2-p-1": "不久前,我们观察到几次大选被一家知名咨询公司操纵,该公司使用我们的社交图谱扭曲我们对现实世界的看法并操纵我们的选票。", + "privacy-matters-overlay-card-2-p-1": "不久前,我们观察到几次大选被一家知名咨询公司操纵,该公司使用我们的社交图谱扭曲我们对现实世界的看法并操纵我们的选票。", "privacy-matters-overlay-card-2-p-2": "为了客观并做出独立的决定,您需要控制您的信息空间。 而这只有当您使用没有能力访问您的社交图谱的,注重隐私的通信网络时,这才有可能。", "privacy-matters-overlay-card-2-p-3": "SimpleX 是第一个没有设计任何用户标识符的网络,这样能比任何已知的替代方案都更好地保护您的连接图谱。", - "privacy-matters-overlay-card-3-p-2": "最令人震惊的故事之一是 Mohamedou Ould Salahi 在他的回忆录中描述并在毛里塔尼亚电影中展示的经历。 他在未经审判的情况下被关进关塔那摩集中营,并在打电话给他在阿富汗的亲戚后在那里遭受了 15 年的折磨,他被怀疑参与了 9/11 袭击,尽管他在过去 10 年住在德国。", + "privacy-matters-overlay-card-3-p-2": "最令人震惊的故事之一是 Mohamedou Ould Salahi 在他的回忆录中描述并在毛里塔尼亚电影中展示的经历。 他在未经审判的情况下被关进关塔那摩集中营,并在打电话给他在阿富汗的亲戚后在那里遭受了 15 年的折磨,他被怀疑参与了 9/11 袭击,尽管他在过去 10 年住在德国。", "privacy-matters-overlay-card-3-p-1": "每个人都应该关心他们通信的隐私和安全——无害的谈话会让您处于危险之中,即使您没有什么可隐瞒的。", - "privacy-matters-overlay-card-3-p-3": "普通人会因为他们在网上分享的内容而被捕,即使是通过他们的“匿名”账户,即使是在民主国家。", + "privacy-matters-overlay-card-3-p-3": "普通人会因为他们在网上分享的内容而被捕,即使是通过他们的“匿名”账户,即使是在民主国家。", "privacy-matters-overlay-card-3-p-4": "使用端到端加密的即时通讯软件还不够,我们都应该使用保护我们个人网络隐私——即我们与谁有联系的即时通讯软件。", "simplex-unique-overlay-card-1-p-1": "与其他消息网络不同,SimpleX 没有分配给用户的标识符。 它不依赖电话号码、基于域的地址(如电子邮件或 XMPP)、用户名、公钥甚至随机数来识别其用户—— SimpleX 服务器运营方不知道有多少人使用其服务器。", "simplex-unique-overlay-card-1-p-2": "为了传递消息,SimpleX 使用单向消息队列的成对匿名地址,通常通过不同的服务器将接收和发送的消息分开。", @@ -136,7 +136,7 @@ "simplex-unique-overlay-card-4-p-1": "您可以将 SimpleX 与您自己的服务器一起使用,并且仍然可以与使用应用中预配置服务器的人们进行通信。", "simplex-unique-card-3-p-2": "端到端加密的消息在被收到前会暂时保存在 SimpleX 中继服务器上,传送完成后它们会被永久删除。", "simplex-unique-card-1-p-1": "SimpleX 保护您的个人资料、联系人和元数据的隐私,不让 SimpleX 网络服务器和任何观察者看到它们。", - "simplex-unique-overlay-card-4-p-3": "如果您正在考虑为在SimpleX 网络上开发,例如,为 SimpleX 应用程序用户开发聊天机器人,或将 SimpleX 聊天库集成到您的移动应用,请 联系我们获取建议和支持。", + "simplex-unique-overlay-card-4-p-3": "如果您正在考虑为在SimpleX 网络上开发,例如,为 SimpleX 应用程序用户开发聊天机器人,或将 SimpleX 聊天库集成到您的移动应用,请 联系我们获取建议和支持。", "simplex-unique-card-4-p-2": "您可以搭配自己的服务器来使用 SimpleX 或使用我们提供的服务器 — 并仍然连接到任何用户。", "simplex-unique-card-2-p-1": "因为您在 SimpleX 网络上没有标识符或固定地址,所以除非您以二维码或链接的形式分享一次性或临时用户地址,没有人可以联系您。", "simplex-unique-card-3-p-1": "SimpleX 以便携式加密数据库格式将所有用户数据存储在客户端设备上—— 它可以转移到另一个设备。", @@ -202,7 +202,7 @@ "see-here": "参见此处", "comparison-section-list-point-7": "P2P 网络要么拥有中央权威,要么整个网络可能被攻陷", "simplex-private-card-5-point-1": "SimpleX 为每个加密层进行内容填充来对抗长度扩展攻击。", - "simplex-unique-overlay-card-4-p-2": "SimpleX 网络使用开放协议并提供用于创建聊天机器人的 SDK, 允许用户实现通过 SimpleX Chat 应用程序与之交互的服务—我们真的很期待看到您会依托SimpleX构建哪些服务。", + "simplex-unique-overlay-card-4-p-2": "SimpleX 网络使用开放协议并提供用于创建聊天机器人的 SDK, 允许用户实现通过 SimpleX Chat 应用程序与之交互的服务—我们真的很期待看到您会依托SimpleX构建哪些服务。", "simplex-unique-overlay-card-2-p-2": "即使使用可选的用户地址,当它被用于发送垃圾邮件联系请求,您可以更改或完全删除它而不会丢失任何连接。", "simplex-unique-overlay-card-3-p-1": "SimpleX Chat 使用便携式加密数据库格式仅将所有用户数据存储在客户端设备上,该格式可以导出并传输到任何支持的设备。", "donate-here-to-help-us": "在这里捐款来帮助我们", diff --git a/website/langs/zh_Hant.json b/website/langs/zh_Hant.json index ced46c6b69..bf7403e053 100644 --- a/website/langs/zh_Hant.json +++ b/website/langs/zh_Hant.json @@ -69,12 +69,12 @@ "see-here": "見此", "privacy-matters-overlay-card-3-p-4": "僅使用端對端加密的通訊工具是不夠的,我們都應使用能保護個人網路隱私的通訊工具。", "hero-overlay-card-1-p-1": "很多用戶問: 如果 SimpleX 沒有用戶標識符,它何以知道向何處傳送訊息?", - "simplex-unique-overlay-card-4-p-2": "SimpleX網路使用開源協議並提供開發包以編寫聊天機器人,允許使用者透過SimpleX Chat應用程式與服務互動—我們非常期待看到您的SimpleX服務之作!", + "simplex-unique-overlay-card-4-p-2": "SimpleX網路使用開源協議並提供開發包以編寫聊天機器人,允許使用者透過SimpleX Chat應用程式與服務互動—我們非常期待看到您的SimpleX服務之作!", "hero-overlay-card-1-p-2": "為傳送訊息,SimpleX 使用消息佇列中的臨時匿名標識符對,每連線各異,而不像其他網路一樣使用用戶ID— SimpleX 沒有長期標識符。", - "privacy-matters-overlay-card-3-p-2": "最令人震驚的故事之一是 Mohamedou Ould Salahi 的經歷,描述於他的回憶錄中,並在《The Mauritanian movie》中呈現。他在未經審訊的情況下被送進關塔納摩集中營,並在那裏遭受了長達 15 年的酷刑,原因是他被懷疑參與了 9/11 襲擊事件,而此前 10 年他一直住在德國。", + "privacy-matters-overlay-card-3-p-2": "最令人震驚的故事之一是 Mohamedou Ould Salahi 的經歷,描述於他的回憶錄中,並在《The Mauritanian movie》中呈現。他在未經審訊的情況下被送進關塔納摩集中營,並在那裏遭受了長達 15 年的酷刑,原因是他被懷疑參與了 9/11 襲擊事件,而此前 10 年他一直住在德國。", "simplex-private-2-title": "伺服器附加加密層", "hero-overlay-card-2-p-1": "如使用者擁有持久性身份,即使只是一個隨機數,例如 Session ID,也會有服務提供者或攻擊者可以觀察使用者如何連線以及傳送了多少條訊息的風險。", - "simplex-unique-overlay-card-4-p-3": "如您正考慮針對 SimpleX 網路進行開發,例如針對 SimpleX 使用者的聊天機器人,或將 SimpleX 聊天函式庫整合至您的手機應用程式,請與我們聯絡以獲取支持與建議。", + "simplex-unique-overlay-card-4-p-3": "如您正考慮針對 SimpleX 網路進行開發,例如針對 SimpleX 使用者的聊天機器人,或將 SimpleX 聊天函式庫整合至您的手機應用程式,請與我們聯絡以獲取支持與建議。", "simplex-network-overlay-card-1-li-1": "點對點網路依賴 DHT 的某些變體來路由訊息。DHT 設計必須平衡傳送保證和延遲。與點對點相比,SimpleX 具有更好的傳送保證和更低的延遲,因為訊息可以使用收件者選擇的伺服器,經由多個伺服器並行冗餘地傳送。在點對點網路中,訊息依次經由O(log N)個由演算法選擇的節點。", "simplex-private-card-3-point-3": "連線恢復被停用,以防止會話攻擊。", "simplex-private-card-2-point-1": "傳送至收件者的伺服器加密附加層,以防止 TLS 遭到攻擊時,接收和傳送的伺服器流量之間的關聯。", @@ -127,7 +127,7 @@ "simplex-unique-3-overlay-1-title": "您的數據的擁有權、控制與安全", "hero-overlay-card-2-p-4": "SimpleX 通過在設計中不使用任何使用者 ID以防止這些攻擊。而且,如果您使用匿名聊天模式,每個聯絡人都會有不同的顯示名稱,避免他們之間有任何共用資料。", "hero-overlay-card-2-p-3": "即使是使用 Tor v3 服務的最隱私的應用程式,如果您透過相同的設定檔與兩個不同的人交談,他們也可以證明他們連線的是同一個人。", - "hero-overlay-card-1-p-6": "請參閱 SimpleX 白皮書以閱讀更多資訊。", + "hero-overlay-card-1-p-6": "請參閱 SimpleX 白皮書以閱讀更多資訊。", "hero-overlay-card-3-p-1": "Trail of Bits 是一家領先的安全和技術顧問公司,客戶包括大型科技公司、政府機構和主要的區塊鏈專案。", "simplex-network-overlay-card-1-p-1": "點對點通訊協定和應用程式有多種問題,使得它們不如 SimpleX 可靠、分析起來更複雜,而且易受幾種類型的攻擊。", "simplex-network-overlay-card-1-li-2": "SimpleX 的設計與大多數點對點網路不同,沒有任何類型的全局使用者標識符,即使是臨時標識符。SimpleX 只使用臨時的標識符對,提供更好的匿名性和元資料保護。", @@ -145,8 +145,8 @@ "simplex-unique-overlay-card-3-p-4": "傳送與接收的伺服器流量之間沒有共同的標識符或密文—如果有人觀察,即使 TLS 遭到破壞,也無法輕易確定誰與誰通訊。", "join-the-REDDIT-community": "加入Reddit社群", "privacy-matters-overlay-card-3-p-1": "人人都應關心自己通訊的隱私和安全;無害的對話可能會讓您陷入危險,即使您沒有什麼要隱瞞的。", - "privacy-matters-overlay-card-3-p-3": "普通人會因為他們在網路上分享的內容而被捕,即使是透過他們的「匿名」帳號,即使是在民主國家。", - "privacy-matters-overlay-card-2-p-1": "不久前我們觀察到大選被一家聲譽良好的顧問公司操縱,該公司利用我們的社交圖來扭曲我們對真實世界的看法,並操縱我們的選票。", + "privacy-matters-overlay-card-3-p-3": "普通人會因為他們在網路上分享的內容而被捕,即使是透過他們的「匿名」帳號,即使是在民主國家。", + "privacy-matters-overlay-card-2-p-1": "不久前我們觀察到大選被一家聲譽良好的顧問公司操縱,該公司利用我們的社交圖來扭曲我們對真實世界的看法,並操縱我們的選票。", "comparison-section-list-point-2": "基於DNS的位址", "comparison-section-list-point-1": "通常基於電話號碼,有時用戶名", "simplex-private-card-6-point-1": "許多通訊網路容易受到伺服器或網路供應商的中間人攻擊。", diff --git a/website/src/_includes/blog_previews/20221108.html b/website/src/_includes/blog_previews/20221108.html index f07180a17b..64174a0bcb 100644 --- a/website/src/_includes/blog_previews/20221108.html +++ b/website/src/_includes/blog_previews/20221108.html @@ -2,7 +2,7 @@

SimpleX Chat security has been assessed by - Trail of Bits, + Trail of Bits, 4 issues were identified, and 3 of them are fixed in v4.2.

diff --git a/website/src/_includes/blog_previews/20221206.html b/website/src/_includes/blog_previews/20221206.html index 55530cd301..df7e113250 100644 --- a/website/src/_includes/blog_previews/20221206.html +++ b/website/src/_includes/blog_previews/20221206.html @@ -1,9 +1,9 @@

SimpleX Chat November reviews:

-

Privacy Guides recommendations.

-

Review by Mike Kuketz.

-

The messenger matrix.

-

Supernova review and messenger ratings.

+

Privacy Guides recommendations.

+

Review by Mike Kuketz.

+

The messenger matrix.

+

Supernova review and messenger ratings.

v4.3 is released:

diff --git a/website/src/_includes/contact_page.html b/website/src/_includes/contact_page.html index 0616665a70..5fbd13c1f2 100644 --- a/website/src/_includes/contact_page.html +++ b/website/src/_includes/contact_page.html @@ -17,11 +17,11 @@

{{ "contact-hero-p-3" | i18n({}, lang ) | safe }}

- App Store - Google Play + App Store + Google Play - TestFlight - Android APK + TestFlight + Android APK
@@ -54,8 +54,8 @@

{{ "install-simplex-app" | i18n({}, lang ) | safe }}

- - + +
diff --git a/website/src/_includes/footer.html b/website/src/_includes/footer.html index 31c9d9e557..521e717eb5 100644 --- a/website/src/_includes/footer.html +++ b/website/src/_includes/footer.html @@ -8,9 +8,11 @@ `; @@ -674,7 +674,7 @@ if (nextMeta.description) html += `

${nextMeta.description}

`; html += '

'; html += `${appState.totalMinted} NFTs minted. `; - html += `Explorer ${linkSvg}`; + html += `Explorer ${linkSvg}`; html += '

'; if (withAttributes) { html += renderAttributes(nextMeta.attributes, appState.nextOpenedTraits); diff --git a/website/src/_includes/navbar.html b/website/src/_includes/navbar.html index ab56059931..0d4d9619e5 100644 --- a/website/src/_includes/navbar.html +++ b/website/src/_includes/navbar.html @@ -67,12 +67,12 @@ @@ -126,7 +126,7 @@ diff --git a/website/src/crowdfunding.html b/website/src/crowdfunding.html index cf12b7b31a..3b0b293f02 100644 --- a/website/src/crowdfunding.html +++ b/website/src/crowdfunding.html @@ -1149,7 +1149,7 @@ templateEngineOverride: njk
-

Get a stake
in SimpleX Chat

+

Own a part
of SimpleX Chat

We are building a network that people own.
We invite you to invest and become part of it.

diff --git a/website/src/directory.html b/website/src/directory.html index 08bb047aa5..679534d41a 100644 --- a/website/src/directory.html +++ b/website/src/directory.html @@ -267,7 +267,7 @@ active_directory: true

SimpleX Directory

Welcome to the selected users' communities that you can join via SimpleX Chat app.

-

SimpleX Directory is also available as a SimpleX chat bot.

+

SimpleX Directory is also available as a SimpleX chat bot.

Read about how to add your community.

diff --git a/website/src/fdroid.html b/website/src/fdroid.html index ff56b9cc95..67e4821e20 100644 --- a/website/src/fdroid.html +++ b/website/src/fdroid.html @@ -48,7 +48,7 @@ templateEngineOverride: njk

{{ "f-droid-org-repo" | i18n({}, lang ) | safe }}

- +

{{ "stable-versions-built-by-f-droid-org" | i18n({}, lang ) | safe }}

{{ "releases-to-this-repo-are-done-1-2-days-later" | i18n({}, lang ) | safe }}.

{{ "f-droid-page-f-droid-org-repo-section-text" | i18n({}, lang ) | safe }}

diff --git a/website/src/file.html b/website/src/file.html index 5eb545ef39..e5a25b446b 100644 --- a/website/src/file.html +++ b/website/src/file.html @@ -230,11 +230,11 @@ noEvents: true {{ "file-cta-subheading" | i18n({}, lang) | safe }}

- - + + - - + +
diff --git a/website/src/index.html b/website/src/index.html index 57580c1f4d..4b8491640c 100644 --- a/website/src/index.html +++ b/website/src/index.html @@ -166,7 +166,7 @@ active_home: true

{{ "index-hero-h1" | i18n({}, lang) | safe }}

{{ "index-hero-h2" | i18n({}, lang) | safe }}

{{ "index-hero-p1" | i18n({}, lang) | safe }}

-

{{ "index-hero-invest" | i18n({}, lang) | safe }} {{ "learn-more" | i18n({}, lang) }}.

+

{{ "index-hero-invest" | i18n({}, lang) | safe }} {{ "learn-more" | i18n({}, lang) }}.

- - + + - - + +
@@ -251,7 +251,7 @@ active_home: true

{{ "index-token-h2" | i18n({}, lang) }}

{{ "index-token-p1" | i18n({}, lang) }}

{{ "index-token-p2-cf" | i18n({}, lang) | safe }}

- {{ "index-token-cta-cf" | i18n({}, lang) | safe }} + {{ "index-token-cta-cf" | i18n({}, lang) | safe }} @@ -288,36 +288,36 @@ active_home: true
- - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + +

{{ "index-directory-h2" | i18n({}, lang) }}

diff --git a/website/src/js/channel-preview.jsc b/website/src/js/channel-preview.jsc index 2f88e885b9..b0a1696010 100644 --- a/website/src/js/channel-preview.jsc +++ b/website/src/js/channel-preview.jsc @@ -1037,11 +1037,11 @@ function renderInfoContent(container, data, channelLink, subscriberCount, showAp } } -var BADGE_APPLE = 'App Store'; -var BADGE_GOOGLE = 'Google Play'; -var BADGE_FDROID = 'F-Droid'; -var BADGE_APK = 'APK Download'; -var BADGE_TESTFLIGHT = 'TestFlight'; +var BADGE_APPLE = 'App Store'; +var BADGE_GOOGLE = 'Google Play'; +var BADGE_FDROID = 'F-Droid'; +var BADGE_APK = 'APK Download'; +var BADGE_TESTFLIGHT = 'TestFlight'; function renderAppBadges(container) { const title = document.createElement('p'); diff --git a/website/src/js/directory.jsc b/website/src/js/directory.jsc index 7d99e71dad..5ca3b7fbcf 100644 --- a/website/src/js/directory.jsc +++ b/website/src/js/directory.jsc @@ -338,6 +338,7 @@ function displayEntries(entries) { imgLinkElement.href = groupLinkUri; } imgLinkElement.target = "_blank"; + imgLinkElement.rel = "noopener"; imgLinkElement.title = `Join ${displayName}`; const imgElement = document.createElement('img'); diff --git a/website/src/js/simplex-lib.jsc b/website/src/js/simplex-lib.jsc index ffec278ca2..d05dd6a931 100644 --- a/website/src/js/simplex-lib.jsc +++ b/website/src/js/simplex-lib.jsc @@ -51,7 +51,7 @@ function isCurrentSite(uri) { } function targetBlank(uri) { - return isCurrentSite(uri) ? '' : ' target="_blank"' + return isCurrentSite(uri) ? '' : ' target="_blank" rel="noopener"' } function renderMarkdown(fts) { @@ -98,7 +98,7 @@ function renderMarkdown(fts) { case 'simplexLink': { const { showText, linkType, simplexUri, smpHosts } = format; const linkText = showText ? escapeHtml(showText) : getSimplexLinkDescr(linkType); - html += `${linkText} (${escapeHtml(viaHost(smpHosts))})`; + html += `${linkText} (${escapeHtml(viaHost(smpHosts))})`; break; } case 'command': diff --git a/website/src/livestream.html b/website/src/livestream.html index a0a6a2762e..33f6575101 100644 --- a/website/src/livestream.html +++ b/website/src/livestream.html @@ -79,7 +79,7 @@ templateEngineOverride: njk
diff --git a/website/src/messaging.html b/website/src/messaging.html index 994aa37b1b..33b01cfd9e 100644 --- a/website/src/messaging.html +++ b/website/src/messaging.html @@ -318,7 +318,7 @@ templateEngineOverride: njk
  • {{ "messengers-comparison-section-list-point-1" | i18n({}, lang ) | safe }}
  • {{ "messengers-comparison-section-list-point-2" | i18n({}, lang ) | safe }}
  • {{ "messengers-comparison-section-list-point-3" | i18n({}, lang ) | safe }}
  • -
  • {{ "messengers-comparison-section-list-point-4" | i18n({}, lang ) | safe }} — {{ "see-here" | i18n({}, lang ) | safe }}.
  • +
  • {{ "messengers-comparison-section-list-point-4" | i18n({}, lang ) | safe }} — {{ "see-here" | i18n({}, lang ) | safe }}.
  • {{ "messengers-comparison-section-list-point-5" | i18n({}, lang ) | safe }}
  • {{ "messengers-comparison-section-list-point-6" | i18n({}, lang ) | safe }}
  • From 000258224c3410a37fa03797a9a0dcb77d0980cd Mon Sep 17 00:00:00 2001 From: Narasimha-sc <166327228+Narasimha-sc@users.noreply.github.com> Date: Wed, 14 Oct 2026 21:14:55 +0000 Subject: [PATCH 12/21] desktop: fix crash in SimpleX address screen when there is no profile (#7679) * desktop: fix crash in SimpleX address screen when there is no profile * desktop: close SimpleX address screen when there is no profile * desktop: rerun address screen self-close when its modal is reused or the stack changes * desktop: close SimpleX address screen only when it is the top modal * plans: correct address no-profile crash triggers and fix description * plans: correct address no-profile crash fix description and verification * plans: tighten address no-profile crash wording * plans: add self-destruct trigger to address no-profile crash * plans: qualify self-destruct trigger on desktop * plans: drop desktop-only title from address no-profile crash * plans: no connected mobile, not no mobile * spec, product: document isLastModal and address screen without profile * plans, spec, product: plan sections, address row spec, GAP-08 and GAP-09 * spec, product, plans: fix GAP-08/09 wording, table placement, ModalManager line * spec, product: GAP-08/09 accuracy, gap count, clickable ModalManager location * product, plans: GAP-09 at the level the code supports; qualify picker auto-open * spec, product, plans: fix GAP-09 locations, stale line links, wording * spec, product, plans: update plain-text line numbers; GAP-09 read wording * plans: name the picker auto-open condition * spec, plans: wording; security note * plans: self-destruct did not crash on Android master; not a trigger * plans: self-destruct path possible but unconfirmed; Android recovers silently * spec, product, plans: drop GAP-09 and unrelated line refreshes * plan: Android self-destruct path confirmed in logcat * desktop: close Chat preferences screen when there is no profile, as the address screen does --- .../common/views/chatlist/UserPicker.kt | 22 ++++- .../simplex/common/views/helpers/ModalView.kt | 2 + .../product/views/user-profiles.md | 4 +- apps/multiplatform/spec/client/chat-list.md | 2 + apps/multiplatform/spec/client/navigation.md | 1 + ...-10-10-desktop-address-no-profile-crash.md | 99 +++++++++++++++++++ 6 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 plans/2026-10-10-desktop-address-no-profile-crash.md diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt index 652d072c9c..62fd634d29 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt @@ -220,14 +220,32 @@ fun UserPicker( UserPickerOptionRow( painterResource(MR.images.ic_qr_code), if (chatModel.userAddress.value != null) generalGetString(MR.strings.your_simplex_contact_address) else generalGetString(MR.strings.create_simplex_address), - showCustomModal { it, close -> UserAddressView(it, shareViaProfile = it.currentUser.value!!.addressShared, close = close) }, disabled = stopped + showCustomModal { it, close -> + val user = it.currentUser.value + if (user != null) { + UserAddressView(it, shareViaProfile = user.addressShared, close = close) + } else { + val modal = this + LaunchedEffect(modal, ModalManager.start.modalCount.value) { + if (ModalManager.start.isLastModal(modal)) close() + } + } + }, disabled = stopped ) UserPickerOptionRow( painterResource(MR.images.ic_toggle_on), stringResource(MR.strings.chat_preferences), click = if (stopped) null else ({ showCustomModal { m, close -> - PreferencesView(m, m.currentUser.value ?: return@showCustomModal, close) + val user = m.currentUser.value + if (user != null) { + PreferencesView(m, user, close) + } else { + val modal = this + LaunchedEffect(modal, ModalManager.start.modalCount.value) { + if (ModalManager.start.isLastModal(modal)) close() + } + } }() }), disabled = stopped diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ModalView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ModalView.kt index b13c321f70..f8e81c8ee2 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ModalView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ModalView.kt @@ -115,6 +115,8 @@ class ModalManager(private val placement: ModalPlacement? = null) { fun isLastModalOpen(id: ModalViewId): Boolean = modalViews.lastOrNull()?.id == id + fun isLastModal(data: ModalData): Boolean = modalViews.getOrNull(modalViews.size - toRemove.size - 1)?.data === data + fun showModal(settings: Boolean = false, showClose: Boolean = true, id: ModalViewId? = null, forceAnimated: Boolean = false, cardScreen: Boolean = false, endButtons: @Composable RowScope.() -> Unit = {}, content: @Composable ModalData.() -> Unit) { showCustomModal(id = id, forceAnimated = forceAnimated) { close -> ModalView(close, showClose = showClose, cardScreen = cardScreen, endButtons = endButtons, content = { content() }) diff --git a/apps/multiplatform/product/views/user-profiles.md b/apps/multiplatform/product/views/user-profiles.md index dfc37a5e8d..9f31c8285e 100644 --- a/apps/multiplatform/product/views/user-profiles.md +++ b/apps/multiplatform/product/views/user-profiles.md @@ -28,8 +28,8 @@ Overlay panel triggered from `ChatListView` toolbar: | Device picker row | `DevicePickerRow` showing local device and connected remote hosts (Desktop only); pill-shaped buttons with connect/disconnect actions | | Active user profile | `ProfilePreview` of current user (Desktop: single row; Android: full user list) | | User list | `UserPickerUsersSection` with all visible non-hidden profiles; tap to switch, long-press disabled | -| SimpleX address | Row to open `UserAddressView` (create or view address) | -| Chat preferences | Row to open `PreferencesView` | +| SimpleX address | Row to open `UserAddressView` (create or view address); with no active profile the screen closes itself (if another screen covers it, when the user goes back to it) | +| Chat preferences | Row to open `PreferencesView`; with no active profile the screen closes itself (if another screen covers it, when the user goes back to it) | | Chat profiles | Row to open `UserProfilesView` (or `CreateProfile` when no users exist on Desktop) | | Use from desktop/mobile | Android: "Use from desktop" (`ConnectDesktopView`); Desktop: "Link a mobile" / "Linked mobiles" (`ConnectMobileView`) | | Settings | Row to open `SettingsView` with `ColorModeSwitcher` trailing | diff --git a/apps/multiplatform/spec/client/chat-list.md b/apps/multiplatform/spec/client/chat-list.md index e34da9613d..aba6d3ad5f 100644 --- a/apps/multiplatform/spec/client/chat-list.md +++ b/apps/multiplatform/spec/client/chat-list.md @@ -279,6 +279,8 @@ fun UserPicker( - **Android:** Renders as a slide-up overlay panel on the chat list, triggered by tapping the user profile button in the toolbar. - **Desktop:** Rendered inline in the left column of `DesktopScreen`, always accessible. - Closes automatically when any `ModalManager.start` modal opens. +- The SimpleX address row opens `UserAddressView` in `ModalManager.start`. With no active user (`currentUser == null`) the modal draws nothing and closes itself when it is the top modal (`ModalManager.start.isLastModal`); if another screen covers it, it closes when the user goes back to it. +- The Chat preferences row closes its modal the same way when there is no active user. ### Content diff --git a/apps/multiplatform/spec/client/navigation.md b/apps/multiplatform/spec/client/navigation.md index d0340e8d23..7838abae69 100644 --- a/apps/multiplatform/spec/client/navigation.md +++ b/apps/multiplatform/spec/client/navigation.md @@ -256,6 +256,7 @@ Each `ModalManager` maintains a stack of `ModalViewHolder` objects with: | `closeModals` | Pop all modals from the stack | | `closeModalsExceptFirst` | Pop all but the bottom modal | | `hasModalsOpen()` | Check if any modals are on the stack | +| `isLastModal(data)` | Check if the modal with this `ModalData` is the top modal not being removed | | `showInView` | Render the current modal stack into the composable tree | ### Usage Pattern diff --git a/plans/2026-10-10-desktop-address-no-profile-crash.md b/plans/2026-10-10-desktop-address-no-profile-crash.md new file mode 100644 index 0000000000..c51eeaff04 --- /dev/null +++ b/plans/2026-10-10-desktop-address-no-profile-crash.md @@ -0,0 +1,99 @@ +# Crash in the SimpleX address screen when there is no profile + +## Problem + +A Windows user who uses the desktop app only through a linked mobile reported this crash: + +``` +java.lang.NullPointerException + at chat.simplex.common.views.chatlist.ComposableSingletons$UserPickerKt$lambda$-1608374373$1.invoke(UserPicker.kt:223) + at chat.simplex.common.views.chatlist.UserPickerKt$UserPicker$6$showCustomModal$1$1$1.invoke(UserPicker.kt:157) + at chat.simplex.common.views.helpers.ModalManager$showInView$4$1$1.invoke(ModalView.kt:218) +``` + +The "SimpleX address" screen crashes whenever it is shown while there is no active user. + +## Solution summary + +When there is no current user, the address modal closes itself instead of composing `UserAddressView`. It closes only while it is the top modal, so it does not close a different screen. The new `ModalManager.isLastModal` checks that. + +The "Chat preferences" row, which in paths 1 and 2 below left an invisible modal instead of crashing, closes itself the same way. + +## Technical design + +### Cause + +The "SimpleX address" row of the user picker opens `UserAddressView` with `shareViaProfile = it.currentUser.value!!.addressShared`. The modal content reads `currentUser`, so it recomposes whenever `currentUser` changes. It throws whenever it is composed while `currentUser` is null, that is, when there is no active user. A desktop that was only used with a linked mobile has no active user of its own. + +The screen can be shown in that state in three ways: + +1. **The mobile disconnects.** The screen was opened for the mobile's profile, and then the mobile disconnects. `switchUIRemoteHost(null)` sets `currentUser` to the local user, which is null, and the left-panel modals stay open. + - If the address screen is on top, it crashes when it recomposes. + - If another screen is on top of it, it crashes when the user goes back to it. + + `UserAddressView` closes itself when the user changes (`KeyChangeEffect` on the user), but with a null user it is never reached. +2. **The row is clicked with no active user.** With no active profile, the user picker still shows the "Create SimpleX address" row. With no profile and no mobile in use, the picker even opens by itself, at startup or when the state begins while no left-panel screen is open (`App.kt`, `desktopNoUserNoRemote`). Clicking the row crashes immediately. + + At startup, no profile and no connected mobile needs a mobile that was linked before; with no linked mobile, startup shows onboarding instead (`Core.kt`). + + No active user also follows from deleting the active profile when no other visible profile remains, including the only profile, and including the last visible profile of a connected mobile. `doRemoveUser` (`UserProfilesView.kt`) then calls `changeActiveUser_` with no user, which sets `currentUser` from `apiGetActiveUser`. After the deletion there is no active user, so it is null. + +3. **The self-destruct passcode is entered while the screen is open.** `deleteStorageAndRestart` (`LocalAuthView.kt`) calls `reinitChatController`. That calls `initChatController`, which sets `currentUser` from the new empty database to null (`Core.kt`). Only later does `deleteStorageAndRestart` create the new profile and call `closeAllModalsEverywhere`. + - On Android, the shared modal stack stays composed under the lock screen, so the address screen can recompose with a null user and throw. It throws only if a frame falls after `currentUser` becomes null and before onboarding replaces the main screen, so it does not happen on every run. Android's main loop catches exceptions (`SimplexApp.kt`). `GlobalExceptionsHandler` (`UI.android.kt`) then closes the top modal, recreates the activity and posts a crash alert, and the self-destruct flow hides all alerts at its end, so no crash is visible. + - On desktop, `initChatController` replaces the main screen with the splash screen shortly afterwards (`localUserCreated = null`), so the crash needs a frame to fall in that short window. This was not tested. + +The "Chat preferences" row has the same cause without the crash. It opens `PreferencesView(m, m.currentUser.value ?: return@showCustomModal, close)`, so with no active user the modal content returns early. `PreferencesView` closes itself when the user changes (`KeyChangeEffect` on the user, `Preferences.kt`), but with a null user it is never reached either. The modal stays on the left-panel stack with nothing drawn and no back button, in paths 1 and 2 above. + +### Self-closing modal + +Both rows use the same effect. `ModalManager.closeModal` closes the top modal that is not already being removed, whichever modal calls it. So the effect closes only when `ModalManager.isLastModal(data)` reports that this modal is that one. + +The effect is keyed on the modal and on the modal count of `ModalManager.start` (the left panel on desktop, the shared stack on Android). So it checks again when the count changes, or when its composition is reused for another modal, while this content is still composed. Other than the top modal, a modal stays composed only while it animates out (250 ms, longer if the animation is interrupted). For example, a quick second click on the row reuses the composition of the first modal, which is still animating out. + +A modal that is covered after an animation ends is disposed. It is composed afresh when it becomes the top again, for example when the user goes back to it, and the effect runs then. + +With the `isLastModal` check, the effect does not close a different screen, such as one opened just before or just after the address screen during an animation. This holds for stack changes on the main thread. As before, `ModalManager` is not synchronized with background calls such as `closeAllModalsEverywhere`. + +### Visible behaviour + +When there is no active user: +- Clicking either row now only hides the picker, as opening any modal does. The modal closes itself, and the picker is not reopened. +- After the mobile disconnects with the address or preferences screen open, the picker does not reopen by itself (observed in testing). When the state with no user and no mobile in use begins, the app checks for open left-panel modals, and the modal closes itself only after that check. Master does the same when any other left-panel screen is open. + +The change is in common code. On Android the user picker is not reachable without an active user, because deleting the last visible profile returns to onboarding. In the self-destruct case on Android, the screen can no longer throw, so `GlobalExceptionsHandler` does not have to recover. With an active user, behaviour does not change on any platform. + +### Security + +The change is UI-only and crosses no trust boundary: it reads local state and closes a local screen. It sends nothing and changes no stored data. + +### Rejected alternative + +An early `return@showCustomModal`, as the "Chat preferences" row did, would avoid the crash. But it would leave an invisible modal on the left-panel stack, with no back button. It stays until something closes the left-panel modals, such as a click on the centre panel. If a profile is created first, it comes back as the address screen. The "Chat preferences" row has this behaviour on master; this change gives it the self-closing effect too. + +## Implementation steps + +1. `views/helpers/ModalView.kt`: add `ModalManager.isLastModal(data)`, true when `data` belongs to the top modal that is not being removed. +2. `views/chatlist/UserPicker.kt`: in the "SimpleX address" row, compose `UserAddressView` only when `currentUser` is not null. Otherwise run `LaunchedEffect(modal, ModalManager.start.modalCount.value)`, which closes the modal when `isLastModal(modal)` is true. Do the same in the "Chat preferences" row for `PreferencesView`. +3. Documentation, under `apps/multiplatform/`: + - `spec/client/navigation.md`: list `isLastModal` among the `ModalManager` methods. + - `spec/client/chat-list.md`, UserPicker section: describe the address and preferences rows closing themselves with no active user. + - `product/views/user-profiles.md`: describe both rows with no active profile. + +## Verification + +Desktop AppImages from master and from this branch, each with a fresh database, unless stated otherwise. The v7.1.0-beta.6 CLI (core v7.1.0.10) acts as the linked mobile (`/connect remote ctrl`). "Leftover" is checked by creating a profile afterwards: a leftover modal reappears as the address screen when onboarding finishes. + +- master (at the merge base), case 1: NPE at `UserPicker.kt:223`. +- master (at the merge base), case 2: NPE at `UserPicker.kt:223` with the reported stack (`showCustomModal` 157, `ModalView.kt:218`). +- This branch, case 1 with the address screen on top: no crash, and the screen closes. +- This branch, case 1 with the "Address or 1-time link?" screen (opened from "SimpleX address or 1-time link?") on top: no crash. That screen stays open after the disconnect, as on master. Going back closes the address screen and shows the chat list. +- This branch, case 2, double click (seven runs, 8 to 40 ms apart): no crash, and no leftover. With `LaunchedEffect(Unit)` the address screen reappeared after onboarding in 2 of 3 runs at 30 ms. +- This branch, case 2, "Settings" then "Create SimpleX address" 100 ms apart: no crash, and Settings stays open. With the close keyed on the modal count but without the `isLastModal` check, Settings was closed too. +- This branch, with a local profile: the address screen opens and closes normally. When the mobile disconnects with its address screen open, the screen closes through `UserAddressView`'s own user-change effect, and the app returns to the local profile. +- "Chat preferences", desktop with no profile, the same CLI linked as the mobile: + - Preferences open, then the mobile disconnects. Before this change the screen disappeared, and after a profile was created it came back as "Your preferences" over the chat. With this change it does not come back, and there is no crash. + - Row clicked with no profile and no mobile: nothing opens, and nothing comes back after a profile is created. + - With a profile: the screen opens and closes normally. +- Android (Waydroid: Android 13 with arm64 translation), debug APKs of master and of this branch, fresh app data, case 3 with the address screen opened from the user picker: + - master, two runs: in one, logcat shows `App crashed, thread name: main` with the NPE at `UserPicker.kt:223`; in the other, no exception. No crash is visible in either, as in an earlier manual test. + - This branch: one run completed to the new profile with no exception in logcat. As master did not throw on every run, this checks for no new failure rather than proving the fix. An earlier run hung during self-destruct, with the main thread, a coroutine thread and a Haskell worker all waiting for the native allocator lock under arm64 translation. From 23ca3ea42fa03a064243379a8b969f19b16e0893 Mon Sep 17 00:00:00 2001 From: Narasimha-sc <166327228+Narasimha-sc@users.noreply.github.com> Date: Thu, 15 Oct 2026 04:37:37 +0000 Subject: [PATCH 13/21] desktop: hide SimpleX address and chat preferences rows with no profile and no mobile in use (#7717) --- .../common/views/chatlist/UserPicker.kt | 56 ++++++++++--------- .../product/views/user-profiles.md | 4 +- apps/multiplatform/spec/client/chat-list.md | 1 + ...sktop-hide-profile-rows-without-profile.md | 49 ++++++++++++++++ 4 files changed, 81 insertions(+), 29 deletions(-) create mode 100644 plans/2026-10-14-desktop-hide-profile-rows-without-profile.md diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt index 62fd634d29..ea567d7da6 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt @@ -217,41 +217,43 @@ fun UserPicker( @Composable fun SecondSection() { - UserPickerOptionRow( - painterResource(MR.images.ic_qr_code), - if (chatModel.userAddress.value != null) generalGetString(MR.strings.your_simplex_contact_address) else generalGetString(MR.strings.create_simplex_address), - showCustomModal { it, close -> - val user = it.currentUser.value - if (user != null) { - UserAddressView(it, shareViaProfile = user.addressShared, close = close) - } else { - val modal = this - LaunchedEffect(modal, ModalManager.start.modalCount.value) { - if (ModalManager.start.isLastModal(modal)) close() - } - } - }, disabled = stopped - ) - UserPickerOptionRow( - painterResource(MR.images.ic_toggle_on), - stringResource(MR.strings.chat_preferences), - click = if (stopped) null else ({ - showCustomModal { m, close -> - val user = m.currentUser.value + if (!chatModel.desktopNoUserNoRemote) { + UserPickerOptionRow( + painterResource(MR.images.ic_qr_code), + if (chatModel.userAddress.value != null) generalGetString(MR.strings.your_simplex_contact_address) else generalGetString(MR.strings.create_simplex_address), + showCustomModal { it, close -> + val user = it.currentUser.value if (user != null) { - PreferencesView(m, user, close) + UserAddressView(it, shareViaProfile = user.addressShared, close = close) } else { val modal = this LaunchedEffect(modal, ModalManager.start.modalCount.value) { if (ModalManager.start.isLastModal(modal)) close() } } - }() - }), - disabled = stopped - ) + }, disabled = stopped + ) + UserPickerOptionRow( + painterResource(MR.images.ic_toggle_on), + stringResource(MR.strings.chat_preferences), + click = if (stopped) null else ({ + showCustomModal { m, close -> + val user = m.currentUser.value + if (user != null) { + PreferencesView(m, user, close) + } else { + val modal = this + LaunchedEffect(modal, ModalManager.start.modalCount.value) { + if (ModalManager.start.isLastModal(modal)) close() + } + } + }() + }), + disabled = stopped + ) + } if (appPlatform.isDesktop) { - Divider(Modifier.padding(DEFAULT_PADDING)) + if (!chatModel.desktopNoUserNoRemote) Divider(Modifier.padding(DEFAULT_PADDING)) val inactiveUsers = users.filter { !it.user.activeUser } diff --git a/apps/multiplatform/product/views/user-profiles.md b/apps/multiplatform/product/views/user-profiles.md index 9f31c8285e..38a0e13874 100644 --- a/apps/multiplatform/product/views/user-profiles.md +++ b/apps/multiplatform/product/views/user-profiles.md @@ -28,8 +28,8 @@ Overlay panel triggered from `ChatListView` toolbar: | Device picker row | `DevicePickerRow` showing local device and connected remote hosts (Desktop only); pill-shaped buttons with connect/disconnect actions | | Active user profile | `ProfilePreview` of current user (Desktop: single row; Android: full user list) | | User list | `UserPickerUsersSection` with all visible non-hidden profiles; tap to switch, long-press disabled | -| SimpleX address | Row to open `UserAddressView` (create or view address); with no active profile the screen closes itself (if another screen covers it, when the user goes back to it) | -| Chat preferences | Row to open `PreferencesView`; with no active profile the screen closes itself (if another screen covers it, when the user goes back to it) | +| SimpleX address | Row to open `UserAddressView` (create or view address); hidden on Desktop when there is no active profile and no mobile is in use; a screen shown with no active profile closes itself (if another screen covers it, when the user goes back to it) | +| Chat preferences | Row to open `PreferencesView`; hidden on Desktop when there is no active profile and no mobile is in use; a screen shown with no active profile closes itself (if another screen covers it, when the user goes back to it) | | Chat profiles | Row to open `UserProfilesView` (or `CreateProfile` when no users exist on Desktop) | | Use from desktop/mobile | Android: "Use from desktop" (`ConnectDesktopView`); Desktop: "Link a mobile" / "Linked mobiles" (`ConnectMobileView`) | | Settings | Row to open `SettingsView` with `ColorModeSwitcher` trailing | diff --git a/apps/multiplatform/spec/client/chat-list.md b/apps/multiplatform/spec/client/chat-list.md index aba6d3ad5f..5a63ad92bc 100644 --- a/apps/multiplatform/spec/client/chat-list.md +++ b/apps/multiplatform/spec/client/chat-list.md @@ -279,6 +279,7 @@ fun UserPicker( - **Android:** Renders as a slide-up overlay panel on the chat list, triggered by tapping the user profile button in the toolbar. - **Desktop:** Rendered inline in the left column of `DesktopScreen`, always accessible. - Closes automatically when any `ModalManager.start` modal opens. +- On Desktop, when there is no active user and no mobile is in use (`desktopNoUserNoRemote`), the SimpleX address and Chat preferences rows and the divider below them are hidden. - The SimpleX address row opens `UserAddressView` in `ModalManager.start`. With no active user (`currentUser == null`) the modal draws nothing and closes itself when it is the top modal (`ModalManager.start.isLastModal`); if another screen covers it, it closes when the user goes back to it. - The Chat preferences row closes its modal the same way when there is no active user. diff --git a/plans/2026-10-14-desktop-hide-profile-rows-without-profile.md b/plans/2026-10-14-desktop-hide-profile-rows-without-profile.md new file mode 100644 index 0000000000..e906a6f275 --- /dev/null +++ b/plans/2026-10-14-desktop-hide-profile-rows-without-profile.md @@ -0,0 +1,49 @@ +# Hide the address and preferences rows when there is no profile and no mobile in use + +## Problem + +On a desktop with no active profile and no mobile in use, the user picker shows "Create SimpleX address" as its first option row, then "Chat preferences" and, below a divider, "Create chat profile". The picker opens by itself in this state when no left-panel screen is open (`App.kt`). Neither of the first two rows can work without a profile: + +- "Create SimpleX address" crashed with a NullPointerException at `UserPicker.kt:223` (`:216` in v6.1, `:222` in v6.2–v6.4) before #7679. GitHub reports from using the row: #5068, #5476, #6194, #7067 (Linux; comments on #5068 and #6194 report the same on Windows 11). +- "Chat preferences" left an invisible modal with no back button. +- With #7679 both screens close themselves instead, so a click only hides the picker and nothing opens. + +Users have told the team that the crash stops them from creating a profile. On v7.0.4 a profile can still be created from "Create chat profile" after the crash, but the first row the user tries is one that cannot work. + +The same crash also happens when a mobile disconnects while its address screen is open (#6452, macOS: the same stack, reported alongside phone disconnects). That path is fixed by #7679, not by this change. + +## Solution summary + +Hide the "SimpleX address" and "Chat preferences" rows when the desktop has no active user and no mobile in use (`chatModel.desktopNoUserNoRemote`). The divider below them is hidden with them. This is the state in which the picker already shows "Create chat profile" instead of "Your chat profiles". While a connected mobile is in use, both rows stay and act on the mobile's profile; if the mobile has no active profile, the screens close themselves (#7679). + +Before the new user picker (#4796), `SettingsView` showed both rows only inside `if (profile != null)`, with "Create chat profile" in the `else if (chatModel.localUserCreated.value == false)` branch. + +## Technical design + +`SecondSection` in `UserPicker.kt` emits both rows inside `if (!chatModel.desktopNoUserNoRemote)`. The desktop divider below them belongs to the same group and is drawn under the same condition. The composable getter reads `currentUser` and `currentRemoteHost` state, so the picker updates as soon as a profile is created, a mobile is connected, selected or disconnected, or the desktop is switched to "This device" with no profile. On Android `desktopNoUserNoRemote` is always false, so nothing changes there. + +The self-closing modals from #7679 stay. Hiding the rows only removes the entry point in that state. A screen shown with no active user in any other state still relies on #7679's handling; #7679's plan lists the paths. For example: +- a screen opened while there was a user, which then loses it: a mobile disconnects, or the self-destruct passcode is entered, with the address or preferences screen open; +- a mobile in use with no active profile (its last visible profile deleted): the rows are shown, and the screens close themselves. + +### Security + +UI-only: it hides two rows and the divider below them, based on local state. No data or trust boundary is involved. + +## Implementation steps + +1. `views/chatlist/UserPicker.kt`: in `SecondSection`, wrap the "SimpleX address" and "Chat preferences" rows in `if (!chatModel.desktopNoUserNoRemote)`, and draw the desktop divider below them under the same condition. +2. Documentation, under `apps/multiplatform/`: + - `spec/client/chat-list.md`, UserPicker section: when the two rows are hidden. + - `product/views/user-profiles.md`: when both rows are hidden. + +## Verification + +Desktop AppImage of this branch, fresh database, the v7.1.0-beta.6 CLI as the linked mobile; Android debug APK of this branch on Waydroid. + +- Mobile connected, no desktop profile: both rows are shown for the mobile's profile. "Create SimpleX address" creates the address on the mobile (`/show_address` on the CLI returns it). +- Mobile connected, switched to "This device" (no profile): both rows are hidden and "Create chat profile" is shown. Switching back to the mobile shows them again. +- Address screen open, then the mobile disconnects: the screen closes (#7679), no crash, and the picker then shows no address or preferences rows. +- "Create chat profile" from that state: the profile is created, both rows appear and open their screens. +- No profile, mobile linked but not in use: the device row, then "Create chat profile", with no divider. After deleting the linked mobile ("Linked mobiles", right-click, "Delete"): "Create chat profile" is the first row. The spacing matches the gap between rows. After creating a profile, both rows and the divider are back. +- Android, with a profile: both rows are shown and open their screens; no crash in logcat. From f5b13f608f3d56f59d772d3c3e1f264fbe2be280 Mon Sep 17 00:00:00 2001 From: Simplex Operations Date: Thu, 15 Oct 2026 06:18:30 +0000 Subject: [PATCH 14/21] packages: release nodejs 7.1.0-beta.6.3 (#7720) --- packages/simplex-chat-nodejs/package.json | 2 +- packages/simplex-chat-nodejs/src/release.ts | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/simplex-chat-nodejs/package.json b/packages/simplex-chat-nodejs/package.json index ca62e57fa6..67eefdee4a 100644 --- a/packages/simplex-chat-nodejs/package.json +++ b/packages/simplex-chat-nodejs/package.json @@ -1,6 +1,6 @@ { "name": "simplex-chat", - "version": "7.1.0-beta.6.2", + "version": "7.1.0-beta.6.3", "main": "dist/index.js", "types": "dist/index.d.ts", "gypfile": false, diff --git a/packages/simplex-chat-nodejs/src/release.ts b/packages/simplex-chat-nodejs/src/release.ts index ccd436e250..b56435e210 100644 --- a/packages/simplex-chat-nodejs/src/release.ts +++ b/packages/simplex-chat-nodejs/src/release.ts @@ -2,7 +2,7 @@ export const LIBS_RELEASE = "core-7.1.0.11" -export const ADDON_RELEASE: string | undefined = undefined +export const ADDON_RELEASE: string | undefined = "nodejs-addon-308c0960c707" export const SHA256: {readonly [asset: string]: string} = { "simplex-chat-libs-linux-aarch64.zip": "d66aa1b30b9fa20225dad8ef75a9c605961e1c0adde2ba0283e2fcac4e15d260", @@ -11,4 +11,9 @@ export const SHA256: {readonly [asset: string]: string} = { "simplex-chat-libs-macos-aarch64.zip": "aa74608b149fff7508a47eaac4284dcc6d189c6ed8b7c18428df9158bc176a61", "simplex-chat-libs-macos-x86_64.zip": "a0e21457e1077be7f71b46c1c27452945b2cee2b6c50efb0004412ce5b7042ec", "simplex-chat-libs-windows-x86_64.zip": "c01dad574daa101a1cbfd31ddbf2b3d0d986a4a42f97d789d2f878b2050b2b4b", + "simplex-chat-nodejs-linux-aarch64.node": "3636a82c56c48229e0326fa03d6ceb5c16dd3afbcc1e86337783dc53a7328d67", + "simplex-chat-nodejs-linux-x86_64.node": "55f3479b03fdb57591179ed730da744f1aa362e7529df55221b60028415746e9", + "simplex-chat-nodejs-macos-aarch64.node": "a3472872fb0816f435f52ee5471088e6545ebffb95e89fbf8e1ab82275f2d56e", + "simplex-chat-nodejs-macos-x86_64.node": "290901f7f227019a8cee3acc832541c930c4926fc08110df25169810a9002f5a", + "simplex-chat-nodejs-windows-x86_64.node": "ec689373b4f0fef73253a8328f60b8e902a45ea67e756d42a1f759b44dfd7339", } From e8c98e82316cc9444fd24ebfa1bd388d9d8877f1 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Thu, 15 Oct 2026 08:41:57 +0100 Subject: [PATCH 15/21] core, ios: fix notifications for forwarded and channel messages (#7706) * plan: add notification flag for forwarded messages * core, ui: fix notifications for forwarded and relay messages * improvements * only send duplicate message event in NSE * query plans --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --- apps/ios/SimpleX NSE/NSEAPITypes.swift | 3 + .../ios/SimpleX NSE/NotificationService.swift | 20 +- apps/ios/SimpleXChat/ChatTypes.swift | 11 +- bots/src/API/Docs/Events.hs | 1 + .../2026-10-13-forwarded-notification-flag.md | 330 ++++++++++++++++++ simplex-chat.cabal | 2 + src/Simplex/Chat/Controller.hs | 1 + src/Simplex/Chat/Delivery.hs | 1 + src/Simplex/Chat/Library/Commands.hs | 2 +- src/Simplex/Chat/Library/Internal.hs | 43 ++- src/Simplex/Chat/Library/Subscriber.hs | 17 +- src/Simplex/Chat/Protocol.hs | 5 + src/Simplex/Chat/Store/Delivery.hs | 18 +- src/Simplex/Chat/Store/Postgres/Migrations.hs | 4 +- .../M20261012_delivery_job_notification.hs | 19 + src/Simplex/Chat/Store/SQLite/Migrations.hs | 4 +- .../M20261012_delivery_job_notification.hs | 18 + .../SQLite/Migrations/agent_query_plans.txt | 13 +- .../SQLite/Migrations/chat_query_plans.txt | 10 +- .../Store/SQLite/Migrations/chat_schema.sql | 3 +- src/Simplex/Chat/View.hs | 1 + tests/ChatTests/Groups.hs | 208 ++++++++++- 22 files changed, 674 insertions(+), 60 deletions(-) create mode 100644 plans/2026-10-13-forwarded-notification-flag.md create mode 100644 src/Simplex/Chat/Store/Postgres/Migrations/M20261012_delivery_job_notification.hs create mode 100644 src/Simplex/Chat/Store/SQLite/Migrations/M20261012_delivery_job_notification.hs diff --git a/apps/ios/SimpleX NSE/NSEAPITypes.swift b/apps/ios/SimpleX NSE/NSEAPITypes.swift index 35a838fff9..28073ef342 100644 --- a/apps/ios/SimpleX NSE/NSEAPITypes.swift +++ b/apps/ios/SimpleX NSE/NSEAPITypes.swift @@ -89,6 +89,7 @@ enum NSEChatEvent: Decodable, ChatAPIResult { case contactConnected(user: UserRef, contact: Contact, userCustomProfile: Profile?) case receivedContactRequest(user: UserRef, contactRequest: UserContactRequest) case newChatItems(user: UserRef, chatItems: [AChatItem]) + case duplicateGroupMessage(user: UserRef, member: GroupMember) case rcvFileSndCancelled(user: UserRef, chatItem: AChatItem, rcvFileTransfer: RcvFileTransfer) case sndFileComplete(user: UserRef, chatItem: AChatItem, sndFileTransfer: SndFileTransfer) case sndFileRcvCancelled(user: UserRef, chatItem_: AChatItem?, sndFileTransfer: SndFileTransfer) @@ -101,6 +102,7 @@ enum NSEChatEvent: Decodable, ChatAPIResult { case .contactConnected: "contactConnected" case .receivedContactRequest: "receivedContactRequest" case .newChatItems: "newChatItems" + case .duplicateGroupMessage: "duplicateGroupMessage" case .rcvFileSndCancelled: "rcvFileSndCancelled" case .sndFileComplete: "sndFileComplete" case .sndFileRcvCancelled: "sndFileRcvCancelled" @@ -117,6 +119,7 @@ enum NSEChatEvent: Decodable, ChatAPIResult { case let .newChatItems(u, chatItems): let itemsString = chatItems.map { chatItem in String(describing: chatItem) }.joined(separator: "\n") return withUser(u, itemsString) + case let .duplicateGroupMessage(u, member): return withUser(u, String(describing: member)) case let .rcvFileSndCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem)) case let .sndFileComplete(u, chatItem, _): return withUser(u, String(describing: chatItem)) case let .sndFileRcvCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem)) diff --git a/apps/ios/SimpleX NSE/NotificationService.swift b/apps/ios/SimpleX NSE/NotificationService.swift index 25df063f82..38e1b604be 100644 --- a/apps/ios/SimpleX NSE/NotificationService.swift +++ b/apps/ios/SimpleX NSE/NotificationService.swift @@ -73,6 +73,16 @@ public enum NSENotificationData { default: nil } } + + func replaces(_ bestAttempt: NSENotificationData) -> Bool { + switch (self, bestAttempt) { + case (.noNtf, .connectionEvent): true + case (.noNtf, _): false + case (.callInvitation, _): true + case (_, .callInvitation): false + default: true + } + } } // Once the last thread in the process completes processing chat controller is suspended, and the database is closed, to avoid @@ -573,9 +583,9 @@ class NotificationService: UNNotificationServiceExtension { // A better logic could be to check whether we have a call in the best attempt while processing .msgInfo marker above. // If the best attempt is a call, and its marker is received, and the call is recent (e.g., the last 30 seconds), it would deliver at once, // instead of requesting further messages. - if ntfEntity.msgBestAttemptNtf.callInvitation == nil || ntf.callInvitation != nil { + if let bestAttempt = notificationEntities[id]?.msgBestAttemptNtf, ntf.replaces(bestAttempt) { notificationEntities[id]?.msgBestAttemptNtf = ntf - } // otherwise keep call as best attempt + } return false } else { // We should not get to this branch, as notifications are not delivered for muted users. @@ -1022,8 +1032,8 @@ func receivedMsgNtf(_ res: NSEChatEvent) async -> (String, NSENotificationData)? cItem = autoReceiveFile(file) ?? cItem } let ntf: NSENotificationData = (cInfo.ntfsEnabled(chatItem: cItem) && cItem.showNotification) ? .messageReceived(user, cInfo, cItem) : .noNtf - let chatIdOrMemberId = if case let .groupRcv(groupMember) = chatItem.chatItem.chatDir { - groupMember.id + let chatIdOrMemberId = if case let .group(groupInfo, _) = cInfo, let memberId = cItem.meta.forwardedByMember ?? cItem.chatDir.groupMemberId { + groupMemberEntityId(groupInfo.groupId, memberId) } else { chatItem.chatInfo.id } @@ -1031,6 +1041,8 @@ func receivedMsgNtf(_ res: NSEChatEvent) async -> (String, NSENotificationData)? } else { return nil } + case let .duplicateGroupMessage(_, member): + return (member.id, .noNtf) case let .rcvFileSndCancelled(_, aChatItem, _): cleanupFile(aChatItem) return nil diff --git a/apps/ios/SimpleXChat/ChatTypes.swift b/apps/ios/SimpleXChat/ChatTypes.swift index a8c5de3672..186d7dab64 100644 --- a/apps/ios/SimpleXChat/ChatTypes.swift +++ b/apps/ios/SimpleXChat/ChatTypes.swift @@ -3255,7 +3255,7 @@ public struct GroupMember: Identifiable, Decodable, Hashable { public var relayLink: String? public var memberVerifiedCode: SecurityCode? - public var id: String { "#\(groupId) @\(groupMemberId)" } + public var id: String { groupMemberEntityId(groupId, groupMemberId) } public var ready: Bool { get { activeConn?.connStatus == .ready } } public var sndReady: Bool { get { ready || activeConn?.connStatus == .sndReady } } public var sendMsgEnabled: Bool { get { @@ -3444,6 +3444,10 @@ public struct GroupMember: Identifiable, Decodable, Hashable { ) } +public func groupMemberEntityId(_ groupId: Int64, _ groupMemberId: Int64) -> String { + "#\(groupId) @\(groupMemberId)" +} + public struct GroupSupportChat: Codable, Hashable { public var chatTs: Date public var unread: Int @@ -4217,6 +4221,10 @@ public enum CIDirection: Decodable, Hashable { } } + public var groupMemberId: Int64? { + if case let .groupRcv(groupMember) = self { groupMember.groupMemberId } else { nil } + } + public func sameDirection(_ dir: CIDirection) -> Bool { switch (self, dir) { case let (.groupRcv(m1), .groupRcv(m2)): m1.groupMemberId == m2.groupMemberId @@ -4242,6 +4250,7 @@ public struct CIMeta: Decodable, Hashable { public var userMention: Bool public var deletable: Bool public var editable: Bool + public var forwardedByMember: Int64? public var showGroupAsSender: Bool public var msgVerified: MsgVerified? diff --git a/bots/src/API/Docs/Events.hs b/bots/src/API/Docs/Events.hs index 48f307f6e4..e310fd54ef 100644 --- a/bots/src/API/Docs/Events.hs +++ b/bots/src/API/Docs/Events.hs @@ -203,6 +203,7 @@ undocumentedEvents = "CEvtContactRequestRejected", "CEvtContactSwitch", "CEvtCustomChatEvent", + "CEvtDuplicateGroupMessage", "CEvtGroupMemberRatchetSync", "CEvtGroupMemberSwitch", "CEvtServiceSubStatus", diff --git a/plans/2026-10-13-forwarded-notification-flag.md b/plans/2026-10-13-forwarded-notification-flag.md new file mode 100644 index 0000000000..a1962a6e0f --- /dev/null +++ b/plans/2026-10-13-forwarded-notification-flag.md @@ -0,0 +1,330 @@ +# Notification flag of forwarded messages + +Date: 2026-10-13. + +A message forwarded by a chat relay or by a forwarding group host is sent with the SMP notification flag when its original event has the flag by the rule of `hasNotification`. + +References are to `/code/simplex-chat-4`, branch `master`, commit `2f4a443dd`. Haskell paths without a directory are in `src/Simplex/Chat/`. `SMQ/` is `dist-newstyle/src/simplexmq-f058c38b0eecb6c0/src/Simplex/Messaging/`. `NSE` is `apps/ios/SimpleX NSE/NotificationService.swift`. + +## 1. Present behaviour +Path of a forwarded message: +``` +received Library/Subscriber.hs:1014 MSG of a member connection; the flag of the received SMP message is ignored + Library/Subscriber.hs:1069-1144 processEvent: a NewMessageDeliveryTask is returned for each event to forward +task Library/Subscriber.hs:1022 created when the user is the relay of a channel, or an admin or owner of a group (:3800), and the author is not blocked by an admin + Library/Subscriber.hs:1158-1176 createDeliveryTasks; createMsgDeliveryTask (Store/Delivery.hs:78) is called at :1165 and :1172 only + Store/Delivery.hs:137-170 the task is read with its message: msg_body is decoded to verifiedMsg, the original event +job Library/Subscriber.hs:4233-4240 DJDeliveryJob: accepted tasks of one scope (Store/Delivery.hs:178) are batched into one job (Messages/Batch.hs:84) + Library/Subscriber.hs:4246-4251 DJRelayRemoved: one job for one task + Library/Commands.hs:3256-3265 leaveChannelRelay: one job with the x.grp.leave of the relay + Store/Delivery.hs:285-328 the job is read as scope, sender ids, body and cursor; its events are stored only as the encoded body +sent Library/Subscriber.hs:4379-4423 channel: for each recipient, overflow profile batches, then the body; MsgFlags False (:4416) + Library/Subscriber.hs:4424-4487 channel support scope and regular group: deliver; MsgFlags False (:4483) +``` +Types (`Delivery.hs`): +``` +DeliveryTaskContext :48 + jobScope :: DeliveryJobScope + sentAsGroup :: ShowGroupAsSender +DeliveryJobScope :54 + DJSGroup {jobSpec :: DeliveryJobSpec} + DJSMemberSupport {supportGMId :: GroupMemberId} +DeliveryJobSpec :60 + DJDeliveryJob {includePending :: Bool} + DJRelayRemoved +NewMessageDeliveryTask :122 + messageId :: MessageId + taskContext :: DeliveryTaskContext +MessageDeliveryTask :128 + taskId :: Int64 + jobScope :: DeliveryJobScope + senderGMId :: GroupMemberId + fwdSender :: FwdSender + brokerTs :: UTCTime + verifiedMsg :: VerifiedMsg 'Json +MessageDeliveryJob :161 + jobId :: Int64 + jobScope :: DeliveryJobScope + senderGMIds :: [GroupMemberId] + body :: ByteString + cursorGMId_ :: Maybe GroupMemberId +``` +Tables (`Store/SQLite/Migrations/M20250813_delivery_tasks.hs:54, :103`, `M20260529_delivery_job_senders.hs:17`; the same columns in Postgres): +``` +both tables + group_id + worker_scope + job_scope_spec_tag + job_scope_include_pending + job_scope_support_gm_id + failed + created_at + updated_at +delivery_tasks + delivery_task_id + sender_group_member_id + message_id -- messages.chat_msg_event: the tag; messages.msg_body: the original event + message_from_channel + task_status + task_err_reason +delivery_jobs + delivery_job_id + sender_group_member_ids + body + cursor_group_member_id + job_status + job_err_reason +``` +The flag, from the forwarder to the notification: +``` +forwarder agent SMQ/Agent.hs:2186-2203, 2563 one flag for each MsgReq, stored with the sent message; a body shared by VRRef is sent with the flag of each request + SMQ/Agent.hs:2637-2644 the flag is a parameter of SEND +SMP server SMQ/Server.hs:2038-2039 a notification is stored for a flagged message to a queue with notifier credentials (:2131-2136), and for such a message only + SMQ/Server.hs:361-414 the stored notification is sent as NMSG to the subscriber of the notifier, the notification server +notification server SMQ/Notifications/Server.hs:545-553 NMSG: a push notification with the last notifications of the device token is sent to the push provider (:693-702) +recipient Library/Subscriber.hs:890 the notification subscription of a member connection is created unless the chat is muted, MFNone (Library/Commands.hs:1922-1929; SMQ/Agent.hs:3345-3361) + SMQ/Agent.hs:2093-2144, 3741 the agent gets the message of the notified queue and passes MSG with the flag; forwarded content is opaque to the agent + Library/Subscriber.hs:1014 the core ignores the flag; the item is created from the forwarded event (:4008-4064) +``` +iOS notification extension, for a forwarded message: +``` +entity key NSE:489-498 id of the notified connection: "# @" of the relay or the host (apps/ios/SimpleXChat/ChatTypes.swift:3624-3632, :3258) +item key NSE:1025-1029 the member of the item, its author, for groupRcv; "#" for channelRcv +forwarded item NSE:173-187 the keys are different: the notification of the item is added to droppedNotifications, outside the entity of the notified connection +shown NSE:1196-1216 the default of the connection: MFAll: title "# :", text "message received" (apps/ios/SimpleXChat/Notifications.swift:102); MFMentions: empty content +``` +- With the flag alone the push notification is sent, and the message is received by the core of the NSE: the item is created. The shown notification is the default one. The NSE change of section 7 is required for a notification with the message text. + +Regular groups, the same message sent to a member by its author and by the host: +``` +author Library/Internal.hs:2847-2865 member with a ready connection: sent to the member; otherwise forwarded by the host (MSAForwarded) or stored as pending +host Library/Subscriber.hs:4447-4458 job recipients: members whose relation with the sender is MRIntroduced +both sent while the author is connected to the member and the relation at the host is MRIntroduced + the second copy is a duplicate: one item; the recipient sends x.grp.mem.con (Library/Internal.hs:2939-2945, 2958-2968), the host stops forwarding (Library/Subscriber.hs:3724-3729) +rule each copy of x.msg.new is flagged: two notifications for one item in that interval, one otherwise +``` + +## 2. Rule +``` +forwarded message flag = hasNotification of the tag of its original event (Protocol.hs:1338) + the flag of the received SMP message is unused +SMP message notification = True when the flag of any forwarded message of its body is True, as for sent events (Library/Internal.hs:2248, 2748) +job one value for the body, computed when the job is created; the same for every recipient, scope and cursor page +channel pieces body (id 0) and extBody, profiles with the body (id 1): the job value + overflow profile batches (id 2 and above): False; their elements are x.grp.mem.new of the relay (Library/Subscriber.hs:4277) +``` +Events (handlers at `Library/Subscriber.hs:1080-1132`; forwarded events are those of `isForwardedGroupMsg`, `Protocol.hs:528`): +``` +event flag +x.msg.new True every content message, with or without a file +x.grp.del True forwarded by the relay-removed job (:1118) +x.msg.file.descr False +x.msg.update False +x.msg.del False +x.msg.react False +x.file.cancel False +x.info False +x.grp.relay.new False +x.grp.mem.new False +x.grp.mem.role False +x.grp.mem.restrict False +x.grp.mem.del False also the relay-removed job of a removed relay or host (:3746) +x.grp.leave False also the job of a relay that leaves (Library/Commands.hs:3263) +x.grp.info False +x.grp.prefs False +x.grp.roster False +x.file True handled without a delivery task (:1096) +x.grp.mem.fwd True handled without a delivery task (:1110) +x.grp.inv True handled without a delivery task (:1124) +x.call.inv True handled without a delivery task (:1132) +x.contact True handled without a delivery task (:1132) +``` + +## 3. Core changes +``` +Protocol.hs + msgHasNotification new, next to hasNotification (:1338): VerifiedMsg e -> Bool + hasNotification of the tag of the event of the message (verifiedChatMsg, :1583) +Delivery.hs + MessageDeliveryJob (:161) notification :: Bool -- new, after body +Store/Delivery.hs + createMsgDeliveryJob (:251) new parameter notification :: Bool, before body; written to delivery_jobs.notification + MessageDeliveryJobRow (:283) BoolInt added after the body + getNextDeliveryJob (:309-328) notification selected after body; set in MessageDeliveryJob +Library/Subscriber.hs + processDeliveryTask (:4235) notification: True when msgHasNotification of verifiedMsg is True for any of acceptedTasks; computed next to senderGMIds, passed at :4237 + (:4250) msgHasNotification verifiedMsg of the task is passed + processDeliveryJob (:4331) notification is bound from the job + recipientBodyPieces (:4417) each piece is returned with its flag: overflow batch False; extBody and body: notification + addPiece (:4410-4416) MsgFlags {notification = the flag of the piece} + deliver (:4483) MsgFlags {notification} +Library/Commands.hs + leaveChannelRelay (:3263) hasNotification XGrpLeave_ is passed +Library/Internal.hs + sendFwdMemberMessage (:2903) MsgFlags {notification = msgHasNotification verifiedMsg} + False for both callers: x.grp.roster (:1307), x.grp.mem.del (Library/Subscriber.hs:3798) +``` +Sites with a fixed flag, unchanged: +``` +Library/Internal.hs:1428 sendHistory: history for a new member, with the welcome message of a group; MsgFlags False (open question 1) +Library/Internal.hs:2459 sendGroupMemberMessages: events of the user to one member; notification = True +Library/Internal.hs:2910 sendPendingGroupMessages: pending messages of the user to one member; notification = True +tests/ChatTests/Groups.hs:5681, 10907, 13320, 13452, 13608, 13701 elements sent by tests through the agent; MsgFlags False +``` +- Every other `MsgFlags` value of the repository is built for events of the user, from `hasNotification` of their tags: `Library/Internal.hs:2248, 2552, 2748`; `Library/Commands.hs:2672, 4131`; `Library/Subscriber.hs:356`. +- Forwarded content is sent as `>` elements only (`encodeFwdElement`, `Messages/Batch.hs:125`; used at `:98`, `Library/Subscriber.hs:4248`, `Library/Internal.hs:1423, 2902`). `XGrpMsgForward` events are only received (`Library/Subscriber.hs:1127`). + +## 4. Storage +Migration `M20261012_delivery_job_notification`, SQLite and Postgres, registered in `Store/SQLite/Migrations.hs` (`:180`, `:358`), `Store/Postgres/Migrations.hs` (`:57`, `:112`) and `simplex-chat.cabal` (`:172`, `:351`). It is one `ADD COLUMN`; indexes are unchanged. +``` +delivery_jobs + notification INTEGER NOT NULL DEFAULT 0 -- SMALLINT in Postgres +``` +- Down migration: the column is dropped. `delivery_tasks` is unchanged: the event of a task is read with its message. +- Applied migrations must be a prefix of the list sorted by name (`SMQ/Agent/Store/Migrations.hs:23-34`; `Store/SQLite/Migrations.hs:363`). The name is ordered after `20261010_invite_member_via_group` and before the unreleased `20261013_chat_layout` and `20261013_msg_priority` of branch `ep/llm-ux` (open question 3). +- The schema dumps and `chat_query_plans.txt` are regenerated by the tests "verify and overwrite schema dump" (`tests/SchemaDump.hs:58`, `tests/PostgresSchemaDump.hs:29`) and "verify and overwrite query plans" (`tests/SchemaDump.hs:165`); they are never edited by hand. + +## 5. Compatibility and cost +``` +chat protocol unchanged; the flag is a parameter of SMP SEND (SMQ/Protocol.hs:598) +recipient every version; the flag is used by the SMP server only +earlier relay or host sends without the flag, as now; a channel member is notified by each relay of this release +job of an earlier release notification = 0: sent without the flag +downgrade the column is dropped +traffic count and size of SMP messages unchanged +notifications one for each flagged SMP message to a queue with notifier credentials: a device with a notification token and the chat set to MFAll or MFMentions + one job is one flagged SMP message for a recipient, for all its events +channel a post is sent by each relay to each member (Library/Internal.hs:2843-2845; Store/Delivery.hs:352) + with N members and R relays up to N × R notifications are sent for a post; one item is created (Library/Internal.hs:2958-2959) + shown on iOS: one notification for a post; a duplicate copy is shown with empty content (section 7) +profile overflow batches unflagged +``` + +## 6. Tests +`tests/ChatTests/Groups.hs`, helpers next to `relayConnIdToMember` (`:13140`): +``` +agentSendBody cc connId body body sent to the connection by the agent of cc, MsgFlags False; a failure is a test failure +lastSndMsgFlags cc connId n msg_flags, "T" or "F", of the last n sent rows of type A_MSG ("M") of agent table messages for the connection, in sending order; withCCAgentTransaction (tests/ChatTests/Utils.hs:675) +lastJobNotification cc notification of the last row of delivery_jobs; withCCTransaction +``` +- The agent row of a sent message is kept after delivery until its receipt (`SMQ/Agent/Store/AgentStore.hs:1318-1349`; `SMQ/Agent.hs:2866-2869`): recipients run `/set receipts all off` before the checked messages (as `:1561`). + +New tests: +``` +group message forwarding (:191) + testGroupMsgForwardNotification createGroup3, setupGroupForwarding (:5802); bob sends a message: alice to cath "T", job True; bob edits it: "F", job False +relay delivery, single relay (:283) + testChannels1RelayDeliverNotification createChannel1Relay (:9523); alice posts: bob to cath, dan and eve "T", job True; cath adds a reaction: bob to dan and eve "F", job False +deliver member profiles via relay (:295) + testChannelOverflowProfileNotification the scenario of testChannelLargeProfileFits (:9956) with an image of 10000 characters and a message of 7000 characters, so that the profile is sent in an overflow batch: bob to dan, the rows added by the message: "F" for the overflow profile batch, "T" for the message +``` +- One sender and one message give one job. +Assertions added to present tests, on `delivery_jobs` only (the connections and their agent rows are deleted after the job, `Library/Subscriber.hs:4328`): +``` +testGroupMsgForwardGroupDeletion (:6135) alice: job True, x.grp.del +testChannelDeleteGroupSigned (:11031) bob: job True, x.grp.del +testGroupMsgForwardAdminRemoval (:6096) alice: job False, x.grp.mem.del +testChannelRelayLeave (:11220) bob: job False, x.grp.leave +``` +Present tests of delivery jobs, unchanged: "group message forwarding" (`:191-206`), "group scoped messages" (`:255-264`), "relay delivery" (`:282-292`), "deliver member profiles via relay" (`:295-301`), `testRelayBatchAllLarge` and `testRelayBatchSingletonOverflow` (`tests/MessageBatching.hs:164, 186`). + +## 7. iOS notification of a forwarded message and of a duplicate copy +Event, `Controller.hs:1048`, next to `CEvtUnknownMemberAnnounced`: +``` +CEvtDuplicateGroupMessage JSON type duplicateGroupMessage + user :: User + member :: GroupMember -- the member whose connection delivered the copy +``` +Emission, `Library/Internal.hs`, before the present handling of the duplicate, only when the chat is started with `main=off` (the NSE and the share extension): +``` +isMainApp :2976 subscriptionMode is SMSubscribe; startChatController sets SMOnlyCreate for main=off (Library/Commands.hs:235) +saveGroupRcvMsg :2925 direct copy; member: the member of the connection; every SEDuplicateGroupMessage + x.grp.mem.con to the forwarding member when the stored copy was forwarded, as now (:2940-2944) +saveGroupFwdRcvMsg :2959 channel; member: the forwarding relay; Nothing is returned, as now + :2960-2969 group; member: the forwarding host; x.grp.mem.con and the error, as now +``` +Consumers: +``` +terminal view View.hs:471: no output, as CEvtNtfMessage (:547) +API docs bots/src/API/Docs/Events.hs:205: in undocumentedEvents, as CEvtUnknownMemberCreated +apps, CLI, bots not emitted: started with main=on +NSE decoded: case duplicateGroupMessage (SimpleX NSE/NSEAPITypes.swift:91) +``` +Swift types, `apps/ios/SimpleXChat/ChatTypes.swift`: +``` +CIMeta.forwardedByMember :4253 Int64?, after editable, as in core CIMeta (Messages.hs:525); omitted in JSON for Nothing +groupMemberEntityId :3447 (groupId, groupMemberId) -> "# @"; used by GroupMember.id (:3258) and the NSE +CIDirection.groupMemberId :4224 groupMemberId of the member of groupRcv; nil for other directions +``` + +NSE, `apps/ios/SimpleX NSE/NotificationService.swift`: +``` +receivedMsgNtf (:1035-1039) + newChatItems key of a group item: groupMemberEntityId of the group and of the member whose connection delivered the item: + forwardedByMember, otherwise chatDir.groupMemberId; the id of the connection entity (ChatTypes.swift:3624-3632) + other items, and channelRcv without forwardedByMember: chatInfo.id, as now + duplicateGroupMessage key: member.id; notification: .noNtf +processReceivedNtf (:586-588): the best attempt of the entity is read from notificationEntities[id] and replaced when ntf.replaces(bestAttempt) +NSENotificationData.replaces (:77-85), new notification, best attempt: + .noNtf, .connectionEvent true: the default of the connection + .noNtf, other false + .callInvitation, any true + other, .callInvitation false + other, other true +``` +- `.noNtf` is used: `.connectionEvent` is created only as the default (`defaultBestAttemptNtf`, `:1208-1229`). A received item, a contact event and a call are never replaced by `.noNtf`. +- The best attempt is read from `notificationEntities[id]`: `processDroppedNotifications` (`:514-529`) passes the entity copied before its notifications are processed. + +Results: +``` +forwarded post, MFAll the text of the post (createMessageReceivedNtf, apps/ios/SimpleXChat/Notifications.swift:66) +forwarded post, MFMentions the text for a mention or a reply; empty content otherwise +duplicate copy empty content, unless an item of the same push notification is shown +mention, then other item the mention is shown; before this patch, .noNtf of the other item replaced it +``` +Tests, `tests/ChatTests/Groups.hs`; helpers next to `lastJobNotification`: +``` +duplicateMessageEventsCfg testCfg with an eventHook (Controller.hs:257) that records the member names of CEvtDuplicateGroupMessage +setMainAppOff cc subscriptionMode of cc set to SMOnlyCreate, as by /_start main=off; called after the group is created +``` +Copies are sent through the agent of the sender by `agentSendBody`, as in `testGroupMsgForwardOwnMembershipRejected` (`:5666`), so that the order of copies is fixed: +``` +group message forwarding (:191) + testGroupMsgForwardDuplicateEvent createGroup3; bob's message is received by cath directly; alice's agent sends a forwarded copy + (Library/Internal.hs:2960-2969): cath "duplicate group message, group id: 1"; no event + setMainAppOff cath; alice's agent sends the copy again: the same line; event member alice + testGroupMsgDirectDuplicateEvent alice's agent sends a forwarded copy; setMainAppOff cath; bob's agent sends the direct copy + (Library/Internal.hs:2939-2946): cath "duplicate group message, group id: 1"; event member bob +relay delivery, single relay (:283) + testChannelDuplicateEvent createChannel1Relay; alice posts; setMainAppOff cath; bob's agent sends a second forwarded copy + (Library/Internal.hs:2959): event member bob; cath without output +``` +- The NSE and the Swift types are not compiled on this machine. Manual checks on iOS: a post in a channel with 2 relays, with MFAll and with MFMentions; a forwarded message in a group; a mention followed by an ordinary message in one push notification. + +## Interfaces +``` +core functions msgHasNotification :: VerifiedMsg e -> Bool + createMsgDeliveryJob, new parameter notification + isMainApp :: CM Bool +core types MessageDeliveryJob.notification +core events CEvtDuplicateGroupMessage, JSON type duplicateGroupMessage +storage delivery_jobs.notification + migration M20261012_delivery_job_notification +iOS CIMeta.forwardedByMember + groupMemberEntityId + CIDirection.groupMemberId + NSEChatEvent.duplicateGroupMessage + NSENotificationData.replaces +test helpers agentSendBody + lastSndMsgFlags + lastJobNotification + duplicateMessageEventsCfg + setMainAppOff +``` +- `eventHasNotification :: ChatMsgEvent e -> Bool` of `2026-10-13-message-priority.md` is accepted by one change: `msgHasNotification` is computed as `eventHasNotification` of the event of the message. Its three uses, the job column and `hasNotification XGrpLeave_` in `leaveChannelRelay` are unchanged. +- With that change a forwarded `x.msg.update` that raises priority is flagged. The line of the priority plan, section 4, "delivery jobs of relays and forwarding hosts, MsgFlags False for every message" is replaced by this plan. + +## Open questions +1. History and the welcome message (`sendHistory`), flagged until commit `93831a0f2` (2026-07-17) and unflagged since: apply the rule? Recommended: unchanged, sent without the flag. +2. The job flag as a column, or computed by the job worker from the decoded body, without a migration? Recommended: the column, computed where the events of the tasks are read. +3. Release order with `ep/llm-ux`. Recommended: this correction first, with date `20261012` in the migration name; otherwise a date after the last released migration. diff --git a/simplex-chat.cabal b/simplex-chat.cabal index c9a29b3ac8..205939a9a2 100644 --- a/simplex-chat.cabal +++ b/simplex-chat.cabal @@ -170,6 +170,7 @@ library Simplex.Chat.Store.Postgres.Migrations.M20260923_preferences_json Simplex.Chat.Store.Postgres.Migrations.M20260925_badge_bindings Simplex.Chat.Store.Postgres.Migrations.M20261010_invite_member_via_group + Simplex.Chat.Store.Postgres.Migrations.M20261012_delivery_job_notification else exposed-modules: Simplex.Chat.Archive @@ -349,6 +350,7 @@ library Simplex.Chat.Store.SQLite.Migrations.M20260923_preferences_json Simplex.Chat.Store.SQLite.Migrations.M20260925_badge_bindings Simplex.Chat.Store.SQLite.Migrations.M20261010_invite_member_via_group + Simplex.Chat.Store.SQLite.Migrations.M20261012_delivery_job_notification other-modules: Paths_simplex_chat hs-source-dirs: diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 2d553bdc9b..65c70e75fe 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -1046,6 +1046,7 @@ data ChatEvent | CEvtUnknownMemberCreated {user :: User, groupInfo :: GroupInfo, forwardedByMember :: GroupMember, member :: GroupMember} | CEvtUnknownMemberBlocked {user :: User, groupInfo :: GroupInfo, blockedByMember :: GroupMember, member :: GroupMember} | CEvtUnknownMemberAnnounced {user :: User, groupInfo :: GroupInfo, announcingMember :: GroupMember, unknownMember :: GroupMember, announcedMember :: GroupMember} + | CEvtDuplicateGroupMessage {user :: User, member :: GroupMember} | CEvtGroupDeleted {user :: User, groupInfo :: GroupInfo, member :: GroupMember, msgSigned :: Maybe MsgSigStatus} | CEvtGroupUpdated {user :: User, fromGroup :: GroupInfo, toGroup :: GroupInfo, member_ :: Maybe GroupMember, msgSigned :: Maybe MsgSigStatus} -- there is the same command response | CEvtAcceptingGroupJoinRequestMember {user :: User, groupInfo :: GroupInfo, member :: GroupMember} diff --git a/src/Simplex/Chat/Delivery.hs b/src/Simplex/Chat/Delivery.hs index a6ccc74247..a83c624274 100644 --- a/src/Simplex/Chat/Delivery.hs +++ b/src/Simplex/Chat/Delivery.hs @@ -163,6 +163,7 @@ data MessageDeliveryJob = MessageDeliveryJob jobScope :: DeliveryJobScope, senderGMIds :: [GroupMemberId], body :: ByteString, + notification :: Bool, cursorGMId_ :: Maybe GroupMemberId } deriving (Show) diff --git a/src/Simplex/Chat/Library/Commands.hs b/src/Simplex/Chat/Library/Commands.hs index c37c45ec90..7a4e6d2d36 100644 --- a/src/Simplex/Chat/Library/Commands.hs +++ b/src/Simplex/Chat/Library/Commands.hs @@ -3260,7 +3260,7 @@ processChatCommand cxt nm = \case withFastStore' $ \db -> do deleteGroupDeliveryTasks db gInfo deleteGroupDeliveryJobs db gInfo - createMsgDeliveryJob db gInfo (DJSGroup {jobSpec = DJRelayRemoved}) [] body + createMsgDeliveryJob db gInfo (DJSGroup {jobSpec = DJRelayRemoved}) [] (hasNotification XGrpLeave_) body lift . void $ getDeliveryJobWorker True (groupId, DWSGroup) pure msg leaveGroupSendMsg user g@(GIK gInfo _) = do diff --git a/src/Simplex/Chat/Library/Internal.hs b/src/Simplex/Chat/Library/Internal.hs index 6ea428ac1f..418d454ada 100644 --- a/src/Simplex/Chat/Library/Internal.hs +++ b/src/Simplex/Chat/Library/Internal.hs @@ -2900,7 +2900,7 @@ sendFwdMemberMessage :: GroupMember -> GrpMsgForward -> VerifiedMsg 'Json -> CM sendFwdMemberMessage member fwd verifiedMsg = forM_ (readyMemberConn member) $ \(_, conn) -> do let body = encodeBinaryBatch [encodeFwdElement fwd verifiedMsg] - void $ withAgent $ \a -> sendMessages a [(aConnId conn, PQEncOff, MsgFlags False, VRValue Nothing body)] + void $ withAgent $ \a -> sendMessages a [(aConnId conn, PQEncOff, MsgFlags {notification = msgHasNotification verifiedMsg}, VRValue Nothing body)] -- TODO ensure order - pending messages interleave with user input messages sendPendingGroupMessages :: User -> GroupInfo -> GroupMember -> Connection -> CM () @@ -2937,11 +2937,13 @@ saveGroupRcvMsg user groupId authorMember conn@Connection {connId} agentMsgMeta msg <- withStore (\db -> createNewMessageAndRcvMsgDelivery db (GroupId groupId) newMsg sharedMsgId_ rcvMsgDelivery $ Just amGroupMemId) `catchAllErrors` \e -> case e of - ChatErrorStore (SEDuplicateGroupMessage _ _ _ (Just forwardedByGroupMemberId)) -> do - cxt <- chatStoreCxt - fm <- withStore $ \db -> getGroupMember db cxt user groupId forwardedByGroupMemberId - forM_ (memberConn fm) $ \fmConn -> - void $ sendDirectMemberMessage fmConn (XGrpMemCon amMemId) groupId + ChatErrorStore (SEDuplicateGroupMessage _ _ _ forwardedByGroupMemberId_) -> do + unlessM isMainApp $ toView $ CEvtDuplicateGroupMessage user am' + forM_ forwardedByGroupMemberId_ $ \forwardedByGroupMemberId -> do + cxt <- chatStoreCxt + fm <- withStore $ \db -> getGroupMember db cxt user groupId forwardedByGroupMemberId + forM_ (memberConn fm) $ \fmConn -> + void $ sendDirectMemberMessage fmConn (XGrpMemCon amMemId) groupId throwError e _ -> throwError e pure (am', conn', msg) @@ -2955,20 +2957,25 @@ saveGroupFwdRcvMsg user gInfo@GroupInfo {groupId} forwardingMember refAuthorMemb -- TODO [relays] TBC highlighting difference between deduplicated messages (useRelays branch) withStore' (\db -> runExceptT $ createNewRcvMessage db (GroupId groupId) newMsg sharedMsgId_ refAuthorId fwdMemberId) >>= \case Right msg -> pure $ Just msg - Left e@SEDuplicateGroupMessage {authorGroupMemberId, forwardedByGroupMemberId} - | useRelays' gInfo -> pure Nothing -- with chat relays, duplicates are expected - | otherwise -> case (authorGroupMemberId, forwardedByGroupMemberId) of - (Just authorGMId, Nothing) -> do - cxt <- chatStoreCxt - am@GroupMember {memberId = amMemberId} <- withStore $ \db -> getGroupMember db cxt user groupId authorGMId - if maybe False (\ref -> sameMemberId (memberId' ref) am) refAuthorMember_ - then forM_ (memberConn forwardingMember) $ \fmConn -> - void $ sendDirectMemberMessage fmConn (XGrpMemCon amMemberId) groupId - else toView $ CEvtMessageError user "error" "saveGroupFwdRcvMsg: referenced author member id doesn't match message member id" - throwError $ ChatErrorStore e - _ -> throwError $ ChatErrorStore e + Left e@SEDuplicateGroupMessage {authorGroupMemberId, forwardedByGroupMemberId} -> do + unlessM isMainApp $ toView $ CEvtDuplicateGroupMessage user forwardingMember + if + | useRelays' gInfo -> pure Nothing -- with chat relays, duplicates are expected + | otherwise -> case (authorGroupMemberId, forwardedByGroupMemberId) of + (Just authorGMId, Nothing) -> do + cxt <- chatStoreCxt + am@GroupMember {memberId = amMemberId} <- withStore $ \db -> getGroupMember db cxt user groupId authorGMId + if maybe False (\ref -> sameMemberId (memberId' ref) am) refAuthorMember_ + then forM_ (memberConn forwardingMember) $ \fmConn -> + void $ sendDirectMemberMessage fmConn (XGrpMemCon amMemberId) groupId + else toView $ CEvtMessageError user "error" "saveGroupFwdRcvMsg: referenced author member id doesn't match message member id" + throwError $ ChatErrorStore e + _ -> throwError $ ChatErrorStore e Left e -> throwError $ ChatErrorStore e +isMainApp :: CM Bool +isMainApp = (SMSubscribe ==) <$> chatReadVar subscriptionMode + saveSndChatItem :: ChatTypeI c => User -> ChatDirection c 'MDSnd -> SndMessage -> CIContent 'MDSnd -> CM (ChatItem c 'MDSnd) saveSndChatItem user cd msg content = saveSndChatItem' user cd msg content Nothing Nothing Nothing Nothing False diff --git a/src/Simplex/Chat/Library/Subscriber.hs b/src/Simplex/Chat/Library/Subscriber.hs index a1e748831c..9047ca1532 100644 --- a/src/Simplex/Chat/Library/Subscriber.hs +++ b/src/Simplex/Chat/Library/Subscriber.hs @@ -4233,8 +4233,9 @@ runDeliveryTaskWorker a deliveryKey Worker {doWork} = do withWorkItems a doWork (withStore' $ \db -> getNextDeliveryTasks db gInfo task) $ \nextTasks -> do let (body_, acceptedTasks, largeTasks) = batchDeliveryTasks1 (vr cxt) maxForwardBatchLength nextTasks senderGMIds = S.toList . S.fromList $ map (\MessageDeliveryTask {senderGMId} -> senderGMId) acceptedTasks + notification = any (\MessageDeliveryTask {verifiedMsg} -> msgHasNotification verifiedMsg) acceptedTasks withStore' $ \db -> do - forM_ body_ $ \body -> createMsgDeliveryJob db gInfo jobScope senderGMIds body + forM_ body_ $ \body -> createMsgDeliveryJob db gInfo jobScope senderGMIds notification body forM_ acceptedTasks $ \t -> updateDeliveryTaskStatus db (deliveryTaskId t) DTSProcessed forM_ largeTasks $ \t -> setDeliveryTaskErrStatus db (deliveryTaskId t) "large" when (isJust body_) . lift . void $ getDeliveryJobWorker True deliveryKey @@ -4247,7 +4248,7 @@ runDeliveryTaskWorker a deliveryKey Worker {doWork} = do fwd = GrpMsgForward {fwdSender, fwdBrokerTs} body = encodeBinaryBatch [encodeFwdElement fwd verifiedMsg] withStore' $ \db -> do - createMsgDeliveryJob db gInfo jobScope [senderGMId] body + createMsgDeliveryJob db gInfo jobScope [senderGMId] (msgHasNotification verifiedMsg) body updateDeliveryTaskStatus db (deliveryTaskId task) DTSProcessed lift . void $ getDeliveryJobWorker True deliveryKey @@ -4328,7 +4329,7 @@ runDeliveryJobWorker a deliveryKey Worker {doWork} = do deleteGroupConnections user gInfo True withStore' $ \db -> updateDeliveryJobStatus db jobId DJSComplete where - MessageDeliveryJob {jobId, jobScope, senderGMIds, body, cursorGMId_ = startingCursor} = job + MessageDeliveryJob {jobId, jobScope, senderGMIds, body, notification, cursorGMId_ = startingCursor} = job singleSenderGMId_ = case senderGMIds of [s] -> Just s _ -> Nothing @@ -4407,16 +4408,16 @@ runDeliveryJobWorker a deliveryKey Worker {doWork} = do addRecipient acc r = case readyMemberConn r of Just (_, conn) -> snd $ foldl' (addPiece conn) (0 :: Int, acc) (recipientBodyPieces r) Nothing -> acc - addPiece conn (k, (issued, reqs)) (bid, msgBody) = + addPiece conn (k, (issued, reqs)) (bid, ntf, msgBody) = let vor | IS.member bid issued = VRRef bid | otherwise = VRValue (Just bid) msgBody issued' = IS.insert bid issued connId = if k == 0 then aConnId conn else B.empty - in (k + 1, (issued', (connId, PQEncOff, MsgFlags False, vor) : reqs)) + in (k + 1, (issued', (connId, PQEncOff, MsgFlags {notification = ntf}, vor) : reqs)) recipientBodyPieces r = - [(i, b) | (i, (b, ss)) <- overflowWithIds, any missing ss] - <> [if any missing inBodySenders then (1, extBody) else (0, body)] + [(i, False, b) | (i, (b, ss)) <- overflowWithIds, any missing ss] + <> [if any missing inBodySenders then (1, notification, extBody) else (0, notification, body)] where missing s = case M.lookup (groupMemberId' s) senderVec of Just vec -> getRelation (indexInGroup r) vec == MRNew @@ -4480,7 +4481,7 @@ runDeliveryJobWorker a deliveryKey Worker {doWork} = do addReq conn (memIdx_, reqs) = (subtract 1 <$> memIdx_, req : reqs) where - req = (aConnId conn, PQEncOff, MsgFlags False, vrValue_) + req = (aConnId conn, PQEncOff, MsgFlags {notification}, vrValue_) vrValue_ = case memIdx_ of Nothing -> VRValue Nothing msgBody -- sending to one member, do not reference body Just 1 -> VRValue (Just 1) msgBody diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index 1266104e0e..a4fb12cc72 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -1346,6 +1346,11 @@ hasNotification = \case XCallInv_ -> True _ -> False +msgHasNotification :: VerifiedMsg e -> Bool +msgHasNotification verifiedMsg = hasNotification $ toCMEventTag chatMsgEvent + where + ChatMessage {chatMsgEvent} = verifiedChatMsg verifiedMsg + hasDeliveryReceipt :: CMEventTag e -> Bool hasDeliveryReceipt = \case XMsgNew_ -> True diff --git a/src/Simplex/Chat/Store/Delivery.hs b/src/Simplex/Chat/Store/Delivery.hs index c80264da7a..c8875baab2 100644 --- a/src/Simplex/Chat/Store/Delivery.hs +++ b/src/Simplex/Chat/Store/Delivery.hs @@ -248,8 +248,8 @@ deleteDoneDeliveryTasks db createdAtCutoff = do |] (createdAtCutoff, DTSProcessed, DTSError) -createMsgDeliveryJob :: DB.Connection -> GroupInfo -> DeliveryJobScope -> [GroupMemberId] -> ByteString -> IO () -createMsgDeliveryJob db gInfo jobScope senderGMIds body = do +createMsgDeliveryJob :: DB.Connection -> GroupInfo -> DeliveryJobScope -> [GroupMemberId] -> Bool -> ByteString -> IO () +createMsgDeliveryJob db gInfo jobScope senderGMIds notification body = do currentTs <- getCurrentTime DB.execute db @@ -257,10 +257,10 @@ createMsgDeliveryJob db gInfo jobScope senderGMIds body = do INSERT INTO delivery_jobs ( group_id, worker_scope, job_scope_spec_tag, job_scope_include_pending, job_scope_support_gm_id, - sender_group_member_ids, body, job_status, created_at, updated_at - ) VALUES (?,?,?,?,?,?,?,?,?,?) + sender_group_member_ids, body, notification, job_status, created_at, updated_at + ) VALUES (?,?,?,?,?,?,?,?,?,?,?) |] - ((Only groupId) :. jobScopeRow_ jobScope :. (senderColumn, Binary body, DJSPending, currentTs, currentTs)) + ((Only groupId) :. jobScopeRow_ jobScope :. (senderColumn, Binary body, BI notification, DJSPending, currentTs, currentTs)) where GroupInfo {groupId} = gInfo -- NULL ↔ []; non-empty list ↔ comma-separated decimal Int64s. @@ -280,7 +280,7 @@ getPendingDeliveryJobScopes db = |] (Only DJSPending) -type MessageDeliveryJobRow = (Only Int64) :. DeliveryJobScopeRow :. (Maybe Text, Binary ByteString, Maybe GroupMemberId) +type MessageDeliveryJobRow = (Only Int64) :. DeliveryJobScopeRow :. (Maybe Text, Binary ByteString, BoolInt, Maybe GroupMemberId) getNextDeliveryJob :: DB.Connection -> DeliveryWorkerKey -> IO (Either StoreError (Maybe MessageDeliveryJob)) getNextDeliveryJob db deliveryKey = do @@ -310,14 +310,14 @@ getNextDeliveryJob db deliveryKey = do SELECT delivery_job_id, worker_scope, job_scope_spec_tag, job_scope_include_pending, job_scope_support_gm_id, - sender_group_member_ids, body, cursor_group_member_id + sender_group_member_ids, body, notification, cursor_group_member_id FROM delivery_jobs WHERE delivery_job_id = ? |] (Only jobId) where toDeliveryJob :: MessageDeliveryJobRow -> Either StoreError MessageDeliveryJob - toDeliveryJob ((Only jobId') :. jobScopeRow :. (senderGMIdsText_, Binary body, cursorGMId_)) = do + toDeliveryJob ((Only jobId') :. jobScopeRow :. (senderGMIdsText_, Binary body, BI notification, cursorGMId_)) = do jobScope <- maybe (Left $ SEInvalidDeliveryJob jobId') Right $ toJobScope_ jobScopeRow -- NULL or empty string means []; otherwise the value must parse -- as a comma-separated decimal Int64 list. An unparseable @@ -325,7 +325,7 @@ getNextDeliveryJob db deliveryKey = do senderGMIds <- case senderGMIdsText_ of Nothing -> Right [] Just t -> maybe (Left $ SEInvalidDeliveryJob jobId') Right $ parseSenderGMIds t - Right $ MessageDeliveryJob {jobId = jobId', jobScope, senderGMIds, body, cursorGMId_} + Right $ MessageDeliveryJob {jobId = jobId', jobScope, senderGMIds, body, notification, cursorGMId_} parseSenderGMIds :: Text -> Maybe [GroupMemberId] parseSenderGMIds t | T.null t = Just [] diff --git a/src/Simplex/Chat/Store/Postgres/Migrations.hs b/src/Simplex/Chat/Store/Postgres/Migrations.hs index dc2de08739..7fceb20dfe 100644 --- a/src/Simplex/Chat/Store/Postgres/Migrations.hs +++ b/src/Simplex/Chat/Store/Postgres/Migrations.hs @@ -55,6 +55,7 @@ import Simplex.Chat.Store.Postgres.Migrations.M20260918_badge_issue_errors import Simplex.Chat.Store.Postgres.Migrations.M20260923_preferences_json import Simplex.Chat.Store.Postgres.Migrations.M20260925_badge_bindings import Simplex.Chat.Store.Postgres.Migrations.M20261010_invite_member_via_group +import Simplex.Chat.Store.Postgres.Migrations.M20261012_delivery_job_notification import Simplex.Messaging.Agent.Store.Shared (Migration (..)) schemaMigrations :: [(String, Text, Maybe Text)] @@ -109,7 +110,8 @@ schemaMigrations = ("20260918_badge_issue_errors", m20260918_badge_issue_errors, Just down_m20260918_badge_issue_errors), ("20260923_preferences_json", m20260923_preferences_json, Just down_m20260923_preferences_json), ("20260925_badge_bindings", m20260925_badge_bindings, Just down_m20260925_badge_bindings), - ("20261010_invite_member_via_group", m20261010_invite_member_via_group, Just down_m20261010_invite_member_via_group) + ("20261010_invite_member_via_group", m20261010_invite_member_via_group, Just down_m20261010_invite_member_via_group), + ("20261012_delivery_job_notification", m20261012_delivery_job_notification, Just down_m20261012_delivery_job_notification) ] -- | The list of migrations in ascending order by date diff --git a/src/Simplex/Chat/Store/Postgres/Migrations/M20261012_delivery_job_notification.hs b/src/Simplex/Chat/Store/Postgres/Migrations/M20261012_delivery_job_notification.hs new file mode 100644 index 0000000000..b8e025f53e --- /dev/null +++ b/src/Simplex/Chat/Store/Postgres/Migrations/M20261012_delivery_job_notification.hs @@ -0,0 +1,19 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE QuasiQuotes #-} + +module Simplex.Chat.Store.Postgres.Migrations.M20261012_delivery_job_notification where + +import Data.Text (Text) +import Text.RawString.QQ (r) + +m20261012_delivery_job_notification :: Text +m20261012_delivery_job_notification = + [r| +ALTER TABLE delivery_jobs ADD COLUMN notification SMALLINT NOT NULL DEFAULT 0; +|] + +down_m20261012_delivery_job_notification :: Text +down_m20261012_delivery_job_notification = + [r| +ALTER TABLE delivery_jobs DROP COLUMN notification; +|] diff --git a/src/Simplex/Chat/Store/SQLite/Migrations.hs b/src/Simplex/Chat/Store/SQLite/Migrations.hs index 3e6d6694c6..6ed3e81d1f 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations.hs +++ b/src/Simplex/Chat/Store/SQLite/Migrations.hs @@ -178,6 +178,7 @@ import Simplex.Chat.Store.SQLite.Migrations.M20260918_badge_issue_errors import Simplex.Chat.Store.SQLite.Migrations.M20260923_preferences_json import Simplex.Chat.Store.SQLite.Migrations.M20260925_badge_bindings import Simplex.Chat.Store.SQLite.Migrations.M20261010_invite_member_via_group +import Simplex.Chat.Store.SQLite.Migrations.M20261012_delivery_job_notification import Simplex.Messaging.Agent.Store.Shared (Migration (..)) schemaMigrations :: [(String, Query, Maybe Query)] @@ -355,7 +356,8 @@ schemaMigrations = ("20260918_badge_issue_errors", m20260918_badge_issue_errors, Just down_m20260918_badge_issue_errors), ("20260923_preferences_json", m20260923_preferences_json, Just down_m20260923_preferences_json), ("20260925_badge_bindings", m20260925_badge_bindings, Just down_m20260925_badge_bindings), - ("20261010_invite_member_via_group", m20261010_invite_member_via_group, Just down_m20261010_invite_member_via_group) + ("20261010_invite_member_via_group", m20261010_invite_member_via_group, Just down_m20261010_invite_member_via_group), + ("20261012_delivery_job_notification", m20261012_delivery_job_notification, Just down_m20261012_delivery_job_notification) ] -- | The list of migrations in ascending order by date diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/M20261012_delivery_job_notification.hs b/src/Simplex/Chat/Store/SQLite/Migrations/M20261012_delivery_job_notification.hs new file mode 100644 index 0000000000..9a582823b5 --- /dev/null +++ b/src/Simplex/Chat/Store/SQLite/Migrations/M20261012_delivery_job_notification.hs @@ -0,0 +1,18 @@ +{-# LANGUAGE QuasiQuotes #-} + +module Simplex.Chat.Store.SQLite.Migrations.M20261012_delivery_job_notification where + +import Database.SQLite.Simple (Query) +import Database.SQLite.Simple.QQ (sql) + +m20261012_delivery_job_notification :: Query +m20261012_delivery_job_notification = + [sql| +ALTER TABLE delivery_jobs ADD COLUMN notification INTEGER NOT NULL DEFAULT 0; +|] + +down_m20261012_delivery_job_notification :: Query +down_m20261012_delivery_job_notification = + [sql| +ALTER TABLE delivery_jobs DROP COLUMN notification; +|] diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/agent_query_plans.txt b/src/Simplex/Chat/Store/SQLite/Migrations/agent_query_plans.txt index d12b3c98cf..dc8ad0f248 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/agent_query_plans.txt +++ b/src/Simplex/Chat/Store/SQLite/Migrations/agent_query_plans.txt @@ -614,15 +614,6 @@ Query: Plan: SEARCH address_ratchet_keys USING INDEX idx_address_ratchet_keys (conn_id=? AND ratchet_key_id=?) -Query: - UPDATE rcv_messages - SET receive_attempts = receive_attempts + 1 - WHERE conn_id = ? AND internal_id = ? - RETURNING receive_attempts - -Plan: -SEARCH rcv_messages USING COVERING INDEX idx_rcv_messages_conn_id_internal_id (conn_id=? AND internal_id=?) - Query: DELETE FROM address_ratchet_keys WHERE conn_id = ? @@ -1431,6 +1422,10 @@ Query: SELECT last_insert_rowid() Plan: SCAN CONSTANT ROW +Query: SELECT msg_flags FROM messages WHERE conn_id = ? AND internal_snd_id IS NOT NULL AND msg_type = ? ORDER BY internal_id DESC LIMIT ? +Plan: +SEARCH messages USING PRIMARY KEY (conn_id=?) + Query: SELECT ratchet_state FROM ratchets WHERE conn_id = ? Plan: SEARCH ratchets USING PRIMARY KEY (conn_id=?) 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 433c2ec43f..64aea20620 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt +++ b/src/Simplex/Chat/Store/SQLite/Migrations/chat_query_plans.txt @@ -774,7 +774,7 @@ Query: SELECT delivery_job_id, worker_scope, job_scope_spec_tag, job_scope_include_pending, job_scope_support_gm_id, - sender_group_member_ids, body, cursor_group_member_id + sender_group_member_ids, body, notification, cursor_group_member_id FROM delivery_jobs WHERE delivery_job_id = ? @@ -5058,8 +5058,8 @@ Query: INSERT INTO delivery_jobs ( group_id, worker_scope, job_scope_spec_tag, job_scope_include_pending, job_scope_support_gm_id, - sender_group_member_ids, body, job_status, created_at, updated_at - ) VALUES (?,?,?,?,?,?,?,?,?,?) + sender_group_member_ids, body, notification, job_status, created_at, updated_at + ) VALUES (?,?,?,?,?,?,?,?,?,?,?) Plan: @@ -7838,6 +7838,10 @@ Query: SELECT note_folder_id FROM note_folders WHERE user_id = ? LIMIT 1 Plan: SEARCH note_folders USING COVERING INDEX note_folders_user_id (user_id=?) +Query: SELECT notification FROM delivery_jobs ORDER BY delivery_job_id DESC LIMIT 1 +Plan: +SCAN delivery_jobs + Query: SELECT purchase_key FROM badge_code_redemptions Plan: SCAN badge_code_redemptions diff --git a/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql b/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql index d60a41047f..71abf5d37f 100644 --- a/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql +++ b/src/Simplex/Chat/Store/SQLite/Migrations/chat_schema.sql @@ -806,7 +806,8 @@ CREATE TABLE delivery_jobs( created_at TEXT NOT NULL DEFAULT(datetime('now')), updated_at TEXT NOT NULL DEFAULT(datetime('now')) , - sender_group_member_ids TEXT + sender_group_member_ids TEXT, + notification INTEGER NOT NULL DEFAULT 0 ) STRICT; CREATE TABLE group_member_status_predicates( member_status TEXT NOT NULL PRIMARY KEY, diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index e3e58cdf49..03b116202b 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -469,6 +469,7 @@ chatEventToView hu ChatConfig {logLevel, showReactions, showReceipts, testView} CEvtUnknownMemberCreated u g fwdM um -> ttyUser u [ttyGroup' g <> ": " <> ttyMember fwdM <> " forwarded a message from an unknown member, creating unknown member record " <> ttyMember um] CEvtUnknownMemberBlocked u g byM um -> ttyUser u [ttyGroup' g <> ": " <> ttyMember byM <> " blocked an unknown member, creating unknown member record " <> ttyMember um] CEvtUnknownMemberAnnounced u g _ um m -> ttyUser u [ttyGroup' g <> ": unknown member " <> ttyMember um <> " updated to " <> ttyMember m] + CEvtDuplicateGroupMessage {} -> [] CEvtRcvFileDescrReady _ _ _ _ -> [] CEvtRcvFileAccepted u ci -> ttyUser u $ savingFile' ci CEvtRcvFileAcceptedSndCancelled u ft -> ttyUser u $ viewRcvFileSndCancelled ft diff --git a/tests/ChatTests/Groups.hs b/tests/ChatTests/Groups.hs index a127b160f5..58c1c9c51c 100644 --- a/tests/ChatTests/Groups.hs +++ b/tests/ChatTests/Groups.hs @@ -18,7 +18,7 @@ import ChatTests.Profiles (addTestBadge, futureDate, issueTestBadge, testBadgeKe import ChatTests.Utils import Control.Concurrent (threadDelay) import Control.Concurrent.Async (concurrently_) -import Control.Concurrent.STM (atomically) +import Control.Concurrent.STM (TVar, atomically, modifyTVar', newTVarIO, readTVarIO, writeTVar) import Control.Monad (forM_, void, when) import Control.Monad.Except (runExceptT) import Control.Monad.Reader (runReaderT) @@ -32,14 +32,14 @@ import Data.List (intercalate, isInfixOf, isSuffixOf) import qualified Data.Map.Strict as M import qualified Data.Text as T import Simplex.Chat.Badges (FileSizeLimits (..)) -import Simplex.Chat.Controller (ChatController (ChatController, smpAgent), ChatConfig (..), ChatHooks (..), ChatLogLevel (..), defaultChatHooks, withAgent) +import Simplex.Chat.Controller (ChatController (ChatController, smpAgent, subscriptionMode), ChatConfig (..), ChatEvent (CEvtDuplicateGroupMessage), ChatHooks (..), ChatLogLevel (..), defaultChatHooks, withAgent) import Simplex.Chat.Library.Internal (encodeConnInfo, getShortLinkConnReq, uniqueMsgMentions, updatedMentionNames) import Simplex.Chat.Markdown (parseMaybeMarkdownList) import Simplex.Chat.Messages (CIMention (..), CIMentionMember (..), ChatItemId) import Simplex.Chat.Messages.Batch (encodeBinaryBatch, encodeFwdElement) import Simplex.Chat.Messages.CIContent (publicGroupNoE2EText) import Simplex.Chat.Options -import Simplex.Chat.Protocol (ChatMessage (ChatMessage), ChatMsgEvent (XGrpMemNew, XGrpRelayInv, XInfo, XMsgUpdate, XMsgNew, XMsgDel), FwdSender (FwdMember, FwdChannel), GrpMsgForward (GrpMsgForward), MsgContainer (..), MsgMention (..), MsgContent (..), VerifiedMsg (VMUnsigned), mcSimple, msgContentText) +import Simplex.Chat.Protocol (ChatMessage (ChatMessage), ChatMsgEvent (XGrpMemNew, XGrpRelayInv, XInfo, XMsgUpdate, XMsgNew, XMsgDel), FwdSender (FwdMember, FwdChannel), GrpMsgForward (GrpMsgForward), MsgContainer (..), MsgMention (..), MsgContent (..), VerifiedMsg (VMUnsigned), chatMsgToBody, mcSimple, msgContentText) import Simplex.Chat.Types import Simplex.Chat.Types.MemberRelations (MemberRelation (..), getRelation, setRelation) import Simplex.Chat.Types.Shared (GroupMemberRole (..), GroupAcceptance (..)) @@ -47,7 +47,7 @@ import Simplex.Messaging.Agent (joinConnection, prepareConnectionToJoin, sendMes import Simplex.Messaging.Agent.Env.SQLite import Simplex.Messaging.Agent.RetryInterval import qualified Simplex.Messaging.Agent.Store.DB as DB -import Simplex.Messaging.Agent.Store.DB (Binary (..)) +import Simplex.Messaging.Agent.Store.DB (Binary (..), BoolInt (..)) import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Crypto.BBS (bbsKeyGen) import Simplex.Messaging.Client (pattern NRMInteractive) @@ -194,7 +194,10 @@ chatGroupTests = do it "forward batched messages" testGroupMsgForwardBatched it "forward reports to moderators, don't forward to members (x.msg.new, MCReport)" testGroupMsgForwardReport it "deduplicate forwarded messages" testGroupMsgForwardDeduplicate + it "report duplicate when forwarded copy is received after direct copy" testGroupMsgForwardDuplicateEvent + it "report duplicate when direct copy is received after forwarded copy" testGroupMsgDirectDuplicateEvent it "forward message edit (x.msg.update)" testGroupMsgForwardEdit + it "set notification flag of forwarded messages by the original event" testGroupMsgForwardNotification it "forward message reaction (x.msg.react)" testGroupMsgForwardReaction it "forward message deletion (x.msg.del)" testGroupMsgForwardDeletion it "forward file (x.msg.file.descr)" testGroupMsgForwardFile @@ -282,6 +285,8 @@ chatGroupTests = do describe "relay delivery" $ do describe "single relay" $ do it "should deliver messages to members" testChannels1RelayDeliver + it "should set notification flag of delivered messages" testChannels1RelayDeliverNotification + it "should report duplicate message delivered by relay" testChannelDuplicateEvent describe "should deliver messages in a loop over members" $ do it "number of recipients is multiple of bucket size (3/1)" (testChannels1RelayDeliverLoop 1) it "number of recipients is NOT multiple of bucket size (3/2)" (testChannels1RelayDeliverLoop 2) @@ -298,6 +303,7 @@ chatGroupTests = do it "multi senders disseminate independently" testChannelMultiSendersIndependent it "large profile fits in body" testChannelLargeProfileFits it "multiple large profiles pack across batches in one multi-sender job" testChannelMultipleLargeProfiles + it "overflow profile batch is sent without notification flag" testChannelOverflowProfileNotification it "profile update reuses existing announcement (no re-prepend)" testChannelProfileUpdateNoRePrepend describe "channel operations" $ do it "should update channel profile (signed)" testChannelUpdateProfileSigned @@ -5912,6 +5918,53 @@ testGroupMsgForwardDeduplicate = cath <#. "#team bob> hi there" cath <# "#team hey team" +testGroupMsgForwardDuplicateEvent :: HasCallStack => TestParams -> IO () +testGroupMsgForwardDuplicateEvent ps = do + (dups, cfg) <- duplicateMessageEventsCfg + withNewTestChat ps "alice" aliceProfile $ \alice -> + withNewTestChat ps "bob" bobProfile $ \bob -> + withNewTestChatCfg ps cfg "cath" cathProfile $ \cath -> do + createGroup3 "team" alice bob cath + groupForwardingRelations alice "bob" "cath" `shouldEventuallyReturn` (MRConnected, MRConnected) + + bob #> "#team hi there" + [alice, cath] *<# "#team bob> hi there" + + sharedMsgId <- itemSharedMsgId cath + bobMemId <- memberIdByName cath "bob" + connId <- relayConnIdToMember alice "cath" + ts <- getCurrentTime + let chatMsg = ChatMessage chatInitialVRange (Just sharedMsgId) (XMsgNew $ mcSimple $ MCText "hi there") + sendForwardedCopy = agentSendBody alice connId $ encodeBinaryBatch [encodeFwdElement (GrpMsgForward (FwdMember bobMemId "bob") ts) (VMUnsigned chatMsg)] + sendForwardedCopy + cath <##. "duplicate group message, group id: 1" + readTVarIO dups `shouldReturn` [] + setMainAppOff cath + sendForwardedCopy + cath <##. "duplicate group message, group id: 1" + readTVarIO dups `shouldReturn` ["alice"] + +testGroupMsgDirectDuplicateEvent :: HasCallStack => TestParams -> IO () +testGroupMsgDirectDuplicateEvent ps = do + (dups, cfg) <- duplicateMessageEventsCfg + withNewTestChat ps "alice" aliceProfile $ \alice -> + withNewTestChat ps "bob" bobProfile $ \bob -> + withNewTestChatCfg ps cfg "cath" cathProfile $ \cath -> do + createGroup3 "team" alice bob cath + + bobMemId <- memberIdByName cath "bob" + aliceConnId <- relayConnIdToMember alice "cath" + bobConnId <- relayConnIdToMember bob "cath" + ts <- getCurrentTime + let chatMsg = ChatMessage chatInitialVRange (Just $ SharedMsgId "duplicate_msg1") (XMsgNew $ mcSimple $ MCText "hi there") + agentSendBody alice aliceConnId $ encodeBinaryBatch [encodeFwdElement (GrpMsgForward (FwdMember bobMemId "bob") ts) (VMUnsigned chatMsg)] + cath <# "#team bob> hi there [>>]" + + setMainAppOff cath + agentSendBody bob bobConnId $ chatMsgToBody chatMsg + cath <##. "duplicate group message, group id: 1" + readTVarIO dups `shouldReturn` ["bob"] + testGroupMsgForwardEdit :: HasCallStack => TestParams -> IO () testGroupMsgForwardEdit = testChat3 aliceProfile bobProfile cathProfile $ @@ -5936,6 +5989,29 @@ testGroupMsgForwardEdit = cath ##> "/tail #team 1" cath <# "#team bob> hello there [>>]" +testGroupMsgForwardNotification :: HasCallStack => TestParams -> IO () +testGroupMsgForwardNotification = + testChat3 aliceProfile bobProfile cathProfile $ + \alice bob cath -> do + createGroup3 "team" alice bob cath + setupGroupForwarding alice bob cath + cath ##> "/set receipts all off" + cath <## "ok" + cathConnId <- relayConnIdToMember alice "cath" + + bob #> "#team hi there" + alice <# "#team bob> hi there" + cath <# "#team bob> hi there [>>]" + lastSndMsgFlags alice cathConnId 1 `shouldReturn` ["T"] + lastJobNotification alice `shouldReturn` True + + bob ##> "! #team hello there" + bob <# "#team [edited] hello there" + alice <# "#team bob> [edited] hello there" + cath <# "#team bob> [edited] hello there" + lastSndMsgFlags alice cathConnId 1 `shouldReturn` ["F"] + lastJobNotification alice `shouldReturn` False + testGroupMsgForwardReaction :: HasCallStack => TestParams -> IO () testGroupMsgForwardReaction = testChat3 aliceProfile bobProfile cathProfile $ @@ -6118,6 +6194,7 @@ testGroupMsgForwardAdminRemoval = alice <## "use /d #team to delete the group", cath <## "#team: bob removed alice from the group (signed)" ] + lastJobNotification alice `shouldReturn` False -- there is no forwarding admin anymore between bob and cath, so messages don't get delivered -- (this is not a desired behavior, just a test demonstration/proof of current implementation) @@ -6159,6 +6236,7 @@ testGroupMsgForwardGroupDeletion = cath <## "#team: bob deleted the group (signed)" cath <## "use /d #team to delete the local copy of the group" ] + lastJobNotification alice `shouldReturn` True alice ##> "/groups" alice <## "#team (group deleted, delete local copy: /d #team)" @@ -9520,6 +9598,65 @@ testChannels1RelayDeliver ps = cath <## "group ID: 1" cath <## "subscribers: 4" +testChannels1RelayDeliverNotification :: HasCallStack => TestParams -> IO () +testChannels1RelayDeliverNotification ps = + withNewTestChat ps "alice" aliceProfile $ \alice -> do + withNewTestChatOpts ps relayTestOpts "bob" bobProfile $ \bob -> do + withNewTestChat ps "cath" cathProfile $ \cath -> do + withNewTestChat ps "dan" danProfile $ \dan -> do + withNewTestChat ps "eve" eveProfile $ \eve -> do + createChannel1Relay "team" alice bob cath dan eve + forM_ [cath, dan, eve] $ \cc -> do + cc ##> "/set receipts all off" + cc <## "ok" + cathConnId <- relayConnIdToMember bob "cath" + danConnId <- relayConnIdToMember bob "dan" + eveConnId <- relayConnIdToMember bob "eve" + + alice #> "#team hi" + bob <# "#team> hi" + [cath, dan, eve] *<# "#team> hi [>>]" + forM_ [cathConnId, danConnId, eveConnId] $ \connId -> + lastSndMsgFlags bob connId 1 `shouldReturn` ["T"] + lastJobNotification bob `shouldReturn` True + + cath ##> "+1 #team hi" + cath <## "added 👍" + bob <# "#team cath> > hi" + bob <## " + 👍" + alice <# "#team cath> > hi" + alice <## " + 👍" + dan <## "#team: bob introduced cath (Catherine) in the channel" + dan <# "#team cath> > hi" + dan <## " + 👍" + eve <## "#team: bob introduced cath (Catherine) in the channel" + eve <# "#team cath> > hi" + eve <## " + 👍" + forM_ [danConnId, eveConnId] $ \connId -> + lastSndMsgFlags bob connId 1 `shouldReturn` ["F"] + lastJobNotification bob `shouldReturn` False + +testChannelDuplicateEvent :: HasCallStack => TestParams -> IO () +testChannelDuplicateEvent ps = do + (dups, cfg) <- duplicateMessageEventsCfg + withNewTestChat ps "alice" aliceProfile $ \alice -> + withNewTestChatOpts ps relayTestOpts "bob" bobProfile $ \bob -> + withNewTestChatCfg ps cfg "cath" cathProfile $ \cath -> + withNewTestChat ps "dan" danProfile $ \dan -> + withNewTestChat ps "eve" eveProfile $ \eve -> do + createChannel1Relay "team" alice bob cath dan eve + alice #> "#team hi" + bob <# "#team> hi" + [cath, dan, eve] *<# "#team> hi [>>]" + + sharedMsgId <- itemSharedMsgId cath + connId <- relayConnIdToMember bob "cath" + ts <- getCurrentTime + let chatMsg = ChatMessage chatInitialVRange (Just sharedMsgId) (XMsgNew $ mcSimple $ MCText "hi") + setMainAppOff cath + agentSendBody bob connId $ encodeBinaryBatch [encodeFwdElement (GrpMsgForward FwdChannel ts) (VMUnsigned chatMsg)] + readTVarIO dups `shouldEventuallyReturn` ["bob"] + createChannel1Relay :: String -> TestCC -> TestCC -> TestCC -> TestCC -> TestCC -> IO () createChannel1Relay gName owner relay cath dan eve = do (shortLink, fullLink) <- prepareChannel1Relay gName owner relay @@ -9979,6 +10116,34 @@ testChannelLargeProfileFits ps = memberIntroducedTo bob "cath" "dan" +testChannelOverflowProfileNotification :: HasCallStack => TestParams -> IO () +testChannelOverflowProfileNotification ps = + withNewTestChat ps "alice" aliceProfile $ \alice -> do + withNewTestChatOpts ps relayTestOpts "bob" bobProfile $ \bob -> do + withNewTestChat ps "cath" cathProfile $ \cath -> do + withNewTestChat ps "dan" danProfile $ \dan -> do + (shortLink, fullLink) <- prepareChannel1Relay "team" alice bob + memberJoinChannel "team" [bob] [alice] shortLink fullLink cath + memberJoinChannel "team" [bob] [alice] shortLink fullLink dan + promoteChannelMember "team" alice bob cath [dan] + dan ##> "/set receipts all off" + dan <## "ok" + danConnId <- relayConnIdToMember bob "dan" + + let bigImage = T.pack ("data:image/png;base64," <> replicate 10000 'A') + longText = replicate 7000 'x' + withCCTransaction bob $ \db -> + DB.execute db "UPDATE contact_profiles SET image = ? WHERE display_name = ?" (bigImage, "cath" :: T.Text) + sentBefore <- length <$> lastSndMsgFlags bob danConnId 100 + + cath #> ("#team " <> longText) + bob <# ("#team cath> " <> longText) + alice <# ("#team cath> " <> longText <> " [>>]") + dan <### [EndsWith "updated to cath"] + dan <## "#team: bob introduced cath (Catherine) in the channel" + dan <# ("#team cath> " <> longText <> " [>>]") + drop sentBefore <$> lastSndMsgFlags bob danConnId 100 `shouldReturn` ["F", "T"] + testChannelMultipleLargeProfiles :: HasCallStack => TestParams -> IO () testChannelMultipleLargeProfiles ps = withNewTestChat ps "alice" aliceProfile $ \alice -> do @@ -11053,6 +11218,7 @@ testChannelDeleteGroupSigned ps = eve <## "#team: alice deleted the group (signed)" eve <## "use /d #team to delete the local copy of the group" ] + lastJobNotification bob `shouldReturn` True testChannelDeleteGroupCleanup :: HasCallStack => TestParams -> IO () testChannelDeleteGroupCleanup ps = @@ -11246,6 +11412,7 @@ testChannelRelayLeave ps = dan <## "#team: bob left the group (signed)", eve <## "#team: bob left the group (signed)" ] + lastJobNotification bob `shouldReturn` False -- verify relay1 member status is "left" on all clients that know bob checkMemberStatus alice "bob" (Just "left") @@ -13149,6 +13316,39 @@ relayConnIdToMember cc name = do (Only connId : _) -> pure connId _ -> fail $ "no relay connection to member " <> T.unpack name +agentSendBody :: TestCC -> ByteString -> ByteString -> IO () +agentSendBody cc connId body = do + let ChatController {smpAgent} = chatController cc + runExceptT (sendMessages smpAgent [(connId, PQEncOff, MsgFlags False, vrValue body)]) >>= either (fail . show) (const $ pure ()) + +lastSndMsgFlags :: TestCC -> ByteString -> Int -> IO [T.Text] +lastSndMsgFlags cc connId n = do + rows <- withCCAgentTransaction cc $ \db -> + DB.query + db + "SELECT msg_flags FROM messages WHERE conn_id = ? AND internal_snd_id IS NOT NULL AND msg_type = ? ORDER BY internal_id DESC LIMIT ?" + (connId, Binary ("M" :: ByteString), n) + pure $ reverse $ map fromOnly rows + +lastJobNotification :: TestCC -> IO Bool +lastJobNotification cc = do + [Only (BI ntf)] <- withCCTransaction cc $ \db -> + DB.query_ db "SELECT notification FROM delivery_jobs ORDER BY delivery_job_id DESC LIMIT 1" + pure ntf + +duplicateMessageEventsCfg :: IO (TVar [ContactName], ChatConfig) +duplicateMessageEventsCfg = do + dups <- newTVarIO [] + let hook _ evt = do + case evt of + Right (CEvtDuplicateGroupMessage _ GroupMember {localDisplayName}) -> atomically $ modifyTVar' dups (<> [localDisplayName]) + _ -> pure () + pure evt + pure (dups, testCfg {chatHooks = defaultChatHooks {eventHook = Just hook}}) + +setMainAppOff :: TestCC -> IO () +setMainAppOff cc = atomically $ writeTVar (subscriptionMode $ chatController cc) SMOnlyCreate + itemSharedMsgId :: TestCC -> IO SharedMsgId itemSharedMsgId cc = do rows <- withCCTransaction cc $ \db -> From 41f0c429b3d250249b3a748619d96b418c8de882 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Thu, 15 Oct 2026 12:50:38 +0100 Subject: [PATCH 16/21] website: update home page (#7724) * website: update home page * update --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> --- website/langs/ar.json | 10 +- website/langs/cs.json | 10 +- website/langs/de.json | 9 +- website/langs/en.json | 10 +- website/langs/es.json | 9 +- website/langs/fr.json | 9 +- website/langs/hu.json | 9 +- website/langs/id.json | 10 +- website/langs/it.json | 9 +- website/langs/ja.json | 10 +- website/langs/pl.json | 10 +- website/langs/pt_BR.json | 10 +- website/langs/ru.json | 10 +- website/langs/zh_Hans.json | 2 +- website/src/crowdfunding.html | 26 +- website/src/css/design3-nav.css | 14 +- website/src/css/design3.css | 367 +++++++++++++++++++++------ website/src/css/livestream.css | 2 +- website/src/fdroid.html | 5 + website/src/img/new/f_droid_icon.svg | 50 ++++ website/src/index.html | 97 +++---- website/src/js/design3.js | 5 +- website/src/livestream.html | 8 +- 23 files changed, 507 insertions(+), 194 deletions(-) create mode 100644 website/src/img/new/f_droid_icon.svg diff --git a/website/langs/ar.json b/website/langs/ar.json index 39d72effc2..92d7ddb72a 100644 --- a/website/langs/ar.json +++ b/website/langs/ar.json @@ -261,7 +261,13 @@ "about-and-contact-us": "عن واتصل بنا", "index-hero-h1": "كن
    حراً", "index-hero-h2": "في شبكتك", - "index-hero-p1": "أول شبكة بدون معرّفات مستخدمين.
    أنت تملك جهات اتصالك ومجموعاتك وقنواتك.", + "index-hero-p1": "شبكة المراسلة الوحيدة بدون أي معرّفات للمستخدمين.
    أنت تملك جهات اتصالك ومجموعاتك وقنواتك.", + "index-hero-own-part": "امتلك جزءًا منها.", + "index-hero-invest": "استثمر في SimpleX Chat.", + "index-hero-downloads": "تنزيلات", + "index-hero-monthly-users": "مستخدمون شهريًا", + "index-hero-countdown-lead": "إطلاق نطاقات SimpleX", + "index-hero-countdown-done": "احصل على نطاق SimpleX", "index-hero-download-desktop-btn-title": "نزّل تطبيق سطح مكتب SimpleX", "index-testflight-title": "إصدار SimpleX التجريبي ل iOS على TestFlight", "index-f-droid-title": "تطبيق SimpleX من خلال F-Droid", @@ -296,7 +302,7 @@ "index-roadmap-3-title": "اجعل مجتمعاتك تنمو", "index-roadmap-3-desc": "أدوات لتعزيز مجتمعاتك", "index-directory-h2": "انضم إلى مجتمعات SimpleX", - "index-directory-p1": "أكثر من 2 مليون شخص قاموا بتنزيل تطبيقات SimpleX.", + "index-directory-p1": "أكثر من 3 ملايين شخص قاموا بتنزيل تطبيقات SimpleX.", "index-directory-p2": "ابحث عن قنواتك ومجتمعاتك في الدليل وأنشئ قنواتك الخاصة!", "index-directory-cta": "اعرض دليل SimpleX", "index-directory-users-group-title": "مجموعة مستخدمي SimpleX", diff --git a/website/langs/cs.json b/website/langs/cs.json index 0df72eeab9..800578e2c4 100644 --- a/website/langs/cs.json +++ b/website/langs/cs.json @@ -262,7 +262,13 @@ "navbar-token": "Token", "index-hero-h1": "
    Žijte
    svobodně", "index-hero-h2": "Ve Své Síti", - "index-hero-p1": "První síť bez uživatelských ID.
    Vaše kontakty, skupiny a kanály patří vám.", + "index-hero-p1": "Jediná síť pro zasílání zpráv bez jakýchkoli uživatelských identifikátorů.
    Vaše kontakty, skupiny a kanály patří vám.", + "index-hero-own-part": "Vlastněte její část.", + "index-hero-invest": "Investujte do SimpleX Chat.", + "index-hero-downloads": "Stažení", + "index-hero-monthly-users": "Uživatelů měsíčně", + "index-hero-countdown-lead": "Spuštění domén SimpleX", + "index-hero-countdown-done": "Získejte doménu SimpleX", "index-hero-download-desktop-btn-title": "Stáhněte si desktopovou aplikaci SimpleX", "index-testflight-title": "Beta verze SimpleX pro iOS na TestFlight", "index-f-droid-title": "Stáhnout aplikaci SimpleX přes F-Droid", @@ -300,7 +306,7 @@ "index-roadmap-3-title": "Pomozte své komunitě růst", "index-roadmap-3-desc": "Nástroje na podporu vašich komunit", "index-directory-h2": "Zapojte se do komunit SimpleX", - "index-directory-p1": "Více než 2 miliony lidí si stáhly aplikace SimpleX.", + "index-directory-p1": "Více než 3 miliony lidí si stáhly aplikace SimpleX.", "index-directory-p2": "Najděte své kanály a komunity v katalogu a vytvořte si vlastní!", "index-directory-cta": "Zobrazit katalog SimpleX", "index-directory-users-group-title": "Uživatelské skupiny SimpleX", diff --git a/website/langs/de.json b/website/langs/de.json index 34cecb8e4d..0f144010ea 100644 --- a/website/langs/de.json +++ b/website/langs/de.json @@ -261,7 +261,12 @@ "directory": "Verzeichnis", "index-hero-h1": "Sei
    frei", "index-hero-h2": "In Deinem Netzwerk", - "index-hero-p1": "Das erste Netzwerk ohne Benutzer-IDs.
    Ihre Kontakte, Gruppen und Kanäle gehören Ihnen.", + "index-hero-p1": "Das einzige Messaging-Netzwerk ohne jegliche Benutzerkennungen.
    Ihre Kontakte, Gruppen und Kanäle gehören Ihnen.", + "index-hero-own-part": "Besitzen Sie einen Teil davon.", + "index-hero-downloads": "Downloads", + "index-hero-monthly-users": "Monatliche Nutzer", + "index-hero-countdown-lead": "Start der SimpleX-Domains", + "index-hero-countdown-done": "SimpleX-Domain sichern", "index-hero-download-desktop-btn-title": "Download der SimpleX Desktop-App", "index-testflight-title": "Öffentlicher iOS-Preview auf TestFlight", "index-f-droid-title": "SimpleX-App über das F-Droid-Repository", @@ -299,7 +304,7 @@ "index-roadmap-3-title": "Lassen Sie Ihre Communitys wachsen", "index-roadmap-3-desc": "Tools zur Förderung Ihrer Communitys", "index-directory-h2": "Treten Sie SimpleX-Communitys bei", - "index-directory-p1": "Mehr als 2 Millionen Nutzer haben schon SimpleX-Apps heruntergeladen.", + "index-directory-p1": "Mehr als 3 Millionen Nutzer haben schon SimpleX-Apps heruntergeladen.", "index-directory-p2": "Finden Sie Kanäle und Communitys im Verzeichnis oder erstellen Sie Ihre Eigenen!", "index-directory-cta": "SimpleX-Verzeichnis anzeigen", "index-directory-users-group-title": "SimpleX-Nutzergruppe", diff --git a/website/langs/en.json b/website/langs/en.json index 6138b34c8c..1293d83aff 100644 --- a/website/langs/en.json +++ b/website/langs/en.json @@ -255,6 +255,7 @@ "stable-and-beta-versions-built-by-developers": "Stable and beta versions built by the developers", "f-droid-page-simplex-chat-repo-section-text": "To add it to your F-Droid client, scan the QR code or use this URL:", "signing-key-fingerprint": "Signing key fingerprint (SHA-256)", + "f-droid-page-apk-text": "Or download the APK directly:", "f-droid-org-repo": "F-Droid.org repo", "stable-versions-built-by-f-droid-org": "Stable versions built by F-Droid.org", "releases-to-this-repo-are-done-1-2-days-later": "The releases to this repo are done several days later", @@ -264,9 +265,14 @@ "please-use-link-in-mobile-app": "Please use the link in the mobile app", "index-hero-h1": "Be
    Free", "index-hero-h2": "In Your Network", - "index-hero-p1": "The first network without user IDs.
    You own your contacts, groups and channels.", + "index-hero-p1": "The only messaging network without any user identifiers.
    You own your contacts, groups and channels.", "index-hero-invest": "Invest in SimpleX Chat.", "index-hero-invest-cta": "Learn more on Wefunder.", + "index-hero-own-part": "Own a part of it.", + "index-hero-downloads": "Downloads", + "index-hero-monthly-users": "Monthly users", + "index-hero-countdown-lead": "SimpleX domains launch", + "index-hero-countdown-done": "Get SimpleX domain", "index-hero-download-desktop-btn-title": "Download SimpleX Desktop App", "index-testflight-title": "SimpleX iOS beta-release on TestFlight", "index-f-droid-title": "SimpleX app via F-Droid", @@ -304,7 +310,7 @@ "index-roadmap-3-title": "Make Your Communities Grow", "index-roadmap-3-desc": "Tools to promote your communities", "index-directory-h2": "Join SimpleX Communities", - "index-directory-p1": "More than 2 million people downloaded SimpleX apps.", + "index-directory-p1": "More than 3 million people downloaded SimpleX apps.", "index-directory-p2": "Find your channels and communities in directory and create your own!", "index-directory-cta": "View SimpleX Directory", "index-directory-users-group-title": "SimpleX users group", diff --git a/website/langs/es.json b/website/langs/es.json index 22b69cfbeb..1e9d4e6159 100644 --- a/website/langs/es.json +++ b/website/langs/es.json @@ -261,7 +261,12 @@ "about-and-contact-us": "Quiénes somos & Contacto", "index-hero-h1": "Sé
    Libre", "index-hero-h2": "En Tu Red", - "index-hero-p1": "La primera red sin IDs de usuario.
    Tus contactos, grupos y canales te pertenecen.", + "index-hero-p1": "La única red de mensajería sin ningún identificador de usuario.
    Tus contactos, grupos y canales te pertenecen.", + "index-hero-own-part": "Hazte dueño de una parte.", + "index-hero-downloads": "Descargas", + "index-hero-monthly-users": "Usuarios mensuales", + "index-hero-countdown-lead": "Lanzamiento de dominios SimpleX", + "index-hero-countdown-done": "Obtén un dominio SimpleX", "index-hero-download-desktop-btn-title": "Descargar SimpleX Desktop App", "index-testflight-title": "Betas SimpleX para iOS en TestFlight", "index-f-droid-title": "SimpleX app vía F-Droid", @@ -299,7 +304,7 @@ "index-roadmap-3-title": "Haz Crecer Tus Comunidades", "index-roadmap-3-desc": "Herramientas para la promoción", "index-directory-h2": "Únete a las Comunidades SimpleX", - "index-directory-p1": "Más de 2 millones de personas descargaron las aplicaciones SimpleX.", + "index-directory-p1": "Más de 3 millones de personas descargaron las aplicaciones SimpleX.", "index-directory-p2": "¡Encuentra tus canales y comunidades en el directorio y crea los tuyos!", "index-directory-cta": "Ir al Directorio SimpleX", "index-directory-users-group-title": "SimpleX users group", diff --git a/website/langs/fr.json b/website/langs/fr.json index 124c7ddc67..b53bd0a14e 100644 --- a/website/langs/fr.json +++ b/website/langs/fr.json @@ -263,7 +263,12 @@ "about-and-contact-us": "À propos & Contactez-nous", "index-hero-h1": "Soyez
    Libre", "index-hero-h2": "Dans Votre Réseau", - "index-hero-p1": "Le premier réseau sans identifiants d'utilisateur.
    Vos contacts, groupes et canaux vous appartiennent.", + "index-hero-p1": "Le seul réseau de messagerie sans aucun identifiant d'utilisateur.
    Vos contacts, groupes et canaux vous appartiennent.", + "index-hero-own-part": "Possédez-en une part.", + "index-hero-downloads": "Téléchargements", + "index-hero-monthly-users": "Utilisateurs mensuels", + "index-hero-countdown-lead": "Lancement des domaines SimpleX", + "index-hero-countdown-done": "Obtenez un domaine SimpleX", "index-hero-download-desktop-btn-title": "Téléchargez l’application de bureau SimpleX", "index-testflight-title": "Version bêta de SimpleX pour iOS sur TestFlight", "index-f-droid-title": "Application SimpleX via F-Droid", @@ -301,7 +306,7 @@ "index-token-cta-cf": "Investir dans SimpleX Chat", "index-token-p2": "Cela couvrira l'infrastructure, le développement logiciel et la gouvernance du réseau.", "index-directory-h2": "Rejoindre les communautés SimpleX", - "index-directory-p1": "Plus de 2 millions de personnes ont téléchargé les applications SimpleX.", + "index-directory-p1": "Plus de 3 millions de personnes ont téléchargé les applications SimpleX.", "index-directory-p2": "Trouvez vos chaînes et communautés dans l'annuaire et créez-en les vôtres !", "index-directory-cta": "Afficher l’annuaire SimpleX", "index-directory-users-group-title": "Groupe d'utilisateurs SimpleX", diff --git a/website/langs/hu.json b/website/langs/hu.json index 1b3a4f5869..fec3633ed9 100644 --- a/website/langs/hu.json +++ b/website/langs/hu.json @@ -260,7 +260,12 @@ "directory": "Csoportjegyzék", "about-and-contact-us": "Névjegy és kapcsolat", "index-hero-h1": "
    Legyen
    szabad
    ", - "index-hero-p1": "Az első hálózat felhasználói azonosítók nélkül.
    A saját partnerei, csoportjai és csatornái felett Ön rendelkezik.", + "index-hero-p1": "Az egyetlen üzenetküldő hálózat semmilyen felhasználói azonosító nélkül.
    A saját partnerei, csoportjai és csatornái felett Ön rendelkezik.", + "index-hero-own-part": "Legyen résztulajdonos.", + "index-hero-downloads": "Letöltés", + "index-hero-monthly-users": "Havi felhasználó", + "index-hero-countdown-lead": "A SimpleX domainek indulása", + "index-hero-countdown-done": "Szerezzen SimpleX domaint", "index-hero-download-desktop-btn-title": "SimpleX számítógépes alkalmazásának letöltése", "index-security-assessment-title": "Biztonsági auditok", "index-security-review-2022-title": "Biztonsági audit 2022", @@ -299,7 +304,7 @@ "index-roadmap-3-title": "Közösségek növelése", "index-roadmap-3-desc": "Eszközök biztosítása a közösségek népszerűsítéséhez", "index-directory-h2": "Csatlakozzon a SimpleX közösségekhez", - "index-directory-p1": "Több mint 2 millió ember töltötte le a SimpleX alkalmazások egyikét.", + "index-directory-p1": "Több mint 3 millió ember töltötte le a SimpleX alkalmazások egyikét.", "index-directory-p2": "Találja meg a kedvenc csatornáit és közösségeit a csoportjegyzékben, vagy hozza létre a sajátját!", "index-directory-cta": "SimpleX-csoportjegyzék megtekintése", "index-directory-users-group-title": "SimpleX felhasználók csoportja", diff --git a/website/langs/id.json b/website/langs/id.json index b1134bcaff..a88a5aaff3 100644 --- a/website/langs/id.json +++ b/website/langs/id.json @@ -261,7 +261,13 @@ "get-simplex": "Dapatkan aplikasi desktop SimpleX", "index-hero-h1": "
    Bebaslah
    ", "index-hero-h2": "Di Jaringan Anda", - "index-hero-p1": "Jaringan pertama tanpa ID pengguna.
    Anda pemilik kontak, grup, dan kanal Anda.", + "index-hero-p1": "Satu-satunya jaringan pesan tanpa pengenal pengguna apa pun.
    Anda pemilik kontak, grup, dan kanal Anda.", + "index-hero-own-part": "Miliki sebagian darinya.", + "index-hero-invest": "Berinvestasi di SimpleX Chat.", + "index-hero-downloads": "Unduhan", + "index-hero-monthly-users": "Pengguna bulanan", + "index-hero-countdown-lead": "Peluncuran domain SimpleX", + "index-hero-countdown-done": "Dapatkan domain SimpleX", "index-hero-download-desktop-btn-title": "Unduh Aplikasi Desktop SimpleX", "index-testflight-title": "Pratinjau iOS publik di TestFlight", "index-f-droid-title": "Aplikasi SimpleX via F-Droid", @@ -299,7 +305,7 @@ "index-roadmap-3-title": "Buat Komunitas Anda Meningkat", "index-roadmap-3-desc": "Alat untuk promosikan komunitas Anda", "index-directory-h2": "Gabung ke Komunitas SimpleX", - "index-directory-p1": "Lebih dari 2 juta orang telah mengunduh aplikasi SimpleX.", + "index-directory-p1": "Lebih dari 3 juta orang telah mengunduh aplikasi SimpleX.", "index-directory-p2": "Temukan kanal dan komunitas Anda di direktori dan buat milik Anda sendiri!", "index-directory-cta": "Lihat Direktori SimpleX", "index-directory-users-group-title": "Grup pengguna SimpleX", diff --git a/website/langs/it.json b/website/langs/it.json index 649637b8e2..013b3be7f0 100644 --- a/website/langs/it.json +++ b/website/langs/it.json @@ -260,7 +260,12 @@ "about-and-contact-us": "Informazioni e contatti", "directory": "Directory", "index-hero-h2": "nella tua rete", - "index-hero-p1": "La prima rete senza ID utente.
    I tuoi contatti, gruppi e canali sono tuoi.", + "index-hero-p1": "L'unica rete di messaggistica senza alcun identificatore utente.
    I tuoi contatti, gruppi e canali sono tuoi.", + "index-hero-own-part": "Possiedine una parte.", + "index-hero-downloads": "Download", + "index-hero-monthly-users": "Utenti mensili", + "index-hero-countdown-lead": "Lancio dei domini SimpleX", + "index-hero-countdown-done": "Ottieni un dominio SimpleX", "index-hero-download-desktop-btn-title": "Scarica l'app desktop di SimpleX", "index-testflight-title": "Anteprima pubblica per iOS su TestFlight", "index-f-droid-title": "SimpleX via F-Droid", @@ -299,7 +304,7 @@ "index-roadmap-3-title": "Fare crescere le tue comunità", "index-roadmap-3-desc": "Strumenti per promuovere le tue comunità", "index-directory-h2": "Unisciti alle comunità di SimpleX", - "index-directory-p1": "Più di 2 milioni di persone hanno scaricato le app SimpleX.", + "index-directory-p1": "Più di 3 milioni di persone hanno scaricato le app SimpleX.", "index-directory-p2": "Trova i tuoi canali e le comunità nella directory e creane di tuoi!", "index-directory-cta": "Vedi la directory di SimpleX", "index-directory-users-group-title": "Gruppo utenti SimpleX", diff --git a/website/langs/ja.json b/website/langs/ja.json index 4c850853e4..af2e29e26f 100644 --- a/website/langs/ja.json +++ b/website/langs/ja.json @@ -261,7 +261,13 @@ "about-and-contact-us": "概要・お問い合わせ", "index-hero-h1": "自由で
    あれ", "index-hero-h2": "あなたのネットワークで", - "index-hero-p1": "ユーザーIDのない世界初のネットワーク。
    連絡先、グループ、チャンネルはあなたのものです。", + "index-hero-p1": "ユーザー識別子を一切持たない唯一のメッセージングネットワーク。
    連絡先、グループ、チャンネルはあなたのものです。", + "index-hero-own-part": "その一部を所有しよう。", + "index-hero-invest": "SimpleX Chatに投資しよう。", + "index-hero-downloads": "ダウンロード", + "index-hero-monthly-users": "月間ユーザー", + "index-hero-countdown-lead": "SimpleXドメイン公開", + "index-hero-countdown-done": "SimpleXドメインを取得", "index-hero-download-desktop-btn-title": "SimpleX デスクトップアプリをダウンロード", "index-security-assessment-title": "セキュリティ監査", "index-security-review-2022-title": "セキュリティ監査 2022", @@ -291,7 +297,7 @@ "index-roadmap-3-title": "コミュニティの成長", "index-roadmap-3-desc": "コミュニティを広げるツール", "index-directory-h2": "SimpleXコミュニティに参加する", - "index-directory-p1": "200万人以上がSimpleXアプリをダウンロードしました。", + "index-directory-p1": "300万人以上がSimpleXアプリをダウンロードしました。", "index-directory-p2": "ディレクトリでチャンネルやコミュニティを見つけて、あなた自身のものを作成しましょう!", "index-directory-cta": "SimpleXディレクトリを見る", "index-directory-users-group-title": "SimpleXユーザグループ", diff --git a/website/langs/pl.json b/website/langs/pl.json index a760dc34cf..90690c2c1c 100644 --- a/website/langs/pl.json +++ b/website/langs/pl.json @@ -262,7 +262,13 @@ "about-and-contact-us": "O nas i Kontakt", "index-hero-h1": "Bądź
    Wolny", "index-hero-h2": "W Swojej Sieci", - "index-hero-p1": "Pierwsza sieć bez identyfikatorów użytkowników.
    Twoje kontakty, grupy i kanały należą do Ciebie.", + "index-hero-p1": "Jedyna sieć komunikacyjna bez żadnych identyfikatorów użytkowników.
    Twoje kontakty, grupy i kanały należą do Ciebie.", + "index-hero-own-part": "Miej w niej udział.", + "index-hero-invest": "Zainwestuj w SimpleX Chat.", + "index-hero-downloads": "Pobrań", + "index-hero-monthly-users": "Użytkowników miesięcznie", + "index-hero-countdown-lead": "Start domen SimpleX", + "index-hero-countdown-done": "Zdobądź domenę SimpleX", "index-hero-download-desktop-btn-title": "Pobierz Aplikację SimpleX na Komputer", "index-testflight-title": "SimpleX iOS beta-wydanie na TestFlight", "index-f-droid-title": "SimpleX app z F-Droid", @@ -300,7 +306,7 @@ "index-roadmap-3-title": "Spraw, aby Twoje społeczności Rosły", "index-roadmap-3-desc": "Narzędzia do promowania Twoich społeczności", "index-directory-h2": "Dołącz do Społeczności SimpleX", - "index-directory-p1": "Ponad 2 miliony osób pobrało aplikacje SimpleX.", + "index-directory-p1": "Ponad 3 miliony osób pobrało aplikacje SimpleX.", "index-directory-p2": "Znajdź swoje kanały i społeczności w katalogu i stwórz własne!", "index-directory-cta": "Zobacz katalog SimpleX", "index-directory-users-group-title": "Grupa użytkowników SimpleX", diff --git a/website/langs/pt_BR.json b/website/langs/pt_BR.json index 4b663e75ec..22f7a1dc45 100644 --- a/website/langs/pt_BR.json +++ b/website/langs/pt_BR.json @@ -262,7 +262,13 @@ "navbar-token": "Token", "index-hero-h1": "Seja
    Livre", "index-hero-h2": "Na Sua Rede", - "index-hero-p1": "A primeira rede sem IDs de usuário.
    Seus contatos, grupos e canais pertencem a você.", + "index-hero-p1": "A única rede de mensagens sem nenhum identificador de usuário.
    Seus contatos, grupos e canais pertencem a você.", + "index-hero-own-part": "Seja dono de uma parte dela.", + "index-hero-invest": "Invista no SimpleX Chat.", + "index-hero-downloads": "Downloads", + "index-hero-monthly-users": "Usuários mensais", + "index-hero-countdown-lead": "Lançamento de domínios SimpleX", + "index-hero-countdown-done": "Obtenha um domínio SimpleX", "index-hero-download-desktop-btn-title": "Baixe o aplicativo SimpleX Desktop", "index-testflight-title": "SimpleX iOS - versão beta no TestFlight", "index-f-droid-title": "Aplicativo SimpleX no F-Droid", @@ -300,7 +306,7 @@ "index-roadmap-3-title": "Faça Suas Comunidades Crescerem", "index-roadmap-3-desc": "Ferramentas para promover suas comunidades", "index-directory-h2": "Participe das Comunidades SimpleX", - "index-directory-p1": "Mais de 2 milhões de pessoas baixaram os aplicativos SimpleX.", + "index-directory-p1": "Mais de 3 milhões de pessoas baixaram os aplicativos SimpleX.", "index-directory-p2": "Encontre seus canais e comunidades no diretório e crie os seus próprios!", "index-directory-cta": "Ver diretório do SimpleX", "how-secure-comparison-title": "Comparação da segurança da criptografia de ponta a ponta em diferentes mensageiros", diff --git a/website/langs/ru.json b/website/langs/ru.json index a8bdfa8725..687e5481b2 100644 --- a/website/langs/ru.json +++ b/website/langs/ru.json @@ -261,7 +261,13 @@ "docs-dropdown-14": "SimpleX для бизнеса", "index-hero-h1": "
    Будь
    Свободен
    ", "index-hero-h2": "В Своей Сети", - "index-hero-p1": "Первая сеть без идентификаторов пользователей.
    Ваши контакты, группы и каналы принадлежат Вам.", + "index-hero-p1": "Единственная сеть обмена сообщениями без идентификаторов пользователей.
    Ваши контакты, группы и каналы принадлежат Вам.", + "index-hero-own-part": "Владейте её частью.", + "index-hero-invest": "Инвестируйте в SimpleX Chat.", + "index-hero-downloads": "Загрузок", + "index-hero-monthly-users": "Пользователей в месяц", + "index-hero-countdown-lead": "Запуск доменов SimpleX", + "index-hero-countdown-done": "Получите домен SimpleX", "index-hero-download-desktop-btn-title": "Загрузить приложение SimpleX для компьютера", "index-testflight-title": "Бета-релиз для iOS на TestFlight", "index-f-droid-title": "Загрузить через F-Droid", @@ -299,7 +305,7 @@ "index-roadmap-3-title": "Поддержка роста Ваших групп", "index-roadmap-3-desc": "Инструменты для продвижения групп", "index-directory-h2": "Вступайте в Группы SimpleX", - "index-directory-p1": "Более 2 миллионов человек скачали приложения SimpleX.", + "index-directory-p1": "Более 3 миллионов человек скачали приложения SimpleX.", "index-directory-p2": "Найдите каналы и сообщества в каталоге и создайте свои!", "index-directory-cta": "Открыть каталог SimpleX", "index-directory-users-group-title": "Группа пользователей SimpleX", diff --git a/website/langs/zh_Hans.json b/website/langs/zh_Hans.json index 45cee06f5a..ac7afb074e 100644 --- a/website/langs/zh_Hans.json +++ b/website/langs/zh_Hans.json @@ -296,7 +296,7 @@ "index-roadmap-3-title": "促进社区发展", "index-roadmap-3-desc": "用于推广社区的工具", "index-directory-h2": "加入 SimpleX 社区", - "index-directory-p1": "超过 200 万人下载了 SimpleX 应用。", + "index-directory-p1": "超过 300 万人下载了 SimpleX 应用。", "index-directory-p2": "在目录中找到您的频道和社区,并创建您自己的!", "index-directory-cta": "查看 SimpleX 目录", "index-directory-users-group-title": "SimpleX 用户群组", diff --git a/website/src/crowdfunding.html b/website/src/crowdfunding.html index 3b0b293f02..11d28742b9 100644 --- a/website/src/crowdfunding.html +++ b/website/src/crowdfunding.html @@ -65,7 +65,7 @@ templateEngineOverride: njk --cf-area-off: calc((var(--sec-h) - 100svh) / 2); } - @media screen and (max-width: 959px) and (orientation: portrait) { + @media screen and (max-width: 1015px) and (orientation: portrait) { :root { --cf-grad-light: linear-gradient(30deg, #e8f3ff 0%, #c0e2ff 40%, #e9ffff 80%, #ffefd6 90%); --cf-grad-dark: linear-gradient(30deg, #000000 0%, #131d49 52%, #3f5598 65%, #c3faff 85%, #fff6e0 90%); @@ -379,7 +379,7 @@ templateEngineOverride: njk .cf-pill svg { height: 30px; width: auto; display: block; transform: translateY(-3.5px); } - @media screen and (min-width: 960px), screen and (max-width: 959px) and (orientation: landscape) { + @media screen and (min-width: 1016px), screen and (max-width: 1015px) and (orientation: landscape) { .cf-pill { width: 252px; justify-content: center; } } @@ -389,7 +389,7 @@ templateEngineOverride: njk .cf-cta-link:hover, .cf .text-container a.gradient-text:hover { filter: brightness(1.12); } - @media screen and (min-width: 960px), screen and (max-width: 959px) and (orientation: landscape) { + @media screen and (min-width: 1016px), screen and (max-width: 1015px) and (orientation: landscape) { .cf-network .text-container h2 { max-width: calc(var(--sec-vwu) * 23.5) !important; } .cf-network .text-container p { max-width: calc(var(--sec-vwu) * 31) !important; } .cf-network .text-container a { max-width: calc(var(--sec-vwu) * 31) !important; } @@ -429,7 +429,7 @@ templateEngineOverride: njk #navbar.on-light > a.logo.logo-light { display: flex; } #navbar.on-light > a.logo.logo-dark { display: none; } - @media screen and (min-width: 960px), screen and (max-width: 959px) and (orientation: landscape) { + @media screen and (min-width: 1016px), screen and (max-width: 1015px) and (orientation: landscape) { header#navbar { height: 74px; } header#navbar > a.logo { top: 0; @@ -443,7 +443,7 @@ templateEngineOverride: njk } /* keep the navbar visible on mobile (no slide-in menu here) */ - @media screen and (max-width: 959px) { + @media screen and (max-width: 1015px) { header#navbar { visibility: visible !important; opacity: 1 !important; @@ -451,7 +451,7 @@ templateEngineOverride: njk background: transparent !important; } } - @media screen and (max-width: 959px) and (orientation: portrait) { + @media screen and (max-width: 1015px) and (orientation: portrait) { header#navbar { height: 54px !important; } header#navbar > a.logo { top: 0 !important; height: 100% !important; align-items: center; transform: translateY(var(--cf-nav-shift)); } [dir="ltr"] header#navbar > a.logo { padding-left: var(--cf-text-pad); } @@ -459,7 +459,7 @@ templateEngineOverride: njk } /* ---- mobile: portrait art on top, text at the bottom (home page system) ---- */ - @media screen and (max-width: 959px) and (orientation: portrait) { + @media screen and (max-width: 1015px) and (orientation: portrait) { .cf .text-container h2 { font-size: calc(var(--sec-vwu) * 9.4); } .cf .text-container { margin-left: 0 !important; @@ -751,15 +751,15 @@ templateEngineOverride: njk .cf-tag .n { font-weight: 700; font-size: calc(var(--sec-vwu) * 1.5); line-height: 1.35; } .cf-tag .v { font-weight: 500; font-size: calc(var(--sec-vwu) * 1.2); line-height: 1.45; } .cf-tag .v b { font-weight: 700; } - @media screen and (min-width: 960px), screen and (max-width: 959px) and (orientation: landscape) { + @media screen and (min-width: 1016px), screen and (max-width: 1015px) and (orientation: landscape) { .cf-tags-art, .cf-art { display: none; } } - @media screen and (max-width: 959px) and (orientation: portrait) { + @media screen and (max-width: 1015px) and (orientation: portrait) { .cf-tag .n { font-size: 3.54cqh; line-height: 1.3; } } - @media screen and (max-width: 959px) and (orientation: portrait) and (max-aspect-ratio: 62/100) { + @media screen and (max-width: 1015px) and (orientation: portrait) and (max-aspect-ratio: 62/100) { .cf-hero h1 { font-size: calc(var(--sec-vwu) * 9.6); } :root { --cf-hero-text-b: calc(var(--cf-top-base) + max(0px, calc(-1 * var(--cf-area-off))) + 72.8vw); } } @@ -800,7 +800,7 @@ templateEngineOverride: njk .cf-fine-links a { color: #9fb2d8; text-decoration: none; } .cf-fine-links a:hover { color: #ffffff; } - @media screen and (max-width: 959px) and (orientation: portrait) { + @media screen and (max-width: 1015px) and (orientation: portrait) { /* padding-bottom comes from .safari-ios section.page, which a fit-content footer does not need */ section.page.cf.cf-footer { height: fit-content; min-height: fit-content; padding-bottom: 0; } @@ -935,7 +935,7 @@ templateEngineOverride: njk .cf-modal-panel { padding: 28px 20px; } .cf-form-submit { padding: 0 12px; font-size: 15px; } } - @media screen and (max-width: 959px) and (orientation: landscape) { + @media screen and (max-width: 1015px) and (orientation: landscape) { :root { --cover-bg-w: calc(min((3200 / 1920) * 100vw, (3200 / 1080) * 100svh)); --cover-bg-h: calc(min((1800 / 1920) * 100vw, (1800 / 1080) * 100svh)); @@ -1322,7 +1322,7 @@ templateEngineOverride: njk var hero = block.closest('section'); var leadShort = document.querySelector('.cf-hero .lead-short'); var logos = document.querySelectorAll('#navbar .logo img'); - var narrow = window.matchMedia('(max-width: 959px) and (orientation: portrait)'); + var narrow = window.matchMedia('(max-width: 1015px) and (orientation: portrait)'); function logoBottom() { var bottom = 0; for (var i = 0; i < logos.length; i++) { var r = logos[i].getBoundingClientRect(); if (r.height > 0) bottom = Math.max(bottom, r.bottom); } diff --git a/website/src/css/design3-nav.css b/website/src/css/design3-nav.css index f6f91a8567..1e3883caaa 100644 --- a/website/src/css/design3-nav.css +++ b/website/src/css/design3-nav.css @@ -10,7 +10,7 @@ body.change-nav-color { --nav-color: #001796; } -@media screen and (min-width: 960px) { +@media screen and (min-width: 1016px) { .logo { background: rgba(255, 255, 255, 0.05); backdrop-filter: blur(10px); @@ -182,7 +182,7 @@ header#navbar ul a { white-space: nowrap; } -@media screen and (min-width: 960px) { +@media screen and (min-width: 1016px) { header#navbar ul .nav-link a, header#navbar ul .nav-link-text { font-size: 18px; @@ -258,7 +258,7 @@ header#navbar ul a span svg { fill: #fff; } -@media screen and (min-width: 960px) { +@media screen and (min-width: 1016px) { header#navbar nav#menu>ul>li>a { color: var(--nav-color); } @@ -366,7 +366,7 @@ header#navbar .nav-link:focus-within .sub-menu { fill: white; } -@media screen and (min-width: 960px) { +@media screen and (min-width: 1016px) { .flag-container>a { color: var(--nav-color) !important; } @@ -427,7 +427,7 @@ header#navbar button.theme-switch-btn svg path { fill: white; } -@media screen and (min-width: 960px) { +@media screen and (min-width: 1016px) { header#navbar button.theme-switch-btn svg path { fill: var(--nav-color) !important; transition: all 0.3s ease; @@ -497,7 +497,7 @@ button#cross-btn { height: auto; } -@media (min-width: 960px) { +@media (min-width: 1016px) { @media (max-width: 1050px) { header#navbar nav#menu .nav-link.send-file { display: none; @@ -505,7 +505,7 @@ button#cross-btn { } } -@media (max-width: 959px) { +@media (max-width: 1015px) { #mobile-header { position: fixed; top: 0; diff --git a/website/src/css/design3.css b/website/src/css/design3.css index 4b645804eb..6ebbf92198 100644 --- a/website/src/css/design3.css +++ b/website/src/css/design3.css @@ -171,7 +171,7 @@ #fff6e0 90%); } -@media screen and (max-width: 959px) { +@media screen and (max-width: 1015px) { :root { --bg-grad: linear-gradient(30deg, #e8f3ff 0%, @@ -225,7 +225,7 @@ body { background: #0a0f2b; } -@media screen and (min-width: 960px) { +@media screen and (min-width: 1016px) { html { overscroll-behavior: none; } @@ -364,55 +364,104 @@ section.page .area { /* --- COVER --- */ section.cover { + --cover-area-off: calc((var(--sec-h) - 100svh) / 2); + --cover-fade-rgb: 33, 70, 110; background-image: url("/img/design_3/cover-light.webp"); background-size: var(--cover-bg-w) var(--cover-bg-h); - background-position: center; + background-position: calc(50% + var(--sec-vwu) * 8.5) center; background-repeat: no-repeat; + overflow: hidden; z-index: 12; } .dark section.cover { + --cover-fade-rgb: 2, 7, 29; background-image: url("/img/design_3/cover.webp"); } .chrome-ios section.cover, .safari-ios section.cover { - background-position: center calc(50% - calc(110lvh - 100svh) / 2); + --cover-bottom-ext: calc(110lvh - 100svh); + background-position: calc(50% + var(--sec-vwu) * 8.5) calc(50% - calc(110lvh - 100svh) / 2); } .chrome-android section.cover { - background-position: center calc(50% - calc(100lvh - 100svh) / 2); + --cover-bottom-ext: calc(100lvh - 100svh); + background-position: calc(50% + var(--sec-vwu) * 8.5) calc(50% - calc(100lvh - 100svh) / 2); +} + +.cover-art { + position: absolute; + width: var(--sec-w); + height: var(--sec-h); + z-index: 1; + pointer-events: none; +} + +.cover-phone { + position: absolute; + right: calc(var(--sec-vwu) * 4); + bottom: calc(var(--sec-vhu) * -55.56); + height: calc(var(--sec-vhu) * 129.96); + width: auto; +} + +.cover-fade { + position: absolute; + right: calc(var(--sec-vwu) * 4); + width: calc(var(--sec-vhu) * 129.96 * 0.4914); + bottom: var(--cover-area-off); + height: calc(var(--sec-vhu) * 38); + background: linear-gradient(to top, rgb(var(--cover-fade-rgb)) 22%, rgba(var(--cover-fade-rgb), .8) 52%, rgba(var(--cover-fade-rgb), 0) 100%); } section.cover div.content { display: flex; flex-direction: column; justify-content: center; - align-items: center; - text-align: center; + align-items: flex-start; + text-align: start; gap: calc(var(--sec-vhu) * 3.5); width: var(--sec-w); height: var(--sec-h); + padding-left: calc(var(--sec-vwu) * 4); +} + +@media (min-width: 1016px), (max-width: 1015px) and (orientation: landscape) { + [dir="rtl"] section.cover div.content { + padding-left: 0; + padding-right: calc(var(--sec-vwu) * 4); + } + + [dir="rtl"] .cover-phone, + [dir="rtl"] .cover-fade { + left: calc(var(--sec-vwu) * 4); + right: auto; + } + + [dir="rtl"] .cover-stats { + left: 30px; + right: auto; + } } section.cover div.content h1 { font-family: "GT-Walsheim", sans-serif; font-weight: 700; - font-size: calc(var(--sec-vwu) * 11.7); + font-size: calc(var(--sec-vwu) * 10); line-height: 0.9; letter-spacing: -0.025em; color: #ffffff; - text-align: center; } section.cover div.content h1 .small { - font-size: calc(var(--sec-vwu) * 8); + font-size: calc(var(--sec-vwu) * 6.85); line-height: 1; margin-bottom: -10px; } section.cover div.content h1 .medium { - font-size: calc(var(--sec-vwu) * 10); + font-size: calc(var(--sec-vwu) * 8.55); line-height: 1; margin-bottom: -10px; } @@ -431,7 +480,12 @@ section.cover div.content p { font-size: calc(var(--sec-vwu) * 2.14); align-items: center; color: #ffffff; - max-width: calc(var(--sec-vwu) * 53); + max-width: calc(var(--sec-vwu) * 55); + text-wrap: balance; +} + +section.cover div.content p .nowrap { + white-space: nowrap; } section.cover div.content p.invest { @@ -444,39 +498,65 @@ section.cover div.content p.invest a { white-space: nowrap; } -.publications-btns { +html:not(.dark) section.cover div.content p { + font-weight: 300; + max-width: calc(var(--sec-vwu) * 55.2); +} + +html:not(.dark) section.cover div.content p.invest { + font-weight: 400; +} + +html:not(.dark) section.cover div.content p.invest a { + font-weight: 600; +} + +html:not(.dark) section.cover div.content .gradient-text { + background: linear-gradient(90deg, + #ffb55d 0%, + #fff494 50%, + #fbffdd 100%); + -webkit-background-clip: text; + background-clip: text; +} + +.cover-stats { position: absolute; - bottom: 24px; - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: center; - gap: 12px; -} - -[dir="ltr"] .publications-btns { - left: 30px; -} - -[dir="rtl"] .publications-btns { right: 30px; + bottom: calc(74px + var(--cover-bottom-ext, 0px)); + display: flex; + align-items: center; + gap: 48px; } -.publications-btns img { - width: 32px; +.cover-stat { + display: flex; + align-items: center; + gap: 13px; } -.publications-btns img.kuketz { - width: 38px; +.cover-stat b { + font-family: "Manrope", sans-serif; + font-weight: 700; + font-size: 37px; + line-height: 1.1; + color: #ffffff; } -.publications-btns img.whonix { - width: 30px; +.cover-stat span { + font-family: "Manrope", sans-serif; + font-weight: 400; + font-size: 18px; + color: #ffffff; +} + +.dark .cover-stat span { + color: #dfeaff; } .security-btns { position: absolute; - bottom: 24px; + bottom: calc(24px + var(--cover-bottom-ext, 0px)); display: flex; flex-wrap: wrap; align-items: center; @@ -485,13 +565,11 @@ section.cover div.content p.invest a { } [dir="ltr"] .security-btns { - left: 0; - transform: translateX(calc((100vw / 2) - 50%)); + left: 30px; } [dir="rtl"] .security-btns { - right: 0; - transform: translateX(calc(-1 * ((100vw / 2) - 50%))); + right: 30px; } .security-btns img { @@ -514,26 +592,9 @@ section.cover div.content p.invest a { text-align: right; } -@media screen and (max-width: 1279px) { - .publications-btns { - display: none; - visibility: hidden; - } - - [dir="ltr"] .security-btns { - transform: translateX(0); - left: 16px; - } - - [dir="rtl"] .security-btns { - transform: translateX(0); - right: 16px; - } -} - .socials { position: absolute; - bottom: 22px; + bottom: calc(22px + var(--cover-bottom-ext, 0px)); display: flex; align-items: center; justify-content: center; @@ -541,7 +602,7 @@ section.cover div.content p.invest a { flex-wrap: wrap; } -@media (min-width: 960px) { +@media (min-width: 1016px) { .socials .desktop-app-btn, .socials .apple-store-btn, .socials .google-play-btn { @@ -603,14 +664,7 @@ section.cover div.content p.invest a { gap: 6px; } -@media screen and (max-width: 959px) { - - .publications-btns, - .security-btns, - .socials { - position: static; - } - +@media screen and (max-width: 1015px) and (orientation: portrait) { .socials { min-height: 42px; } @@ -722,7 +776,7 @@ main .section-bg { background-image: url("/img/design_3/section-5-desktop.webp"); } -@media screen and (max-width: 959px) { +@media screen and (max-width: 1015px) { .page-2 { background-image: url("/img/design_3/section-2-mobile-light.webp"); background-size: auto calc(var(--cover-bg-h) * 0.84); @@ -967,41 +1021,154 @@ main .section-bg { display: none; } -@media (max-width: 959px) { - section.cover div.content { - gap: calc(var(--sec-vhu) * 2.5); - transform: translateY(calc(var(--sec-vhu) * 3)); +@media (max-width: 1015px) and (orientation: portrait) { + section.cover { + --cover-crest: 10px; + --cover-text-top: calc(var(--cover-crest) + var(--cover-bg-w) * 0.0125); + --cover-text-b: calc(var(--cover-text-top) + var(--cover-bg-w) * 0.19); + --cover-phone-w: 82vw; + --cover-phone-top: calc(var(--cover-text-b) + 16px + var(--cover-area-off)); + --cover-cta-bottom: max(12px, env(safe-area-inset-bottom, 0px)); + --cover-audits-top: calc(100svh - var(--cover-cta-bottom) - 36px); + --cover-socials-top: calc(var(--cover-audits-top) - 66px); + --cover-row-gap: clamp(14px, 4.5vw, 20px); + --cover-row-start: calc(50% - (var(--cover-socials-w, 165px) + var(--cover-row-gap) + var(--cover-stats-w, 141px)) / 2); + --cover-fade-ext: max(0px, calc(110lvh - 100svh)); + --cover-fade-dyn: max(0px, calc(110lvh - 100dvh)); + --cover-fade-base: calc(var(--cover-fade-dyn) + var(--cover-cta-bottom) + 114px); + --cover-fade-ramp: 48px; } - .publications-btns, - .security-btns, - .socials { - margin-top: calc(var(--sec-vhu) * 2); + section.cover, + .chrome-ios section.cover, + .safari-ios section.cover, + .chrome-android section.cover { + background-position: 50% calc(var(--cover-crest) - var(--cover-bg-h) * 0.2678); + } + + .cover-phone { + left: 50%; + right: auto; + transform: translateX(-50%); + top: var(--cover-phone-top); + bottom: auto; + width: var(--cover-phone-w); + height: auto; + } + + .cover-fade { + left: 50%; + right: auto; + transform: translateX(-50%); + width: 100vw; + bottom: calc(var(--cover-area-off) - var(--cover-fade-ext)); + height: calc(var(--cover-fade-base) + var(--cover-fade-ramp)); + background: linear-gradient(to top, rgba(var(--cover-fade-rgb), .9) var(--cover-fade-base), rgba(var(--cover-fade-rgb), 0) 100%); + } + + [dir="ltr"] .socials { + top: var(--cover-socials-top); + bottom: auto; + left: var(--cover-row-start); + right: auto; + } + + [dir="rtl"] .socials { + top: var(--cover-socials-top); + bottom: auto; + left: auto; + right: var(--cover-row-start); + } + + [dir="ltr"] .cover-stats { + top: var(--cover-socials-top); + bottom: auto; + left: calc(var(--cover-row-start) + var(--cover-socials-w, 165px) + var(--cover-row-gap)); + right: auto; + } + + [dir="rtl"] .cover-stats { + top: var(--cover-socials-top); + bottom: auto; + left: auto; + right: calc(var(--cover-row-start) + var(--cover-socials-w, 165px) + var(--cover-row-gap)); + } + + [dir="ltr"] .security-btns, + [dir="rtl"] .security-btns { + top: var(--cover-audits-top); + bottom: auto; + left: 0; + right: 0; + } +} + +@media (max-width: 1015px) and (orientation: landscape) { + section.cover { + --cover-bg-w: calc(min((3200 / 1920) * 100vw, (3200 / 1080) * 100svh)); + --cover-bg-h: calc(min((1800 / 1920) * 100vw, (1800 / 1080) * 100svh)); + --sec-w: calc(min(100vw, (1920 / 1080) * 100svh)); + --sec-h: calc(min((1080 / 1920) * 100vw, 100svh)); + --sec-vwu: calc(var(--sec-w) / 100); + --sec-vhu: calc(var(--sec-h) / 100); + } +} + +@media (max-width: 1015px) and (orientation: landscape) and (max-height: 500px) { + section.cover div.content h1 br { + display: none; + } + + .cover-stats { + gap: 24px; + } + + .cover-stat { + gap: 8px; + } + + .cover-stat b { + font-size: 24px; + } + + .cover-stat span { + font-size: 13px; + } +} + +@media (max-width: 1015px) and (orientation: portrait) { + section.cover div.content { + justify-content: flex-start; + align-items: center; + text-align: center; + gap: calc(var(--sec-vwu) * 3); + margin-top: min(0px, calc(var(--cover-text-top) + var(--cover-area-off))); + padding: max(0px, calc(var(--cover-text-top) + var(--cover-area-off))) 21.6px 0; } section.cover div.content h1 { line-height: 0.85; font-weight: 700; - font-size: calc(var(--sec-vwu) * 30); + font-size: calc(var(--sec-vwu) * 24.75); } section.cover div.content h1 .small { - font-size: calc(var(--sec-vwu) * 16); + font-size: calc(var(--sec-vwu) * 13.2); } section.cover div.content h1 .medium { - font-size: calc(var(--sec-vwu) * 24); + font-size: calc(var(--sec-vwu) * 19.8); } section.cover div.content h2 { font-weight: 500; - font-size: calc(var(--sec-vwu) * 12.85); + font-size: calc(var(--sec-vwu) * 10.6); } section.cover div.content p { font-weight: 400; - font-size: calc(var(--sec-vwu) * 5.5); - max-width: calc(var(--sec-vwu) * 93); + font-size: calc(var(--sec-vwu) * 4.1); + max-width: none; } section.cover div.content p.invest { @@ -1012,6 +1179,40 @@ main .section-bg { font-weight: 600; } + html:not(.dark) section.cover div.content p, + html:not(.dark) section.cover div.content p.invest { + font-weight: 500; + max-width: none; + } + + html:not(.dark) section.cover div.content p.invest a { + font-weight: 700; + } + + .cover-stats { + height: 42px; + gap: clamp(12px, 4vw, 16px); + } + + .cover-stat { + display: block; + text-align: start; + } + + .cover-stat b { + display: block; + font-size: clamp(19px, 5.6vw, 22px); + line-height: 1.05; + } + + .cover-stat span { + display: block; + font-size: clamp(10px, 2.8vw, 11px); + line-height: 1.25; + } +} + +@media (max-width: 1015px) { /* --- MAIN SECTIONS --- */ .page .text-container { justify-content: flex-end; @@ -1108,7 +1309,7 @@ main .section-bg { border-radius: calc(var(--sec-vhu) * 1.8); } -@media screen and (max-width: 959px) { +@media screen and (max-width: 1015px) { .page-6 .group-images { width: 100%; height: 10%; @@ -1321,7 +1522,7 @@ main .section-bg { margin: calc(var(--sec-vhu) * 2); } -@media screen and (max-width: 959px) { +@media screen and (max-width: 1015px) { .page-6 .group-images>a.group-image:nth-child(1) { --c: 3; --r: 1; @@ -1525,7 +1726,7 @@ section.page.footer { min-height: fit-content; } -@media screen and (max-width: 959px) { +@media screen and (max-width: 1015px) { section.page.footer { height: 100lvh; min-height: 100lvh; @@ -1560,7 +1761,7 @@ section.page.footer { } } -@media (min-width: 960px) { +@media (min-width: 1016px) { .footer .container-md, .footer .container-sm, diff --git a/website/src/css/livestream.css b/website/src/css/livestream.css index 831f4a0569..bb8ab8d373 100644 --- a/website/src/css/livestream.css +++ b/website/src/css/livestream.css @@ -126,7 +126,7 @@ main .section-bg { color: #000000; } -@media screen and (max-width: 959px) { +@media screen and (max-width: 1015px) { .livestream { background-image: url("/img/design_3/section-4-mobile-light.webp"); background-size: auto calc(var(--cover-bg-h) * 0.78); diff --git a/website/src/fdroid.html b/website/src/fdroid.html index 67e4821e20..c3441d9680 100644 --- a/website/src/fdroid.html +++ b/website/src/fdroid.html @@ -38,6 +38,11 @@ templateEngineOverride: njk 9F 35 8F F2 84 D1 F7 16 56 A2 BF AF 0E 00 5D EA E6 AA 14 14 37 20 E0 89 F1 1F F2 DD CF EB 01 BA

    +

    + {{ "f-droid-page-apk-text" | i18n({}, lang ) | safe }} + aarch64, + armv7 +