Compare commits

..

8 Commits

Author SHA1 Message Date
Moritz Angermann fc3ae49214 bumped haskell.nix 2024-09-20 15:14:26 +01:00
Evgeny Poberezkin 6adf8f29b0 6.0.4: ios 236, android 237, desktop 65 2024-08-30 19:59:40 +01:00
Stanislav Dmitrenko 4ca1b57e1b android, desktop: small enhancements to new chat sheet (#4803)
* android, desktop: small enhancements to new chat sheet

* padding

* normal view matching stable

* fix one hand layout

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
Co-authored-by: Diogo <diogofncunha@gmail.com>
2024-08-30 18:34:53 +01:00
Evgeny Poberezkin 9432a5e5cd ios: update core library 2024-08-30 17:09:49 +01:00
Evgeny Poberezkin a9ec1f9ec1 core: 6.0.4.0 (simplexmq 6.0.3.0) 2024-08-30 13:39:35 +01:00
Stanislav Dmitrenko 122387d180 android, desktop: fix loading chat items when search was not empty (#4802) 2024-08-30 11:11:26 +01:00
Stanislav Dmitrenko 6edea46dad android, desktop: improvement to a lock UI (#4769)
* android, desktop: improvement to a lock UI

* oneTime passcode screen which allows to pass verification while in call

* change

* unused line

* don't ask to set up auth if already has

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-29 13:15:11 +01:00
Diogo 2fe3acf4df fix android simulator build (#4795) 2024-08-29 12:01:29 +01:00
34 changed files with 358 additions and 285 deletions
+40 -40
View File
@@ -214,11 +214,11 @@
D77B92DC2952372200A5A1CC /* SwiftyGif in Frameworks */ = {isa = PBXBuildFile; productRef = D77B92DB2952372200A5A1CC /* SwiftyGif */; };
D7F0E33929964E7E0068AF69 /* LZString in Frameworks */ = {isa = PBXBuildFile; productRef = D7F0E33829964E7E0068AF69 /* LZString */; };
E51CC1E62C62085600DB91FE /* OneHandUICard.swift in Sources */ = {isa = PBXBuildFile; fileRef = E51CC1E52C62085600DB91FE /* OneHandUICard.swift */; };
E51ED5A82C7F5F4B009F2C7C /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E51ED5A32C7F5F4B009F2C7C /* libgmpxx.a */; };
E51ED5A92C7F5F4B009F2C7C /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E51ED5A42C7F5F4B009F2C7C /* libffi.a */; };
E51ED5AA2C7F5F4B009F2C7C /* libHSsimplex-chat-6.0.3.0-7BSMDwqB9CRFek7eb5Gzw5.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E51ED5A52C7F5F4B009F2C7C /* libHSsimplex-chat-6.0.3.0-7BSMDwqB9CRFek7eb5Gzw5.a */; };
E51ED5AB2C7F5F4B009F2C7C /* libHSsimplex-chat-6.0.3.0-7BSMDwqB9CRFek7eb5Gzw5-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E51ED5A62C7F5F4B009F2C7C /* libHSsimplex-chat-6.0.3.0-7BSMDwqB9CRFek7eb5Gzw5-ghc9.6.3.a */; };
E51ED5AC2C7F5F4B009F2C7C /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E51ED5A72C7F5F4B009F2C7C /* libgmp.a */; };
E5BD844D2C8220D0008C24D1 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5BD84482C8220D0008C24D1 /* libffi.a */; };
E5BD844E2C8220D0008C24D1 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5BD84492C8220D0008C24D1 /* libgmpxx.a */; };
E5BD844F2C8220D0008C24D1 /* libHSsimplex-chat-6.0.4.0-2x1D8vVukGZOGJwEVzeob-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5BD844A2C8220D0008C24D1 /* libHSsimplex-chat-6.0.4.0-2x1D8vVukGZOGJwEVzeob-ghc9.6.3.a */; };
E5BD84502C8220D0008C24D1 /* libHSsimplex-chat-6.0.4.0-2x1D8vVukGZOGJwEVzeob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5BD844B2C8220D0008C24D1 /* libHSsimplex-chat-6.0.4.0-2x1D8vVukGZOGJwEVzeob.a */; };
E5BD84512C8220D0008C24D1 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5BD844C2C8220D0008C24D1 /* libgmp.a */; };
E5DCF8DB2C56FAC1007928CC /* SimpleXChat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; };
E5DCF9712C590272007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF96F2C590272007928CC /* Localizable.strings */; };
E5DCF9842C5902CE007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF9822C5902CE007928CC /* Localizable.strings */; };
@@ -550,11 +550,11 @@
D741547929AF90B00022400A /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/PushKit.framework; sourceTree = DEVELOPER_DIR; };
D7AA2C3429A936B400737B40 /* MediaEncryption.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = MediaEncryption.playground; path = Shared/MediaEncryption.playground; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
E51CC1E52C62085600DB91FE /* OneHandUICard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OneHandUICard.swift; sourceTree = "<group>"; };
E51ED5A32C7F5F4B009F2C7C /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
E51ED5A42C7F5F4B009F2C7C /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
E51ED5A52C7F5F4B009F2C7C /* libHSsimplex-chat-6.0.3.0-7BSMDwqB9CRFek7eb5Gzw5.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.0.3.0-7BSMDwqB9CRFek7eb5Gzw5.a"; sourceTree = "<group>"; };
E51ED5A62C7F5F4B009F2C7C /* libHSsimplex-chat-6.0.3.0-7BSMDwqB9CRFek7eb5Gzw5-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.0.3.0-7BSMDwqB9CRFek7eb5Gzw5-ghc9.6.3.a"; sourceTree = "<group>"; };
E51ED5A72C7F5F4B009F2C7C /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
E5BD84482C8220D0008C24D1 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
E5BD84492C8220D0008C24D1 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
E5BD844A2C8220D0008C24D1 /* libHSsimplex-chat-6.0.4.0-2x1D8vVukGZOGJwEVzeob-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.0.4.0-2x1D8vVukGZOGJwEVzeob-ghc9.6.3.a"; sourceTree = "<group>"; };
E5BD844B2C8220D0008C24D1 /* libHSsimplex-chat-6.0.4.0-2x1D8vVukGZOGJwEVzeob.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.0.4.0-2x1D8vVukGZOGJwEVzeob.a"; sourceTree = "<group>"; };
E5BD844C2C8220D0008C24D1 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
E5DCF9702C590272007928CC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
E5DCF9722C590274007928CC /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = "<group>"; };
E5DCF9732C590275007928CC /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
@@ -645,14 +645,14 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
E51ED5A82C7F5F4B009F2C7C /* libgmpxx.a in Frameworks */,
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
E51ED5AC2C7F5F4B009F2C7C /* libgmp.a in Frameworks */,
E5BD844E2C8220D0008C24D1 /* libgmpxx.a in Frameworks */,
E5BD84512C8220D0008C24D1 /* libgmp.a in Frameworks */,
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
E51ED5AB2C7F5F4B009F2C7C /* libHSsimplex-chat-6.0.3.0-7BSMDwqB9CRFek7eb5Gzw5-ghc9.6.3.a in Frameworks */,
E51ED5A92C7F5F4B009F2C7C /* libffi.a in Frameworks */,
E51ED5AA2C7F5F4B009F2C7C /* libHSsimplex-chat-6.0.3.0-7BSMDwqB9CRFek7eb5Gzw5.a in Frameworks */,
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */,
E5BD84502C8220D0008C24D1 /* libHSsimplex-chat-6.0.4.0-2x1D8vVukGZOGJwEVzeob.a in Frameworks */,
E5BD844D2C8220D0008C24D1 /* libffi.a in Frameworks */,
E5BD844F2C8220D0008C24D1 /* libHSsimplex-chat-6.0.4.0-2x1D8vVukGZOGJwEVzeob-ghc9.6.3.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -729,11 +729,11 @@
5C764E5C279C70B7000C6508 /* Libraries */ = {
isa = PBXGroup;
children = (
E51ED5A42C7F5F4B009F2C7C /* libffi.a */,
E51ED5A72C7F5F4B009F2C7C /* libgmp.a */,
E51ED5A32C7F5F4B009F2C7C /* libgmpxx.a */,
E51ED5A62C7F5F4B009F2C7C /* libHSsimplex-chat-6.0.3.0-7BSMDwqB9CRFek7eb5Gzw5-ghc9.6.3.a */,
E51ED5A52C7F5F4B009F2C7C /* libHSsimplex-chat-6.0.3.0-7BSMDwqB9CRFek7eb5Gzw5.a */,
E5BD84482C8220D0008C24D1 /* libffi.a */,
E5BD844C2C8220D0008C24D1 /* libgmp.a */,
E5BD84492C8220D0008C24D1 /* libgmpxx.a */,
E5BD844A2C8220D0008C24D1 /* libHSsimplex-chat-6.0.4.0-2x1D8vVukGZOGJwEVzeob-ghc9.6.3.a */,
E5BD844B2C8220D0008C24D1 /* libHSsimplex-chat-6.0.4.0-2x1D8vVukGZOGJwEVzeob.a */,
);
path = Libraries;
sourceTree = "<group>";
@@ -1879,7 +1879,7 @@
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 235;
CURRENT_PROJECT_VERSION = 236;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
@@ -1904,7 +1904,7 @@
"@executable_path/Frameworks",
);
LLVM_LTO = YES_THIN;
MARKETING_VERSION = 6.0.3;
MARKETING_VERSION = 6.0.4;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX;
SDKROOT = iphoneos;
@@ -1928,7 +1928,7 @@
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 235;
CURRENT_PROJECT_VERSION = 236;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
@@ -1953,7 +1953,7 @@
"@executable_path/Frameworks",
);
LLVM_LTO = YES;
MARKETING_VERSION = 6.0.3;
MARKETING_VERSION = 6.0.4;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX;
SDKROOT = iphoneos;
@@ -1969,11 +1969,11 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 235;
CURRENT_PROJECT_VERSION = 236;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 6.0.3;
MARKETING_VERSION = 6.0.4;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@@ -1989,11 +1989,11 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 235;
CURRENT_PROJECT_VERSION = 236;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 6.0.3;
MARKETING_VERSION = 6.0.4;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@@ -2014,7 +2014,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 235;
CURRENT_PROJECT_VERSION = 236;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GCC_OPTIMIZATION_LEVEL = s;
@@ -2029,7 +2029,7 @@
"@executable_path/../../Frameworks",
);
LLVM_LTO = YES;
MARKETING_VERSION = 6.0.3;
MARKETING_VERSION = 6.0.4;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -2051,7 +2051,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 235;
CURRENT_PROJECT_VERSION = 236;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_CODE_COVERAGE = NO;
@@ -2066,7 +2066,7 @@
"@executable_path/../../Frameworks",
);
LLVM_LTO = YES;
MARKETING_VERSION = 6.0.3;
MARKETING_VERSION = 6.0.4;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -2088,7 +2088,7 @@
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 235;
CURRENT_PROJECT_VERSION = 236;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -2114,7 +2114,7 @@
"$(PROJECT_DIR)/Libraries/sim",
);
LLVM_LTO = YES;
MARKETING_VERSION = 6.0.3;
MARKETING_VERSION = 6.0.4;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos;
@@ -2139,7 +2139,7 @@
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 235;
CURRENT_PROJECT_VERSION = 236;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -2165,7 +2165,7 @@
"$(PROJECT_DIR)/Libraries/sim",
);
LLVM_LTO = YES;
MARKETING_VERSION = 6.0.3;
MARKETING_VERSION = 6.0.4;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos;
@@ -2190,7 +2190,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 235;
CURRENT_PROJECT_VERSION = 236;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
@@ -2205,7 +2205,7 @@
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 6.0.3;
MARKETING_VERSION = 6.0.4;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@@ -2224,7 +2224,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 235;
CURRENT_PROJECT_VERSION = 236;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
@@ -2239,7 +2239,7 @@
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 6.0.3;
MARKETING_VERSION = 6.0.4;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@@ -770,6 +770,7 @@ fun PreviewActiveCallOverlayVideo() {
callState = CallState.Negotiated,
localMedia = CallMediaType.Video,
peerMedia = CallMediaType.Video,
callUUID = "",
connectionInfo = ConnectionInfo(
RTCIceCandidate(RTCIceCandidateType.Host, "tcp"),
RTCIceCandidate(RTCIceCandidateType.Host, "tcp")
@@ -799,6 +800,7 @@ fun PreviewActiveCallOverlayAudio() {
callState = CallState.Negotiated,
localMedia = CallMediaType.Audio,
peerMedia = CallMediaType.Audio,
callUUID = "",
connectionInfo = ConnectionInfo(
RTCIceCandidate(RTCIceCandidateType.Host, "udp"),
RTCIceCandidate(RTCIceCandidateType.Host, "udp")
@@ -14,6 +14,7 @@ actual fun authenticate(
promptSubtitle: String,
selfDestruct: Boolean,
usingLAMode: LAMode,
oneTime: Boolean,
completed: (LAResult) -> Unit
) {
val activity = mainActivity.get() ?: return completed(LAResult.Error(""))
@@ -27,7 +28,7 @@ actual fun authenticate(
else -> completed(LAResult.Unavailable())
}
LAMode.PASSCODE -> {
authenticateWithPasscode(promptTitle, promptSubtitle, selfDestruct, completed)
authenticateWithPasscode(promptTitle, promptSubtitle, selfDestruct, oneTime, completed)
}
}
}
@@ -74,6 +74,7 @@ fun MainScreen() {
LaunchedEffect(showAdvertiseLAAlert) {
if (
!chatModel.controller.appPrefs.laNoticeShown.get()
&& !appPrefs.performLA.get()
&& showAdvertiseLAAlert
&& chatModel.controller.appPrefs.onboardingStage.get() == OnboardingStage.OnboardingComplete
&& chatModel.chats.size > 3
@@ -211,10 +212,8 @@ fun MainScreen() {
} else {
ActiveCallView()
}
} else {
// It's needed for privacy settings toggle, so it can be shown even if the app is passcode unlocked
ModalManager.fullscreen.showPasscodeInView()
}
ModalManager.fullscreen.showOneTimePasscodeInView()
AlertManager.privacySensitive.showInView()
if (onboarding == OnboardingStage.OnboardingComplete) {
LaunchedEffect(chatModel.currentUser.value, chatModel.appOpenUrl.value) {
@@ -5,6 +5,7 @@ import androidx.compose.material.*
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.Modifier
import chat.simplex.common.model.*
import chat.simplex.common.model.ChatController.appPrefs
import chat.simplex.common.platform.*
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.localauth.SetAppPasscodeView
@@ -31,7 +32,7 @@ object AppLock {
fun showLANotice(laNoticeShown: SharedPreference<Boolean>) {
Log.d(TAG, "showLANotice")
if (!laNoticeShown.get()) {
if (!laNoticeShown.get() && !appPrefs.performLA.get()) {
laNoticeShown.set(true)
AlertManager.shared.showAlertDialog(
title = generalGetString(MR.strings.la_notice_title_simplex_lock),
@@ -57,6 +58,8 @@ object AppLock {
private fun showChooseLAMode() {
Log.d(TAG, "showLANotice")
if (appPrefs.performLA.get()) return
AlertManager.shared.showAlertDialogStacked(
title = generalGetString(MR.strings.la_lock_mode),
text = null,
@@ -80,21 +83,23 @@ object AppLock {
authenticate(
generalGetString(MR.strings.auth_enable_simplex_lock),
generalGetString(MR.strings.auth_confirm_credential),
oneTime = true,
completed = { laResult ->
when (laResult) {
LAResult.Success -> {
m.performLA.value = true
m.showAuthScreen.value = true
appPrefs.performLA.set(true)
laTurnedOnAlert()
}
is LAResult.Failed -> { /* Can be called multiple times on every failure */ }
is LAResult.Error -> {
m.performLA.value = false
appPrefs.performLA.set(false)
m.showAuthScreen.value = false
// Don't drop auth pref in case of state inconsistency (eg, you have set passcode but somehow bypassed toggle and turned it off and then on)
// appPrefs.performLA.set(false)
laFailedAlert()
}
is LAResult.Unavailable -> {
m.performLA.value = false
m.showAuthScreen.value = false
appPrefs.performLA.set(false)
m.showAdvertiseLAUnavailableAlert.value = true
}
@@ -104,19 +109,22 @@ object AppLock {
}
private fun setPasscode() {
if (appPrefs.performLA.get()) return
val appPrefs = ChatController.appPrefs
ModalManager.fullscreen.showCustomModal { close ->
Surface(Modifier.fillMaxSize(), color = MaterialTheme.colors.background, contentColor = LocalContentColor.current) {
SetAppPasscodeView(
submit = {
ChatModel.performLA.value = true
ChatModel.showAuthScreen.value = true
appPrefs.performLA.set(true)
appPrefs.laMode.set(LAMode.PASSCODE)
laTurnedOnAlert()
},
cancel = {
ChatModel.performLA.value = false
appPrefs.performLA.set(false)
ChatModel.showAuthScreen.value = false
// Don't drop auth pref in case of state inconsistency (eg, you have set passcode but somehow bypassed toggle and turned it off and then on)
// appPrefs.performLA.set(false)
laPasscodeNotSetAlert()
},
close = close
@@ -147,6 +155,7 @@ object AppLock {
else
generalGetString(MR.strings.auth_unlock),
selfDestruct = true,
oneTime = false,
completed = { laResult ->
when (laResult) {
LAResult.Success ->
@@ -160,7 +169,7 @@ object AppLock {
}
is LAResult.Unavailable -> {
userAuthorized.value = true
m.performLA.value = false
m.showAuthScreen.value = false
m.controller.appPrefs.performLA.set(false)
laUnavailableTurningOffAlert()
}
@@ -192,22 +201,23 @@ object AppLock {
generalGetString(MR.strings.auth_confirm_credential)
else
"",
oneTime = true,
completed = { laResult ->
val prefPerformLA = m.controller.appPrefs.performLA
when (laResult) {
LAResult.Success -> {
m.performLA.value = true
m.showAuthScreen.value = true
prefPerformLA.set(true)
laTurnedOnAlert()
}
is LAResult.Failed -> { /* Can be called multiple times on every failure */ }
is LAResult.Error -> {
m.performLA.value = false
m.showAuthScreen.value = false
prefPerformLA.set(false)
laFailedAlert()
}
is LAResult.Unavailable -> {
m.performLA.value = false
m.showAuthScreen.value = false
prefPerformLA.set(false)
laUnavailableInstructionAlert()
}
@@ -227,12 +237,13 @@ object AppLock {
generalGetString(MR.strings.auth_confirm_credential)
else
generalGetString(MR.strings.auth_disable_simplex_lock),
oneTime = true,
completed = { laResult ->
val prefPerformLA = m.controller.appPrefs.performLA
val selfDestructPref = m.controller.appPrefs.selfDestruct
when (laResult) {
LAResult.Success -> {
m.performLA.value = false
m.showAuthScreen.value = false
prefPerformLA.set(false)
DatabaseUtils.ksAppPassword.remove()
selfDestructPref.set(false)
@@ -240,12 +251,12 @@ object AppLock {
}
is LAResult.Failed -> { /* Can be called multiple times on every failure */ }
is LAResult.Error -> {
m.performLA.value = true
m.showAuthScreen.value = true
prefPerformLA.set(true)
laFailedAlert()
}
is LAResult.Unavailable -> {
m.performLA.value = false
m.showAuthScreen.value = false
prefPerformLA.set(false)
laUnavailableTurningOffAlert()
}
@@ -20,7 +20,6 @@ import dev.icerock.moko.resources.ImageResource
import dev.icerock.moko.resources.StringResource
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.internal.ChannelFlow
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.datetime.*
@@ -98,7 +97,7 @@ object ChatModel {
}
)
}
val performLA by lazy { mutableStateOf(ChatController.appPrefs.performLA.get()) }
val showAuthScreen by lazy { mutableStateOf(ChatController.appPrefs.performLA.get()) }
val showAdvertiseLAUnavailableAlert = mutableStateOf(false)
val showChatPreviews by lazy { mutableStateOf(ChatController.appPrefs.privacyShowChatPreviews.get()) }
@@ -86,6 +86,7 @@ fun ChatView(staleChatId: State<String?>, onComposed: suspend (chatId: String) -
.collect { chatId ->
markUnreadChatAsRead(chatId)
showSearch.value = false
searchText.value = ""
selectedChatItems.value = null
}
}
@@ -423,6 +423,7 @@ fun authStopChat(m: ChatModel, progressIndicator: MutableState<Boolean>? = null,
authenticate(
generalGetString(MR.strings.auth_stop_chat),
generalGetString(MR.strings.auth_log_in_using_credential),
oneTime = true,
completed = { laResult ->
when (laResult) {
LAResult.Success, is LAResult.Unavailable -> {
@@ -34,6 +34,7 @@ expect fun authenticate(
promptSubtitle: String,
selfDestruct: Boolean = false,
usingLAMode: LAMode = ChatModel.controller.appPrefs.laMode.get(),
oneTime: Boolean,
completed: (LAResult) -> Unit
)
@@ -41,10 +42,11 @@ fun authenticateWithPasscode(
promptTitle: String,
promptSubtitle: String,
selfDestruct: Boolean,
oneTime: Boolean,
completed: (LAResult) -> Unit
) {
val password = DatabaseUtils.ksAppPassword.get() ?: return completed(LAResult.Unavailable(generalGetString(MR.strings.la_no_app_password)))
ModalManager.fullscreen.showPasscodeCustomModal { close ->
ModalManager.fullscreen.showPasscodeCustomModal(oneTime) { close ->
BackHandler {
close()
completed(LAResult.Error(generalGetString(MR.strings.authentication_cancelled)))
@@ -69,6 +69,7 @@ class ModalManager(private val placement: ModalPlacement? = null) {
// Don't use mutableStateOf() here, because it produces this if showing from SimpleXAPI.startChat():
// java.lang.IllegalStateException: Reading a state that was created after the snapshot was taken or in a snapshot that has not yet been applied
private var passcodeView: MutableStateFlow<(@Composable (close: () -> Unit) -> Unit)?> = MutableStateFlow(null)
private var onTimePasscodeView: MutableStateFlow<(@Composable (close: () -> Unit) -> Unit)?> = MutableStateFlow(null)
fun showModal(settings: Boolean = false, showClose: Boolean = true, closeOnTop: Boolean = true, endButtons: @Composable RowScope.() -> Unit = {}, content: @Composable ModalData.() -> Unit) {
val data = ModalData()
@@ -105,9 +106,13 @@ class ModalManager(private val placement: ModalPlacement? = null) {
}
}
fun showPasscodeCustomModal(modal: @Composable (close: () -> Unit) -> Unit) {
Log.d(TAG, "ModalManager.showPasscodeCustomModal")
passcodeView.value = modal
fun showPasscodeCustomModal(oneTime: Boolean, modal: @Composable (close: () -> Unit) -> Unit) {
Log.d(TAG, "ModalManager.showPasscodeCustomModal, oneTime: $oneTime")
if (oneTime) {
onTimePasscodeView.value = modal
} else {
passcodeView.value = modal
}
}
fun hasModalsOpen() = modalCount.value > 0
@@ -179,6 +184,11 @@ class ModalManager(private val placement: ModalPlacement? = null) {
passcodeView.collectAsState().value?.invoke { passcodeView.value = null }
}
@Composable
fun showOneTimePasscodeInView() {
onTimePasscodeView.collectAsState().value?.invoke { onTimePasscodeView.value = null }
}
/**
* Allows to modify a list without getting [ConcurrentModificationException]
* */
@@ -1,7 +1,9 @@
package chat.simplex.common.views.newchat
import SectionDivider
import SectionDividerSpaced
import SectionItemView
import SectionSpacer
import SectionView
import TextIconSpaced
import androidx.compose.desktop.ui.tooling.preview.Preview
@@ -164,6 +166,10 @@ private fun NewChatSheetLayout(
)
val sectionModifier = Modifier.fillMaxWidth()
val deletedContactTypes = listOf(ContactType.CHAT_DELETED)
val deletedChats by remember(chatModel.chats.value, deletedContactTypes) {
derivedStateOf { filterContactTypes(chatModel.chats.value, deletedContactTypes) }
}
LazyColumnWithScrollBar(
Modifier.fillMaxSize(),
@@ -224,7 +230,9 @@ private fun NewChatSheetLayout(
}
}
item {
Spacer(Modifier.padding(bottom = 27.dp))
if (searchText.value.text.isEmpty()) {
Spacer(Modifier.padding(bottom = 27.dp))
}
val actionButtonsOriginal = listOf(
Triple(
@@ -262,37 +270,30 @@ private fun NewChatSheetLayout(
}
}
}
val deletedContactTypes = listOf(ContactType.CHAT_DELETED)
val deletedChats by remember(chatModel.chats.value, deletedContactTypes) {
derivedStateOf { filterContactTypes(chatModel.chats.value, deletedContactTypes) }
}
if (deletedChats.isNotEmpty()) {
SectionDividerSpaced(maxBottomPadding = false)
Row(modifier = sectionModifier) {
SectionView {
SectionItemView(
click = {
ModalManager.start.showCustomModal { closeDeletedChats ->
ModalView(
close = closeDeletedChats,
closeOnTop = !oneHandUI.value,
) {
DeletedContactsView(rh = rh, closeDeletedChats = closeDeletedChats, close = {
ModalManager.start.closeModals()
})
}
SectionView {
SectionItemView(
click = {
ModalManager.start.showCustomModal { closeDeletedChats ->
ModalView(
close = closeDeletedChats,
closeOnTop = !oneHandUI.value,
) {
DeletedContactsView(rh = rh, closeDeletedChats = closeDeletedChats, close = {
ModalManager.start.closeModals()
})
}
}
) {
Icon(
painterResource(MR.images.ic_inventory_2),
contentDescription = stringResource(MR.strings.deleted_chats),
tint = MaterialTheme.colors.secondary,
)
TextIconSpaced(false)
Text(text = stringResource(MR.strings.deleted_chats), color = MaterialTheme.colors.onBackground)
}
) {
Icon(
painterResource(MR.images.ic_inventory_2),
contentDescription = stringResource(MR.strings.deleted_chats),
tint = MaterialTheme.colors.secondary,
)
TextIconSpaced(false)
Text(text = stringResource(MR.strings.deleted_chats), color = MaterialTheme.colors.onBackground)
}
}
}
@@ -300,16 +301,28 @@ private fun NewChatSheetLayout(
}
item {
if (filteredContactChats.isNotEmpty() && !oneHandUI.value) {
if (searchText.value.text.isNotEmpty()) {
Spacer(Modifier.height(DEFAULT_PADDING))
} else {
if (filteredContactChats.isNotEmpty() && searchText.value.text.isEmpty()) {
if (!oneHandUI.value) {
SectionDividerSpaced()
SectionView(stringResource(MR.strings.contact_list_header_title).uppercase(), headerBottomPadding = DEFAULT_PADDING_HALF) {}
} else {
SectionDividerSpaced(maxTopPadding = false, maxBottomPadding = false)
SectionView(stringResource(MR.strings.contact_list_header_title).uppercase(), headerBottomPadding = DEFAULT_PADDING_HALF) {}
Spacer(Modifier.height(DEFAULT_PADDING_HALF))
}
}
}
item {
if (filteredContactChats.isEmpty() && allChats.isNotEmpty()) {
Column(sectionModifier.fillMaxSize().padding(DEFAULT_PADDING)) {
Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
Text(
generalGetString(MR.strings.no_filtered_contacts),
color = MaterialTheme.colors.secondary
)
}
}
Text(
stringResource(MR.strings.contact_list_header_title).uppercase(), color = MaterialTheme.colors.secondary, style = MaterialTheme.typography.body2,
modifier = sectionModifier.padding(start = DEFAULT_PADDING, bottom = DEFAULT_PADDING_HALF), fontSize = 12.sp
)
}
}
@@ -322,17 +335,6 @@ private fun NewChatSheetLayout(
ContactListNavLinkView(chat, nextChatSelected, showDeletedChatIcon = true)
}
}
if (filteredContactChats.isEmpty() && allChats.isNotEmpty()) {
Column(sectionModifier.fillMaxSize().padding(DEFAULT_PADDING)) {
Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
Text(
generalGetString(MR.strings.no_filtered_contacts),
color = MaterialTheme.colors.secondary
)
}
}
}
}
@Composable
@@ -555,78 +557,75 @@ private fun DeletedContactsView(rh: RemoteHostInfo?, closeDeletedChats: () -> Un
}
}
}
) {
Column(
Modifier
.fillMaxSize()
.padding(it)
) { contentPadding ->
val listState = rememberLazyListState(lazyListState.first, lazyListState.second)
val searchText = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) }
val searchShowingSimplexLink = remember { mutableStateOf(false) }
val searchChatFilteredBySimplexLink = remember { mutableStateOf<String?>(null) }
val showUnreadAndFavorites = remember { appPrefs.showUnreadAndFavorites.state }.value
val allChats by remember(chatModel.chats.value) {
derivedStateOf { filterContactTypes(chatModel.chats.value, listOf(ContactType.CHAT_DELETED)) }
}
val filteredContactChats = filteredContactChats(
showUnreadAndFavorites = showUnreadAndFavorites,
searchChatFilteredBySimplexLink = searchChatFilteredBySimplexLink,
searchShowingSimplexLink = searchShowingSimplexLink,
searchText = searchText.value.text,
contactChats = allChats
)
LazyColumnWithScrollBar(
Modifier.fillMaxSize(),
contentPadding = contentPadding,
reverseLayout = oneHandUI.value,
) {
if (!oneHandUI.value) {
Box(contentAlignment = Alignment.Center) {
val bottomPadding = DEFAULT_PADDING
AppBarTitle(
stringResource(MR.strings.deleted_chats),
hostDevice(rh?.remoteHostId),
bottomPadding = bottomPadding
)
}
}
val listState = rememberLazyListState(lazyListState.first, lazyListState.second)
val searchText = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) }
val searchShowingSimplexLink = remember { mutableStateOf(false) }
val searchChatFilteredBySimplexLink = remember { mutableStateOf<String?>(null) }
val showUnreadAndFavorites = remember { appPrefs.showUnreadAndFavorites.state }.value
val allChats by remember(chatModel.chats.value) {
derivedStateOf { filterContactTypes(chatModel.chats.value, listOf(ContactType.CHAT_DELETED)) }
}
val filteredContactChats = filteredContactChats(
showUnreadAndFavorites = showUnreadAndFavorites,
searchChatFilteredBySimplexLink = searchChatFilteredBySimplexLink,
searchShowingSimplexLink = searchShowingSimplexLink,
searchText = searchText.value.text,
contactChats = allChats
)
LazyColumnWithScrollBar(
Modifier.fillMaxSize(),
reverseLayout = oneHandUI.value,
) {
item {
if (!oneHandUI.value) {
Divider()
}
ContactsSearchBar(
listState = listState,
searchText = searchText,
searchShowingSimplexLink = searchShowingSimplexLink,
searchChatFilteredBySimplexLink = searchChatFilteredBySimplexLink,
close = close,
)
Divider()
Spacer(Modifier.padding(bottom = DEFAULT_PADDING))
}
itemsIndexed(filteredContactChats) { index, chat ->
val nextChatSelected = remember(chat.id, filteredContactChats) {
derivedStateOf {
chatModel.chatId.value != null && filteredContactChats.getOrNull(index + 1)?.id == chatModel.chatId.value
}
}
ContactListNavLinkView(chat, nextChatSelected, showDeletedChatIcon = false)
}
}
if (filteredContactChats.isEmpty() && allChats.isNotEmpty()) {
Column(Modifier.fillMaxSize().padding(DEFAULT_PADDING)) {
Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
Text(
generalGetString(MR.strings.no_filtered_contacts),
color = MaterialTheme.colors.secondary,
item {
if (!oneHandUI.value) {
Box(contentAlignment = Alignment.Center) {
val bottomPadding = DEFAULT_PADDING
AppBarTitle(
stringResource(MR.strings.deleted_chats),
hostDevice(rh?.remoteHostId),
bottomPadding = bottomPadding
)
}
}
}
item {
if (!oneHandUI.value) {
Divider()
}
ContactsSearchBar(
listState = listState,
searchText = searchText,
searchShowingSimplexLink = searchShowingSimplexLink,
searchChatFilteredBySimplexLink = searchChatFilteredBySimplexLink,
close = close,
)
Divider()
}
item {
if (filteredContactChats.isEmpty() && allChats.isNotEmpty()) {
Column(Modifier.fillMaxSize().padding(DEFAULT_PADDING)) {
Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
Text(
generalGetString(MR.strings.no_filtered_contacts),
color = MaterialTheme.colors.secondary,
)
}
}
}
}
itemsIndexed(filteredContactChats) { index, chat ->
val nextChatSelected = remember(chat.id, filteredContactChats) {
derivedStateOf {
chatModel.chatId.value != null && filteredContactChats.getOrNull(index + 1)?.id == chatModel.chatId.value
}
}
ContactListNavLinkView(chat, nextChatSelected, showDeletedChatIcon = false)
}
}
}
}
@@ -1,12 +1,10 @@
package chat.simplex.common.views.usersettings
import SectionBottomSpacer
import SectionCustomFooter
import SectionDividerSpaced
import SectionItemView
import SectionTextFooter
import SectionView
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
@@ -34,8 +32,6 @@ import chat.simplex.common.views.onboarding.ReadableText
import chat.simplex.common.model.ChatModel
import chat.simplex.common.model.ChatModel.withChats
import chat.simplex.common.platform.*
import kotlin.math.min
import kotlin.math.roundToInt
enum class LAMode {
SYSTEM,
@@ -374,7 +370,8 @@ fun SimplexLockView(
currentLAMode: SharedPreference<LAMode>,
setPerformLA: (Boolean) -> Unit
) {
val performLA = remember { chatModel.performLA }
val showAuthScreen = remember { chatModel.showAuthScreen }
val performLA = remember { appPrefs.performLA.state }
val laMode = remember { chatModel.controller.appPrefs.laMode.state }
val laLockDelay = remember { chatModel.controller.appPrefs.laLockDelay }
val showChangePasscode = remember { derivedStateOf { performLA.value && currentLAMode.state.value == LAMode.PASSCODE } }
@@ -382,13 +379,9 @@ fun SimplexLockView(
val selfDestructDisplayName = remember { mutableStateOf(chatModel.controller.appPrefs.selfDestructDisplayName.get() ?: "") }
val selfDestructDisplayNamePref = remember { chatModel.controller.appPrefs.selfDestructDisplayName }
fun resetLAEnabled(onOff: Boolean) {
chatModel.controller.appPrefs.performLA.set(onOff)
chatModel.performLA.value = onOff
}
fun disableUnavailableLA() {
resetLAEnabled(false)
chatModel.controller.appPrefs.performLA.set(false)
chatModel.showAuthScreen.value = false
currentLAMode.set(LAMode.default)
laUnavailableInstructionAlert()
}
@@ -405,7 +398,8 @@ fun SimplexLockView(
} else {
generalGetString(MR.strings.chat_lock)
},
generalGetString(MR.strings.change_lock_mode)
generalGetString(MR.strings.change_lock_mode),
oneTime = true,
) { laResult ->
when (laResult) {
is LAResult.Error -> {
@@ -415,7 +409,7 @@ fun SimplexLockView(
LAResult.Success -> {
when (toLAMode) {
LAMode.SYSTEM -> {
authenticate(generalGetString(MR.strings.auth_enable_simplex_lock), promptSubtitle = "", usingLAMode = toLAMode) { laResult ->
authenticate(generalGetString(MR.strings.auth_enable_simplex_lock), promptSubtitle = "", usingLAMode = toLAMode, oneTime = true) { laResult ->
when (laResult) {
LAResult.Success -> {
currentLAMode.set(toLAMode)
@@ -451,7 +445,7 @@ fun SimplexLockView(
}
fun toggleSelfDestruct(selfDestruct: SharedPreference<Boolean>) {
authenticate(generalGetString(MR.strings.la_current_app_passcode), generalGetString(MR.strings.change_self_destruct_mode)) { laResult ->
authenticate(generalGetString(MR.strings.la_current_app_passcode), generalGetString(MR.strings.change_self_destruct_mode), oneTime = true) { laResult ->
when (laResult) {
is LAResult.Error -> laFailedAlert()
is LAResult.Failed -> { /* Can be called multiple times on every failure */ }
@@ -470,7 +464,7 @@ fun SimplexLockView(
}
fun changeLAPassword() {
authenticate(generalGetString(MR.strings.la_current_app_passcode), generalGetString(MR.strings.la_change_app_passcode)) { laResult ->
authenticate(generalGetString(MR.strings.la_current_app_passcode), generalGetString(MR.strings.la_change_app_passcode), oneTime = true) { laResult ->
when (laResult) {
LAResult.Success -> {
ModalManager.fullscreen.showCustomModal { close ->
@@ -494,7 +488,7 @@ fun SimplexLockView(
}
fun changeSelfDestructPassword() {
authenticate(generalGetString(MR.strings.la_current_app_passcode), generalGetString(MR.strings.change_self_destruct_passcode)) { laResult ->
authenticate(generalGetString(MR.strings.la_current_app_passcode), generalGetString(MR.strings.change_self_destruct_passcode), oneTime = true) { laResult ->
when (laResult) {
LAResult.Success -> {
ModalManager.fullscreen.showCustomModal { close ->
@@ -525,8 +519,8 @@ fun SimplexLockView(
) {
AppBarTitle(stringResource(MR.strings.chat_lock))
SectionView {
EnableLock(performLA) { performLAToggle ->
performLA.value = performLAToggle
EnableLock(remember { appPrefs.performLA.state }) { performLAToggle ->
showAuthScreen.value = performLAToggle
chatModel.controller.appPrefs.laNoticeShown.set(true)
if (performLAToggle) {
when (currentLAMode.state.value) {
@@ -543,7 +537,9 @@ fun SimplexLockView(
passcodeAlert(generalGetString(MR.strings.passcode_set))
},
cancel = {
resetLAEnabled(false)
chatModel.showAuthScreen.value = false
// Don't drop auth pref in case of state inconsistency (eg, you have set passcode but somehow bypassed toggle and turned it off and then on)
// chatModel.controller.appPrefs.performLA.set(false)
},
close = close
)
@@ -660,7 +656,7 @@ private fun EnableSelfDestruct(
}
@Composable
private fun EnableLock(performLA: MutableState<Boolean>, onCheckedChange: (Boolean) -> Unit) {
private fun EnableLock(performLA: State<Boolean>, onCheckedChange: (Boolean) -> Unit) {
SectionItemView {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(
@@ -22,6 +22,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.*
import chat.simplex.common.model.*
import chat.simplex.common.model.ChatController.appPrefs
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.CreateProfile
@@ -234,7 +235,7 @@ fun ChatLockItem(
showSettingsModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
setPerformLA: (Boolean) -> Unit
) {
val performLA = remember { ChatModel.performLA }
val performLA = remember { appPrefs.performLA.state }
val currentLAMode = remember { ChatModel.controller.appPrefs.laMode }
SettingsActionItemWithContent(
click = showSettingsModal { SimplexLockView(ChatModel, currentLAMode, setPerformLA) },
@@ -505,6 +506,7 @@ private fun runAuth(title: String, desc: String, onFinish: (success: Boolean) ->
authenticate(
title,
desc,
oneTime = true,
completed = { laResult ->
onFinish(laResult == LAResult.Success || laResult is LAResult.Unavailable)
}
@@ -14,7 +14,6 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.*
import chat.simplex.common.model.*
import chat.simplex.common.model.ChatController.appPrefs
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.DEFAULT_START_MODAL_WIDTH
import chat.simplex.common.ui.theme.SimpleXTheme
@@ -27,7 +26,6 @@ import kotlinx.coroutines.*
import java.awt.event.WindowEvent
import java.awt.event.WindowFocusListener
import java.io.File
import kotlin.math.sqrt
import kotlin.system.exitProcess
val simplexWindowState = SimplexWindowState()
@@ -172,7 +170,7 @@ private fun ApplicationScope.AppWindow(closedByError: MutableState<Boolean>) {
var windowFocused by remember { simplexWindowState.windowFocused }
LaunchedEffect(windowFocused) {
val delay = ChatController.appPrefs.laLockDelay.get()
if (!windowFocused && ChatModel.performLA.value && delay > 0) {
if (!windowFocused && ChatModel.showAuthScreen.value && delay > 0) {
delay(delay * 1000L)
// Trigger auth state check when delay ends (and if it ends)
AppLock.recheckAuthState()
@@ -7,10 +7,11 @@ actual fun authenticate(
promptSubtitle: String,
selfDestruct: Boolean,
usingLAMode: LAMode,
oneTime: Boolean,
completed: (LAResult) -> Unit
) {
when (usingLAMode) {
LAMode.PASSCODE -> authenticateWithPasscode(promptTitle, promptSubtitle, selfDestruct, completed)
LAMode.PASSCODE -> authenticateWithPasscode(promptTitle, promptSubtitle, selfDestruct, oneTime, completed)
else -> {}
}
}
+4 -4
View File
@@ -26,11 +26,11 @@ android.enableJetifier=true
kotlin.mpp.androidSourceSetLayoutVersion=2
kotlin.jvm.target=11
android.version_name=6.0.3
android.version_code=235
android.version_name=6.0.4
android.version_code=237
desktop.version_name=6.0.3
desktop.version_code=64
desktop.version_name=6.0.4
desktop.version_code=65
kotlin.version=1.9.23
gradle.plugin.version=8.2.0
+1 -1
View File
@@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd
source-repository-package
type: git
location: https://github.com/simplex-chat/simplexmq.git
tag: f940ce234ebc3831c3bbedb3fc653907cdb7a064
tag: d559a66145cf7b4cd367c09974ed1ce8393940b2
source-repository-package
type: git
Generated
+82 -29
View File
@@ -156,11 +156,11 @@
"ghc98X": {
"flake": false,
"locked": {
"lastModified": 1696643148,
"narHash": "sha256-E02DfgISH7EvvNAu0BHiPvl1E5FGMDi0pWdNZtIBC9I=",
"lastModified": 1715066704,
"narHash": "sha256-F0EVR8x/fcpj1st+hz96Wdsz5uwVIOziGKAwRxLOYJw=",
"ref": "ghc-9.8",
"rev": "443e870d977b1ab6fc05f47a9a17bc49296adbd6",
"revCount": 61642,
"rev": "78a253543d466ac511a1664a3e6aff032ca684d5",
"revCount": 61757,
"submodules": true,
"type": "git",
"url": "https://gitlab.haskell.org/ghc/ghc"
@@ -175,11 +175,11 @@
"ghc99": {
"flake": false,
"locked": {
"lastModified": 1697054644,
"narHash": "sha256-kKarOuXUaAH3QWv7ASx+gGFMHaHKe0pK5Zu37ky2AL4=",
"lastModified": 1726585445,
"narHash": "sha256-IdwQBex4boY6s0Plj5+ixf36rfYSUyMdTWrztKvZH30=",
"ref": "refs/heads/master",
"rev": "f383a242c76f90bcca8a4d7ee001dcb49c172a9a",
"revCount": 62040,
"rev": "7fd9e5e29ab54eb406880077463e8552e2ddd39a",
"revCount": 67238,
"submodules": true,
"type": "git",
"url": "https://gitlab.haskell.org/ghc/ghc"
@@ -225,6 +225,8 @@
"hls-2.2": "hls-2.2",
"hls-2.3": "hls-2.3",
"hls-2.4": "hls-2.4",
"hls-2.5": "hls-2.5",
"hls-2.6": "hls-2.6",
"hpc-coveralls": "hpc-coveralls",
"hydra": "hydra",
"iserv-proxy": "iserv-proxy",
@@ -238,16 +240,17 @@
"nixpkgs-2205": "nixpkgs-2205",
"nixpkgs-2211": "nixpkgs-2211",
"nixpkgs-2305": "nixpkgs-2305",
"nixpkgs-2311": "nixpkgs-2311",
"nixpkgs-unstable": "nixpkgs-unstable",
"old-ghc-nix": "old-ghc-nix",
"stackage": "stackage"
},
"locked": {
"lastModified": 1701163700,
"narHash": "sha256-sOrewUS3LnzV09nGr7+3R6Q6zsgU4smJc61QsHq+4DE=",
"lastModified": 1705833500,
"narHash": "sha256-rUIr6JNbCedt1g4gVYVvE9t0oFU6FUspCA0DS5cA8Bg=",
"owner": "input-output-hk",
"repo": "haskell.nix",
"rev": "2808bfe3e62e9eb4ee8974cd623a00e1611f302b",
"rev": "d0c35e75cbbc6858770af42ac32b0b85495fbd71",
"type": "github"
},
"original": {
@@ -328,16 +331,50 @@
"hls-2.4": {
"flake": false,
"locked": {
"lastModified": 1696939266,
"narHash": "sha256-VOMf5+kyOeOmfXTHlv4LNFJuDGa7G3pDnOxtzYR40IU=",
"lastModified": 1699862708,
"narHash": "sha256-YHXSkdz53zd0fYGIYOgLt6HrA0eaRJi9mXVqDgmvrjk=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "362fdd1293efb4b82410b676ab1273479f6d17ee",
"rev": "54507ef7e85fa8e9d0eb9a669832a3287ffccd57",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "2.4.0.0",
"ref": "2.4.0.1",
"repo": "haskell-language-server",
"type": "github"
}
},
"hls-2.5": {
"flake": false,
"locked": {
"lastModified": 1701080174,
"narHash": "sha256-fyiR9TaHGJIIR0UmcCb73Xv9TJq3ht2ioxQ2mT7kVdc=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "27f8c3d3892e38edaef5bea3870161815c4d014c",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "2.5.0.0",
"repo": "haskell-language-server",
"type": "github"
}
},
"hls-2.6": {
"flake": false,
"locked": {
"lastModified": 1705325287,
"narHash": "sha256-+P87oLdlPyMw8Mgoul7HMWdEvWP/fNlo8jyNtwME8E8=",
"owner": "haskell",
"repo": "haskell-language-server",
"rev": "6e0b342fa0327e628610f2711f8c3e4eaaa08b1e",
"type": "github"
},
"original": {
"owner": "haskell",
"ref": "2.6.0.0",
"repo": "haskell-language-server",
"type": "github"
}
@@ -384,11 +421,11 @@
"iserv-proxy": {
"flake": false,
"locked": {
"lastModified": 1691634696,
"narHash": "sha256-MZH2NznKC/gbgBu8NgIibtSUZeJ00HTLJ0PlWKCBHb0=",
"lastModified": 1707968597,
"narHash": "sha256-C53NqToxl+n9s1pQ0iLtiH6P5vX3rM+NW/mFt4Ykpsk=",
"ref": "hkm/remote-iserv",
"rev": "43a979272d9addc29fbffc2e8542c5d96e993d73",
"revCount": 14,
"rev": "1b7f8aeb37bbc7c00f04e44d9379aa15a4409e8b",
"revCount": 18,
"type": "git",
"url": "https://gitlab.haskell.org/hamishmack/iserv-proxy.git"
},
@@ -552,11 +589,11 @@
},
"nixpkgs-2305": {
"locked": {
"lastModified": 1695416179,
"narHash": "sha256-610o1+pwbSu+QuF3GE0NU5xQdTHM3t9wyYhB9l94Cd8=",
"lastModified": 1705033721,
"narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "715d72e967ec1dd5ecc71290ee072bcaf5181ed6",
"rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea",
"type": "github"
},
"original": {
@@ -566,6 +603,22 @@
"type": "github"
}
},
"nixpkgs-2311": {
"locked": {
"lastModified": 1719957072,
"narHash": "sha256-gvFhEf5nszouwLAkT9nWsDzocUTqLWHuL++dvNjMp9I=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7144d6241f02d171d25fba3edeaf15e0f2592105",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-23.11-darwin",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"dir": "lib",
@@ -602,17 +655,17 @@
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1695318763,
"narHash": "sha256-FHVPDRP2AfvsxAdc+AsgFJevMz5VBmnZglFUMlxBkcY=",
"lastModified": 1694822471,
"narHash": "sha256-6fSDCj++lZVMZlyqOe9SIOL8tYSBz1bI8acwovRwoX8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e12483116b3b51a185a33a272bf351e357ba9a99",
"rev": "47585496bcb13fb72e4a90daeea2f434e2501998",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"rev": "47585496bcb13fb72e4a90daeea2f434e2501998",
"type": "github"
}
},
@@ -664,11 +717,11 @@
"stackage": {
"flake": false,
"locked": {
"lastModified": 1699834215,
"narHash": "sha256-g/JKy0BCvJaxPuYDl3QVc4OY8cFEomgG+hW/eEV470M=",
"lastModified": 1726532152,
"narHash": "sha256-LRXbVY3M2S8uQWdwd2zZrsnVPEvt2GxaHGoy8EFFdJA=",
"owner": "input-output-hk",
"repo": "stackage.nix",
"rev": "47aacd04abcce6bad57f43cbbbd133538380248e",
"rev": "c77b3530cebad603812cb111c6f64968c2d2337d",
"type": "github"
},
"original": {
+1 -1
View File
@@ -1,5 +1,5 @@
name: simplex-chat
version: 6.0.3.0
version: 6.0.4.0
#synopsis:
#description:
homepage: https://github.com/simplex-chat/simplex-chat#readme
+1 -1
View File
@@ -1,5 +1,5 @@
{
"https://github.com/simplex-chat/simplexmq.git"."f940ce234ebc3831c3bbedb3fc653907cdb7a064" = "16y4ix3v7sd8qd1qfrxk0nshvh0p1a415y2sw5am2jhx0mjr8w1q";
"https://github.com/simplex-chat/simplexmq.git"."d559a66145cf7b4cd367c09974ed1ce8393940b2" = "1jav7jmriims6vlkxg8gmal03f9mbgrwc8v6g0rp95ivkx8gfjyw";
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d";
"https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";
+1 -1
View File
@@ -5,7 +5,7 @@ cabal-version: 1.12
-- see: https://github.com/sol/hpack
name: simplex-chat
version: 6.0.3.0
version: 6.0.4.0
category: Web, System, Services, Cryptography
homepage: https://github.com/simplex-chat/simplex-chat#readme
author: simplex.chat
+9 -10
View File
@@ -40,7 +40,6 @@ import Data.Either (fromRight, lefts, partitionEithers, rights)
import Data.Fixed (div')
import Data.Functor (($>))
import Data.Functor.Identity
import Data.IORef (IORef)
import Data.Int (Int64)
import Data.List (find, foldl', isSuffixOf, mapAccumL, partition, sortOn)
import Data.List.NonEmpty (NonEmpty (..), nonEmpty, toList, (<|))
@@ -1058,7 +1057,7 @@ processChatCommand' vr = \case
fsNewPath <- liftIO $ filesFolder `uniqueCombine` fileName
liftIO $ B.writeFile fsNewPath "" -- create empty file
encrypt <- chatReadVar encryptLocalFiles
cfArgs <- if encrypt then Just <$> (liftIO . CF.randomArgs =<< asks random) else pure Nothing
cfArgs <- if encrypt then Just <$> (atomically . CF.randomArgs =<< asks random) else pure Nothing
let toCF = CryptoFile fsNewPath cfArgs
-- to keep forwarded file in case original is deleted
liftIOEither $ runExceptT $ withExceptT (ChatError . CEInternalError . show) $ copyCryptoFile (fromCF {filePath = fsFromPath} :: CryptoFile) toCF
@@ -1265,9 +1264,9 @@ processChatCommand' vr = \case
calls <- asks currentCalls
withContactLock "sendCallInvitation" contactId $ do
g <- asks random
callId <- liftIO $ CallId <$> C.randomBytes 16 g
callId <- atomically $ CallId <$> C.randomBytes 16 g
callUUID <- UUID.toText <$> liftIO V4.nextRandom
dhKeyPair <- liftIO $ if encryptedCall callType then Just <$> C.generateKeyPair g else pure Nothing
dhKeyPair <- atomically $ if encryptedCall callType then Just <$> C.generateKeyPair g else pure Nothing
let invitation = CallInvitation {callType, callDhPubKey = fst <$> dhKeyPair}
callState = CallInvitationSent {localCallType = callType, localDhPrivKey = snd <$> dhKeyPair}
(msg, _) <- sendDirectContactMessage user ct (XCallInv callId invitation)
@@ -2235,7 +2234,7 @@ processChatCommand' vr = \case
SetFileToReceive fileId userApprovedRelays encrypted_ -> withUser $ \_ -> do
withFileLock "setFileToReceive" fileId . procCmd $ do
encrypt <- (`fromMaybe` encrypted_) <$> chatReadVar encryptLocalFiles
cfArgs <- if encrypt then Just <$> (liftIO . CF.randomArgs =<< asks random) else pure Nothing
cfArgs <- if encrypt then Just <$> (atomically . CF.randomArgs =<< asks random) else pure Nothing
withStore' $ \db -> setRcvFileToReceive db fileId userApprovedRelays cfArgs
ok_
CancelFile fileId -> withUser $ \user@User {userId} ->
@@ -2720,7 +2719,7 @@ processChatCommand' vr = \case
forM_ (timed_ >>= timedDeleteAt') $
startProximateTimedItemThread user (ChatRef CTDirect contactId, chatItemId' ci)
drgRandomBytes :: Int -> CM ByteString
drgRandomBytes n = asks random >>= liftIO . C.randomBytes n
drgRandomBytes n = asks random >>= atomically . C.randomBytes n
privateGetUser :: UserId -> CM User
privateGetUser userId =
tryChatError (withStore (`getUser` userId)) >>= \case
@@ -3245,7 +3244,7 @@ toFSFilePath f =
setFileToEncrypt :: RcvFileTransfer -> CM RcvFileTransfer
setFileToEncrypt ft@RcvFileTransfer {fileId} = do
cfArgs <- liftIO . CF.randomArgs =<< asks random
cfArgs <- atomically . CF.randomArgs =<< asks random
withStore' $ \db -> setFileCryptoArgs db fileId cfArgs
pure (ft :: RcvFileTransfer) {cryptoArgs = Just cfArgs}
@@ -3523,7 +3522,7 @@ agentSubscriber = do
toView' $ CRChatError Nothing $ ChatErrorAgent (CRITICAL True $ "Message reception stopped: " <> show e) Nothing
E.throwIO e
where
process :: (ACorrId, EntityId, AEvt) -> CM' ()
process :: (ACorrId, AEntityId, AEvt) -> CM' ()
process (corrId, entId, AEvt e msg) = run $ case e of
SAENone -> processAgentMessageNoConn msg
SAEConn -> processAgentMessage corrId entId msg
@@ -5957,7 +5956,7 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage =
if featureAllowed SCFCalls forContact ct
then do
g <- asks random
dhKeyPair <- liftIO $ if encryptedCall callType then Just <$> C.generateKeyPair g else pure Nothing
dhKeyPair <- atomically $ if encryptedCall callType then Just <$> C.generateKeyPair g else pure Nothing
ci <- saveCallItem CISCallPending
callUUID <- UUID.toText <$> liftIO V4.nextRandom
let sharedKey = C.Key . C.dhBytes' <$> (C.dh' <$> callDhPubKey <*> (snd <$> dhKeyPair))
@@ -6876,7 +6875,7 @@ createSndMessages idsEvents = do
vr <- chatVersionRange'
withStoreBatch $ \db -> fmap (createMsg db g vr) idsEvents
where
createMsg :: DB.Connection -> IORef ChaChaDRG -> VersionRangeChat -> (ConnOrGroupId, ChatMsgEvent e) -> IO (Either ChatError SndMessage)
createMsg :: DB.Connection -> TVar ChaChaDRG -> VersionRangeChat -> (ConnOrGroupId, ChatMsgEvent e) -> IO (Either ChatError SndMessage)
createMsg db g vr (connOrGroupId, evnt) = runExceptT $ do
withExceptT ChatErrorStore $ createNewSndMessage db g connOrGroupId evnt encodeMessage
where
+1 -2
View File
@@ -36,7 +36,6 @@ import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as B
import Data.Char (ord)
import Data.Constraint (Dict (..))
import Data.IORef (IORef)
import Data.Int (Int64)
import Data.List.NonEmpty (NonEmpty)
import Data.Map.Strict (Map)
@@ -210,7 +209,7 @@ data ChatController = ChatController
agentAsync :: TVar (Maybe (Async (), Maybe (Async ()))),
chatStore :: SQLiteStore,
chatStoreChanged :: TVar Bool, -- if True, chat should be fully restarted
random :: IORef ChaChaDRG,
random :: TVar ChaChaDRG,
eventSeq :: TVar Int,
inputQ :: TBQueue String,
outputQ :: TBQueue (Maybe CorrId, Maybe RemoteHostId, ChatResponse),
+3 -3
View File
@@ -42,7 +42,7 @@ import qualified Simplex.Messaging.Crypto.File as CF
import Simplex.Messaging.Encoding.String
import Simplex.Messaging.Parsers (dropPrefix, sumTypeJSON)
import Simplex.Messaging.Util (catchAll)
import UnliftIO (Handle, IOMode (..), withFile)
import UnliftIO (Handle, IOMode (..), atomically, withFile)
data WriteFileResult
= WFResult {cryptoArgs :: CryptoFileArgs}
@@ -60,7 +60,7 @@ cChatWriteFile cc cPath ptr len = do
chatWriteFile :: ChatController -> FilePath -> ByteString -> IO WriteFileResult
chatWriteFile ChatController {random} path s = do
cfArgs <- CF.randomArgs random
cfArgs <- atomically $ CF.randomArgs random
let file = CryptoFile path $ Just cfArgs
either WFError (\_ -> WFResult cfArgs)
<$> runCatchExceptT (withExceptT show $ CF.writeFile file $ LB.fromStrict s)
@@ -104,7 +104,7 @@ chatEncryptFile ChatController {random} fromPath toPath =
either WFError WFResult <$> runCatchExceptT encrypt
where
encrypt = do
cfArgs <- liftIO $ CF.randomArgs random
cfArgs <- atomically $ CF.randomArgs random
encryptFile fromPath toPath cfArgs
pure cfArgs
+2 -1
View File
@@ -26,6 +26,7 @@ import Foreign.StablePtr
import Simplex.Chat.Controller (ChatController (..))
import Simplex.Chat.Mobile.Shared
import qualified Simplex.Messaging.Crypto as C
import UnliftIO (atomically)
cChatEncryptMedia :: StablePtr ChatController -> CString -> Ptr Word8 -> CInt -> IO CString
cChatEncryptMedia = cTransformMedia . chatEncryptMedia
@@ -47,7 +48,7 @@ chatEncryptMedia cc keyStr frame = do
ChatController {random} <- liftIO $ deRefStablePtr cc
len <- checkFrameLen frame
key <- decodeKey keyStr
iv <- liftIO $ C.randomGCMIV random
iv <- atomically $ C.randomGCMIV random
(tag, frame') <- withExceptT show $ C.encryptAESNoPad key iv $ B.take len frame
pure $ frame' <> BA.convert (C.unAuthTag tag) <> C.unGCMIV iv
+1 -1
View File
@@ -352,7 +352,7 @@ storeRemoteFile rhId encrypted_ localPath = do
tmpDir <- lift getChatTempDirectory
createDirectoryIfMissing True tmpDir
tmpFile <- liftIO $ tmpDir `uniqueCombine` takeFileName localPath
cfArgs <- liftIO . CF.randomArgs =<< asks random
cfArgs <- atomically . CF.randomArgs =<< asks random
liftError (ChatError . CEFileWrite tmpFile) $ encryptFile localPath tmpFile cfArgs
pure $ CryptoFile tmpFile $ Just cfArgs
+1 -1
View File
@@ -24,7 +24,7 @@ type EncryptedFile = ((Handle, Word32), C.CbNonce, LC.SbState)
prepareEncryptedFile :: RemoteCrypto -> (Handle, Word32) -> ExceptT RemoteProtocolError IO EncryptedFile
prepareEncryptedFile RemoteCrypto {drg, hybridKey} f = do
nonce <- liftIO $ C.randomCbNonce drg
nonce <- atomically $ C.randomCbNonce drg
sbState <- liftEitherWith (const $ PRERemoteControl RCEEncrypt) $ LC.kcbInit hybridKey nonce
pure (f, nonce, sbState)
+1 -2
View File
@@ -16,7 +16,6 @@ import Control.Exception (Exception)
import Crypto.Random (ChaChaDRG)
import qualified Data.Aeson.TH as J
import Data.ByteString (ByteString)
import Data.IORef (IORef)
import Data.Int (Int64)
import Data.Text (Text)
import Data.Word (Word16)
@@ -41,7 +40,7 @@ data RemoteHostClient = RemoteHostClient
}
data RemoteCrypto = RemoteCrypto
{ drg :: IORef ChaChaDRG,
{ drg :: TVar ChaChaDRG,
counter :: TVar Int64,
sessionCode :: ByteString,
hybridKey :: KEMHybridSecret,
+6 -6
View File
@@ -127,7 +127,6 @@ import Control.Monad.IO.Class
import Crypto.Random (ChaChaDRG)
import Data.Bifunctor (second)
import Data.Either (rights)
import Data.IORef (IORef)
import Data.Int (Int64)
import Data.List (partition, sortOn)
import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing)
@@ -152,6 +151,7 @@ import Simplex.Messaging.Crypto.Ratchet (pattern PQEncOff, pattern PQSupportOff)
import Simplex.Messaging.Protocol (SubscriptionMode (..))
import Simplex.Messaging.Util (eitherToMaybe, ($>>=), (<$$>))
import Simplex.Messaging.Version
import UnliftIO.STM
type GroupInfoRow = (Int64, GroupName, GroupName, Text, Maybe Text, Maybe ImageData, Maybe ProfileId, Maybe MsgFilter, Maybe Bool, Bool, Maybe GroupPreferences) :. (UTCTime, UTCTime, Maybe UTCTime, Maybe UTCTime, Maybe UIThemeEntityOverrides, Maybe CustomData) :. GroupMemberRow
@@ -311,7 +311,7 @@ getGroupAndMember db User {userId, userContactId} groupMemberId vr =
in (groupInfo, (member :: GroupMember) {activeConn = toMaybeConnection vr connRow})
-- | creates completely new group with a single member - the current user
createNewGroup :: DB.Connection -> VersionRangeChat -> IORef ChaChaDRG -> User -> GroupProfile -> Maybe Profile -> ExceptT StoreError IO GroupInfo
createNewGroup :: DB.Connection -> VersionRangeChat -> TVar ChaChaDRG -> User -> GroupProfile -> Maybe Profile -> ExceptT StoreError IO GroupInfo
createNewGroup db vr gVar user@User {userId} groupProfile incognitoProfile = ExceptT $ do
let GroupProfile {displayName, fullName, description, image, groupPreferences} = groupProfile
fullGroupPreferences = mergeGroupPreferences groupPreferences
@@ -798,7 +798,7 @@ getGroupInvitation db vr user groupId =
firstRow fromOnly (SEGroupNotFound groupId) $
DB.query db "SELECT g.inv_queue_info FROM groups g WHERE g.group_id = ? AND g.user_id = ?" (groupId, userId)
createNewContactMember :: DB.Connection -> IORef ChaChaDRG -> User -> GroupInfo -> Contact -> GroupMemberRole -> ConnId -> ConnReqInvitation -> SubscriptionMode -> ExceptT StoreError IO GroupMember
createNewContactMember :: DB.Connection -> TVar ChaChaDRG -> User -> GroupInfo -> Contact -> GroupMemberRole -> ConnId -> ConnReqInvitation -> SubscriptionMode -> ExceptT StoreError IO GroupMember
createNewContactMember _ _ _ _ Contact {localDisplayName, activeConn = Nothing} _ _ _ _ = throwError $ SEContactNotReady localDisplayName
createNewContactMember db gVar User {userId, userContactId} GroupInfo {groupId, membership} Contact {contactId, localDisplayName, profile, activeConn = Just Connection {connChatVersion, peerChatVRange}} memberRole agentConnId connRequest subMode =
createWithRandomId gVar $ \memId -> do
@@ -847,7 +847,7 @@ createNewContactMember db gVar User {userId, userContactId} GroupInfo {groupId,
:. (minV, maxV)
)
createNewContactMemberAsync :: DB.Connection -> IORef ChaChaDRG -> User -> GroupInfo -> Contact -> GroupMemberRole -> (CommandId, ConnId) -> VersionChat -> VersionRangeChat -> SubscriptionMode -> ExceptT StoreError IO ()
createNewContactMemberAsync :: DB.Connection -> TVar ChaChaDRG -> User -> GroupInfo -> Contact -> GroupMemberRole -> (CommandId, ConnId) -> VersionChat -> VersionRangeChat -> SubscriptionMode -> ExceptT StoreError IO ()
createNewContactMemberAsync db gVar user@User {userId, userContactId} GroupInfo {groupId, membership} Contact {contactId, localDisplayName, profile} memberRole (cmdId, agentConnId) chatV peerChatVRange subMode =
createWithRandomId gVar $ \memId -> do
createdAt <- liftIO getCurrentTime
@@ -872,7 +872,7 @@ createNewContactMemberAsync db gVar user@User {userId, userContactId} GroupInfo
:. (minV, maxV)
)
createAcceptedMember :: DB.Connection -> IORef ChaChaDRG -> User -> GroupInfo -> UserContactRequest -> GroupMemberRole -> ExceptT StoreError IO (GroupMemberId, MemberId)
createAcceptedMember :: DB.Connection -> TVar ChaChaDRG -> User -> GroupInfo -> UserContactRequest -> GroupMemberRole -> ExceptT StoreError IO (GroupMemberId, MemberId)
createAcceptedMember
db
gVar
@@ -1544,7 +1544,7 @@ getMatchingMemberContacts db vr user@User {userId} GroupMember {memberProfile =
AND p.display_name = ? AND p.full_name = ?
|]
createSentProbe :: DB.Connection -> IORef ChaChaDRG -> UserId -> ContactOrMember -> ExceptT StoreError IO (Probe, Int64)
createSentProbe :: DB.Connection -> TVar ChaChaDRG -> UserId -> ContactOrMember -> ExceptT StoreError IO (Probe, Int64)
createSentProbe db gVar userId to =
createWithRandomBytes 32 gVar $ \probe -> do
currentTs <- getCurrentTime
+1 -2
View File
@@ -124,7 +124,6 @@ import Crypto.Random (ChaChaDRG)
import Data.Bifunctor (first)
import Data.ByteString.Char8 (ByteString)
import Data.Either (fromRight, rights)
import Data.IORef (IORef)
import Data.Int (Int64)
import Data.List (sortBy)
import Data.Maybe (fromMaybe, isJust, mapMaybe)
@@ -177,7 +176,7 @@ deleteGroupChatItemsMessages db User {userId} GroupInfo {groupId} = do
DB.execute db "DELETE FROM chat_item_reactions WHERE group_id = ?" (Only groupId)
DB.execute db "DELETE FROM chat_items WHERE user_id = ? AND group_id = ?" (userId, groupId)
createNewSndMessage :: MsgEncodingI e => DB.Connection -> IORef ChaChaDRG -> ConnOrGroupId -> ChatMsgEvent e -> (SharedMsgId -> EncodedChatMessage) -> ExceptT StoreError IO SndMessage
createNewSndMessage :: MsgEncodingI e => DB.Connection -> TVar ChaChaDRG -> ConnOrGroupId -> ChatMsgEvent e -> (SharedMsgId -> EncodedChatMessage) -> ExceptT StoreError IO SndMessage
createNewSndMessage db gVar connOrGroupId chatMsgEvent encodeMessage =
createWithRandomId' gVar $ \sharedMsgId ->
case encodeMessage (SharedMsgId sharedMsgId) of
+7 -7
View File
@@ -20,7 +20,6 @@ import Crypto.Random (ChaChaDRG)
import qualified Data.Aeson.TH as J
import qualified Data.ByteString.Base64 as B64
import Data.ByteString.Char8 (ByteString)
import Data.IORef (IORef)
import Data.Int (Int64)
import Data.Maybe (fromMaybe, isJust, listToMaybe)
import Data.Text (Text)
@@ -45,6 +44,7 @@ import Simplex.Messaging.Parsers (dropPrefix, sumTypeJSON)
import Simplex.Messaging.Protocol (SubscriptionMode (..))
import Simplex.Messaging.Util (allFinally)
import Simplex.Messaging.Version
import UnliftIO.STM
data ChatLockEntity
= CLInvitation ByteString
@@ -488,16 +488,16 @@ withLocalDisplayName db userId displayName action = getLdnSuffix >>= (`tryCreate
|]
(ldn, displayName, ldnSuffix, userId, ts, ts)
createWithRandomId :: forall a. IORef ChaChaDRG -> (ByteString -> IO a) -> ExceptT StoreError IO a
createWithRandomId :: forall a. TVar ChaChaDRG -> (ByteString -> IO a) -> ExceptT StoreError IO a
createWithRandomId = createWithRandomBytes 12
createWithRandomId' :: forall a. IORef ChaChaDRG -> (ByteString -> IO (Either StoreError a)) -> ExceptT StoreError IO a
createWithRandomId' :: forall a. TVar ChaChaDRG -> (ByteString -> IO (Either StoreError a)) -> ExceptT StoreError IO a
createWithRandomId' = createWithRandomBytes' 12
createWithRandomBytes :: forall a. Int -> IORef ChaChaDRG -> (ByteString -> IO a) -> ExceptT StoreError IO a
createWithRandomBytes :: forall a. Int -> TVar ChaChaDRG -> (ByteString -> IO a) -> ExceptT StoreError IO a
createWithRandomBytes size gVar create = createWithRandomBytes' size gVar (fmap Right . create)
createWithRandomBytes' :: forall a. Int -> IORef ChaChaDRG -> (ByteString -> IO (Either StoreError a)) -> ExceptT StoreError IO a
createWithRandomBytes' :: forall a. Int -> TVar ChaChaDRG -> (ByteString -> IO (Either StoreError a)) -> ExceptT StoreError IO a
createWithRandomBytes' size gVar create = tryCreate 3
where
tryCreate :: Int -> ExceptT StoreError IO a
@@ -510,8 +510,8 @@ createWithRandomBytes' size gVar create = tryCreate 3
| SQL.sqlError e == SQL.ErrorConstraint -> tryCreate (n - 1)
| otherwise -> throwError . SEInternalError $ show e
encodedRandomBytes :: IORef ChaChaDRG -> Int -> IO ByteString
encodedRandomBytes gVar n = B64.encode <$> C.randomBytes n gVar
encodedRandomBytes :: TVar ChaChaDRG -> Int -> IO ByteString
encodedRandomBytes gVar n = atomically $ B64.encode <$> C.randomBytes n gVar
assertNotUser :: DB.Connection -> User -> Contact -> ExceptT StoreError IO ()
assertNotUser db User {userId} Contact {contactId, localDisplayName} = do
+2 -2
View File
@@ -216,7 +216,7 @@ sndRcvImg pqEnc enabled (cc1, msg, v1) (cc2, v2) = do
name1 <- userName cc1
name2 <- userName cc2
g <- C.newRandom
img <- B64.encode <$> C.randomBytes lrgLen g
img <- atomically $ B64.encode <$> C.randomBytes lrgLen g
cc1 `send` ("/_send @2 json {\"msgContent\":{\"type\":\"image\",\"text\":\"" <> msg <> "\",\"image\":\"" <> B.unpack img <> "\"}}")
cc1 .<## "}}"
cc1 <### ([ConsoleString (name2 <> ": quantum resistant end-to-end encryption enabled") | enabled] <> [WithTime ("@" <> name2 <> " " <> msg)])
@@ -231,7 +231,7 @@ sndRcvImg pqEnc enabled (cc1, msg, v1) (cc2, v2) = do
genProfileImg :: IO ByteString
genProfileImg = do
g <- C.newRandom
B64.encode <$> C.randomBytes lrgLen g
atomically $ B64.encode <$> C.randomBytes lrgLen g
where
lrgLen = maxEncodedInfoLength * 3 `div` 4 - 420
+7 -6
View File
@@ -8,6 +8,7 @@
module MobileTests where
import ChatTests.Utils
import Control.Concurrent.STM
import Control.Monad.Except
import Data.Aeson (FromJSON)
import qualified Data.Aeson as J
@@ -231,8 +232,8 @@ testMediaApi :: HasCallStack => FilePath -> IO ()
testMediaApi tmp = do
Right c@ChatController {random = g} <- chatMigrateInit (tmp </> "1") "" "yesUp"
cc <- newStablePtr c
key <- C.randomBytes 32 g
frame <- C.randomBytes 100 g
key <- atomically $ C.randomBytes 32 g
frame <- atomically $ C.randomBytes 100 g
let keyStr = strEncode key
reserved = B.replicate (C.authTagSize + C.gcmIVSize) 0
frame' = frame <> reserved
@@ -245,8 +246,8 @@ testMediaCApi :: HasCallStack => FilePath -> IO ()
testMediaCApi tmp = do
Right c@ChatController {random = g} <- chatMigrateInit (tmp </> "1") "" "yesUp"
cc <- newStablePtr c
key <- C.randomBytes 32 g
frame <- C.randomBytes 100 g
key <- atomically $ C.randomBytes 32 g
frame <- atomically $ C.randomBytes 100 g
let keyStr = strEncode key
reserved = B.replicate (C.authTagSize + C.gcmIVSize) 0
frame' = frame <> reserved
@@ -299,7 +300,7 @@ testMissingFileCApi :: FilePath -> IO ()
testMissingFileCApi tmp = do
let path = tmp </> "missing_file"
cPath <- newCString path
CFArgs key nonce <- CF.randomArgs =<< C.newRandom
CFArgs key nonce <- atomically . CF.randomArgs =<< C.newRandom
cKey <- encodedCString key
cNonce <- encodedCString nonce
ptr <- cChatReadFile cPath cKey cNonce
@@ -336,7 +337,7 @@ testMissingFileEncryptionCApi tmp = do
r <- peekCAString =<< cChatEncryptFile cc cFromPath cToPath
Just (WFError err) <- jDecode r
err `shouldContain` fromPath
CFArgs key nonce <- CF.randomArgs =<< C.newRandom
CFArgs key nonce <- atomically . CF.randomArgs =<< C.newRandom
cKey <- encodedCString key
cNonce <- encodedCString nonce
let toPath' = tmp </> "missing_file.decrypted.pdf"
+2 -2
View File
@@ -16,7 +16,7 @@ import Simplex.Chat.Types.Shared
import Simplex.Messaging.Agent.Protocol
import qualified Simplex.Messaging.Crypto as C
import Simplex.Messaging.Crypto.Ratchet
import Simplex.Messaging.Protocol (supportedSMPClientVRange)
import Simplex.Messaging.Protocol (EntityId (..), supportedSMPClientVRange)
import Simplex.Messaging.ServiceScheme
import Simplex.Messaging.Version
import Test.Hspec
@@ -33,7 +33,7 @@ queue =
supportedSMPClientVRange
SMPQueueAddress
{ smpServer = srv,
senderId = "\223\142z\251",
senderId = EntityId "\223\142z\251",
dhPublicKey = "MCowBQYDK2VuAyEAjiswwI3O/NlS8Fk3HJUW870EY2bAwmttMBsvRB9eV3o=",
sndSecure = False
}