From 53d5dc2df873f3ee8052d8235c52bbc864a74616 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Sun, 2 Nov 2025 21:57:01 +0000 Subject: [PATCH 01/11] v6.4.7: ios 311 --- apps/ios/SimpleX.xcodeproj/project.pbxproj | 36 +++++++++++----------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index e226b696e6..ce4785c7ab 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -178,8 +178,8 @@ 64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */; }; 64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829982D54AEED006B9E89 /* libgmp.a */; }; 64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829992D54AEEE006B9E89 /* libffi.a */; }; - 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf-ghc9.6.3.a */; }; - 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf.a */; }; + 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW-ghc9.6.3.a */; }; + 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW.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 */; }; @@ -545,8 +545,8 @@ 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTimePicker.swift; sourceTree = ""; }; 64C829982D54AEED006B9E89 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = ""; }; 64C829992D54AEEE006B9E89 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = ""; }; - 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf-ghc9.6.3.a"; sourceTree = ""; }; - 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf.a"; sourceTree = ""; }; + 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW-ghc9.6.3.a"; sourceTree = ""; }; + 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW.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 = ""; }; @@ -708,8 +708,8 @@ 64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */, 64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */, 64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */, - 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf-ghc9.6.3.a in Frameworks */, - 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf.a in Frameworks */, + 64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW-ghc9.6.3.a in Frameworks */, + 64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW.a in Frameworks */, CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -795,8 +795,8 @@ 64C829992D54AEEE006B9E89 /* libffi.a */, 64C829982D54AEED006B9E89 /* libgmp.a */, 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */, - 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf-ghc9.6.3.a */, - 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.0-2IZGcIMPMD3yeDBZO9qRf.a */, + 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW-ghc9.6.3.a */, + 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.7.1-A5UtRanLEGiKe0gro2y2nW.a */, ); path = Libraries; sourceTree = ""; @@ -2003,7 +2003,7 @@ CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 310; + CURRENT_PROJECT_VERSION = 311; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; @@ -2053,7 +2053,7 @@ CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 310; + CURRENT_PROJECT_VERSION = 311; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; @@ -2095,7 +2095,7 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 310; + CURRENT_PROJECT_VERSION = 311; DEVELOPMENT_TEAM = 5NN7GUYB6T; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; @@ -2115,7 +2115,7 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 310; + CURRENT_PROJECT_VERSION = 311; DEVELOPMENT_TEAM = 5NN7GUYB6T; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 15.0; @@ -2140,7 +2140,7 @@ CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 310; + CURRENT_PROJECT_VERSION = 311; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; GCC_OPTIMIZATION_LEVEL = s; @@ -2177,7 +2177,7 @@ CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 310; + CURRENT_PROJECT_VERSION = 311; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; ENABLE_CODE_COVERAGE = NO; @@ -2214,7 +2214,7 @@ CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 310; + CURRENT_PROJECT_VERSION = 311; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; DYLIB_COMPATIBILITY_VERSION = 1; @@ -2265,7 +2265,7 @@ CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 310; + CURRENT_PROJECT_VERSION = 311; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 5NN7GUYB6T; DYLIB_COMPATIBILITY_VERSION = 1; @@ -2316,7 +2316,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 310; + CURRENT_PROJECT_VERSION = 311; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -2350,7 +2350,7 @@ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 310; + CURRENT_PROJECT_VERSION = 311; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; From 2dbf9280d1c05d8fc4c72ca7b3f51e5000a300a3 Mon Sep 17 00:00:00 2001 From: sh <37271604+shumvgolove@users.noreply.github.com> Date: Mon, 3 Nov 2025 09:10:19 +0000 Subject: [PATCH 02/11] readme: switch image links to bypass CORS/ORB (#6418) --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1e80c14424..2f0f92f5f7 100644 --- a/README.md +++ b/README.md @@ -24,15 +24,15 @@ ## Install the app -[iOS app](https://apps.apple.com/us/app/simplex-chat/id1605771084) +[iOS app](https://apps.apple.com/us/app/simplex-chat/id1605771084)   -[![Android app](https://github.com/simplex-chat/.github/blob/master/profile/images/google_play.svg)](https://play.google.com/store/apps/details?id=chat.simplex.app) +[![Android app](https://raw.githubusercontent.com/simplex-chat/.github/refs/heads/master/profile/images/google_play.svg)](https://play.google.com/store/apps/details?id=chat.simplex.app)   -[F-Droid](https://app.simplex.chat) +[F-Droid](https://app.simplex.chat)   -[iOS TestFlight](https://testflight.apple.com/join/DWuT2LQu) +[iOS TestFlight](https://testflight.apple.com/join/DWuT2LQu)   -[APK](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex.apk) +[APK](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex.apk) - 🖲 Protects your messages and metadata - who you talk to and when. - 🔐 Double ratchet end-to-end encryption, with additional encryption layer. @@ -102,7 +102,7 @@ You need to share a link with your friend or scan a QR code from their phone, in The channel through which you share the link does not have to be secure - it is enough that you can confirm who sent you the message and that your SimpleX connection is established. -Make a private connection Conversation Video call +Make a private connection Conversation Video call After you connect, you can [verify connection security code](./blog/20230103-simplex-chat-v4.4-disappearing-messages.md#connection-security-verification). @@ -443,12 +443,12 @@ Please do NOT report security vulnerabilities via GitHub issues. This software is licensed under the GNU Affero General Public License version 3 (AGPLv3). See the [LICENSE](./LICENSE) file for details. The SimpleX and SimpleX Chat name, logo, and associated branding materials are not covered by this license and are subject to the terms outlined in the [TRADEMARK](./docs/TRADEMARK.md) file. -[iOS app](https://apps.apple.com/us/app/simplex-chat/id1605771084) +[iOS app](https://apps.apple.com/us/app/simplex-chat/id1605771084)   -[![Android app](https://github.com/simplex-chat/.github/blob/master/profile/images/google_play.svg)](https://play.google.com/store/apps/details?id=chat.simplex.app) +[![Android app](https://raw.githubusercontent.com/simplex-chat/.github/refs/heads/master/profile/images/google_play.svg)](https://play.google.com/store/apps/details?id=chat.simplex.app)   -[F-Droid](https://app.simplex.chat) +[F-Droid](https://app.simplex.chat)   -[iOS TestFlight](https://testflight.apple.com/join/DWuT2LQu) +[iOS TestFlight](https://testflight.apple.com/join/DWuT2LQu)   -[APK](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex.apk) +[APK](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex.apk) From f853f84d033e29b59a13097e855f45ea3898cade Mon Sep 17 00:00:00 2001 From: BarbossHack Date: Mon, 17 Nov 2025 10:06:07 +0100 Subject: [PATCH 03/11] ci: free up disk space before executing reproducible script (#6441) * ci: free up disk space before executing reproducible script * ci: use cleanup script template instead Co-authored-by: sh <37271604+shumvgolove@users.noreply.github.com> --------- Co-authored-by: sh <37271604+shumvgolove@users.noreply.github.com> --- .github/workflows/reproduce-schedule.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/reproduce-schedule.yml b/.github/workflows/reproduce-schedule.yml index 7d28d6f70c..7364976467 100644 --- a/.github/workflows/reproduce-schedule.yml +++ b/.github/workflows/reproduce-schedule.yml @@ -23,6 +23,10 @@ jobs: grep -i "tag_name" | \ awk -F \" '{print "TAG="$4}' >> $GITHUB_ENV + # Otherwise we run out of disk space with Docker build + - name: Free disk space + shell: bash + run: ./scripts/ci/linux_util_free_space.sh - name: Execute reproduce script run: | ${GITHUB_WORKSPACE}/scripts/simplex-chat-reproduce-builds.sh "$TAG" || : From 247ab16a74af2c76a63dd8b9ee8fee3e6cf8a406 Mon Sep 17 00:00:00 2001 From: sh <37271604+shumvgolove@users.noreply.github.com> Date: Thu, 20 Nov 2025 09:33:37 +0000 Subject: [PATCH 04/11] ci/reproducible builds: pin Java version (#6447) * ci/reproducible builds: pin Java version * ci/reproducible builds: fix aarch64 builds * ci/reproducible builds: chech java hash --- Dockerfile.build | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/Dockerfile.build b/Dockerfile.build index 3c841cfb25..fddc96b6c2 100644 --- a/Dockerfile.build +++ b/Dockerfile.build @@ -6,7 +6,9 @@ FROM ubuntu:${TAG} AS build ARG GHC=9.6.3 ARG CABAL=3.10.2.0 -ARG JAVA=17 +ARG JAVA_VER=17.0.17.10.1 +ARG JAVA_HASH_AMD64=e3e11daa5c22a45153bbeff1a0c21bf08631791e4e8d8ed14deba31c7cf9af1a +ARG JAVA_HASH_ARM64=2b460859b681757b33a7591b6238ecaf51569d05d2684984e5f0a89c6514acbc ENV TZ=Etc/UTC \ DEBIAN_FRONTEND=noninteractive @@ -44,10 +46,26 @@ RUN apt-get update && \ # depends on libjpeg.so.8 and liblcms2.so.2 which are NOT copied into final # /usr/lib/runtime/lib directory and I do not have time to figure out gradle.kotlin # to fix this :( -RUN curl --proto '=https' --tlsv1.2 -sSf 'https://apt.corretto.aws/corretto.key' | gpg --dearmor -o /usr/share/keyrings/corretto-keyring.gpg &&\ - echo "deb [signed-by=/usr/share/keyrings/corretto-keyring.gpg] https://apt.corretto.aws stable main" > /etc/apt/sources.list.d/corretto.list &&\ - apt update &&\ - apt install -y java-${JAVA}-amazon-corretto-jdk +RUN export JAVA_FILENAME='java-corretto.deb' \ + JAVA_VER_MAJOR=$(printf "${JAVA_VER}" | awk -F. '{print $1}') \ + JAVA_VER_DEB=$(printf "${JAVA_VER}" | sed 's/\.1$/-1/') && \ + case "$(uname -m)" in \ + x86_64) export JAVA_ARCH='amd64' JAVA_HASH="$JAVA_HASH_AMD64" ;; \ + aarch64) export JAVA_ARCH='arm64' JAVA_HASH="$JAVA_HASH_ARM64" ;; \ + *) echo "unknown arch $(uname -m)" && exit 1 ;; \ + esac && \ + curl --proto '=https' --tlsv1.2 -sSf \ + "https://corretto.aws/downloads/resources/${JAVA_VER}/java-${JAVA_VER_MAJOR}-amazon-corretto-jdk_${JAVA_VER_DEB}_${JAVA_ARCH}.deb" \ + -o "${JAVA_FILENAME}" && \ + if echo "${JAVA_HASH} ${JAVA_FILENAME}" | sha256sum -c -; then \ + if apt install -y ./"${JAVA_FILENAME}"; then \ + rm ./"${JAVA_FILENAME}"; \ + else \ + echo "Failed to install Java Corretto" && exit 1; \ + fi \ + else \ + echo "Checksum mismatch" && exit 1; \ + fi # Specify bootstrap Haskell versions ENV BOOTSTRAP_HASKELL_GHC_VERSION=${GHC} From efbc4835a8e64ae85fb11cc0d38bff4f0fcab2f4 Mon Sep 17 00:00:00 2001 From: sh <37271604+shumvgolove@users.noreply.github.com> Date: Tue, 25 Nov 2025 10:06:40 +0000 Subject: [PATCH 05/11] tests: skip multicast discovery in Mac CI (#6458) * tests: skip multicast discovery in Mac CI * tests: remove unneeded xitMacCI'' --- tests/ChatTests/Utils.hs | 3 +++ tests/RemoteTests.hs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/ChatTests/Utils.hs b/tests/ChatTests/Utils.hs index dfb0aece74..778ddec72f 100644 --- a/tests/ChatTests/Utils.hs +++ b/tests/ChatTests/Utils.hs @@ -93,6 +93,9 @@ xit' = if os == "linux" then xit else it xit'' :: (HasCallStack, Example a) => String -> a -> SpecWith (Arg a) xit'' = ifCI xit Hspec.it +xitMacCI :: HasCallStack => String -> (TestParams -> Expectation) -> SpecWith (Arg (TestParams -> Expectation)) +xitMacCI = ifCI (if os == "darwin" then xit else it) it + xdescribe'' :: HasCallStack => String -> SpecWith a -> SpecWith a xdescribe'' = ifCI xdescribe describe diff --git a/tests/RemoteTests.hs b/tests/RemoteTests.hs index e21f97d69e..9790681f3b 100644 --- a/tests/RemoteTests.hs +++ b/tests/RemoteTests.hs @@ -33,7 +33,7 @@ remoteTests = describe "Remote" $ do it "connects with new pairing (stops mobile)" $ remoteHandshakeTest False it "connects with new pairing (stops desktop)" $ remoteHandshakeTest True it "connects with stored pairing" remoteHandshakeStoredTest - it "connects with multicast discovery" remoteHandshakeDiscoverTest + xitMacCI "connects with multicast discovery" remoteHandshakeDiscoverTest it "refuses invalid client cert" remoteHandshakeRejectTest it "connects with stored server bindings" storedBindingsTest it "sends messages" remoteMessageTest From 5e160298410b87975b194163d843d18e7360cecd Mon Sep 17 00:00:00 2001 From: Evgeny Date: Tue, 25 Nov 2025 15:49:33 +0000 Subject: [PATCH 06/11] ios: reduce memory used by iOS share extension (#6465) --- apps/ios/SimpleX SE/ShareAPI.swift | 38 +++++++++++++++++++++++++--- apps/ios/SimpleX SE/ShareModel.swift | 12 ++++----- apps/ios/SimpleXChat/ChatUtils.swift | 4 +-- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/apps/ios/SimpleX SE/ShareAPI.swift b/apps/ios/SimpleX SE/ShareAPI.swift index 4dad9d5d15..6495d09b03 100644 --- a/apps/ios/SimpleX SE/ShareAPI.swift +++ b/apps/ios/SimpleX SE/ShareAPI.swift @@ -48,7 +48,7 @@ func apiSetEncryptLocalFiles(_ enable: Bool) throws { throw r.unexpected } -func apiGetChats(userId: User.ID) throws -> Array { +func apiGetChats(userId: User.ID) throws -> Array { let r: APIResult = sendSimpleXCmd(SEChatCommand.apiGetChats(userId: userId)) if case let .result(.apiChats(user: _, chats: chats)) = r { return chats } throw r.unexpected @@ -170,7 +170,7 @@ enum SEChatResponse: Decodable, ChatAPIResult { case activeUser(user: User) case chatStarted case chatRunning - case apiChats(user: UserRef, chats: [ChatData]) + case apiChats(user: UserRef, chats: [SEChatData]) case newChatItems(user: UserRef, chatItems: [AChatItem]) case cmdOk(user_: UserRef?) @@ -199,7 +199,7 @@ enum SEChatResponse: Decodable, ChatAPIResult { } static func fallbackResult(_ type: String, _ json: NSDictionary) -> SEChatResponse? { - if type == "apiChats", let r = parseApiChats(json) { + if type == "apiChats", let r = seParseApiChats(json) { .apiChats(user: r.user, chats: r.chats) } else { nil @@ -239,3 +239,35 @@ enum SEChatEvent: Decodable, ChatAPIResult { } } } + +public struct SEChatData: Decodable, Identifiable, Hashable, ChatLike { + public var chatInfo: ChatInfo + + public var id: ChatId { get { chatInfo.id } } + + public init(chatInfo: ChatInfo) { + self.chatInfo = chatInfo + } + + public static func invalidJSON(_ json: Data?) -> SEChatData { + SEChatData(chatInfo: .invalidJSON(json: json)) + } +} + +public func seParseApiChats(_ jResp: NSDictionary) -> (user: UserRef, chats: [SEChatData])? { + if let jApiChats = jResp["apiChats"] as? NSDictionary, + let user: UserRef = try? decodeObject(jApiChats["user"] as Any), + let jChats = jApiChats["chats"] as? NSArray { + let chats: [SEChatData] = jChats.map { jChat in + if let jChatDict = jChat as? NSDictionary, + let jChatInfo = jChatDict["chatInfo"], + let chatInfo: ChatInfo = try? decodeObject(jChatInfo) { + return SEChatData(chatInfo: chatInfo) + } + return SEChatData.invalidJSON(serializeJSON(jChat, options: .prettyPrinted)) + } + return (user, chats) + } else { + return nil + } +} diff --git a/apps/ios/SimpleX SE/ShareModel.swift b/apps/ios/SimpleX SE/ShareModel.swift index 5080cf2040..fd5c4c990f 100644 --- a/apps/ios/SimpleX SE/ShareModel.swift +++ b/apps/ios/SimpleX SE/ShareModel.swift @@ -19,11 +19,11 @@ private let MAX_DOWNSAMPLE_SIZE: Int64 = 2000 class ShareModel: ObservableObject { @Published var sharedContent: SharedContent? - @Published var chats: [ChatData] = [] + @Published var chats: [SEChatData] = [] @Published var profileImages: [ChatInfo.ID: UIImage] = [:] @Published var search = "" @Published var comment = "" - @Published var selected: ChatData? + @Published var selected: SEChatData? @Published var isLoaded = false @Published var bottomBar: BottomBar = .loadingSpinner @Published var errorAlert: ErrorAlert? @@ -60,13 +60,13 @@ class ShareModel: ObservableObject { } } - func isProhibited(_ chat: ChatData?) -> Bool { + func isProhibited(_ chat: SEChatData?) -> Bool { if let chat, let sharedContent { sharedContent.prohibited(in: chat, hasSimplexLink: hasSimplexLink) } else { false } } - var filteredChats: [ChatData] { + var filteredChats: [SEChatData] { search.isEmpty ? filterChatsToForwardTo(chats: chats) : filterChatsToForwardTo(chats: chats) @@ -253,7 +253,7 @@ class ShareModel: ObservableObject { } } - private func fetchChats() -> Result, ErrorAlert> { + private func fetchChats() -> Result, ErrorAlert> { do { guard let user = try apiGetActiveUser() else { return .failure( @@ -396,7 +396,7 @@ enum SharedContent { } } - func prohibited(in chatData: ChatData, hasSimplexLink: Bool) -> Bool { + func prohibited(in chatData: SEChatData, hasSimplexLink: Bool) -> Bool { chatData.prohibitedByPref( hasSimplexLink: hasSimplexLink, isMediaOrFileAttachment: cryptoFile != nil, diff --git a/apps/ios/SimpleXChat/ChatUtils.swift b/apps/ios/SimpleXChat/ChatUtils.swift index 98ee9cd5d4..451ac8b4ef 100644 --- a/apps/ios/SimpleXChat/ChatUtils.swift +++ b/apps/ios/SimpleXChat/ChatUtils.swift @@ -9,9 +9,7 @@ import Foundation public protocol ChatLike { - var chatInfo: ChatInfo { get} - var chatItems: [ChatItem] { get } - var chatStats: ChatStats { get } + var chatInfo: ChatInfo { get } } extension ChatLike { From 8ff0ccf3922c8ecdd054bef9a8abba1aa2cf1582 Mon Sep 17 00:00:00 2001 From: sh <37271604+shumvgolove@users.noreply.github.com> Date: Mon, 1 Dec 2025 10:13:18 +0000 Subject: [PATCH 07/11] ci: build simplex-chat CLI deb package (#6474) * ci: build simplex-chat CLI deb package * scripts/build-cli-deb: reproducible size (also actual file size) * scripts/build-cli-deb: fix epoch * scripts/build-cli-deb: set epoch to 1764547200 --- .github/workflows/build.yml | 40 ++++++++++++++++++++++++++++---- scripts/desktop/build-cli-deb.sh | 37 +++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 5 deletions(-) create mode 100755 scripts/desktop/build-cli-deb.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1fc1c18fe1..04c70546fb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -221,6 +221,16 @@ jobs: done strip /out/simplex-chat + - name: Build CLI deb + if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true + shell: docker exec -t builder sh -eu {0} + run: | + version=${{ github.ref }} + version=${version#refs/tags/v} + version=${version%-*} + + ./scripts/desktop/build-cli-deb.sh "$version" + - name: Copy tests from container if: matrix.should_run == true shell: bash @@ -232,21 +242,41 @@ jobs: if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true shell: bash run: | - docker cp builder:/out/simplex-chat ./simplex-chat-ubuntu-${{ matrix.os_underscore }}-${{ matrix.arch }} - path="${{ github.workspace }}/simplex-chat-ubuntu-${{ matrix.os_underscore }}-${{ matrix.arch }}" - echo "bin_path=$path" >> $GITHUB_OUTPUT - echo "bin_hash=$(echo SHA2-256\(simplex-chat-ubuntu-${{ matrix.os_underscore }}-${{ matrix.arch }}\)= $(openssl sha256 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT + cli_name="simplex-chat-ubuntu-${{ matrix.os_underscore }}-${{ matrix.arch }}" + cli_deb_name="${cli_name}.deb" + cli_path="${{ github.workspace }}" + + docker cp builder:/out/simplex-chat "./${cli_name}" + docker cp builder:/out/deb-build/simplex-chat.deb "./${cli_deb_name}" + + echo "bin_name=${cli_name}" >> $GITHUB_OUTPUT + echo "bin_path=${cli_path}/${cli_name}" >> $GITHUB_OUTPUT + echo "bin_hash=$(echo SHA2-256\(${cli_name}\)= $(openssl sha256 "${cli_path}/${cli_name}" | cut -d' ' -f 2))" >> $GITHUB_OUTPUT + + echo "deb_name=${cli_deb_name}" >> $GITHUB_OUTPUT + echo "deb_path=${cli_path}/${cli_deb_name}" >> $GITHUB_OUTPUT + echo "deb_hash=$(echo SHA2-256\(${cli_deb_name}\)= $(openssl sha256 "${cli_path}/${cli_deb_name}" | cut -d' ' -f 2))" >> $GITHUB_OUTPUT - name: Upload CLI if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true uses: ./.github/actions/prepare-release with: + bin_name: ${{ steps.linux_cli_prepare.outputs.bin_name }} bin_path: ${{ steps.linux_cli_prepare.outputs.bin_path }} - bin_name: simplex-chat-ubuntu-${{ matrix.os_underscore }}-${{ matrix.arch }} bin_hash: ${{ steps.linux_cli_prepare.outputs.bin_hash }} github_ref: ${{ github.ref }} github_token: ${{ secrets.GITHUB_TOKEN }} + - name: Upload CLI deb + if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true + uses: ./.github/actions/prepare-release + with: + bin_name: ${{ steps.linux_cli_prepare.outputs.deb_name }} + bin_path: ${{ steps.linux_cli_prepare.outputs.deb_path }} + bin_hash: ${{ steps.linux_cli_prepare.outputs.deb_hash }} + github_ref: ${{ github.ref }} + github_token: ${{ secrets.GITHUB_TOKEN }} + - name: Build Desktop if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true shell: docker exec -t builder sh -eu {0} diff --git a/scripts/desktop/build-cli-deb.sh b/scripts/desktop/build-cli-deb.sh new file mode 100755 index 0000000000..7422520d71 --- /dev/null +++ b/scripts/desktop/build-cli-deb.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env sh +set -eu + +export SOURCE_DATE_EPOCH=1764547200 + +CLI_VERSION="$1" +CLI_PATH_TO_BIN="${2:-/out/simplex-chat}" +BUILD_FOLDER="${3:-/out/deb-build}" + +size=$(stat -c '%s' "$CLI_PATH_TO_BIN" | awk '{printf "%.0f\n", ($1+1023)/1024}') +arch=$(case "$(uname -m)" in x86_64) printf "amd64" ;; aarch64) printf "arm64" ;; *) printf "unknown" ;; esac) +package='simplex-chat' + +mkdir "$BUILD_FOLDER" +cd "$BUILD_FOLDER" + +mkdir -p ./${package}/DEBIAN +mkdir -p ./${package}/usr/bin +cat > ./${package}/DEBIAN/control << EOF +Package: ${package} +Version: ${CLI_VERSION} +Section: Messenger +Priority: optional +Architecture: ${arch} +Maintainer: SimpleX Chat +Description: SimpleX - the first messaging platform that has no user identifiers of any kind - 100% private by design! (CLI) +Installed-Size: ${size} +EOF + +cp "$CLI_PATH_TO_BIN" ./${package}/usr/bin/simplex-chat +chmod +x ./${package}/usr/bin/simplex-chat + +find ./${package} -exec touch -d "@${SOURCE_DATE_EPOCH}" {} + + +dpkg-deb --build --root-owner-group --uniform-compression ./${package} + +strip-nondeterminism "./${package}.deb" From 702f198566bc9a0bdada26eda5fccf580af3df8b Mon Sep 17 00:00:00 2001 From: sh <37271604+shumvgolove@users.noreply.github.com> Date: Tue, 2 Dec 2025 11:33:30 +0000 Subject: [PATCH 08/11] ci: add cli deb and fix formatting to reproducible-builds (#6483) * scripts/reproducible-builds: add cli deb and fix formatting * scripts/reproducible-builds: properly strip v from tag --- scripts/simplex-chat-reproduce-builds.sh | 216 ++++++++++++----------- 1 file changed, 117 insertions(+), 99 deletions(-) diff --git a/scripts/simplex-chat-reproduce-builds.sh b/scripts/simplex-chat-reproduce-builds.sh index c7d9170c4a..e1a62dc73a 100755 --- a/scripts/simplex-chat-reproduce-builds.sh +++ b/scripts/simplex-chat-reproduce-builds.sh @@ -20,115 +20,133 @@ package direct-sqlcipher export DOCKER_BUILDKIT=1 +version=${TAG#v} +version=${version%-*} + cleanup() { - docker exec -t "${container_name}" sh -c 'rm -rf ./dist-newstyle ./apps' 2>/dev/null || : - rm -rf -- "${tempdir}" - docker rm --force "${container_name}" 2>/dev/null || : - docker image rm "${image_name}" 2>/dev/null || : - cd "${init_dir}" + docker exec -t "${container_name}" sh -c 'rm -rf ./dist-newstyle ./apps' 2>/dev/null || : + rm -rf -- "${tempdir}" + docker rm --force "${container_name}" 2>/dev/null || : + docker image rm "${image_name}" 2>/dev/null || : + cd "${init_dir}" } trap 'cleanup' EXIT INT mkdir -p "${init_dir}/${TAG}-${repo_name}/from-source" "${init_dir}/${TAG}-${repo_name}/prebuilt" git -C "${tempdir}" clone "${repo}.git" &&\ - cd "${tempdir}/${repo_name}" &&\ - git checkout "${TAG}" + cd "${tempdir}/${repo_name}" &&\ + git checkout "${TAG}" for os in '22.04' '24.04'; do - os_url="$(printf '%s' "${os}" | tr '.' '_')" + os_url="$(printf '%s' "${os}" | tr '.' '_')" - cli_name="simplex-chat-ubuntu-${os_url}-x86_64" - deb_name="simplex-desktop-ubuntu-${os_url}-x86_64.deb" - appimage_name="simplex-desktop-x86_64.AppImage" + cli_name="simplex-chat-ubuntu-${os_url}-x86_64" + deb_name="simplex-desktop-ubuntu-${os_url}-x86_64.deb" + appimage_name="simplex-desktop-x86_64.AppImage" - # Build image - docker build \ - --no-cache \ - --build-arg TAG="${os}" \ - --build-arg GHC="${ghc}" \ - -f "${tempdir}/${repo_name}/Dockerfile.build" \ - -t "${image_name}" \ - . + # Build image + docker build \ + --no-cache \ + --build-arg TAG="${os}" \ + --build-arg GHC="${ghc}" \ + -f "${tempdir}/${repo_name}/Dockerfile.build" \ + -t "${image_name}" \ + . - printf '%s' "${cabal_local}" > "${tempdir}/${repo_name}/cabal.project.local" + printf '%s' "${cabal_local}" > "${tempdir}/${repo_name}/cabal.project.local" - # Run container in background - docker run -t -d \ - --name "${container_name}" \ - --device /dev/fuse \ - --cap-add SYS_ADMIN \ - --security-opt apparmor:unconfined \ - -v "${tempdir}/${repo_name}:/project" \ - "${image_name}" + # Run container in background + docker run -t -d \ + --name "${container_name}" \ + --device /dev/fuse \ + --cap-add SYS_ADMIN \ + --security-opt apparmor:unconfined \ + -v "${tempdir}/${repo_name}:/project" \ + "${image_name}" - # Consistent permissions - docker exec \ - -t "${container_name}" \ - sh -c 'find /project -type d -exec chmod 755 {} \; ; find /project -type f -perm /111 -exec chmod 755 {} \; ; find /project -type f ! -perm /111 -exec chmod 644 {} \;' + # Consistent permissions + docker exec \ + -t "${container_name}" \ + sh -c 'find /project -type d -exec chmod 755 {} \; ; find /project -type f -perm /111 -exec chmod 755 {} \; ; find /project -type f ! -perm /111 -exec chmod 644 {} \;' - # CLI - docker exec \ - -t "${container_name}" \ - sh -c 'cabal clean && cabal update && cabal build -j && mkdir -p /out && for i in simplex-chat; do bin=$(find /project/dist-newstyle -name "$i" -type f -executable) && chmod +x "$bin" && mv "$bin" /out/; done && strip /out/simplex-chat' + # CLI + docker exec \ + -t "${container_name}" \ + sh -c 'cabal clean && cabal update && cabal build -j && mkdir -p /out && for i in simplex-chat; do bin=$(find /project/dist-newstyle -name "$i" -type f -executable) && chmod +x "$bin" && mv "$bin" /out/; done && strip /out/simplex-chat' - # Copy CLI - docker cp \ - "${container_name}":/out/simplex-chat \ - "${init_dir}/${TAG}-${repo_name}/from-source/${cli_name}" + # Copy CLI + docker cp \ + "${container_name}":/out/simplex-chat \ + "${init_dir}/${TAG}-${repo_name}/from-source/${cli_name}" - # Download prebuilt CLI binary - curl -L \ - --output-dir "${init_dir}/${TAG}-${repo_name}/prebuilt/" \ - -O "${repo}/releases/download/${TAG}/${cli_name}" + # Download prebuilt CLI binary + curl -L \ + --output-dir "${init_dir}/${TAG}-${repo_name}/prebuilt/" \ + -O "${repo}/releases/download/${TAG}/${cli_name}" - # Desktop: deb - docker exec \ - -t "${container_name}" \ - sh -c './scripts/desktop/make-deb-linux.sh' + # CLI: deb + docker exec \ + -t "${container_name}" \ + sh -c "./scripts/desktop/build-cli-deb.sh ${version}" - # Copy deb - docker cp \ - "${container_name}":/project/apps/multiplatform/release/main/deb/simplex_x86_64.deb \ - "${init_dir}/${TAG}-${repo_name}/from-source/${deb_name}" + # Copy CLI: deb + docker cp \ + "${container_name}":/out/deb-build/simplex-chat.deb \ + "${init_dir}/${TAG}-${repo_name}/from-source/${cli_name}.deb" - # Download prebuilt deb package - curl -L \ - --output-dir "${init_dir}/${TAG}-${repo_name}/prebuilt/" \ - -O "${repo}/releases/download/${TAG}/${deb_name}" + # Download prebuilt CLI: deb binary + curl -L \ + --output-dir "${init_dir}/${TAG}-${repo_name}/prebuilt/" \ + -O "${repo}/releases/download/${TAG}/${cli_name}.deb" - # Desktop: appimage. Build only on 22.04 - case "$os" in - 22.04) - # Appimage - docker exec \ - -t "${container_name}" \ - sh -c './scripts/desktop/make-appimage-linux.sh && mv ./apps/multiplatform/release/main/*imple*.AppImage ./apps/multiplatform/release/main/simplex.appimage' + # Desktop: deb + docker exec \ + -t "${container_name}" \ + sh -c './scripts/desktop/make-deb-linux.sh' - # Copy appimage - docker cp \ - "${container_name}":/project/apps/multiplatform/release/main/simplex.appimage \ - "${init_dir}/${TAG}-${repo_name}/from-source/${appimage_name}" + # Copy deb + docker cp \ + "${container_name}":/project/apps/multiplatform/release/main/deb/simplex_x86_64.deb \ + "${init_dir}/${TAG}-${repo_name}/from-source/${deb_name}" - # Download prebuilt appimage binary - curl -L \ - --output-dir "${init_dir}/${TAG}-${repo_name}/prebuilt/" \ - -O "${repo}/releases/download/${TAG}/${appimage_name}" - ;; - esac - - # Important! Remove dist-newstyle for the next interation - docker exec \ - -t "${container_name}" \ - sh -c 'rm -rf ./dist-newstyle ./apps/multiplatform' + # Download prebuilt deb package + curl -L \ + --output-dir "${init_dir}/${TAG}-${repo_name}/prebuilt/" \ + -O "${repo}/releases/download/${TAG}/${deb_name}" - # Also restore git to previous state - git reset --hard && git clean -dfx + # Desktop: appimage. Build only on 22.04 + case "$os" in + 22.04) + # Appimage + docker exec \ + -t "${container_name}" \ + sh -c './scripts/desktop/make-appimage-linux.sh && mv ./apps/multiplatform/release/main/*imple*.AppImage ./apps/multiplatform/release/main/simplex.appimage' - # Stop containers, delete images - docker stop "${container_name}" - docker rm --force "${container_name}" - docker image rm "${image_name}" + # Copy appimage + docker cp \ + "${container_name}":/project/apps/multiplatform/release/main/simplex.appimage \ + "${init_dir}/${TAG}-${repo_name}/from-source/${appimage_name}" + + # Download prebuilt appimage binary + curl -L \ + --output-dir "${init_dir}/${TAG}-${repo_name}/prebuilt/" \ + -O "${repo}/releases/download/${TAG}/${appimage_name}" + ;; + esac + + # Important! Remove dist-newstyle for the next interation + docker exec \ + -t "${container_name}" \ + sh -c 'rm -rf ./dist-newstyle ./apps/multiplatform' + + # Also restore git to previous state + git reset --hard && git clean -dfx + + # Stop containers, delete images + docker stop "${container_name}" + docker rm --force "${container_name}" + docker image rm "${image_name}" done # Cleanup @@ -145,27 +163,27 @@ bad=0 # Check hashes for all binaries for file in "${path_bin}"/from-source/*; do - # Extract binary name - app="$(basename ${file})" + # Extract binary name + app="$(basename ${file})" - # Compute hash for compiled binary - compiled=$(sha256sum "${path_bin}/from-source/${app}" | awk '{print $1}') - # Compute hash for prebuilt binary - prebuilt=$(sha256sum "${path_bin}/prebuilt/${app}" | awk '{print $1}') + # Compute hash for compiled binary + compiled=$(sha256sum "${path_bin}/from-source/${app}" | awk '{print $1}') + # Compute hash for prebuilt binary + prebuilt=$(sha256sum "${path_bin}/prebuilt/${app}" | awk '{print $1}') - # Compare - if [ "${compiled}" != "${prebuilt}" ]; then - # If hashes doesn't match, set bad... - bad=1 + # Compare + if [ "${compiled}" != "${prebuilt}" ]; then + # If hashes doesn't match, set bad... + bad=1 - # ... and print affected binary - printf "%s - sha256sum hash doesn't match\n" "${app}" - fi + # ... and print affected binary + printf "%s - sha256sum hash doesn't match\n" "${app}" + fi done # If everything is still okay, compute checksums file if [ "${bad}" = 0 ]; then - sha256sum "${path_bin}"/from-source/* | sed -e "s|$PWD/||g" -e 's|from-source/||g' -e "s|-$repo_name||g" > "${path_bin}/_sha256sums" + sha256sum "${path_bin}"/from-source/* | sed -e "s|$PWD/||g" -e 's|from-source/||g' -e "s|-$repo_name||g" > "${path_bin}/_sha256sums" - printf 'Checksums computed - %s\n' "${path_bin}/_sha256sums" + printf 'Checksums computed - %s\n' "${path_bin}/_sha256sums" fi From 2ed76fd3867c0962c7b2518b5a4191933eda15f2 Mon Sep 17 00:00:00 2001 From: sh <37271604+shumvgolove@users.noreply.github.com> Date: Wed, 3 Dec 2025 11:16:37 +0000 Subject: [PATCH 09/11] ci: execute reproducible script from tag (#6485) * ci: execute reproducible script from tag * ci: target our repository tags --- .github/workflows/reproduce-schedule.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/reproduce-schedule.yml b/.github/workflows/reproduce-schedule.yml index 7364976467..0febed4c87 100644 --- a/.github/workflows/reproduce-schedule.yml +++ b/.github/workflows/reproduce-schedule.yml @@ -9,9 +9,6 @@ jobs: reproduce: runs-on: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v3 - - name: Get latest release shell: bash run: | @@ -23,6 +20,12 @@ jobs: grep -i "tag_name" | \ awk -F \" '{print "TAG="$4}' >> $GITHUB_ENV + - name: Checkout code + uses: actions/checkout@v3 + with: + ref: ${{ env.TAG }} + repository: simplex-chat/simplex-chat + # Otherwise we run out of disk space with Docker build - name: Free disk space shell: bash From 8089a8c7ef9843cd303820b0ae1d5b5d70543608 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Wed, 10 Dec 2025 09:51:03 +0000 Subject: [PATCH 10/11] desktop: fix linking mobile and connecting after onboarding. Also fixes other cases when core controller changes in android/desktop. (#6489) --- .../chat/simplex/common/model/CryptoFile.kt | 4 +- .../chat/simplex/common/model/SimpleXAPI.kt | 37 ++++++++++++++----- .../chat/simplex/common/platform/Core.kt | 5 ++- .../chat/simplex/common/ui/theme/Theme.kt | 3 +- .../common/views/chatlist/UserPicker.kt | 2 +- .../common/views/database/DatabaseView.kt | 3 +- .../common/views/localauth/LocalAuthView.kt | 4 +- .../common/views/migration/MigrateToDevice.kt | 2 +- 8 files changed, 40 insertions(+), 20 deletions(-) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/CryptoFile.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/CryptoFile.kt index 28b46f592d..6ef56a9124 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/CryptoFile.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/CryptoFile.kt @@ -21,7 +21,7 @@ sealed class WriteFileResult { * */ fun writeCryptoFile(path: String, data: ByteArray): CryptoFileArgs { - val ctrl = ChatController.ctrl ?: throw Exception("Controller is not initialized") + val ctrl = ChatController.getChatCtrl() ?: throw Exception("Controller is not initialized") val buffer = ByteBuffer.allocateDirect(data.size) buffer.put(data) buffer.rewind() @@ -44,7 +44,7 @@ fun readCryptoFile(path: String, cryptoArgs: CryptoFileArgs): ByteArray { } fun encryptCryptoFile(fromPath: String, toPath: String): CryptoFileArgs { - val ctrl = ChatController.ctrl ?: throw Exception("Controller is not initialized") + val ctrl = ChatController.getChatCtrl() ?: throw Exception("Controller is not initialized") val str = chatEncryptFile(ctrl, fromPath, toPath) val d = json.decodeFromString(WriteFileResult.serializer(), str) return when (d) { 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 09aae9a7d3..6a80e50285 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 @@ -482,17 +482,26 @@ class AppPreferences { private const val MESSAGE_TIMEOUT: Int = 300_000_000 object ChatController { - var ctrl: ChatCtrl? = -1 + private var chatCtrl: ChatCtrl? = -1 val appPrefs: AppPreferences by lazy { AppPreferences() } val messagesChannel: Channel = Channel() val chatModel = ChatModel - private var receiverStarted = false + private var receiverJob: Job? = null var lastMsgReceivedTimestamp: Long = System.currentTimeMillis() private set - fun hasChatCtrl() = ctrl != -1L && ctrl != null + fun hasChatCtrl() = chatCtrl != -1L && chatCtrl != null + + fun getChatCtrl(): ChatCtrl? = chatCtrl + + fun setChatCtrl(ctrl: ChatCtrl?) { + val wasRunning = receiverJob != null + stopReceiver() + chatCtrl = ctrl + if (wasRunning && ctrl != null) startReceiver() + } suspend fun getAgentSubsTotal(rh: Long?): Pair? { val userId = currentUserId("getAgentSubsTotal") @@ -639,17 +648,16 @@ object ChatController { private fun startReceiver() { Log.d(TAG, "ChatController startReceiver") - if (receiverStarted) return - receiverStarted = true - CoroutineScope(Dispatchers.IO).launch { + if (receiverJob != null || chatCtrl == null) return + receiverJob = CoroutineScope(Dispatchers.IO).launch { var releaseLock: (() -> Unit) = {} - while (true) { + while (isActive) { /** Global [ctrl] can be null. It's needed for having the same [ChatModel] that already made in [ChatController] without the need * to change it everywhere in code after changing a database. * Since it can be changed in background thread, making this check to prevent NullPointerException */ - val ctrl = ctrl + val ctrl = chatCtrl if (ctrl == null) { - receiverStarted = false + stopReceiver() break } try { @@ -689,6 +697,15 @@ object ChatController { } } + private fun stopReceiver() { + Log.d(TAG, "ChatController stopReceiver") + val job = receiverJob + if (job != null) { + receiverJob = null + job.cancel() + } + } + private suspend fun sendCmdWithRetry(rhId: Long?, cmd: CC, inProgress: MutableState? = null, retryNum: Int = 0): API? { val r = sendCmd(rhId, cmd, retryNum = retryNum) val alert = if (r is API.Error) retryableNetworkErrorAlert(r.err) else null @@ -773,7 +790,7 @@ object ChatController { } suspend fun sendCmd(rhId: Long?, cmd: CC, otherCtrl: ChatCtrl? = null, retryNum: Int = 0, log: Boolean = true): API { - val ctrl = otherCtrl ?: ctrl ?: throw Exception("Controller is not initialized") + val ctrl = otherCtrl ?: chatCtrl ?: throw Exception("Controller is not initialized") return withContext(Dispatchers.IO) { val c = cmd.cmdString diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt index 959e4749dc..d0ce703033 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt @@ -56,6 +56,7 @@ fun initChatControllerOnStart() { } suspend fun initChatController(useKey: String? = null, confirmMigrations: MigrationConfirmation? = null, startChat: () -> CompletableDeferred = { CompletableDeferred(true) }) { + Log.d(TAG, "initChatController") try { if (chatModel.ctrlInitInProgress.value) return chatModel.ctrlInitInProgress.value = true @@ -92,7 +93,7 @@ suspend fun initChatController(useKey: String? = null, confirmMigrations: Migrat val ctrl = if (res is DBMigrationResult.OK) { migrated[1] as Long } else null - chatController.ctrl = ctrl + chatController.setChatCtrl(ctrl) chatModel.chatDbEncrypted.value = dbKey != "" chatModel.chatDbStatus.value = res if (res != DBMigrationResult.OK) { @@ -206,7 +207,7 @@ fun chatInitControllerRemovingDatabases() { }.getOrElse { DBMigrationResult.Unknown(migrated[0] as String) } val ctrl = migrated[1] as Long - chatController.ctrl = ctrl + chatController.setChatCtrl(ctrl) // We need only controller, not databases File(dbPath + "_chat.db").delete() File(dbPath + "_agent.db").delete() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt index 01e19ea478..df9af7fbf6 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt @@ -804,7 +804,8 @@ fun SimpleXTheme(darkTheme: Boolean? = null, content: @Composable () -> Unit) { LocalAppColors provides rememberedAppColors, LocalAppWallpaper provides rememberedWallpaper, LocalDensity provides density, - content = content) + content = content + ) } ) } 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 13351a2111..ed74e083e7 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 @@ -131,7 +131,7 @@ fun UserPicker( } LaunchedEffect(Unit) { // Controller.ctrl can be null when self-destructing activates - if (controller.ctrl != null && controller.ctrl != -1L) { + if (controller.hasChatCtrl()) { withBGApi { controller.reloadRemoteHosts() } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt index 4a911fa6f0..d55d89f26b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt @@ -366,6 +366,7 @@ fun startChat( chatDbChanged: MutableState, progressIndicator: MutableState? = null ) { + Log.d(TAG, "startChat") withLongRunningApi { try { progressIndicator?.value = true @@ -532,7 +533,7 @@ fun deleteChatDatabaseFilesAndState() { appPrefs.newDatabaseInitialized.set(false) chatModel.desktopOnboardingRandomPassword.value = false controller.appPrefs.storeDBPassphrase.set(true) - controller.ctrl = null + controller.setChatCtrl(null) // Clear sensitive data on screen just in case ModalManager will fail to prevent hiding its modals while database encrypts itself chatModel.chatId.value = null diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt index 8021a605db..f86edb0388 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt @@ -33,7 +33,7 @@ fun LocalAuthView(m: ChatModel, authRequest: LocalAuthRequest) { } } else { val r: LAResult = if (passcode.value == authRequest.password) { - if (authRequest.selfDestruct && sdPassword != null && controller.ctrl == -1L) { + if (authRequest.selfDestruct && sdPassword != null && controller.getChatCtrl() == -1L) { initChatControllerOnStart() } LAResult.Success @@ -58,7 +58,7 @@ private fun deleteStorageAndRestart(m: ChatModel, password: String, completed: ( if (m.chatRunning.value == true) { stopChatAsync(m) } - val ctrl = m.controller.ctrl + val ctrl = m.controller.getChatCtrl() if (ctrl != null && ctrl != -1L) { /** * The following sequence can bring a user here: diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/migration/MigrateToDevice.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/migration/MigrateToDevice.kt index d74846f8a3..6199621c39 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/migration/MigrateToDevice.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/migration/MigrateToDevice.kt @@ -633,7 +633,7 @@ private fun MutableState.startDownloading( private fun MutableState.importArchive(archivePath: String, netCfg: NetCfg, networkProxy: NetworkProxy?) { withLongRunningApi { try { - if (ChatController.ctrl == null || ChatController.ctrl == -1L) { + if (!ChatController.hasChatCtrl()) { chatInitControllerRemovingDatabases() } controller.apiDeleteStorage() From f29a50f6fe310eb578b7236daca3c02dc5d28d0b Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Wed, 10 Dec 2025 17:45:55 +0000 Subject: [PATCH 11/11] core: 6.4.8.0 --- simplex-chat.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/simplex-chat.cabal b/simplex-chat.cabal index 75ddceac5e..c66c7b51ee 100644 --- a/simplex-chat.cabal +++ b/simplex-chat.cabal @@ -5,7 +5,7 @@ cabal-version: 1.12 -- see: https://github.com/sol/hpack name: simplex-chat -version: 6.4.7.1 +version: 6.4.8.0 category: Web, System, Services, Cryptography homepage: https://github.com/simplex-chat/simplex-chat#readme author: simplex.chat