Compare commits

..

10 Commits

Author SHA1 Message Date
Evgeny Poberezkin
1aa464bdb2 core: unify dependencies for GHC 8.10.7 and 9.6.3 (#3774) 2024-01-30 23:16:39 +00:00
Evgeny Poberezkin
bce829ef58 v5.5.1: ios 195, android 177, desktop 27 2024-01-27 23:23:29 +00:00
Evgeny Poberezkin
7df300cf36 core: 5.5.1.0 2024-01-27 19:18:41 +00:00
Stanislav Dmitrenko
9b35ff6f11 android, desktop: possible fix of chat items race (#3520)
* android, desktop: possible fix of chat items race

* fix compose threads desynchronization

* optimization and preventing race

* removed unused logs

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-01-27 18:46:27 +00:00
M. Sarmad Qadeer
c4cbb49f57 website: update contact page layout if JS is disabled (#3331)
* website: update page layout for the case if javascript is disabled in browser.

* website: add noscript tag & update heading

* website: do some changes in layout of contact page without JS

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-01-27 16:51:21 +00:00
Stanislav Dmitrenko
2b3eebb7a2 android, desktop: show different text when database migrates (#3762)
* android, desktop: show different text when database migrates

* one more check

* refactor

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-01-26 23:08:48 +00:00
Evgeny Poberezkin
a1328c287c core: remove unused events from api (#3764)
* core: remove unused events from api

* fix test
2024-01-26 18:54:08 +00:00
Stanislav Dmitrenko
f2d498dd79 android, desktop: removed timer for some long running jobs (#3761) 2024-01-26 18:15:20 +00:00
Stanislav Dmitrenko
6b8fc6fdcf android, desktop: lower limit of terminal items for non-developers (#3763) 2024-01-26 18:14:53 +00:00
spaced4ndy
0e585d5e5b core: fix group invitation marked deleted 2024-01-26 20:30:21 +04:00
30 changed files with 222 additions and 139 deletions

View File

@@ -61,11 +61,6 @@
5C7505A527B679EE00BE3227 /* NavLinkPlain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A427B679EE00BE3227 /* NavLinkPlain.swift */; };
5C7505A827B6D34800BE3227 /* ChatInfoToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A727B6D34800BE3227 /* ChatInfoToolbar.swift */; };
5C764E89279CBCB3000C6508 /* ChatModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C764E88279CBCB3000C6508 /* ChatModel.swift */; };
5C83A1AD2B5EF67D00AE0A4A /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C83A1A82B5EF67D00AE0A4A /* libgmp.a */; };
5C83A1AE2B5EF67D00AE0A4A /* libHSsimplex-chat-5.5.0.4-HTW6wkBBAjO2GDtnvnI9O3-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C83A1A92B5EF67D00AE0A4A /* libHSsimplex-chat-5.5.0.4-HTW6wkBBAjO2GDtnvnI9O3-ghc9.6.3.a */; };
5C83A1AF2B5EF67D00AE0A4A /* libHSsimplex-chat-5.5.0.4-HTW6wkBBAjO2GDtnvnI9O3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C83A1AA2B5EF67D00AE0A4A /* libHSsimplex-chat-5.5.0.4-HTW6wkBBAjO2GDtnvnI9O3.a */; };
5C83A1B02B5EF67D00AE0A4A /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C83A1AB2B5EF67D00AE0A4A /* libffi.a */; };
5C83A1B12B5EF67D00AE0A4A /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C83A1AC2B5EF67D00AE0A4A /* libgmpxx.a */; };
5C8F01CD27A6F0D8007D2C8D /* CodeScanner in Frameworks */ = {isa = PBXBuildFile; productRef = 5C8F01CC27A6F0D8007D2C8D /* CodeScanner */; };
5C93292F29239A170090FFF9 /* ProtocolServersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C93292E29239A170090FFF9 /* ProtocolServersView.swift */; };
5C93293129239BED0090FFF9 /* ProtocolServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C93293029239BED0090FFF9 /* ProtocolServerView.swift */; };
@@ -142,6 +137,11 @@
5CE4407927ADB701007B033A /* EmojiItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE4407827ADB701007B033A /* EmojiItemView.swift */; };
5CEACCE327DE9246000BD591 /* ComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCE227DE9246000BD591 /* ComposeView.swift */; };
5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; };
5CEB651B2B65B25500EF2982 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CEB65162B65B25400EF2982 /* libgmpxx.a */; };
5CEB651C2B65B25500EF2982 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CEB65172B65B25400EF2982 /* libffi.a */; };
5CEB651D2B65B25500EF2982 /* libHSsimplex-chat-5.5.1.0-3edL3RXPYL6hI3kOCWWU9.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CEB65182B65B25400EF2982 /* libHSsimplex-chat-5.5.1.0-3edL3RXPYL6hI3kOCWWU9.a */; };
5CEB651E2B65B25500EF2982 /* libHSsimplex-chat-5.5.1.0-3edL3RXPYL6hI3kOCWWU9-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CEB65192B65B25400EF2982 /* libHSsimplex-chat-5.5.1.0-3edL3RXPYL6hI3kOCWWU9-ghc9.6.3.a */; };
5CEB651F2B65B25500EF2982 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CEB651A2B65B25500EF2982 /* libgmp.a */; };
5CEBD7462A5C0A8F00665FE2 /* KeyboardPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */; };
5CEBD7482A5F115D00665FE2 /* SetDeliveryReceiptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */; };
5CF9371E2B23429500E1D781 /* ConcurrentQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF9371D2B23429500E1D781 /* ConcurrentQueue.swift */; };
@@ -325,11 +325,6 @@
5C7505A427B679EE00BE3227 /* NavLinkPlain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavLinkPlain.swift; sourceTree = "<group>"; };
5C7505A727B6D34800BE3227 /* ChatInfoToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatInfoToolbar.swift; sourceTree = "<group>"; };
5C764E88279CBCB3000C6508 /* ChatModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatModel.swift; sourceTree = "<group>"; };
5C83A1A82B5EF67D00AE0A4A /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5C83A1A92B5EF67D00AE0A4A /* libHSsimplex-chat-5.5.0.4-HTW6wkBBAjO2GDtnvnI9O3-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.5.0.4-HTW6wkBBAjO2GDtnvnI9O3-ghc9.6.3.a"; sourceTree = "<group>"; };
5C83A1AA2B5EF67D00AE0A4A /* libHSsimplex-chat-5.5.0.4-HTW6wkBBAjO2GDtnvnI9O3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.5.0.4-HTW6wkBBAjO2GDtnvnI9O3.a"; sourceTree = "<group>"; };
5C83A1AB2B5EF67D00AE0A4A /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5C83A1AC2B5EF67D00AE0A4A /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5C84FE9129A216C800D95B1A /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
5C84FE9329A2179C00D95B1A /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = "nl.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
5C84FE9429A2179C00D95B1A /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@@ -429,6 +424,11 @@
5CE6C7B42AAB1527007F345C /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
5CEACCE227DE9246000BD591 /* ComposeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeView.swift; sourceTree = "<group>"; };
5CEACCEC27DEA495000BD591 /* MsgContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MsgContentView.swift; sourceTree = "<group>"; };
5CEB65162B65B25400EF2982 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5CEB65172B65B25400EF2982 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5CEB65182B65B25400EF2982 /* libHSsimplex-chat-5.5.1.0-3edL3RXPYL6hI3kOCWWU9.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.5.1.0-3edL3RXPYL6hI3kOCWWU9.a"; sourceTree = "<group>"; };
5CEB65192B65B25400EF2982 /* libHSsimplex-chat-5.5.1.0-3edL3RXPYL6hI3kOCWWU9-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.5.1.0-3edL3RXPYL6hI3kOCWWU9-ghc9.6.3.a"; sourceTree = "<group>"; };
5CEB651A2B65B25500EF2982 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardPadding.swift; sourceTree = "<group>"; };
5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDeliveryReceiptsView.swift; sourceTree = "<group>"; };
5CF9371D2B23429500E1D781 /* ConcurrentQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConcurrentQueue.swift; sourceTree = "<group>"; };
@@ -514,13 +514,13 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5CEB651B2B65B25500EF2982 /* libgmpxx.a in Frameworks */,
5CEB651F2B65B25500EF2982 /* libgmp.a in Frameworks */,
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
5C83A1B02B5EF67D00AE0A4A /* libffi.a in Frameworks */,
5C83A1AD2B5EF67D00AE0A4A /* libgmp.a in Frameworks */,
5C83A1AE2B5EF67D00AE0A4A /* libHSsimplex-chat-5.5.0.4-HTW6wkBBAjO2GDtnvnI9O3-ghc9.6.3.a in Frameworks */,
5C83A1AF2B5EF67D00AE0A4A /* libHSsimplex-chat-5.5.0.4-HTW6wkBBAjO2GDtnvnI9O3.a in Frameworks */,
5CEB651D2B65B25500EF2982 /* libHSsimplex-chat-5.5.1.0-3edL3RXPYL6hI3kOCWWU9.a in Frameworks */,
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
5C83A1B12B5EF67D00AE0A4A /* libgmpxx.a in Frameworks */,
5CEB651C2B65B25500EF2982 /* libffi.a in Frameworks */,
5CEB651E2B65B25500EF2982 /* libHSsimplex-chat-5.5.1.0-3edL3RXPYL6hI3kOCWWU9-ghc9.6.3.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -582,11 +582,11 @@
5C764E5C279C70B7000C6508 /* Libraries */ = {
isa = PBXGroup;
children = (
5C83A1AB2B5EF67D00AE0A4A /* libffi.a */,
5C83A1A82B5EF67D00AE0A4A /* libgmp.a */,
5C83A1AC2B5EF67D00AE0A4A /* libgmpxx.a */,
5C83A1A92B5EF67D00AE0A4A /* libHSsimplex-chat-5.5.0.4-HTW6wkBBAjO2GDtnvnI9O3-ghc9.6.3.a */,
5C83A1AA2B5EF67D00AE0A4A /* libHSsimplex-chat-5.5.0.4-HTW6wkBBAjO2GDtnvnI9O3.a */,
5CEB65172B65B25400EF2982 /* libffi.a */,
5CEB651A2B65B25500EF2982 /* libgmp.a */,
5CEB65162B65B25400EF2982 /* libgmpxx.a */,
5CEB65192B65B25400EF2982 /* libHSsimplex-chat-5.5.1.0-3edL3RXPYL6hI3kOCWWU9-ghc9.6.3.a */,
5CEB65182B65B25400EF2982 /* libHSsimplex-chat-5.5.1.0-3edL3RXPYL6hI3kOCWWU9.a */,
);
path = Libraries;
sourceTree = "<group>";
@@ -1509,7 +1509,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES;
@@ -1531,7 +1531,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 5.5;
MARKETING_VERSION = 5.5.1;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX;
SDKROOT = iphoneos;
@@ -1552,7 +1552,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES;
@@ -1574,7 +1574,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 5.5;
MARKETING_VERSION = 5.5.1;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX;
SDKROOT = iphoneos;
@@ -1633,7 +1633,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GENERATE_INFOPLIST_FILE = YES;
@@ -1646,7 +1646,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 5.5;
MARKETING_VERSION = 5.5.1;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1665,7 +1665,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GENERATE_INFOPLIST_FILE = YES;
@@ -1678,7 +1678,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 5.5;
MARKETING_VERSION = 5.5.1;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1697,7 +1697,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1721,7 +1721,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Libraries/sim",
);
MARKETING_VERSION = 5.5;
MARKETING_VERSION = 5.5.1;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos;
@@ -1743,7 +1743,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194;
CURRENT_PROJECT_VERSION = 195;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1767,7 +1767,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Libraries/sim",
);
MARKETING_VERSION = 5.5;
MARKETING_VERSION = 5.5.1;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos;

