Compare commits

...

83 Commits

Author SHA1 Message Date
Evgeny Poberezkin 545f110fb8 nix: fix direct-sqlcipher content hash 2023-12-13 20:29:36 +00:00
Evgeny Poberezkin 4c6387c854 nix: fix script 2023-12-13 20:28:32 +00:00
Evgeny Poberezkin b3544cfbc3 Merge branch 'stable' into stable-android 2023-12-12 10:12:42 +00:00
Evgeny Poberezkin bfa8717ed4 Merge branch 'master-android' into stable-android 2023-12-09 20:43:21 +00:00
Evgeny Poberezkin 2f7632a70f 5.4.1: ios 185, android 164, desktop 19 2023-12-07 21:01:14 +00:00
Evgeny Poberezkin cdb3b6aafd Merge branch 'master-ghc8107' into master-android 2023-12-07 15:12:49 +00:00
Evgeny Poberezkin 9f3d3e8ba4 Merge branch 'master' into master-ghc8107 2023-12-07 15:00:23 +00:00
Evgeny Poberezkin 27c14f32f1 core: 5.4.0.7 2023-12-07 14:30:42 +00:00
Stanislav Dmitrenko 13a32f7864 android: made minimum supported version of Android as 9 (#3525) 2023-12-07 10:49:16 +00:00
Stanislav Dmitrenko b1652b8930 desktop: fix toasts theme (#3524) 2023-12-06 21:19:30 +00:00
Stanislav Dmitrenko a9b36e8e39 desktop: fix onboarding when choosing random password (#3523) 2023-12-06 20:33:53 +00:00
Evgeny Poberezkin ee163a6540 core: add missing field 2023-12-06 12:34:10 +00:00
Evgeny Poberezkin 4fd6405113 core: update simplexmq (better suspend agent) 2023-12-06 00:19:24 +00:00
Stanislav Dmitrenko ccc62274ee android, desktop: crash handler addition (#3517)
* android, desktop: crash handler addition

* added
2023-12-05 22:50:25 +00:00
Stanislav Dmitrenko 4c6d52ba75 android, desktop: crash handler (#3516)
* android, desktop: crash handler

* test

* rename

* string

* Revert "test"

This reverts commit 530faf39c1.

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-12-05 21:31:49 +00:00
Evgeny Poberezkin 9df63160e5 ios: fix simplex address view (#3515)
* ios: fix simplex address view

* fix lib paths

* fix call
2023-12-05 09:48:04 +00:00
Stanislav Dmitrenko c8e9788c29 desktop: enhancements to remote desktop connect UI (#3513)
* desktop: enhancements to remote desktop connect UI

* changes

* more changes

This reverts commit e8323e8bfa.

* color

* random port
2023-12-04 21:04:58 +00:00
spaced4ndy 7099776357 docs: fix typo 2023-12-04 19:17:24 +04:00
Evgeny Poberezkin 047aad592e Merge branch 'master-ghc8107' into master-android 2023-12-04 12:32:05 +00:00
Evgeny Poberezkin 087acd9180 changes to support GHC 8.10.7 (#3512)
* Revert "raise lower bound on mtl to a real version (#3499)"

This reverts commit f94c0311c1.

* Revert "core: expand ranges to fit ghc 8.10 & 9.6 (#3496)"

This reverts commit 9a1c7f41f7.

* update simplexmq

* remove netword-transport fork

* compatibility with GHC 8.10.7

* simplexmq

* fix test

* simplexmq, deps

* update sqlcipher deps in sha256nix

* fix index-state in cabal.project

* index-state

* remove import

* add cabal.project.freeze

* simplexmq

* remove freeze

* simplexmq

* bytestring,simplexmq

* template-haskell, simplexmq

* simplexmq

* simplexmq

* simplexmq

* mtl

* simplexmq

* remove duplicate index-state
2023-12-04 12:29:49 +00:00
Evgeny Poberezkin 0b822e4a5c Merge branch 'master' into master-ghc8107 2023-12-04 10:07:16 +00:00
Evgeny Poberezkin 3481d379c6 core: compatibility with GHC 8.10.7, narrow dependency ranges (#3503)
* Revert "raise lower bound on mtl to a real version (#3499)"

This reverts commit f94c0311c1.

* Revert "core: expand ranges to fit ghc 8.10 & 9.6 (#3496)"

This reverts commit 9a1c7f41f7.

* update simplexmq

* remove netword-transport fork

* simplexmq

* fix test

* fix index-state in cabal.project

* simplexmq

* simplexmq

* bytestring,simplexmq

* template-haskell, simplexmq

* simplexmq

* simplexmq

* mtl

* simplexmq
2023-12-04 10:01:37 +00:00
Evgeny Poberezkin 85c1e871dc Merge branch 'stable' 2023-12-04 00:06:53 +00:00
Evgeny Poberezkin fec5ff3f15 docs: SimpleX address (#3508)
* docs: SimpleX address

* table

* header
2023-12-03 22:21:13 +00:00
Evgeny Poberezkin acaa597c90 desktop, android: fix image not appearing in view when received (#3504)
* desktop, android: fix image not appearing in view when received

* change to KeyChangeEffect
2023-12-03 15:42:43 +00:00
Evgeny Poberezkin 6a9a67db14 cli: option to mark shown messages as read (off by default) (#3506)
* cli: option to mark shown messages as read (off by default)

* fix tests

* fix tests
2023-12-03 15:42:26 +00:00
spaced4ndy 79e208193a Merge branch 'master-ghc8107' into master-android 2023-11-30 21:11:25 +04:00
Stanislav Dmitrenko f31054de4f desktop (windows): fix action (#3479) 2023-11-27 22:11:53 +00:00
spaced4ndy 5f1aa6fa9d Merge branch 'master-ghc8107' into master-android 2023-11-27 19:18:22 +04:00
Evgeny Poberezkin ef21fd1d26 Merge branch 'master-ghc8107' into master-android 2023-11-26 19:59:46 +00:00
Evgeny Poberezkin 8b3300e197 Merge branch 'master-android' into stable-android 2023-11-26 13:14:27 +00:00
Evgeny Poberezkin f27de052cf Merge branch 'master' into master-android 2023-11-26 11:51:16 +00:00
Evgeny Poberezkin dc8ca4cf89 Merge tag 'v5.4.0' into master-android 2023-11-25 11:23:23 +00:00
Evgeny Poberezkin b62dd801f1 Merge branch 'master-ghc8107' into master-android 2023-11-24 20:02:05 +00:00
Evgeny Poberezkin cc127e56fe Merge branch 'master' into master-android 2023-11-23 16:23:02 +00:00
Evgeny Poberezkin 831231d8e6 Merge branch 'master-ghc8107' into master-android 2023-11-21 21:16:04 +00:00
spaced4ndy f323c8e112 Merge branch 'master-ghc8107' into master-android 2023-11-21 19:42:28 +04:00
Evgeny Poberezkin d8373262bc Merge branch 'master-ghc8107' into master-android 2023-11-21 00:01:20 +00:00
Evgeny Poberezkin bd4259e89e update hpack 2023-11-20 14:43:20 +00:00
Evgeny Poberezkin 5ef0eda2d7 Merge branch 'master-ghc8107' into master-android 2023-11-20 14:30:27 +00:00
Evgeny Poberezkin 45ada450a2 Merge branch 'master-ghc8107' into master-android 2023-11-20 13:24:07 +00:00
spaced4ndy 901610eec5 Merge branch 'master-ghc8107' into master-android 2023-11-20 14:08:10 +04:00
Evgeny Poberezkin 13215d91d7 Merge branch 'master-ghc8107' into master-android 2023-11-20 00:07:12 +00:00
Evgeny Poberezkin 5fcbade1bc Merge branch 'master-ghc8107' into master-android 2023-11-17 11:49:35 +00:00
Evgeny Poberezkin 80ddb50e1c Merge branch 'master-ghc8107' into master-android 2023-11-16 18:55:17 +00:00
Evgeny Poberezkin 0c23ff9ae3 Merge branch 'master-ghc8107' into master-android 2023-11-11 13:57:32 +00:00
Evgeny Poberezkin 1e2104cabf Merge branch 'master-ghc8107' into master-android 2023-11-11 09:52:07 +00:00
Evgeny Poberezkin f0991cc0ba Merge branch 'master-ghc8107' into master-android 2023-11-10 21:22:19 +00:00
Evgeny Poberezkin 82cd70a75c Merge branch 'master-ghc8107' into master-android 2023-11-10 21:08:11 +00:00
Evgeny Poberezkin c459e71d02 Merge branch 'master-ghc8107' into master-android 2023-11-06 11:26:40 +00:00
spaced4ndy 477d98d75a Merge branch 'master-ghc8107' into master-android 2023-11-06 11:42:39 +04:00
Evgeny Poberezkin ca78958667 Merge branch 'master-ghc8107' into master-android 2023-11-04 13:39:35 +00:00
spaced4ndy 8343285d93 Merge branch 'master-ghc8107' into master-android 2023-10-30 21:01:16 +04:00
Evgeny Poberezkin fb9485190d Merge branch 'master-ghc8107' into master-android 2023-10-29 18:27:00 +00:00
spaced4ndy 9ed723bafa Merge branch 'master-ghc8107' into master-android 2023-10-25 10:48:37 +04:00
spaced4ndy bb374c68b1 Merge branch 'master-ghc8107' into master-android 2023-10-24 18:14:22 +04:00
spaced4ndy 7c12e82042 Merge branch 'master-ghc8107' into master-android 2023-10-24 17:41:19 +04:00
Evgeny Poberezkin c4d7e5307c Merge branch 'master-ghc8107' into master-android 2023-10-22 15:35:55 +01:00
Evgeny Poberezkin 7fd3b4d6ba Merge branch 'master-ghc8107' into master-android 2023-10-18 22:45:11 +01:00
spaced4ndy 95008eeeaf Merge branch 'master-ghc8107' into master-android 2023-10-16 20:05:49 +04:00
spaced4ndy ea2b5f2ccf Merge branch 'master-ghc8107' into master-android 2023-10-16 19:28:48 +04:00
Evgeny Poberezkin 5c14c3b349 Merge branch 'master-ghc8107' into master-android 2023-10-15 18:54:16 +01:00
Evgeny Poberezkin 02db38ffd3 Merge branch 'master-ghc8107' into master-android 2023-10-11 21:58:16 +01:00
Evgeny Poberezkin c435cbdc7b Merge branch 'master-ghc8107' into master-android 2023-10-11 21:28:43 +01:00
spaced4ndy 41eb2e5689 Merge branch 'master-ghc8107' into master-android 2023-10-11 13:22:30 +04:00
Evgeny Poberezkin c60af078d7 Merge branch 'stable' into stable-android 2023-10-09 19:55:51 +01:00
Evgeny Poberezkin f66405e79b Merge branch 'master-ghc8107' into master-android 2023-10-09 17:31:56 +01:00
Evgeny Poberezkin e4fbe66d95 Merge branch 'stable' into stable-android 2023-10-09 14:36:03 +01:00
Evgeny Poberezkin 187fef0c5a Merge branch 'master-ghc8107' into master-android 2023-10-09 14:05:21 +01:00
Evgeny Poberezkin cb52d75ff0 Merge branch 'stable' into stable-android 2023-10-08 17:31:23 +01:00
Evgeny Poberezkin bcbee67709 Merge branch 'master-ghc8107' into master-android 2023-10-08 08:38:40 +01:00
Evgeny Poberezkin 2bd049db87 Merge branch 'master-ghc8107' into master-android 2023-10-07 21:10:22 +01:00
Evgeny Poberezkin 30db24265e Merge branch 'master-ghc8107' into master-android 2023-10-02 23:04:44 +01:00
Evgeny Poberezkin b4257f7767 Merge branch 'master-ghc8107' into master-android 2023-10-01 13:21:50 +01:00
Evgeny Poberezkin cf46469cd5 Merge branch 'master-ghc8107' into master-android 2023-10-01 11:19:50 +01:00
Evgeny Poberezkin 9defa44f0c Merge branch 'master-ghc8107' into master-android 2023-09-29 13:15:23 +01:00
Evgeny Poberezkin f81557b4fd Merge branch 'master-ghc8107' into master-android 2023-09-27 22:10:06 +01:00
spaced4ndy a63caf4640 Merge branch 'master-ghc8107' into master-android 2023-09-27 20:38:36 +04:00
Evgeny Poberezkin 340552321e Merge branch 'master-ghc8107' into master-android 2023-09-27 16:07:24 +01:00
Evgeny Poberezkin 6a578cfe3c Merge branch 'master-ghc8107' into master-android 2023-09-25 16:53:04 +01:00
spaced4ndy 55418e2bc0 Merge branch 'master-ghc8107' into master-android 2023-09-25 17:43:59 +04:00
spaced4ndy 5ebdf5dba9 Merge branch 'master-ghc8107' into master-android 2023-09-25 17:07:14 +04:00
Evgeny Poberezkin 503d3d77e6 Merge branch 'master' into master-ghc8107 2023-09-23 08:47:28 +01:00
68 changed files with 643 additions and 438 deletions
+1 -1
View File
@@ -264,7 +264,7 @@ jobs:
# * And GitHub Actions does not support parameterizing shell in a matrix job - https://github.community/t/using-matrix-to-specify-shell-is-it-possible/17065
- name: 'Setup MSYS2'
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'windows-latest'
if: matrix.os == 'windows-latest'
uses: msys2/setup-msys2@v2
with:
msystem: ucrt64
+2 -2
View File
@@ -26,10 +26,10 @@ struct SimpleXApp: App {
@State private var showInitializationView = false
init() {
DispatchQueue.global(qos: .background).sync {
// DispatchQueue.global(qos: .background).sync {
haskell_init()
// hs_init(0, nil)
}
// }
UserDefaults.standard.register(defaults: appDefaults)
setGroupDefaults()
registerGroupDefaults()
+12 -20
View File
@@ -11,20 +11,12 @@ import CoreImage.CIFilterBuiltins
struct MutableQRCode: View {
@Binding var uri: String
@State private var image: UIImage?
var withLogo: Bool = true
var tintColor = UIColor(red: 0.023, green: 0.176, blue: 0.337, alpha: 1)
var body: some View {
ZStack {
if let image = image {
qrCodeImage(image)
}
}
.onAppear {
image = generateImage(uri)
}
.onChange(of: uri) { _ in
image = generateImage(uri)
}
QRCode(uri: uri, withLogo: withLogo, tintColor: tintColor)
.id("simplex-qrcode-view-for-\(uri)")
}
}
@@ -49,7 +41,7 @@ struct QRCode: View {
var withLogo: Bool = true
var tintColor = UIColor(red: 0.023, green: 0.176, blue: 0.337, alpha: 1)
@State private var image: UIImage? = nil
@State private var makeScreenshotBinding: () -> Void = {}
@State private var makeScreenshotFunc: () -> Void = {}
var body: some View {
ZStack {
@@ -70,18 +62,18 @@ struct QRCode: View {
}
}
.onAppear {
makeScreenshotBinding = {
makeScreenshotFunc = {
let size = CGSizeMake(1024 / UIScreen.main.scale, 1024 / UIScreen.main.scale)
showShareSheet(items: [makeScreenshot(geo.frame(in: .local).origin, size)])}
showShareSheet(items: [makeScreenshot(geo.frame(in: .local).origin, size)])
}
}
.frame(width: geo.size.width, height: geo.size.height)
}
}
.onTapGesture(perform: makeScreenshotBinding)
.onTapGesture(perform: makeScreenshotFunc)
.onAppear {
image = image ?? generateImage(uri)?.replaceColor(UIColor.black, tintColor)
image = image ?? generateImage(uri, tintColor: tintColor)
}
}
}
@@ -93,13 +85,13 @@ private func qrCodeImage(_ image: UIImage) -> some View {
.textSelection(.enabled)
}
private func generateImage(_ uri: String) -> UIImage? {
private func generateImage(_ uri: String, tintColor: UIColor) -> UIImage? {
let context = CIContext()
let filter = CIFilter.qrCodeGenerator()
filter.message = Data(uri.utf8)
if let outputImage = filter.outputImage,
let cgImage = context.createCGImage(outputImage, from: outputImage.extent) {
return UIImage(cgImage: cgImage)
return UIImage(cgImage: cgImage).replaceColor(UIColor.black, tintColor)
}
return nil
}
@@ -190,7 +190,8 @@ struct UserAddressView: View {
@ViewBuilder private func existingAddressView(_ userAddress: UserContactLink) -> some View {
Section {
MutableQRCode(uri: Binding.constant(simplexChatLink(userAddress.connReqContact)))
SimpleXLinkQRCode(uri: userAddress.connReqContact)
.id("simplex-contact-address-qrcode-\(userAddress.connReqContact)")
shareQRCodeButton(userAddress)
if MFMailComposeViewController.canSendMail() {
shareViaEmailButton(userAddress)
+32 -32
View File
@@ -120,11 +120,6 @@
5CCD403727A5F9A200368C90 /* ScanToConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */; };
5CD67B8F2B0E858A00C510B1 /* hs_init.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CD67B8D2B0E858A00C510B1 /* hs_init.h */; settings = {ATTRIBUTES = (Public, ); }; };
5CD67B902B0E858A00C510B1 /* hs_init.c in Sources */ = {isa = PBXBuildFile; fileRef = 5CD67B8E2B0E858A00C510B1 /* hs_init.c */; };
5CD67BA02B120ADF00C510B1 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CD67B9B2B120ADF00C510B1 /* libgmp.a */; };
5CD67BA12B120ADF00C510B1 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CD67B9C2B120ADF00C510B1 /* libgmpxx.a */; };
5CD67BA22B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CD67B9D2B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u.a */; };
5CD67BA32B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CD67B9E2B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u-ghc8.10.7.a */; };
5CD67BA42B120ADF00C510B1 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CD67B9F2B120ADF00C510B1 /* libffi.a */; };
5CDCAD482818589900503DA2 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CDCAD472818589900503DA2 /* NotificationService.swift */; };
5CE2BA702845308900EC33A6 /* SimpleXChat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; };
5CE2BA712845308900EC33A6 /* SimpleXChat.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@@ -150,6 +145,11 @@
5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; };
5CEBD7462A5C0A8F00665FE2 /* KeyboardPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */; };
5CEBD7482A5F115D00665FE2 /* SetDeliveryReceiptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */; };
5CF937182B22552700E1D781 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF937132B22552700E1D781 /* libffi.a */; };
5CF937192B22552700E1D781 /* libHSsimplex-chat-5.4.0.7-EoJ0xKOyE47DlSpHXf0V4-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF937142B22552700E1D781 /* libHSsimplex-chat-5.4.0.7-EoJ0xKOyE47DlSpHXf0V4-ghc8.10.7.a */; };
5CF9371A2B22552700E1D781 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF937152B22552700E1D781 /* libgmp.a */; };
5CF9371B2B22552700E1D781 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF937162B22552700E1D781 /* libgmpxx.a */; };
5CF9371C2B22552700E1D781 /* libHSsimplex-chat-5.4.0.7-EoJ0xKOyE47DlSpHXf0V4.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF937172B22552700E1D781 /* libHSsimplex-chat-5.4.0.7-EoJ0xKOyE47DlSpHXf0V4.a */; };
5CFA59C42860BC6200863A68 /* MigrateToAppGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */; };
5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59CF286477B400863A68 /* ChatArchiveView.swift */; };
5CFE0921282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; };
@@ -408,11 +408,6 @@
5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanToConnectView.swift; sourceTree = "<group>"; };
5CD67B8D2B0E858A00C510B1 /* hs_init.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hs_init.h; sourceTree = "<group>"; };
5CD67B8E2B0E858A00C510B1 /* hs_init.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hs_init.c; sourceTree = "<group>"; };
5CD67B9B2B120ADF00C510B1 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5CD67B9C2B120ADF00C510B1 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5CD67B9D2B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u.a"; sourceTree = "<group>"; };
5CD67B9E2B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u-ghc8.10.7.a"; sourceTree = "<group>"; };
5CD67B9F2B120ADF00C510B1 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5CDCAD452818589900503DA2 /* SimpleX NSE.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "SimpleX NSE.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
5CDCAD472818589900503DA2 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
5CDCAD492818589900503DA2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -439,6 +434,11 @@
5CEACCEC27DEA495000BD591 /* MsgContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MsgContentView.swift; sourceTree = "<group>"; };
5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardPadding.swift; sourceTree = "<group>"; };
5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDeliveryReceiptsView.swift; sourceTree = "<group>"; };
5CF937132B22552700E1D781 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5CF937142B22552700E1D781 /* libHSsimplex-chat-5.4.0.7-EoJ0xKOyE47DlSpHXf0V4-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.0.7-EoJ0xKOyE47DlSpHXf0V4-ghc8.10.7.a"; sourceTree = "<group>"; };
5CF937152B22552700E1D781 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5CF937162B22552700E1D781 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5CF937172B22552700E1D781 /* libHSsimplex-chat-5.4.0.7-EoJ0xKOyE47DlSpHXf0V4.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.0.7-EoJ0xKOyE47DlSpHXf0V4.a"; sourceTree = "<group>"; };
5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToAppGroupView.swift; sourceTree = "<group>"; };
5CFA59CF286477B400863A68 /* ChatArchiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatArchiveView.swift; sourceTree = "<group>"; };
5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ZoomableScrollView.swift; path = Shared/Views/ZoomableScrollView.swift; sourceTree = SOURCE_ROOT; };
@@ -521,12 +521,12 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5CD67BA12B120ADF00C510B1 /* libgmpxx.a in Frameworks */,
5CD67BA22B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u.a in Frameworks */,
5CF9371C2B22552700E1D781 /* libHSsimplex-chat-5.4.0.7-EoJ0xKOyE47DlSpHXf0V4.a in Frameworks */,
5CF9371B2B22552700E1D781 /* libgmpxx.a in Frameworks */,
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
5CD67BA02B120ADF00C510B1 /* libgmp.a in Frameworks */,
5CD67BA42B120ADF00C510B1 /* libffi.a in Frameworks */,
5CD67BA32B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u-ghc8.10.7.a in Frameworks */,
5CF9371A2B22552700E1D781 /* libgmp.a in Frameworks */,
5CF937182B22552700E1D781 /* libffi.a in Frameworks */,
5CF937192B22552700E1D781 /* libHSsimplex-chat-5.4.0.7-EoJ0xKOyE47DlSpHXf0V4-ghc8.10.7.a in Frameworks */,
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -589,11 +589,11 @@
5C764E5C279C70B7000C6508 /* Libraries */ = {
isa = PBXGroup;
children = (
5CD67B9F2B120ADF00C510B1 /* libffi.a */,
5CD67B9B2B120ADF00C510B1 /* libgmp.a */,
5CD67B9C2B120ADF00C510B1 /* libgmpxx.a */,
5CD67B9E2B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u-ghc8.10.7.a */,
5CD67B9D2B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u.a */,
5CF937132B22552700E1D781 /* libffi.a */,
5CF937152B22552700E1D781 /* libgmp.a */,
5CF937162B22552700E1D781 /* libgmpxx.a */,
5CF937142B22552700E1D781 /* libHSsimplex-chat-5.4.0.7-EoJ0xKOyE47DlSpHXf0V4-ghc8.10.7.a */,
5CF937172B22552700E1D781 /* libHSsimplex-chat-5.4.0.7-EoJ0xKOyE47DlSpHXf0V4.a */,
);
path = Libraries;
sourceTree = "<group>";
@@ -1512,7 +1512,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 184;
CURRENT_PROJECT_VERSION = 185;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES;
@@ -1534,7 +1534,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 5.4;
MARKETING_VERSION = 5.4.1;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX;
SDKROOT = iphoneos;
@@ -1555,7 +1555,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 184;
CURRENT_PROJECT_VERSION = 185;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES;
@@ -1577,7 +1577,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 5.4;
MARKETING_VERSION = 5.4.1;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX;
SDKROOT = iphoneos;
@@ -1636,7 +1636,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 184;
CURRENT_PROJECT_VERSION = 185;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GENERATE_INFOPLIST_FILE = YES;
@@ -1649,7 +1649,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 5.4;
MARKETING_VERSION = 5.4.1;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1668,7 +1668,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 184;
CURRENT_PROJECT_VERSION = 185;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GENERATE_INFOPLIST_FILE = YES;
@@ -1681,7 +1681,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 5.4;
MARKETING_VERSION = 5.4.1;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1700,7 +1700,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 184;
CURRENT_PROJECT_VERSION = 185;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1724,7 +1724,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Libraries/sim",
);
MARKETING_VERSION = 5.4;
MARKETING_VERSION = 5.4.1;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos;
@@ -1746,7 +1746,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 184;
CURRENT_PROJECT_VERSION = 185;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1770,7 +1770,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Libraries/sim",
);
MARKETING_VERSION = 5.4;
MARKETING_VERSION = 5.4.1;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos;
+1 -1
View File
@@ -12,7 +12,7 @@ android {
defaultConfig {
applicationId = "chat.simplex.app"
minSdkVersion(26)
minSdkVersion(28)
targetSdkVersion(33)
// !!!
// skip version code after release to F-Droid, as it uses two version codes
@@ -41,9 +41,7 @@ class MainActivity: FragmentActivity() {
)
}
setContent {
SimpleXTheme {
AppScreen()
}
AppScreen()
}
SimplexApp.context.schedulePeriodicServiceRestartWorker()
SimplexApp.context.schedulePeriodicWakeUp()
@@ -32,7 +32,9 @@ class SimplexApp: Application(), LifecycleEventObserver {
override fun onCreate() {
super.onCreate()
if (ProcessPhoenix.isPhoenixProcess(this)) {
return;
return
} else {
registerGlobalErrorHandler()
}
context = this
initHaskell()
+1 -1
View File
@@ -110,7 +110,7 @@ android {
compileSdkVersion(34)
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdkVersion(26)
minSdkVersion(28)
targetSdkVersion(33)
}
compileOptions {
@@ -8,10 +8,14 @@ import android.os.Build
import android.view.*
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.activity.compose.setContent
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalView
import chat.simplex.common.views.helpers.KeyboardState
import chat.simplex.common.AppScreen
import chat.simplex.common.ui.theme.SimpleXTheme
import chat.simplex.common.views.helpers.*
import androidx.compose.ui.platform.LocalContext as LocalContext1
import chat.simplex.res.MR
actual fun showToast(text: String, timeout: Long) = Toast.makeText(androidAppContext, text, Toast.LENGTH_SHORT).show()
@@ -71,3 +75,37 @@ actual fun hideKeyboard(view: Any?) {
}
actual fun androidIsFinishingMainActivity(): Boolean = (mainActivity.get()?.isFinishing == true)
actual class GlobalExceptionsHandler: Thread.UncaughtExceptionHandler {
actual override fun uncaughtException(thread: Thread, e: Throwable) {
Log.e(TAG, "App crashed, thread name: " + thread.name + ", exception: " + e.stackTraceToString())
if (ModalManager.start.hasModalsOpen()) {
ModalManager.start.closeModal()
} else if (chatModel.chatId.value != null) {
// Since no modals are open, the problem is probably in ChatView
chatModel.chatId.value = null
chatModel.chatItems.clear()
} else {
// ChatList, nothing to do. Maybe to show other view except ChatList
}
chatModel.activeCall.value?.let {
withBGApi {
chatModel.callManager.endCall(it)
}
}
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.app_was_crashed),
text = e.stackTraceToString()
)
//mainActivity.get()?.recreate()
mainActivity.get()?.apply {
window
?.decorView
?.findViewById<ViewGroup>(android.R.id.content)
?.removeViewAt(0)
setContent {
AppScreen()
}
}
}
}
@@ -42,9 +42,11 @@ data class SettingsViewState(
@Composable
fun AppScreen() {
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
Surface(color = MaterialTheme.colors.background) {
MainScreen()
SimpleXTheme {
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
Surface(color = MaterialTheme.colors.background) {
MainScreen()
}
}
}
}
@@ -2,7 +2,6 @@ package chat.simplex.common.model
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.snapshots.SnapshotStateMap
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.SpanStyle
@@ -68,6 +67,9 @@ object ChatModel {
// set when app opened from external intent
val clearOverlays = mutableStateOf<Boolean>(false)
// Only needed during onboarding when user skipped password setup (left as random password)
val desktopOnboardingRandomPassword = mutableStateOf(false)
// set when app is opened via contact or invitation URI
val appOpenUrl = mutableStateOf<URI?>(null)
@@ -16,3 +16,11 @@ expect fun getKeyboardState(): State<KeyboardState>
expect fun hideKeyboard(view: Any?)
expect fun androidIsFinishingMainActivity(): Boolean
fun registerGlobalErrorHandler() {
Thread.setDefaultUncaughtExceptionHandler(GlobalExceptionsHandler())
}
expect class GlobalExceptionsHandler(): Thread.UncaughtExceptionHandler {
override fun uncaughtException(thread: Thread, e: Throwable)
}
@@ -207,12 +207,12 @@ fun createProfileInProfiles(chatModel: ChatModel, displayName: String, close: ()
fun createProfileOnboarding(chatModel: ChatModel, displayName: String, close: () -> Unit) {
withApi {
chatModel.controller.apiCreateActiveUser(
chatModel.currentUser.value = chatModel.controller.apiCreateActiveUser(
null, Profile(displayName.trim(), "", null)
) ?: return@withApi
val onboardingStage = chatModel.controller.appPrefs.onboardingStage
if (chatModel.users.isEmpty()) {
onboardingStage.set(if (appPlatform.isDesktop && chatModel.controller.appPrefs.initialRandomDBPassphrase.get()) {
onboardingStage.set(if (appPlatform.isDesktop && chatModel.controller.appPrefs.initialRandomDBPassphrase.get() && !chatModel.desktopOnboardingRandomPassword.value) {
OnboardingStage.Step2_5_SetupDatabasePassphrase
} else {
OnboardingStage.Step3_CreateSimpleXAddress
@@ -164,6 +164,12 @@ fun CIImageView(
}
}
}
} else {
KeyChangeEffect(file) {
if (res.value == null) {
res.value = imageAndFilePath(file)
}
}
}
val loaded = res.value
if (loaded != null) {
@@ -1,8 +1,11 @@
package chat.simplex.common.views.helpers
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
@@ -233,53 +236,71 @@ private fun alertTitle(title: String): (@Composable () -> Unit)? {
@Composable
private fun AlertContent(text: String?, hostDevice: Pair<Long?, String>?, extraPadding: Boolean = false, content: @Composable (() -> Unit)) {
Column(
Modifier
.padding(bottom = if (appPlatform.isDesktop) DEFAULT_PADDING else DEFAULT_PADDING_HALF)
) {
if (appPlatform.isDesktop) {
HostDeviceTitle(hostDevice, extraPadding = extraPadding)
} else {
Spacer(Modifier.size(DEFAULT_PADDING_HALF))
}
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) {
if (text != null) {
Text(
escapedHtmlToAnnotatedString(text, LocalDensity.current),
Modifier.fillMaxWidth().padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING * 1.5f),
fontSize = 16.sp,
textAlign = TextAlign.Center,
color = MaterialTheme.colors.secondary
)
BoxWithConstraints {
Column(
Modifier
.padding(bottom = if (appPlatform.isDesktop) DEFAULT_PADDING else DEFAULT_PADDING_HALF)
) {
if (appPlatform.isDesktop) {
HostDeviceTitle(hostDevice, extraPadding = extraPadding)
} else {
Spacer(Modifier.size(DEFAULT_PADDING_HALF))
}
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) {
if (text != null) {
Column(Modifier.heightIn(max = this@BoxWithConstraints.maxHeight * 0.7f)
.verticalScroll(rememberScrollState())
) {
SelectionContainer {
Text(
escapedHtmlToAnnotatedString(text, LocalDensity.current),
Modifier.fillMaxWidth().padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING * 1.5f),
fontSize = 16.sp,
textAlign = TextAlign.Center,
color = MaterialTheme.colors.secondary
)
}
}
}
}
content()
}
content()
}
}
@Composable
private fun AlertContent(text: AnnotatedString?, hostDevice: Pair<Long?, String>?, extraPadding: Boolean = false, content: @Composable (() -> Unit)) {
Column(
Modifier
.padding(bottom = if (appPlatform.isDesktop) DEFAULT_PADDING else DEFAULT_PADDING_HALF)
) {
if (appPlatform.isDesktop) {
HostDeviceTitle(hostDevice, extraPadding = extraPadding)
} else {
Spacer(Modifier.size(DEFAULT_PADDING_HALF))
}
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) {
if (text != null) {
Text(
text,
Modifier.fillMaxWidth().padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING * 1.5f),
fontSize = 16.sp,
textAlign = TextAlign.Center,
color = MaterialTheme.colors.secondary
)
BoxWithConstraints {
Column(
Modifier
.verticalScroll(rememberScrollState())
.padding(bottom = if (appPlatform.isDesktop) DEFAULT_PADDING else DEFAULT_PADDING_HALF)
) {
if (appPlatform.isDesktop) {
HostDeviceTitle(hostDevice, extraPadding = extraPadding)
} else {
Spacer(Modifier.size(DEFAULT_PADDING_HALF))
}
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) {
if (text != null) {
Column(
Modifier.heightIn(max = this@BoxWithConstraints.maxHeight * 0.7f)
.verticalScroll(rememberScrollState())
) {
SelectionContainer {
Text(
text,
Modifier.fillMaxWidth().padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING * 1.5f),
fontSize = 16.sp,
textAlign = TextAlign.Center,
color = MaterialTheme.colors.secondary
)
}
}
}
}
content()
}
content()
}
}
@@ -19,8 +19,8 @@ import dev.icerock.moko.resources.compose.painterResource
import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.*
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.*
import chat.simplex.common.views.database.PassphraseStrength
import chat.simplex.common.views.database.validKey
import chat.simplex.res.MR
@@ -123,6 +123,7 @@ fun DefaultConfigurableTextField(
isValid: (String) -> Boolean,
keyboardActions: KeyboardActions = KeyboardActions(),
keyboardType: KeyboardType = KeyboardType.Text,
fontSize: TextUnit = 16.sp,
dependsOn: State<Any?>? = null,
) {
var valid by remember { mutableStateOf(isValid(state.value.text)) }
@@ -175,14 +176,14 @@ fun DefaultConfigurableTextField(
textStyle = TextStyle.Default.copy(
color = color,
fontWeight = FontWeight.Normal,
fontSize = 16.sp
fontSize = fontSize
),
interactionSource = interactionSource,
decorationBox = @Composable { innerTextField ->
TextFieldDefaults.TextFieldDecorationBox(
value = state.value.text,
innerTextField = innerTextField,
placeholder = { Text(placeholder, color = MaterialTheme.colors.secondary) },
placeholder = { Text(placeholder, color = MaterialTheme.colors.secondary, fontSize = fontSize, maxLines = 1, overflow = TextOverflow.Ellipsis) },
singleLine = true,
enabled = enabled,
isError = !valid,
@@ -20,6 +20,7 @@ fun <T> ExposedDropDownSetting(
values: List<Pair<T, String>>,
selection: State<T>,
textColor: Color = MaterialTheme.colors.secondary,
fontSize: TextUnit = 16.sp,
label: String? = null,
enabled: State<Boolean> = mutableStateOf(true),
minWidth: Dp = 200.dp,
@@ -43,7 +44,8 @@ fun <T> ExposedDropDownSetting(
Modifier.widthIn(max = maxWidth),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = textColor
color = textColor,
fontSize = fontSize,
)
Spacer(Modifier.size(12.dp))
Icon(
@@ -69,6 +71,7 @@ fun <T> ExposedDropDownSetting(
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = if (isInDarkTheme()) MenuTextColorDark else Color.Black,
fontSize = fontSize,
)
}
}
@@ -91,6 +94,6 @@ fun <T> ExposedDropDownSettingRow(
onSelected: (T) -> Unit
) {
SettingsActionItemWithContent(icon, title, iconColor = iconTint, disabled = !enabled.value) {
ExposedDropDownSetting(values, selection ,textColor, label, enabled, minWidth, maxWidth, onSelected)
ExposedDropDownSetting(values, selection ,textColor, label = label, enabled = enabled, minWidth = minWidth, maxWidth = maxWidth, onSelected = onSelected)
}
}
@@ -73,7 +73,8 @@ private fun LinkAMobileLayout(
}
Box(Modifier.weight(0.7f)) {
AddingMobileDevice(false, staleQrCode, connecting) {
if (chatModel.remoteHosts.isEmpty()) {
// currentRemoteHost will be set instantly but remoteHosts may be delayed
if (chatModel.remoteHosts.isEmpty() && chatModel.currentRemoteHost.value == null) {
chatModel.controller.appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo)
} else {
chatModel.controller.appPrefs.onboardingStage.set(OnboardingStage.OnboardingComplete)
@@ -1,10 +1,7 @@
package chat.simplex.common.views.onboarding
import SectionBottomSpacer
import SectionItemView
import SectionItemViewSpaceBetween
import SectionTextFooter
import SectionView
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardActions
@@ -15,14 +12,12 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.key.*
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.text.input.ImeAction
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import chat.simplex.common.model.*
import chat.simplex.common.platform.*
@@ -177,7 +172,10 @@ private fun SetupDatabasePassphraseLayout(
}
Spacer(Modifier.weight(1f))
SkipButton(progressIndicator.value, nextStep)
SkipButton(progressIndicator.value) {
chatModel.desktopOnboardingRandomPassword.value = true
nextStep()
}
SectionBottomSpacer()
}
@@ -174,8 +174,6 @@ private fun ConnectMobileViewLayout(
sessionCode: String?,
port: String?,
staleQrCode: Boolean = false,
editEnabled: Boolean = false,
editClicked: () -> Unit = {},
refreshQrCode: () -> Unit = {},
UnderQrLayout: @Composable () -> Unit = {},
) {
@@ -201,16 +199,7 @@ private fun ConnectMobileViewLayout(
}
}
SectionTextFooter(annotatedStringResource(MR.strings.open_on_mobile_and_scan_qr_code), textAlign = TextAlign.Center)
Row(verticalAlignment = Alignment.CenterVertically) {
SectionTextFooter(annotatedStringResource(MR.strings.waiting_for_mobile_to_connect_on_port, port), textAlign = TextAlign.Center)
if (editEnabled) {
Spacer(Modifier.width(4.dp))
IconButton(editClicked, Modifier.size(16.dp)) {
Icon(painterResource(MR.images.ic_edit), stringResource(MR.strings.edit_verb), Modifier.size(16.dp), tint = MaterialTheme.colors.primary)
}
Spacer(Modifier.width(DEFAULT_PADDING))
}
}
SectionTextFooter(annotatedStringResource(MR.strings.waiting_for_mobile_to_connect), textAlign = TextAlign.Center)
UnderQrLayout()
@@ -249,7 +238,9 @@ private fun ConnectMobileViewLayout(
}
}
}
SectionBottomSpacer()
if (invitation != null) {
SectionBottomSpacer()
}
}
}
@@ -275,10 +266,9 @@ private fun showAddingMobileDevice(connecting: MutableState<Boolean>) {
@Composable
fun AddingMobileDevice(showTitle: Boolean, staleQrCode: MutableState<Boolean>, connecting: MutableState<Boolean>, close: () -> Unit) {
val cachedR = remember { mutableStateOf<CR.RemoteHostStarted?>(null) }
var cachedR by remember { mutableStateOf<CR.RemoteHostStarted?>(null) }
val customAddress = rememberSaveable { mutableStateOf<RemoteCtrlAddress?>(null) }
val customPort = rememberSaveable { mutableStateOf<Int?>(null) }
var editing by rememberSaveable { mutableStateOf(false) }
val startRemoteHost = suspend {
val r = chatModel.controller.startRemoteHost(
rhId = null,
@@ -287,9 +277,9 @@ fun AddingMobileDevice(showTitle: Boolean, staleQrCode: MutableState<Boolean>, c
port = if (customPort.value != cachedR.port) customPort.value else cachedR.rh?.bindPort_
)
if (r != null) {
cachedR.value = r
cachedR = r
connecting.value = true
customAddress.value = cachedR.address
customAddress.value = cachedR.addresses.firstOrNull()
customPort.value = cachedR.port
chatModel.remoteHostPairing.value = null to RemoteHostSessionState.Starting
}
@@ -307,23 +297,20 @@ fun AddingMobileDevice(showTitle: Boolean, staleQrCode: MutableState<Boolean>, c
val remoteDeviceName = pairing.value?.first?.hostDeviceName
ConnectMobileViewLayout(
title = if (!showTitle) null else if (cachedSessionCode == null) stringResource(MR.strings.link_a_mobile) else stringResource(MR.strings.verify_connection),
invitation = cachedR.invitation,
invitation = cachedR?.invitation,
deviceName = remoteDeviceName,
sessionCode = cachedSessionCode,
port = cachedR.value?.ctrlPort,
staleQrCode = staleQrCode.value || (cachedR.address != customAddress.value && customAddress.value != null) || (cachedR.port != customPort.value && customPort.value != null),
editEnabled = !editing && cachedR.addresses.isNotEmpty(),
editClicked = { editing = true },
port = cachedR?.ctrlPort,
staleQrCode = staleQrCode.value || (cachedR.address != customAddress.value && customAddress.value != null) || cachedR.port != customPort.value,
refreshQrCode = {
withBGApi {
if (chatController.stopRemoteHost(null)) {
startRemoteHost()
staleQrCode.value = false
editing = false
}
}
},
UnderQrLayout = { UnderQrLayout(editing, cachedR, customAddress, customPort) }
UnderQrLayout = { UnderQrLayout(cachedR, customAddress, customPort) }
)
val oldRemoteHostId by remember { mutableStateOf(chatModel.currentRemoteHost.value?.remoteHostId) }
LaunchedEffect(remember { chatModel.currentRemoteHost }.value) {
@@ -353,10 +340,9 @@ fun AddingMobileDevice(showTitle: Boolean, staleQrCode: MutableState<Boolean>, c
private fun showConnectMobileDevice(rh: RemoteHostInfo, connecting: MutableState<Boolean>) {
ModalManager.start.showModalCloseable { close ->
val cachedR = remember { mutableStateOf<CR.RemoteHostStarted?>(null) }
var cachedR by remember { mutableStateOf<CR.RemoteHostStarted?>(null) }
val customAddress = rememberSaveable { mutableStateOf<RemoteCtrlAddress?>(null) }
val customPort = rememberSaveable { mutableStateOf<Int?>(null) }
var editing by rememberSaveable { mutableStateOf(false) }
val startRemoteHost = suspend {
val r = chatModel.controller.startRemoteHost(
rhId = rh.remoteHostId,
@@ -365,9 +351,9 @@ private fun showConnectMobileDevice(rh: RemoteHostInfo, connecting: MutableState
port = if (customPort.value != cachedR.port) customPort.value else cachedR.rh?.bindPort_ ?: rh.bindPort_
)
if (r != null) {
cachedR.value = r
cachedR = r
connecting.value = true
customAddress.value = cachedR.address
customAddress.value = cachedR.addresses.firstOrNull()
customPort.value = cachedR.port
chatModel.remoteHostPairing.value = null to RemoteHostSessionState.Starting
}
@@ -384,22 +370,19 @@ private fun showConnectMobileDevice(rh: RemoteHostInfo, connecting: MutableState
}
ConnectMobileViewLayout(
title = if (cachedSessionCode == null) stringResource(MR.strings.scan_from_mobile) else stringResource(MR.strings.verify_connection),
invitation = cachedR.invitation,
invitation = cachedR?.invitation,
deviceName = pairing.value?.first?.hostDeviceName ?: rh.hostDeviceName,
sessionCode = cachedSessionCode,
port = cachedR.value?.ctrlPort,
staleQrCode = (cachedR.address != customAddress.value && customAddress.value != null) || (cachedR.port != customPort.value && customPort.value != null),
editEnabled = !editing && cachedR.addresses.isNotEmpty(),
editClicked = { editing = true },
port = cachedR?.ctrlPort,
staleQrCode = (cachedR.address != customAddress.value && customAddress.value != null) || cachedR.port != customPort.value,
refreshQrCode = {
withBGApi {
if (chatController.stopRemoteHost(rh.remoteHostId)) {
startRemoteHost()
editing = false
}
}
},
UnderQrLayout = { UnderQrLayout(editing, cachedR, customAddress, customPort) }
UnderQrLayout = { UnderQrLayout(cachedR, customAddress, customPort) }
)
LaunchedEffect(remember { chatModel.currentRemoteHost }.value) {
if (cachedR.remoteHostId != null && chatModel.currentRemoteHost.value?.remoteHostId == cachedR.remoteHostId) {
@@ -453,48 +436,75 @@ private fun showConnectedMobileDevice(rh: RemoteHostInfo, disconnectHost: () ->
}
@Composable
private fun UnderQrLayout(editing: Boolean, cachedR: State<CR.RemoteHostStarted?>, customAddress: MutableState<RemoteCtrlAddress?>, customPort: MutableState<Int?>) {
if (editing) {
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center) {
private fun UnderQrLayout(cachedR: CR.RemoteHostStarted?, customAddress: MutableState<RemoteCtrlAddress?>, customPort: MutableState<Int?>) {
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center) {
if (cachedR.addresses.size > 1) {
ExposedDropDownSetting(
cachedR.addresses.map { it to it.address + " (${it.`interface`})" },
customAddress,
textColor = MaterialTheme.colors.onBackground,
fontSize = 14.sp,
minWidth = 250.dp,
maxWidth = with(LocalDensity.current) { 250.sp.toDp() },
enabled = remember { mutableStateOf(cachedR.addresses.size > 1) },
onSelected = {
customAddress.value = it
}
)
val portUnsaved = rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(TextFieldValue((customPort.value ?: cachedR.port!!).toString()))
}
Spacer(Modifier.width(DEFAULT_PADDING))
} else {
Spacer(Modifier.width(10.dp))
Text(customAddress.value?.address + " (${customAddress.value?.`interface`})", fontSize = 14.sp, color = MaterialTheme.colors.onBackground)
}
val portUnsaved = rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(TextFieldValue((customPort.value ?: cachedR.port!!).toString()))
}
Spacer(Modifier.width(DEFAULT_PADDING))
Box {
DefaultConfigurableTextField(
portUnsaved,
stringResource(MR.strings.port_verb),
modifier = Modifier.widthIn(max = 100.dp),
isValid = { validPort(it) && it.toInt() > 1023 },
stringResource(MR.strings.random_port),
modifier = Modifier.widthIn(max = 132.dp),
isValid = { (validPort(it) && it.toInt() > 1023) || it.isBlank() },
keyboardActions = KeyboardActions(onDone = { defaultKeyboardAction(ImeAction.Done) }),
keyboardType = KeyboardType.Number,
fontSize = 14.sp,
)
LaunchedEffect(Unit) {
snapshotFlow { portUnsaved.value.text }
.distinctUntilChanged()
.collect {
if (validPort(it) && it.toInt() > 1023) {
customPort.value = it.toInt()
}
if (validPort(portUnsaved.value.text) && portUnsaved.value.text.toInt() > 1023) {
Icon(painterResource(MR.images.ic_edit), stringResource(MR.strings.edit_verb), Modifier.padding(end = 56.dp).size(16.dp).align(Alignment.CenterEnd), tint = MaterialTheme.colors.secondary)
IconButton(::showOpenPortAlert, Modifier.align(Alignment.TopEnd).padding(top = 2.dp)) {
Icon(painterResource(MR.images.ic_info), null, tint = MaterialTheme.colors.primary)
}
}
}
LaunchedEffect(Unit) {
snapshotFlow { portUnsaved.value.text }
.distinctUntilChanged()
.collect {
if (validPort(it) && it.toInt() > 1023) {
customPort.value = it.toInt()
} else {
customPort.value = null
}
}
}
KeyChangeEffect(customPort.value) {
if (customPort.value != null) {
portUnsaved.value = portUnsaved.value.copy(text = customPort.value.toString())
}
}
}
}
private val State<CR.RemoteHostStarted?>.rh: RemoteHostInfo? get() = value?.remoteHost_
private val State<CR.RemoteHostStarted?>.remoteHostId: Long? get() = value?.remoteHost_?.remoteHostId
private val State<CR.RemoteHostStarted?>.invitation: String? get() = value?.invitation
private val State<CR.RemoteHostStarted?>.address: RemoteCtrlAddress? get() = value?.localAddrs?.firstOrNull()
private val State<CR.RemoteHostStarted?>.addresses: List<RemoteCtrlAddress> get() =
(if (controller.appPrefs.developerTools.get()) value?.localAddrs else value?.localAddrs?.filterNot { it.address == "127.0.0.1" }) ?: emptyList()
private val State<CR.RemoteHostStarted?>.port: Int? get() = value?.ctrlPort?.toIntOrNull()
private fun showOpenPortAlert() {
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.open_port_in_firewall_title),
text = generalGetString(MR.strings.open_port_in_firewall_desc),
)
}
private val CR.RemoteHostStarted?.rh: RemoteHostInfo? get() = this?.remoteHost_
private val CR.RemoteHostStarted?.remoteHostId: Long? get() = this?.remoteHost_?.remoteHostId
private val CR.RemoteHostStarted?.address: RemoteCtrlAddress? get() = this?.localAddrs?.firstOrNull()
private val CR.RemoteHostStarted?.addresses: List<RemoteCtrlAddress> get() =
(if (controller.appPrefs.developerTools.get() || this?.localAddrs?.indexOfFirst { it.address == "127.0.0.1" } == 0) this?.localAddrs else this?.localAddrs?.filterNot { it.address == "127.0.0.1" }) ?: emptyList()
private val CR.RemoteHostStarted?.port: Int? get() = this?.ctrlPort?.toIntOrNull()
@@ -18,6 +18,7 @@
<string name="opening_database">Opening database…</string>
<string name="non_content_uri_alert_title">Invalid file path</string>
<string name="non_content_uri_alert_text">You shared an invalid file path. Report the issue to the app developers.</string>
<string name="app_was_crashed">View crashed</string>
<!-- Server info - ChatModel.kt -->
<string name="server_connected">connected</string>
@@ -1665,7 +1666,7 @@
<string name="disconnect_desktop_question">Disconnect desktop?</string>
<string name="only_one_device_can_work_at_the_same_time">Only one device can work at the same time</string>
<string name="open_on_mobile_and_scan_qr_code"><![CDATA[Open <i>Use from desktop</i> in mobile app and scan QR code.]]></string>
<string name="waiting_for_mobile_to_connect_on_port"><![CDATA[Waiting for mobile to connect on port <i>%s</i>]]></string>
<string name="waiting_for_mobile_to_connect">Waiting for mobile to connect:</string>
<string name="bad_desktop_address">Bad desktop address</string>
<string name="desktop_incompatible_version">Incompatible version</string>
<string name="desktop_app_version_is_incompatible">Desktop app version %s is not compatible with this app.</string>
@@ -1693,6 +1694,9 @@
<string name="not_compatible">Not compatible!</string>
<string name="refresh_qr_code">Refresh</string>
<string name="no_connected_mobile">No connected mobile</string>
<string name="random_port">Random</string>
<string name="open_port_in_firewall_title">Open port in firewall</string>
<string name="open_port_in_firewall_desc">To allow a mobile app to connect to the desktop, open this port in your firewall, if you have it enabled</string>
<!-- Under development -->
<string name="in_developing_title">Coming soon!</string>
@@ -1581,7 +1581,7 @@
<string name="v5_4_better_groups_descr">Schnellerer Gruppenbeitritt und zuverlässigere Nachrichtenzustellung.</string>
<string name="linked_mobiles">Verknüpfte Mobiltelefone</string>
<string name="this_device_name">Dieser Gerätename</string>
<string name="waiting_for_mobile_to_connect_on_port"><![CDATA[Auf die Mobiltelefonverbindung über Port <i>%s</i> warten]]></string>
<string name="waiting_for_mobile_to_connect">Auf die Mobiltelefonverbindung warten:</string>
<string name="loading_remote_file_title">Laden der Datei</string>
<string name="link_a_mobile">Zu einem Mobiltelefon verbinden</string>
<string name="settings_section_title_use_from_desktop">Vom Desktop aus nutzen</string>
@@ -1486,7 +1486,7 @@
<string name="desktop_device">Bureau</string>
<string name="connected_to_desktop">Connecté au bureau</string>
<string name="this_device_name">Ce nom d\'appareil</string>
<string name="waiting_for_mobile_to_connect_on_port"><![CDATA[En attente d\'une connexion mobile sur le port <i>%s</i>]]></string>
<string name="waiting_for_mobile_to_connect">En attente d\'une connexion mobile:</string>
<string name="loading_remote_file_title">Chargement du fichier</string>
<string name="connecting_to_desktop">Connexion au bureau</string>
<string name="desktop_devices">Appareils de bureau</string>
@@ -1517,6 +1517,6 @@
<string name="v5_4_more_things_descr">- avvisa facoltativamente i contatti eliminati.
\n- nomi del profilo con spazi.
\n- e molto altro!</string>
<string name="waiting_for_mobile_to_connect_on_port"><![CDATA[In attesa che il cellulare si connetta alla porta <i>%s</i>]]></string>
<string name="waiting_for_mobile_to_connect">In attesa che il cellulare si connette:</string>
<string name="group_member_role_author">autore</string>
</resources>
@@ -1516,7 +1516,7 @@
\n- en meer!</string>
<string name="remote_host_was_disconnected_toast"><![CDATA[Mobiele verbinding <b>%s</b> is verbroken]]></string>
<string name="group_member_role_author">auteur</string>
<string name="waiting_for_mobile_to_connect_on_port"><![CDATA[Wachten tot mobiel verbinding maakt op poort <i>%s</i>]]></string>
<string name="waiting_for_mobile_to_connect">Wachten tot mobiel verbinding maakt:</string>
<string name="multicast_connect_automatically">Automatisch verbinden</string>
<string name="waiting_for_desktop">Wachten op desktop…</string>
<string name="found_desktop">Desktop gevonden</string>
@@ -1496,7 +1496,7 @@
<string name="v5_4_better_groups_descr">Szybsze dołączenie i bardziej niezawodne wiadomości.</string>
<string name="linked_mobiles">Połączone telefony</string>
<string name="this_device_name">Nazwa tego urządzenia</string>
<string name="waiting_for_mobile_to_connect_on_port"><![CDATA[Oczekiwanie na połączenie telefonu na port <i>%s</i>]]></string>
<string name="waiting_for_mobile_to_connect">Oczekiwanie na połączenie telefonu:</string>
<string name="loading_remote_file_title">Ładowanie pliku</string>
<string name="found_desktop">Znaleziono komputer</string>
<string name="desktop_devices">Urządzenia komputerowe</string>
@@ -1601,7 +1601,7 @@
<string name="verify_connection">Проверить соединение</string>
<string name="multicast_connect_automatically">Соединяться автоматически</string>
<string name="waiting_for_desktop">Ожидается подключение…</string>
<string name="waiting_for_mobile_to_connect_on_port"><![CDATA[Ожидается подключение мобильного через порт <i>%s</i>]]></string>
<string name="waiting_for_mobile_to_connect">Ожидается подключение мобильного:</string>
<string name="found_desktop">Компьютер найден</string>
<string name="not_compatible">Несовместимая версия!</string>
<string name="group_member_role_author">автор</string>
@@ -1517,7 +1517,7 @@
<string name="v5_4_more_things_descr">- 可选择通知已删除的联系人。
\n- 带空格的个人资料名称。
\n- 以及更多!</string>
<string name="waiting_for_mobile_to_connect_on_port"><![CDATA[正等待移动设备在端口 <i>%s</i> 进行连接]]></string>
<string name="waiting_for_mobile_to_connect">正等待移动设备 进行连接:</string>
<string name="group_member_role_author">作者</string>
<string name="multicast_connect_automatically">自动连接</string>
<string name="waiting_for_desktop">等待桌面中…</string>
@@ -9,30 +9,73 @@ import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.awt.ComposeWindow
import androidx.compose.ui.input.key.*
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.*
import chat.simplex.common.model.ChatController
import chat.simplex.common.model.ChatModel
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.DEFAULT_START_MODAL_WIDTH
import chat.simplex.common.ui.theme.SimpleXTheme
import chat.simplex.common.views.TerminalView
import chat.simplex.common.views.helpers.FileDialogChooser
import chat.simplex.common.views.helpers.escapedHtmlToAnnotatedString
import chat.simplex.common.views.helpers.*
import chat.simplex.res.MR
import dev.icerock.moko.resources.compose.stringResource
import kotlinx.coroutines.*
import java.awt.event.WindowEvent
import java.awt.event.WindowFocusListener
import java.io.File
import kotlin.system.exitProcess
val simplexWindowState = SimplexWindowState()
fun showApp() = application {
fun showApp() {
val closedByError = mutableStateOf(true)
while (closedByError.value) {
application(exitProcessOnExit = false) {
CompositionLocalProvider(
LocalWindowExceptionHandlerFactory provides WindowExceptionHandlerFactory { window ->
WindowExceptionHandler { e ->
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.app_was_crashed),
text = e.stackTraceToString()
)
Log.e(TAG, "App crashed, thread name: " + Thread.currentThread().name + ", exception: " + e.stackTraceToString())
window.dispatchEvent(WindowEvent(window, WindowEvent.WINDOW_CLOSING))
closedByError.value = true
// If the left side of screen has open modal, it's probably caused the crash
if (ModalManager.start.hasModalsOpen()) {
ModalManager.start.closeModal()
} else if (ModalManager.center.hasModalsOpen() || ModalManager.end.hasModalsOpen()) {
ModalManager.center.closeModal()
ModalManager.end.closeModal()
// Better to not close fullscreen since it can contain passcode
} else {
// The last possible cause that can be closed
chatModel.chatId.value = null
chatModel.chatItems.clear()
}
chatModel.activeCall.value?.let {
withBGApi {
chatModel.callManager.endCall(it)
}
}
}
}
) {
AppWindow(closedByError)
}
}
}
exitProcess(0)
}
@Composable
private fun ApplicationScope.AppWindow(closedByError: MutableState<Boolean>) {
// Creates file if not exists; comes with proper defaults
val state = getStoredWindowState()
val windowState: WindowState = rememberWindowState(
placement = WindowPlacement.Floating,
width = state.width.dp,
@@ -46,59 +89,62 @@ fun showApp() = application {
windowState.size.width.value,
windowState.size.height.value
) {
storeWindowState(WindowPositionSize(
x = windowState.position.x.value.toInt(),
y = windowState.position.y.value.toInt(),
width = windowState.size.width.value.toInt(),
height = windowState.size.height.value.toInt()
))
storeWindowState(
WindowPositionSize(
x = windowState.position.x.value.toInt(),
y = windowState.position.y.value.toInt(),
width = windowState.size.width.value.toInt(),
height = windowState.size.height.value.toInt()
)
)
}
simplexWindowState.windowState = windowState
// Reload all strings in all @Composable's after language change at runtime
if (remember { ChatController.appPrefs.appLanguage.state }.value != "") {
Window(state = windowState, onCloseRequest = ::exitApplication, onKeyEvent = {
Window(state = windowState, onCloseRequest = { closedByError.value = false; exitApplication() }, onKeyEvent = {
if (it.key == Key.Escape && it.type == KeyEventType.KeyUp) {
simplexWindowState.backstack.lastOrNull()?.invoke() != null
} else {
false
}
}, title = "SimpleX") {
SimpleXTheme {
AppScreen()
if (simplexWindowState.openDialog.isAwaiting) {
FileDialogChooser(
title = "SimpleX",
isLoad = true,
params = simplexWindowState.openDialog.params,
onResult = {
simplexWindowState.openDialog.onResult(it.firstOrNull())
}
)
}
simplexWindowState.window = window
AppScreen()
if (simplexWindowState.openDialog.isAwaiting) {
FileDialogChooser(
title = "SimpleX",
isLoad = true,
params = simplexWindowState.openDialog.params,
onResult = {
simplexWindowState.openDialog.onResult(it.firstOrNull())
}
)
}
if (simplexWindowState.openMultipleDialog.isAwaiting) {
FileDialogChooser(
title = "SimpleX",
isLoad = true,
params = simplexWindowState.openMultipleDialog.params,
onResult = {
simplexWindowState.openMultipleDialog.onResult(it)
}
)
}
if (simplexWindowState.openMultipleDialog.isAwaiting) {
FileDialogChooser(
title = "SimpleX",
isLoad = true,
params = simplexWindowState.openMultipleDialog.params,
onResult = {
simplexWindowState.openMultipleDialog.onResult(it)
}
)
}
if (simplexWindowState.saveDialog.isAwaiting) {
FileDialogChooser(
title = "SimpleX",
isLoad = false,
params = simplexWindowState.saveDialog.params,
onResult = { simplexWindowState.saveDialog.onResult(it.firstOrNull()) }
)
}
val toasts = remember { simplexWindowState.toasts }
val toast = toasts.firstOrNull()
if (toast != null) {
if (simplexWindowState.saveDialog.isAwaiting) {
FileDialogChooser(
title = "SimpleX",
isLoad = false,
params = simplexWindowState.saveDialog.params,
onResult = { simplexWindowState.saveDialog.onResult(it.firstOrNull()) }
)
}
val toasts = remember { simplexWindowState.toasts }
val toast = toasts.firstOrNull()
if (toast != null) {
SimpleXTheme {
Box(Modifier.fillMaxSize().padding(bottom = 20.dp), contentAlignment = Alignment.BottomCenter) {
Text(
escapedHtmlToAnnotatedString(toast.first, LocalDensity.current),
@@ -107,11 +153,11 @@ fun showApp() = application {
style = MaterialTheme.typography.body1
)
}
// Shows toast in insertion order with preferred delay per toast. New one will be shown once previous one expires
LaunchedEffect(toast, toasts.size) {
delay(toast.second)
simplexWindowState.toasts.removeFirst()
}
}
// Shows toast in insertion order with preferred delay per toast. New one will be shown once previous one expires
LaunchedEffect(toast, toasts.size) {
delay(toast.second)
simplexWindowState.toasts.removeFirst()
}
}
var windowFocused by remember { simplexWindowState.windowFocused }
@@ -160,6 +206,7 @@ class SimplexWindowState {
val saveDialog = DialogState<File?>()
val toasts = mutableStateListOf<Pair<String, Long>>()
var windowFocused = mutableStateOf(true)
var window: ComposeWindow? = null
}
data class DialogParams(
@@ -188,7 +235,5 @@ class DialogState<T> {
@Preview
@Composable
fun AppPreview() {
SimpleXTheme {
AppScreen()
}
AppScreen()
}
@@ -19,3 +19,9 @@ actual fun getKeyboardState(): State<KeyboardState> = remember { mutableStateOf(
actual fun hideKeyboard(view: Any?) {}
actual fun androidIsFinishingMainActivity(): Boolean = false
actual class GlobalExceptionsHandler: Thread.UncaughtExceptionHandler {
actual override fun uncaughtException(thread: Thread, e: Throwable) {
Log.e(TAG, "App crashed, thread name: " + thread.name + ", exception: " + e.stackTraceToString())
}
}
@@ -6,6 +6,7 @@ import androidx.compose.runtime.Composable
import chat.simplex.common.model.ChatModel.controller
import chat.simplex.common.model.SharedPreference
import chat.simplex.common.model.User
import chat.simplex.common.platform.chatModel
import chat.simplex.common.ui.theme.DEFAULT_PADDING
import chat.simplex.res.MR
import dev.icerock.moko.resources.compose.painterResource
@@ -14,7 +15,7 @@ import dev.icerock.moko.resources.compose.painterResource
actual fun OnboardingActionButton(user: User?, onboardingStage: SharedPreference<OnboardingStage>, onclick: (() -> Unit)?) {
if (user == null) {
Row(horizontalArrangement = Arrangement.spacedBy(DEFAULT_PADDING * 2.5f)) {
OnboardingActionButton(MR.strings.link_a_mobile, onboarding = if (controller.appPrefs.initialRandomDBPassphrase.get()) OnboardingStage.Step2_5_SetupDatabasePassphrase else OnboardingStage.LinkAMobile, true, icon = painterResource(MR.images.ic_smartphone_300), onclick = onclick)
OnboardingActionButton(MR.strings.link_a_mobile, onboarding = if (controller.appPrefs.initialRandomDBPassphrase.get() && !chatModel.desktopOnboardingRandomPassword.value) OnboardingStage.Step2_5_SetupDatabasePassphrase else OnboardingStage.LinkAMobile, true, icon = painterResource(MR.images.ic_smartphone_300), onclick = onclick)
OnboardingActionButton(MR.strings.create_your_profile, onboarding = OnboardingStage.Step2_CreateProfile, true, icon = painterResource(MR.images.ic_desktop), onclick = onclick)
}
} else {
@@ -3,10 +3,6 @@ package chat.simplex.desktop
import chat.simplex.common.platform.*
import chat.simplex.common.showApp
import java.io.File
import java.nio.file.*
import java.nio.file.attribute.BasicFileAttributes
import java.nio.file.attribute.FileTime
import kotlin.io.path.setLastModifiedTime
fun main() {
initHaskell()
+4 -4
View File
@@ -25,11 +25,11 @@ android.nonTransitiveRClass=true
android.enableJetifier=true
kotlin.mpp.androidSourceSetLayoutVersion=2
android.version_name=5.4
android.version_code=162
android.version_name=5.4.1
android.version_code=164
desktop.version_name=5.4
desktop.version_code=18
desktop.version_name=5.4.1
desktop.version_code=19
kotlin.version=1.8.20
gradle.plugin.version=7.4.2
+1 -1
View File
@@ -8,7 +8,7 @@ module Main where
import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Monad.Reader
import Control.Monad
import qualified Data.Text as T
import Simplex.Chat.Bot
import Simplex.Chat.Controller
@@ -9,7 +9,7 @@ module Broadcast.Bot where
import Control.Concurrent (forkIO)
import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Monad.Reader
import Control.Monad
import qualified Data.Text as T
import Broadcast.Options
import Simplex.Chat.Bot
@@ -83,5 +83,6 @@ mkChatOpts BroadcastBotOpts {coreOptions} =
allowInstantFiles = True,
autoAcceptFileSize = 0,
muteNotifications = True,
markRead = False,
maintenance = False
}
+1
View File
@@ -8,6 +8,7 @@
module Server where
import Control.Monad
import Control.Monad.Except
import Control.Monad.Reader
import Data.Aeson (FromJSON, ToJSON)
@@ -5,10 +5,10 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Directory.Options
( DirectoryOpts (..),
getDirectoryOpts,
mkChatOpts,
)
( DirectoryOpts (..),
getDirectoryOpts,
mkChatOpts,
)
where
import Options.Applicative
@@ -35,8 +35,8 @@ directoryOpts appDir defaultDbFileName = do
<> help "Comma-separated list of super-users in the format CONTACT_ID:DISPLAY_NAME who will be allowed to manage the directory"
)
directoryLog <-
Just <$>
strOption
Just
<$> strOption
( long "directory-file"
<> metavar "DIRECTORY_FILE"
<> help "Append only log for directory state"
@@ -81,5 +81,6 @@ mkChatOpts DirectoryOpts {coreOptions} =
allowInstantFiles = True,
autoAcceptFileSize = 0,
muteNotifications = True,
markRead = False,
maintenance = False
}
@@ -15,7 +15,7 @@ where
import Control.Concurrent (forkIO)
import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Monad.Reader
import Control.Monad
import qualified Data.ByteString.Char8 as B
import Data.List (sortOn)
import Data.Maybe (fromMaybe, maybeToList)
+11 -3
View File
@@ -4,12 +4,14 @@ packages: .
with-compiler: ghc-8.10.7
index-state: 2023-10-06T00:00:00Z
constraints: zip +disable-bzip2 +disable-zstd
source-repository-package
type: git
location: https://github.com/simplex-chat/simplexmq.git
tag: 117168ccce93ef5ac478c02e3f02a018fa8a2200
tag: a860936072172e261480fa6bdd95203976e366b2
source-repository-package
type: git
@@ -24,12 +26,12 @@ source-repository-package
source-repository-package
type: git
location: https://github.com/simplex-chat/direct-sqlcipher.git
tag: 34309410eb2069b029b8fc1872deb1e0db123294
tag: f814ee68b16a9447fbb467ccc8f29bdd3546bfd9
source-repository-package
type: git
location: https://github.com/simplex-chat/sqlcipher-simple.git
tag: 5e154a2aeccc33ead6c243ec07195ab673137221
tag: a46bd361a19376c5211f1058908fc0ae6bf42446
source-repository-package
type: git
@@ -45,3 +47,9 @@ source-repository-package
type: git
location: https://github.com/simplex-chat/android-support.git
tag: 9aa09f148089d6752ce563b14c2df1895718d806
-- TODO this fork is only needed to compile with GHC 8.10.7 - it allows previous base version
source-repository-package
type: git
location: https://github.com/simplex-chat/zip.git
tag: bd421c6b19cc4c465cd7af1f6f26169fb8ee1ebc
+2
View File
@@ -45,6 +45,8 @@ When people connect to you via this address, you will receive a connection reque
If you start receiving too many requests via this address it is always safe to remove it all the connections you created via this address will remain active, as this address is not used to deliver the messages.
See the comparison with [1-time invitation links](./making-connections.md#comparison-of-1-time-invitation-links-and-simplex-contact-addresses).
Read more in [this post](../../blog/20221108-simplex-chat-v4.2-security-audit-new-website.md#auto-accept-contact-requests).
### Chat preferences
+65
View File
@@ -13,6 +13,71 @@ Private Connection — connect using an invitation link or QR code via video or
Group Chat — Users have the option to create a secret group, share their contact link [which can be deleted later on], or generate a one-time invitation link.
## Your SimpleX contact address
You can [create an optional long term address](./app-settings.md#your-simplex-contact-address) for other people to connect with you. Unlike 1-time invitation links, these addresses can be used many times, that makes them good to share online, e.g. on social media platforms, or in email signatures. That helps more people discover SimpleX Chat, so please do it!
When people connect to you via this address, you will receive a connection request that you can accept or reject. You can configure an automatic acceptance of connection request and an automatic welcome message that will be sent to the new contacts. You can also share this address as part of your SimpleX profile, so group members can connect to you, and your contacts can share it with others - if this is something that you want.
If you start receiving too many requests via this address it is always safe to remove it all the connections you created via this address will remain active, as this address is not used to deliver the messages.
### Comparison of 1-time invitation links and SimpleX Contact addresses
<table>
<tr>
<th></th>
<th>1-time invitation link</th>
<th>SimpleX contact address</th>
</tr>
<tr>
<td>Can be used many times?</td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td>Can be included in user profile?</td>
<td>No, as it can only be used once.</td>
<td>Yes, to allow group members to connect directly, and your contacts to pass it on to their contacts.</td>
</tr>
<tr>
<td>When to use it?</td>
<td>With somebody you know, via another communication channel or QR code (in person or during a video call)</td>
<td>Where many people can see and connect via it, e.g. in email signature, website, social media or group chat.</td>
</tr>
<tr>
<td>Security</td>
<td>More secure, as can only be used once, and the initial connection request (including profile) is encrypted with double ratchet.</td>
<td>Initial connection request is also e2e encrypted, but without double ratchet (it is initialized when request is accepted).</td>
</tr>
<tr>
<td>Identification</td>
<td>Both sides know who they connect to, as they know with whom and by who the link was shared. You can attach alias to this invitation as soon as you share it or use it, to identify the other person when connection is established.</td>
<td>Only the person using the address knows who they connect to, via the channel where they found the address (email, social media, etc.). The address owner can only see the user profile of the request, and has no proof of identity from the person sending the request<sup>*</sup>.</td>
</tr>
<tr>
<td>Advantages over other platforms</td>
<td>There is no direct analogy, other platforms dont offer one-time invitations without any fixed part identifying the user.</td>
<td>Unlike addresses in other platforms, SimpleX addresses are not used to deliver the messages &mdash; only the initial connection requests.<br>It means that removing this address will not break the contacts made via it (like changing an email address would), it would only prevent new connections, which makes it a good solution against spam and abuse.</td>
</tr>
<tr>
<td>Vulnerability to attacks</td>
<td>Until the connection is established, anybody who intercepts this link can connect to it, so it has to be verified with the original contact that the connection succeeded.</td>
<td>These addresses are vulnerable to connection request spam. Unlike other platforms, you can delete or change the address, without losing any contacts (see above).</td>
</tr>
<tr>
<td>Passive attacks on connection links</td>
<td colspan="2">Both types of links are not vulnerable if simply observed &mdash; they only contain public keys. So they can be safely shared via insecure or public channels, as long as you can confirm that you connected to the intended person.</td>
</tr>
<tr>
<td>Active attacks on connection links</td>
<td colspan="2">If the link is substituted via the attack on the channel used to share it, the connection security can be compromised, and the original messages monitored (man-in-the-middle attack). If it is a real risk then security code should be verified to mitigate it - doing so proves<sup>**</sup> that the link and keys were not substituted, and that the end-to-end encryption is secure.</td>
</tr>
</table>
<sup>*</sup> Adding optional verified identities that we plan in the future will change it &mdash; the address owner will have an option to request identity verification before accepting the connection.
<sup>**</sup> Connection security code is the cryptographic hash (SHA256) of combined public keys of both sides &mdash; there are 2<sup>256</sup> possible security codes (1 with 77 zeros about [1000 times smaller](https://www.wolframalpha.com/input?i=2%5E256) than the estimated number of atoms in the visible universe).
## Conversation preferences
Tap on one of your conversations to open conversation preferences.
+16 -9
View File
@@ -1,5 +1,5 @@
name: simplex-chat
version: 5.4.0.6
version: 5.4.0.7
#synopsis:
#description:
homepage: https://github.com/simplex-chat/simplex-chat#readme
@@ -19,11 +19,10 @@ dependencies:
- attoparsec == 0.14.*
- base >= 4.7 && < 5
- base64-bytestring >= 1.0 && < 1.3
- bytestring == 0.10.*
- composition == 1.0.*
- constraints >= 0.12 && < 0.14
- containers == 0.6.*
- cryptonite >= 0.27 && < 0.30
- cryptonite == 0.30.*
- data-default >= 0.7 && < 0.8
- directory == 1.3.*
- direct-sqlcipher == 2.3.*
@@ -32,10 +31,10 @@ dependencies:
- filepath == 1.4.*
- http-types == 0.12.*
- http2 >= 4.2.2 && < 4.3
- memory >= 0.15 && < 0.19
- mtl >= 2.3.1 && < 3
- memory == 0.18.*
- mtl >= 2.3.1 && < 3.0
- network >= 3.1.2.7 && < 3.2
- network-transport >= 0.5.6 && < 0.6
- network-transport == 0.5.6
- optparse-applicative >= 0.15 && < 0.17
- process == 1.6.*
- random >= 1.1 && < 1.3
@@ -45,14 +44,12 @@ dependencies:
- socks == 0.6.*
- sqlcipher-simple == 0.4.*
- stm == 2.5.*
- template-haskell >= 2.16 && < 2.21
- terminal == 0.2.*
- text >= 2.0 && < 3
- time == 1.9.*
- tls >= 1.6.0 && < 1.7
- unliftio == 0.2.*
- unliftio-core == 0.2.*
- zip >= 1.7 && < 2.1
- zip == 2.0.*
flags:
swift:
@@ -64,6 +61,16 @@ when:
- condition: flag(swift)
cpp-options:
- -DswiftJSON
- condition: impl(ghc >= 9.6.2)
dependencies:
- bytestring == 0.11.*
- template-haskell == 2.20.*
- text >= 2.0.1 && < 2.2
- condition: impl(ghc < 9.6.2)
dependencies:
- bytestring == 0.10.*
- template-haskell == 2.16.*
- text >= 1.2.3.0 && < 1.3
library:
source-dirs: src
+4 -3
View File
@@ -1,10 +1,11 @@
{
"https://github.com/simplex-chat/simplexmq.git"."117168ccce93ef5ac478c02e3f02a018fa8a2200" = "1091c4b8qjp9v29z862b7clda90w3kb4v2aqp5b2jh6yprlga98w";
"https://github.com/simplex-chat/simplexmq.git"."a860936072172e261480fa6bdd95203976e366b2" = "16rwnh5zzphmw8d8ypvps6xjvzbmf5ljr6zzy15gz2g0jyh7hd91";
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
"https://github.com/kazu-yamamoto/http2.git"."f5525b755ff2418e6e6ecc69e877363b0d0bcaeb" = "0fyx0047gvhm99ilp212mmz37j84cwrfnpmssib5dw363fyb88b6";
"https://github.com/simplex-chat/direct-sqlcipher.git"."34309410eb2069b029b8fc1872deb1e0db123294" = "0kwkmhyfsn2lixdlgl15smgr1h5gjk7fky6abzh8rng2h5ymnffd";
"https://github.com/simplex-chat/sqlcipher-simple.git"."5e154a2aeccc33ead6c243ec07195ab673137221" = "1d1gc5wax4vqg0801ajsmx1sbwvd9y7p7b8mmskvqsmpbwgbh0m0";
"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/aeson.git"."aab7b5a14d6c5ea64c64dcaee418de1bb00dcc2b" = "0jz7kda8gai893vyvj96fy962ncv8dcsx71fbddyy8zrvc88jfrr";
"https://github.com/simplex-chat/haskell-terminal.git"."f708b00009b54890172068f168bf98508ffcd495" = "0zmq7lmfsk8m340g47g5963yba7i88n4afa6z93sg9px5jv1mijj";
"https://github.com/simplex-chat/android-support.git"."9aa09f148089d6752ce563b14c2df1895718d806" = "0pbf2pf13v2kjzi397nr13f1h3jv0imvsq8rpiyy2qyx5vd50pqn";
"https://github.com/simplex-chat/zip.git"."bd421c6b19cc4c465cd7af1f6f26169fb8ee1ebc" = "1csqfjhvc8wb5h4kxxndmb6iw7b4ib9ff2n81hrizsmnf45a6gg0";
}
+1 -1
View File
@@ -11,7 +11,7 @@ BEGIN {
/tag/ && isGit == true { ref=$2 }
isGit == true && loc != "" && ref != "" {
cmd = "nix-prefetch-git --quiet "loc" "ref" | jq -r .sha256"
cmd = "nix-prefetch-git --fetch-submodules --quiet "loc" "ref" | jq -r .sha256"
cmd | getline sha256
close(cmd)
print " \""loc"\".\""ref"\" = \""sha256"\";";
+106 -57
View File
@@ -5,7 +5,7 @@ cabal-version: 1.12
-- see: https://github.com/sol/hpack
name: simplex-chat
version: 5.4.0.6
version: 5.4.0.7
category: Web, System, Services, Cryptography
homepage: https://github.com/simplex-chat/simplex-chat#readme
author: simplex.chat
@@ -171,11 +171,10 @@ library
, attoparsec ==0.14.*
, base >=4.7 && <5
, base64-bytestring >=1.0 && <1.3
, bytestring ==0.10.*
, composition ==1.0.*
, constraints >=0.12 && <0.14
, containers ==0.6.*
, cryptonite >=0.27 && <0.30
, cryptonite ==0.30.*
, data-default ==0.7.*
, direct-sqlcipher ==2.3.*
, directory ==1.3.*
@@ -184,10 +183,10 @@ library
, filepath ==1.4.*
, http-types ==0.12.*
, http2 >=4.2.2 && <4.3
, memory >=0.15 && <0.19
, mtl >=2.3.1 && <3
, memory ==0.18.*
, mtl >=2.3.1 && <3.0
, network >=3.1.2.7 && <3.2
, network-transport >=0.5.6 && <0.6
, network-transport ==0.5.6
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
, random >=1.1 && <1.3
@@ -197,17 +196,25 @@ library
, socks ==0.6.*
, sqlcipher-simple ==0.4.*
, stm ==2.5.*
, template-haskell >=2.16 && <2.21
, terminal ==0.2.*
, text >=2.0 && <3
, time ==1.9.*
, tls >=1.6.0 && <1.7
, unliftio ==0.2.*
, unliftio-core ==0.2.*
, zip >=1.7 && <2.1
, zip ==2.0.*
default-language: Haskell2010
if flag(swift)
cpp-options: -DswiftJSON
if impl(ghc >= 9.6.2)
build-depends:
bytestring ==0.11.*
, template-haskell ==2.20.*
, text >=2.0.1 && <2.2
if impl(ghc < 9.6.2)
build-depends:
bytestring ==0.10.*
, template-haskell ==2.16.*
, text >=1.2.3.0 && <1.3
executable simplex-bot
main-is: Main.hs
@@ -223,11 +230,10 @@ executable simplex-bot
, attoparsec ==0.14.*
, base >=4.7 && <5
, base64-bytestring >=1.0 && <1.3
, bytestring ==0.10.*
, composition ==1.0.*
, constraints >=0.12 && <0.14
, containers ==0.6.*
, cryptonite >=0.27 && <0.30
, cryptonite ==0.30.*
, data-default ==0.7.*
, direct-sqlcipher ==2.3.*
, directory ==1.3.*
@@ -236,10 +242,10 @@ executable simplex-bot
, filepath ==1.4.*
, http-types ==0.12.*
, http2 >=4.2.2 && <4.3
, memory >=0.15 && <0.19
, mtl >=2.3.1 && <3
, memory ==0.18.*
, mtl >=2.3.1 && <3.0
, network >=3.1.2.7 && <3.2
, network-transport >=0.5.6 && <0.6
, network-transport ==0.5.6
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
, random >=1.1 && <1.3
@@ -250,17 +256,25 @@ executable simplex-bot
, socks ==0.6.*
, sqlcipher-simple ==0.4.*
, stm ==2.5.*
, template-haskell >=2.16 && <2.21
, terminal ==0.2.*
, text >=2.0 && <3
, time ==1.9.*
, tls >=1.6.0 && <1.7
, unliftio ==0.2.*
, unliftio-core ==0.2.*
, zip >=1.7 && <2.1
, zip ==2.0.*
default-language: Haskell2010
if flag(swift)
cpp-options: -DswiftJSON
if impl(ghc >= 9.6.2)
build-depends:
bytestring ==0.11.*
, template-haskell ==2.20.*
, text >=2.0.1 && <2.2
if impl(ghc < 9.6.2)
build-depends:
bytestring ==0.10.*
, template-haskell ==2.16.*
, text >=1.2.3.0 && <1.3
executable simplex-bot-advanced
main-is: Main.hs
@@ -276,11 +290,10 @@ executable simplex-bot-advanced
, attoparsec ==0.14.*
, base >=4.7 && <5
, base64-bytestring >=1.0 && <1.3
, bytestring ==0.10.*
, composition ==1.0.*
, constraints >=0.12 && <0.14
, containers ==0.6.*
, cryptonite >=0.27 && <0.30
, cryptonite ==0.30.*
, data-default ==0.7.*
, direct-sqlcipher ==2.3.*
, directory ==1.3.*
@@ -289,10 +302,10 @@ executable simplex-bot-advanced
, filepath ==1.4.*
, http-types ==0.12.*
, http2 >=4.2.2 && <4.3
, memory >=0.15 && <0.19
, mtl >=2.3.1 && <3
, memory ==0.18.*
, mtl >=2.3.1 && <3.0
, network >=3.1.2.7 && <3.2
, network-transport >=0.5.6 && <0.6
, network-transport ==0.5.6
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
, random >=1.1 && <1.3
@@ -303,17 +316,25 @@ executable simplex-bot-advanced
, socks ==0.6.*
, sqlcipher-simple ==0.4.*
, stm ==2.5.*
, template-haskell >=2.16 && <2.21
, terminal ==0.2.*
, text >=2.0 && <3
, time ==1.9.*
, tls >=1.6.0 && <1.7
, unliftio ==0.2.*
, unliftio-core ==0.2.*
, zip >=1.7 && <2.1
, zip ==2.0.*
default-language: Haskell2010
if flag(swift)
cpp-options: -DswiftJSON
if impl(ghc >= 9.6.2)
build-depends:
bytestring ==0.11.*
, template-haskell ==2.20.*
, text >=2.0.1 && <2.2
if impl(ghc < 9.6.2)
build-depends:
bytestring ==0.10.*
, template-haskell ==2.16.*
, text >=1.2.3.0 && <1.3
executable simplex-broadcast-bot
main-is: ../Main.hs
@@ -331,11 +352,10 @@ executable simplex-broadcast-bot
, attoparsec ==0.14.*
, base >=4.7 && <5
, base64-bytestring >=1.0 && <1.3
, bytestring ==0.10.*
, composition ==1.0.*
, constraints >=0.12 && <0.14
, containers ==0.6.*
, cryptonite >=0.27 && <0.30
, cryptonite ==0.30.*
, data-default ==0.7.*
, direct-sqlcipher ==2.3.*
, directory ==1.3.*
@@ -344,10 +364,10 @@ executable simplex-broadcast-bot
, filepath ==1.4.*
, http-types ==0.12.*
, http2 >=4.2.2 && <4.3
, memory >=0.15 && <0.19
, mtl >=2.3.1 && <3
, memory ==0.18.*
, mtl >=2.3.1 && <3.0
, network >=3.1.2.7 && <3.2
, network-transport >=0.5.6 && <0.6
, network-transport ==0.5.6
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
, random >=1.1 && <1.3
@@ -358,17 +378,25 @@ executable simplex-broadcast-bot
, socks ==0.6.*
, sqlcipher-simple ==0.4.*
, stm ==2.5.*
, template-haskell >=2.16 && <2.21
, terminal ==0.2.*
, text >=2.0 && <3
, time ==1.9.*
, tls >=1.6.0 && <1.7
, unliftio ==0.2.*
, unliftio-core ==0.2.*
, zip >=1.7 && <2.1
, zip ==2.0.*
default-language: Haskell2010
if flag(swift)
cpp-options: -DswiftJSON
if impl(ghc >= 9.6.2)
build-depends:
bytestring ==0.11.*
, template-haskell ==2.20.*
, text >=2.0.1 && <2.2
if impl(ghc < 9.6.2)
build-depends:
bytestring ==0.10.*
, template-haskell ==2.16.*
, text >=1.2.3.0 && <1.3
executable simplex-chat
main-is: Main.hs
@@ -385,11 +413,10 @@ executable simplex-chat
, attoparsec ==0.14.*
, base >=4.7 && <5
, base64-bytestring >=1.0 && <1.3
, bytestring ==0.10.*
, composition ==1.0.*
, constraints >=0.12 && <0.14
, containers ==0.6.*
, cryptonite >=0.27 && <0.30
, cryptonite ==0.30.*
, data-default ==0.7.*
, direct-sqlcipher ==2.3.*
, directory ==1.3.*
@@ -398,10 +425,10 @@ executable simplex-chat
, filepath ==1.4.*
, http-types ==0.12.*
, http2 >=4.2.2 && <4.3
, memory >=0.15 && <0.19
, mtl >=2.3.1 && <3
, memory ==0.18.*
, mtl >=2.3.1 && <3.0
, network ==3.1.*
, network-transport >=0.5.6 && <0.6
, network-transport ==0.5.6
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
, random >=1.1 && <1.3
@@ -412,18 +439,26 @@ executable simplex-chat
, socks ==0.6.*
, sqlcipher-simple ==0.4.*
, stm ==2.5.*
, template-haskell >=2.16 && <2.21
, terminal ==0.2.*
, text >=2.0 && <3
, time ==1.9.*
, tls >=1.6.0 && <1.7
, unliftio ==0.2.*
, unliftio-core ==0.2.*
, websockets ==0.12.*
, zip >=1.7 && <2.1
, zip ==2.0.*
default-language: Haskell2010
if flag(swift)
cpp-options: -DswiftJSON
if impl(ghc >= 9.6.2)
build-depends:
bytestring ==0.11.*
, template-haskell ==2.20.*
, text >=2.0.1 && <2.2
if impl(ghc < 9.6.2)
build-depends:
bytestring ==0.10.*
, template-haskell ==2.16.*
, text >=1.2.3.0 && <1.3
executable simplex-directory-service
main-is: ../Main.hs
@@ -443,11 +478,10 @@ executable simplex-directory-service
, attoparsec ==0.14.*
, base >=4.7 && <5
, base64-bytestring >=1.0 && <1.3
, bytestring ==0.10.*
, composition ==1.0.*
, constraints >=0.12 && <0.14
, containers ==0.6.*
, cryptonite >=0.27 && <0.30
, cryptonite ==0.30.*
, data-default ==0.7.*
, direct-sqlcipher ==2.3.*
, directory ==1.3.*
@@ -456,10 +490,10 @@ executable simplex-directory-service
, filepath ==1.4.*
, http-types ==0.12.*
, http2 >=4.2.2 && <4.3
, memory >=0.15 && <0.19
, mtl >=2.3.1 && <3
, memory ==0.18.*
, mtl >=2.3.1 && <3.0
, network >=3.1.2.7 && <3.2
, network-transport >=0.5.6 && <0.6
, network-transport ==0.5.6
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
, random >=1.1 && <1.3
@@ -470,17 +504,25 @@ executable simplex-directory-service
, socks ==0.6.*
, sqlcipher-simple ==0.4.*
, stm ==2.5.*
, template-haskell >=2.16 && <2.21
, terminal ==0.2.*
, text >=2.0 && <3
, time ==1.9.*
, tls >=1.6.0 && <1.7
, unliftio ==0.2.*
, unliftio-core ==0.2.*
, zip >=1.7 && <2.1
, zip ==2.0.*
default-language: Haskell2010
if flag(swift)
cpp-options: -DswiftJSON
if impl(ghc >= 9.6.2)
build-depends:
bytestring ==0.11.*
, template-haskell ==2.20.*
, text >=2.0.1 && <2.2
if impl(ghc < 9.6.2)
build-depends:
bytestring ==0.10.*
, template-haskell ==2.16.*
, text >=1.2.3.0 && <1.3
test-suite simplex-chat-test
type: exitcode-stdio-1.0
@@ -524,11 +566,10 @@ test-suite simplex-chat-test
, attoparsec ==0.14.*
, base >=4.7 && <5
, base64-bytestring >=1.0 && <1.3
, bytestring ==0.10.*
, composition ==1.0.*
, constraints >=0.12 && <0.14
, containers ==0.6.*
, cryptonite >=0.27 && <0.30
, cryptonite ==0.30.*
, data-default ==0.7.*
, deepseq ==1.4.*
, direct-sqlcipher ==2.3.*
@@ -540,10 +581,10 @@ test-suite simplex-chat-test
, hspec ==2.7.*
, http-types ==0.12.*
, http2 >=4.2.2 && <4.3
, memory >=0.15 && <0.19
, mtl >=2.3.1 && <3
, memory ==0.18.*
, mtl >=2.3.1 && <3.0
, network ==3.1.*
, network-transport >=0.5.6 && <0.6
, network-transport ==0.5.6
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
, random >=1.1 && <1.3
@@ -555,14 +596,22 @@ test-suite simplex-chat-test
, socks ==0.6.*
, sqlcipher-simple ==0.4.*
, stm ==2.5.*
, template-haskell >=2.16 && <2.21
, terminal ==0.2.*
, text >=2.0 && <3
, time ==1.9.*
, tls >=1.6.0 && <1.7
, unliftio ==0.2.*
, unliftio-core ==0.2.*
, zip >=1.7 && <2.1
, zip ==2.0.*
default-language: Haskell2010
if flag(swift)
cpp-options: -DswiftJSON
if impl(ghc >= 9.6.2)
build-depends:
bytestring ==0.11.*
, template-haskell ==2.20.*
, text >=2.0.1 && <2.2
if impl(ghc < 9.6.2)
build-depends:
bytestring ==0.10.*
, template-haskell ==2.16.*
, text >=1.2.3.0 && <1.3
+1
View File
@@ -18,6 +18,7 @@ module Simplex.Chat where
import Control.Applicative (optional, (<|>))
import Control.Concurrent.STM (retry)
import Control.Logger.Simple
import Control.Monad
import Control.Monad.Except
import Control.Monad.IO.Unlift
import Control.Monad.Reader
+1
View File
@@ -14,6 +14,7 @@ module Simplex.Chat.Archive
where
import qualified Codec.Archive.Zip as Z
import Control.Monad
import Control.Monad.Except
import Control.Monad.Reader
import Data.Functor (($>))
+1
View File
@@ -8,6 +8,7 @@ module Simplex.Chat.Bot where
import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Monad
import Control.Monad.Reader
import qualified Data.ByteString.Char8 as B
import qualified Data.Text as T
+1
View File
@@ -189,6 +189,7 @@ mobileChatOpts dbFilePrefix dbKey =
allowInstantFiles = True,
autoAcceptFileSize = 0,
muteNotifications = True,
markRead = False,
maintenance = True
}
+1
View File
@@ -16,6 +16,7 @@ module Simplex.Chat.Mobile.File
)
where
import Control.Monad
import Control.Monad.Except
import Control.Monad.IO.Class
import qualified Data.Aeson as J
+2
View File
@@ -8,7 +8,9 @@ module Simplex.Chat.Mobile.WebRTC
reservedSize,
) where
import Control.Monad
import Control.Monad.Except
import Control.Monad.IO.Class
import qualified Crypto.Cipher.Types as AES
import Data.Bifunctor (bimap)
import qualified Data.ByteArray as BA
+8
View File
@@ -42,6 +42,7 @@ data ChatOpts = ChatOpts
allowInstantFiles :: Bool,
autoAcceptFileSize :: Integer,
muteNotifications :: Bool,
markRead :: Bool,
maintenance :: Bool
}
@@ -268,6 +269,12 @@ chatOptsP appDir defaultDbFileName = do
( long "mute"
<> help "Mute notifications"
)
markRead <-
switch
( long "mark-read"
<> short 'r'
<> help "Mark shown messages as read"
)
maintenance <-
switch
( long "maintenance"
@@ -286,6 +293,7 @@ chatOptsP appDir defaultDbFileName = do
allowInstantFiles,
autoAcceptFileSize,
muteNotifications,
markRead,
maintenance
}
+1
View File
@@ -16,6 +16,7 @@ module Simplex.Chat.Store.Connections
where
import Control.Applicative ((<|>))
import Control.Monad
import Control.Monad.Except
import Data.Int (Int64)
import Data.Maybe (catMaybes, fromMaybe)
+2
View File
@@ -66,7 +66,9 @@ module Simplex.Chat.Store.Direct
)
where
import Control.Monad
import Control.Monad.Except
import Control.Monad.IO.Class
import Data.Either (rights)
import Data.Functor (($>))
import Data.Int (Int64)
+2
View File
@@ -77,7 +77,9 @@ module Simplex.Chat.Store.Files
where
import Control.Applicative ((<|>))
import Control.Monad
import Control.Monad.Except
import Control.Monad.IO.Class
import Data.Either (rights)
import Data.Int (Int64)
import Data.Maybe (fromMaybe, isJust, listToMaybe)
+2
View File
@@ -113,7 +113,9 @@ module Simplex.Chat.Store.Groups
)
where
import Control.Monad
import Control.Monad.Except
import Control.Monad.IO.Class
import Crypto.Random (ChaChaDRG)
import Data.Either (rights)
import Data.Int (Int64)
+2
View File
@@ -101,7 +101,9 @@ module Simplex.Chat.Store.Messages
)
where
import Control.Monad
import Control.Monad.Except
import Control.Monad.IO.Class
import Crypto.Random (ChaChaDRG)
import Data.Bifunctor (first)
import Data.ByteString.Char8 (ByteString)
+1
View File
@@ -59,6 +59,7 @@ module Simplex.Chat.Store.Profiles
)
where
import Control.Monad
import Control.Monad.Except
import Control.Monad.IO.Class
import qualified Data.Aeson.TH as J
+2
View File
@@ -12,7 +12,9 @@ module Simplex.Chat.Store.Shared where
import Control.Exception (Exception)
import qualified Control.Exception as E
import Control.Monad
import Control.Monad.Except
import Control.Monad.IO.Class
import Crypto.Random (ChaChaDRG, randomBytesGenerate)
import qualified Data.Aeson.TH as J
import qualified Data.ByteString.Base64 as B64
+4 -4
View File
@@ -5,7 +5,7 @@
module Simplex.Chat.Terminal where
import Control.Exception (handle, throwIO)
import Control.Monad.Except
import Control.Monad
import qualified Data.List.NonEmpty as L
import Database.SQLite.Simple (SQLError (..))
import qualified Database.SQLite.Simple as DB
@@ -44,7 +44,7 @@ simplexChatTerminal cfg opts t =
handle checkDBKeyError . simplexChatCore cfg opts $ \u cc -> do
ct <- newChatTerminal t opts
when (firstTime cc) . printToTerminal ct $ chatWelcome u
runChatTerminal ct cc
runChatTerminal ct cc opts
checkDBKeyError :: SQLError -> IO ()
checkDBKeyError e = case sqlError e of
@@ -53,5 +53,5 @@ checkDBKeyError e = case sqlError e of
exitFailure
_ -> throwIO e
runChatTerminal :: ChatTerminal -> ChatController -> IO ()
runChatTerminal ct cc = raceAny_ [runTerminalInput ct cc, runTerminalOutput ct cc, runInputLoop ct cc]
runChatTerminal :: ChatTerminal -> ChatController -> ChatOpts -> IO ()
runChatTerminal ct cc opts = raceAny_ [runTerminalInput ct cc, runTerminalOutput ct cc opts, runInputLoop ct cc]
+1
View File
@@ -12,6 +12,7 @@ module Simplex.Chat.Terminal.Input where
import Control.Applicative (optional, (<|>))
import Control.Concurrent (forkFinally, forkIO, killThread, mkWeakThreadId, threadDelay)
import Control.Monad
import Control.Monad.Except
import Control.Monad.Reader
import qualified Data.Attoparsec.ByteString.Char8 as A
+4 -4
View File
@@ -142,13 +142,13 @@ withTermLock ChatTerminal {termLock} action = do
action
atomically $ putTMVar termLock ()
runTerminalOutput :: ChatTerminal -> ChatController -> IO ()
runTerminalOutput ct cc@ChatController {outputQ, showLiveItems, logFilePath} = do
runTerminalOutput :: ChatTerminal -> ChatController -> ChatOpts -> IO ()
runTerminalOutput ct cc@ChatController {outputQ, showLiveItems, logFilePath} ChatOpts {markRead} = do
forever $ do
(_, outputRH, r) <- atomically $ readTBQueue outputQ
case r of
CRNewChatItem u ci -> markChatItemRead u ci
CRChatItemUpdated u ci -> markChatItemRead u ci
CRNewChatItem u ci -> when markRead $ markChatItemRead u ci
CRChatItemUpdated u ci -> when markRead $ markChatItemRead u ci
CRRemoteHostConnected {remoteHost = RemoteHostInfo {remoteHostId}} -> getRemoteUser remoteHostId
CRRemoteHostStopped {remoteHostId_} -> mapM_ removeRemoteUser remoteHostId_
_ -> pure ()
+1
View File
@@ -2,6 +2,7 @@ module Simplex.Chat.Util (week, encryptFile, chunkSize) where
import Control.Monad
import Control.Monad.Except
import Control.Monad.IO.Class
import qualified Data.ByteString.Lazy as LB
import Data.Time (NominalDiffTime)
import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..))
-96
View File
@@ -1,96 +0,0 @@
# This file was automatically generated by 'stack init'
#
# Some commonly used options have been documented as comments in this file.
# For advanced use and comprehensive documentation of the format, please see:
# https://docs.haskellstack.org/en/stable/yaml_configuration/
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
# A snapshot resolver dictates the compiler version and the set of packages
# to be used for project dependencies. For example:
#
# resolver: lts-3.5
# resolver: nightly-2015-09-21
# resolver: ghc-7.10.2
#
# The location of a snapshot can be provided as a file or url. Stack assumes
# a snapshot provided as a file might change, whereas a url resource does not.
#
# resolver: ./custom-snapshot.yaml
# resolver: https://example.com/snapshots/2018-01-01.yaml
resolver: lts-18.21
# User packages to be built.
# Various formats can be used as shown in the example below.
#
# packages:
# - some-directory
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
# subdirs:
# - auto-update
# - wai
packages:
- .
# Dependency packages to be pulled from upstream that are not in the resolver.
# These entries can reference officially published versions as well as
# forks / in-progress versions pinned to a git hash. For example:
#
extra-deps:
- cryptostore-0.2.1.0@sha256:9896e2984f36a1c8790f057fd5ce3da4cbcaf8aa73eb2d9277916886978c5b19,3881
- network-3.1.2.7@sha256:e3d78b13db9512aeb106e44a334ab42b7aa48d26c097299084084cb8be5c5568,4888
- simple-logger-0.1.0@sha256:be8ede4bd251a9cac776533bae7fb643369ebd826eb948a9a18df1a8dd252ff8,1079
- tls-1.6.0@sha256:7ae39373fd2de27fb80e90f76d22aeeb9a074a0ddd120cbd02c9c52f516a9e55,6987
# below hackage dependencies are to update Aeson to 2.0.3
- OneTuple-0.3.1@sha256:a848c096c9d29e82ffdd30a9998aa2931cbccb3a1bc137539d80f6174d31603e,2262
- attoparsec-0.14.4@sha256:79584bdada8b730cb5138fca8c35c76fbef75fc1d1e01e6b1d815a5ee9843191,5810
- hashable-1.4.0.2@sha256:0cddd0229d1aac305ea0404409c0bbfab81f075817bd74b8b2929eff58333e55,5005
- semialign-1.2.0.1@sha256:0e179b4d3a8eff79001d374d6c91917c6221696b9620f0a4d86852fc6a9b9501,2836
- text-short-0.1.5@sha256:962c6228555debdc46f758d0317dea16e5240d01419b42966674b08a5c3d8fa6,3498
- time-compat-1.9.6.1@sha256:42d8f2e08e965e1718917d54ad69e1d06bd4b87d66c41dc7410f59313dba4ed1,5033
# - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561
# - ../simplexmq
- github: simplex-chat/simplexmq
commit: 117168ccce93ef5ac478c02e3f02a018fa8a2200
- github: kazu-yamamoto/http2
commit: f5525b755ff2418e6e6ecc69e877363b0d0bcaeb
# - ../direct-sqlcipher
- github: simplex-chat/direct-sqlcipher
commit: 34309410eb2069b029b8fc1872deb1e0db123294
# - ../sqlcipher-simple
- github: simplex-chat/sqlcipher-simple
commit: 5e154a2aeccc33ead6c243ec07195ab673137221
# - terminal-0.2.0.0@sha256:de6770ecaae3197c66ac1f0db5a80cf5a5b1d3b64a66a05b50f442de5ad39570,2977
- github: simplex-chat/aeson
commit: aab7b5a14d6c5ea64c64dcaee418de1bb00dcc2b
- github: simplex-chat/haskell-terminal
commit: f708b00009b54890172068f168bf98508ffcd495
- github: simplex-chat/android-support
commit: 9aa09f148089d6752ce563b14c2df1895718d806
#
# extra-deps: []
# Override default flag values for local packages and extra-deps
flags:
zip:
disable-bzip2: true
disable-zstd: true
direct-sqlcipher:
openssl: true
# Extra package databases containing global packages
# extra-package-dbs: []
# Control whether we use the GHC we find on the path
# system-ghc: true
#
# Require a specific version of stack, using version ranges
# require-stack-version: -any # Default
# require-stack-version: ">=2.1"
#
# Override the architecture used by stack, especially useful on Windows
# arch: i386
# arch: x86_64
#
# Extra directories used by stack for building
# extra-lib-dirs: [/path/to/dir]
#
# Allow a newer minor version of GHC than the snapshot specifies
# compiler-check: newer-minor
+3 -1
View File
@@ -13,6 +13,7 @@ import Control.Concurrent (forkIOWithUnmask, killThread, threadDelay)
import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Exception (bracket, bracket_)
import Control.Monad
import Control.Monad.Except
import Data.Functor (($>))
import Data.List (dropWhileEnd, find)
@@ -81,6 +82,7 @@ testOpts =
allowInstantFiles = True,
autoAcceptFileSize = 0,
muteNotifications = True,
markRead = True,
maintenance = False
}
@@ -173,7 +175,7 @@ startTestChat_ db cfg opts user = do
t <- withVirtualTerminal termSettings pure
ct <- newChatTerminal t opts
cc <- newChatController db (Just user) cfg opts
chatAsync <- async . runSimplexChat opts user cc . const $ runChatTerminal ct
chatAsync <- async . runSimplexChat opts user cc $ \_u cc' -> runChatTerminal ct cc' opts
atomically . unless (maintenance opts) $ readTVar (agentAsync cc) >>= \a -> when (isNothing a) retry
termQ <- newTQueueIO
termAsync <- async $ readTerminalOutput t termQ
+3 -3
View File
@@ -162,13 +162,13 @@ storedBindingsTest = testChat2 aliceProfile aliceDesktopProfile $ \mobile deskto
desktop ##> "/list remote hosts"
desktop <## "Remote hosts:"
desktop <## "1. Mobile (connected) [lo 127.0.0.1:52230]"
desktop <##. "1. Mobile (connected) ["
stopDesktop mobile desktop
desktop ##> "/list remote hosts"
desktop <## "Remote hosts:"
desktop <## "1. Mobile [lo 127.0.0.1:52230]"
desktop <##. "1. Mobile ["
-- TODO: more parser tests
-- TODO: more parser tests
remoteMessageTest :: HasCallStack => FilePath -> IO ()
remoteMessageTest = testChat3 aliceProfile aliceDesktopProfile bobProfile $ \mobile desktop bob -> do