Compare commits

...

16 Commits

Author SHA1 Message Date
Evgeny Poberezkin
6d523d5b4b 5.5.4: ios 199, android 183, desktop 30 2024-02-17 22:50:13 +00:00
Evgeny Poberezkin
2a321b3ff8 ios: fix showing notification on sent messages 2024-02-17 21:09:19 +00:00
Stanislav Dmitrenko
865a32c608 android, desktop: refactor alerts for slow calls (#3811)
* android, desktop: refactor alerts for slow calls

* sharing text in alerts

* more time to send message

* removed suspend modifier from processing messages

* change

* Revert "removed suspend modifier from processing messages"

This reverts commit 895e804c1b.

* Revert "change"

This reverts commit 013abf49e6.
2024-02-17 18:01:04 +00:00
Evgeny Poberezkin
91f10c056f docs: change download links to the latest release 2024-02-11 16:26:10 +00:00
Evgeny Poberezkin
fec34ca875 5.5.3: ios 198, android 181, desktop 29 2024-02-11 11:14:07 +00:00
Evgeny Poberezkin
3a0920e950 core: 5.5.3.0 (simplexmq 5.5.2.0) 2024-02-10 23:24:00 +00:00
Evgeny Poberezkin
7e9e71ffbd core: update simplexmq (extensible smp handshake) 2024-02-06 07:39:32 +00:00
Evgeny Poberezkin
09bbaa1c94 5.5.2: ios 196, android 179, desktop 28 2024-02-02 15:32:25 +00:00
Evgeny Poberezkin
3a879b755b core: 5.5.2.0 (simplexmq 5.5.1.2) 2024-02-02 08:30:26 +00:00
Evgeny Poberezkin
c6f4d62d6c core: update simplexmq (fix socket/memory leak on resubscriptions) 2024-02-01 16:36:32 +00:00
Stanislav Dmitrenko
5ad356172f scripts: check that all symbols were exported (#3779)
* scripts: check that all symbols were exported

* changes

* windows

---------

Co-authored-by: Avently <avently@local>
2024-01-31 14:47:10 +00:00
Stanislav Dmitrenko
0a6c464a8d desktop (windows): added a missing symbol to lib build (#3778) 2024-01-31 13:58:30 +00:00
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
45 changed files with 256 additions and 193 deletions

View File

@@ -1861,7 +1861,9 @@ func chatItemSimpleUpdate(_ user: any UserLike, _ aChatItem: AChatItem) async {
let cItem = aChatItem.chatItem let cItem = aChatItem.chatItem
if active(user) { if active(user) {
if await MainActor.run(body: { m.upsertChatItem(cInfo, cItem) }) { if await MainActor.run(body: { m.upsertChatItem(cInfo, cItem) }) {
NtfManager.shared.notifyMessageReceived(user, cInfo, cItem) if cItem.showNotification {
NtfManager.shared.notifyMessageReceived(user, cInfo, cItem)
}
} }
} }
} }

View File

@@ -29,6 +29,11 @@
5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; }; 5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; };
5C13730B28156D2700F43030 /* ContactConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C13730A28156D2700F43030 /* ContactConnectionView.swift */; }; 5C13730B28156D2700F43030 /* ContactConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C13730A28156D2700F43030 /* ContactConnectionView.swift */; };
5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; }; 5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; };
5C29C3AF2B783F82003DF84C /* libHSsimplex-chat-5.5.3.0-AUrnxTuqxo1yzY63w39Bk-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C29C3AA2B783F82003DF84C /* libHSsimplex-chat-5.5.3.0-AUrnxTuqxo1yzY63w39Bk-ghc9.6.3.a */; };
5C29C3B02B783F82003DF84C /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C29C3AB2B783F82003DF84C /* libgmpxx.a */; };
5C29C3B12B783F82003DF84C /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C29C3AC2B783F82003DF84C /* libffi.a */; };
5C29C3B22B783F82003DF84C /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C29C3AD2B783F82003DF84C /* libgmp.a */; };
5C29C3B32B783F82003DF84C /* libHSsimplex-chat-5.5.3.0-AUrnxTuqxo1yzY63w39Bk.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C29C3AE2B783F82003DF84C /* libHSsimplex-chat-5.5.3.0-AUrnxTuqxo1yzY63w39Bk.a */; };
5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; }; 5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; };
5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260A27A30CFA00F70299 /* ChatListView.swift */; }; 5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260A27A30CFA00F70299 /* ChatListView.swift */; };
5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260E27A30FDC00F70299 /* ChatView.swift */; }; 5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260E27A30FDC00F70299 /* ChatView.swift */; };
@@ -61,11 +66,6 @@
5C7505A527B679EE00BE3227 /* NavLinkPlain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A427B679EE00BE3227 /* NavLinkPlain.swift */; }; 5C7505A527B679EE00BE3227 /* NavLinkPlain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A427B679EE00BE3227 /* NavLinkPlain.swift */; };
5C7505A827B6D34800BE3227 /* ChatInfoToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A727B6D34800BE3227 /* ChatInfoToolbar.swift */; }; 5C7505A827B6D34800BE3227 /* ChatInfoToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A727B6D34800BE3227 /* ChatInfoToolbar.swift */; };
5C764E89279CBCB3000C6508 /* ChatModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C764E88279CBCB3000C6508 /* ChatModel.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 */; }; 5C8F01CD27A6F0D8007D2C8D /* CodeScanner in Frameworks */ = {isa = PBXBuildFile; productRef = 5C8F01CC27A6F0D8007D2C8D /* CodeScanner */; };
5C93292F29239A170090FFF9 /* ProtocolServersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C93292E29239A170090FFF9 /* ProtocolServersView.swift */; }; 5C93292F29239A170090FFF9 /* ProtocolServersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C93292E29239A170090FFF9 /* ProtocolServersView.swift */; };
5C93293129239BED0090FFF9 /* ProtocolServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C93293029239BED0090FFF9 /* ProtocolServerView.swift */; }; 5C93293129239BED0090FFF9 /* ProtocolServerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C93293029239BED0090FFF9 /* ProtocolServerView.swift */; };
@@ -278,6 +278,11 @@
5C245F3C2B501E98001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; }; 5C245F3C2B501E98001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
5C245F3D2B501F13001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = "tr.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; }; 5C245F3D2B501F13001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = "tr.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
5C245F3E2B501F13001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; }; 5C245F3E2B501F13001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
5C29C3AA2B783F82003DF84C /* libHSsimplex-chat-5.5.3.0-AUrnxTuqxo1yzY63w39Bk-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.5.3.0-AUrnxTuqxo1yzY63w39Bk-ghc9.6.3.a"; sourceTree = "<group>"; };
5C29C3AB2B783F82003DF84C /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5C29C3AC2B783F82003DF84C /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5C29C3AD2B783F82003DF84C /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5C29C3AE2B783F82003DF84C /* libHSsimplex-chat-5.5.3.0-AUrnxTuqxo1yzY63w39Bk.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.5.3.0-AUrnxTuqxo1yzY63w39Bk.a"; sourceTree = "<group>"; };
5C2E260627A2941F00F70299 /* SimpleXAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleXAPI.swift; sourceTree = "<group>"; }; 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleXAPI.swift; sourceTree = "<group>"; };
5C2E260A27A30CFA00F70299 /* ChatListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListView.swift; sourceTree = "<group>"; }; 5C2E260A27A30CFA00F70299 /* ChatListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListView.swift; sourceTree = "<group>"; };
5C2E260E27A30FDC00F70299 /* ChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatView.swift; sourceTree = "<group>"; }; 5C2E260E27A30FDC00F70299 /* ChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatView.swift; sourceTree = "<group>"; };
@@ -325,11 +330,6 @@
5C7505A427B679EE00BE3227 /* NavLinkPlain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavLinkPlain.swift; sourceTree = "<group>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 5C84FE9429A2179C00D95B1A /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@@ -514,13 +514,13 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
5C29C3B02B783F82003DF84C /* libgmpxx.a in Frameworks */,
5C29C3B32B783F82003DF84C /* libHSsimplex-chat-5.5.3.0-AUrnxTuqxo1yzY63w39Bk.a in Frameworks */,
5CE2BA93284534B000EC33A6 /* libiconv.tbd 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 */,
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */, 5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
5C83A1B12B5EF67D00AE0A4A /* libgmpxx.a in Frameworks */, 5C29C3B12B783F82003DF84C /* libffi.a in Frameworks */,
5C29C3B22B783F82003DF84C /* libgmp.a in Frameworks */,
5C29C3AF2B783F82003DF84C /* libHSsimplex-chat-5.5.3.0-AUrnxTuqxo1yzY63w39Bk-ghc9.6.3.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -582,11 +582,11 @@
5C764E5C279C70B7000C6508 /* Libraries */ = { 5C764E5C279C70B7000C6508 /* Libraries */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5C83A1AB2B5EF67D00AE0A4A /* libffi.a */, 5C29C3AC2B783F82003DF84C /* libffi.a */,
5C83A1A82B5EF67D00AE0A4A /* libgmp.a */, 5C29C3AD2B783F82003DF84C /* libgmp.a */,
5C83A1AC2B5EF67D00AE0A4A /* libgmpxx.a */, 5C29C3AB2B783F82003DF84C /* libgmpxx.a */,
5C83A1A92B5EF67D00AE0A4A /* libHSsimplex-chat-5.5.0.4-HTW6wkBBAjO2GDtnvnI9O3-ghc9.6.3.a */, 5C29C3AA2B783F82003DF84C /* libHSsimplex-chat-5.5.3.0-AUrnxTuqxo1yzY63w39Bk-ghc9.6.3.a */,
5C83A1AA2B5EF67D00AE0A4A /* libHSsimplex-chat-5.5.0.4-HTW6wkBBAjO2GDtnvnI9O3.a */, 5C29C3AE2B783F82003DF84C /* libHSsimplex-chat-5.5.3.0-AUrnxTuqxo1yzY63w39Bk.a */,
); );
path = Libraries; path = Libraries;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1509,7 +1509,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194; CURRENT_PROJECT_VERSION = 199;
DEVELOPMENT_TEAM = 5NN7GUYB6T; DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@@ -1531,7 +1531,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 5.5; MARKETING_VERSION = 5.5.4;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX; PRODUCT_NAME = SimpleX;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@@ -1552,7 +1552,7 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194; CURRENT_PROJECT_VERSION = 199;
DEVELOPMENT_TEAM = 5NN7GUYB6T; DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@@ -1574,7 +1574,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 5.5; MARKETING_VERSION = 5.5.4;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX; PRODUCT_NAME = SimpleX;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@@ -1633,7 +1633,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194; CURRENT_PROJECT_VERSION = 199;
DEVELOPMENT_TEAM = 5NN7GUYB6T; DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -1646,7 +1646,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 5.5; MARKETING_VERSION = 5.5.4;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1665,7 +1665,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194; CURRENT_PROJECT_VERSION = 199;
DEVELOPMENT_TEAM = 5NN7GUYB6T; DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -1678,7 +1678,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 5.5; MARKETING_VERSION = 5.5.4;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1697,7 +1697,7 @@
APPLICATION_EXTENSION_API_ONLY = YES; APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194; CURRENT_PROJECT_VERSION = 199;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T; DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1721,7 +1721,7 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Libraries/sim", "$(PROJECT_DIR)/Libraries/sim",
); );
MARKETING_VERSION = 5.5; MARKETING_VERSION = 5.5.4;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos; SDKROOT = iphoneos;
@@ -1743,7 +1743,7 @@
APPLICATION_EXTENSION_API_ONLY = YES; APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 194; CURRENT_PROJECT_VERSION = 199;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T; DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1767,7 +1767,7 @@
"$(inherited)", "$(inherited)",
"$(PROJECT_DIR)/Libraries/sim", "$(PROJECT_DIR)/Libraries/sim",
); );
MARKETING_VERSION = 5.5; MARKETING_VERSION = 5.5.4;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos; SDKROOT = iphoneos;

