Compare commits

..

10 Commits

Author SHA1 Message Date
spaced4ndy
420d80ad6c 5.3.1: android 154, ios 174, desktop 11
* 5.3.1

* 5.3.1: ios 174, desktop 11

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-09-25 16:41:53 +01:00
spaced4ndy
343131c64e core: 5.3.1.0 2023-09-25 17:43:03 +04:00
spaced4ndy
9b107fbdeb core: fix invited member contact (do not display invitation context in UI) (#3122) 2023-09-25 16:39:27 +04:00
spaced4ndy
60d13e258e ios, android: show rcv integrity error items based on developer tools default (#3123) 2023-09-25 16:38:48 +04:00
Evgeny Poberezkin
4f42c2b0d8 blog: v5.3 announcement (#3093)
* blog: v5.3 announcement draft

* update

* update post

* add images and previews

* website: add imageWide property

* website: add .float-to-left class

* update

* update images

* update readme

* fix typo

---------

Co-authored-by: M Sarmad Qadeer <MSarmadQadeer@gmail.com>
2023-09-25 09:32:27 +01:00
Stanislav Dmitrenko
48ae1111a6 desktop: fix lib copy (#3120) 2023-09-25 09:30:44 +01:00
Stanislav Dmitrenko
76dbe32cfc desktop: fix JNI (#3119) 2023-09-25 09:30:21 +01:00
Evgeny Poberezkin
120f42cbba readme: update languages 2023-09-23 18:37:03 +01:00
Evgeny Poberezkin
5f46433f40 docs: update translations in readme 2023-09-23 18:33:54 +01:00
Evgeny Poberezkin
7b71078c76 update downloads page 2023-09-23 17:36:44 +01:00
60 changed files with 662 additions and 400 deletions

View File

@@ -78,10 +78,10 @@ jobs:
uses: actions/checkout@v3
- name: Setup Haskell
uses: haskell/actions/setup@v2
uses: haskell-actions/setup@v2
with:
ghc-version: "8.10.7"
cabal-version: "latest"
ghc-version: "9.6.2"
cabal-version: "3.10.1.0"
- name: Cache dependencies
uses: actions/cache@v3
@@ -187,7 +187,7 @@ jobs:
APPLE_SIMPLEX_NOTARIZATION_APPLE_ID: ${{ secrets.APPLE_SIMPLEX_NOTARIZATION_APPLE_ID }}
APPLE_SIMPLEX_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_SIMPLEX_NOTARIZATION_PASSWORD }}
run: |
scripts/build-desktop-mac.sh
scripts/ci/build-desktop-mac.sh
path=$(echo $PWD/apps/multiplatform/release/main/dmg/SimpleX-*.dmg)
echo "package_path=$path" >> $GITHUB_OUTPUT
echo "package_hash=$(echo SHA2-512\(${{ matrix.desktop_asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
@@ -258,15 +258,15 @@ jobs:
# Unix /
# / Windows
# * In powershell multiline commands do not fail if individual commands fail - https://github.community/t/multiline-commands-on-windows-do-not-fail-if-individual-commands-fail/16753
# * 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
# rm -rf dist-newstyle/src/direct-sq* is here because of the bug in cabal's dependency which prevents second build from finishing
- name: Windows build
id: windows_build
if: matrix.os == 'windows-latest'
shell: cmd
shell: bash
run: |
rm -rf dist-newstyle/src/direct-sq*
sed -i "s/, unix /--, unix /" simplex-chat.cabal
cabal build --enable-tests
rm -rf dist-newstyle/src/direct-sq*
path=$(cabal list-bin simplex-chat | tail -n 1)

View File

@@ -119,19 +119,22 @@ Join our translators to help SimpleX grow!
|locale|language |contributor|[Android](https://play.google.com/store/apps/details?id=chat.simplex.app) and [iOS](https://apps.apple.com/us/app/simplex-chat/id1605771084)|[website](https://simplex.chat)|Github docs|
|:----:|:-------:|:---------:|:---------:|:---------:|:---------:|
|🇬🇧 en|English | |✓|✓|✓|✓|
|ar|العربية |[jermanuts](https://github.com/jermanuts)||[![website](https://hosted.weblate.org/widgets/simplex-chat/ar/website/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/website/ar/)||
|🇧🇬 bg|Български |-|[![android app](https://hosted.weblate.org/widgets/simplex-chat/bg/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/bg/)<br>-|||
|ar|العربية |[jermanuts](https://github.com/jermanuts)|[![android app](https://hosted.weblate.org/widgets/simplex-chat/ar/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/ar/)<br>-|[![website](https://hosted.weblate.org/widgets/simplex-chat/ar/website/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/website/ar/)||
|🇧🇬 bg|Български | |[![android app](https://hosted.weblate.org/widgets/simplex-chat/bg/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/bg/)<br>[![ios app](https://hosted.weblate.org/widget/simplex-chat/ios/bg/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/ios/bg/)|||
|🇨🇿 cs|Čeština |[zen0bit](https://github.com/zen0bit)|[![android app](https://hosted.weblate.org/widgets/simplex-chat/cs/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/cs/)<br>[![ios app](https://hosted.weblate.org/widgets/simplex-chat/cs/ios/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/ios/cs/)|[![website](https://hosted.weblate.org/widgets/simplex-chat/cs/website/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/website/cs/)|[](https://github.com/simplex-chat/simplex-chat/tree/master/docs/lang/cs)|
|🇩🇪 de|Deutsch |[mlanp](https://github.com/mlanp)|[![android app](https://hosted.weblate.org/widgets/simplex-chat/de/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/de/)<br>[![ios app](https://hosted.weblate.org/widgets/simplex-chat/de/ios/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/ios/de/)|[![website](https://hosted.weblate.org/widgets/simplex-chat/de/website/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/website/de/)||
|🇪🇸 es|Español |[Mateyhv](https://github.com/Mateyhv)|[![android app](https://hosted.weblate.org/widgets/simplex-chat/es/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/es/)<br>[![ios app](https://hosted.weblate.org/widgets/simplex-chat/es/ios/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/ios/es/)|[![website](https://hosted.weblate.org/widgets/simplex-chat/es/website/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/website/es/)||
|🇫🇮 fi|Suomi | |[![android app](https://hosted.weblate.org/widgets/simplex-chat/fi/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/fi/)<br>[![ios app](https://hosted.weblate.org/widgets/simplex-chat/fi/ios/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/ios/fi/)|[![website](https://hosted.weblate.org/widgets/simplex-chat/fi/website/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/website/fi/)||
|🇫🇷 fr|Français |[ishi_sama](https://github.com/ishi-sama)|[![android app](https://hosted.weblate.org/widgets/simplex-chat/fr/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/fr/)<br>[![ios app](https://hosted.weblate.org/widgets/simplex-chat/fr/ios/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/ios/fr/)|[![website](https://hosted.weblate.org/widgets/simplex-chat/fr/website/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/website/fr/)|[](https://github.com/simplex-chat/simplex-chat/tree/master/docs/lang/fr)|
|🇮🇱 he|עִברִית | |[![android app](https://hosted.weblate.org/widgets/simplex-chat/he/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/he/)<br>-|||
|🇮🇹 it|Italiano |[unbranched](https://github.com/unbranched)|[![android app](https://hosted.weblate.org/widgets/simplex-chat/it/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/it/)<br>[![ios app](https://hosted.weblate.org/widgets/simplex-chat/it/ios/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/ios/it/)|[![website](https://hosted.weblate.org/widgets/simplex-chat/it/website/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/website/it/)||
|🇯🇵 ja|Japanese ||[![android app](https://hosted.weblate.org/widgets/simplex-chat/ja/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/ja/)<br>[![ios app](https://hosted.weblate.org/widgets/simplex-chat/ja/ios/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/ios/ja/)|||
|🇯🇵 ja|日本語 | |[![android app](https://hosted.weblate.org/widgets/simplex-chat/ja/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/ja/)<br>[![ios app](https://hosted.weblate.org/widgets/simplex-chat/ja/ios/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/ios/ja/)|[![website](https://hosted.weblate.org/widgets/simplex-chat/ja/website/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/website/ja/)||
|🇳🇱 nl|Nederlands|[mika-nl](https://github.com/mika-nl)|[![android app](https://hosted.weblate.org/widgets/simplex-chat/nl/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/nl/)<br>[![ios app](https://hosted.weblate.org/widgets/simplex-chat/nl/ios/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/ios/nl/)|[![website](https://hosted.weblate.org/widgets/simplex-chat/nl/website/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/website/nl/)||
|🇵🇱 pl|Polski |[BxOxSxS](https://github.com/BxOxSxS)|[![android app](https://hosted.weblate.org/widgets/simplex-chat/pl/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/pl/)<br>[![ios app](https://hosted.weblate.org/widgets/simplex-chat/pl/ios/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/ios/pl/)|||
|🇧🇷 pt-BR|Português||[![android app](https://hosted.weblate.org/widgets/simplex-chat/pt_BR/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/pt_BR/)<br>-|[![website](https://hosted.weblate.org/widgets/simplex-chat/pt_BR/website/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/website/pt_BR/)||
|🇷🇺 ru|Русский ||[![android app](https://hosted.weblate.org/widgets/simplex-chat/ru/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/ru/)<br>[![ios app](https://hosted.weblate.org/widgets/simplex-chat/ru/ios/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/ios/ru/)|||
|🇹🇭 th|ภาษาไทย |[titapa-punpun](https://github.com/titapa-punpun)|[![android app](https://hosted.weblate.org/widgets/simplex-chat/th/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/th/)<br>[![ios app](https://hosted.weblate.org/widgets/simplex-chat/th/ios/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/ios/th/)|||
|🇺🇦 uk|Українська| |[![android app](https://hosted.weblate.org/widgets/simplex-chat/uk/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/uk/)<br>[![ios app](https://hosted.weblate.org/widgets/simplex-chat/uk/ios/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/ios/uk/)|[![website](https://hosted.weblate.org/widgets/simplex-chat/uk/website/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/website/uk/)||
|🇨🇳 zh-CHS|简体中文|[sith-on-mars](https://github.com/sith-on-mars)<br><br>[Float-hu](https://github.com/Float-hu)|[![android app](https://hosted.weblate.org/widgets/simplex-chat/zh_Hans/android/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/)<br>[![ios app](https://hosted.weblate.org/widgets/simplex-chat/zh_Hans/ios/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/ios/zh_Hans/)<br>&nbsp;|<br><br>[![website](https://hosted.weblate.org/widgets/simplex-chat/zh_Hans/website/svg-badge.svg)](https://hosted.weblate.org/projects/simplex-chat/website/zh_Hans/)||
Languages in progress: Arabic, Japanese, Korean, Portuguese and [others](https://hosted.weblate.org/projects/simplex-chat/#languages). We will be adding more languages as some of the already added are completed please suggest new languages, review the [translation guide](./docs/TRANSLATIONS.md) and get in touch with us!
@@ -227,24 +230,18 @@ You can use SimpleX with your own servers and still communicate with people usin
## News and updates
Recent updates:
Recent and important updates:
[July 22, 2023. SimpleX Chat: v5.2 released with message delivery receipts](./blog/20230722-simplex-chat-v5-2-message-delivery-receipts.md).
[Sep 25, 2023. SimpleX Chat v5.3 released: desktop app, local file encryption, improved groups and directory service](./blog/20230925-simplex-chat-v5-3-desktop-app-local-file-encryption-directory-service.md).
[Jul 22, 2023. SimpleX Chat: v5.2 released with message delivery receipts](./blog/20230722-simplex-chat-v5-2-message-delivery-receipts.md).
[May 23, 2023. SimpleX Chat: v5.1 released with message reactions and self-destruct passcode](./blog/20230523-simplex-chat-v5-1-message-reactions-self-destruct-passcode.md).
[Apr 22, 2023. SimpleX Chat: vision and funding, v5.0 released with videos and files up to 1gb](./blog/20230422-simplex-chat-vision-funding-v5-videos-files-passcode.md).
[Mar 28, 2023. v4.6 released - with Android 8+ and ARMv7a support, hidden profiles, community moderation, improved audio/video calls and reduced battery usage](./blog/20230328-simplex-chat-v4-6-hidden-profiles.md).
[Mar 1, 2023. SimpleX File Transfer Protocol send large files efficiently, privately and securely, soon to be integrated into SimpleX Chat apps.](./blog/20230301-simplex-file-transfer-protocol.md).
[Feb 4, 2023. v4.5 released - with multiple user profiles, message draft, transport isolation and Italian interface](./blog/20230204-simplex-chat-v4-5-user-chat-profiles.md).
[Jan 3, 2023. v4.4 released - with disappearing messages, "live" messages, connection security verifications, GIFs and stickers and with French interface language](./blog/20230103-simplex-chat-v4.4-disappearing-messages.md).
[Dec 6, 2022. November reviews and v4.3 released - with instant voice messages, irreversible deletion of sent messages and improved server configuration](./blog/20221206-simplex-chat-v4.3-voice-messages.md).
[Nov 8, 2022. Security audit by Trail of Bits, the new website and v4.2 released](./blog/20221108-simplex-chat-v4.2-security-audit-new-website.md).
[Sep 28, 2022. v4.0: encrypted local chat database and many other changes](./blog/20220928-simplex-chat-v4-encrypted-database.md).
@@ -290,18 +287,20 @@ What is already implemented:
3. [Double ratchet](./docs/GLOSSARY.md#double-ratchet-algorithm) end-to-end encryption in each conversation between two users (or group members). This is the same algorithm that is used in Signal and many other messaging apps; it provides OTR messaging with [forward secrecy](./docs/GLOSSARY.md#forward-secrecy) (each message is encrypted by its own ephemeral key) and [break-in recovery](./docs/GLOSSARY.md#post-compromise-security) (the keys are frequently re-negotiated as part of the message exchange). Two pairs of Curve448 keys are used for the initial [key agreement](./docs/GLOSSARY.md#key-agreement-protocol), initiating party passes these keys via the connection link, accepting side - in the header of the confirmation message.
4. Additional layer of encryption using NaCL cryptobox for the messages delivered from the server to the recipient. This layer avoids having any ciphertext in common between sent and received traffic of the server inside TLS (and there are no identifiers in common as well).
5. Several levels of [content padding](./docs/GLOSSARY.md#message-padding) to frustrate message size attacks.
6. Starting from v2 of SMP protocol (the current version is v4) all message metadata, including the time when the message was received by the server (rounded to a second) is sent to the recipients inside an encrypted envelope, so even if TLS is compromised it cannot be observed.
6. All message metadata, including the time when the message was received by the server (rounded to a second) is sent to the recipients inside an encrypted envelope, so even if TLS is compromised it cannot be observed.
7. Only TLS 1.2/1.3 are allowed for client-server connections, limited to cryptographic algorithms: CHACHA20POLY1305_SHA256, Ed25519/Ed448, Curve25519/Curve448.
8. To protect against replay attacks SimpleX servers require [tlsunique channel binding](https://www.rfc-editor.org/rfc/rfc5929.html) as session ID in each client command signed with per-queue ephemeral key.
9. To protect your IP address all SimpleX Chat clients support accessing messaging servers via Tor - see [v3.1 release announcement](./blog/20220808-simplex-chat-v3.1-chat-groups.md) for more details.
10. Local database encryption with passphrase - your contacts, groups and all sent and received messages are stored encrypted. If you used SimpleX Chat before v4.0 you need to enable the encryption via the app settings.
11. Transport isolation - different TCP connections and Tor circuits are used for traffic of different user profiles, optionally - for different contacts and group member connections.
12. Manual messaging queue rotations to move conversation to another SMP relay.
13. Sending end-to-end encrypted files using [XFTP protocol](https://simplex.chat/blog/20230301-simplex-file-transfer-protocol.html).
14. Local files encryption, except videos (to be added later).
We plan to add:
1. Local files encryption. Currently the images and files you send and receive are stored in the app unencrypted, you can delete them via `Settings / Database passphrase & export`. This is currently in progress.
2. Senders' SMP relays and recipients' XFTP relays to reduce traffic and conceal IP addresses from the relays chosen, and potentially controlled, by another party.
1. Senders' SMP relays and recipients' XFTP relays to reduce traffic and conceal IP addresses from the relays chosen, and potentially controlled, by another party.
2. Post-quantum resistant key exchange in double ratchet protocol.
3. Automatic message queue rotation and redundancy. Currently the queues created between two users are used until the queue is manually changed by the user or contact is deleted. We are planning to add automatic queue rotation to make these identifiers temporary and rotate based on some schedule TBC (e.g., every X messages, or every X hours/days).
4. Message "mixing" - adding latency to message delivery, to protect against traffic correlation by message time.
5. Reproducible builds this is the limitation of the development stack, but we will be investing into solving this problem. Users can still build all applications and services from the source code.
@@ -365,22 +364,26 @@ Please also join [#simplex-devs](https://simplex.chat/contact#/?v=1-2&smp=smp%3A
- ✅ Message editing history
- ✅ Reduced battery and traffic usage in large groups.
- ✅ Message delivery confirmation (with sender opt-out per contact).
- 🏗 Desktop client.
- Desktop client.
- ✅ Encryption of local files stored in the app.
- 🏗 Using mobile profiles from the desktop app.
- Message delivery relay for senders (to conceal IP address from the recipients' servers and to reduce the traffic).
- Post-quantum resistant key exchange in double ratchet protocol.
- Large groups, communities and public channels.
- Privacy & security slider - a simple way to set all settings at once.
- Improve sending videos (including encryption of locally stored videos).
- Improve experience for the new users.
- SMP queue redundancy and rotation (manual is supported).
- Include optional message into connection request sent via contact address.
- Local app files encryption.
- Improved navigation and search in the conversation (expand and scroll to quoted message, scroll to search results, etc.).
- Large groups, communities and public channels.
- Feeds/broadcasts.
- Ephemeral/disappearing/OTR conversations with the existing contacts.
- Privately share your location.
- Web widgets for custom interactivity in the chats.
- Programmable chat automations / rules (automatic replies/forward/deletion/sending, reminders, etc.).
- Supporting the same profile on multiple devices.
- Privacy-preserving identity server for optional DNS-based contact/group addresses to simplify connection and discovery, but not used to deliver messages:
- keep all your contacts and groups even if you lose the domain.
- the server doesn't have information about your contacts and groups.
- Message delivery relay for senders (to conceal IP address from the recipients' servers and to reduce the traffic).
- High capacity multi-node SMP relays.
## Disclaimers

View File

@@ -60,6 +60,7 @@ struct ChatItemContentView<Content: View>: View {
var chatInfo: ChatInfo
var chatItem: ChatItem
var msgContentView: () -> Content
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
var body: some View {
switch chatItem.content {
@@ -69,7 +70,12 @@ struct ChatItemContentView<Content: View>: View {
case .rcvDeleted: deletedItemView()
case let .sndCall(status, duration): callItemView(status, duration)
case let .rcvCall(status, duration): callItemView(status, duration)
case let .rcvIntegrityError(msgError): IntegrityErrorItemView(msgError: msgError, chatItem: chatItem)
case let .rcvIntegrityError(msgError):
if developerTools {
IntegrityErrorItemView(msgError: msgError, chatItem: chatItem)
} else {
ZStack {}
}
case let .rcvDecryptionError(msgDecryptError, msgCount): CIRcvDecryptionError(msgDecryptError: msgDecryptError, msgCount: msgCount, chatItem: chatItem)
case let .rcvGroupInvitation(groupInvitation, memberRole): groupInvitationItemView(groupInvitation, memberRole)
case let .sndGroupInvitation(groupInvitation, memberRole): groupInvitationItemView(groupInvitation, memberRole)

View File

@@ -48,11 +48,11 @@
5C55A921283CCCB700C4E99E /* IncomingCallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C55A920283CCCB700C4E99E /* IncomingCallView.swift */; };
5C55A923283CEDE600C4E99E /* SoundPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C55A922283CEDE600C4E99E /* SoundPlayer.swift */; };
5C55A92E283D0FDE00C4E99E /* sounds in Resources */ = {isa = PBXBuildFile; fileRef = 5C55A92D283D0FDE00C4E99E /* sounds */; };
5C5625102ABDFA8900A21210 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C56250B2ABDFA8900A21210 /* libffi.a */; };
5C5625112ABDFA8900A21210 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C56250C2ABDFA8900A21210 /* libgmp.a */; };
5C5625122ABDFA8900A21210 /* libHSsimplex-chat-5.3.0.10-9vCXcrdx54qEnAYPtVuU9m.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C56250D2ABDFA8900A21210 /* libHSsimplex-chat-5.3.0.10-9vCXcrdx54qEnAYPtVuU9m.a */; };
5C5625132ABDFA8900A21210 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C56250E2ABDFA8900A21210 /* libgmpxx.a */; };
5C5625142ABDFA8900A21210 /* libHSsimplex-chat-5.3.0.10-9vCXcrdx54qEnAYPtVuU9m-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C56250F2ABDFA8900A21210 /* libHSsimplex-chat-5.3.0.10-9vCXcrdx54qEnAYPtVuU9m-ghc8.10.7.a */; };
5C56251A2AC1DE5900A21210 /* libHSsimplex-chat-5.3.1.0-625aldG8rLm27VEosiv5y7-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C5625152AC1DE5900A21210 /* libHSsimplex-chat-5.3.1.0-625aldG8rLm27VEosiv5y7-ghc8.10.7.a */; };
5C56251B2AC1DE5900A21210 /* libHSsimplex-chat-5.3.1.0-625aldG8rLm27VEosiv5y7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C5625162AC1DE5900A21210 /* libHSsimplex-chat-5.3.1.0-625aldG8rLm27VEosiv5y7.a */; };
5C56251C2AC1DE5900A21210 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C5625172AC1DE5900A21210 /* libgmpxx.a */; };
5C56251D2AC1DE5900A21210 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C5625182AC1DE5900A21210 /* libgmp.a */; };
5C56251E2AC1DE5900A21210 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C5625192AC1DE5900A21210 /* libffi.a */; };
5C577F7D27C83AA10006112D /* MarkdownHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C577F7C27C83AA10006112D /* MarkdownHelp.swift */; };
5C58BCD6292BEBE600AF9E4F /* CIChatFeatureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C58BCD5292BEBE600AF9E4F /* CIChatFeatureView.swift */; };
5C5DB70E289ABDD200730FFF /* AppearanceSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5DB70D289ABDD200730FFF /* AppearanceSettings.swift */; };
@@ -293,11 +293,11 @@
5C55A920283CCCB700C4E99E /* IncomingCallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncomingCallView.swift; sourceTree = "<group>"; };
5C55A922283CEDE600C4E99E /* SoundPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundPlayer.swift; sourceTree = "<group>"; };
5C55A92D283D0FDE00C4E99E /* sounds */ = {isa = PBXFileReference; lastKnownFileType = folder; path = sounds; sourceTree = "<group>"; };
5C56250B2ABDFA8900A21210 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5C56250C2ABDFA8900A21210 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5C56250D2ABDFA8900A21210 /* libHSsimplex-chat-5.3.0.10-9vCXcrdx54qEnAYPtVuU9m.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.3.0.10-9vCXcrdx54qEnAYPtVuU9m.a"; sourceTree = "<group>"; };
5C56250E2ABDFA8900A21210 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5C56250F2ABDFA8900A21210 /* libHSsimplex-chat-5.3.0.10-9vCXcrdx54qEnAYPtVuU9m-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.3.0.10-9vCXcrdx54qEnAYPtVuU9m-ghc8.10.7.a"; sourceTree = "<group>"; };
5C5625152AC1DE5900A21210 /* libHSsimplex-chat-5.3.1.0-625aldG8rLm27VEosiv5y7-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.3.1.0-625aldG8rLm27VEosiv5y7-ghc8.10.7.a"; sourceTree = "<group>"; };
5C5625162AC1DE5900A21210 /* libHSsimplex-chat-5.3.1.0-625aldG8rLm27VEosiv5y7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.3.1.0-625aldG8rLm27VEosiv5y7.a"; sourceTree = "<group>"; };
5C5625172AC1DE5900A21210 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5C5625182AC1DE5900A21210 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5C5625192AC1DE5900A21210 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5C577F7C27C83AA10006112D /* MarkdownHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownHelp.swift; sourceTree = "<group>"; };
5C58BCD5292BEBE600AF9E4F /* CIChatFeatureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIChatFeatureView.swift; sourceTree = "<group>"; };
5C5B67912ABAF4B500DA9412 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -507,12 +507,12 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5C5625132ABDFA8900A21210 /* libgmpxx.a in Frameworks */,
5C5625122ABDFA8900A21210 /* libHSsimplex-chat-5.3.0.10-9vCXcrdx54qEnAYPtVuU9m.a in Frameworks */,
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
5C5625142ABDFA8900A21210 /* libHSsimplex-chat-5.3.0.10-9vCXcrdx54qEnAYPtVuU9m-ghc8.10.7.a in Frameworks */,
5C5625102ABDFA8900A21210 /* libffi.a in Frameworks */,
5C5625112ABDFA8900A21210 /* libgmp.a in Frameworks */,
5C56251C2AC1DE5900A21210 /* libgmpxx.a in Frameworks */,
5C56251B2AC1DE5900A21210 /* libHSsimplex-chat-5.3.1.0-625aldG8rLm27VEosiv5y7.a in Frameworks */,
5C56251A2AC1DE5900A21210 /* libHSsimplex-chat-5.3.1.0-625aldG8rLm27VEosiv5y7-ghc8.10.7.a in Frameworks */,
5C56251E2AC1DE5900A21210 /* libffi.a in Frameworks */,
5C56251D2AC1DE5900A21210 /* libgmp.a in Frameworks */,
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -574,11 +574,11 @@
5C764E5C279C70B7000C6508 /* Libraries */ = {
isa = PBXGroup;
children = (
5C56250B2ABDFA8900A21210 /* libffi.a */,
5C56250C2ABDFA8900A21210 /* libgmp.a */,
5C56250E2ABDFA8900A21210 /* libgmpxx.a */,
5C56250F2ABDFA8900A21210 /* libHSsimplex-chat-5.3.0.10-9vCXcrdx54qEnAYPtVuU9m-ghc8.10.7.a */,
5C56250D2ABDFA8900A21210 /* libHSsimplex-chat-5.3.0.10-9vCXcrdx54qEnAYPtVuU9m.a */,
5C5625192AC1DE5900A21210 /* libffi.a */,
5C5625182AC1DE5900A21210 /* libgmp.a */,
5C5625172AC1DE5900A21210 /* libgmpxx.a */,
5C5625152AC1DE5900A21210 /* libHSsimplex-chat-5.3.1.0-625aldG8rLm27VEosiv5y7-ghc8.10.7.a */,
5C5625162AC1DE5900A21210 /* libHSsimplex-chat-5.3.1.0-625aldG8rLm27VEosiv5y7.a */,
);
path = Libraries;
sourceTree = "<group>";
@@ -1486,7 +1486,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 173;
CURRENT_PROJECT_VERSION = 174;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES;
@@ -1507,7 +1507,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 5.3;
MARKETING_VERSION = 5.3.1;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX;
SDKROOT = iphoneos;
@@ -1528,7 +1528,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 173;
CURRENT_PROJECT_VERSION = 174;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES;
@@ -1549,7 +1549,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 5.3;
MARKETING_VERSION = 5.3.1;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX;
SDKROOT = iphoneos;
@@ -1608,7 +1608,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 173;
CURRENT_PROJECT_VERSION = 174;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GENERATE_INFOPLIST_FILE = YES;
@@ -1621,7 +1621,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 5.3;
MARKETING_VERSION = 5.3.1;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1640,7 +1640,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 173;
CURRENT_PROJECT_VERSION = 174;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GENERATE_INFOPLIST_FILE = YES;
@@ -1653,7 +1653,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 5.3;
MARKETING_VERSION = 5.3.1;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1672,7 +1672,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 173;
CURRENT_PROJECT_VERSION = 174;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1696,7 +1696,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Libraries/sim",
);
MARKETING_VERSION = 5.3;
MARKETING_VERSION = 5.3.1;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos;
@@ -1718,7 +1718,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 173;
CURRENT_PROJECT_VERSION = 174;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1742,7 +1742,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Libraries/sim",
);
MARKETING_VERSION = 5.3;
MARKETING_VERSION = 5.3.1;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos;

View File

@@ -60,7 +60,7 @@ Java_chat_simplex_common_platform_CoreKt_chatMigrateInit(JNIEnv *env, __unused j
jstring res = (*env)->NewStringUTF(env, chat_migrate_init(_dbPath, _dbKey, _confirm, &_ctrl));
(*env)->ReleaseStringUTFChars(env, dbPath, _dbPath);
(*env)->ReleaseStringUTFChars(env, dbKey, _dbKey);
(*env)->ReleaseStringUTFChars(env, dbKey, _confirm);
(*env)->ReleaseStringUTFChars(env, confirm, _confirm);
// Creating array of Object's (boxed values can be passed, eg. Long instead of long)
jobjectArray ret = (jobjectArray)(*env)->NewObjectArray(env, 2, (*env)->FindClass(env, "java/lang/Object"), NULL);

View File

@@ -67,7 +67,7 @@ if(NOT APPLE)
else()
# Without direct linking it can't find hs_init in linking step
add_library( rts SHARED IMPORTED )
FILE(GLOB RTSLIB ${CMAKE_SOURCE_DIR}/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/deps/libHSrts_thr-*.${OS_LIB_EXT})
FILE(GLOB RTSLIB ${CMAKE_SOURCE_DIR}/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/deps/libHSrts*_thr-*.${OS_LIB_EXT})
set_target_properties( rts PROPERTIES IMPORTED_LOCATION ${RTSLIB})
target_link_libraries(app-lib rts simplex)

View File

@@ -394,6 +394,7 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: suspend (chatId:
}
},
onComposed,
developerTools = chatModel.controller.appPrefs.developerTools.get(),
)
}
}
@@ -435,6 +436,7 @@ fun ChatLayout(
changeNtfsState: (Boolean, currentValue: MutableState<Boolean>) -> Unit,
onSearchValueChanged: (String) -> Unit,
onComposed: suspend (chatId: String) -> Unit,
developerTools: Boolean,
) {
val scope = rememberCoroutineScope()
val attachmentDisabled = remember { derivedStateOf { composeState.value.attachmentDisabled } }
@@ -500,7 +502,7 @@ fun ChatLayout(
useLinkPreviews, linkMode, showMemberInfo, loadPrevMessages, deleteMessage,
receiveFile, cancelFile, joinGroup, acceptCall, acceptFeature, openDirectChat,
updateContactStats, updateMemberStats, syncContactConnection, syncMemberConnection, findModelChat, findModelMember,
setReaction, showItemDetails, markRead, setFloatingButton, onComposed,
setReaction, showItemDetails, markRead, setFloatingButton, onComposed, developerTools,
)
}
}
@@ -695,6 +697,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
markRead: (CC.ItemRange, unreadCountAfter: Int?) -> Unit,
setFloatingButton: (@Composable () -> Unit) -> Unit,
onComposed: suspend (chatId: String) -> Unit,
developerTools: Boolean,
) {
val listState = rememberLazyListState()
val scope = rememberCoroutineScope()
@@ -831,7 +834,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
) {
MemberImage(member)
}
ChatItemView(chat.chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, deleteMessage = deleteMessage, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = {}, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, setReaction = setReaction, showItemDetails = showItemDetails, getConnectedMemberNames = ::getConnectedMemberNames)
ChatItemView(chat.chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, deleteMessage = deleteMessage, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = {}, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, setReaction = setReaction, showItemDetails = showItemDetails, getConnectedMemberNames = ::getConnectedMemberNames, developerTools = developerTools)
}
}
} else {
@@ -840,7 +843,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
.padding(start = 8.dp + MEMBER_IMAGE_SIZE + 4.dp, end = if (voiceWithTransparentBack) 12.dp else 66.dp)
.then(swipeableModifier)
) {
ChatItemView(chat.chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, deleteMessage = deleteMessage, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = {}, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, setReaction = setReaction, showItemDetails = showItemDetails, getConnectedMemberNames = ::getConnectedMemberNames)
ChatItemView(chat.chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, deleteMessage = deleteMessage, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = {}, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, setReaction = setReaction, showItemDetails = showItemDetails, getConnectedMemberNames = ::getConnectedMemberNames, developerTools = developerTools)
}
}
}
@@ -850,7 +853,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
.padding(start = if (voiceWithTransparentBack) 12.dp else 104.dp, end = 12.dp)
.then(swipeableModifier)
) {
ChatItemView(chat.chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, deleteMessage = deleteMessage, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = {}, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, setReaction = setReaction, showItemDetails = showItemDetails)
ChatItemView(chat.chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, deleteMessage = deleteMessage, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = {}, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, setReaction = setReaction, showItemDetails = showItemDetails, developerTools = developerTools)
}
}
} else { // direct message
@@ -861,7 +864,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
end = if (sent || voiceWithTransparentBack) 12.dp else 76.dp,
).then(swipeableModifier)
) {
ChatItemView(chat.chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, deleteMessage = deleteMessage, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, setReaction = setReaction, showItemDetails = showItemDetails)
ChatItemView(chat.chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, deleteMessage = deleteMessage, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, setReaction = setReaction, showItemDetails = showItemDetails, developerTools = developerTools)
}
}
@@ -1300,6 +1303,7 @@ fun PreviewChatLayout() {
changeNtfsState = { _, _ -> },
onSearchValueChanged = {},
onComposed = {},
developerTools = false,
)
}
}
@@ -1368,6 +1372,7 @@ fun PreviewGroupChatLayout() {
changeNtfsState = { _, _ -> },
onSearchValueChanged = {},
onComposed = {},
developerTools = false,
)
}
}

View File

@@ -64,6 +64,7 @@ fun ChatItemView(
setReaction: (ChatInfo, ChatItem, Boolean, MsgReaction) -> Unit,
showItemDetails: (ChatInfo, ChatItem) -> Unit,
getConnectedMemberNames: (() -> List<String>)? = null,
developerTools: Boolean,
) {
val uriHandler = LocalUriHandler.current
val sent = cItem.chatDir.sent
@@ -343,7 +344,11 @@ fun ChatItemView(
is CIContent.RcvDeleted -> DeletedItem()
is CIContent.SndCall -> CallItem(c.status, c.duration)
is CIContent.RcvCall -> CallItem(c.status, c.duration)
is CIContent.RcvIntegrityError -> IntegrityErrorItemView(c.msgError, cItem, cInfo.timedMessagesTTL)
is CIContent.RcvIntegrityError -> if (developerTools) {
IntegrityErrorItemView(c.msgError, cItem, cInfo.timedMessagesTTL)
} else {
Box(Modifier.size(0.dp)) {}
}
is CIContent.RcvDecryptionError -> CIRcvDecryptionError(c.msgDecryptError, c.msgCount, cInfo, cItem, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember)
is CIContent.RcvGroupInvitation -> CIGroupInvitationView(cItem, c.groupInvitation, c.memberRole, joinGroup = joinGroup, chatIncognito = cInfo.incognito)
is CIContent.SndGroupInvitation -> CIGroupInvitationView(cItem, c.groupInvitation, c.memberRole, joinGroup = joinGroup, chatIncognito = cInfo.incognito)
@@ -583,6 +588,7 @@ fun PreviewChatItemView() {
findModelMember = { null },
setReaction = { _, _, _, _ -> },
showItemDetails = { _, _ -> },
developerTools = false,
)
}
}
@@ -613,6 +619,7 @@ fun PreviewChatItemViewDeletedContent() {
findModelMember = { null },
setReaction = { _, _, _, _ -> },
showItemDetails = { _, _ -> },
developerTools = false,
)
}
}

View File

@@ -153,102 +153,106 @@ afterEvaluate {
tasks.create("cmakeBuildAndCopy") {
dependsOn("cmakeBuild")
val copyDetails = mutableMapOf<String, ArrayList<FileCopyDetails>>()
copy {
from("${project(":desktop").buildDir}/cmake/main/linux-amd64", "$cppPath/desktop/libs/linux-x86_64", "$cppPath/desktop/libs/linux-x86_64/deps")
into("src/jvmMain/resources/libs/linux-x86_64")
include("*.so*")
eachFile {
path = name
}
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
copy {
val destinationDir = "src/jvmMain/resources/libs/linux-x86_64/vlc"
from("$cppPath/desktop/libs/linux-x86_64/deps/vlc")
into(destinationDir)
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
copyIfNeeded(destinationDir, copyDetails)
}
copy {
from("${project(":desktop").buildDir}/cmake/main/linux-aarch64", "$cppPath/desktop/libs/linux-aarch64", "$cppPath/desktop/libs/linux-aarch64/deps")
into("src/jvmMain/resources/libs/linux-aarch64")
include("*.so*")
eachFile {
path = name
}
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
copy {
val destinationDir = "src/jvmMain/resources/libs/linux-aarch64/vlc"
from("$cppPath/desktop/libs/linux-aarch64/deps/vlc")
into(destinationDir)
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
copyIfNeeded(destinationDir, copyDetails)
}
copy {
from("${project(":desktop").buildDir}/cmake/main/win-amd64", "$cppPath/desktop/libs/windows-x86_64", "$cppPath/desktop/libs/windows-x86_64/deps")
into("src/jvmMain/resources/libs/windows-x86_64")
include("*.dll")
eachFile {
path = name
}
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
copy {
val destinationDir = "src/jvmMain/resources/libs/windows-x86_64/vlc"
from("$cppPath/desktop/libs/windows-x86_64/deps/vlc")
into(destinationDir)
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
copyIfNeeded(destinationDir, copyDetails)
}
copy {
from("${project(":desktop").buildDir}/cmake/main/mac-x86_64", "$cppPath/desktop/libs/mac-x86_64", "$cppPath/desktop/libs/mac-x86_64/deps")
into("src/jvmMain/resources/libs/mac-x86_64")
include("*.dylib")
eachFile {
path = name
}
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
copy {
val destinationDir = "src/jvmMain/resources/libs/mac-x86_64/vlc"
from("$cppPath/desktop/libs/mac-x86_64/deps/vlc")
into(destinationDir)
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
copyIfNeeded(destinationDir, copyDetails)
}
copy {
from("${project(":desktop").buildDir}/cmake/main/mac-aarch64", "$cppPath/desktop/libs/mac-aarch64", "$cppPath/desktop/libs/mac-aarch64/deps")
into("src/jvmMain/resources/libs/mac-aarch64")
include("*.dylib")
eachFile {
path = name
}
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
copy {
val destinationDir = "src/jvmMain/resources/libs/mac-aarch64/vlc"
from("$cppPath/desktop/libs/mac-aarch64/deps/vlc")
into(destinationDir)
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
copyIfNeeded(destinationDir, copyDetails)
}
doLast {
copyDetails.forEach { (destinationDir, details) ->
details.forEach { detail ->
val target = File(projectDir.absolutePath + File.separator + destinationDir + File.separator + detail.path)
if (target.exists()) {
target.setLastModified(detail.lastModified)
copy {
from("${project(":desktop").buildDir}/cmake/main/linux-amd64", "$cppPath/desktop/libs/linux-x86_64", "$cppPath/desktop/libs/linux-x86_64/deps")
into("src/jvmMain/resources/libs/linux-x86_64")
include("*.so*")
eachFile {
path = name
}
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
copy {
val destinationDir = "src/jvmMain/resources/libs/linux-x86_64/vlc"
from("$cppPath/desktop/libs/linux-x86_64/deps/vlc")
into(destinationDir)
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
copyIfNeeded(destinationDir, copyDetails)
}
copy {
from("${project(":desktop").buildDir}/cmake/main/linux-aarch64", "$cppPath/desktop/libs/linux-aarch64", "$cppPath/desktop/libs/linux-aarch64/deps")
into("src/jvmMain/resources/libs/linux-aarch64")
include("*.so*")
eachFile {
path = name
}
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
copy {
val destinationDir = "src/jvmMain/resources/libs/linux-aarch64/vlc"
from("$cppPath/desktop/libs/linux-aarch64/deps/vlc")
into(destinationDir)
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
copyIfNeeded(destinationDir, copyDetails)
}
copy {
from("${project(":desktop").buildDir}/cmake/main/win-amd64", "$cppPath/desktop/libs/windows-x86_64", "$cppPath/desktop/libs/windows-x86_64/deps")
into("src/jvmMain/resources/libs/windows-x86_64")
include("*.dll")
eachFile {
path = name
}
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
copy {
val destinationDir = "src/jvmMain/resources/libs/windows-x86_64/vlc"
from("$cppPath/desktop/libs/windows-x86_64/deps/vlc")
into(destinationDir)
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
copyIfNeeded(destinationDir, copyDetails)
}
copy {
from("${project(":desktop").buildDir}/cmake/main/mac-x86_64", "$cppPath/desktop/libs/mac-x86_64", "$cppPath/desktop/libs/mac-x86_64/deps")
into("src/jvmMain/resources/libs/mac-x86_64")
include("*.dylib")
eachFile {
path = name
}
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
copy {
val destinationDir = "src/jvmMain/resources/libs/mac-x86_64/vlc"
from("$cppPath/desktop/libs/mac-x86_64/deps/vlc")
into(destinationDir)
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
copyIfNeeded(destinationDir, copyDetails)
}
copy {
from("${project(":desktop").buildDir}/cmake/main/mac-aarch64", "$cppPath/desktop/libs/mac-aarch64", "$cppPath/desktop/libs/mac-aarch64/deps")
into("src/jvmMain/resources/libs/mac-aarch64")
include("*.dylib")
eachFile {
path = name
}
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
copy {
val destinationDir = "src/jvmMain/resources/libs/mac-aarch64/vlc"
from("$cppPath/desktop/libs/mac-aarch64/deps/vlc")
into(destinationDir)
includeEmptyDirs = false
duplicatesStrategy = DuplicatesStrategy.INCLUDE
copyIfNeeded(destinationDir, copyDetails)
}
}
afterEvaluate {
doLast {
copyDetails.forEach { (destinationDir, details) ->
details.forEach { detail ->
val target = File(projectDir.absolutePath + File.separator + destinationDir + File.separator + detail.path)
if (target.exists()) {
target.setLastModified(detail.lastModified)
}
}
}
}

View File

@@ -25,11 +25,11 @@ android.nonTransitiveRClass=true
android.enableJetifier=true
kotlin.mpp.androidSourceSetLayoutVersion=2
android.version_name=5.3
android.version_code=152
android.version_name=5.3.1
android.version_code=154
desktop.version_name=5.3
desktop.version_code=10
desktop.version_name=5.3.1
desktop.version_code=11
kotlin.version=1.8.20
gradle.plugin.version=7.4.2

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

View File

@@ -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

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)

View File

@@ -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)

View File

@@ -4,6 +4,7 @@ title: "SimpleX File Transfer Protocol - a new protocol for sending large files
date: 2023-03-01
preview: CLI and relays implementing the new XFTP protocol are released - you can use them now!
image: images/20230301-xftp.jpg
imageWide: true
permalink: "/blog/20230301-simplex-file-transfer-protocol.html"
---

View File

@@ -63,7 +63,7 @@ To accelerate product development and growth we will be raising a seed funding t
### Send videos and files up to 1gb!
<img src="./images/20230422-video.png" width="288">
<img src="./images/20230422-video.png" width="288" class="float-to-left">
In the beginning of March [we released servers and command-line utility to send and receive files via XFTP protocol](./20230301-simplex-file-transfer-protocol.md) - a very private and secure protocol that sends end-to-end encrypted files in chunks, protecting meta-data better than any alternatives we know of.
@@ -88,7 +88,7 @@ Now you can choose whether to use faster and more convenient system biometric au
### Networking improvements
<img src="./images/20230422-socks.png" width="288">
<img src="./images/20230422-socks.png" width="288" class="float-to-left">
Two small improvements to the app networking capabilities were added in this version.

View File

@@ -36,7 +36,7 @@ Also, we added Japanese and Portuguese (Brazil)<sup>*</sup> interface languages,
## Message reactions
<img src="./images/20230523-reactions.png" width="288">
<img src="./images/20230523-reactions.png" width="288" class="float-to-left">
No idea why it took us so long to add them finally we have them, and they are great.
@@ -50,7 +50,7 @@ The next app version will allow prohibiting the reactions per conversation, as y
### Voice messages: up to 5 minutes, better quality, playback control
<img src="./images/20230523-voice.png" width="288">
<img src="./images/20230523-voice.png" width="288" class="float-to-left">
Since [v4.3](./20221206-simplex-chat-v4.3-voice-messages.md#instant-voice-messages) voice messages were sent in small 16kb chunks, so we had to limit them to 30-40 seconds for better user experience, as sending larger files would require the sender to be online.
@@ -66,7 +66,7 @@ This version allows to configure the time for messages to disappear more granula
### Message editing history
<img src="./images/20230523-info.png" width="288">
<img src="./images/20230523-info.png" width="288" class="float-to-left">
I [wrote previously](./20221206-simplex-chat-v4.3-voice-messages.md#irreversible-message-deletion) why we decided to require the recipient concent before the messages can be fully deleted by the sender - in short, it is to support recipient's data sovereignty and prevent the possibility of offensive messages being removed without any trace. By default, when the sender deletes the message it is marked as deleted, rather than fully deleted, and you can reveal the original message.
@@ -74,7 +74,7 @@ You've found the workaround for it of course - it's enough to simply edit the me
## Customize and share color themes
<img src="./images/20230523-theme.png" width="288">
<img src="./images/20230523-theme.png" width="288" class="float-to-left">
Android app now allows choosing between three color themes - Light, Dark and SimpleX (a dark blue theme). You can customize any theme by setting 9 different colors used in the app, including titles, menus, accent colors and colors for sent and received messages.
@@ -82,7 +82,7 @@ You can share your theme with other users by exporting it to a file and sending
## Self-destruct passcode
<img src="./images/20230523-self-destruct.png" width="288">
<img src="./images/20230523-self-destruct.png" width="288" class="float-to-left">
This is something many of you asked before - when asked to enter the app passcode under duress, to be able to enter a special self-destruct code that would remove the app data. This feature is offered in many security tools, and now you can configure it in SimpleX Chat as well.

View File

@@ -38,7 +38,7 @@ permalink: "/blog/20230722-simplex-chat-v5-2-message-delivery-receipts.html"
### Message delivery receipts
<img src="./images/20230722-receipts.png" width="330">
<img src="./images/20230722-receipts.png" width="330" class="float-to-left">
Most messaging apps add two ticks to sent messages the first one to show that the message is accepted by the server, and the second that it is delivered to the recipient's device. It confirms that the network is functioning, and that the message is not lost or delayed. SimpleX Chat now has this feature too!
@@ -48,7 +48,7 @@ To avoid compromising your privacy, sending delivery receipts is disabled for al
### Filter favorite and unread chats
<img src="./images/20230722-filter.png" width="288">
<img src="./images/20230722-filter.png" width="288" class="float-to-left">
You can now mark your contacts and groups as _favorite_, to be able to find them faster. With filter enabled, you will only see favorite chats, chats that contain unread messages and also any unaccepted group invitations and contact requests.
@@ -58,13 +58,13 @@ Active SimpleX Chat users know how broken the current group experience is, and t
#### What is this in reply to?
<img src="./images/20230722-quoted.png" width="330">
<img src="./images/20230722-quoted.png" width="330" class="float-to-left">
A major problem is that you can see replies to the messages you've not seen before - this would happen both when you just join the group, and didn't connect to most other members, and also when other new members join the group and they didn't yet connect to you so literally all the time, and the bigger the group gets, the worse it becomes. While this problem cannot be solved without major group protocol changes, at least there is now ability to see the original message that was replied to via the message information.
#### How to connect to this member?
<img src="./images/20230722-search.png" width="330">
<img src="./images/20230722-search.png" width="330" class="float-to-left">
To simplify direct connections with other group members, you can now share your SimpleX address via your chat profile, and group members can send you a contact request even if the group does not allow direct messages.

View File

@@ -2,14 +2,130 @@
layout: layouts/article.html
title: "SimpleX Chat v5.3 released: desktop app, local file encryption and improved groups with directory service"
date: 2023-09-25
# image: images/20230925-desktop-app.png
# previewBody: blog_previews/20230722.html
image: /docs/images/simplex-desktop-light.png
imageWide: true
previewBody: blog_previews/20230925.html
permalink: "/blog/20230925-simplex-chat-v5-3-desktop-app-local-file-encryption-directory-service.html"
draft: true
---
# SimpleX Chat v5.3 released: desktop app, local file encryption and improved groups
**Published:** September 25, 2023
This is a placeholder for the release announcement
**What's new in v5.3:**
- [new desktop app!](#multiplatform-desktop-app)!
- [directory service and other group improvements](#group-directory-service-and-other-group-improvements).
- [encrypted local files and media with forward secrecy](#encrypted-local-files-and-media-with-forward-secrecy).
- [simplified incognito mode](#simplified-incognito-mode).
There are a lot of other improvements and fixes in this release:
- improved app responsiveness and stability.
- app memory usage is reduced by 40%.
- new privacy settings: show last messages & save draft.
- fixes:
- bug preventing group members connecting (it will only help the new connections).
- playing videos on full screen<sup>**</sup>.
- screen reader for messages<sup>**</sup>.
- reduced background crashes<sup>**</sup>.
Also, we added 6 new interface languages: Arabic<sup>*</sup>, Bulgarian, Finnish, Hebrew<sup>*</sup>, Thai and Ukrainian - thanks to [our users and Weblate](https://github.com/simplex-chat/simplex-chat#help-translating-simplex-chat).
\* Android app.
\*\* iOS app.
## Multiplatform desktop app
<img src="/docs/images/simplex-desktop-light.png" width="640">
Thanks a lot to everybody who was testing the desktop app since July it really helped to make it stable!
To use desktop app you need to **create a new profile**. As SimpleX platform has no user accounts, it's not as simple as for centralized apps to access the same profile from two devices.
The next app version will allow using your mobile profile from desktop app. For now, as a workaround, you can join groups from both mobile and desktop devices, and use small groups instead of direct conversations.
When you start the app first time, you will be offered to **set database passphrase** you have to memorize it, as there is no way to recover it. If you skip it, a random passphrase will be generated and stored on your desktop device as plaintext (unencrypted) you can change it later.
Other limitations of the desktop app:
- you cannot send voice messages.
- there is no support for calls yet.
## Group directory service and other group improvements
<img src="./images/20230925-directory.png" width="330" class="float-to-left">
Directory service provides a way to search for public groups submitted by the users. To use it, you need to connect to it via SimpleX Chat, as you would connect to any other contact, and type some words to search.
You can also create and register your group, with some limitations explained [here](../docs/DIRECTORY.md).
Other group improvements in this release:
- you can send delivery receipts to the groups up to 20 members.
- if the group settings allow it, you can send direct messages to group members even after you deleted the contact.
- connections between members are made faster, and the bug that prevented the connections in some cases is fixed in this release.
The next release will reduce the time it takes to send messages to the group, especially when there are many members or when you have a slow device storage.
## Encrypted local files and media with forward secrecy
<img src="./images/20230925-encrypted.png" width="330" class="float-to-left">
All messages, files and media sent via SimpleX Chat were always end-to-end encrypted from the very beginning. SimpleX Chat uses double-ratchet algorithm with encrypted message headers, for the best possible meta-data protection.
You contacts, groups and messages are stored in the local database on your device, and this database was encrypted from [v4.0 released a year ago](./20220928-simplex-chat-v4-encrypted-database.md).
But until this version all files and media in the app storage were not encrypted, and when you exported the chat archive, they were unencrypted there as well.
From v5.3 all files and media (except videos, for now) are encrypted with a random symmetric key - in many cases they are encrypted before they are written to the storage. Local file encryption can be disabled via Privacy & Security settings, for example, if you need to access the files from the storage outside of the app.
In addition to the videos that are stored unencrypted, there are other rare scenarios when the received files may be unencrypted in this release. Files have an open or closed lock icons to indicate whether they were encrypted locally. These limitations will be addressed in the next release. In any case, all files and media are always sent end-to-end encrypted, without any exceptions.
The keys used to encrypt files locally are associated with the messages and stored in the encrypted database. If you delete a message with the attached file or media, the key will be irreversibly deleted as well. Even if an attacker gains access to your database passphrase later and to the copy of the encrypted file, they won't be able to decrypt the file.
This approach provides forward secrecy for locally stored files, unlike file encryption schemes used in some other apps when the same passphrase is used for all files.
## Simplified incognito mode
<img src="./images/20230925-incognito.png" width="330" class="float-to-left">
Incognito mode was [added a year ago](./20220901-simplex-chat-v3.2-incognito-mode.md) to improve anonymity of your profile, but it was confusing for some users - it was a global setting, but it only affected the new connections.
It is now simpler to use - you can decide whether to connect to a contact or join a group using your main profile at a point when you create an invitation link or connect via a link or QR code.
When you are connecting to people your know you usually want to share your main profile, and when connecting to public groups or strangers, you may prefer to use a random profile.
## SimpleX platform
Some links to answer the most common questions:
[SimpleX Chat security assessment](./20221108-simplex-chat-v4.2-security-audit-new-website.md).
[How can SimpleX deliver messages without user identifiers](https://simplex.chat/#how-simplex-works).
[What are the risks to have identifiers assigned to the users](https://simplex.chat/#why-ids-bad-for-privacy).
[Technical details and limitations](https://github.com/simplex-chat/simplex-chat#privacy-technical-details-and-limitations).
[How SimpleX is different from Session, Matrix, Signal, etc.](https://github.com/simplex-chat/simplex-chat/blob/stable/README.md#frequently-asked-questions).
Visit our [website](https://simplex.chat) to learn more.
## Help us with donations
Huge thank you to everybody who donated to SimpleX Chat!
We are prioritizing users privacy and security - it would be impossible without your support.
Our pledge to our users is that SimpleX protocols are and will remain open, and in public domain, - so anybody can build the future implementations of the clients and the servers. We are building SimpleX platform based on the same principles as email and web, but much more private and secure.
Your donations help us raise more funds any amount, even the price of the cup of coffee, makes a big difference for us.
See [this section](https://github.com/simplex-chat/simplex-chat/tree/master#help-us-with-donations) for the ways to donate.
Thank you,
Evgeny
SimpleX Chat founder

Binary file not shown.

After

Width:  |  Height:  |  Size: 343 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 475 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

View File

@@ -2,14 +2,14 @@ packages: .
-- packages: . ../simplexmq
-- packages: . ../simplexmq ../direct-sqlcipher ../sqlcipher-simple
with-compiler: ghc-8.10.7
with-compiler: ghc-9.6.2
constraints: zip +disable-bzip2 +disable-zstd
source-repository-package
type: git
location: https://github.com/simplex-chat/simplexmq.git
tag: 53c793d5590d3c781aa3fbf72993eee262c7aa83
tag: 8d47f690838371bc848e4b31a4b09ef6bf67ccc5
source-repository-package
type: git
@@ -24,17 +24,17 @@ 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
location: https://github.com/simplex-chat/aeson.git
tag: 3eb66f9a68f103b5f1489382aad89f5712a64db7
tag: 68330dce8208173c6acf5f62b23acb500ab5d873
source-repository-package
type: git
@@ -43,5 +43,10 @@ source-repository-package
source-repository-package
type: git
location: https://github.com/zw3rk/android-support.git
tag: 3c3a5ab0b8b137a072c98d3d0937cbdc96918ddb
location: https://github.com/simplex-chat/android-support.git
tag: 9aa09f148089d6752ce563b14c2df1895718d806
source-repository-package
type: git
location: https://github.com/simplex-chat/network-transport.git
tag: 0013798272a683e35ca38d2fdaf480942311fba8

View File

@@ -7,6 +7,8 @@ revision: 20.09.2023
| Updated 20.09.2023 | Languages: EN |
# Download SimpleX apps
The latest version is v5.3.
- [desktop](#desktop-app)
- [mobile](#mobile-apps)
- [terminal](#terminal-console-app) (console)
@@ -15,13 +17,11 @@ revision: 20.09.2023
<img src="/docs/images/simplex-desktop-light.png" alt="desktop app" width=500>
The latest version of desktop app is v5.3-beta.9 (1.6.0 in the app).
Using the same profile as on mobile device is not yet supported you need to create a separate profile to use desktop apps.
**Linux**: [AppImage](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0-beta.9/simplex-desktop-x86_64.AppImage) (most Linux distros), [Ubuntu 20.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0-beta.9/simplex-desktop-ubuntu-20_04-x86_64.deb) (and Debian-based distros), [Ubuntu 22.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0-beta.9/simplex-desktop-ubuntu-22_04-x86_64.deb).
**Linux**: [AppImage](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0/simplex-desktop-x86_64.AppImage) (most Linux distros), [Ubuntu 20.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0/simplex-desktop-ubuntu-20_04-x86_64.deb) (and Debian-based distros), [Ubuntu 22.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0/simplex-desktop-ubuntu-22_04-x86_64.deb).
**Mac**: [x86_64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0-beta.9/simplex-desktop-macos-x86_64.dmg) (Intel), [aarch64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0-beta.9/simplex-desktop-macos-aarch64.dmg) (Apple Silicon).
**Mac**: [x86_64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0/simplex-desktop-macos-x86_64.dmg) (Intel), [aarch64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0/simplex-desktop-macos-aarch64.dmg) (Apple Silicon).
**Windows**: coming soon.
@@ -29,14 +29,14 @@ Using the same profile as on mobile device is not yet supported you need to
**iOS**: [App store](https://apps.apple.com/us/app/simplex-chat/id1605771084) (v5.2.3), [TestFlight](https://testflight.apple.com/join/DWuT2LQu) (v5.3-beta.9).
**Android**: [Play store](https://play.google.com/store/apps/details?id=chat.simplex.app), [F-Droid](https://simplex.chat/fdroid/), [APK aarch64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0-beta.9/simplex.apk), [APK armv7](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0-beta.9/simplex-armv7a.apk).
**Android**: [Play store](https://play.google.com/store/apps/details?id=chat.simplex.app), [F-Droid](https://simplex.chat/fdroid/), [APK aarch64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0/simplex.apk), [APK armv7](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0/simplex-armv7a.apk).
## Terminal (console) app
See [Using terminal app](/docs/CLI.md).
**Linux**: [Ubuntu 20.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0-beta.9/simplex-chat-ubuntu-20_04-x86-64), [Ubuntu 22.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0-beta.9/simplex-chat-ubuntu-22_04-x86-64).
**Linux**: [Ubuntu 20.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0/simplex-chat-ubuntu-20_04-x86-64), [Ubuntu 22.04](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0/simplex-chat-ubuntu-22_04-x86-64).
**Mac** [x86_64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0-beta.9/simplex-chat-macos-x86-64), aarch64 - [compile from source](./CLI.md#).
**Mac** [x86_64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0/simplex-chat-macos-x86-64), aarch64 - [compile from source](./CLI.md#).
**Windows**: [x86_64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0-beta.9/simplex-chat-windows-x86-64).
**Windows**: [x86_64](https://github.com/simplex-chat/simplex-chat/releases/download/v5.3.0/simplex-chat-windows-x86-64).

View File

@@ -1,5 +1,5 @@
name: simplex-chat
version: 5.3.0.10
version: 5.3.1.0
#synopsis:
#description:
homepage: https://github.com/simplex-chat/simplex-chat#readme
@@ -13,25 +13,25 @@ extra-source-files:
- cabal.project
dependencies:
- aeson == 2.0.*
- aeson == 2.2.*
- ansi-terminal >= 0.10 && < 0.12
- async == 2.2.*
- attoparsec == 0.14.*
- base >= 4.7 && < 5
- base64-bytestring >= 1.0 && < 1.3
- bytestring == 0.10.*
- bytestring == 0.11.*
- composition == 1.0.*
- constraints >= 0.12 && < 0.14
- containers == 0.6.*
- cryptonite >= 0.27 && < 0.30
- cryptonite == 0.30.*
- directory == 1.3.*
- direct-sqlcipher == 2.3.*
- email-validate == 2.3.*
- exceptions == 0.10.*
- filepath == 1.4.*
- http-types == 0.12.*
- memory == 0.15.*
- mtl == 2.2.*
- memory == 0.18.*
- mtl == 2.3.*
- network >= 3.1.2.7 && < 3.2
- optparse-applicative >= 0.15 && < 0.17
- process == 1.6.*
@@ -42,13 +42,13 @@ dependencies:
- socks == 0.6.*
- sqlcipher-simple == 0.4.*
- stm == 2.5.*
- template-haskell == 2.16.*
- template-haskell == 2.20.*
- terminal == 0.2.*
- text == 1.2.*
- text == 2.0.*
- time == 1.9.*
- unliftio == 0.2.*
- unliftio-core == 0.2.*
- zip == 1.7.*
- zip == 2.0.*
flags:
swift:
@@ -118,7 +118,7 @@ tests:
- simplex-chat
- async == 2.2.*
- deepseq == 1.4.*
- hspec == 2.7.*
- hspec == 2.11.*
- network == 3.1.*
- silently == 1.2.*
- stm == 2.5.*

View File

@@ -2,7 +2,7 @@
set -e
trap "rm apps/multiplatform/local.properties || true; rm local.properties || true; rm /tmp/simplex.keychain || true" EXIT
trap "rm apps/multiplatform/local.properties 2> /dev/null || true; rm local.properties 2> /dev/null || true; rm /tmp/simplex.keychain" EXIT
echo "desktop.mac.signing.identity=Developer ID Application: SimpleX Chat Ltd (5NN7GUYB6T)" >> apps/multiplatform/local.properties
echo "desktop.mac.signing.keychain=/tmp/simplex.keychain" >> apps/multiplatform/local.properties
echo "desktop.mac.notarization.apple_id=$APPLE_SIMPLEX_NOTARIZATION_APPLE_ID" >> apps/multiplatform/local.properties
@@ -10,6 +10,10 @@ echo "desktop.mac.notarization.password=$APPLE_SIMPLEX_NOTARIZATION_PASSWORD" >>
echo "desktop.mac.notarization.team_id=5NN7GUYB6T" >> apps/multiplatform/local.properties
echo "$APPLE_SIMPLEX_SIGNING_KEYCHAIN" | base64 --decode - > /tmp/simplex.keychain
security unlock-keychain -p "" /tmp/simplex.keychain
# Adding keychain to the list of keychains.
# Otherwise, it can find cert but exits while signing with "error: The specified item could not be found in the keychain."
security list-keychains -s `security list-keychains | xargs` /tmp/simplex.keychain
scripts/desktop/build-lib-mac.sh
cd apps/multiplatform
./gradlew packageDmg

View File

@@ -0,0 +1,10 @@
#!/bin/bash
security create-keychain -p "" simplex.keychain
security set-keychain-settings -u simplex.keychain
security add-certificates -k simplex.keychain "Developer ID Application: SimpleX Chat Ltd (5NN7GUYB6T).cer"
security add-certificates -k simplex.keychain "Developer ID Certification Authority.cer"
# Private key with access from any app
security import "SimpleX Chat.p12" -P "" -k simplex.keychain -A
# Public key
security import "SimpleX Chat.pem" -k simplex.keychain

View File

@@ -2,12 +2,12 @@
OS=linux
ARCH=${1:-`uname -a | rev | cut -d' ' -f2 | rev`}
GHC_VERSION=8.10.7
GHC_VERSION=9.6.2
BUILD_DIR=dist-newstyle/build/$ARCH-$OS/ghc-${GHC_VERSION}/simplex-chat-*
rm -rf $BUILD_DIR
cabal build lib:simplex-chat --ghc-options='-optl-Wl,-rpath,$ORIGIN' --ghc-options="-optl-L$(ghc --print-libdir)/rts -optl-Wl,--as-needed,-lHSrts_thr-ghc$GHC_VERSION"
cabal build lib:simplex-chat --ghc-options='-optl-Wl,-rpath,$ORIGIN -flink-rts -threaded'
cd $BUILD_DIR/build
#patchelf --add-needed libHSrts_thr-ghc${GHC_VERSION}.so libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so
#patchelf --add-rpath '$ORIGIN' libHSsimplex-chat-*-inplace-ghc${GHC_VERSION}.so

View File

@@ -2,9 +2,12 @@
OS=mac
ARCH="${1:-`uname -a | rev | cut -d' ' -f1 | rev`}"
GHC_VERSION=9.6.2
if [ "$ARCH" == "arm64" ]; then
ARCH=aarch64
fi
LIB_EXT=dylib
LIB=libHSsimplex-chat-*-inplace-ghc*.$LIB_EXT
GHC_LIBS_DIR=$(ghc --print-libdir)
@@ -12,13 +15,26 @@ GHC_LIBS_DIR=$(ghc --print-libdir)
BUILD_DIR=dist-newstyle/build/$ARCH-*/ghc-*/simplex-chat-*
rm -rf $BUILD_DIR
cabal build lib:simplex-chat lib:simplex-chat --ghc-options="-optl-Wl,-rpath,@loader_path -optl-Wl,-L$GHC_LIBS_DIR/rts -optl-lHSrts_thr-ghc8.10.7 -optl-lffi"
cabal build lib:simplex-chat lib:simplex-chat --ghc-options="-optl-Wl,-rpath,@loader_path -optl-Wl,-L$GHC_LIBS_DIR/$ARCH-osx-ghc-$GHC_VERSION -optl-lHSrts_thr-ghc$GHC_VERSION -optl-lffi"
cd $BUILD_DIR/build
mkdir deps 2> /dev/null
# It's not included by default for some reason. Compiled lib tries to find system one but it's not always available
cp $GHC_LIBS_DIR/rts/libffi.dylib ./deps
#cp $GHC_LIBS_DIR/libffi.dylib ./deps
(
BUILD=$PWD
cp /tmp/libffi-3.4.4/*-apple-darwin*/.libs/libffi.dylib $BUILD/deps || \
( \
cd /tmp && \
curl "https://gitlab.haskell.org/ghc/libffi-tarballs/-/raw/libffi-3.4.4/libffi-3.4.4.tar.gz?inline=false" -o libffi.tar.gz && \
tar -xzvf libffi.tar.gz && \
cd "libffi-3.4.4" && \
./configure && \
make && \
cp *-apple-darwin*/.libs/libffi.dylib $BUILD/deps \
)
)
DYLIBS=`otool -L $LIB | grep @rpath | tail -n +2 | cut -d' ' -f 1 | cut -d'/' -f2`
RPATHS=`otool -l $LIB | grep "path "| cut -d' ' -f11`
@@ -59,11 +75,13 @@ function copy_deps() {
}
copy_deps $LIB
# Special case
cp $(ghc --print-libdir)/$ARCH-osx-ghc-$GHC_VERSION/libHSghc-boot-th-$GHC_VERSION-ghc$GHC_VERSION.dylib deps
rm deps/`basename $LIB`
if [ -e deps/libHSdrct-*.$LIB_EXT ]; then
LIBCRYPTO_PATH=$(otool -l deps/libHSdrct-*.$LIB_EXT | grep libcrypto | cut -d' ' -f11)
install_name_tool -change $LIBCRYPTO_PATH @rpath/libcrypto.1.1.$LIB_EXT deps/libHSdrct*.$LIB_EXT
install_name_tool -change $LIBCRYPTO_PATH @rpath/libcrypto.1.1.$LIB_EXT deps/libHSdrct-*.$LIB_EXT
cp $LIBCRYPTO_PATH deps/libcrypto.1.1.$LIB_EXT
chmod 755 deps/libcrypto.1.1.$LIB_EXT
fi

View File

@@ -1,10 +1,11 @@
{
"https://github.com/simplex-chat/simplexmq.git"."53c793d5590d3c781aa3fbf72993eee262c7aa83" = "0f0ldlgqwrapgfw5gnaj00xvb14c8nykyjr9fhy79h4r16g614x8";
"https://github.com/simplex-chat/simplexmq.git"."8d47f690838371bc848e4b31a4b09ef6bf67ccc5" = "1pwasv22ii3wy4xchaknlwczmy5ws7adx7gg2g58lxzrgdjm3650";
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
"https://github.com/kazu-yamamoto/http2.git"."b5a1b7200cf5bc7044af34ba325284271f6dff25" = "0dqb50j57an64nf4qcf5vcz4xkd1vzvghvf8bk529c1k30r9nfzb";
"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/aeson.git"."3eb66f9a68f103b5f1489382aad89f5712a64db7" = "0kilkx59fl6c3qy3kjczqvm8c3f4n3p0bdk9biyflf51ljnzp4yp";
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "0kiwhvml42g9anw4d2v0zd1fpc790pj9syg5x3ik4l97fnkbbwpp";
"https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";
"https://github.com/simplex-chat/aeson.git"."68330dce8208173c6acf5f62b23acb500ab5d873" = "1l51p1v54c88c1jmxcvbz4gy0cns7l46ihzzfjwxxrvcrrrxgcjp";
"https://github.com/simplex-chat/haskell-terminal.git"."f708b00009b54890172068f168bf98508ffcd495" = "0zmq7lmfsk8m340g47g5963yba7i88n4afa6z93sg9px5jv1mijj";
"https://github.com/zw3rk/android-support.git"."3c3a5ab0b8b137a072c98d3d0937cbdc96918ddb" = "1r6jyxbim3dsvrmakqfyxbd6ms6miaghpbwyl0sr6dzwpgaprz97";
"https://github.com/simplex-chat/android-support.git"."9aa09f148089d6752ce563b14c2df1895718d806" = "0pbf2pf13v2kjzi397nr13f1h3jv0imvsq8rpiyy2qyx5vd50pqn";
"https://github.com/simplex-chat/network-transport.git"."0013798272a683e35ca38d2fdaf480942311fba8" = "0dnn62apgvc248df0m8ib7phrzn63wm0xs71xvlypv52j6cgwzkb";
}

View File

@@ -5,7 +5,7 @@ cabal-version: 1.12
-- see: https://github.com/sol/hpack
name: simplex-chat
version: 5.3.0.10
version: 5.3.1.0
category: Web, System, Services, Cryptography
homepage: https://github.com/simplex-chat/simplex-chat#readme
author: simplex.chat
@@ -145,25 +145,25 @@ library
src
ghc-options: -Wall -Wcompat -Werror=incomplete-patterns -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns -Wunused-type-patterns
build-depends:
aeson ==2.0.*
aeson ==2.2.*
, ansi-terminal >=0.10 && <0.12
, async ==2.2.*
, attoparsec ==0.14.*
, base >=4.7 && <5
, base64-bytestring >=1.0 && <1.3
, bytestring ==0.10.*
, bytestring ==0.11.*
, composition ==1.0.*
, constraints >=0.12 && <0.14
, containers ==0.6.*
, cryptonite >=0.27 && <0.30
, cryptonite ==0.30.*
, direct-sqlcipher ==2.3.*
, directory ==1.3.*
, email-validate ==2.3.*
, exceptions ==0.10.*
, filepath ==1.4.*
, http-types ==0.12.*
, memory ==0.15.*
, mtl ==2.2.*
, memory ==0.18.*
, mtl ==2.3.*
, network >=3.1.2.7 && <3.2
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
@@ -174,13 +174,13 @@ library
, socks ==0.6.*
, sqlcipher-simple ==0.4.*
, stm ==2.5.*
, template-haskell ==2.16.*
, template-haskell ==2.20.*
, terminal ==0.2.*
, text ==1.2.*
, text ==2.0.*
, time ==1.9.*
, unliftio ==0.2.*
, unliftio-core ==0.2.*
, zip ==1.7.*
, zip ==2.0.*
default-language: Haskell2010
if flag(swift)
cpp-options: -DswiftJSON
@@ -193,25 +193,25 @@ executable simplex-bot
apps/simplex-bot
ghc-options: -Wall -Wcompat -Werror=incomplete-patterns -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns -Wunused-type-patterns -threaded
build-depends:
aeson ==2.0.*
aeson ==2.2.*
, ansi-terminal >=0.10 && <0.12
, async ==2.2.*
, attoparsec ==0.14.*
, base >=4.7 && <5
, base64-bytestring >=1.0 && <1.3
, bytestring ==0.10.*
, bytestring ==0.11.*
, composition ==1.0.*
, constraints >=0.12 && <0.14
, containers ==0.6.*
, cryptonite >=0.27 && <0.30
, cryptonite ==0.30.*
, direct-sqlcipher ==2.3.*
, directory ==1.3.*
, email-validate ==2.3.*
, exceptions ==0.10.*
, filepath ==1.4.*
, http-types ==0.12.*
, memory ==0.15.*
, mtl ==2.2.*
, memory ==0.18.*
, mtl ==2.3.*
, network >=3.1.2.7 && <3.2
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
@@ -223,13 +223,13 @@ executable simplex-bot
, socks ==0.6.*
, sqlcipher-simple ==0.4.*
, stm ==2.5.*
, template-haskell ==2.16.*
, template-haskell ==2.20.*
, terminal ==0.2.*
, text ==1.2.*
, text ==2.0.*
, time ==1.9.*
, unliftio ==0.2.*
, unliftio-core ==0.2.*
, zip ==1.7.*
, zip ==2.0.*
default-language: Haskell2010
if flag(swift)
cpp-options: -DswiftJSON
@@ -242,25 +242,25 @@ executable simplex-bot-advanced
apps/simplex-bot-advanced
ghc-options: -Wall -Wcompat -Werror=incomplete-patterns -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns -Wunused-type-patterns -threaded
build-depends:
aeson ==2.0.*
aeson ==2.2.*
, ansi-terminal >=0.10 && <0.12
, async ==2.2.*
, attoparsec ==0.14.*
, base >=4.7 && <5
, base64-bytestring >=1.0 && <1.3
, bytestring ==0.10.*
, bytestring ==0.11.*
, composition ==1.0.*
, constraints >=0.12 && <0.14
, containers ==0.6.*
, cryptonite >=0.27 && <0.30
, cryptonite ==0.30.*
, direct-sqlcipher ==2.3.*
, directory ==1.3.*
, email-validate ==2.3.*
, exceptions ==0.10.*
, filepath ==1.4.*
, http-types ==0.12.*
, memory ==0.15.*
, mtl ==2.2.*
, memory ==0.18.*
, mtl ==2.3.*
, network >=3.1.2.7 && <3.2
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
@@ -272,13 +272,13 @@ executable simplex-bot-advanced
, socks ==0.6.*
, sqlcipher-simple ==0.4.*
, stm ==2.5.*
, template-haskell ==2.16.*
, template-haskell ==2.20.*
, terminal ==0.2.*
, text ==1.2.*
, text ==2.0.*
, time ==1.9.*
, unliftio ==0.2.*
, unliftio-core ==0.2.*
, zip ==1.7.*
, zip ==2.0.*
default-language: Haskell2010
if flag(swift)
cpp-options: -DswiftJSON
@@ -293,25 +293,25 @@ executable simplex-broadcast-bot
apps/simplex-broadcast-bot/src
ghc-options: -Wall -Wcompat -Werror=incomplete-patterns -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns -Wunused-type-patterns -threaded
build-depends:
aeson ==2.0.*
aeson ==2.2.*
, ansi-terminal >=0.10 && <0.12
, async ==2.2.*
, attoparsec ==0.14.*
, base >=4.7 && <5
, base64-bytestring >=1.0 && <1.3
, bytestring ==0.10.*
, bytestring ==0.11.*
, composition ==1.0.*
, constraints >=0.12 && <0.14
, containers ==0.6.*
, cryptonite >=0.27 && <0.30
, cryptonite ==0.30.*
, direct-sqlcipher ==2.3.*
, directory ==1.3.*
, email-validate ==2.3.*
, exceptions ==0.10.*
, filepath ==1.4.*
, http-types ==0.12.*
, memory ==0.15.*
, mtl ==2.2.*
, memory ==0.18.*
, mtl ==2.3.*
, network >=3.1.2.7 && <3.2
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
@@ -323,13 +323,13 @@ executable simplex-broadcast-bot
, socks ==0.6.*
, sqlcipher-simple ==0.4.*
, stm ==2.5.*
, template-haskell ==2.16.*
, template-haskell ==2.20.*
, terminal ==0.2.*
, text ==1.2.*
, text ==2.0.*
, time ==1.9.*
, unliftio ==0.2.*
, unliftio-core ==0.2.*
, zip ==1.7.*
, zip ==2.0.*
default-language: Haskell2010
if flag(swift)
cpp-options: -DswiftJSON
@@ -343,25 +343,25 @@ executable simplex-chat
apps/simplex-chat
ghc-options: -Wall -Wcompat -Werror=incomplete-patterns -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns -Wunused-type-patterns -threaded
build-depends:
aeson ==2.0.*
aeson ==2.2.*
, ansi-terminal >=0.10 && <0.12
, async ==2.2.*
, attoparsec ==0.14.*
, base >=4.7 && <5
, base64-bytestring >=1.0 && <1.3
, bytestring ==0.10.*
, bytestring ==0.11.*
, composition ==1.0.*
, constraints >=0.12 && <0.14
, containers ==0.6.*
, cryptonite >=0.27 && <0.30
, cryptonite ==0.30.*
, direct-sqlcipher ==2.3.*
, directory ==1.3.*
, email-validate ==2.3.*
, exceptions ==0.10.*
, filepath ==1.4.*
, http-types ==0.12.*
, memory ==0.15.*
, mtl ==2.2.*
, memory ==0.18.*
, mtl ==2.3.*
, network ==3.1.*
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
@@ -373,14 +373,14 @@ executable simplex-chat
, socks ==0.6.*
, sqlcipher-simple ==0.4.*
, stm ==2.5.*
, template-haskell ==2.16.*
, template-haskell ==2.20.*
, terminal ==0.2.*
, text ==1.2.*
, text ==2.0.*
, time ==1.9.*
, unliftio ==0.2.*
, unliftio-core ==0.2.*
, websockets ==0.12.*
, zip ==1.7.*
, zip ==2.0.*
default-language: Haskell2010
if flag(swift)
cpp-options: -DswiftJSON
@@ -397,25 +397,25 @@ executable simplex-directory-service
apps/simplex-directory-service/src
ghc-options: -Wall -Wcompat -Werror=incomplete-patterns -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns -Wunused-type-patterns -threaded
build-depends:
aeson ==2.0.*
aeson ==2.2.*
, ansi-terminal >=0.10 && <0.12
, async ==2.2.*
, attoparsec ==0.14.*
, base >=4.7 && <5
, base64-bytestring >=1.0 && <1.3
, bytestring ==0.10.*
, bytestring ==0.11.*
, composition ==1.0.*
, constraints >=0.12 && <0.14
, containers ==0.6.*
, cryptonite >=0.27 && <0.30
, cryptonite ==0.30.*
, direct-sqlcipher ==2.3.*
, directory ==1.3.*
, email-validate ==2.3.*
, exceptions ==0.10.*
, filepath ==1.4.*
, http-types ==0.12.*
, memory ==0.15.*
, mtl ==2.2.*
, memory ==0.18.*
, mtl ==2.3.*
, network >=3.1.2.7 && <3.2
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
@@ -427,13 +427,13 @@ executable simplex-directory-service
, socks ==0.6.*
, sqlcipher-simple ==0.4.*
, stm ==2.5.*
, template-haskell ==2.16.*
, template-haskell ==2.20.*
, terminal ==0.2.*
, text ==1.2.*
, text ==2.0.*
, time ==1.9.*
, unliftio ==0.2.*
, unliftio-core ==0.2.*
, zip ==1.7.*
, zip ==2.0.*
default-language: Haskell2010
if flag(swift)
cpp-options: -DswiftJSON
@@ -470,27 +470,27 @@ test-suite simplex-chat-test
apps/simplex-directory-service/src
ghc-options: -Wall -Wcompat -Werror=incomplete-patterns -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns -Wunused-type-patterns -threaded
build-depends:
aeson ==2.0.*
aeson ==2.2.*
, ansi-terminal >=0.10 && <0.12
, async ==2.2.*
, attoparsec ==0.14.*
, base >=4.7 && <5
, base64-bytestring >=1.0 && <1.3
, bytestring ==0.10.*
, bytestring ==0.11.*
, composition ==1.0.*
, constraints >=0.12 && <0.14
, containers ==0.6.*
, cryptonite >=0.27 && <0.30
, cryptonite ==0.30.*
, deepseq ==1.4.*
, direct-sqlcipher ==2.3.*
, directory ==1.3.*
, email-validate ==2.3.*
, exceptions ==0.10.*
, filepath ==1.4.*
, hspec ==2.7.*
, hspec ==2.11.*
, http-types ==0.12.*
, memory ==0.15.*
, mtl ==2.2.*
, memory ==0.18.*
, mtl ==2.3.*
, network ==3.1.*
, optparse-applicative >=0.15 && <0.17
, process ==1.6.*
@@ -503,13 +503,13 @@ test-suite simplex-chat-test
, socks ==0.6.*
, sqlcipher-simple ==0.4.*
, stm ==2.5.*
, template-haskell ==2.16.*
, template-haskell ==2.20.*
, terminal ==0.2.*
, text ==1.2.*
, text ==2.0.*
, time ==1.9.*
, unliftio ==0.2.*
, unliftio-core ==0.2.*
, zip ==1.7.*
, zip ==2.0.*
default-language: Haskell2010
if flag(swift)
cpp-options: -DswiftJSON

View File

@@ -5,6 +5,7 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
@@ -12,12 +13,15 @@
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
module Simplex.Chat where
import Control.Applicative (optional, (<|>))
import Control.Concurrent.STM (retry, stateTVar)
import Control.Concurrent.STM (retry)
import qualified Control.Exception as E
import Control.Logger.Simple
import Control.Monad
import Control.Monad.Except
import Control.Monad.IO.Unlift
import Control.Monad.Reader
@@ -213,8 +217,8 @@ newChatController ChatDatabase {chatStore, agentStore} user cfg@ChatConfig {agen
where
configServers :: DefaultAgentServers
configServers =
let smp' = fromMaybe (smp (defaultServers :: DefaultAgentServers)) (nonEmpty smpServers)
xftp' = fromMaybe (xftp (defaultServers :: DefaultAgentServers)) (nonEmpty xftpServers)
let smp' = fromMaybe (defaultServers.smp) (nonEmpty smpServers)
xftp' = fromMaybe (defaultServers.xftp) (nonEmpty xftpServers)
in defaultServers {smp = smp', xftp = xftp', netCfg = networkConfig}
agentServers :: ChatConfig -> IO InitialAgentServers
agentServers config@ChatConfig {defaultServers = defServers@DefaultAgentServers {ntf, netCfg}} = do
@@ -241,9 +245,9 @@ activeAgentServers ChatConfig {defaultServers} p =
. filter (\ServerCfg {enabled} -> enabled)
cfgServers :: UserProtocol p => SProtocolType p -> (DefaultAgentServers -> NonEmpty (ProtoServerWithAuth p))
cfgServers = \case
SPSMP -> smp
SPXFTP -> xftp
cfgServers p s = case p of
SPSMP -> s.smp
SPXFTP -> s.xftp
startChatController :: forall m. ChatMonad' m => Bool -> Bool -> Bool -> m (Async ())
startChatController subConns enableExpireCIs startXFTPWorkers = do
@@ -699,7 +703,9 @@ processChatCommand = \case
MCVoice {} -> False
MCUnknown {} -> True
qText = msgContentText qmc
qFileName = maybe qText (T.pack . (fileName :: CIFile d -> String)) ciFile_
getFileName :: CIFile d -> String
getFileName CIFile{fileName} = fileName
qFileName = maybe qText (T.pack . getFileName) ciFile_
qTextOrFile = if T.null qText then qFileName else qText
xftpSndFileTransfer :: User -> CryptoFile -> Integer -> Int -> ContactOrGroup -> m (FileInvitation, CIFile 'MDSnd, FileTransferMeta)
xftpSndFileTransfer user file@(CryptoFile filePath cfArgs) fileSize n contactOrGroup = do
@@ -912,7 +918,7 @@ processChatCommand = \case
pure $ CRContactConnectionDeleted user conn
CTGroup -> do
Group gInfo@GroupInfo {membership} members <- withStore $ \db -> getGroup db user chatId
let isOwner = memberRole (membership :: GroupMember) == GROwner
let isOwner = membership.memberRole == GROwner
canDelete = isOwner || not (memberCurrent membership)
unless canDelete $ throwChatError $ CEGroupUserRole gInfo GROwner
filesInfo <- withStore' $ \db -> getGroupFileInfo db user gInfo
@@ -1089,7 +1095,9 @@ processChatCommand = \case
APIGetNtfMessage nonce encNtfInfo -> withUser $ \_ -> do
(NotificationInfo {ntfConnId, ntfMsgMeta}, msgs) <- withAgent $ \a -> getNotificationMessage a nonce encNtfInfo
let ntfMessages = map (\SMP.SMPMsgMeta {msgTs, msgFlags} -> NtfMsgInfo {msgTs = systemToUTCTime msgTs, msgFlags}) msgs
msgTs' = systemToUTCTime . (SMP.msgTs :: SMP.NMsgMeta -> SystemTime) <$> ntfMsgMeta
getMsgTs :: SMP.NMsgMeta -> SystemTime
getMsgTs SMP.NMsgMeta{msgTs} = msgTs
msgTs' = systemToUTCTime . getMsgTs <$> ntfMsgMeta
agentConnId = AgentConnId ntfConnId
user_ <- withStore' (`getUserByAConnId` agentConnId)
connEntity <-
@@ -1496,7 +1504,7 @@ processChatCommand = \case
Contact {activeConn = Connection {peerChatVRange}} = ct
withChatLock "joinGroup" . procCmd $ do
subMode <- chatReadVar subscriptionMode
dm <- directMessage $ XGrpAcpt (memberId (membership :: GroupMember))
dm <- directMessage $ XGrpAcpt membership.memberId
agentConnId <- withAgent $ \a -> joinConnection a (aUserId user) True connRequest dm subMode
withStore' $ \db -> do
createMemberConnection db userId fromMember agentConnId (fromJVersionRange peerChatVRange) subMode
@@ -1650,7 +1658,7 @@ processChatCommand = \case
case memberConn m of
Just mConn -> do
let msg = XGrpDirectInv cReq msgContent_
(sndMsg, _) <- sendDirectMessage mConn msg (GroupId $ groupId (g :: GroupInfo))
(sndMsg, _) <- sendDirectMessage mConn msg (GroupId $ g.groupId)
withStore' $ \db -> setContactGrpInvSent db ct True
let ct' = ct {contactGrpInvSent = True}
forM_ msgContent_ $ \mc -> do
@@ -2014,7 +2022,7 @@ processChatCommand = \case
pure $ CRGroupUpdated user g g' Nothing
assertUserGroupRole :: GroupInfo -> GroupMemberRole -> m ()
assertUserGroupRole g@GroupInfo {membership} requiredRole = do
when (memberRole (membership :: GroupMember) < requiredRole) $ throwChatError $ CEGroupUserRole g requiredRole
when (membership.memberRole < requiredRole) $ throwChatError $ CEGroupUserRole g requiredRole
when (memberStatus membership == GSMemInvited) $ throwChatError (CEGroupNotJoined g)
when (memberRemoved membership) $ throwChatError CEGroupMemberUserRemoved
unless (memberActive membership) $ throwChatError CEGroupMemberNotActive
@@ -2032,7 +2040,7 @@ processChatCommand = \case
runUpdateGroupProfile user g $ update p
isReady :: Contact -> Bool
isReady ct =
let s = connStatus $ activeConn (ct :: Contact)
let s = connStatus $ ct.activeConn
in s == ConnReady || s == ConnSndReady
withCurrentCall :: ContactId -> (User -> Contact -> Call -> m (Maybe Call)) -> m ChatResponse
withCurrentCall ctId action = do
@@ -3214,7 +3222,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do
| sameMemberId memId m -> do
-- TODO update member profile
-- [async agent commands] no continuation needed, but command should be asynchronous for stability
allowAgentConnectionAsync user conn' confId $ XGrpMemInfo (memberId (membership :: GroupMember)) (fromLocalProfile $ memberProfile membership)
allowAgentConnectionAsync user conn' confId $ XGrpMemInfo membership.memberId (fromLocalProfile $ memberProfile membership)
| otherwise -> messageError "x.grp.mem.info: memberId is different from expected"
_ -> messageError "CONF from member must have x.grp.mem.info"
INFO connInfo -> do
@@ -3253,7 +3261,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do
toView $ CRJoinedGroupMember user gInfo m {memberStatus = GSMemConnected}
whenGroupNtfs user gInfo $ do
setActive $ ActiveG gName
showToast ("#" <> gName) $ "member " <> localDisplayName (m :: GroupMember) <> " is connected"
showToast ("#" <> gName) $ "member " <> m.localDisplayName <> " is connected"
intros <- withStore' $ \db -> createIntroductions db members m
void . sendGroupMessage user gInfo members . XGrpMemNew $ memberInfo m
forM_ intros $ \intro ->
@@ -3313,8 +3321,9 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do
&& hasDeliveryReceipt (toCMEventTag event)
&& currentMemCount <= smallGroupsRcptsMemLimit
where
canSend :: GroupMember -> m () -> m ()
canSend mem a
| memberRole (mem :: GroupMember) <= GRObserver = messageError "member is not allowed to send messages"
| mem.memberRole <= GRObserver = messageError "member is not allowed to send messages"
| otherwise = a
RCVD msgMeta msgRcpt ->
withAckMessage' agentConnId conn msgMeta $
@@ -4511,7 +4520,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do
withStore' $ \db -> saveMemberInvitation db toMember introInv
subMode <- chatReadVar subscriptionMode
-- [incognito] send membership incognito profile, create direct connection as incognito
dm <- directMessage $ XGrpMemInfo (memberId (membership :: GroupMember)) (fromLocalProfile $ memberProfile membership)
dm <- directMessage $ XGrpMemInfo membership.memberId (fromLocalProfile $ memberProfile membership)
-- [async agent commands] no continuation needed, but commands should be asynchronous for stability
groupConnIds <- joinAgentConnectionAsync user enableNtfs groupConnReq dm subMode
directConnIds <- forM directConnReq $ \dcr -> joinAgentConnectionAsync user enableNtfs dcr dm subMode
@@ -4521,7 +4530,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do
xGrpMemRole :: GroupInfo -> GroupMember -> MemberId -> GroupMemberRole -> RcvMessage -> MsgMeta -> m ()
xGrpMemRole gInfo@GroupInfo {membership} m@GroupMember {memberRole = senderRole} memId memRole msg msgMeta
| memberId (membership :: GroupMember) == memId =
| membership.memberId == memId =
let gInfo' = gInfo {membership = membership {memberRole = memRole}}
in changeMemberRole gInfo' membership $ RGEUserRole memRole
| otherwise = do
@@ -4545,7 +4554,7 @@ processAgentMessageConn user@User {userId} corrId agentConnId agentMessage = do
xGrpMemDel :: GroupInfo -> GroupMember -> MemberId -> RcvMessage -> MsgMeta -> m ()
xGrpMemDel gInfo@GroupInfo {membership} m@GroupMember {memberRole = senderRole} memId msg msgMeta = do
members <- withStore' $ \db -> getGroupMembers db user gInfo
if memberId (membership :: GroupMember) == memId
if membership.memberId == memId
then checkRole membership $ do
deleteGroupLinkIfExists user gInfo
-- member records are not deleted to keep history
@@ -4787,7 +4796,7 @@ parseFileChunk :: ChatMonad m => ByteString -> m FileChunk
parseFileChunk = liftEither . first (ChatError . CEFileRcvChunk) . smpDecode
appendFileChunk :: forall m. ChatMonad m => RcvFileTransfer -> Integer -> ByteString -> Bool -> m ()
appendFileChunk ft@RcvFileTransfer {fileId, fileInvitation, fileStatus, cryptoArgs} chunkNo chunk final =
appendFileChunk ft@RcvFileTransfer {fileId, fileStatus, cryptoArgs} chunkNo chunk final =
case fileStatus of
RFSConnected RcvFileInfo {filePath} -> append_ filePath
-- sometimes update of file transfer status to FSConnected
@@ -4805,7 +4814,7 @@ appendFileChunk ft@RcvFileTransfer {fileId, fileInvitation, fileStatus, cryptoAr
when final $ do
closeFileHandle fileId rcvFiles
forM_ cryptoArgs $ \cfArgs -> do
tmpFile <- getChatTempDirectory >>= (`uniqueCombine` fileName (fileInvitation :: FileInvitation))
tmpFile <- getChatTempDirectory >>= (`uniqueCombine` ft.fileInvitation.fileName)
tryChatError (liftError encryptErr $ encryptFile fsFilePath tmpFile cfArgs) >>= \case
Right () -> do
removeFile fsFilePath `catchChatError` \_ -> pure ()
@@ -5144,7 +5153,7 @@ createSndFeatureItems :: forall m. ChatMonad m => User -> Contact -> Contact ->
createSndFeatureItems user ct ct' =
createFeatureItems user ct ct' CDDirectSnd CISndChatFeature CISndChatPreference getPref
where
getPref = (preference :: ContactUserPref (FeaturePreference f) -> FeaturePreference f) . userPreference
getPref u = (userPreference u).preference
type FeatureContent a d = ChatFeature -> a -> Maybe Int -> CIContent d
@@ -5229,7 +5238,7 @@ getCreateActiveUser st testView = do
Right user -> pure user
selectUser :: [User] -> IO User
selectUser [user] = do
withTransaction st (`setActiveUser` userId (user :: User))
withTransaction st (`setActiveUser` user.userId)
pure user
selectUser users = do
putStrLn "Select user profile:"
@@ -5244,7 +5253,7 @@ getCreateActiveUser st testView = do
| n <= 0 || n > length users -> putStrLn "invalid user number" >> loop
| otherwise -> do
let user = users !! (n - 1)
withTransaction st (`setActiveUser` userId (user :: User))
withTransaction st (`setActiveUser` user.userId)
pure user
userStr :: User -> String
userStr User {localDisplayName, profile = LocalProfile {fullName}} =

View File

@@ -13,6 +13,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 (($>))

View File

@@ -8,7 +8,7 @@ module Simplex.Chat.Bot where
import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Monad.Reader
import Control.Monad
import qualified Data.ByteString.Char8 as B
import qualified Data.Text as T
import Simplex.Chat.Controller

View File

@@ -6,11 +6,14 @@
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
module Simplex.Chat.Messages where
import Control.Applicative ((<|>))
@@ -373,7 +376,7 @@ contactTimedTTL Contact {mergedPreferences = ContactUserPreferences {timedMessag
| forUser enabled && forContact enabled = Just ttl
| otherwise = Nothing
where
TimedMessagesPreference {ttl} = preference (userPreference :: ContactUserPref TimedMessagesPreference)
TimedMessagesPreference {ttl} = userPreference.preference
groupTimedTTL :: GroupInfo -> Maybe (Maybe Int)
groupTimedTTL GroupInfo {fullGroupPreferences = FullGroupPreferences {timedMessages = TimedMessagesGroupPreference {enable, ttl}}}

View File

@@ -16,7 +16,9 @@ module Simplex.Chat.Mobile.File
)
where
import Control.Monad
import Control.Monad.Except
import Control.Monad.IO.Class
import Data.Aeson (ToJSON)
import qualified Data.Aeson as J
import Data.ByteString (ByteString)

View File

@@ -16,12 +16,12 @@ type JSONByteString = LB.ByteString
getByteString :: Ptr Word8 -> CInt -> IO ByteString
getByteString ptr len = do
fp <- newForeignPtr_ ptr
pure $ PS fp 0 $ fromIntegral len
pure $ BS fp $ fromIntegral len
{-# INLINE getByteString #-}
putByteString :: Ptr Word8 -> ByteString -> IO ()
putByteString ptr (PS fp offset len) =
withForeignPtr fp $ \p -> memcpy ptr (p `plusPtr` offset) len
putByteString ptr (BS fp len) =
withForeignPtr fp $ \p -> memcpy ptr p len
{-# INLINE putByteString #-}
putLazyByteString :: Ptr Word8 -> LB.ByteString -> IO ()

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

View File

@@ -13,6 +13,8 @@
{-# LANGUAGE StrictData #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
module Simplex.Chat.Protocol where
import Control.Applicative ((<|>))

View File

@@ -5,6 +5,8 @@
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
module Simplex.Chat.Store.Connections
( getConnectionEntity,
getConnectionsToSubscribe,
@@ -13,6 +15,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)

View File

@@ -1,4 +1,5 @@
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
@@ -7,6 +8,8 @@
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
module Simplex.Chat.Store.Direct
( updateContact_,
updateContactProfile_,
@@ -61,7 +64,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)
@@ -456,7 +461,7 @@ createOrUpdateContactRequest db user@User {userId} userContactLinkId invId (Vers
ExceptT $
maybeM getContactRequestByXContactId xContactId_ >>= \case
Nothing -> createContactRequest
Just cr -> updateContactRequest cr $> Right (contactRequestId (cr :: UserContactRequest))
Just cr -> updateContactRequest cr $> Right cr.contactRequestId
getContactRequest db user cReqId
createContactRequest :: IO (Either StoreError Int64)
createContactRequest = do

View File

@@ -76,7 +76,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)
@@ -484,7 +486,7 @@ createRcvFileTransfer :: DB.Connection -> UserId -> Contact -> FileInvitation ->
createRcvFileTransfer db userId Contact {contactId, localDisplayName = c} f@FileInvitation {fileName, fileSize, fileConnReq, fileInline, fileDescr} rcvFileInline chunkSize = do
currentTs <- liftIO getCurrentTime
rfd_ <- mapM (createRcvFD_ db userId currentTs) fileDescr
let rfdId = (fileDescrId :: RcvFileDescr -> Int64) <$> rfd_
let rfdId = (\RcvFileDescr {fileDescrId} -> fileDescrId) <$> rfd_
-- cryptoArgs = Nothing here, the decision to encrypt is made when receiving it
xftpRcvFile = (\rfd -> XFTPRcvFile {rcvFileDescription = rfd, agentRcvFileId = Nothing, agentRcvFileDeleted = False}) <$> rfd_
fileProtocol = if isJust rfd_ then FPXFTP else FPSMP
@@ -505,7 +507,7 @@ createRcvGroupFileTransfer :: DB.Connection -> UserId -> GroupMember -> FileInvi
createRcvGroupFileTransfer db userId GroupMember {groupId, groupMemberId, localDisplayName = c} f@FileInvitation {fileName, fileSize, fileConnReq, fileInline, fileDescr} rcvFileInline chunkSize = do
currentTs <- liftIO getCurrentTime
rfd_ <- mapM (createRcvFD_ db userId currentTs) fileDescr
let rfdId = (fileDescrId :: RcvFileDescr -> Int64) <$> rfd_
let rfdId = (\RcvFileDescr {fileDescrId} -> fileDescrId) <$> rfd_
-- cryptoArgs = Nothing here, the decision to encrypt is made when receiving it
xftpRcvFile = (\rfd -> XFTPRcvFile {rcvFileDescription = rfd, agentRcvFileId = Nothing, agentRcvFileDeleted = False}) <$> rfd_
fileProtocol = if isJust rfd_ then FPXFTP else FPSMP

View File

@@ -8,6 +8,9 @@
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
module Simplex.Chat.Store.Groups
( -- * Util methods
@@ -95,7 +98,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)
@@ -885,7 +890,7 @@ saveIntroInvitation db reMember toMember introInv = do
WHERE group_member_intro_id = :intro_id
|]
[ ":intro_status" := GMIntroInvReceived,
":group_queue_info" := groupConnReq (introInv :: IntroInvitation),
":group_queue_info" := introInv.groupConnReq,
":direct_queue_info" := directConnReq introInv,
":updated_at" := currentTs,
":intro_id" := introId intro
@@ -933,7 +938,7 @@ getIntroduction_ db reMember toMember = ExceptT $ do
createIntroReMember :: DB.Connection -> User -> GroupInfo -> GroupMember -> MemberInfo -> (CommandId, ConnId) -> Maybe (CommandId, ConnId) -> Maybe ProfileId -> SubscriptionMode -> ExceptT StoreError IO GroupMember
createIntroReMember db user@User {userId} gInfo@GroupInfo {groupId} _host@GroupMember {memberContactId, activeConn} memInfo@(MemberInfo _ _ memberChatVRange memberProfile) (groupCmdId, groupAgentConnId) directConnIds customUserProfileId subMode = do
let mcvr = maybe chatInitialVRange fromChatVRange memberChatVRange
cLevel = 1 + maybe 0 (connLevel :: Connection -> Int) activeConn
cLevel = 1 + maybe 0 (\Connection {connLevel} -> connLevel) activeConn
currentTs <- liftIO getCurrentTime
newMember <- case directConnIds of
Just (directCmdId, directAgentConnId) -> do
@@ -952,7 +957,7 @@ createIntroReMember db user@User {userId} gInfo@GroupInfo {groupId} _host@GroupM
createIntroToMemberContact :: DB.Connection -> User -> GroupMember -> GroupMember -> VersionRange -> (CommandId, ConnId) -> Maybe (CommandId, ConnId) -> Maybe ProfileId -> SubscriptionMode -> IO ()
createIntroToMemberContact db user@User {userId} GroupMember {memberContactId = viaContactId, activeConn} _to@GroupMember {groupMemberId, localDisplayName} mcvr (groupCmdId, groupAgentConnId) directConnIds customUserProfileId subMode = do
let cLevel = 1 + maybe 0 (connLevel :: Connection -> Int) activeConn
let cLevel = 1 + maybe 0 (\Connection {connLevel} -> connLevel) activeConn
currentTs <- getCurrentTime
Connection {connId = groupConnId} <- createMemberConnection_ db userId groupMemberId groupAgentConnId mcvr viaContactId cLevel currentTs subMode
setCommandConnId db user groupCmdId groupConnId
@@ -1553,7 +1558,7 @@ createMemberContactInvited
contactId <- createContactUpdateMember currentTs userPreferences
ctConn <- createMemberContactConn_ db user connIds gInfo mConn contactId subMode
let mergedPreferences = contactUserPreferences user userPreferences preferences $ connIncognito ctConn
mCt' = Contact {contactId, localDisplayName = memberLDN, profile = memberProfile, activeConn = ctConn, viaGroup = Nothing, contactUsed = True, chatSettings = defaultChatSettings, userPreferences, mergedPreferences, createdAt = currentTs, updatedAt = currentTs, chatTs = Just currentTs, contactGroupMemberId = Just groupMemberId, contactGrpInvSent = False}
mCt' = Contact {contactId, localDisplayName = memberLDN, profile = memberProfile, activeConn = ctConn, viaGroup = Nothing, contactUsed = True, chatSettings = defaultChatSettings, userPreferences, mergedPreferences, createdAt = currentTs, updatedAt = currentTs, chatTs = Just currentTs, contactGroupMemberId = Nothing, contactGrpInvSent = False}
m' = m {memberContactId = Just contactId}
pure (mCt', m')
where

View File

@@ -10,6 +10,8 @@
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
module Simplex.Chat.Store.Messages
( getContactConnIds_,
getDirectChatReactions_,
@@ -96,7 +98,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)

View File

@@ -7,6 +7,8 @@
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
module Simplex.Chat.Store.Profiles
( AutoAccept (..),
UserMsgReceiptSettings (..),
@@ -54,7 +56,9 @@ module Simplex.Chat.Store.Profiles
)
where
import Control.Monad
import Control.Monad.Except
import Control.Monad.IO.Class
import Data.Aeson (ToJSON)
import qualified Data.Aeson as J
import Data.Functor (($>))
@@ -290,7 +294,7 @@ getUserContactProfiles db User {userId} =
|]
(Only userId)
where
toContactProfile :: (ContactName, Text, Maybe ImageData, Maybe ConnReqContact, Maybe Preferences) -> (Profile)
toContactProfile :: (ContactName, Text, Maybe ImageData, Maybe ConnReqContact, Maybe Preferences) -> Profile
toContactProfile (displayName, fullName, image, contactLink, preferences) = Profile {displayName, fullName, image, contactLink, preferences}
createUserContactLink :: DB.Connection -> User -> ConnId -> ConnReqContact -> SubscriptionMode -> ExceptT StoreError IO ()

View File

@@ -10,10 +10,11 @@
module Simplex.Chat.Store.Shared where
import Control.Concurrent.STM (stateTVar)
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 Data.Aeson (ToJSON)
import qualified Data.Aeson as J

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

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

View File

@@ -9,6 +9,7 @@
module Simplex.Chat.Terminal.Output where
import Control.Concurrent (ThreadId)
import Control.Monad
import Control.Monad.Catch (MonadMask)
import Control.Monad.Except
import Control.Monad.Reader

View File

@@ -16,6 +16,8 @@
{-# LANGUAGE StrictData #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# HLINT ignore "Use newtype instead of data" #-}
@@ -56,21 +58,21 @@ class IsContact a where
preferences' :: a -> Maybe Preferences
instance IsContact User where
contactId' = userContactId
contactId' u = u.userContactId
{-# INLINE contactId' #-}
profile' = profile
profile' u = u.profile
{-# INLINE profile' #-}
localDisplayName' = localDisplayName
localDisplayName' u = u.localDisplayName
{-# INLINE localDisplayName' #-}
preferences' User {profile = LocalProfile {preferences}} = preferences
{-# INLINE preferences' #-}
instance IsContact Contact where
contactId' = contactId
contactId' c = c.contactId
{-# INLINE contactId' #-}
profile' = profile
profile' c = c.profile
{-# INLINE profile' #-}
localDisplayName' = localDisplayName
localDisplayName' c = c.localDisplayName
{-# INLINE localDisplayName' #-}
preferences' Contact {profile = LocalProfile {preferences}} = preferences
{-# INLINE preferences' #-}
@@ -183,7 +185,7 @@ instance ToJSON Contact where
toEncoding = J.genericToEncoding J.defaultOptions {J.omitNothingFields = True}
contactConn :: Contact -> Connection
contactConn = activeConn
contactConn Contact{activeConn} = activeConn
contactConnId :: Contact -> ConnId
contactConnId = aConnId . contactConn
@@ -466,7 +468,7 @@ instance ToJSON LocalProfile where
toEncoding = J.genericToEncoding J.defaultOptions {J.omitNothingFields = True}
localProfileId :: LocalProfile -> ProfileId
localProfileId = profileId
localProfileId LocalProfile{profileId} = profileId
toLocalProfile :: ProfileId -> Profile -> LocalAlias -> LocalProfile
toLocalProfile profileId Profile {displayName, fullName, image, contactLink, preferences} localAlias =
@@ -627,7 +629,7 @@ groupMemberRef GroupMember {groupMemberId, memberProfile = p} =
GroupMemberRef {groupMemberId, profile = fromLocalProfile p}
memberConn :: GroupMember -> Maybe Connection
memberConn = activeConn
memberConn GroupMember{activeConn} = activeConn
memberConnId :: GroupMember -> Maybe ConnId
memberConnId GroupMember {activeConn} = aConnId <$> activeConn

View File

@@ -8,12 +8,15 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
{-# HLINT ignore "Use newtype instead of data" #-}
@@ -85,12 +88,12 @@ allChatFeatures =
]
chatPrefSel :: SChatFeature f -> Preferences -> Maybe (FeaturePreference f)
chatPrefSel = \case
SCFTimedMessages -> timedMessages
SCFFullDelete -> fullDelete
SCFReactions -> reactions
SCFVoice -> voice
SCFCalls -> calls
chatPrefSel f ps = case f of
SCFTimedMessages -> ps.timedMessages
SCFFullDelete -> ps.fullDelete
SCFReactions -> ps.reactions
SCFVoice -> ps.voice
SCFCalls -> ps.calls
chatFeature :: SChatFeature f -> ChatFeature
chatFeature = \case
@@ -110,12 +113,12 @@ instance PreferenceI (Maybe Preferences) where
getPreference f prefs = fromMaybe (getPreference f defaultChatPrefs) (chatPrefSel f =<< prefs)
instance PreferenceI FullPreferences where
getPreference = \case
SCFTimedMessages -> timedMessages
SCFFullDelete -> fullDelete
SCFReactions -> reactions
SCFVoice -> voice
SCFCalls -> calls
getPreference f ps = case f of
SCFTimedMessages -> ps.timedMessages
SCFFullDelete -> ps.fullDelete
SCFReactions -> ps.reactions
SCFVoice -> ps.voice
SCFCalls -> ps.calls
{-# INLINE getPreference #-}
setPreference :: forall f. FeatureI f => SChatFeature f -> Maybe FeatureAllowed -> Maybe Preferences -> Preferences
@@ -215,13 +218,13 @@ allGroupFeatures =
]
groupPrefSel :: SGroupFeature f -> GroupPreferences -> Maybe (GroupFeaturePreference f)
groupPrefSel = \case
SGFTimedMessages -> timedMessages
SGFDirectMessages -> directMessages
SGFFullDelete -> fullDelete
SGFReactions -> reactions
SGFVoice -> voice
SGFFiles -> files
groupPrefSel f ps = case f of
SGFTimedMessages -> ps.timedMessages
SGFDirectMessages -> ps.directMessages
SGFFullDelete -> ps.fullDelete
SGFReactions -> ps.reactions
SGFVoice -> ps.voice
SGFFiles -> ps.files
toGroupFeature :: SGroupFeature f -> GroupFeature
toGroupFeature = \case
@@ -242,13 +245,13 @@ instance GroupPreferenceI (Maybe GroupPreferences) where
getGroupPreference pt prefs = fromMaybe (getGroupPreference pt defaultGroupPrefs) (groupPrefSel pt =<< prefs)
instance GroupPreferenceI FullGroupPreferences where
getGroupPreference = \case
SGFTimedMessages -> timedMessages
SGFDirectMessages -> directMessages
SGFFullDelete -> fullDelete
SGFReactions -> reactions
SGFVoice -> voice
SGFFiles -> files
getGroupPreference f ps = case f of
SGFTimedMessages -> ps.timedMessages
SGFDirectMessages -> ps.directMessages
SGFFullDelete -> ps.fullDelete
SGFReactions -> ps.reactions
SGFVoice -> ps.voice
SGFFiles -> ps.files
{-# INLINE getGroupPreference #-}
-- collection of optional group preferences
@@ -428,19 +431,19 @@ class (Eq (FeaturePreference f), HasField "allow" (FeaturePreference f) FeatureA
prefParam :: FeaturePreference f -> Maybe Int
instance HasField "allow" TimedMessagesPreference FeatureAllowed where
hasField p = (\allow -> p {allow}, allow (p :: TimedMessagesPreference))
hasField p = (\allow -> p {allow}, p.allow)
instance HasField "allow" FullDeletePreference FeatureAllowed where
hasField p = (\allow -> p {allow}, allow (p :: FullDeletePreference))
hasField p = (\allow -> p {allow}, p.allow)
instance HasField "allow" ReactionsPreference FeatureAllowed where
hasField p = (\allow -> p {allow}, allow (p :: ReactionsPreference))
hasField p = (\allow -> p {allow}, p.allow)
instance HasField "allow" VoicePreference FeatureAllowed where
hasField p = (\allow -> p {allow}, allow (p :: VoicePreference))
hasField p = (\allow -> p {allow}, p.allow)
instance HasField "allow" CallsPreference FeatureAllowed where
hasField p = (\allow -> p {allow}, allow (p :: CallsPreference))
hasField p = (\allow -> p {allow}, p.allow)
instance FeatureI 'CFTimedMessages where
type FeaturePreference 'CFTimedMessages = TimedMessagesPreference
@@ -517,25 +520,25 @@ class (Eq (GroupFeaturePreference f), HasField "enable" (GroupFeaturePreference
groupPrefParam :: GroupFeaturePreference f -> Maybe Int
instance HasField "enable" GroupPreference GroupFeatureEnabled where
hasField p = (\enable -> p {enable}, enable (p :: GroupPreference))
hasField p = (\enable -> p {enable}, p.enable)
instance HasField "enable" TimedMessagesGroupPreference GroupFeatureEnabled where
hasField p = (\enable -> p {enable}, enable (p :: TimedMessagesGroupPreference))
hasField p = (\enable -> p {enable}, p.enable)
instance HasField "enable" DirectMessagesGroupPreference GroupFeatureEnabled where
hasField p = (\enable -> p {enable}, enable (p :: DirectMessagesGroupPreference))
hasField p = (\enable -> p {enable}, p.enable)
instance HasField "enable" ReactionsGroupPreference GroupFeatureEnabled where
hasField p = (\enable -> p {enable}, enable (p :: ReactionsGroupPreference))
hasField p = (\enable -> p {enable}, p.enable)
instance HasField "enable" FullDeleteGroupPreference GroupFeatureEnabled where
hasField p = (\enable -> p {enable}, enable (p :: FullDeleteGroupPreference))
hasField p = (\enable -> p {enable}, p.enable)
instance HasField "enable" VoiceGroupPreference GroupFeatureEnabled where
hasField p = (\enable -> p {enable}, enable (p :: VoiceGroupPreference))
hasField p = (\enable -> p {enable}, p.enable)
instance HasField "enable" FilesGroupPreference GroupFeatureEnabled where
hasField p = (\enable -> p {enable}, enable (p :: FilesGroupPreference))
hasField p = (\enable -> p {enable}, p.enable)
instance GroupFeatureI 'GFTimedMessages where
type GroupFeaturePreference 'GFTimedMessages = TimedMessagesGroupPreference
@@ -770,9 +773,9 @@ preferenceState pref =
in (allow, param)
getContactUserPreference :: SChatFeature f -> ContactUserPreferences -> ContactUserPreference (FeaturePreference f)
getContactUserPreference = \case
SCFTimedMessages -> timedMessages
SCFFullDelete -> fullDelete
SCFReactions -> reactions
SCFVoice -> voice
SCFCalls -> calls
getContactUserPreference f ps = case f of
SCFTimedMessages -> ps.timedMessages
SCFFullDelete -> ps.fullDelete
SCFReactions -> ps.reactions
SCFVoice -> ps.voice
SCFCalls -> ps.calls

View File

@@ -7,6 +7,7 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE OverloadedRecordDot #-}
module Simplex.Chat.View where
@@ -191,7 +192,7 @@ responseToView user_ ChatConfig {logLevel, showReactions, showReceipts, testView
CRContactConnecting u _ -> ttyUser u []
CRContactConnected u ct userCustomProfile -> ttyUser u $ viewContactConnected ct userCustomProfile testView
CRContactAnotherClient u c -> ttyUser u [ttyContact' c <> ": contact is connected to another client"]
CRSubscriptionEnd u acEntity -> ttyUser u [sShow (connId (entityConnection acEntity :: Connection)) <> ": END"]
CRSubscriptionEnd u acEntity -> ttyUser u [sShow ((entityConnection acEntity).connId) <> ": END"]
CRContactsDisconnected srv cs -> [plain $ "server disconnected " <> showSMPServer srv <> " (" <> contactList cs <> ")"]
CRContactsSubscribed srv cs -> [plain $ "server connected " <> showSMPServer srv <> " (" <> contactList cs <> ")"]
CRContactSubError u c e -> ttyUser u [ttyContact' c <> ": contact error " <> sShow e]
@@ -686,7 +687,9 @@ viewChatCleared (AChatInfo _ chatInfo) = case chatInfo of
viewContactsList :: [Contact] -> [StyledString]
viewContactsList =
let ldn = T.toLower . (localDisplayName :: Contact -> ContactName)
let getLDN :: Contact -> ContactName
getLDN Contact{localDisplayName} = localDisplayName
ldn = T.toLower . getLDN
in map (\ct -> ctIncognito ct <> ttyFullContact ct <> muted' ct <> alias ct) . sortOn ldn
where
muted' Contact {chatSettings, localDisplayName = ldn}
@@ -824,7 +827,8 @@ viewGroupMembers (Group GroupInfo {membership} members) = map groupMember . filt
where
removedOrLeft m = let s = memberStatus m in s == GSMemRemoved || s == GSMemLeft
groupMember m = memIncognito m <> ttyFullMember m <> ": " <> role m <> ", " <> category m <> status m
role m = plain . strEncode $ memberRole (m :: GroupMember)
role :: GroupMember -> StyledString
role m = plain . strEncode $ m.memberRole
category m = case memberCategory m of
GCUserMember -> "you, "
GCInviteeMember -> "invited, "
@@ -856,9 +860,10 @@ viewContactConnected ct@Contact {localDisplayName} userIncognitoProfile testView
viewGroupsList :: [(GroupInfo, GroupSummary)] -> [StyledString]
viewGroupsList [] = ["you have no groups!", "to create: " <> highlight' "/g <name>"]
viewGroupsList gs = map groupSS $ sortOn ldn_ gs
viewGroupsList gs = map groupSS $ sortOn (ldn_ . fst) gs
where
ldn_ = T.toLower . (localDisplayName :: GroupInfo -> GroupName) . fst
ldn_ :: GroupInfo -> Text
ldn_ g = T.toLower g.localDisplayName
groupSS (g@GroupInfo {localDisplayName = ldn, groupProfile = GroupProfile {fullName}, membership, chatSettings}, GroupSummary {currentMembers}) =
case memberStatus membership of
GSMemInvited -> groupInvitation' g
@@ -1406,7 +1411,8 @@ viewFileTransferStatus (FTSnd FileTransferMeta {cancelled} fts@(ft : _), chunksN
case concatMap recipientsTransferStatus $ groupBy ((==) `on` fs) $ sortOn fs fts of
[recipientsStatus] -> ["sending " <> sndFile ft <> " " <> recipientsStatus]
recipientsStatuses -> ("sending " <> sndFile ft <> ": ") : map (" " <>) recipientsStatuses
fs = fileStatus :: SndFileTransfer -> FileStatus
fs :: SndFileTransfer -> FileStatus
fs SndFileTransfer{fileStatus} = fileStatus
recipientsTransferStatus [] = []
recipientsTransferStatus ts@(SndFileTransfer {fileStatus, fileSize, chunkSize} : _) = [sndStatus <> ": " <> listRecipients ts]
where
@@ -1669,7 +1675,8 @@ viewChatError logLevel = \case
Just entity@(UserContactConnection conn UserContact {userContactLinkId}) ->
"[" <> connEntityLabel entity <> ", userContactLinkId: " <> sShow userContactLinkId <> ", connId: " <> cId conn <> "] "
Nothing -> ""
cId conn = sShow (connId (conn :: Connection))
cId :: Connection -> StyledString
cId conn = sShow conn.connId
where
fileNotFound fileId = ["file " <> sShow fileId <> " not found"]
sqliteError' = \case

View File

@@ -49,20 +49,24 @@ extra-deps:
# - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561
# - ../simplexmq
- github: simplex-chat/simplexmq
commit: 53c793d5590d3c781aa3fbf72993eee262c7aa83
commit: 8d47f690838371bc848e4b31a4b09ef6bf67ccc5
- github: kazu-yamamoto/http2
commit: b5a1b7200cf5bc7044af34ba325284271f6dff25
# - ../direct-sqlcipher
- github: simplex-chat/direct-sqlcipher
commit: 34309410eb2069b029b8fc1872deb1e0db123294
commit: f814ee68b16a9447fbb467ccc8f29bdd3546bfd9
# - ../sqlcipher-simple
- github: simplex-chat/sqlcipher-simple
commit: 5e154a2aeccc33ead6c243ec07195ab673137221
commit: a46bd361a19376c5211f1058908fc0ae6bf42446
# - terminal-0.2.0.0@sha256:de6770ecaae3197c66ac1f0db5a80cf5a5b1d3b64a66a05b50f442de5ad39570,2977
- github: simplex-chat/aeson
commit: 3eb66f9a68f103b5f1489382aad89f5712a64db7
commit: 68330dce8208173c6acf5f62b23acb500ab5d873
- github: simplex-chat/haskell-terminal
commit: f708b00009b54890172068f168bf98508ffcd495
- github: simplex-chat/android-support
commit: 9aa09f148089d6752ce563b14c2df1895718d806
- github: simplex-chat/network-transport
commit: 0013798272a683e35ca38d2fdaf480942311fba8
#
# extra-deps: []

View File

@@ -1,5 +1,6 @@
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
module Bots.BroadcastTests where
@@ -33,7 +34,7 @@ broadcastBotProfile = Profile {displayName = "broadcast_bot", fullName = "Broadc
mkBotOpts :: FilePath -> [KnownContact] -> BroadcastBotOpts
mkBotOpts tmp publishers =
BroadcastBotOpts
{ coreOptions = (coreOptions (testOpts :: ChatOpts)) {dbFilePrefix = tmp </> botDbPrefix},
{ coreOptions = testOpts.coreOptions {dbFilePrefix = tmp </> botDbPrefix},
publishers,
welcomeMessage = defaultWelcomeMessage publishers,
prohibitedMessage = defaultWelcomeMessage publishers

View File

@@ -1,5 +1,6 @@
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PostfixOperators #-}
@@ -60,7 +61,7 @@ directoryProfile = Profile {displayName = "SimpleX-Directory", fullName = "", im
mkDirectoryOpts :: FilePath -> [KnownContact] -> DirectoryOpts
mkDirectoryOpts tmp superUsers =
DirectoryOpts
{ coreOptions = (coreOptions (testOpts :: ChatOpts)) {dbFilePrefix = tmp </> serviceDbPrefix},
{ coreOptions = testOpts.coreOptions {dbFilePrefix = tmp </> serviceDbPrefix},
superUsers,
directoryLog = Just $ tmp </> "directory_service.log",
serviceName = "SimpleX-Directory",

View File

@@ -6,12 +6,15 @@
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
module ChatClient where
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)

View File

@@ -2,6 +2,8 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PostfixOperators #-}
{-# OPTIONS_GHC -fno-warn-ambiguous-fields #-}
module ChatTests.Files where
import ChatClient

View File

@@ -0,0 +1,14 @@
<p><strong>v5.3 is released:</strong></p>
<ul class="mb-[12px]">
<li>new desktop app! 💻</li>
<li>encrypt locally storead files & media</li>
<li>directory service and other group improvements</li>
<li>simplified incognito mode</li>
<li>better app responsiveness and stability, and 40% reduced memory usage.</li>
<li>new privacy settings: show last messages & save draft.</li>
</ul>
<p>Also, our users added 6 new interface languages - Arabic*, Bulgarian, Finnish, Hebrew*, Thai and Ukrainian.</p>
<p>* Android app only.</p>

View File

@@ -44,7 +44,9 @@ active_blog: true
<div class="min-h-[inherit] h-full w-full flex items-end px-4 pt-4 justify-center relative">
{% if blog.data.image %}
{% if blog.data.imageBottom %}
<img class="w-full max-w-[240px] h-auto" src="{{ blog.data.image }}" alt="" srcset=""/>
<img class="w-full max-w-[240px] h-auto" src="{{ blog.data.image }}" alt="" srcset=""/>
{% elif blog.data.imageWide %}
<img class="mb-4 self-center w-full h-auto" src="{{ blog.data.image }}" alt="" srcset=""/>
{% else %}
<img class="mb-4 self-center w-full max-w-[240px] h-auto" src="{{ blog.data.image }}" alt="" srcset=""/>
{% endif %}

View File

@@ -188,29 +188,26 @@ h3::before {
outline: none;
}
p,
a,
h1,
h2,
h3,
h4,
h5,
h6,
li,
ul,
ol,
span,
div,
blockquote,
pre,
code {
h6{
clear: both;
}
#article p img {
float: left;
display: inline-block;
}
#article img {
margin-bottom: 1.5rem;
margin: 0.5rem 0 1rem 0;
}
@media (min-width: 1024px) {
#article .float-to-left{
float: left;
margin-right: 3rem;
}
}