View File

@@ -104,7 +104,7 @@ class SimplexService: Service() {
if (wakeLock != null || isStartingService) return
val self = this
isStartingService = true
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
withLongRunningApi {
val chatController = ChatController
waitDbMigrationEnds(chatController)
try {

View File

@@ -4,7 +4,6 @@ import android.app.*
import android.app.TaskStackBuilder
import android.content.*
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.hardware.display.DisplayManager
import android.media.AudioAttributes
@@ -218,7 +217,7 @@ object NtfManager {
.setGroup(MessageGroup)
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN)
.setSmallIcon(R.drawable.ntf_icon)
.setLargeIcon(null as Bitmap?)
.setLargeIcon(null)
.setColor(0x88FFFF)
.setAutoCancel(true)
.setVibrate(null)

View File

@@ -12,6 +12,8 @@ import androidx.activity.compose.setContent
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalView
import chat.simplex.common.AppScreen
import chat.simplex.common.model.clear
import chat.simplex.common.ui.theme.SimpleXTheme
import chat.simplex.common.views.helpers.*
import androidx.compose.ui.platform.LocalContext as LocalContext1
import chat.simplex.res.MR

View File

@@ -108,6 +108,7 @@ fun MainScreen() {
val localUserCreated = chatModel.localUserCreated.value
var showInitializationView by remember { mutableStateOf(false) }
when {
chatModel.dbMigrationInProgress.value -> DefaultProgressView(stringResource(MR.strings.database_migration_in_progress))
chatModel.chatDbStatus.value == null && showInitializationView -> DefaultProgressView(stringResource(MR.strings.opening_database))
showChatDatabaseError -> {
// Prevent showing keyboard on Android when: passcode enabled and database password not saved

View File

@@ -2,6 +2,7 @@ package chat.simplex.common.model
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.runtime.snapshots.SnapshotStateMap
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.SpanStyle
@@ -48,6 +49,7 @@ object ChatModel {
val chatDbEncrypted = mutableStateOf<Boolean?>(false)
val chatDbStatus = mutableStateOf<DBMigrationResult?>(null)
val ctrlInitInProgress = mutableStateOf(false)
val dbMigrationInProgress = mutableStateOf(false)
val chats = mutableStateListOf<Chat>()
// map of connections network statuses, key is agent connection id
val networkStatuses = mutableStateMapOf<String, NetworkStatus>()
@@ -55,7 +57,7 @@ object ChatModel {
// current chat
val chatId = mutableStateOf<String?>(null)
val chatItems = mutableStateListOf<ChatItem>()
val chatItems = mutableStateOf(SnapshotStateList<ChatItem>())
// rhId, chatId
val deletedChats = mutableStateOf<List<Pair<Long?, String>>>(emptyList())
val chatItemStatuses = mutableMapOf<Long, CIStatus>()
@@ -267,18 +269,15 @@ object ChatModel {
} else {
addChat(Chat(remoteHostId = rhId, chatInfo = cInfo, chatItems = arrayListOf(cItem)))
}
Log.d(TAG, "TODOCHAT: addChatItem: adding to chat ${chatId.value} from ${cInfo.id} ${cItem.id}, size ${chatItems.size}")
withContext(Dispatchers.Main) {
// add to current chat
if (chatId.value == cInfo.id) {
Log.d(TAG, "TODOCHAT: addChatItem: chatIds are equal, size ${chatItems.size}")
// Prevent situation when chat item already in the list received from backend
if (chatItems.none { it.id == cItem.id }) {
if (chatItems.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
chatItems.add(kotlin.math.max(0, chatItems.lastIndex), cItem)
if (chatItems.value.none { it.id == cItem.id }) {
if (chatItems.value.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
chatItems.add(kotlin.math.max(0, chatItems.value.lastIndex), cItem)
} else {
chatItems.add(cItem)
Log.d(TAG, "TODOCHAT: addChatItem: added to chat ${chatId.value} from ${cInfo.id} ${cItem.id}, size ${chatItems.size}")
}
}
}
@@ -305,14 +304,13 @@ object ChatModel {
addChat(Chat(remoteHostId = rhId, chatInfo = cInfo, chatItems = arrayListOf(cItem)))
res = true
}
Log.d(TAG, "TODOCHAT: upsertChatItem: upserting to chat ${chatId.value} from ${cInfo.id} ${cItem.id}, size ${chatItems.size}")
return withContext(Dispatchers.Main) {
// update current chat
if (chatId.value == cInfo.id) {
val itemIndex = chatItems.indexOfFirst { it.id == cItem.id }
val items = chatItems.value
val itemIndex = items.indexOfFirst { it.id == cItem.id }
if (itemIndex >= 0) {
chatItems[itemIndex] = cItem
Log.d(TAG, "TODOCHAT: upsertChatItem: updated in chat $chatId from ${cInfo.id} ${cItem.id}, size ${chatItems.size}")
items[itemIndex] = cItem
false
} else {
val status = chatItemStatuses.remove(cItem.id)
@@ -322,7 +320,6 @@ object ChatModel {
cItem
}
chatItems.add(ci)
Log.d(TAG, "TODOCHAT: upsertChatItem: added to chat $chatId from ${cInfo.id} ${cItem.id}, size ${chatItems.size}")
true
}
} else {
@@ -334,9 +331,10 @@ object ChatModel {
suspend fun updateChatItem(cInfo: ChatInfo, cItem: ChatItem, status: CIStatus? = null) {
withContext(Dispatchers.Main) {
if (chatId.value == cInfo.id) {
val itemIndex = chatItems.indexOfFirst { it.id == cItem.id }
val items = chatItems.value
val itemIndex = items.indexOfFirst { it.id == cItem.id }
if (itemIndex >= 0) {
chatItems[itemIndex] = cItem
items[itemIndex] = cItem
}
} else if (status != null) {
chatItemStatuses[cItem.id] = status
@@ -360,10 +358,10 @@ object ChatModel {
}
// remove from current chat
if (chatId.value == cInfo.id) {
val itemIndex = chatItems.indexOfFirst { it.id == cItem.id }
if (itemIndex >= 0) {
AudioPlayer.stop(chatItems[itemIndex])
chatItems.removeAt(itemIndex)
chatItems.removeAll {
val remove = it.id == cItem.id
if (remove) { AudioPlayer.stop(it) }
remove
}
}
}
@@ -404,7 +402,7 @@ object ChatModel {
}
fun removeLiveDummy() {
if (chatItems.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
if (chatItems.value.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
chatItems.removeLast()
}
}
@@ -436,14 +434,14 @@ object ChatModel {
var markedRead = 0
if (chatId.value == cInfo.id) {
var i = 0
Log.d(TAG, "TODOCHAT: markItemsReadInCurrentChat: marking read ${cInfo.id}, current chatId ${chatId.value}, size was ${chatItems.size}")
while (i < chatItems.count()) {
val item = chatItems[i]
val items = chatItems.value
while (i < items.size) {
val item = items[i]
if (item.meta.itemStatus is CIStatus.RcvNew && (range == null || (range.from <= item.id && item.id <= range.to))) {
val newItem = item.withStatus(CIStatus.RcvRead())
chatItems[i] = newItem
items[i] = newItem
if (newItem.meta.itemLive != true && newItem.meta.itemTimed?.ttl != null) {
chatItems[i] = newItem.copy(meta = newItem.meta.copy(itemTimed = newItem.meta.itemTimed.copy(
items[i] = newItem.copy(meta = newItem.meta.copy(itemTimed = newItem.meta.itemTimed.copy(
deleteAt = Clock.System.now() + newItem.meta.itemTimed.ttl.toDuration(DurationUnit.SECONDS)))
)
}
@@ -451,7 +449,6 @@ object ChatModel {
}
i += 1
}
Log.d(TAG, "TODOCHAT: markItemsReadInCurrentChat: marked read ${cInfo.id}, current chatId ${chatId.value}, size now ${chatItems.size}")
}
return markedRead
}
@@ -642,7 +639,8 @@ object ChatModel {
}
fun addTerminalItem(item: TerminalItem) {
if (terminalItems.value.size >= 500) {
val maxItems = if (appPreferences.developerTools.get()) 500 else 200
if (terminalItems.value.size >= maxItems) {
terminalItems.value = terminalItems.value.subList(1, terminalItems.value.size)
}
terminalItems.value += item
@@ -2004,6 +2002,46 @@ data class ChatItem (
}
}
fun MutableState<SnapshotStateList<ChatItem>>.add(index: Int, chatItem: ChatItem) {
value = SnapshotStateList<ChatItem>().apply { addAll(value); add(index, chatItem) }
}
fun MutableState<SnapshotStateList<ChatItem>>.add(chatItem: ChatItem) {
value = SnapshotStateList<ChatItem>().apply { addAll(value); add(chatItem) }
}
fun MutableState<SnapshotStateList<ChatItem>>.addAll(index: Int, chatItems: List<ChatItem>) {
value = SnapshotStateList<ChatItem>().apply { addAll(value); addAll(index, chatItems) }
}
fun MutableState<SnapshotStateList<ChatItem>>.addAll(chatItems: List<ChatItem>) {
value = SnapshotStateList<ChatItem>().apply { addAll(value); addAll(chatItems) }
}
fun MutableState<SnapshotStateList<ChatItem>>.removeAll(block: (ChatItem) -> Boolean) {
value = SnapshotStateList<ChatItem>().apply { addAll(value); removeAll(block) }
}
fun MutableState<SnapshotStateList<ChatItem>>.removeAt(index: Int) {
value = SnapshotStateList<ChatItem>().apply { addAll(value); removeAt(index) }
}
fun MutableState<SnapshotStateList<ChatItem>>.removeLast() {
value = SnapshotStateList<ChatItem>().apply { addAll(value); removeLast() }
}
fun MutableState<SnapshotStateList<ChatItem>>.replaceAll(chatItems: List<ChatItem>) {
value = SnapshotStateList<ChatItem>().apply { addAll(chatItems) }
}
fun MutableState<SnapshotStateList<ChatItem>>.clear() {
value = SnapshotStateList<ChatItem>()
}
fun State<SnapshotStateList<ChatItem>>.asReversed(): MutableList<ChatItem> = value.asReversed()
val State<List<ChatItem>>.size: Int get() = value.size
enum class CIMergeCategory {
MemberConnected,
RcvGroupEvent,

View File

@@ -43,7 +43,7 @@ val appPreferences: AppPreferences
val chatController: ChatController = ChatController
fun initChatControllerAndRunMigrations() {
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
withLongRunningApi {
if (appPreferences.chatStopped.get() && appPreferences.storeDBPassphrase.get() && ksDatabasePassword.get() != null) {
initChatController(startChat = ::showStartChatAfterRestartAlert)
} else {
@@ -59,10 +59,23 @@ suspend fun initChatController(useKey: String? = null, confirmMigrations: Migrat
chatModel.ctrlInitInProgress.value = true
val dbKey = useKey ?: DatabaseUtils.useDatabaseKey()
val confirm = confirmMigrations ?: if (appPreferences.developerTools.get() && appPreferences.confirmDBUpgrades.get()) MigrationConfirmation.Error else MigrationConfirmation.YesUp
val migrated: Array<Any> = chatMigrateInit(dbAbsolutePrefixPath, dbKey, confirm.value)
val res: DBMigrationResult = kotlin.runCatching {
var migrated: Array<Any> = chatMigrateInit(dbAbsolutePrefixPath, dbKey, MigrationConfirmation.Error.value)
var res: DBMigrationResult = runCatching {
json.decodeFromString<DBMigrationResult>(migrated[0] as String)
}.getOrElse { DBMigrationResult.Unknown(migrated[0] as String) }
val rerunMigration = res is DBMigrationResult.ErrorMigration && when (res.migrationError) {
// we don't allow to run down migrations without confirmation in UI, so currently it won't be YesUpDown
is MigrationError.Upgrade -> confirm == MigrationConfirmation.YesUp || confirm == MigrationConfirmation.YesUpDown
is MigrationError.Downgrade -> confirm == MigrationConfirmation.YesUpDown
is MigrationError.Error -> false
}
if (rerunMigration) {
chatModel.dbMigrationInProgress.value = true
migrated = chatMigrateInit(dbAbsolutePrefixPath, dbKey, confirm.value)
res = runCatching {
json.decodeFromString<DBMigrationResult>(migrated[0] as String)
}.getOrElse { DBMigrationResult.Unknown(migrated[0] as String) }
}
val ctrl = if (res is DBMigrationResult.OK) {
migrated[1] as Long
} else null
@@ -120,6 +133,7 @@ suspend fun initChatController(useKey: String? = null, confirmMigrations: Migrat
}
} finally {
chatModel.ctrlInitInProgress.value = false
chatModel.dbMigrationInProgress.value = false
}
}

View File

@@ -55,7 +55,7 @@ abstract class NtfManager {
}
fun openChatAction(userId: Long?, chatId: ChatId) {
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
withLongRunningApi {
awaitChatStartedIfNeeded(chatModel)
if (userId != null && userId != chatModel.currentUser.value?.userId && chatModel.currentUser.value != null) {
// TODO include remote host ID in desktop notifications?
@@ -70,7 +70,7 @@ abstract class NtfManager {
}
fun showChatsAction(userId: Long?) {
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
withLongRunningApi {
awaitChatStartedIfNeeded(chatModel)
if (userId != null && userId != chatModel.currentUser.value?.userId && chatModel.currentUser.value != null) {
// TODO include remote host ID in desktop notifications?

View File

@@ -67,14 +67,12 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: suspend (chatId:
launch {
snapshotFlow { chatModel.chatId.value }
.distinctUntilChanged()
.onEach { Log.d(TAG, "TODOCHAT: chatId: activeChatId ${activeChat.value?.id} == new chatId $it ${activeChat.value?.id == it} ") }
.filterNotNull()
.collect { chatId ->
if (activeChat.value?.id != chatId) {
// Redisplay the whole hierarchy if the chat is different to make going from groups to direct chat working correctly
// Also for situation when chatId changes after clicking in notification, etc
activeChat.value = chatModel.getChat(chatId)
Log.d(TAG, "TODOCHAT: chatId: activeChatId became ${activeChat.value?.id}")
}
markUnreadChatAsRead(activeChat, chatModel)
}
@@ -94,12 +92,10 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: suspend (chatId:
}
}
.distinctUntilChanged()
.onEach { Log.d(TAG, "TODOCHAT: chats: activeChatId ${activeChat.value?.id} == new chatId ${it?.id} ${activeChat.value?.id == it?.id} ") }
// Only changed chatInfo is important thing. Other properties can be skipped for reducing recompositions
.filter { it != null && it?.chatInfo != activeChat.value?.chatInfo }
.filter { it != null && it.chatInfo != activeChat.value?.chatInfo }
.collect {
activeChat.value = it
Log.d(TAG, "TODOCHAT: chats: activeChatId became ${activeChat.value?.id}")
}
}
}
@@ -150,7 +146,6 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: suspend (chatId:
},
attachmentOption,
attachmentBottomSheetState,
chatModel.chatItems,
searchText,
useLinkPreviews = useLinkPreviews,
linkMode = chatModel.simplexLinkMode.value,
@@ -228,19 +223,17 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: suspend (chatId:
loadPrevMessages = {
if (chatModel.chatId.value != activeChat.value?.id) return@ChatLayout
val c = chatModel.getChat(chatModel.chatId.value ?: return@ChatLayout)
val firstId = chatModel.chatItems.firstOrNull()?.id
val firstId = chatModel.chatItems.value.firstOrNull()?.id
if (c != null && firstId != null) {
withBGApi {
Log.d(TAG, "TODOCHAT: loadPrevMessages: loading for ${c.id}, current chatId ${ChatModel.chatId.value}, size was ${ChatModel.chatItems.size}")
apiLoadPrevMessages(c, chatModel, firstId, searchText.value)
Log.d(TAG, "TODOCHAT: loadPrevMessages: loaded for ${c.id}, current chatId ${ChatModel.chatId.value}, size now ${ChatModel.chatItems.size}")
}
}
},
deleteMessage = { itemId, mode ->
withBGApi {
val cInfo = chat.chatInfo
val toDeleteItem = chatModel.chatItems.firstOrNull { it.id == itemId }
val toDeleteItem = chatModel.chatItems.value.firstOrNull { it.id == itemId }
val toModerate = toDeleteItem?.memberToModerate(chat.chatInfo)
val groupInfo = toModerate?.first
val groupMember = toModerate?.second
@@ -500,7 +493,6 @@ fun ChatLayout(
composeView: (@Composable () -> Unit),
attachmentOption: MutableState<AttachmentOption?>,
attachmentBottomSheetState: ModalBottomSheetState,
chatItems: List<ChatItem>,
searchValue: State<String>,
useLinkPreviews: Boolean,
linkMode: SimplexLinkMode,
@@ -587,7 +579,7 @@ fun ChatLayout(
.padding(contentPadding)
) {
ChatItemsList(
chat, unreadCount, composeState, chatItems, searchValue,
chat, unreadCount, composeState, searchValue,
useLinkPreviews, linkMode, showMemberInfo, loadPrevMessages, deleteMessage, deleteMessages,
receiveFile, cancelFile, joinGroup, acceptCall, acceptFeature, openDirectChat,
updateContactStats, updateMemberStats, syncContactConnection, syncMemberConnection, findModelChat, findModelMember,
@@ -845,7 +837,6 @@ fun BoxWithConstraintsScope.ChatItemsList(
chat: Chat,
unreadCount: State<Int>,
composeState: MutableState<ComposeState>,
chatItems: List<ChatItem>,
searchValue: State<String>,
useLinkPreviews: Boolean,
linkMode: SimplexLinkMode,
@@ -874,7 +865,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
) {
val listState = rememberLazyListState()
val scope = rememberCoroutineScope()
ScrollToBottom(chat.id, listState, chatItems)
ScrollToBottom(chat.id, listState, chatModel.chatItems)
var prevSearchEmptiness by rememberSaveable { mutableStateOf(searchValue.value.isEmpty()) }
// Scroll to bottom when search value changes from something to nothing and back
LaunchedEffect(searchValue.value.isEmpty()) {
@@ -891,7 +882,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
PreloadItems(listState, ChatPagination.UNTIL_PRELOAD_COUNT, loadPrevMessages)
Spacer(Modifier.size(8.dp))
val reversedChatItems by remember { derivedStateOf { chatItems.reversed().toList() } }
val reversedChatItems by remember { derivedStateOf { chatModel.chatItems.asReversed() } }
val maxHeightRounded = with(LocalDensity.current) { maxHeight.roundToPx() }
val scrollToItem: (Long) -> Unit = { itemId: Long ->
val index = reversedChatItems.indexOfFirst { it.id == itemId }
@@ -944,7 +935,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
}
}
val provider = {
providerForGallery(i, chatItems, cItem.id) { indexInReversed ->
providerForGallery(i, chatModel.chatItems.value, cItem.id) { indexInReversed ->
scope.launch {
listState.scrollToItem(
kotlin.math.min(reversedChatItems.lastIndex, indexInReversed + 1),
@@ -1067,11 +1058,11 @@ fun BoxWithConstraintsScope.ChatItemsList(
}
}
}
FloatingButtons(chatItems, unreadCount, chat.chatStats.minUnreadItemId, searchValue, markRead, setFloatingButton, listState)
FloatingButtons(chatModel.chatItems, unreadCount, chat.chatStats.minUnreadItemId, searchValue, markRead, setFloatingButton, listState)
}
@Composable
private fun ScrollToBottom(chatId: ChatId, listState: LazyListState, chatItems: List<ChatItem>) {
private fun ScrollToBottom(chatId: ChatId, listState: LazyListState, chatItems: State<List<ChatItem>>) {
val scope = rememberCoroutineScope()
// Helps to scroll to bottom after moving from Group to Direct chat
// and prevents scrolling to bottom on orientation change
@@ -1089,7 +1080,7 @@ private fun ScrollToBottom(chatId: ChatId, listState: LazyListState, chatItems:
* When the first visible item (from bottom) is visible (even partially) we can autoscroll to 0 item. Or just scrollBy small distance otherwise
* */
LaunchedEffect(Unit) {
snapshotFlow { chatItems.lastOrNull()?.id }
snapshotFlow { chatItems.value.lastOrNull()?.id }
.distinctUntilChanged()
.filter { listState.layoutInfo.visibleItemsInfo.firstOrNull()?.key != it }
.collect {
@@ -1112,7 +1103,7 @@ private fun ScrollToBottom(chatId: ChatId, listState: LazyListState, chatItems:
@Composable
fun BoxWithConstraintsScope.FloatingButtons(
chatItems: List<ChatItem>,
chatItems: State<List<ChatItem>>,
unreadCount: State<Int>,
minUnreadItemId: Long,
searchValue: State<String>,
@@ -1146,10 +1137,11 @@ fun BoxWithConstraintsScope.FloatingButtons(
val bottomUnreadCount by remember {
derivedStateOf {
if (unreadCount.value == 0) return@derivedStateOf 0
val from = chatItems.lastIndex - firstVisibleIndex - lastIndexOfVisibleItems
if (chatItems.size <= from || from < 0) return@derivedStateOf 0
val items = chatItems.value
val from = items.lastIndex - firstVisibleIndex - lastIndexOfVisibleItems
if (items.size <= from || from < 0) return@derivedStateOf 0
chatItems.subList(from, chatItems.size).count { it.isRcvNew }
items.subList(from, items.size).count { it.isRcvNew }
}
}
val firstVisibleOffset = (-with(LocalDensity.current) { maxHeight.roundToPx() } * 0.8).toInt()
@@ -1195,7 +1187,7 @@ fun BoxWithConstraintsScope.FloatingButtons(
painterResource(MR.images.ic_check),
onClick = {
markRead(
CC.ItemRange(minUnreadItemId, chatItems[chatItems.size - listState.layoutInfo.visibleItemsInfo.lastIndex - 1].id - 1),
CC.ItemRange(minUnreadItemId, chatItems.value[chatItems.size - listState.layoutInfo.visibleItemsInfo.lastIndex - 1].id - 1),
bottomUnreadCount
)
showDropDown.value = false
@@ -1500,7 +1492,6 @@ fun PreviewChatLayout() {
composeView = {},
attachmentOption = remember { mutableStateOf<AttachmentOption?>(null) },
attachmentBottomSheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden),
chatItems = chatItems,
searchValue,
useLinkPreviews = true,
linkMode = SimplexLinkMode.DESCRIPTION,
@@ -1573,7 +1564,6 @@ fun PreviewGroupChatLayout() {
composeView = {},
attachmentOption = remember { mutableStateOf<AttachmentOption?>(null) },
attachmentBottomSheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden),
chatItems = chatItems,
searchValue,
useLinkPreviews = true,
linkMode = SimplexLinkMode.DESCRIPTION,

View File

@@ -665,7 +665,7 @@ fun ComposeView(
fun editPrevMessage() {
if (composeState.value.contextItem != ComposeContextItem.NoContextItem || composeState.value.preview != ComposePreview.NoPreview) return
val lastEditable = chatModel.chatItems.findLast { it.meta.editable }
val lastEditable = chatModel.chatItems.value.findLast { it.meta.editable }
if (lastEditable != null) {
composeState.value = ComposeState(editingItem = lastEditable, useLinkPreviews = useLinkPreviews)
}

View File

@@ -54,7 +54,7 @@ fun AddGroupMembersView(rhId: Long?, groupInfo: GroupInfo, creatingGroup: Boolea
},
inviteMembers = {
allowModifyMembers = false
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
withLongRunningApi(slow = 30_000, deadlock = 120_000) {
for (contactId in selectedContacts) {
val member = chatModel.controller.apiAddMember(rhId, groupInfo.groupId, contactId, selectedRole.value)
if (member != null) {

View File

@@ -3,11 +3,9 @@ package chat.simplex.common.views.chat.group
import InfoRow
import SectionBottomSpacer
import SectionDividerSpaced
import SectionItemView
import SectionSpacer
import SectionTextFooter
import SectionView
import TextIconSpaced
import androidx.compose.desktop.ui.tooling.preview.Preview
import java.net.URI
import androidx.compose.foundation.*
@@ -74,9 +72,8 @@ fun GroupMemberInfoView(
if (chatModel.getContactChat(it) == null) {
chatModel.addChat(c)
}
chatModel.chatItems.clear()
chatModel.chatItemStatuses.clear()
chatModel.chatItems.addAll(c.chatItems)
chatModel.chatItems.replaceAll(c.chatItems)
chatModel.chatId.value = c.id
closeAll()
}

View File

@@ -527,8 +527,9 @@ fun DeleteItemAction(
val range = chatViewItemsRange(currIndex, prevHidden)
if (range != null) {
val itemIds: ArrayList<Long> = arrayListOf()
val reversedChatItems = chatModel.chatItems.asReversed()
for (i in range) {
itemIds.add(chatModel.chatItems.asReversed()[i].id)
itemIds.add(reversedChatItems[i].id)
}
deleteMessagesAlertDialog(itemIds, generalGetString(MR.strings.delete_message_mark_deleted_warning), deleteMessages = deleteMessages)
} else {

View File

@@ -212,18 +212,15 @@ suspend fun openGroupChat(rhId: Long?, groupId: Long, chatModel: ChatModel) {
}
suspend fun openChat(rhId: Long?, chatInfo: ChatInfo, chatModel: ChatModel) {
Log.d(TAG, "TODOCHAT: openChat: opening ${chatInfo.id}, current chatId ${ChatModel.chatId.value}, size ${ChatModel.chatItems.size}")
val chat = chatModel.controller.apiGetChat(rhId, chatInfo.chatType, chatInfo.apiId)
if (chat != null) {
openLoadedChat(chat, chatModel)
Log.d(TAG, "TODOCHAT: openChat: opened ${chatInfo.id}, current chatId ${ChatModel.chatId.value}, size ${ChatModel.chatItems.size}")
}
}
fun openLoadedChat(chat: Chat, chatModel: ChatModel) {
chatModel.chatItems.clear()
chatModel.chatItemStatuses.clear()
chatModel.chatItems.addAll(chat.chatItems)
chatModel.chatItems.replaceAll(chat.chatItems)
chatModel.chatId.value = chat.chatInfo.id
}
@@ -239,8 +236,7 @@ suspend fun apiFindMessages(ch: Chat, chatModel: ChatModel, search: String) {
val chatInfo = ch.chatInfo
val chat = chatModel.controller.apiGetChat(ch.remoteHostId, chatInfo.chatType, chatInfo.apiId, search = search) ?: return
if (chatModel.chatId.value != chat.id) return
chatModel.chatItems.clear()
chatModel.chatItems.addAll(0, chat.chatItems)
chatModel.chatItems.replaceAll(chat.chatItems)
}
suspend fun setGroupMembers(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel) {

View File

@@ -62,7 +62,7 @@ fun DatabaseEncryptionView(m: ChatModel) {
initialRandomDBPassphrase,
progressIndicator,
onConfirmEncrypt = {
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
withLongRunningApi {
encryptDatabase(currentKey, newKey, confirmNewKey, initialRandomDBPassphrase, useKeychain, storedKey, progressIndicator)
}
}

View File

@@ -368,7 +368,7 @@ fun chatArchiveTitle(chatArchiveTime: Instant, chatLastStart: Instant): String {
}
fun startChat(m: ChatModel, chatLastStart: MutableState<Instant?>, chatDbChanged: MutableState<Boolean>, progressIndicator: MutableState<Boolean>? = null) {
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
withLongRunningApi {
try {
progressIndicator?.value = true
if (chatDbChanged.value) {
@@ -581,7 +581,7 @@ private fun importArchive(
progressIndicator.value = true
val archivePath = saveArchiveFromURI(importedArchiveURI)
if (archivePath != null) {
withLongRunningApi(slow = 60_000, deadlock = 180_000) {
withLongRunningApi {
try {
m.controller.apiDeleteStorage()
try {

View File

@@ -5,6 +5,7 @@ import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import chat.simplex.common.ui.theme.DEFAULT_PADDING
@@ -20,7 +21,7 @@ fun DefaultProgressView(description: String?) {
strokeWidth = 2.5.dp
)
if (description != null) {
Text(description)
Text(description, textAlign = TextAlign.Center)
}
}
}

View File

@@ -49,7 +49,7 @@ fun LocalAuthView(m: ChatModel, authRequest: LocalAuthRequest) {
}
private fun deleteStorageAndRestart(m: ChatModel, password: String, completed: (LAResult) -> Unit) {
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
withLongRunningApi {
try {
/** Waiting until [initChatController] finishes */
while (m.ctrlInitInProgress.value) {

View File

@@ -50,7 +50,7 @@ fun SetupDatabasePassphrase(m: ChatModel) {
confirmNewKey,
progressIndicator,
onConfirmEncrypt = {
withLongRunningApi(slow = 30_000, deadlock = 60_000) {
withLongRunningApi {
if (m.chatRunning.value == true) {
// Stop chat if it's started before doing anything
stopChatAsync(m)

View File

@@ -16,6 +16,7 @@
<!-- MainActivity.kt -->
<string name="opening_database">Opening database…</string>
<string name="database_migration_in_progress">Database migration is in progress.\nIt may take a few minutes.</string>
<string name="non_content_uri_alert_title">Invalid file path</string>
<string name="non_content_uri_alert_text">You shared an invalid file path. Report the issue to the app developers.</string>
<string name="app_was_crashed">View crashed</string>

View File

@@ -14,8 +14,7 @@ import androidx.compose.ui.input.key.*
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.*
import chat.simplex.common.model.ChatController
import chat.simplex.common.model.ChatModel
import chat.simplex.common.model.*
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.DEFAULT_START_MODAL_WIDTH
import chat.simplex.common.ui.theme.SimpleXTheme

View File

@@ -25,12 +25,12 @@ android.nonTransitiveRClass=true
android.enableJetifier=true
kotlin.mpp.androidSourceSetLayoutVersion=2
android.version_name=5.5
android.version_code=175
android.version_name=5.5.1
android.version_code=177
desktop.version_name=5.5
desktop.version_code=26
desktop.version_name=5.5.1
desktop.version_code=27
kotlin.version=1.8.20
gradle.plugin.version=7.4.2
compose.version=1.6.0-beta01
compose.version=1.5.10

View File

@@ -1,5 +1,5 @@
name: simplex-chat
version: 5.5.0.4
version: 5.5.1.0
#synopsis:
#description:
homepage: https://github.com/simplex-chat/simplex-chat#readme
@@ -36,7 +36,6 @@ dependencies:
- network >= 3.1.2.7 && < 3.2
- network-transport == 0.5.6
- optparse-applicative >= 0.15 && < 0.17
- process == 1.6.*
- random >= 1.1 && < 1.3
- record-hasfield == 1.0.*
- simple-logger == 0.1.*
@@ -64,11 +63,13 @@ when:
- condition: impl(ghc >= 9.6.2)
dependencies:
- bytestring == 0.11.*
- process == 1.6.*
- template-haskell == 2.20.*
- text >= 2.0.1 && < 2.2
- condition: impl(ghc < 9.6.2)
dependencies:
- bytestring == 0.10.*
- process >= 1.6 && < 1.6.18
- template-haskell == 2.16.*
- text >= 1.2.3.0 && < 1.3
@@ -125,13 +126,19 @@ tests:
- apps/simplex-broadcast-bot/src
- apps/simplex-directory-service/src
main: Test.hs
when:
- condition: impl(ghc >= 9.6.2)
dependencies:
- hspec == 2.11.*
- condition: impl(ghc < 9.6.2)
dependencies:
- hspec == 2.7.*
dependencies:
- QuickCheck == 2.14.*
- simplex-chat
- async == 2.2.*
- deepseq == 1.4.*
- generic-random == 1.5.*
- hspec == 2.11.*
- network == 3.1.*
- silently == 1.2.*
- stm == 2.5.*

View File

@@ -5,7 +5,7 @@ cabal-version: 1.12
-- see: https://github.com/sol/hpack
name: simplex-chat
version: 5.5.0.4
version: 5.5.1.0
category: Web, System, Services, Cryptography
homepage: https://github.com/simplex-chat/simplex-chat#readme
author: simplex.chat
@@ -197,7 +197,6 @@ library
, network >=3.1.2.7 && <3.2
, network-transport ==0.5.6
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
, random >=1.1 && <1.3
, record-hasfield ==1.0.*
, simple-logger ==0.1.*
@@ -217,11 +216,13 @@ library
if impl(ghc >= 9.6.2)
build-depends:
bytestring ==0.11.*
, process ==1.6.*
, template-haskell ==2.20.*
, text >=2.0.1 && <2.2
if impl(ghc < 9.6.2)
build-depends:
bytestring ==0.10.*
, process >=1.6 && <1.6.18
, template-haskell ==2.16.*
, text >=1.2.3.0 && <1.3
@@ -256,7 +257,6 @@ executable simplex-bot
, network >=3.1.2.7 && <3.2
, network-transport ==0.5.6
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
, random >=1.1 && <1.3
, record-hasfield ==1.0.*
, simple-logger ==0.1.*
@@ -277,11 +277,13 @@ executable simplex-bot
if impl(ghc >= 9.6.2)
build-depends:
bytestring ==0.11.*
, process ==1.6.*
, template-haskell ==2.20.*
, text >=2.0.1 && <2.2
if impl(ghc < 9.6.2)
build-depends:
bytestring ==0.10.*
, process >=1.6 && <1.6.18
, template-haskell ==2.16.*
, text >=1.2.3.0 && <1.3
@@ -316,7 +318,6 @@ executable simplex-bot-advanced
, network >=3.1.2.7 && <3.2
, network-transport ==0.5.6
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
, random >=1.1 && <1.3
, record-hasfield ==1.0.*
, simple-logger ==0.1.*
@@ -337,11 +338,13 @@ executable simplex-bot-advanced
if impl(ghc >= 9.6.2)
build-depends:
bytestring ==0.11.*
, process ==1.6.*
, template-haskell ==2.20.*
, text >=2.0.1 && <2.2
if impl(ghc < 9.6.2)
build-depends:
bytestring ==0.10.*
, process >=1.6 && <1.6.18
, template-haskell ==2.16.*
, text >=1.2.3.0 && <1.3
@@ -378,7 +381,6 @@ executable simplex-broadcast-bot
, network >=3.1.2.7 && <3.2
, network-transport ==0.5.6
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
, random >=1.1 && <1.3
, record-hasfield ==1.0.*
, simple-logger ==0.1.*
@@ -399,11 +401,13 @@ executable simplex-broadcast-bot
if impl(ghc >= 9.6.2)
build-depends:
bytestring ==0.11.*
, process ==1.6.*
, template-haskell ==2.20.*
, text >=2.0.1 && <2.2
if impl(ghc < 9.6.2)
build-depends:
bytestring ==0.10.*
, process >=1.6 && <1.6.18
, template-haskell ==2.16.*
, text >=1.2.3.0 && <1.3
@@ -439,7 +443,6 @@ executable simplex-chat
, network ==3.1.*
, network-transport ==0.5.6
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
, random >=1.1 && <1.3
, record-hasfield ==1.0.*
, simple-logger ==0.1.*
@@ -461,11 +464,13 @@ executable simplex-chat
if impl(ghc >= 9.6.2)
build-depends:
bytestring ==0.11.*
, process ==1.6.*
, template-haskell ==2.20.*
, text >=2.0.1 && <2.2
if impl(ghc < 9.6.2)
build-depends:
bytestring ==0.10.*
, process >=1.6 && <1.6.18
, template-haskell ==2.16.*
, text >=1.2.3.0 && <1.3
@@ -505,7 +510,6 @@ executable simplex-directory-service
, network >=3.1.2.7 && <3.2
, network-transport ==0.5.6
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
, random >=1.1 && <1.3
, record-hasfield ==1.0.*
, simple-logger ==0.1.*
@@ -526,11 +530,13 @@ executable simplex-directory-service
if impl(ghc >= 9.6.2)
build-depends:
bytestring ==0.11.*
, process ==1.6.*
, template-haskell ==2.20.*
, text >=2.0.1 && <2.2
if impl(ghc < 9.6.2)
build-depends:
bytestring ==0.10.*
, process >=1.6 && <1.6.18
, template-haskell ==2.16.*
, text >=1.2.3.0 && <1.3
@@ -592,7 +598,6 @@ test-suite simplex-chat-test
, exceptions ==0.10.*
, filepath ==1.4.*
, generic-random ==1.5.*
, hspec ==2.11.*
, http-types ==0.12.*
, http2 >=4.2.2 && <4.3
, memory ==0.18.*
@@ -600,7 +605,6 @@ test-suite simplex-chat-test
, network ==3.1.*
, network-transport ==0.5.6
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
, random >=1.1 && <1.3
, record-hasfield ==1.0.*
, silently ==1.2.*
@@ -622,10 +626,18 @@ test-suite simplex-chat-test
if impl(ghc >= 9.6.2)
build-depends:
bytestring ==0.11.*
, process ==1.6.*
, template-haskell ==2.20.*
, text >=2.0.1 && <2.2
if impl(ghc < 9.6.2)
build-depends:
bytestring ==0.10.*
, process >=1.6 && <1.6.18
, template-haskell ==2.16.*
, text >=1.2.3.0 && <1.3
if impl(ghc >= 9.6.2)
build-depends:
hspec ==2.11.*
if impl(ghc < 9.6.2)
build-depends:
hspec ==2.7.*

View File

@@ -1028,6 +1028,7 @@ processChatCommand' vr = \case
when (memberActive membership && isOwner) . void $ sendGroupMessage' user gInfo members XGrpDel
deleteGroupLinkIfExists user gInfo
deleteMembersConnections user members
updateCIGroupInvitationStatus user gInfo CIGISRejected `catchChatError` \_ -> pure ()
-- functions below are called in separate transactions to prevent crashes on android
-- (possibly, race condition on integrity check?)
withStore' $ \db -> deleteGroupConnectionsAndFiles db user gInfo members
@@ -1035,7 +1036,6 @@ processChatCommand' vr = \case
withStore' $ \db -> deleteGroup db user gInfo
let contactIds = mapMaybe memberContactId members
deleteAgentConnectionsAsync user . concat =<< mapM deleteUnusedContact contactIds
updateCIGroupInvitationStatus user gInfo CIGISRejected `catchChatError` \_ -> pure ()
pure $ CRGroupDeletedUser user gInfo
where
deleteUnusedContact :: ContactId -> m [ConnId]

View File

@@ -271,9 +271,18 @@ chatSendRemoteCmd :: ChatController -> Maybe RemoteHostId -> B.ByteString -> IO
chatSendRemoteCmd cc rh s = J.encode . APIResponse Nothing rh <$> runReaderT (execChatCommand rh s) cc
chatRecvMsg :: ChatController -> IO JSONByteString
chatRecvMsg ChatController {outputQ} = json <$> atomically (readTBQueue outputQ)
chatRecvMsg ChatController {outputQ} = json <$> readChatResponse
where
json (corr, remoteHostId, resp) = J.encode APIResponse {corr, remoteHostId, resp}
readChatResponse = do
out@(_, _, cr) <- atomically $ readTBQueue outputQ
if filterEvent cr then pure out else readChatResponse
filterEvent = \case
CRGroupSubscribed {} -> False
CRGroupEmpty {} -> False
CRMemberSubSummary {} -> False
CRPendingSubSummary {} -> False
_ -> True
chatRecvMsgWait :: ChatController -> Int -> IO JSONByteString
chatRecvMsgWait cc time = fromMaybe "" <$> timeout time (chatRecvMsg cc)

View File

@@ -224,8 +224,6 @@ testChatApi tmp = do
chatSendCmd cc "/_start" `shouldReturn` chatStarted
chatRecvMsg cc `shouldReturn` networkStatuses
chatRecvMsg cc `shouldReturn` userContactSubSummary
chatRecvMsg cc `shouldReturn` memberSubSummary
chatRecvMsgWait cc 10000 `shouldReturn` pendingSubSummary
chatRecvMsgWait cc 10000 `shouldReturn` ""
chatParseMarkdown "hello" `shouldBe` "{}"
chatParseMarkdown "*hello*" `shouldBe` parsedMarkdown

View File

@@ -250,5 +250,7 @@
"stable-versions-built-by-f-droid-org": "Stable versions built by F-Droid.org",
"releases-to-this-repo-are-done-1-2-days-later": "The releases to this repo are done 1-2 days later",
"f-droid-page-f-droid-org-repo-section-text": "SimpleX Chat and F-Droid.org repositories sign builds with the different keys. To switch, please <a href='/docs/guide/chat-profiles.html#move-your-chat-profiles-to-another-device'>export</a> the chat database and re-install the app.",
"jobs": "Join team"
"jobs": "Join team",
"please-enable-javascript": "Please enable JavaScript to see the QR code.",
"please-use-link-in-mobile-app": "Please use the link in the mobile app"
}

View File

@@ -30,8 +30,12 @@
<div class="absolute mt-[-100px]">
<img class="" src="/img/new/contact_page_mobile.png" alt="">
</div>
<noscript class="z-10 flex flex-col items-center pt-[40px] ml-[-15px]">
<p class="text-2xl font-medium text-center max-w-[234px] mb-32">{{ "please-enable-javascript" | i18n({}, lang ) | safe }}</p>
</noscript>
<div class="z-10 flex flex-col items-center pt-[40px] ml-[-15px]">
<div class="z-10 flex flex-col items-center pt-[40px] ml-[-15px] d-none-if-js-disabled">
<p class="text-base font-medium text-center max-w-[234px]">{{ "scan-qr-code-from-mobile-app" | i18n({}, lang ) | safe }}</p>
<canvas class="conn_req_uri_qrcode"></canvas>
</div>
@@ -61,7 +65,11 @@
</div>
<div class="flex flex-col justify-center items-center w-full max-w-[468px] h-[131px] rounded-[30px] border-[1px] border-[#A8B0B4] dark:border-white border-opacity-60 mb-6 relative">
<p class="text-xl font-medium text-grey-black dark:text-white mb-4">{{ "connect-in-app" | i18n({}, lang ) | safe }}</p>
<p class="text-xl font-medium text-grey-black dark:text-white mb-4 d-none-if-js-disabled">{{ "connect-in-app" | i18n({}, lang ) | safe }}</p>
<noscript>
<p class="text-xl font-medium text-grey-black dark:text-white mb-4">{{ "please-use-link-in-mobile-app" | i18n({}, lang ) | safe }}</p>
</noscript>
<a id="mobile_conn_req_uri" class="bg-[#0053D0] text-white py-3 px-8 rounded-[34px] h-[44px] text-[16px] leading-[19px] tracking-[0.02em]">{{ "open-simplex-app" | i18n({}, lang ) | safe }}</a>
<div class="absolute bg-[#0197FF] h-[44px] w-[44px] rounded-full flex items-center justify-center top-0 left-0 translate-x-[-30%] translate-y-[-30%]">
@@ -69,7 +77,7 @@
</div>
</div>
<div class="flex flex-col justify-center items-center w-full max-w-[468px] h-[131px] rounded-[30px] border-[1px] border-[#A8B0B4] dark:border-white border-opacity-60 relative">
<div class="flex flex-col justify-center items-center w-full max-w-[468px] h-[131px] rounded-[30px] border-[1px] border-[#A8B0B4] dark:border-white border-opacity-60 relative d-none-if-js-disabled">
<p class="text-xl font-medium text-grey-black dark:text-white max-w-[230px] text-center">{{ "tap-the-connect-button-in-the-app" | i18n({}, lang ) | safe }}</p>
<div class="absolute bg-[#0197FF] h-[44px] w-[44px] rounded-full flex items-center justify-center top-0 left-0 translate-x-[-30%] translate-y-[-30%]">
@@ -81,7 +89,7 @@
</section>
<section class="hidden md:block bg-secondary-bg-light dark:bg-secondary-bg-dark py-[20px]">
<section class="hidden md:block bg-secondary-bg-light dark:bg-secondary-bg-dark py-[20px] d-none-if-js-disabled">
<div class="container px-5">
<div class="text-grey-black dark:text-white">
@@ -164,3 +172,7 @@
{# join simplex #}
{% include "sections/join_simplex.html" %}
<script>
document.querySelectorAll('.d-none-if-js-disabled').forEach(el => el.classList.remove('d-none-if-js-disabled'));
</script>

View File

@@ -957,3 +957,7 @@ p a{
top: calc(66px + 2rem);
}
}
.d-none-if-js-disabled{
display: none !important;
}