View File

@@ -71,7 +71,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
Log.d(TAG, "onStateChanged: $event") Log.d(TAG, "onStateChanged: $event")
withBGApi { withLongRunningApi {
when (event) { when (event) {
Lifecycle.Event.ON_START -> { Lifecycle.Event.ON_START -> {
isAppOnForeground = true isAppOnForeground = true

View File

@@ -14,17 +14,27 @@ import chat.simplex.common.views.helpers.*
import java.io.BufferedOutputStream import java.io.BufferedOutputStream
import java.io.File import java.io.File
import chat.simplex.res.MR import chat.simplex.res.MR
import kotlin.math.min
actual fun ClipboardManager.shareText(text: String) { actual fun ClipboardManager.shareText(text: String) {
val sendIntent: Intent = Intent().apply { var text = text
action = Intent.ACTION_SEND for (i in 10 downTo 1) {
putExtra(Intent.EXTRA_TEXT, text) try {
type = "text/plain" val sendIntent: Intent = Intent().apply {
flags = FLAG_ACTIVITY_NEW_TASK action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, text)
type = "text/plain"
flags = FLAG_ACTIVITY_NEW_TASK
}
val shareIntent = Intent.createChooser(sendIntent, null)
shareIntent.addFlags(FLAG_ACTIVITY_NEW_TASK)
androidAppContext.startActivity(shareIntent)
break
} catch (e: Exception) {
Log.e(TAG, "Failed to share text: ${e.stackTraceToString()}")
text = text.substring(0, min(i * 1000, text.length))
}
} }
val shareIntent = Intent.createChooser(sendIntent, null)
shareIntent.addFlags(FLAG_ACTIVITY_NEW_TASK)
androidAppContext.startActivity(shareIntent)
} }
actual fun shareFile(text: String, fileSource: CryptoFile) { actual fun shareFile(text: String, fileSource: CryptoFile) {

View File

@@ -12,6 +12,8 @@ import androidx.activity.compose.setContent
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import chat.simplex.common.AppScreen 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 chat.simplex.common.views.helpers.*
import androidx.compose.ui.platform.LocalContext as LocalContext1 import androidx.compose.ui.platform.LocalContext as LocalContext1
import chat.simplex.res.MR import chat.simplex.res.MR
@@ -112,7 +114,8 @@ actual class GlobalExceptionsHandler: Thread.UncaughtExceptionHandler {
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
AlertManager.shared.showAlertMsg( AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.app_was_crashed), title = generalGetString(MR.strings.app_was_crashed),
text = e.stackTraceToString() text = e.stackTraceToString(),
shareText = true
) )
} }
} }

View File

@@ -2,6 +2,7 @@ package chat.simplex.common.model
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.runtime.snapshots.SnapshotStateMap import androidx.compose.runtime.snapshots.SnapshotStateMap
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.SpanStyle
@@ -56,7 +57,7 @@ object ChatModel {
// current chat // current chat
val chatId = mutableStateOf<String?>(null) val chatId = mutableStateOf<String?>(null)
val chatItems = mutableStateListOf<ChatItem>() val chatItems = mutableStateOf(SnapshotStateList<ChatItem>())
// rhId, chatId // rhId, chatId
val deletedChats = mutableStateOf<List<Pair<Long?, String>>>(emptyList()) val deletedChats = mutableStateOf<List<Pair<Long?, String>>>(emptyList())
val chatItemStatuses = mutableMapOf<Long, CIStatus>() val chatItemStatuses = mutableMapOf<Long, CIStatus>()
@@ -268,18 +269,15 @@ object ChatModel {
} else { } else {
addChat(Chat(remoteHostId = rhId, chatInfo = cInfo, chatItems = arrayListOf(cItem))) 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) { withContext(Dispatchers.Main) {
// add to current chat // add to current chat
if (chatId.value == cInfo.id) { 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 // Prevent situation when chat item already in the list received from backend
if (chatItems.none { it.id == cItem.id }) { if (chatItems.value.none { it.id == cItem.id }) {
if (chatItems.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) { if (chatItems.value.lastOrNull()?.id == ChatItem.TEMP_LIVE_CHAT_ITEM_ID) {
chatItems.add(kotlin.math.max(0, chatItems.lastIndex), cItem) chatItems.add(kotlin.math.max(0, chatItems.value.lastIndex), cItem)
} else { } else {
chatItems.add(cItem) chatItems.add(cItem)
Log.d(TAG, "TODOCHAT: addChatItem: added to chat ${chatId.value} from ${cInfo.id} ${cItem.id}, size ${chatItems.size}")
} }
} }
} }
@@ -306,14 +304,13 @@ object ChatModel {
addChat(Chat(remoteHostId = rhId, chatInfo = cInfo, chatItems = arrayListOf(cItem))) addChat(Chat(remoteHostId = rhId, chatInfo = cInfo, chatItems = arrayListOf(cItem)))
res = true res = true
} }
Log.d(TAG, "TODOCHAT: upsertChatItem: upserting to chat ${chatId.value} from ${cInfo.id} ${cItem.id}, size ${chatItems.size}")
return withContext(Dispatchers.Main) { return withContext(Dispatchers.Main) {
// update current chat // update current chat
if (chatId.value == cInfo.id) { 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) { if (itemIndex >= 0) {
chatItems[itemIndex] = cItem items[itemIndex] = cItem
Log.d(TAG, "TODOCHAT: upsertChatItem: updated in chat $chatId from ${cInfo.id} ${cItem.id}, size ${chatItems.size}")
false false
} else { } else {
val status = chatItemStatuses.remove(cItem.id) val status = chatItemStatuses.remove(cItem.id)
@@ -323,7 +320,6 @@ object ChatModel {
cItem cItem
} }
chatItems.add(ci) chatItems.add(ci)
Log.d(TAG, "TODOCHAT: upsertChatItem: added to chat $chatId from ${cInfo.id} ${cItem.id}, size ${chatItems.size}")
true true
} }
} else { } else {
@@ -335,9 +331,10 @@ object ChatModel {
suspend fun updateChatItem(cInfo: ChatInfo, cItem: ChatItem, status: CIStatus? = null) { suspend fun updateChatItem(cInfo: ChatInfo, cItem: ChatItem, status: CIStatus? = null) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
if (chatId.value == cInfo.id) { 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) { if (itemIndex >= 0) {
chatItems[itemIndex] = cItem items[itemIndex] = cItem
} }
} else if (status != null) { } else if (status != null) {
chatItemStatuses[cItem.id] = status chatItemStatuses[cItem.id] = status
@@ -361,10 +358,10 @@ object ChatModel {
} }
// remove from current chat // remove from current chat
if (chatId.value == cInfo.id) { if (chatId.value == cInfo.id) {
val itemIndex = chatItems.indexOfFirst { it.id == cItem.id } chatItems.removeAll {
if (itemIndex >= 0) { val remove = it.id == cItem.id
AudioPlayer.stop(chatItems[itemIndex]) if (remove) { AudioPlayer.stop(it) }
chatItems.removeAt(itemIndex) remove
} }
} }
} }
@@ -405,7 +402,7 @@ object ChatModel {
} }
fun removeLiveDummy() { 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() chatItems.removeLast()
} }
} }
@@ -437,14 +434,14 @@ object ChatModel {
var markedRead = 0 var markedRead = 0
if (chatId.value == cInfo.id) { if (chatId.value == cInfo.id) {
var i = 0 var i = 0
Log.d(TAG, "TODOCHAT: markItemsReadInCurrentChat: marking read ${cInfo.id}, current chatId ${chatId.value}, size was ${chatItems.size}") val items = chatItems.value
while (i < chatItems.count()) { while (i < items.size) {
val item = chatItems[i] val item = items[i]
if (item.meta.itemStatus is CIStatus.RcvNew && (range == null || (range.from <= item.id && item.id <= range.to))) { if (item.meta.itemStatus is CIStatus.RcvNew && (range == null || (range.from <= item.id && item.id <= range.to))) {
val newItem = item.withStatus(CIStatus.RcvRead()) val newItem = item.withStatus(CIStatus.RcvRead())
chatItems[i] = newItem items[i] = newItem
if (newItem.meta.itemLive != true && newItem.meta.itemTimed?.ttl != null) { 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))) deleteAt = Clock.System.now() + newItem.meta.itemTimed.ttl.toDuration(DurationUnit.SECONDS)))
) )
} }
@@ -452,7 +449,6 @@ object ChatModel {
} }
i += 1 i += 1
} }
Log.d(TAG, "TODOCHAT: markItemsReadInCurrentChat: marked read ${cInfo.id}, current chatId ${chatId.value}, size now ${chatItems.size}")
} }
return markedRead return markedRead
} }
@@ -2006,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 { enum class CIMergeCategory {
MemberConnected, MemberConnected,
RcvGroupEvent, RcvGroupEvent,

View File

@@ -451,7 +451,21 @@ object ChatController {
} }
try { try {
val msg = recvMsg(ctrl) val msg = recvMsg(ctrl)
if (msg != null) processReceivedMsg(msg) if (msg != null) {
val finishedWithoutTimeout = withTimeoutOrNull(60_000L) {
processReceivedMsg(msg)
}
if (finishedWithoutTimeout == null) {
Log.e(TAG, "Timeout reached while processing received message: " + msg.resp.responseType)
if (appPreferences.developerTools.get() && appPreferences.showSlowApiCalls.get()) {
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.possible_slow_function_title),
text = generalGetString(MR.strings.possible_slow_function_desc).format(60, msg.resp.responseType + "\n" + Exception().stackTraceToString()),
shareText = true
)
}
}
}
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "ChatController recvMsg/processReceivedMsg exception: " + e.stackTraceToString()); Log.e(TAG, "ChatController recvMsg/processReceivedMsg exception: " + e.stackTraceToString());
} catch (e: Throwable) { } catch (e: Throwable) {
@@ -1685,7 +1699,7 @@ object ChatController {
chatModel.networkStatuses[s.agentConnId] = s.networkStatus chatModel.networkStatuses[s.agentConnId] = s.networkStatus
} }
} }
is CR.NewChatItem -> { is CR.NewChatItem -> withBGApi {
val cInfo = r.chatItem.chatInfo val cInfo = r.chatItem.chatInfo
val cItem = r.chatItem.chatItem val cItem = r.chatItem.chatItem
if (active(r.user)) { if (active(r.user)) {
@@ -1700,7 +1714,7 @@ object ChatController {
((mc is MsgContent.MCImage && file.fileSize <= MAX_IMAGE_SIZE_AUTO_RCV) ((mc is MsgContent.MCImage && file.fileSize <= MAX_IMAGE_SIZE_AUTO_RCV)
|| (mc is MsgContent.MCVideo && file.fileSize <= MAX_VIDEO_SIZE_AUTO_RCV) || (mc is MsgContent.MCVideo && file.fileSize <= MAX_VIDEO_SIZE_AUTO_RCV)
|| (mc is MsgContent.MCVoice && file.fileSize <= MAX_VOICE_SIZE_AUTO_RCV && file.fileStatus !is CIFileStatus.RcvAccepted))) { || (mc is MsgContent.MCVoice && file.fileSize <= MAX_VOICE_SIZE_AUTO_RCV && file.fileStatus !is CIFileStatus.RcvAccepted))) {
withBGApi { receiveFile(rhId, r.user, file.fileId, auto = true) } receiveFile(rhId, r.user, file.fileId, auto = true)
} }
if (cItem.showNotification && (allowedToShowNotification() || chatModel.chatId.value != cInfo.id || chatModel.remoteHostId() != rhId)) { if (cItem.showNotification && (allowedToShowNotification() || chatModel.chatId.value != cInfo.id || chatModel.remoteHostId() != rhId)) {
ntfManager.notifyMessageReceived(r.user, cInfo, cItem) ntfManager.notifyMessageReceived(r.user, cInfo, cItem)

View File

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

View File

@@ -267,7 +267,7 @@ fun ComposeView(
fun loadLinkPreview(url: String, wait: Long? = null) { fun loadLinkPreview(url: String, wait: Long? = null) {
if (pendingLinkUrl.value == url) { if (pendingLinkUrl.value == url) {
composeState.value = composeState.value.copy(preview = ComposePreview.CLinkPreview(null)) composeState.value = composeState.value.copy(preview = ComposePreview.CLinkPreview(null))
withLongRunningApi(slow = 30_000, deadlock = 60_000) { withLongRunningApi(slow = 60_000) {
if (wait != null) delay(wait) if (wait != null) delay(wait)
val lp = getLinkPreview(url) val lp = getLinkPreview(url)
if (lp != null && pendingLinkUrl.value == url) { if (lp != null && pendingLinkUrl.value == url) {
@@ -551,7 +551,7 @@ fun ComposeView(
} }
fun sendMessage(ttl: Int?) { fun sendMessage(ttl: Int?) {
withLongRunningApi(slow = 30_000, deadlock = 60_000) { withLongRunningApi(slow = 120_000) {
sendMessageAsync(null, false, ttl) sendMessageAsync(null, false, ttl)
} }
} }
@@ -665,7 +665,7 @@ fun ComposeView(
fun editPrevMessage() { fun editPrevMessage() {
if (composeState.value.contextItem != ComposeContextItem.NoContextItem || composeState.value.preview != ComposePreview.NoPreview) return 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) { if (lastEditable != null) {
composeState.value = ComposeState(editingItem = lastEditable, useLinkPreviews = useLinkPreviews) composeState.value = ComposeState(editingItem = lastEditable, useLinkPreviews = useLinkPreviews)
} }

View File

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

View File

@@ -152,7 +152,7 @@ fun leaveGroupDialog(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel, cl
text = generalGetString(MR.strings.you_will_stop_receiving_messages_from_this_group_chat_history_will_be_preserved), text = generalGetString(MR.strings.you_will_stop_receiving_messages_from_this_group_chat_history_will_be_preserved),
confirmText = generalGetString(MR.strings.leave_group_button), confirmText = generalGetString(MR.strings.leave_group_button),
onConfirm = { onConfirm = {
withBGApi { withLongRunningApi(60_000) {
chatModel.controller.leaveGroup(rhId, groupInfo.groupId) chatModel.controller.leaveGroup(rhId, groupInfo.groupId)
close?.invoke() close?.invoke()
} }

View File

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

View File

@@ -94,7 +94,7 @@ fun CIFileView(
FileProtocol.LOCAL -> {} FileProtocol.LOCAL -> {}
} }
file.fileStatus is CIFileStatus.RcvComplete || (file.fileStatus is CIFileStatus.SndStored && file.fileProtocol == FileProtocol.LOCAL) -> { file.fileStatus is CIFileStatus.RcvComplete || (file.fileStatus is CIFileStatus.SndStored && file.fileProtocol == FileProtocol.LOCAL) -> {
withLongRunningApi(slow = 60_000, deadlock = 600_000) { withLongRunningApi(slow = 600_000) {
var filePath = getLoadedFilePath(file) var filePath = getLoadedFilePath(file)
if (chatModel.connectedToRemote() && filePath == null) { if (chatModel.connectedToRemote() && filePath == null) {
file.loadRemoteFile(true) file.loadRemoteFile(true)

View File

@@ -41,7 +41,7 @@ fun CIVideoView(
val filePath = remember(file, CIFile.cachedRemoteFileRequests.toList()) { mutableStateOf(getLoadedFilePath(file)) } val filePath = remember(file, CIFile.cachedRemoteFileRequests.toList()) { mutableStateOf(getLoadedFilePath(file)) }
if (chatModel.connectedToRemote()) { if (chatModel.connectedToRemote()) {
LaunchedEffect(file) { LaunchedEffect(file) {
withLongRunningApi(slow = 60_000, deadlock = 600_000) { withLongRunningApi(slow = 600_000) {
if (file != null && file.loaded && getLoadedFilePath(file) == null) { if (file != null && file.loaded && getLoadedFilePath(file) == null) {
file.loadRemoteFile(false) file.loadRemoteFile(false)
filePath.value = getLoadedFilePath(file) filePath.value = getLoadedFilePath(file)

View File

@@ -213,7 +213,7 @@ fun ChatItemView(
showMenu.value = false showMenu.value = false
} }
if (chatModel.connectedToRemote() && fileSource == null) { if (chatModel.connectedToRemote() && fileSource == null) {
withLongRunningApi(slow = 60_000, deadlock = 600_000) { withLongRunningApi(slow = 600_000) {
cItem.file?.loadRemoteFile(true) cItem.file?.loadRemoteFile(true)
fileSource = getLoadedFileSource(cItem.file) fileSource = getLoadedFileSource(cItem.file)
shareIfExists() shareIfExists()
@@ -527,8 +527,9 @@ fun DeleteItemAction(
val range = chatViewItemsRange(currIndex, prevHidden) val range = chatViewItemsRange(currIndex, prevHidden)
if (range != null) { if (range != null) {
val itemIds: ArrayList<Long> = arrayListOf() val itemIds: ArrayList<Long> = arrayListOf()
val reversedChatItems = chatModel.chatItems.asReversed()
for (i in range) { 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) deleteMessagesAlertDialog(itemIds, generalGetString(MR.strings.delete_message_mark_deleted_warning), deleteMessages = deleteMessages)
} else { } 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) { 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) val chat = chatModel.controller.apiGetChat(rhId, chatInfo.chatType, chatInfo.apiId)
if (chat != null) { if (chat != null) {
openLoadedChat(chat, chatModel) 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) { fun openLoadedChat(chat: Chat, chatModel: ChatModel) {
chatModel.chatItems.clear()
chatModel.chatItemStatuses.clear() chatModel.chatItemStatuses.clear()
chatModel.chatItems.addAll(chat.chatItems) chatModel.chatItems.replaceAll(chat.chatItems)
chatModel.chatId.value = chat.chatInfo.id chatModel.chatId.value = chat.chatInfo.id
} }
@@ -239,8 +236,7 @@ suspend fun apiFindMessages(ch: Chat, chatModel: ChatModel, search: String) {
val chatInfo = ch.chatInfo val chatInfo = ch.chatInfo
val chat = chatModel.controller.apiGetChat(ch.remoteHostId, chatInfo.chatType, chatInfo.apiId, search = search) ?: return val chat = chatModel.controller.apiGetChat(ch.remoteHostId, chatInfo.chatType, chatInfo.apiId, search = search) ?: return
if (chatModel.chatId.value != chat.id) return if (chatModel.chatId.value != chat.id) return
chatModel.chatItems.clear() chatModel.chatItems.replaceAll(chat.chatItems)
chatModel.chatItems.addAll(0, chat.chatItems)
} }
suspend fun setGroupMembers(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel) { suspend fun setGroupMembers(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatModel) {

View File

@@ -12,6 +12,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.* import androidx.compose.ui.focus.*
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
@@ -22,6 +23,7 @@ import chat.simplex.common.ui.theme.*
import chat.simplex.res.MR import chat.simplex.res.MR
import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.StringResource
import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@@ -189,6 +191,7 @@ class AlertManager {
title: String, text: String? = null, title: String, text: String? = null,
confirmText: String = generalGetString(MR.strings.ok), confirmText: String = generalGetString(MR.strings.ok),
hostDevice: Pair<Long?, String>? = null, hostDevice: Pair<Long?, String>? = null,
shareText: Boolean? = null
) { ) {
showAlert { showAlert {
AlertDialog( AlertDialog(
@@ -202,10 +205,19 @@ class AlertManager {
delay(200) delay(200)
focusRequester.requestFocus() focusRequester.requestFocus()
} }
// Can pass shareText = false to prevent showing Share button if it's needed in a specific case
val showShareButton = text != null && (shareText == true || (shareText == null && text.length > 500))
Row( Row(
Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING), Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING),
horizontalArrangement = Arrangement.Center horizontalArrangement = if (showShareButton) Arrangement.SpaceBetween else Arrangement.Center
) { ) {
val clipboard = LocalClipboardManager.current
if (showShareButton && text != null) {
TextButton(onClick = {
clipboard.shareText(text)
hideAlert()
}) { Text(stringResource(MR.strings.share_verb)) }
}
TextButton( TextButton(
onClick = { onClick = {
hideAlert() hideAlert()

View File

@@ -16,7 +16,7 @@ class ProcessedErrors <T: AgentErrorType>(val interval: Long) {
fun newError(error: T, offerRestart: Boolean) { fun newError(error: T, offerRestart: Boolean) {
timer.cancel() timer.cancel()
timer = withLongRunningApi(slow = 70_000, deadlock = 130_000) { timer = withLongRunningApi(slow = 130_000) {
val delayBeforeNext = (lastShownTimestamp + interval) - System.currentTimeMillis() val delayBeforeNext = (lastShownTimestamp + interval) - System.currentTimeMillis()
if ((lastShownOfferRestart || !offerRestart) && delayBeforeNext >= 0) { if ((lastShownOfferRestart || !offerRestart) && delayBeforeNext >= 0) {
delay(delayBeforeNext) delay(delayBeforeNext)

View File

@@ -37,30 +37,22 @@ fun withBGApi(action: suspend CoroutineScope.() -> Unit): Job =
CoroutineScope(singleThreadDispatcher).launch(block = { wrapWithLogging(action, it) }) CoroutineScope(singleThreadDispatcher).launch(block = { wrapWithLogging(action, it) })
} }
fun withLongRunningApi(slow: Long = Long.MAX_VALUE, deadlock: Long = Long.MAX_VALUE, action: suspend CoroutineScope.() -> Unit): Job = fun withLongRunningApi(slow: Long = Long.MAX_VALUE, action: suspend CoroutineScope.() -> Unit): Job =
Exception().let { Exception().let {
CoroutineScope(Dispatchers.Default).launch(block = { wrapWithLogging(action, it, slow = slow, deadlock = deadlock) }) CoroutineScope(Dispatchers.Default).launch(block = { wrapWithLogging(action, it, slow = slow) })
} }
private suspend fun wrapWithLogging(action: suspend CoroutineScope.() -> Unit, exception: java.lang.Exception, slow: Long = 10_000, deadlock: Long = 60_000) = coroutineScope { private suspend fun wrapWithLogging(action: suspend CoroutineScope.() -> Unit, exception: java.lang.Exception, slow: Long = 20_000) = coroutineScope {
val start = System.currentTimeMillis() val start = System.currentTimeMillis()
val job = launch {
delay(deadlock)
Log.e(TAG, "Possible deadlock of the thread, not finished after ${deadlock / 1000}s:\n${exception.stackTraceToString()}")
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.possible_deadlock_title),
text = generalGetString(MR.strings.possible_deadlock_desc).format(deadlock / 1000, exception.stackTraceToString()),
)
}
action() action()
job.cancel() val end = System.currentTimeMillis()
if (appPreferences.developerTools.get() && appPreferences.showSlowApiCalls.get()) { if (end - start > slow) {
val end = System.currentTimeMillis() Log.e(TAG, "Possible problem with execution of the thread, took ${(end - start) / 1000}s:\n${exception.stackTraceToString()}")
if (end - start > slow) { if (appPreferences.developerTools.get() && appPreferences.showSlowApiCalls.get()) {
Log.e(TAG, "Possible problem with execution of the thread, took ${(end - start) / 1000}s:\n${exception.stackTraceToString()}")
AlertManager.shared.showAlertMsg( AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.possible_slow_function_title), title = generalGetString(MR.strings.possible_slow_function_title),
text = generalGetString(MR.strings.possible_slow_function_desc).format((end - start) / 1000, exception.stackTraceToString()), text = generalGetString(MR.strings.possible_slow_function_desc).format((end - start) / 1000, exception.stackTraceToString()),
shareText = true
) )
} }
} }

View File

@@ -96,7 +96,7 @@ fun PrivacySettingsView(
val currentUser = chatModel.currentUser.value val currentUser = chatModel.currentUser.value
if (currentUser != null) { if (currentUser != null) {
fun setSendReceiptsContacts(enable: Boolean, clearOverrides: Boolean) { fun setSendReceiptsContacts(enable: Boolean, clearOverrides: Boolean) {
withLongRunningApi(slow = 30_000, deadlock = 60_000) { withLongRunningApi(slow = 60_000) {
val mrs = UserMsgReceiptSettings(enable, clearOverrides) val mrs = UserMsgReceiptSettings(enable, clearOverrides)
chatModel.controller.apiSetUserContactReceipts(currentUser, mrs) chatModel.controller.apiSetUserContactReceipts(currentUser, mrs)
chatModel.controller.appPrefs.privacyDeliveryReceiptsSet.set(true) chatModel.controller.appPrefs.privacyDeliveryReceiptsSet.set(true)
@@ -119,7 +119,7 @@ fun PrivacySettingsView(
} }
fun setSendReceiptsGroups(enable: Boolean, clearOverrides: Boolean) { fun setSendReceiptsGroups(enable: Boolean, clearOverrides: Boolean) {
withLongRunningApi(slow = 30_000, deadlock = 60_000) { withLongRunningApi(slow = 60_000) {
val mrs = UserMsgReceiptSettings(enable, clearOverrides) val mrs = UserMsgReceiptSettings(enable, clearOverrides)
chatModel.controller.apiSetUserGroupReceipts(currentUser, mrs) chatModel.controller.apiSetUserGroupReceipts(currentUser, mrs)
chatModel.controller.appPrefs.privacyDeliveryReceiptsSet.set(true) chatModel.controller.appPrefs.privacyDeliveryReceiptsSet.set(true)

View File

@@ -1588,8 +1588,6 @@
<string name="remote_ctrl_error_busy">سطح المكتب مشغول</string> <string name="remote_ctrl_error_busy">سطح المكتب مشغول</string>
<string name="remote_ctrl_error_bad_version">يحتوي سطح المكتب على إصدار غير مدعوم. يُرجى التأكد من استخدام نفس الإصدار على كلا الجهازين</string> <string name="remote_ctrl_error_bad_version">يحتوي سطح المكتب على إصدار غير مدعوم. يُرجى التأكد من استخدام نفس الإصدار على كلا الجهازين</string>
<string name="past_member_vName">العضو السابق %1$s</string> <string name="past_member_vName">العضو السابق %1$s</string>
<string name="possible_deadlock_title">مأزق</string>
<string name="possible_deadlock_desc">يستغرق تنفيذ التعليمات البرمجية وقتًا طويلاً جدًا: %1$d ثانية. من المحتمل أن التطبيق مجمّد: %2$s</string>
<string name="possible_slow_function_title">وظيفة بطيئة</string> <string name="possible_slow_function_title">وظيفة بطيئة</string>
<string name="developer_options_section">خيارات المطور</string> <string name="developer_options_section">خيارات المطور</string>
<string name="profile_update_event_member_name_changed">تغيّر العضو %1$s إلى %2$s</string> <string name="profile_update_event_member_name_changed">تغيّر العضو %1$s إلى %2$s</string>

View File

@@ -147,8 +147,6 @@
<string name="smp_server_test_delete_file">Delete file</string> <string name="smp_server_test_delete_file">Delete file</string>
<string name="error_deleting_user">Error deleting user profile</string> <string name="error_deleting_user">Error deleting user profile</string>
<string name="error_updating_user_privacy">Error updating user privacy</string> <string name="error_updating_user_privacy">Error updating user privacy</string>
<string name="possible_deadlock_title">Deadlock</string>
<string name="possible_deadlock_desc">Execution of code takes too long time: %1$d seconds. Probably, the app is frozen: %2$s</string>
<string name="possible_slow_function_title">Slow function</string> <string name="possible_slow_function_title">Slow function</string>
<string name="possible_slow_function_desc">Execution of function takes too long time: %1$d seconds: %2$s</string> <string name="possible_slow_function_desc">Execution of function takes too long time: %1$d seconds: %2$s</string>

View File

@@ -1555,7 +1555,6 @@
<string name="chat_is_stopped_you_should_transfer_database">Чатът е спрян. Ако вече сте използвали тази база данни на друго устройство, трябва да я прехвърлите обратно, преди да стартирате чата отново.</string> <string name="chat_is_stopped_you_should_transfer_database">Чатът е спрян. Ако вече сте използвали тази база данни на друго устройство, трябва да я прехвърлите обратно, преди да стартирате чата отново.</string>
<string name="remote_ctrl_error_bad_invitation">Настолното устройство има грешен код за връзка</string> <string name="remote_ctrl_error_bad_invitation">Настолното устройство има грешен код за връзка</string>
<string name="remote_ctrl_error_bad_version">Настолното устройство е с неподдържана версия. Моля, уверете се, че използвате една и съща версия и на двете устройства</string> <string name="remote_ctrl_error_bad_version">Настолното устройство е с неподдържана версия. Моля, уверете се, че използвате една и съща версия и на двете устройства</string>
<string name="possible_deadlock_desc">Изпълнението на кода отнема твърде много време: %1$d секунди. Вероятно приложението е замразено: %2$s</string>
<string name="possible_slow_function_title">Бавна функция</string> <string name="possible_slow_function_title">Бавна функция</string>
<string name="possible_slow_function_desc">Изпълнението на функцията отнема твърде много време: %1$d секунди: %2$s</string> <string name="possible_slow_function_desc">Изпълнението на функцията отнема твърде много време: %1$d секунди: %2$s</string>
<string name="show_internal_errors">Покажи вътрешните грешки</string> <string name="show_internal_errors">Покажи вътрешните грешки</string>
@@ -1591,5 +1590,4 @@
\nПрепоръчително е да рестартирате приложението.</string> \nПрепоръчително е да рестартирате приложението.</string>
<string name="developer_options_section">Опции за разработчици</string> <string name="developer_options_section">Опции за разработчици</string>
<string name="show_slow_api_calls">Показване на бавни API заявки</string> <string name="show_slow_api_calls">Показване на бавни API заявки</string>
<string name="possible_deadlock_title">Грешка в заключено положение</string>
</resources> </resources>

View File

@@ -1672,9 +1672,7 @@
<string name="possible_slow_function_title">Langsame Funktion</string> <string name="possible_slow_function_title">Langsame Funktion</string>
<string name="show_slow_api_calls">Zeige langsame API-Aufrufe an</string> <string name="show_slow_api_calls">Zeige langsame API-Aufrufe an</string>
<string name="group_member_status_unknown_short">unbekannt</string> <string name="group_member_status_unknown_short">unbekannt</string>
<string name="possible_deadlock_title">Blockade</string>
<string name="developer_options_section">Optionen für Entwickler</string> <string name="developer_options_section">Optionen für Entwickler</string>
<string name="possible_deadlock_desc">Die Code-Ausführung dauert zu lange: %1$d Sekunden. Wahrscheinlich ist die App eingefroren: %2$s</string>
<string name="group_member_status_unknown">unbekannter Gruppenmitglieds-Status</string> <string name="group_member_status_unknown">unbekannter Gruppenmitglieds-Status</string>
<string name="v5_5_private_notes_descr">Mit verschlüsselten Dateien und Medien.</string> <string name="v5_5_private_notes_descr">Mit verschlüsselten Dateien und Medien.</string>
<string name="v5_5_private_notes">Private Notizen</string> <string name="v5_5_private_notes">Private Notizen</string>

View File

@@ -1559,11 +1559,9 @@
<string name="remote_host_error_bad_state"><![CDATA[État médiocre de la connexion au mobile <b>%s</b>.]]></string> <string name="remote_host_error_bad_state"><![CDATA[État médiocre de la connexion au mobile <b>%s</b>.]]></string>
<string name="remote_ctrl_was_disconnected_title">Connexion interrompue</string> <string name="remote_ctrl_was_disconnected_title">Connexion interrompue</string>
<string name="remote_ctrl_error_bad_state">État médiocre de la connexion avec le bureau</string> <string name="remote_ctrl_error_bad_state">État médiocre de la connexion avec le bureau</string>
<string name="possible_deadlock_title">Impasse</string>
<string name="remote_ctrl_error_bad_version">La version de l\'ordinateur de bureau n\'est pas prise en charge. Veillez à utiliser la même version sur les deux appareils.</string> <string name="remote_ctrl_error_bad_version">La version de l\'ordinateur de bureau n\'est pas prise en charge. Veillez à utiliser la même version sur les deux appareils.</string>
<string name="remote_ctrl_error_disconnected">Le bureau a été déconnecté</string> <string name="remote_ctrl_error_disconnected">Le bureau a été déconnecté</string>
<string name="developer_options_section">Options pour les développeurs</string> <string name="developer_options_section">Options pour les développeurs</string>
<string name="possible_deadlock_desc">Le code prend trop de temps à s\'exécuter: %1$d secondes. Il est probable que l\'application soit figée: %2$s</string>
<string name="agent_internal_error_title">Erreur interne</string> <string name="agent_internal_error_title">Erreur interne</string>
<string name="remote_host_error_bad_version"><![CDATA[La version du mobile <b>%s</b> n\'est pas prise en charge. Veillez à utiliser la même version sur les deux appareils.]]></string> <string name="remote_host_error_bad_version"><![CDATA[La version du mobile <b>%s</b> n\'est pas prise en charge. Veillez à utiliser la même version sur les deux appareils.]]></string>
<string name="show_internal_errors">Afficher les erreurs internes</string> <string name="show_internal_errors">Afficher les erreurs internes</string>

View File

@@ -1583,9 +1583,7 @@
<string name="possible_slow_function_title">Lassú funkció</string> <string name="possible_slow_function_title">Lassú funkció</string>
<string name="show_slow_api_calls">Lassú API-hívások megjelenítése</string> <string name="show_slow_api_calls">Lassú API-hívások megjelenítése</string>
<string name="remote_host_error_inactive"><![CDATA[A(z) <b>%s</b> mobil eszköz inaktív]]></string> <string name="remote_host_error_inactive"><![CDATA[A(z) <b>%s</b> mobil eszköz inaktív]]></string>
<string name="possible_deadlock_title">Elakadt</string>
<string name="developer_options_section">Fejlesztői beállítások</string> <string name="developer_options_section">Fejlesztői beállítások</string>
<string name="possible_deadlock_desc">A kód végrehajtása túl sokáig tart: %1$d másodperc. Valószínűleg az alkalmazás lefagyott: %2$s</string>
<string name="possible_slow_function_desc">A funkció végrehajtása túl sokáig tart: %1$d másodperc: %2$s</string> <string name="possible_slow_function_desc">A funkció végrehajtása túl sokáig tart: %1$d másodperc: %2$s</string>
<string name="remote_host_error_busy"><![CDATA[A(z) <b>%s</b> mobil eszköz elfoglalt]]></string> <string name="remote_host_error_busy"><![CDATA[A(z) <b>%s</b> mobil eszköz elfoglalt]]></string>
<string name="past_member_vName">Legutóbbi tag %1$s</string> <string name="past_member_vName">Legutóbbi tag %1$s</string>

View File

@@ -1591,9 +1591,7 @@
<string name="possible_slow_function_title">Funzione lenta</string> <string name="possible_slow_function_title">Funzione lenta</string>
<string name="show_slow_api_calls">Mostra chiamate API lente</string> <string name="show_slow_api_calls">Mostra chiamate API lente</string>
<string name="group_member_status_unknown_short">sconosciuto</string> <string name="group_member_status_unknown_short">sconosciuto</string>
<string name="possible_deadlock_desc">L\'esecuzione del codice impiega troppo tempo: %1$d secondi. Probabilmente l\'app è congelata: %2$s</string>
<string name="group_member_status_unknown">stato sconosciuto</string> <string name="group_member_status_unknown">stato sconosciuto</string>
<string name="possible_deadlock_title">Stallo</string>
<string name="developer_options_section">Opzioni sviluppatore</string> <string name="developer_options_section">Opzioni sviluppatore</string>
<string name="v5_5_private_notes">Note private</string> <string name="v5_5_private_notes">Note private</string>
<string name="v5_5_new_interface_languages">Interfaccia in ungherese e turco</string> <string name="v5_5_new_interface_languages">Interfaccia in ungherese e turco</string>

View File

@@ -1571,9 +1571,7 @@
<string name="remote_ctrl_error_busy">PC版が処理中</string> <string name="remote_ctrl_error_busy">PC版が処理中</string>
<string name="remote_ctrl_error_disconnected">PC版が切断されました</string> <string name="remote_ctrl_error_disconnected">PC版が切断されました</string>
<string name="remote_ctrl_error_bad_version">ご利用のPC版のバージョンがサポートされてません。両端末が同じバージョンかどうか、ご確認ください。</string> <string name="remote_ctrl_error_bad_version">ご利用のPC版のバージョンがサポートされてません。両端末が同じバージョンかどうか、ご確認ください。</string>
<string name="possible_deadlock_title">デッドロック状態</string>
<string name="developer_options_section">開発者向けの設定</string> <string name="developer_options_section">開発者向けの設定</string>
<string name="possible_deadlock_desc">処理時間が異常にかかるようです: %1$d 秒。アプリが固まった恐れがあります: %2$s</string>
<string name="remote_host_error_busy"><![CDATA[携帯版 <b>%s</b> がただいま処理中]]></string> <string name="remote_host_error_busy"><![CDATA[携帯版 <b>%s</b> がただいま処理中]]></string>
<string name="possible_slow_function_desc">機能の処理時間が以上にかかってます: %1$d 秒: %2$s</string> <string name="possible_slow_function_desc">機能の処理時間が以上にかかってます: %1$d 秒: %2$s</string>
<string name="show_internal_errors">内部エラーを表示</string> <string name="show_internal_errors">内部エラーを表示</string>

View File

@@ -1574,7 +1574,6 @@
<string name="remote_host_error_missing"><![CDATA[Mobiel <b>%s</b> ontbreekt]]></string> <string name="remote_host_error_missing"><![CDATA[Mobiel <b>%s</b> ontbreekt]]></string>
<string name="remote_host_error_bad_state"><![CDATA[De verbinding met de mobiel <b>%s</b> is in slechte staat]]></string> <string name="remote_host_error_bad_state"><![CDATA[De verbinding met de mobiel <b>%s</b> is in slechte staat]]></string>
<string name="remote_ctrl_error_disconnected">De verbinding met desktop is verbroken</string> <string name="remote_ctrl_error_disconnected">De verbinding met desktop is verbroken</string>
<string name="possible_deadlock_title">Impasse</string>
<string name="possible_slow_function_desc">Uitvoering van functie duurt te lang: %1$d seconden: %2$s</string> <string name="possible_slow_function_desc">Uitvoering van functie duurt te lang: %1$d seconden: %2$s</string>
<string name="possible_slow_function_title">Langzame functie</string> <string name="possible_slow_function_title">Langzame functie</string>
<string name="developer_options_section">Ontwikkelaars opties</string> <string name="developer_options_section">Ontwikkelaars opties</string>
@@ -1588,7 +1587,6 @@
<string name="restart_chat_button">Chat opnieuw starten</string> <string name="restart_chat_button">Chat opnieuw starten</string>
<string name="remote_host_error_timeout"><![CDATA[Time-out bereikt tijdens het verbinden met de mobiel <b>%s</b>]]></string> <string name="remote_host_error_timeout"><![CDATA[Time-out bereikt tijdens het verbinden met de mobiel <b>%s</b>]]></string>
<string name="remote_ctrl_error_bad_state">De verbinding met de desktop is in slechte staat</string> <string name="remote_ctrl_error_bad_state">De verbinding met de desktop is in slechte staat</string>
<string name="possible_deadlock_desc">Het uitvoeren van de code duurt te lang: %1$d seconden. Waarschijnlijk is de app vastgelopen: %2$s</string>
<string name="remote_ctrl_error_bad_invitation">Desktop heeft verkeerde uitnodigingscode</string> <string name="remote_ctrl_error_bad_invitation">Desktop heeft verkeerde uitnodigingscode</string>
<string name="remote_host_error_bad_version"><![CDATA[Mobiel <b>%s</b> heeft een niet-ondersteunde versie. Zorg ervoor dat u op beide apparaten dezelfde versie gebruikt]]></string> <string name="remote_host_error_bad_version"><![CDATA[Mobiel <b>%s</b> heeft een niet-ondersteunde versie. Zorg ervoor dat u op beide apparaten dezelfde versie gebruikt]]></string>
<string name="remote_ctrl_error_timeout">Time-out bereikt tijdens het verbinden met de desktop</string> <string name="remote_ctrl_error_timeout">Time-out bereikt tijdens het verbinden met de desktop</string>

View File

@@ -1606,7 +1606,6 @@
<string name="remote_ctrl_error_bad_version">Komputer ma niewspieraną wersję. Proszę upewnić się, że używasz tych samych wersji na obu urządzeniach</string> <string name="remote_ctrl_error_bad_version">Komputer ma niewspieraną wersję. Proszę upewnić się, że używasz tych samych wersji na obu urządzeniach</string>
<string name="blocked_by_admin_items_description">%d wiadomości zablokowanych przez admina</string> <string name="blocked_by_admin_items_description">%d wiadomości zablokowanych przez admina</string>
<string name="error_creating_message">Błąd tworzenia wiadomości</string> <string name="error_creating_message">Błąd tworzenia wiadomości</string>
<string name="possible_deadlock_desc">Wykonanie kodu zajmuje za dużo czasu: %1$d sekund. Prawdopodobnie aplikacja jest zamrożona: %2$s</string>
<string name="possible_slow_function_desc">Wykonanie kodu zajmuje za dużo czasu: %1$d sekund: %2$s</string> <string name="possible_slow_function_desc">Wykonanie kodu zajmuje za dużo czasu: %1$d sekund: %2$s</string>
<string name="note_folder_local_display_name">Prywatne notatki</string> <string name="note_folder_local_display_name">Prywatne notatki</string>
<string name="group_member_status_unknown">nieznany status</string> <string name="group_member_status_unknown">nieznany status</string>
@@ -1621,7 +1620,6 @@
<string name="remote_host_error_inactive"><![CDATA[Telefon <b>%s</b> jest nieaktywny]]></string> <string name="remote_host_error_inactive"><![CDATA[Telefon <b>%s</b> jest nieaktywny]]></string>
<string name="remote_host_error_bad_version"><![CDATA[Telefon <b>%s</b> ma niewspieraną wersję. Proszę, upewnij się, że używasz tej samej wersji na obydwu urządzeniach]]></string> <string name="remote_host_error_bad_version"><![CDATA[Telefon <b>%s</b> ma niewspieraną wersję. Proszę, upewnij się, że używasz tej samej wersji na obydwu urządzeniach]]></string>
<string name="group_member_status_unknown_short">nieznany</string> <string name="group_member_status_unknown_short">nieznany</string>
<string name="possible_deadlock_title">Blokada</string>
<string name="profile_update_event_contact_name_changed">kontakt %1$s zmieniony na %2$s</string> <string name="profile_update_event_contact_name_changed">kontakt %1$s zmieniony na %2$s</string>
<string name="profile_update_event_removed_address">usunięto adres kontaktu</string> <string name="profile_update_event_removed_address">usunięto adres kontaktu</string>
<string name="profile_update_event_removed_picture">usunięto zdjęcie profilu</string> <string name="profile_update_event_removed_picture">usunięto zdjęcie profilu</string>

View File

@@ -1680,8 +1680,6 @@
<string name="error_showing_message">ошибка отображения сообщения</string> <string name="error_showing_message">ошибка отображения сообщения</string>
<string name="error_showing_content">ошибка отображения содержания</string> <string name="error_showing_content">ошибка отображения содержания</string>
<string name="remote_ctrl_disconnected_with_reason">Отсоединён по причине: %s</string> <string name="remote_ctrl_disconnected_with_reason">Отсоединён по причине: %s</string>
<string name="possible_deadlock_title">Взаимная блокировка</string>
<string name="possible_deadlock_desc">Выполнение задачи занимает долгое время: %1$d секунд. Возможно, приложение заблокировано: %2$s</string>
<string name="possible_slow_function_desc">Выполнение задачи занимает долгое время: %1$d секунд: %2$s</string> <string name="possible_slow_function_desc">Выполнение задачи занимает долгое время: %1$d секунд: %2$s</string>
<string name="possible_slow_function_title">Медленный вызов</string> <string name="possible_slow_function_title">Медленный вызов</string>
<string name="profile_update_event_contact_name_changed">контакт %1$s изменён на %2$s</string> <string name="profile_update_event_contact_name_changed">контакт %1$s изменён на %2$s</string>

View File

@@ -1586,8 +1586,6 @@
<string name="remote_host_error_bad_state"><![CDATA[到移动主机 <b>%s</b>的连接状态不佳]]></string> <string name="remote_host_error_bad_state"><![CDATA[到移动主机 <b>%s</b>的连接状态不佳]]></string>
<string name="remote_host_error_timeout"><![CDATA[连接到移动主机<b>%s</b>时超时]]></string> <string name="remote_host_error_timeout"><![CDATA[连接到移动主机<b>%s</b>时超时]]></string>
<string name="failed_to_create_user_invalid_desc">显示名无效。请另选一个名称。</string> <string name="failed_to_create_user_invalid_desc">显示名无效。请另选一个名称。</string>
<string name="possible_deadlock_title">死锁</string>
<string name="possible_deadlock_desc">代码执行花费的时间过久:%1$d秒。应用可能卡住了%2$s</string>
<string name="possible_slow_function_title">慢函数</string> <string name="possible_slow_function_title">慢函数</string>
<string name="show_slow_api_calls">显示缓慢的 API 调用</string> <string name="show_slow_api_calls">显示缓慢的 API 调用</string>
<string name="past_member_vName">过往成员 %1$s</string> <string name="past_member_vName">过往成员 %1$s</string>

View File

@@ -14,8 +14,7 @@ import androidx.compose.ui.input.key.*
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.* import androidx.compose.ui.window.*
import chat.simplex.common.model.ChatController import chat.simplex.common.model.*
import chat.simplex.common.model.ChatModel
import chat.simplex.common.platform.* import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.DEFAULT_START_MODAL_WIDTH import chat.simplex.common.ui.theme.DEFAULT_START_MODAL_WIDTH
import chat.simplex.common.ui.theme.SimpleXTheme import chat.simplex.common.ui.theme.SimpleXTheme
@@ -40,7 +39,8 @@ fun showApp() {
WindowExceptionHandler { e -> WindowExceptionHandler { e ->
AlertManager.shared.showAlertMsg( AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.app_was_crashed), title = generalGetString(MR.strings.app_was_crashed),
text = e.stackTraceToString() text = e.stackTraceToString(),
shareText = true
) )
Log.e(TAG, "App crashed, thread name: " + Thread.currentThread().name + ", exception: " + e.stackTraceToString()) Log.e(TAG, "App crashed, thread name: " + Thread.currentThread().name + ", exception: " + e.stackTraceToString())
window.dispatchEvent(WindowEvent(window, WindowEvent.WINDOW_CLOSING)) window.dispatchEvent(WindowEvent(window, WindowEvent.WINDOW_CLOSING))

View File

@@ -42,7 +42,7 @@ actual fun SaveContentItemAction(cItem: ChatItem, saveFileLauncher: FileChooserL
} }
var fileSource = getLoadedFileSource(cItem.file) var fileSource = getLoadedFileSource(cItem.file)
if (chatModel.connectedToRemote() && fileSource == null) { if (chatModel.connectedToRemote() && fileSource == null) {
withLongRunningApi(slow = 60_000, deadlock = 600_000) { withLongRunningApi(slow = 600_000) {
cItem.file?.loadRemoteFile(true) cItem.file?.loadRemoteFile(true)
fileSource = getLoadedFileSource(cItem.file) fileSource = getLoadedFileSource(cItem.file)
saveIfExists() saveIfExists()
@@ -51,7 +51,7 @@ actual fun SaveContentItemAction(cItem: ChatItem, saveFileLauncher: FileChooserL
}) })
} }
actual fun copyItemToClipboard(cItem: ChatItem, clipboard: ClipboardManager) = withLongRunningApi(slow = 60_000, deadlock = 600_000) { actual fun copyItemToClipboard(cItem: ChatItem, clipboard: ClipboardManager) = withLongRunningApi(slow = 600_000) {
var fileSource = getLoadedFileSource(cItem.file) var fileSource = getLoadedFileSource(cItem.file)
if (chatModel.connectedToRemote() && fileSource == null) { if (chatModel.connectedToRemote() && fileSource == null) {
cItem.file?.loadRemoteFile(true) cItem.file?.loadRemoteFile(true)

View File

@@ -25,11 +25,11 @@ android.nonTransitiveRClass=true
android.enableJetifier=true android.enableJetifier=true
kotlin.mpp.androidSourceSetLayoutVersion=2 kotlin.mpp.androidSourceSetLayoutVersion=2
android.version_name=5.5 android.version_name=5.5.4
android.version_code=175 android.version_code=183
desktop.version_name=5.5 desktop.version_name=5.5.4
desktop.version_code=26 desktop.version_code=30
kotlin.version=1.8.20 kotlin.version=1.8.20
gradle.plugin.version=7.4.2 gradle.plugin.version=7.4.2

View File

@@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd
source-repository-package source-repository-package
type: git type: git
location: https://github.com/simplex-chat/simplexmq.git location: https://github.com/simplex-chat/simplexmq.git
tag: 7a0cd8041bbb7d7ab2f089395a244dc4af0f9e3b tag: e64b6cba4b7e4107f78ae596ab2a6a28ef24ff78
source-repository-package source-repository-package
type: git type: git

View File

@@ -1,13 +1,13 @@
--- ---
title: Download SimpleX apps title: Download SimpleX apps
permalink: /downloads/index.html permalink: /downloads/index.html
revision: 25.11.2023 revision: 11.02.2024
--- ---
| Updated 25.11.2023 | Languages: EN | | Updated 11.02.2024 | Languages: EN |
# Download SimpleX apps # Download SimpleX apps
The latest stable version is v5.5. The latest stable version is v5.5.3.
You can get the latest beta releases from [GitHub](https://github.com/simplex-chat/simplex-chat/releases). You can get the latest beta releases from [GitHub](https://github.com/simplex-chat/simplex-chat/releases).
@@ -21,24 +21,24 @@ You can get the latest beta releases from [GitHub](https://github.com/simplex-ch
Using the same profile as on mobile device is not yet supported you need to create a separate profile to use desktop apps. Using the same profile as on mobile device is not yet supported you need to create a separate profile to use desktop apps.
**Linux**: [AppImage](https://github.com/simplex-chat/simplex-chat/releases/download/v5.5.0/simplex-desktop-x86_64.AppImage) (most Linux distros), [Ubuntu 20.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.5.0/simplex-desktop-ubuntu-20_04-x86_64.deb) (and Debian-based distros), [Ubuntu 22.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.5.0/simplex-desktop-ubuntu-22_04-x86_64.deb). **Linux**: [AppImage](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex-desktop-x86_64.AppImage) (most Linux distros), [Ubuntu 20.04](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex-desktop-ubuntu-20_04-x86_64.deb) (and Debian-based distros), [Ubuntu 22.04](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex-desktop-ubuntu-22_04-x86_64.deb).
**Mac**: [x86_64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.5.0/simplex-desktop-macos-x86_64.dmg) (Intel), [aarch64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.5.0/simplex-desktop-macos-aarch64.dmg) (Apple Silicon). **Mac**: [aarch64](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex-desktop-macos-aarch64.dmg) (Apple Silicon), [x86_64](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex-desktop-macos-x86_64.dmg) (Intel).
**Windows**: [x86_64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.5.0/simplex-desktop-windows-x86_64.msi). **Windows**: [x86_64](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex-desktop-windows-x86_64.msi).
## Mobile apps ## Mobile apps
**iOS**: [App store](https://apps.apple.com/us/app/simplex-chat/id1605771084), [TestFlight](https://testflight.apple.com/join/DWuT2LQu). **iOS**: [App store](https://apps.apple.com/us/app/simplex-chat/id1605771084), [TestFlight](https://testflight.apple.com/join/DWuT2LQu).
**Android**: [Play store](https://play.google.com/store/apps/details?id=chat.simplex.app), [F-Droid](https://simplex.chat/fdroid/), [APK aarch64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.5.0/simplex.apk), [APK armv7](https://github.com/simplex-chat/simplex-chat/releases/download/v5.5.0/simplex-armv7a.apk). **Android**: [Play store](https://play.google.com/store/apps/details?id=chat.simplex.app), [F-Droid](https://simplex.chat/fdroid/), [APK aarch64](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex.apk), [APK armv7](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex-armv7a.apk).
## Terminal (console) app ## Terminal (console) app
See [Using terminal app](/docs/CLI.md). See [Using terminal app](/docs/CLI.md).
**Linux**: [Ubuntu 20.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.5.0/simplex-chat-ubuntu-20_04-x86-64), [Ubuntu 22.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.5.0/simplex-chat-ubuntu-22_04-x86-64). **Linux**: [Ubuntu 20.04](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex-chat-ubuntu-20_04-x86-64), [Ubuntu 22.04](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex-chat-ubuntu-22_04-x86-64).
**Mac** [x86_64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.5.0/simplex-chat-macos-x86-64), aarch64 - [compile from source](/docs/CLI.md#). **Mac** [x86_64](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex-chat-macos-x86-64), aarch64 - [compile from source](/docs/CLI.md#).
**Windows**: [x86_64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.5.0/simplex-chat-windows-x86-64). **Windows**: [x86_64](https://github.com/simplex-chat/simplex-chat/releases/latest/download/simplex-chat-windows-x86-64).

View File

@@ -385,6 +385,7 @@
"chat_send_cmd" "chat_send_cmd"
"chat_send_remote_cmd" "chat_send_remote_cmd"
"chat_valid_name" "chat_valid_name"
"chat_json_length"
"chat_write_file" "chat_write_file"
]; ];
postInstall = '' postInstall = ''
@@ -487,6 +488,7 @@
"chat_send_cmd" "chat_send_cmd"
"chat_send_remote_cmd" "chat_send_remote_cmd"
"chat_valid_name" "chat_valid_name"
"chat_json_length"
"chat_write_file" "chat_write_file"
]; ];
postInstall = '' postInstall = ''

View File

@@ -12,6 +12,7 @@ EXPORTS
chat_parse_server chat_parse_server
chat_password_hash chat_password_hash
chat_valid_name chat_valid_name
chat_json_length
chat_encrypt_media chat_encrypt_media
chat_decrypt_media chat_decrypt_media
chat_write_file chat_write_file

View File

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

View File

@@ -20,6 +20,10 @@ root_dir="$(dirname "$(dirname "$(readlink "$0")")")"
cd $root_dir cd $root_dir
BUILD_DIR=dist-newstyle/build/$ARCH-$OS/ghc-${GHC_VERSION}/simplex-chat-* BUILD_DIR=dist-newstyle/build/$ARCH-$OS/ghc-${GHC_VERSION}/simplex-chat-*
exports=( $(sed 's/foreign export ccall "chat_migrate_init_key"//' src/Simplex/Chat/Mobile.hs | sed 's/foreign export ccall "chat_reopen_store"//' |grep "foreign export ccall" | cut -d '"' -f2) )
for elem in "${exports[@]}"; do count=$(grep -R "$elem$" libsimplex.dll.def | wc -l); if [ $count -ne 1 ]; then echo Wrong exports in libsimplex.dll.def. Add \"$elem\" to that file; exit 1; fi ; done
for elem in "${exports[@]}"; do count=$(grep -R "\"$elem\"" flake.nix | wc -l); if [ $count -ne 2 ]; then echo Wrong exports in flake.nix. Add \"$elem\" in two places of the file; exit 1; fi ; done
rm -rf $BUILD_DIR rm -rf $BUILD_DIR
cabal build lib:simplex-chat --ghc-options='-optl-Wl,-rpath,$ORIGIN -flink-rts -threaded' cabal build lib:simplex-chat --ghc-options='-optl-Wl,-rpath,$ORIGIN -flink-rts -threaded'
cd $BUILD_DIR/build cd $BUILD_DIR/build

View File

@@ -19,6 +19,10 @@ GHC_LIBS_DIR=$(ghc --print-libdir)
BUILD_DIR=dist-newstyle/build/$ARCH-*/ghc-*/simplex-chat-* BUILD_DIR=dist-newstyle/build/$ARCH-*/ghc-*/simplex-chat-*
exports=( $(sed 's/foreign export ccall "chat_migrate_init_key"//' src/Simplex/Chat/Mobile.hs | sed 's/foreign export ccall "chat_reopen_store"//' |grep "foreign export ccall" | cut -d '"' -f2) )
for elem in "${exports[@]}"; do count=$(grep -R "$elem$" libsimplex.dll.def | wc -l); if [ $count -ne 1 ]; then echo Wrong exports in libsimplex.dll.def. Add \"$elem\" to that file; exit 1; fi ; done
for elem in "${exports[@]}"; do count=$(grep -R "\"$elem\"" flake.nix | wc -l); if [ $count -ne 2 ]; then echo Wrong exports in flake.nix. Add \"$elem\" in two places of the file; exit 1; fi ; done
rm -rf $BUILD_DIR rm -rf $BUILD_DIR
cabal build lib:simplex-chat lib:simplex-chat --ghc-options="-optl-Wl,-rpath,@loader_path -optl-Wl,-L$GHC_LIBS_DIR/$ARCH-osx-ghc-$GHC_VERSION -optl-lHSrts_thr-ghc$GHC_VERSION -optl-lffi" cabal build lib:simplex-chat lib:simplex-chat --ghc-options="-optl-Wl,-rpath,@loader_path -optl-Wl,-L$GHC_LIBS_DIR/$ARCH-osx-ghc-$GHC_VERSION -optl-lHSrts_thr-ghc$GHC_VERSION -optl-lffi"

View File

@@ -17,6 +17,10 @@ fi
BUILD_DIR=dist-newstyle/build/$ARCH-$OS/ghc-*/simplex-chat-* BUILD_DIR=dist-newstyle/build/$ARCH-$OS/ghc-*/simplex-chat-*
exports=( $(sed 's/foreign export ccall "chat_migrate_init_key"//' src/Simplex/Chat/Mobile.hs | sed 's/foreign export ccall "chat_reopen_store"//' |grep "foreign export ccall" | cut -d '"' -f2) )
for elem in "${exports[@]}"; do count=$(grep -R "$elem$" libsimplex.dll.def | wc -l); if [ $count -ne 1 ]; then echo Wrong exports in libsimplex.dll.def. Add \"$elem\" to that file; exit 1; fi ; done
for elem in "${exports[@]}"; do count=$(grep -R "\"$elem\"" flake.nix | wc -l); if [ $count -ne 2 ]; then echo Wrong exports in flake.nix. Add \"$elem\" in two places of the file; exit 1; fi ; done
# IMPORTANT: in order to get a working build you should use x86_64 MinGW with make, cmake, gcc. # IMPORTANT: in order to get a working build you should use x86_64 MinGW with make, cmake, gcc.
# 100% working MinGW is https://github.com/brechtsanders/winlibs_mingw/releases/download/13.1.0-16.0.5-11.0.0-ucrt-r5/winlibs-x86_64-posix-seh-gcc-13.1.0-mingw-w64ucrt-11.0.0-r5.zip # 100% working MinGW is https://github.com/brechtsanders/winlibs_mingw/releases/download/13.1.0-16.0.5-11.0.0-ucrt-r5/winlibs-x86_64-posix-seh-gcc-13.1.0-mingw-w64ucrt-11.0.0-r5.zip
# Many other distributions I tested don't work in some cases or don't have required tools. # Many other distributions I tested don't work in some cases or don't have required tools.

View File

@@ -1,5 +1,5 @@
{ {
"https://github.com/simplex-chat/simplexmq.git"."7a0cd8041bbb7d7ab2f089395a244dc4af0f9e3b" = "0jxf9dnsg14ffd1y3i7md2ninrds4daq1fmpnd6j5z99im07ns52"; "https://github.com/simplex-chat/simplexmq.git"."e64b6cba4b7e4107f78ae596ab2a6a28ef24ff78" = "0fxgklq65bh2f4kx36vjicdxqmi88m91xs601hm81v5pn6kk0ppd";
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38"; "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/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d";
"https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl"; "https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";

View File

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