Compare commits

...

209 Commits

Author SHA1 Message Date
Avently ed16f71ee4 creating wallpaper dir at launch 2024-06-20 09:55:41 +01:00
Avently 5bc70bdfed creating directory for wallpapers 2024-06-20 09:55:41 +01:00
Avently fb4927ca87 divided types per target 2024-06-20 09:55:41 +01:00
Avently 2023464a13 types and api 2024-06-20 09:55:41 +01:00
Stanislav Dmitrenko b8bf5871fb ios: wallpapers (#4304)
* ios: wallpapers

* theme selection

* applied theme colors and preset wallpaper

* more places with background

* one more

* accent color

* defaults

* rename

* background

* no change to cell color

* unneeded

* changes

* no global tint

* defaults

* removed unneeded class

* for merging
2024-06-20 09:51:29 +01:00
Diogo 1573b7af13 ios: prevented error on desktop in lack of camera permissions (#4306)
* refactor: 💡 moved ScannerInView to separate module

made it more extensible and ready to be reused

* fix: 🐛 prevented error on desktop in lack of camera permissions

right now, connect to desktop functionality was failing if you didn't
accept permissions before jumping to this screen for the first time. In
here I did pick from other sections where cases of no permissions or
unknown permissions were correctly handled and did reuse the same
mechanism

* chore: 🤖 reverted file move

* chore: 🤖 reverted class change

* chore: 🤖 reverted uncessary change

* chore: 🤖 removed leftover
2024-06-11 09:29:53 +04:00
Narasimha-sc a5bf3cbca8 v5.6 -> v5.8 (#4308) 2024-06-10 18:33:27 +01:00
Stanislav Dmitrenko d5529031ee android, desktop: socks proxy toggle enhancements (#4312) 2024-06-10 18:33:10 +01:00
spaced4ndy d244338b13 ui: differentiate remote ctrl errors, better error texts (#4302) 2024-06-10 18:18:05 +04:00
spaced4ndy ce55c84b96 core: differentiate RCP error in view, test (#4310) 2024-06-10 17:58:53 +04:00
spaced4ndy c52f9baefa desktop: close chat view when chat is stopped (#4313) 2024-06-10 13:11:35 +01:00
Stanislav Dmitrenko ea1c5da65d desktop: change thread who responsible for writting settings (#4311) 2024-06-10 13:05:53 +01:00
sh 5695bb9d46 docs: change smp webpage path (#4307) 2024-06-08 15:42:12 +01:00
spaced4ndy 13751d5705 core: update simplexmq (upload chunks after preparation) (#4299) 2024-06-06 16:09:56 +04:00
spaced4ndy 02803b1491 core: fix testXFTPRcvError test 2024-06-05 21:16:04 +04:00
spaced4ndy 222af69a71 multiplatform: file errors (#4289) 2024-06-05 21:03:59 +04:00
spaced4ndy 5e96e1ea2b ios: file errors (#4281) 2024-06-05 21:03:05 +04:00
spaced4ndy 490e8cead8 core: file errors (#4261) 2024-06-05 21:02:13 +04:00
Evgeny Poberezkin f578ee843b core: update simplexmq (remove protocol encodings) (#4272)
* core: update simplexmq (remove protocol encodings)

* nix

* update simplexmq

* update simplexmq
2024-06-05 20:49:01 +04:00
Evgeny Poberezkin cf2eb76cb7 website: update MoneroKon group link 2024-06-05 17:33:52 +01:00
Evgeny Poberezkin 48f9dcab9a blog: update image 2024-06-04 20:03:42 +01:00
Evgeny Poberezkin ac2ef54838 blog: v5.8 announcement (#4275)
* blog: v5.8 announcement

* update

* update

* update readme

* images, preview

* simplify

* spaces
2024-06-04 19:44:29 +01:00
Evgeny Poberezkin 7e269c6b8c android, desktop: enter app logic (#4288)
Co-authored-by: Avently <7953703+avently@users.noreply.github.com>
2024-06-04 15:59:23 +01:00
Stanislav Dmitrenko 3e739924bb android, desktop: prevent duplication of group in UI when creating one (#4284) 2024-06-04 15:59:02 +01:00
Stanislav Dmitrenko 83706b19da android, desktop: removed additional background under chat bubbles (#4287) 2024-06-04 15:58:29 +01:00
Stanislav Dmitrenko 6f19a7a20f android, desktop: fixes swipe to reply when animations were disabled in the OS (#4286) 2024-06-04 15:57:48 +01:00
spaced4ndy a8740cee4b core: fix cancellation of snd files without description (e.g. while being prepared for upload) (#4285) 2024-06-04 16:17:12 +04:00
sh 4000fe383d docs/smp-server: web server pages configuration and statistics update (#4279)
* docs/smp-server: web server pages configuration and statistics update

* update table

* update

* typo

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-06-03 20:29:12 +01:00
Evgeny Poberezkin 75445e604e 5.8: ios 224, android 219, desktop 53 2024-06-03 14:05:59 +01:00
spaced4ndy 0596278e50 docs: flexible user records rfc (#4205) 2024-06-03 14:15:41 +04:00
Stanislav Dmitrenko 7a77775a42 android, desktop: fix member search in group (#4277) 2024-06-03 08:25:08 +01:00
Evgeny Poberezkin dc8d08ec04 website: translations (#4274)
* Added translation using Weblate (Hungarian)

* Added translation using Weblate (Hungarian)

* Translated using Weblate (Dutch)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/nl/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/uk/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/hu/

* Translated using Weblate (Polish)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/pl/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/ar/

---------

Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: Maksym Lukashenko <livelmaxim@gmail.com>
Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: B.O.S.S <BxOxSxS@protonmail.com>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
2024-06-02 19:43:59 +01:00
Evgeny Poberezkin 4ad0e0f65e ui: translations (#4273)
* Translated using Weblate (Italian)

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/it/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/

* Translated using Weblate (Dutch)

Currently translated at 99.8% (1547 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/nl/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/uk/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/uk/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/tr/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/tr/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/tr/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/tr/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/tr/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (German)

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (German)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (French)

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/fr/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (French)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/fr/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/es/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Persian)

Currently translated at 99.9% (1822 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/it/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/

* Translated using Weblate (Dutch)

Currently translated at 99.8% (1547 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/nl/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/uk/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/uk/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/tr/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/tr/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/tr/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/tr/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/tr/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (German)

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (German)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1819 of 1819 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (French)

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/fr/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (French)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/fr/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/es/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Persian)

Currently translated at 99.9% (1822 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Romanian)

Currently translated at 13.0% (237 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ro/

* Translated using Weblate (German)

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (German)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/es/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/nl/

* Translated using Weblate (Polish)

Currently translated at 99.9% (1822 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/pl/

* Translated using Weblate (Polish)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/pl/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Persian)

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/

* Translated using Weblate (Romanian)

Currently translated at 17.6% (322 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ro/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (German)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/

* Translated using Weblate (Japanese)

Currently translated at 96.9% (1767 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ja/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/

* Translated using Weblate (German)

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (Russian)

Currently translated at 100.0% (1823 of 1823 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ru/

* Translated using Weblate (German)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/

* Translated using Weblate (Russian)

Currently translated at 100.0% (1549 of 1549 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/ru/

* import/export localizations

---------

Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: Maksym Lukashenko <livelmaxim@gmail.com>
Co-authored-by: z0rden <kaanpeker196@gmail.com>
Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: J R <jr@simplex.chat>
Co-authored-by: mlanp <github@lang.xyz>
Co-authored-by: Ophiushi <41908476+ishi-sama@users.noreply.github.com>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
Co-authored-by: khalidbelk <khalid.belkassmi-el-hafi@epitech.eu>
Co-authored-by: Siavash <ciavash@proton.me>
Co-authored-by: Flavian <3zorro.1@gmail.com>
Co-authored-by: No name <CertainBot@users.noreply.hosted.weblate.org>
Co-authored-by: B.O.S.S <BxOxSxS@protonmail.com>
Co-authored-by: Ninja Left <s1lntlwd@proton.me>
Co-authored-by: Miyu Sakatsuki <miyu-sakatsuki@outlook.jp>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
2024-06-02 19:20:46 +01:00
Evgeny Poberezkin d01515e755 blog: Protecting Children's Safety Requires End-to-End Encryption 2024-06-01 21:47:38 +01:00
Evgeny Poberezkin 3fa70ba217 5.8-beta.5: ios 223, android 218, desktop 52 2024-06-01 11:03:22 +01:00
Evgeny Poberezkin 6ae4cddc2e Merge branch 'stable' 2024-06-01 09:33:36 +01:00
Evgeny Poberezkin ac8ee971f5 5.7.5: ios 222, android 216, desktop 51 2024-06-01 09:15:41 +01:00
Evgeny Poberezkin 11d4872186 core: 5.8.0.5 (simplexmq 5.8.0.10) 2024-06-01 00:22:26 +01:00
Evgeny Poberezkin 370e2e87ad Merge branch 'stable' 2024-05-31 23:50:00 +01:00
Evgeny Poberezkin ef30fcf5e2 cli: options for private message routing and fallback mode (#4256)
* cli: options for private message routing and fallback mode

* remove short options

* fix tests

* encoding

* fix
2024-05-31 23:41:20 +01:00
Evgeny Poberezkin 6ad1423f29 core: 5.7.5.0 (simplexmq: 5.7.6.0, file download fix) 2024-05-31 23:35:00 +01:00
Stanislav Dmitrenko c52e5349db ios: more sheets will become hidden while protect screen on (#4259)
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-31 22:47:57 +01:00
Stanislav Dmitrenko 3c98c6acc0 android, desktop: selectable cards for proxy options (#4263)
* android, desktop: selectable cards for proxy options

* title
2024-05-31 22:25:15 +01:00
Stanislav Dmitrenko c251f5e2bd android, desktop: debug subscribed SMP queues (#4262)
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-31 18:54:12 +01:00
Evgeny Poberezkin b165603136 ios: log file error to console 2024-05-31 18:33:57 +01:00
spaced4ndy 184a24b70e ui: fix filtering of contacts that can be invited to group (#4265) 2024-05-31 18:30:59 +01:00
spaced4ndy d46bae6498 ios: allow to save sent file on tap (#4264) 2024-05-31 20:20:49 +04:00
Evgeny Poberezkin 5ac521f6d8 core: commands to debug subscribed SMP queues (#4253)
* core: commands to debug subscribed SMP queues

* getLastRcvMsgInfo

* DESC

* update

* update commands, ui compiles

* update

* ios: fix type

* swap

* simplexmq

* group member queue info

* copy button in the alert
2024-05-31 12:45:58 +01:00
Stanislav Dmitrenko 7b5b747b19 desktop: performant drawing of wallpaper (#4249)
* desktop: low quality drawing of wallpaper

* fast drawing

* clean up

* unused
2024-05-30 17:07:58 +01:00
Stanislav Dmitrenko 037655e30f android, desktop: creating wallpapers dir in some situations (#4254) 2024-05-30 17:04:00 +01:00
spaced4ndy 54088c6d5a core: "/get queues" api to debug agent queues (#4252) 2024-05-30 14:24:33 +04:00
Stanislav Dmitrenko 78f7fcc89f android, desktop: wallpaper fixes (#4251) 2024-05-30 07:03:23 +01:00
Evgeny Poberezkin b402caeb89 5.8-beta.4: ios 221, android 215, desktop 50 2024-05-29 18:49:35 +01:00
Evgeny Poberezkin ff8a8de3c8 Revert "android, desktop: change wallpaper file type from PNG to WebP (#4250)"
This reverts commit d22c0c2614.
2024-05-29 17:35:57 +01:00
Stanislav Dmitrenko d22c0c2614 android, desktop: change wallpaper file type from PNG to WebP (#4250) 2024-05-29 13:31:31 +01:00
Evgeny Poberezkin 73c8d3c2e3 android, desktop: add Persian interface language 2024-05-29 10:06:54 +01:00
Evgeny Poberezkin 0110385e65 ui: v5.8 whats new (#4244)
* ios: v5.8 whats new

* blog post stub

* update

* android

* ios: export localizations
2024-05-29 09:59:41 +01:00
Evgeny Poberezkin ad0a678c5f core: 5.8.0.4 (simplexmq 5.8.0.7) 2024-05-29 08:20:51 +01:00
sh 0495a4cd78 docs/smp-server: expand tor configuration, add security practices (#4228)
* docs/smp-server: expand tor configuration, add security practices

* docs/smp-server: add TOC

* update

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-28 22:26:15 +01:00
Stanislav Dmitrenko 02267c5263 android: show alert of exception while initializing WebView (#4245)
* android: show alert of exception while initializing WebView

* Update apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-28 22:01:45 +01:00
Stanislav Dmitrenko cad3bc048f desktop: prevent corruption of settings file (#4243)
* desktop: prevent corruption of settings file

* check for existence

* unneeded

* simplify

* unused
2024-05-28 21:41:22 +01:00
Stanislav Dmitrenko 1b04423745 android, desktop: close notes tab on switching between chats (#4246) 2024-05-28 16:35:43 +01:00
spaced4ndy 2143eb2d7a core: batch send pending group messages (#4242) 2024-05-28 18:32:29 +04:00
spaced4ndy 3d395b0e45 core: inactive connections/members (#4009) 2024-05-28 16:42:07 +04:00
Stanislav Dmitrenko d79b4dfb5a android: replace new line in Uri before transforming it to URI (#4240) 2024-05-28 11:24:57 +01:00
Evgeny Poberezkin 0ae9f5ebba website: translations (#4241)
* Translated using Weblate (German)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/de/

* Translated using Weblate (Italian)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/it/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/zh_Hans/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/ja/

* Translated using Weblate (German)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/de/

* Translated using Weblate (Italian)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/it/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/zh_Hans/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/ja/

---------

Co-authored-by: mlanp <github@lang.xyz>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: Miyu Sakatsuki <miyu-sakatsuki@outlook.jp>
2024-05-28 11:23:58 +01:00
Evgeny Poberezkin 57c80786dc ui: translations, remove translations for wallpaper names used in exported themes (#4239)
* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1741 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Persian)

Currently translated at 54.1% (943 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1515 of 1515 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Persian)

Currently translated at 65.5% (1141 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1741 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1515 of 1515 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Persian)

Currently translated at 76.3% (1330 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/

* Translated using Weblate (Persian)

Currently translated at 83.0% (1446 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/

* Translated using Weblate (German)

Currently translated at 100.0% (1751 of 1751 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1751 of 1751 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1751 of 1751 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1772 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1772 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Hungarian)

Currently translated at 99.3% (1760 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Persian)

Currently translated at 88.3% (1566 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1772 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Persian)

Currently translated at 100.0% (1772 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/

* Translated using Weblate (Polish)

Currently translated at 98.3% (1743 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/pl/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1772 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1772 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1772 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (1772 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/

* Translated using Weblate (Japanese)

Currently translated at 98.8% (1751 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ja/

* Translated using Weblate (Polish)

Currently translated at 99.9% (1771 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/pl/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1772 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1515 of 1515 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (German)

Currently translated at 100.0% (1772 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1772 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1515 of 1515 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 95.6% (1449 of 1515 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/zh_Hans/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1772 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/tr/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1772 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Romanian)

Currently translated at 9.8% (175 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ro/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1515 of 1515 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Vietnamese)

Currently translated at 10.6% (189 of 1772 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/vi/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1778 of 1778 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1515 of 1515 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1778 of 1778 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1778 of 1778 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1778 of 1778 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (1778 of 1778 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/

* Translated using Weblate (Polish)

Currently translated at 99.9% (1777 of 1778 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/pl/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1778 of 1778 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1515 of 1515 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (German)

Currently translated at 100.0% (1778 of 1778 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (German)

Currently translated at 100.0% (1515 of 1515 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/

* Translated using Weblate (French)

Currently translated at 100.0% (1515 of 1515 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/fr/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (1778 of 1778 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1778 of 1778 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1515 of 1515 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1778 of 1778 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (French)

Currently translated at 100.0% (1778 of 1778 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fr/

* Translated using Weblate (Persian)

Currently translated at 100.0% (1778 of 1778 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/

* Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/

* Translated using Weblate (Hungarian)

Currently translated at 98.8% (1794 of 1814 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Czech)

Currently translated at 94.2% (1710 of 1814 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/cs/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1814 of 1814 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Polish)

Currently translated at 99.9% (1813 of 1814 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/pl/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1814 of 1814 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Turkish)

Currently translated at 99.6% (1807 of 1814 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/tr/

* Translated using Weblate (German)

Currently translated at 100.0% (1814 of 1814 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1814 of 1814 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1814 of 1814 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1814 of 1814 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1515 of 1515 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/es/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (1814 of 1814 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1814 of 1814 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/tr/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1814 of 1814 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1515 of 1515 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Vietnamese)

Currently translated at 11.2% (204 of 1814 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/vi/

* Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/

* Translated using Weblate (Ukrainian)

Currently translated at 97.1% (1764 of 1815 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/uk/

* process localizations

* removed wallpapers names

---------

Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: Siavash <ciavash@proton.me>
Co-authored-by: mlanp <github@lang.xyz>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
Co-authored-by: B.O.S.S <BxOxSxS@protonmail.com>
Co-authored-by: No name <CertainBot@users.noreply.hosted.weblate.org>
Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: Miyu Sakatsuki <miyu-sakatsuki@outlook.jp>
Co-authored-by: sith-on-mars <groguko36@pm.me>
Co-authored-by: z0rden <kaanpeker196@gmail.com>
Co-authored-by: Flavian <3zorro.1@gmail.com>
Co-authored-by: tuananh-ng <158744840+tuananh-ng@users.noreply.github.com>
Co-authored-by: indiandoc <marc@nymtech.net>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: Ophiushi <41908476+ishi-sama@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: zenobit <zen@osowoso.xyz>
Co-authored-by: Maksym Lukashenko <livelmaxim@gmail.com>
Co-authored-by: Avently <7953703+avently@users.noreply.github.com>
2024-05-28 11:20:52 +01:00
Stanislav Dmitrenko da1cad96fc android: fix showing empty notification (#4237)
* android: fix showing empty notification

* fix desktop concurrent notifications
2024-05-28 08:04:23 +01:00
Stanislav Dmitrenko b58ccedf23 android, desktop: enhancements to wallpapers (#4234)
* android, desktop: enhancements to wallpapers

* fixes

* unneeded
2024-05-28 08:02:45 +01:00
spaced4ndy 01cadefde7 core: avoid creating duplicate introductions between group members connecting concurrently (#4235) 2024-05-27 18:42:39 +04:00
Evgeny Poberezkin c70e7223d9 core: delete connection records when connecting (JOIN) fails (#4233)
* core: delete connection records when connecting (JOIN) fails

* show errors in commands
2024-05-27 15:32:09 +01:00
Evgeny Poberezkin a1d4f52185 5.8-beta.3: ios 220, android 214, desktop 49 2024-05-25 20:57:07 +01:00
Evgeny Poberezkin 373070f388 ui: show UI for additional group preferences (SimpleX links and per-role permissions) 2024-05-25 19:54:05 +01:00
Evgeny Poberezkin a03ccd0ba6 ios: update core library 5.8.0.3 2024-05-25 18:55:15 +01:00
Evgeny Poberezkin a7982cccf9 Merge branch 'stable' 2024-05-25 17:22:25 +01:00
Evgeny Poberezkin dccd6631af 5.7.4: ios 219, android 212, desktop 48 2024-05-25 17:06:11 +01:00
Evgeny Poberezkin b0516b6440 core: 5.7.4.1 2024-05-25 11:18:49 +01:00
Stanislav Dmitrenko bf91666f07 android, desktop: wallpapers (#4109)
* android, desktop: wallpapers

* changes

* removed unused

* changes

* backgrounds moved to themes. can be exported/imported

* changes and refactoring

* scrollable

* change

* reorder

* theme per background

* changes

* don't drop customization in same cases

* changes

* changes

* fix state update

* different defaults

* fix Android

* removed migration

* temporary

* returned theme name as uppercase back

* resilient to wrong preset name

* more

* live color change without button

* fix

* Revert "temporary"

This reverts commit 705c92ee56.

* changes

* changes

* almost candy

* old picker and ability to specify hex color

* different image

* picker

* user theme override, fixes, import/export theme with image

* revert when about to merge: useful layout for theming

* custom image displaying and deletion work correctly

* performance

* moved code up

* changes

* color changes in picker when changing themes

* reset color without closing modal

* unused

* log

* currentTheme instead of MaterialTheme

* different inheritance logic

* fix and move sent/received message colors to top

* remove background

* performance

* fix wrong base

* changes

* fix

* small

* custom image selection made normally

* better color picker

* better picker

* refactor, fixes, optimizations

* working with unknown base theme + tests

* change

* delete wallpaper dir when importing archive

* function rename

* fix bug with state update

* update colors

* fix text colors

* update images

* changes

* remote desktop and theme mode dropdown

* default theme

* additional accent 2 changes

* fix default wallpaper

* removed ChatListAlwaysVisible

* renaming

* update messages

* changes

* default theme

* unused

* member icon background color

* image quality

* fix wallpaper on empty notes screen

* update colors

* change

* update colors

* fix update on android

* fix update on both platforms

* update colors, remove serializable

* fix color

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-25 09:52:16 +01:00
Evgeny Poberezkin ab47a5a27e Revert "ui: comment smp proxy ui (#4204)"
This reverts commit a0d6ae15ab.
2024-05-24 22:59:37 +01:00
Evgeny Poberezkin 23581f59ae core: 5.8.0.3 2024-05-24 21:33:45 +01:00
Evgeny Poberezkin 291df6e9d0 core: logging of chat events (#4216)
* core: update simplexmq (persist server errors)

* fix

* same config

* logging

* logging 2

* log

* log 2

* finally

* catch better

* more logs

* logs

* fix

* more logging, context from PROHIBITED

* warning

* more logs

* logs3

* logs4

* logs in simplexmq

* log locks from simplemq

* log queue size

* log sendMessagesB in simplexmq

* update simplexmq

* logs5

* logs6

* logs7

* logs8

* logs8

* logs9

* logs10

* log11

* log12

* fix test

* more logs

* logging

* clean up

* refactor

* simplify

* tags

* log level

* remove network errors from the log

* rename
2024-05-24 21:09:21 +01:00
Evgeny Poberezkin dae0b63c22 Merge branch 'stable' 2024-05-23 22:56:04 +01:00
Evgeny Poberezkin 0964982782 core: ensure that messages are sent to different members (#4221)
* core: ensure that messages are sent to different members

* update simplexmq
2024-05-23 22:50:23 +01:00
Evgeny Poberezkin 9eb6034e1a core: 5.7.4.0 (simplexmq 5.7.4.1: networking improvements) 2024-05-22 20:30:24 +01:00
Evgeny Poberezkin c59eb06b8a 5.8-beta.2: ios 218, android 210, desktop 47 2024-05-20 21:23:40 +01:00
Evgeny Poberezkin 423fc96638 core: 5.8.0.2 (simplexmq 5.8.0.2) 2024-05-20 18:38:46 +01:00
spaced4ndy ba203faad4 android: check known relays before file reception, support user approval of unknown relays (#4196) 2024-05-20 17:58:30 +04:00
spaced4ndy ec7b35adb9 ios: check known relays before file reception, support user approval of unknown relays (#4044)
* ios: check known relays before file reception, support user approval of unknown relays

* add privacyAskToApproveRelays to AppSettings

* less text

* ui improvements, fixes

* fix ntf

* shorter alert

* simplify settings

* rename

* icon

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-20 17:49:19 +04:00
spaced4ndy d2d450d1d7 core: check known relays before file reception, support user approval of unknown relays (#4043)
* core: check known relays before file reception, support user approval of unknown relays

* comment

* reset on not approved agent error

* add privacyAskToApproveRelays to AppSettings

* filter distinct servers

* update simplexmq

* remember user_approved_relays

* refactor

* rename

* update simplexmq

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-20 17:23:29 +04:00
Evgeny Poberezkin b4caefb17c 5.8-beta.1: ios 217, android 209, desktop 46 2024-05-18 08:20:38 +01:00
Evgeny Poberezkin fd791f3fb8 ios: update core library 2024-05-17 15:54:51 +01:00
Evgeny Poberezkin 0720292ebf core: 5.8.0.1 (simplexmq: 5.8.0.1, faster reconnection when switching networks) 2024-05-17 15:39:58 +01:00
spaced4ndy a0d6ae15ab ui: comment smp proxy ui (#4204) 2024-05-17 16:36:25 +04:00
Evgeny Poberezkin 3e76a2d49c blog: image 2024-05-16 19:47:24 +01:00
Evgeny Poberezkin ed3365bb6d website: fix links 2024-05-16 18:16:00 +01:00
Evgeny Poberezkin b2a8797ed4 blog: Redefining Privacy by Making Hard Choices (#4195)
* blog: Redefining Privacy by Making Hard Choices

* update

* update

* revert

* update

* corrections

Co-authored-by: Esra'a Al Shafei <alshafei@users.noreply.github.com>

* update

---------

Co-authored-by: Esra'a Al Shafei <alshafei@users.noreply.github.com>
2024-05-16 18:02:37 +01:00
spaced4ndy d3e522e169 ui: disable send field for disabled connection (based on authErrCounter) (#4193) 2024-05-16 17:08:31 +04:00
spaced4ndy 49e09ca449 core: contact disabled event (#4194) 2024-05-16 17:08:13 +04:00
spaced4ndy d1dde3d0c8 ui: support message forwarding with custom ttl (#4192) 2024-05-16 14:12:58 +04:00
spaced4ndy ef75eceb40 core: support message forwarding with custom ttl (#4191) 2024-05-16 14:11:55 +04:00
spaced4ndy 96ce59f330 multiplatform: SMP proxy configuration buttons (#4188)
* multiplatform: SMP proxy configuration buttons

* fix

* icons

* icon

* icon
2024-05-15 20:09:51 +01:00
spaced4ndy 98a67fa2e4 multiplatform: SMP proxy support (#4179)
* android: SMP proxy support

* android types

* show in meta

* reserve

* remove comment

* texts
2024-05-15 13:56:29 +01:00
spaced4ndy 4b13512950 ios: SMP proxy support (#4178)
* ios: SMP proxy support

* statuses

* group statuses

* error texts

* update

* change icon

* texts

* texts

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-15 13:09:42 +01:00
spaced4ndy 4c0d47bbd4 core: message statuses for sending proxies (#4161)
* core: delivery path

* update simplexmq

* via proxy snd flags

* error statuses

* rework errors

* proxy expired errors

* corrections

* move backwards compatibile parser to new type

* update simplexmq

* names

* refactor, style

* simplexmq

* refactor

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-15 12:30:05 +01:00
Evgeny Poberezkin 3129602a78 Merge branch 'stable' 2024-05-15 11:44:22 +01:00
Stanislav Dmitrenko a0c257962e android, desktop: fix file menu and icon (#4185)
* android, desktop: fix file menu and icon

* simplify

* space
2024-05-15 11:43:20 +01:00
Evgeny Poberezkin 93ae1145bc core: update simplexmq (use MVar for better DB connection concurrency) (#4183)
* core: update simplexmq (use MVar for better DB connection concurrency)

* focus failing tests

* add timeouts to test

* fix tests

* more delays

* increase timeouts

* prints

* delay

* delay

* empty

* more delays

* enable all

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-05-15 11:16:38 +01:00
Evgeny Poberezkin d02668386f directory: debug logging (#4104) 2024-05-14 21:32:53 +01:00
M. Sarmad Qadeer bd8c8b5a8c web: fix the links of faq & some other (#4144)
* web: fix the links of faq & some other

* web: fix links

* web: update .eleventy.js to fix links

* web: fix some links and improve the logic

* web: fix links & improve the logic

* remove semicolon

* update links

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-14 19:59:51 +01:00
Evgeny Poberezkin e359ec02f5 5.8-beta.0: ios 216, android 208, desktop 45 2024-05-13 23:34:14 +01:00
Evgeny Poberezkin a1948d549b ios: update core library 2024-05-13 22:24:10 +01:00
Evgeny Poberezkin 396b73b480 core: 5.8.0.0 2024-05-13 21:13:07 +01:00
Evgeny Poberezkin 331b4633fc core: platform independent encoding for delete command (#4176) 2024-05-13 20:39:13 +01:00
Evgeny Poberezkin 29db2b4e9b core: command to show duplicate message count (#4172)
* core: command to show duplicate message count

* update

* updated

* update

* update

* simplexmq
2024-05-13 19:55:57 +01:00
spaced4ndy f091384aa7 ui: apply model updates only for active user for switch and ratchet sync events (#4175) 2024-05-13 17:25:24 +01:00
spaced4ndy f40ba6f04d core: api to differentiate contacts and conversations (#4111) 2024-05-13 16:51:54 +04:00
Evgeny Poberezkin 06d61ea73e core: update simplexmq 2024-05-13 08:19:40 +01:00
Evgeny Poberezkin 84d7a77a9f core: update simplexmq (better subscription management) (#4131)
* core: update simplexmq (better subscription management)

* ui: update settings

* update simplexmq

* simplexmq

* simplexmq

* test output

* delay

* disable test
2024-05-13 08:04:12 +01:00
Evgeny Poberezkin 878eea774d core: save app themes as map with any text key (#4159)
* core: save app themes as map with any text key

* remove theme preset name

* list of themes

* theme id

* theme IDs

* moar

* colors

* default to dark

* fix
2024-05-12 23:35:14 +01:00
Narasimha-sc 4575dc6e30 langs: change 1-2 days to several days on fdroid (#4170) 2024-05-12 18:19:27 +01:00
Evgeny Poberezkin 554ef4eefc 5.7.3: ios 215, android 206, desktop 44 2024-05-12 11:36:30 +01:00
Evgeny Poberezkin 567e9981be core: 5.7.3.0 2024-05-11 23:48:39 +01:00
Evgeny Poberezkin 1183fe427f Merge branch 'stable' 2024-05-10 20:30:42 +01:00
Evgeny Poberezkin c49a1f42e7 5.7.2: ios 214, android 204, desktop 43 2024-05-10 18:32:07 +01:00
Narasimha-sc 4f173215c6 docs: fix broken simplex group invite links (#4155)
* Fix non-working #simplex-devs invite

* Fix #simplex-devs link in pl

* replace SimpleX-Group-2

* replace simplex-group-2 link

* Update README.md
2024-05-10 17:43:06 +01:00
Evgeny Poberezkin 41597e563e website: translations (#4165)
* Translated using Weblate (French)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/fr/

* Translated using Weblate (Czech)

Currently translated at 98.0% (252 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/cs/

* Translated using Weblate (Russian)

Currently translated at 99.6% (256 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/ru/

* Translated using Weblate (French)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/fr/

* Translated using Weblate (Czech)

Currently translated at 98.0% (252 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/cs/

* Translated using Weblate (Russian)

Currently translated at 99.6% (256 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/ru/

---------

Co-authored-by: J'informatique <hello@jinformatique.net>
Co-authored-by: zenobit <zen@osowoso.xyz>
Co-authored-by: link2xt <link2xt@testrun.org>
2024-05-10 17:39:51 +01:00
Evgeny Poberezkin aa127f0cea ui: translations (#4164)
* Translated using Weblate (Japanese)

Currently translated at 100.0% (1741 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ja/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1515 of 1515 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/tr/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1741 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/tr/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1741 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Persian)

Currently translated at 25.3% (441 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1515 of 1515 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Vietnamese)

Currently translated at 10.1% (176 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/vi/

* Translated using Weblate (Persian)

Currently translated at 26.8% (467 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/

* Translated using Weblate (Persian)

Currently translated at 33.6% (586 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/

* Translated using Weblate (Romanian)

Currently translated at 6.0% (105 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ro/

* Translated using Weblate (Persian)

Currently translated at 36.8% (642 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/

* Translated using Weblate (Romanian)

Currently translated at 9.1% (159 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ro/

* Translated using Weblate (Persian)

Currently translated at 39.0% (679 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/

* Translated using Weblate (Persian)

Currently translated at 39.0% (680 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/

* import/export localizations

---------

Co-authored-by: Miyu Sakatsuki <miyu-sakatsuki@outlook.jp>
Co-authored-by: z0rden <kaanpeker196@gmail.com>
Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: Siavash <ciavash@proton.me>
Co-authored-by: tuananh-ng <158744840+tuananh-ng@users.noreply.github.com>
Co-authored-by: Flavian <3zorro.1@gmail.com>
2024-05-10 17:38:42 +01:00
Evgeny Poberezkin f9c64ba39c ios: update core library 2024-05-10 17:22:39 +01:00
Evgeny Poberezkin 1025fa94a1 core: 5.7.2.0 (simplexmq 5.7.3.0: update protocol versions, fix file downloads) 2024-05-10 15:18:42 +01:00
Stanislav Dmitrenko a59769b91d android, desktop: fix crash in contact preferences when updating user profile (#4158) 2024-05-10 09:45:44 +01:00
spaced4ndy 76248b4878 core: on receive set file crypto args only for new files (#4152) 2024-05-09 15:40:23 +04:00
Stanislav Dmitrenko 93a7ddb128 android, desktop: fix presenting large images (#4139)
* android, desktop: fix presenting large images

* 4320 px limitation

* revert line

* onClick

* onClick
2024-05-09 10:39:49 +01:00
Evgeny Poberezkin bc5af35a3e core: wallpapers api (#4110)
* core: wallpapers api

* optional colors

* update

* api

* update

* whitespace

* typo

* test, fix

* fix color parsing

* separate UI and Theme color schemes

* update

* enable test

* multiple themes, one per color scheme

* theme overrides as a separate type

* rename

---------

Co-authored-by: Avently <7953703+avently@users.noreply.github.com>
2024-05-08 15:36:20 +01:00
Evgeny Poberezkin e38d5bd885 core: update simplexmq (upgrade client protocol versions SMP v7, NTF v2) 2024-05-08 13:40:25 +01:00
M. Sarmad Qadeer 10f41aa1c9 web: add scroll to navbar (#4100)
* web: add scroll to navbar

* web: add scroll to whole navbar instead of scrolling the opened menu
2024-05-08 09:31:02 +01:00
spaced4ndy 1b081197d4 multiplatform: add error to response (#4140) 2024-05-06 20:27:35 +04:00
M. Sarmad Qadeer 9615e525c0 web: fix list numbering for safari (#4106)
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-06 08:36:13 +01:00
M. Sarmad Qadeer db136116d3 web: make image of full width for mobile screen (#4101) 2024-05-06 08:31:34 +01:00
Evgeny Poberezkin 98f5e5d22b 5.7.1: ios 213, android 202, desktop 42 2024-05-05 21:01:57 +01:00
Evgeny Poberezkin 9f33e5d35e website: translations (#4136)
* Translated using Weblate (German)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/de/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/nl/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/ar/

* Translated using Weblate (Italian)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/it/

* Translated using Weblate (Polish)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/pl/

* Translated using Weblate (German)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/de/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/nl/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/ar/

* Translated using Weblate (Italian)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/it/

* Translated using Weblate (Polish)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/pl/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/ar/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/zh_Hans/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/ja/

---------

Co-authored-by: mlanp <github@lang.xyz>
Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: B.O.S.S <BxOxSxS@protonmail.com>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: Miyu Sakatsuki <miyu-sakatsuki@outlook.jp>
2024-05-05 19:00:16 +01:00
Evgeny Poberezkin b8e8124faf ui: translations (#4135)
* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1515 of 1515 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (1741 of 1741 strings)

Co-authored-by: summoner001 <summoner@vivaldi.net>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/
Translation: SimpleX Chat/SimpleX Chat Android
Translation: SimpleX Chat/SimpleX Chat iOS

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1515 of 1515 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (1741 of 1741 strings)

Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/
Translation: SimpleX Chat/SimpleX Chat Android
Translation: SimpleX Chat/SimpleX Chat iOS

* Translated using Weblate (Polish)

Currently translated at 100.0% (1515 of 1515 strings)

Translated using Weblate (Polish)

Currently translated at 99.9% (1740 of 1741 strings)

Translated using Weblate (Polish)

Currently translated at 99.1% (1727 of 1741 strings)

Co-authored-by: B.O.S.S <BxOxSxS@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/pl/
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/pl/
Translation: SimpleX Chat/SimpleX Chat Android
Translation: SimpleX Chat/SimpleX Chat iOS

* Translated using Weblate (German)

Currently translated at 100.0% (1515 of 1515 strings)

Translated using Weblate (German)

Currently translated at 100.0% (1741 of 1741 strings)

Co-authored-by: mlanp <github@lang.xyz>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/
Translation: SimpleX Chat/SimpleX Chat Android
Translation: SimpleX Chat/SimpleX Chat iOS

* Translated using Weblate (Italian)

Currently translated at 100.0% (1515 of 1515 strings)

Translated using Weblate (Italian)

Currently translated at 100.0% (1741 of 1741 strings)

Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/it/
Translation: SimpleX Chat/SimpleX Chat Android
Translation: SimpleX Chat/SimpleX Chat iOS

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1515 of 1515 strings)

Translated using Weblate (Spanish)

Currently translated at 100.0% (1741 of 1741 strings)

Co-authored-by: No name <CertainBot@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/es/
Translation: SimpleX Chat/SimpleX Chat Android
Translation: SimpleX Chat/SimpleX Chat iOS

* Translated using Weblate (Dutch)

Currently translated at 100.0% (1515 of 1515 strings)

Translated using Weblate (Dutch)

Currently translated at 100.0% (1741 of 1741 strings)

Co-authored-by: M1K4 <oomikaoo@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/nl/
Translation: SimpleX Chat/SimpleX Chat Android
Translation: SimpleX Chat/SimpleX Chat iOS

* Translated using Weblate (Arabic)

Currently translated at 100.0% (1741 of 1741 strings)

Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/
Translation: SimpleX Chat/SimpleX Chat Android

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1741 of 1741 strings)

Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/
Translation: SimpleX Chat/SimpleX Chat Android

* Translated using Weblate (Japanese)

Currently translated at 100.0% (1741 of 1741 strings)

Co-authored-by: Miyu Sakatsuki <miyu-sakatsuki@outlook.jp>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ja/
Translation: SimpleX Chat/SimpleX Chat Android

* Translated using Weblate (Czech)

Currently translated at 98.1% (1709 of 1741 strings)

Co-authored-by: zenobit <zen@osowoso.xyz>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/cs/
Translation: SimpleX Chat/SimpleX Chat Android

* Translated using Weblate (Bulgarian)

Currently translated at 100.0% (1741 of 1741 strings)

Translated using Weblate (Bulgarian)

Currently translated at 100.0% (1515 of 1515 strings)

Co-authored-by: elgratea <weblate@fastmail.com>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/bg/
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/bg/
Translation: SimpleX Chat/SimpleX Chat Android
Translation: SimpleX Chat/SimpleX Chat iOS

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1741 of 1741 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (1515 of 1515 strings)

Co-authored-by: z0rden <kaanpeker196@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/tr/
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/tr/
Translation: SimpleX Chat/SimpleX Chat Android
Translation: SimpleX Chat/SimpleX Chat iOS

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1515 of 1515 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (1741 of 1741 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (1515 of 1515 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (1741 of 1741 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (1515 of 1515 strings)

Translated using Weblate (Hungarian)

Currently translated at 100.0% (1741 of 1741 strings)

Co-authored-by: summoner001 <summoner@vivaldi.net>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/
Translation: SimpleX Chat/SimpleX Chat Android
Translation: SimpleX Chat/SimpleX Chat iOS

* Translated using Weblate (Japanese)

Currently translated at 80.7% (1224 of 1515 strings)

Translated using Weblate (Japanese)

Currently translated at 80.3% (1218 of 1515 strings)

Co-authored-by: koreirozzu <trueworldfacts+asjfsajfhajfh@proton.me>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/ja/
Translation: SimpleX Chat/SimpleX Chat iOS

* Translated using Weblate (Japanese)

Currently translated at 80.3% (1218 of 1515 strings)

Co-authored-by: a4318 <dalse.077@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/ja/
Translation: SimpleX Chat/SimpleX Chat iOS

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1515 of 1515 strings)

Co-authored-by: xe1st <dnzkckali@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/tr/
Translation: SimpleX Chat/SimpleX Chat iOS

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1741 of 1741 strings)

Co-authored-by: J R <jr@simplex.chat>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/tr/
Translation: SimpleX Chat/SimpleX Chat Android

* Translated using Weblate (Persian)

Currently translated at 9.8% (171 of 1741 strings)

Translated using Weblate (Persian)

Currently translated at 9.4% (164 of 1741 strings)

Co-authored-by: Siavash <ciavash@proton.me>
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/
Translation: SimpleX Chat/SimpleX Chat Android

* Translated using Weblate (Persian)

Currently translated at 10.1% (176 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/

* Translated using Weblate (French)

Currently translated at 100.0% (1741 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fr/

* Translated using Weblate (French)

Currently translated at 100.0% (1515 of 1515 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/fr/

* Translated using Weblate (Persian)

Currently translated at 11.9% (208 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fa/

* Translated using Weblate (Vietnamese)

Currently translated at 8.5% (148 of 1741 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/vi/

* import/export localizations

* tr revert

---------

Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
Co-authored-by: B.O.S.S <BxOxSxS@protonmail.com>
Co-authored-by: mlanp <github@lang.xyz>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: No name <CertainBot@users.noreply.hosted.weblate.org>
Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: Miyu Sakatsuki <miyu-sakatsuki@outlook.jp>
Co-authored-by: zenobit <zen@osowoso.xyz>
Co-authored-by: elgratea <weblate@fastmail.com>
Co-authored-by: z0rden <kaanpeker196@gmail.com>
Co-authored-by: koreirozzu <trueworldfacts+asjfsajfhajfh@proton.me>
Co-authored-by: a4318 <dalse.077@gmail.com>
Co-authored-by: xe1st <dnzkckali@gmail.com>
Co-authored-by: J R <jr@simplex.chat>
Co-authored-by: Siavash <ciavash@proton.me>
Co-authored-by: Ophiushi <41908476+ishi-sama@users.noreply.github.com>
Co-authored-by: tuananh-ng <158744840+tuananh-ng@users.noreply.github.com>
2024-05-05 18:57:52 +01:00
Evgeny Poberezkin 156e41950a ios: update library 2024-05-05 18:01:14 +01:00
Evgeny Poberezkin 26c5ed4caf core: 5.7.1.0 (simplexmq 5.7.2.0: reduce "ping" traffic, remove TLS timeouts) 2024-05-05 17:43:09 +01:00
Evgeny Poberezkin d832afa7e8 docs: calls without e2e encryption (#4134) 2024-05-05 16:56:34 +01:00
Stanislav Dmitrenko 2e18b97077 android: enable Android 8 support (API 26) (#4132) 2024-05-05 16:30:17 +01:00
Evgeny Poberezkin b6e57c0fa2 core: fix connection failure because of race condition when connecting via link or joining group via invitation (#4133)
* core: fix connection failure because of race condition when connecting via link or joining group via invitation

* fix race for connection via contact address

* simplexmq
2024-05-05 13:11:30 +01:00
spaced4ndy 15a226cfd1 ui: limit length of member names in chat view (#4122) 2024-05-03 12:34:15 +01:00
Evgeny Poberezkin c4cbf751b9 Merge branch 'stable' 2024-05-02 23:28:59 +01:00
Stanislav Dmitrenko 535ff1d836 actions: add macos aarch64 build and fix openssl include path (#4107) 2024-05-02 21:41:15 +01:00
Evgeny Poberezkin fb0718adac core: forward agent errors without connections to UI (to show CRITICAL alerts) (#4120) 2024-05-02 21:40:08 +01:00
Stanislav Dmitrenko 5f501f1cb9 android, desktop: fix android video calls (#4118) 2024-05-02 16:47:52 +01:00
Evgeny Poberezkin 7cc86574fe core: update simplexmq to 5.7.1.0 2024-05-02 16:43:08 +01:00
Evgeny Poberezkin 19fc44efae rfc: commercial model for infrastructure operators (#4102) 2024-04-30 07:53:27 +01:00
spaced4ndy 37e03a838c core: forward group message before ack (fixes forwarding message that deleted connection causing error in ackMsg) (#4108) 2024-04-29 19:49:04 +04:00
spaced4ndy aeb28400e9 core: take entity lock before processing file (#4105) 2024-04-29 19:32:53 +04:00
sh d5a0c5e56a scripts/build-android: update nix and refactor (#4099) 2024-04-27 09:23:14 +01:00
Evgeny Poberezkin a28d965a65 website: fix privacy page 2024-04-26 23:24:54 +01:00
Evgeny Poberezkin 9148425eba blog: legally binding transparency, v5.7 announcement (#4097)
* blog: legally binding transparency, v5.7 announcement

* update

* update, images

* readme

* update
2024-04-26 23:06:40 +01:00
M. Sarmad Qadeer 406c35958e web: add privacy page to website (#4096)
* web: add privacy page to website

* web: fix a blog frontmatter

* web: fix privacy link from other docs

* update

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-26 23:00:49 +01:00
Evgeny Poberezkin be9ae61a32 5.7: ios 212, android 200, desktop 41 2024-04-26 20:59:21 +01:00
Stanislav Dmitrenko 9db65a1775 ios: audio device picker (#4091)
* ios: audio device picker

* removed unused

* removed logs

* correct routing

* Revert "removed unused"

This reverts commit d883d7a719.

* changes

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-26 19:59:00 +01:00
Evgeny Poberezkin 1033f55597 ios: fix sound in outgoing calls in iOS 15 2024-04-26 19:41:44 +01:00
Stanislav Dmitrenko 7f2462c936 android: black background in call (#4098) 2024-04-26 18:31:52 +01:00
Evgeny Poberezkin e675efe6f3 ios: update library 2024-04-26 17:38:57 +01:00
Evgeny Poberezkin b3f8068446 core: 5.7.0.5, increase min version of remote host/controller to 5.7.0.3 2024-04-26 15:42:13 +01:00
Evgeny Poberezkin 957eda8a78 ui: variable profile image size (#4094)
* ui: variable profile image size

* changes

* fix

* clean up

---------

Co-authored-by: Avently <7953703+avently@users.noreply.github.com>
2024-04-25 22:09:06 +01:00
Evgeny Poberezkin 0fe949e225 5.7-beta.3: ios 210, android 199, desktop 40 2024-04-25 15:51:23 +01:00
Evgeny Poberezkin 5325b0a543 core: update simplexmq to 5.7.0.3 2024-04-25 13:40:06 +01:00
Evgeny Poberezkin f712eb0d1f ui: add link to the release post 2024-04-25 13:16:49 +01:00
spaced4ndy fc6fa5315b android: show disappearing message timer on calls and group invitations (#4090)
* android: show disappearing message timer on calls and group invitations

* fix padding
2024-04-25 12:15:52 +01:00
Evgeny Poberezkin 3eb88ab896 5.7.0.4 (simplexmq 5.7.0.3) 2024-04-25 11:15:28 +01:00
Alexander Bondarenko 20fa366231 core: use contextless decompression (#4089)
* core: use contextless decompression

* fuse concatMap/map

* nix

* update simplexmq

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-25 10:52:26 +01:00
Evgeny Poberezkin db05436e50 Merge branch 'stable' 2024-04-25 10:42:28 +01:00
Evgeny Poberezkin a1093b0671 docs: update privacy policy (#4076) 2024-04-25 10:41:27 +01:00
spaced4ndy 6a8a8f242a ui: allow to delete any items (#4082)
* ios: allow to delete any items

* android

* remove button android

* remove button ios
2024-04-25 10:20:52 +01:00
spaced4ndy 1163fe7b56 ios: show disappearing message timer on calls and group invitations (#4086) 2024-04-25 12:41:20 +04:00
spaced4ndy 063a6dbc92 core: apply disappearing messages setting to call and group invitation chat items (#4080)
* core: apply disappearing messages setting to call and group invitation chat items

* remove comment

* fix incorrectly set edited field

* sent group invitations

* refactor

* Revert "refactor"

This reverts commit 4dd3070c2d.
2024-04-25 09:37:05 +01:00
Stanislav Dmitrenko 9e2a3fe848 android, desktop: more relaxed way of showing alerts for encryption/decryption errors (#4085) 2024-04-24 21:39:59 +01:00
Stanislav Dmitrenko 2b7f3099a6 android, desktop: customizable profile images (#4087)
* android, desktop: customizable profile images

* better

* fixes

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-24 21:22:52 +01:00
Evgeny Poberezkin cfa7e0bb28 ios: customizable profile images (#4084)
* ios: customizable profile images

* update slider

* whats new
2024-04-24 21:20:26 +01:00
Evgeny Poberezkin 1967ac0880 ui: translations (#4079)
* Translated using Weblate (German)

Currently translated at 100.0% (1738 of 1738 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (Russian)

Currently translated at 100.0% (1738 of 1738 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ru/

* Translated using Weblate (German)

Currently translated at 100.0% (1512 of 1512 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/

* Translated using Weblate (Russian)

Currently translated at 100.0% (1512 of 1512 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/ru/

* update

---------

Co-authored-by: mlanp <github@lang.xyz>
2024-04-24 14:42:17 +01:00
Stanislav Dmitrenko 68966a1551 android, desktop: alert for local encryption/decryption errors (#4081)
* android, desktop: more alert in case of local encryption/decryption errors

* sending a message without a file
2024-04-24 14:39:06 +01:00
Evgeny Poberezkin 5ba7b58d7d website: translations (#4078)
* Translated using Weblate (German)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/de/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (257 of 257 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/ja/

---------

Co-authored-by: mlanp <github@lang.xyz>
Co-authored-by: Miyu Sakatsuki <miyu-sakatsuki@outlook.jp>
2024-04-24 12:45:20 +01:00
Evgeny Poberezkin 9979983742 ui: translations (#4075)
* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1507 of 1507 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* ios: Hungarian translations

* Translated using Weblate (German)

Currently translated at 99.5% (1730 of 1738 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1738 of 1738 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1512 of 1512 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/es/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (1738 of 1738 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (1512 of 1512 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/nl/

* Translated using Weblate (Lithuanian)

Currently translated at 100.0% (1738 of 1738 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/lt/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1512 of 1512 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/tr/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1738 of 1738 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/tr/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1512 of 1512 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* import/export

---------

Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: No name <CertainBot@users.noreply.hosted.weblate.org>
Co-authored-by: Anonimas <Anonimas3@users.noreply.hosted.weblate.org>
Co-authored-by: z0rden <kaanpeker196@gmail.com>
2024-04-24 09:44:44 +01:00
Evgeny Poberezkin ab76e93df7 docs: security policy (#4068)
* docs: security policy

* update

* add to website
2024-04-23 19:30:42 +01:00
Evgeny Poberezkin f30287c4df docs: FAQ (#4013)
* Addd FAQ.md (#3814)

* faq

* typos

* add troubleshooting

* update links

---------

Co-authored-by: ungespurv <87089788+ungespurv@users.noreply.github.com>
2024-04-23 19:19:51 +01:00
Evgeny Poberezkin 2807e53d4c ui: traslations (#4070)
* Translated using Weblate (Dutch)

Currently translated at 99.9% (1728 of 1729 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/

* Translated using Weblate (Dutch)

Currently translated at 99.9% (1506 of 1507 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/nl/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (1729 of 1729 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1729 of 1729 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1507 of 1507 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (German)

Currently translated at 100.0% (1730 of 1730 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (German)

Currently translated at 100.0% (1507 of 1507 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1730 of 1730 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1507 of 1507 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/it/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1730 of 1730 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Romanian)

Currently translated at 5.1% (89 of 1730 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ro/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 99.8% (1736 of 1738 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Japanese)

Currently translated at 80.8% (1218 of 1507 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/ja/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (1738 of 1738 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ja/

* Translated using Weblate (Lithuanian)

Currently translated at 98.5% (1713 of 1738 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/lt/

* Translated using Weblate (Bulgarian)

Currently translated at 100.0% (1507 of 1507 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/bg/

* Translated using Weblate (Bulgarian)

Currently translated at 100.0% (1738 of 1738 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/bg/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1738 of 1738 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1738 of 1738 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Romanian)

Currently translated at 5.3% (93 of 1738 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ro/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1507 of 1507 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1738 of 1738 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Lithuanian)

Currently translated at 98.8% (1718 of 1738 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/lt/

* Translated using Weblate (Romanian)

Currently translated at 5.4% (94 of 1738 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ro/

* import/export localizations

---------

Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: mlanp <github@lang.xyz>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
Co-authored-by: Flavian <3zorro.1@gmail.com>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: yokoba0413 <yokoba0413@gmail.com>
Co-authored-by: Anonimas <Anonimas3@users.noreply.hosted.weblate.org>
Co-authored-by: elgratea <weblate@fastmail.com>
2024-04-23 19:16:12 +01:00
Stanislav Dmitrenko 642e6d677e android, desktop: updating delivery tab in chat item info (#4074) 2024-04-23 19:14:58 +01:00
Stanislav Dmitrenko 944a0b3aad android: longer delay before showing keyboard when creating a group (#4073) 2024-04-23 19:14:35 +01:00
Stanislav Dmitrenko e5ac472da3 android, desktop: restore message TTL in database settings if confirmation is dismissed (#4072) 2024-04-23 18:51:04 +01:00
Stanislav Dmitrenko 2f79bfb075 android: current audio device will be set in init step (#4071) 2024-04-23 18:47:15 +01:00
Evgeny Poberezkin ae9e63d1be ios: export localizations 2024-04-23 09:59:16 +01:00
Evgeny Poberezkin d864603d01 website: do not build from stable 2024-04-23 00:14:02 +01:00
Evgeny Poberezkin 06ac15b282 5.7-beta.2: ios 209, android 198, desktop 39 2024-04-23 00:11:37 +01:00
Stanislav Dmitrenko 4c1dfc76d4 android: sound from speaker fix (#4067) 2024-04-22 23:26:01 +01:00
Evgeny Poberezkin bea19f76e9 5.7.0.3 (simplexmq 5.7.0.2) 2024-04-22 22:24:00 +01:00
Stanislav Dmitrenko f80ac6dc50 android: proper hiding keyboard when entering passcode (#4066) 2024-04-22 22:13:14 +01:00
spaced4ndy 2b6764fc36 android: 5.7 what's new (#4064)
* android: 5.7 what's new

* icon

* update LT language name

* Lithuanian

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-22 22:03:18 +01:00
Esra'a Al Shafei 7c74f02d01 docs: typo in simplex-chat.md (#4037)
typo fix
2024-04-22 18:26:41 +01:00
Stanislav Dmitrenko d53aa69691 android: managing audio sources in call in a better way (#4063) 2024-04-22 18:24:26 +01:00
spaced4ndy 9e3f528d44 android: remove experimental PQ toggle (#4060) 2024-04-22 17:50:52 +01:00
spaced4ndy 22fd2d7189 ios: remove experimental PQ toggle (#4051)
* ios: remove experimental PQ toggle

* remove header
2024-04-22 17:50:28 +01:00
spaced4ndy 7567791866 core: enable PQ encryption for contacts (#4049)
* wip

* migration

* remove migration

* remove flag wip

* Revert "remove migration"

This reverts commit 9e64663f9d.

* wip

* remove vrange parameterization

* tests mostly pass

* more tests pass

* enable for upgraded contacts

* remove migration

* core: test large messages (#4061)

* update simplexmq, fix tests

* test test

* another test

* enable tests

* empty

* empty
2024-04-22 17:46:48 +01:00
spaced4ndy 529ba7a0e7 ios: 5.7 what's new (#4062)
* ios: 5.7 what's new

* add about pq

* update

* update

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-22 17:46:14 +01:00
Evgeny Poberezkin 3b3a68da15 core: 5.7.0.2 2024-04-22 08:07:42 +01:00
Evgeny Poberezkin e0e94d78b0 core, ui: revert timeout changes (#4059)
* core, ui: revert timeout changes

* update core library
2024-04-22 07:45:20 +01:00
Evgeny Poberezkin d68369b32a android, desktop: add Lithuanian language 2024-04-21 20:37:14 +01:00
Evgeny Poberezkin d29886dbd2 ui: translations (#4056)
* Translated using Weblate (French)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fr/

* Translated using Weblate (Spanish)

Currently translated at 99.8% (1693 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 95.0% (1405 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (French)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fr/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/es/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ja/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/es/

* Translated using Weblate (Japanese)

Currently translated at 82.4% (1218 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/ja/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ja/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/tr/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (German)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (Portuguese)

Currently translated at 8.0% (119 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/pt/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (German)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (German)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Portuguese)

Currently translated at 10.5% (156 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/pt/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Vietnamese)

Currently translated at 6.3% (108 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/vi/

* Translated using Weblate (Portuguese)

Currently translated at 10.6% (157 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/pt/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (French)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fr/

* Translated using Weblate (Spanish)

Currently translated at 99.8% (1693 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 95.0% (1405 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (French)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fr/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/es/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ja/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/es/

* Translated using Weblate (Japanese)

Currently translated at 82.4% (1218 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/ja/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ja/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/tr/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (German)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (Portuguese)

Currently translated at 8.0% (119 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/pt/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (German)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (German)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Portuguese)

Currently translated at 10.5% (156 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/pt/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Vietnamese)

Currently translated at 6.3% (108 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/vi/

* Translated using Weblate (Portuguese)

Currently translated at 10.6% (157 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/pt/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (German)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (German)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/

* Translated using Weblate (Lithuanian)

Currently translated at 64.3% (1091 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/lt/

* Translated using Weblate (Turkish)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/tr/

* Translated using Weblate (Lithuanian)

Currently translated at 72.1% (1223 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/lt/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/

* Translated using Weblate (Lithuanian)

Currently translated at 72.4% (1228 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/lt/

* Translated using Weblate (Polish)

Currently translated at 99.9% (1695 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/pl/

* Translated using Weblate (Lithuanian)

Currently translated at 78.4% (1330 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/lt/

* Translated using Weblate (Lithuanian)

Currently translated at 100.0% (1696 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/lt/

* Translated using Weblate (Polish)

Currently translated at 99.9% (1695 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/pl/

* Translated using Weblate (Polish)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/pl/

* Translated using Weblate (Vietnamese)

Currently translated at 6.6% (113 of 1696 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/vi/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1702 of 1702 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Russian)

Currently translated at 100.0% (1702 of 1702 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ru/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1702 of 1702 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Chinese (Traditional))

Currently translated at 71.2% (1213 of 1702 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hant/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1702 of 1702 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Vietnamese)

Currently translated at 7.2% (124 of 1702 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/vi/

* Update translation files

Updated by "Remove blank strings" hook in Weblate.

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (1703 of 1703 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/

* Translated using Weblate (Japanese)

Currently translated at 99.6% (1697 of 1703 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ja/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1703 of 1703 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (German)

Currently translated at 100.0% (1703 of 1703 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1703 of 1703 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Japanese)

Currently translated at 99.9% (1702 of 1703 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ja/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (1703 of 1703 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1703 of 1703 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1703 of 1703 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Hungarian)

Currently translated at 99.8% (1712 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (German)

Currently translated at 100.0% (1715 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (Japanese)

Currently translated at 99.8% (1713 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ja/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (1715 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/

* Translated using Weblate (Polish)

Currently translated at 99.9% (1714 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/pl/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1715 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1715 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (1715 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/

* Translated using Weblate (Japanese)

Currently translated at 99.8% (1713 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ja/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1715 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (French)

Currently translated at 100.0% (1715 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/fr/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1715 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Polish)

Currently translated at 99.9% (1714 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/pl/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1715 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Vietnamese)

Currently translated at 7.5% (130 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/vi/

* Translated using Weblate (Vietnamese)

Currently translated at 7.8% (135 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/vi/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1715 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/es/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1715 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1715 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/it/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1715 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/es/

* Translated using Weblate (Bulgarian)

Currently translated at 99.8% (1712 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/bg/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1715 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1715 of 1715 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1725 of 1725 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (1725 of 1725 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1725 of 1725 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1725 of 1725 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1725 of 1725 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1725 of 1725 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1478 of 1478 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/es/

* Translated using Weblate (Polish)

Currently translated at 99.9% (1724 of 1725 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/pl/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1728 of 1728 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* process localizations

* update RU translations

* revert PL changes for "passphrase"

---------

Co-authored-by: Ophiushi <41908476+ishi-sama@users.noreply.github.com>
Co-authored-by: khalidbelk <khalid.belkassmi-el-hafi@epitech.eu>
Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: No name <CertainBot@users.noreply.hosted.weblate.org>
Co-authored-by: Miyu Sakatsuki <miyu-sakatsuki@outlook.jp>
Co-authored-by: z0rden <kaanpeker196@gmail.com>
Co-authored-by: mlanp <github@lang.xyz>
Co-authored-by: Andre <andrefeliciano@duck.com>
Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: tuananh-ng <158744840+tuananh-ng@users.noreply.github.com>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: Anonimas <qdq5ih+3ewax7y4esw98@cs.email>
Co-authored-by: B.O.S.S <BxOxSxS@protonmail.com>
Co-authored-by: Anonimas <Anonimas3@users.noreply.hosted.weblate.org>
Co-authored-by: Arturro43 <Arturro43.kontakt@gmail.com>
Co-authored-by: Alex <github@lippiece.anonaddy.me>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Kingpin <radoslav.najdenov@gmail.com>
2024-04-21 20:16:08 +01:00
Evgeny Poberezkin 50d3a29715 website: translations (#4057)
* Translated using Weblate (French)

Currently translated at 100.0% (255 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/fr/

* Translated using Weblate (German)

Currently translated at 100.0% (255 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/de/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (255 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/nl/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (255 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/ar/

* Translated using Weblate (Italian)

Currently translated at 100.0% (255 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/it/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (255 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/es/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (255 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/zh_Hans/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (255 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/ja/

* Translated using Weblate (Russian)

Currently translated at 100.0% (255 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/ru/

* Translated using Weblate (French)

Currently translated at 100.0% (255 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/fr/

* Translated using Weblate (German)

Currently translated at 100.0% (255 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/de/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (255 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/nl/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (255 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/ar/

* Translated using Weblate (Italian)

Currently translated at 100.0% (255 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/it/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (255 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/es/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (255 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/zh_Hans/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (255 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/ja/

* Translated using Weblate (Russian)

Currently translated at 100.0% (255 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/ru/

* Translated using Weblate (Bulgarian)

Currently translated at 13.7% (35 of 255 strings)

Translation: SimpleX Chat/SimpleX Chat website
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/website/bg/

---------

Co-authored-by: khalidbelk <khalid.belkassmi-el-hafi@epitech.eu>
Co-authored-by: mlanp <github@lang.xyz>
Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: Miyu Sakatsuki <miyu-sakatsuki@outlook.jp>
Co-authored-by: Alex <github@lippiece.anonaddy.me>
Co-authored-by: Kingpin <radoslav.najdenov@gmail.com>
2024-04-21 11:10:07 +01:00
439 changed files with 28218 additions and 5706 deletions
+32 -9
View File
@@ -65,6 +65,11 @@ jobs:
asset_name: simplex-chat-ubuntu-22_04-x86-64
desktop_asset_name: simplex-desktop-ubuntu-22_04-x86_64.deb
- os: macos-latest
ghc: "9.6.3"
cache_path: ~/.cabal/store
asset_name: simplex-chat-macos-aarch64
desktop_asset_name: simplex-desktop-macos-aarch64.dmg
- os: macos-13
ghc: "9.6.3"
cache_path: ~/.cabal/store
asset_name: simplex-chat-macos-x86-64
@@ -107,18 +112,36 @@ jobs:
if: matrix.os == 'macos-latest'
shell: bash
run: |
echo "ignore-project: False" >> cabal.project.local
echo "package direct-sqlcipher" >> cabal.project.local
echo " extra-include-dirs: /usr/local/opt/openssl@1.1/include" >> cabal.project.local
echo " extra-lib-dirs: /usr/local/opt/openssl@1.1/lib" >> cabal.project.local
echo " flags: +openssl" >> cabal.project.local
echo "ignore-project: False" >> cabal.project.local
echo "package simplexmq" >> cabal.project.local
echo " extra-include-dirs: /opt/homebrew/opt/openssl@1.1/include" >> cabal.project.local
echo " extra-lib-dirs: /opt/homebrew/opt/openssl@1.1/lib" >> cabal.project.local
echo "" >> cabal.project.local
echo "package direct-sqlcipher" >> cabal.project.local
echo " extra-include-dirs: /opt/homebrew/opt/openssl@1.1/include" >> cabal.project.local
echo " extra-lib-dirs: /opt/homebrew/opt/openssl@1.1/lib" >> cabal.project.local
echo " flags: +openssl" >> cabal.project.local
- name: Unix prepare cabal.project.local for Mac
if: matrix.os == 'macos-13'
shell: bash
run: |
echo "ignore-project: False" >> cabal.project.local
echo "package simplexmq" >> cabal.project.local
echo " extra-include-dirs: /usr/local/opt/openssl@1.1/include" >> cabal.project.local
echo " extra-lib-dirs: /usr/local/opt/openssl@1.1/lib" >> cabal.project.local
echo "" >> cabal.project.local
echo "package direct-sqlcipher" >> cabal.project.local
echo " extra-include-dirs: /usr/local/opt/openssl@1.1/include" >> cabal.project.local
echo " extra-lib-dirs: /usr/local/opt/openssl@1.1/lib" >> cabal.project.local
echo " flags: +openssl" >> cabal.project.local
- name: Install AppImage dependencies
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os == 'ubuntu-20.04'
run: sudo apt install -y desktop-file-utils
- name: Install pkg-config for Mac
if: matrix.os == 'macos-latest'
if: matrix.os == 'macos-latest' || matrix.os == 'macos-13'
run: brew install pkg-config
- name: Unix prepare cabal.project.local for Ubuntu
@@ -190,7 +213,7 @@ jobs:
- name: Mac build desktop
id: mac_desktop_build
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'macos-latest'
if: startsWith(github.ref, 'refs/tags/v') && (matrix.os == 'macos-latest' || matrix.os == 'macos-13')
shell: bash
env:
APPLE_SIMPLEX_SIGNING_KEYCHAIN: ${{ secrets.APPLE_SIMPLEX_SIGNING_KEYCHAIN }}
@@ -241,7 +264,7 @@ jobs:
${{ steps.linux_appimage_build.outputs.appimage_hash }}
- name: Mac upload desktop package to release
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'macos-latest'
if: startsWith(github.ref, 'refs/tags/v') && (matrix.os == 'macos-latest' || matrix.os == 'macos-13')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
@@ -250,7 +273,7 @@ jobs:
tag: ${{ github.ref }}
- name: Mac update desktop package hash
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'macos-latest'
if: startsWith(github.ref, 'refs/tags/v') && (matrix.os == 'macos-latest' || matrix.os == 'macos-13')
uses: softprops/action-gh-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-1
View File
@@ -4,7 +4,6 @@ on:
push:
branches:
- master
- stable
paths:
- website/**
- images/**
+1
View File
@@ -54,6 +54,7 @@ website/translations.json
website/src/img/images/
website/src/images/
website/src/js/lottie.min.js
website/src/privacy.md
# Generated files
website/package/generated*
+17 -10
View File
@@ -1,16 +1,21 @@
---
layout: layouts/privacy.html
permalink: /privacy/index.html
---
# SimpleX Chat Privacy Policy and Conditions of Use
SimpleX Chat is the first communication network based on a new protocol stack that builds on the same ideas of complete openness and decentralization as email and web, with the focus on providing security and privacy of communications, and without compromising on usability.
SimpleX Chat communication protocol is the first protocol that has no user profile IDs of any kind, not even random numbers, cryptographic keys or hashes that identify the users. SimpleX Chat apps allow their users to send messages and files via relay server infrastructure. Relay server owners and providers do not have any access to your messages, thanks to double-ratchet end-to-end encryption algorithm (also known as Signal algorithm - do not confuse with Signal protocols or platform) and additional encryption layers, and they also have no access to your profile and contacts - as they do not provide any user accounts.
Double ratchet algorithm has such important properties as [forward secrecy](./docs/GLOSSARY.md#forward-secrecy), sender [repudiation](./docs/GLOSSARY.md#) and break-in recovery (also known as [post-compromise security](./docs/GLOSSARY.md#post-compromise-security)).
Double ratchet algorithm has such important properties as [forward secrecy](/docs/GLOSSARY.md#forward-secrecy), sender [repudiation](/docs/GLOSSARY.md#) and break-in recovery (also known as [post-compromise security](/docs/GLOSSARY.md#post-compromise-security)).
If you believe that any part of this document is not aligned with our mission or values, please raise it with us via [email](chat@simplex.chat) or [chat](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23%2F%3Fv%3D1%26dh%3DMCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion).
If you believe that any part of this document is not aligned with our mission or values, please raise it with us via [email](mailto:chat@simplex.chat) or [chat](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23%2F%3Fv%3D1%26dh%3DMCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion).
## Privacy Policy
SimpleX Chat Ltd uses the best industry practices for security and encryption to provide client and server software for secure [end-to-end encrypted](./docs/GLOSSARY.md#end-to-end-encryption) messaging via private connections. This encryption cannot be compromised by the relays servers, even if they are modified or compromised, via [man-in-the-middle attack](./docs/GLOSSARY.md#man-in-the-middle-attack), unlike most other communication platforms, services and networks.
SimpleX Chat Ltd uses the best industry practices for security and encryption to provide client and server software for secure [end-to-end encrypted](/docs/GLOSSARY.md#end-to-end-encryption) messaging via private connections. This encryption cannot be compromised by the relays servers, even if they are modified or compromised, via [man-in-the-middle attack](/docs/GLOSSARY.md#man-in-the-middle-attack), unlike most other communication platforms, services and networks.
SimpleX Chat software is built on top of SimpleX messaging and application protocols, based on a new message routing protocol allowing to establish private connections without having any kind of addresses or other identifiers assigned to its users - it does not use emails, phone numbers, usernames, identity keys or any other user profile identifiers to pass messages between the user applications.
@@ -20,7 +25,7 @@ While SimpleX Chat Ltd is not a communication service provider, and provide publ
We see users and data sovereignty, and device and provider portability as critically important properties for any communication system.
SimpleX Chat security assessment was done in October 2022 by [Trail of Bits](https://www.trailofbits.com/about), and most fixes were released in v4.2 see [the announcement](./blog/20221108-simplex-chat-v4.2-security-audit-new-website.md).
SimpleX Chat security assessment was done in October 2022 by [Trail of Bits](https://www.trailofbits.com/about), and most fixes were released in v4.2 see [the announcement](/blog/20221108-simplex-chat-v4.2-security-audit-new-website.md).
### Your information
@@ -34,7 +39,7 @@ You can transfer the profile to another device by creating a backup of the app d
#### Messages and Files
SimpleX relay servers cannot decrypt or otherwise access the content or even the size of your messages and files you send or receive. Each message is padded to a fixed size of 16kb. Each file is sent in chunks of 64kb, 256kb, 1mb or 8mb via all or some of the configured file relay servers. Both messages and files are sent end-to-end encrypted, and the servers do not have technical means to compromise this encryption, because part of the [key exchange](./docs/GLOSSARY.md#key-exchange) happens out-of-band.
SimpleX relay servers cannot decrypt or otherwise access the content or even the size of your messages and files you send or receive. Each message is padded to a fixed size of 16kb. Each file is sent in chunks of 64kb, 256kb, 1mb or 8mb via all or some of the configured file relay servers. Both messages and files are sent end-to-end encrypted, and the servers do not have technical means to compromise this encryption, because part of the [key exchange](/docs/GLOSSARY.md#key-exchange) happens out-of-band.
Your message history is stored only on your own device and the devices of your contacts. While the recipients' devices are offline, messaging relay servers temporarily store end-to-end encrypted messages you can configure which relay servers are used to receive the messages from the new contacts, and you can manually change them for the existing contacts too.
@@ -74,7 +79,7 @@ When you choose to use instant push notifications in SimpleX iOS app, because th
Preset notification server cannot observe the actual addresses of these queues, as a separate address is used to subscribe to the notifications. It also cannot observe who sends messages to you. Apple push notifications servers can only observe how many notifications are sent to you, but not from how many contacts, or from which messaging relays, as notifications are delivered to your device end-to-end encrypted by one of the preset notification servers - these notifications only contain end-to-end encrypted metadata, not even encrypted message content, and they look completely random to Apple push notification servers.
You can read more about the design of iOS push notifications [here](https://simplex.chat/blog/20220404-simplex-chat-instant-notifications.html#our-ios-approach-has-one-trade-off).
You can read more about the design of iOS push notifications [here](./blog/20220404-simplex-chat-instant-notifications.md#our-ios-approach-has-one-trade-off).
#### Another information stored on the servers
@@ -82,7 +87,7 @@ Additional technical information can be stored on our servers, including randoml
#### SimpleX Directory
[SimpleX Directory](./docs/DIRECTORY.md) stores: your search requests, the messages and the members profiles in the registered groups. You can connect to SimpleX Directory via [this address](https://simplex.chat/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).
[SimpleX Directory](/docs/DIRECTORY.md) stores: your search requests, the messages and the members profiles in the registered groups. You can connect to SimpleX Directory via [this address](https://simplex.chat/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).
#### User Support
@@ -103,13 +108,15 @@ The cases when SimpleX Chat Ltd may share the data temporarily stored on the ser
At the time of updating this document, we have never provided or have been requested the access to the preset relay servers or any information from the servers by any third parties. If we are ever requested to provide such access or information, we will follow the due legal process to limit any information shared with the third parties to the minimally required by law.
We will publish information we are legally allowed to share about such requests in the [Transparency reports](./docs/TRANSPARENCY.md).
### Updates
We will update this Privacy Policy as needed so that it is current, accurate, and as clear as possible. Your continued use of our software applications and preset relays infrastructure confirms your acceptance of our updated Privacy Policy.
Please also read our Conditions of Use of Software and Infrastructure below.
If you have questions about our Privacy Policy please contact us via [email](chat@simplex.chat) or [chat](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23%2F%3Fv%3D1%26dh%3DMCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion).
If you have questions about our Privacy Policy please contact us via [email](mailto:chat@simplex.chat) or [chat](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23%2F%3Fv%3D1%26dh%3DMCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion).
## Conditions of Use of Software and Infrastructure
@@ -131,7 +138,7 @@ You accept the Conditions of Use of Software and Infrastructure ("Conditions") b
**Traffic and device costs**. You are solely responsible for the traffic and device costs that you incur while using our Applications, and any associated taxes.
**Legal and acceptable usage**. You agree to use our Applications only for legal and acceptable purposes. You will not use (or assist others in using) our Applications in ways that: 1) violate or infringe the rights of Software users, SimpleX Chat Ltd, or others, including privacy, publicity, intellectual property, or other proprietary rights; 2) involve sending illegal or impermissible communications, e.g. spam. While we cannot access content or identify messages or groups, in some cases the links to the illegal or impermissible communications available via our Applications can be shared publicly on social media or websites. We reserve the right to remove such links from the preset servers and disrupt the conversations that send illegal content via our servers, whether they were reported by the users or discovered by our team.
**Legal usage**. You agree to use our Applications only for legal purposes. You will not use (or assist others in using) our Applications in ways that: 1) violate or infringe the rights of Software users, SimpleX Chat Ltd, or others, including privacy, publicity, intellectual property, or other proprietary rights; 2) involve sending illegal communications, e.g. spam. While we cannot access content or identify messages or groups, in some cases the links to the illegal communications available via our Applications can be shared publicly on social media or websites. We reserve the right to remove such links from the preset servers and disrupt the conversations that send illegal content via our servers, whether they were reported by the users or discovered by our team.
**Damage to SimpleX Chat Ltd**. You must not (or assist others to) access, use, modify, distribute, transfer, or exploit our Applications in unauthorized manners, or in ways that harm Software users, SimpleX Chat Ltd, our Infrastructure, or any other systems. For example, you must not 1) access our Infrastructure or systems without authorization, in any way other than by using the Software; 2) disrupt the integrity or performance of our Infrastructure; 3) collect information about our users in any manner; or 4) sell, rent, or charge for our Infrastructure. This does not prohibit you from providing your own Infrastructure to others, whether free or for a fee, as long as you do not violate these Conditions and AGPLv3 license, including the requirement to publish any modifications of the relay server software.
@@ -165,4 +172,4 @@ You accept the Conditions of Use of Software and Infrastructure ("Conditions") b
**Ending these conditions**. You may end these Conditions with SimpleX Chat Ltd at any time by deleting our Applications from your devices and discontinuing use of our Infrastructure. The provisions related to Licenses, Disclaimers, Limitation of Liability, Resolving dispute, Availability, Changes to the conditions, Enforcing the conditions, and Ending these conditions will survive termination of your relationship with SimpleX Chat Ltd.
Updated February 24, 2024
Updated April 24, 2024
+9 -5
View File
@@ -74,7 +74,7 @@ Messages not following these rules will be deleted, the right to send messages m
You can join an English-speaking users group if you want to ask any questions: [#SimpleX users group](https://simplex.chat/contact#/?v=1-4&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2Fos8FftfoV8zjb2T89fUEjJtF7y64p5av%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAQqMgh0fw2lPhjn3PDIEfAKA_E0-gf8Hr8zzhYnDivRs%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22lBPiveK2mjfUH43SN77R0w%3D%3D%22%7D)
There is also a group [#simplex-devs](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2F6eHqy7uAbZPOcA6qBtrQgQquVlt4Ll91%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAqV_pg3FF00L98aCXp4D3bOs4Sxv_UmSd-gb0juVoQVs%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22XonlixcHBIb2ijCehbZoiw%3D%3D%22%7D) for developers who build on SimpleX platform:
There is also a group [#simplex-devs](https://simplex.chat/contact#/?v=1-4&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FvYCRjIflKNMGYlfTkuHe4B40qSlQ0439%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAHNdcqNbzXZhyMoSBjT2R0-Eb1EPaLyUg3KZjn-kmM1w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion&data=%7B%22type%22%3A%22group%22%2C%22groupLinkId%22%3A%22PD20tcXjw7IpkkMCfR6HLA%3D%3D%22%7D) for developers who build on SimpleX platform:
- chat bots and automations
- integrations with other apps
@@ -234,6 +234,10 @@ You can use SimpleX with your own servers and still communicate with people usin
Recent and important updates:
[Jun 4, 2024. SimpleX network: private message routing, v5.8 released with IP address protection and chat themes](./blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.md)
[Apr 26, 2024. SimpleX network: legally binding transparency, v5.7 released with better calls and messages.](./blog/20240426-simplex-legally-binding-transparency-v5-7-better-user-experience.md)
[Mar 23, 2024. SimpleX network: real privacy and stable profits, non-profits for protocols, v5.6 released with quantum resistant e2e encryption and simple profile migration.](./blog/20240323-simplex-network-privacy-non-profit-v5-6-quantum-resistant-e2e-encryption-simple-migration.md)
[Mar 14, 2024. SimpleX Chat v5.6 beta: adding quantum resistance to Signal double ratchet algorithm.](./blog/20240314-simplex-chat-v5-6-quantum-resistance-signal-double-ratchet-algorithm.md)
@@ -380,10 +384,10 @@ Please also join [#simplex-devs](https://simplex.chat/contact#/?v=1-2&smp=smp%3A
- ✅ Private notes.
- ✅ Improve sending videos (including encryption of locally stored videos).
- ✅ Post-quantum resistant key exchange in double ratchet protocol.
- ✅ Message delivery relay for senders (to conceal IP address from the recipients' servers and to reduce the traffic).
- 🏗 Improve stability and reduce battery usage.
- 🏗 Improve experience for the new users.
- 🏗 Large groups, communities and public channels.
- 🏗 Message delivery relay for senders (to conceal IP address from the recipients' servers and to reduce the traffic).
- Privacy & security slider - a simple way to set all settings at once.
- SMP queue redundancy and rotation (manual is supported).
- Include optional message into connection request sent via contact address.
@@ -412,13 +416,13 @@ We have never provided or have been requested access to our servers or any infor
We do not log IP addresses of the users and we do not perform any traffic correlation on our servers. If transport level security is critical you must use Tor or some other similar network to access messaging servers. We will be improving the client applications to reduce the opportunities for traffic correlation.
Please read more in [Terms & privacy policy](./PRIVACY.md).
Please read more in [Privacy Policy](./PRIVACY.md).
## Security contact
To report a security vulnerability, please send us email to chat@simplex.chat. We will coordinate the fix and disclosure. Please do NOT report security vulnerabilities via GitHub issues.
Please see our [Security Policy](./docs/SECURITY.md) on how to report security vulnerabilities to us. We will coordinate the fix and disclosure.
Please treat any findings of possible traffic correlation attacks allowing to correlate two different conversations to the same user, other than covered in [the threat model](https://github.com/simplex-chat/simplexmq/blob/stable/protocol/overview-tjr.md#threat-model), as security vulnerabilities, and follow this disclosure process.
Please do NOT report security vulnerabilities via GitHub issues.
## License
+9 -2
View File
@@ -17,6 +17,7 @@ class AppDelegate: NSObject, UIApplicationDelegate {
if #available(iOS 17.0, *) { trackKeyboard() }
NotificationCenter.default.addObserver(self, selector: #selector(pasteboardChanged), name: UIPasteboard.changedNotification, object: nil)
removePasscodesIfReinstalled()
prepareForLaunch()
return true
}
@@ -141,6 +142,10 @@ class AppDelegate: NSObject, UIApplicationDelegate {
}
}
private func prepareForLaunch() {
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
}
static func keepScreenOn(_ on: Bool) {
UIApplication.shared.isIdleTimerDisabled = on
}
@@ -148,13 +153,15 @@ class AppDelegate: NSObject, UIApplicationDelegate {
class SceneDelegate: NSObject, ObservableObject, UIWindowSceneDelegate {
var window: UIWindow?
static var windowStatic: UIWindow?
var windowScene: UIWindowScene?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
UITableView.appearance().backgroundColor = .clear
guard let windowScene = scene as? UIWindowScene else { return }
self.windowScene = windowScene
window = windowScene.keyWindow
window?.tintColor = UIColor(cgColor: getUIAccentColorDefault())
window?.overrideUserInterfaceStyle = getUserInterfaceStyleDefault()
SceneDelegate.windowStatic = windowScene.keyWindow
ThemeManager.applyTheme(currentThemeDefault.get())
}
}
@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "wallpaper_cats@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "wallpaper_flowers@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "wallpaper_hearts@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "wallpaper_kids@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "wallpaper_school@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "wallpaper_travel@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

+21 -2
View File
@@ -14,6 +14,8 @@ struct ContentView: View {
@ObservedObject var alertManager = AlertManager.shared
@ObservedObject var callController = CallController.shared
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
@EnvironmentObject var sceneDelegate: SceneDelegate
var contentAccessAuthenticationExtended: Bool
@@ -51,6 +53,16 @@ struct ContentView: View {
}
var body: some View {
if #available(iOS 16.0, *) {
allViews()
.scrollContentBackground(.hidden)
} else {
// on iOS 15 scroll view background disabled in SceneDelegate
allViews()
}
}
@ViewBuilder func allViews() -> some View {
ZStack {
let showCallArea = chatModel.activeCall != nil && chatModel.activeCall?.callState != .waitCapabilities && chatModel.activeCall?.callState != .invitationAccepted
// contentView() has to be in a single branch, so that enabling authentication doesn't trigger re-rendering and close settings.
@@ -96,6 +108,8 @@ struct ContentView: View {
initializationView()
}
}
//.tint(theme.colors.primary)
.background(theme.colors.background)
.alert(isPresented: $alertManager.presentAlert) { alertManager.alertView! }
.sheet(isPresented: $showSettings) {
SettingsView(showSettings: $showSettings)
@@ -138,6 +152,11 @@ struct ContentView: View {
break
}
}
.onChange(of: colorScheme) { scheme in
if sceneDelegate.window?.overrideUserInterfaceStyle == .unspecified {
reactOnDarkThemeChanges(scheme == .dark)
}
}
}
@ViewBuilder private func contentView() -> some View {
@@ -224,8 +243,8 @@ struct ContentView: View {
.frame(maxWidth: .infinity, maxHeight: .infinity )
.background(
Rectangle()
.fill(.background)
)
.fill(theme.colors.background)
)
}
private func mainView() -> some View {
+1 -1
View File
@@ -179,7 +179,7 @@ class AudioPlayer: NSObject, AVAudioPlayerDelegate {
if playback {
if AVAudioSession.sharedInstance().category != .playback {
logger.log("AudioSession: playback")
try? AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, options: .duckOthers)
try? AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, options: [.duckOthers, .allowBluetooth, .allowAirPlay, .allowBluetoothA2DP])
}
} else {
if AVAudioSession.sharedInstance().category != .soloAmbient {
+10
View File
@@ -422,6 +422,16 @@ final class ChatModel: ObservableObject {
}
}
func updateCurrentUserUiThemes(uiThemes: ThemeModeOverrides?) {
guard var current = currentUser else { return }
current.uiThemes = uiThemes
let i = users.firstIndex(where: { $0.user.userId == current.userId })
if let i {
users[i].user = current
}
currentUser = current
}
func addLiveDummy(_ chatInfo: ChatInfo) -> ChatItem {
let cItem = ChatItem.liveDummy(chatInfo.chatType)
withAnimation {
+37
View File
@@ -205,6 +205,43 @@ func moveTempFileFromURL(_ url: URL) -> CryptoFile? {
}
}
func saveWallpaperFile(url: URL) -> String? {
let destFile = URL(fileURLWithPath: generateNewFileName(getWallpaperDirectory().path + "/" + "wallpaper", "jpg", fullPath: true))
do {
try FileManager.default.copyItem(atPath: url.path, toPath: destFile.path)
return destFile.lastPathComponent
} catch {
logger.error("FileUtils.saveWallpaperFile error: \(error.localizedDescription)")
return nil
}
}
func saveWallpaperFile(image: UIImage) -> String? {
let hasAlpha = imageHasAlpha(image)
let destFile = URL(fileURLWithPath: generateNewFileName(getWallpaperDirectory().path + "/" + "wallpaper", hasAlpha ? "png" : "jpg", fullPath: true))
let dataResized = resizeImageToDataSize(image, maxDataSize: 5_000_000, hasAlpha: hasAlpha)
do {
try dataResized!.write(to: destFile)
return destFile.lastPathComponent
} catch {
logger.error("FileUtils.saveWallpaperFile error: \(error.localizedDescription)")
return nil
}
}
func removeWallpaperFile(fileName: String? = nil) {
do {
try FileManager.default.contentsOfDirectory(atPath: getWallpaperDirectory().path).forEach {
if URL(fileURLWithPath: $0).lastPathComponent == fileName { try FileManager.default.removeItem(atPath: $0) }
}
} catch {
logger.error("FileUtils.removeWallpaperFile error: \(error.localizedDescription)")
}
if let fileName {
WallpaperType.cachedImages.removeValue(forKey: fileName)
}
}
func generateNewFileName(_ prefix: String, _ ext: String, fullPath: Bool = false) -> String {
uniqueCombine("\(prefix)_\(getTimestamp()).\(ext)", fullPath: fullPath)
}
+132 -52
View File
@@ -242,14 +242,8 @@ func apiSuspendChat(timeoutMicroseconds: Int) {
logger.error("apiSuspendChat error: \(String(describing: r))")
}
func apiSetTempFolder(tempFolder: String, ctrl: chat_ctrl? = nil) throws {
let r = chatSendCmdSync(.setTempFolder(tempFolder: tempFolder), ctrl)
if case .cmdOk = r { return }
throw r
}
func apiSetFilesFolder(filesFolder: String, ctrl: chat_ctrl? = nil) throws {
let r = chatSendCmdSync(.setFilesFolder(filesFolder: filesFolder), ctrl)
func apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String, ctrl: chat_ctrl? = nil) throws {
let r = chatSendCmdSync(.apiSetAppFilePaths(filesFolder: filesFolder, tempFolder: tempFolder, assetsFolder: assetsFolder), ctrl)
if case .cmdOk = r { return }
throw r
}
@@ -272,18 +266,6 @@ func apiGetAppSettings(settings: AppSettings) throws -> AppSettings {
throw r
}
func apiSetPQEncryption(_ enable: Bool) throws {
let r = chatSendCmdSync(.apiSetPQEncryption(enable: enable))
if case .cmdOk = r { return }
throw r
}
func apiSetContactPQ(_ contactId: Int64, _ enable: Bool) async throws -> Contact {
let r = await chatSendCmd(.apiSetContactPQ(contactId: contactId, enable: enable))
if case let .contactPQAllowed(_, contact, _) = r { return contact }
throw r
}
func apiExportArchive(config: ArchiveConfig) async throws {
try await sendCommandOkResp(.apiExportArchive(config: config))
}
@@ -353,8 +335,8 @@ func apiGetChatItemInfo(type: ChatType, id: Int64, itemId: Int64) async throws -
throw r
}
func apiForwardChatItem(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemId: Int64) async -> ChatItem? {
let cmd: ChatCommand = .apiForwardChatItem(toChatType: toChatType, toChatId: toChatId, fromChatType: fromChatType, fromChatId: fromChatId, itemId: itemId)
func apiForwardChatItem(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemId: Int64, ttl: Int?) async -> ChatItem? {
let cmd: ChatCommand = .apiForwardChatItem(toChatType: toChatType, toChatId: toChatId, fromChatType: fromChatType, fromChatId: fromChatId, itemId: itemId, ttl: ttl)
return await processSendMessageCmd(toChatType: toChatType, cmd: cmd)
}
@@ -573,6 +555,18 @@ func apiGroupMemberInfo(_ groupId: Int64, _ groupMemberId: Int64) throws -> (Gro
throw r
}
func apiContactQueueInfo(_ contactId: Int64) async throws -> (RcvMsgInfo?, QueueInfo) {
let r = await chatSendCmd(.apiContactQueueInfo(contactId: contactId))
if case let .queueInfo(_, rcvMsgInfo, queueInfo) = r { return (rcvMsgInfo, queueInfo) }
throw r
}
func apiGroupMemberQueueInfo(_ groupId: Int64, _ groupMemberId: Int64) async throws -> (RcvMsgInfo?, QueueInfo) {
let r = await chatSendCmd(.apiGroupMemberQueueInfo(groupId: groupId, groupMemberId: groupMemberId))
if case let .queueInfo(_, rcvMsgInfo, queueInfo) = r { return (rcvMsgInfo, queueInfo) }
throw r
}
func apiSwitchContact(contactId: Int64) throws -> ConnectionStats {
let r = chatSendCmdSync(.apiSwitchContact(contactId: contactId))
if case let .contactSwitchStarted(_, _, connectionStats) = r { return connectionStats }
@@ -831,6 +825,21 @@ func apiSetConnectionAlias(connId: Int64, localAlias: String) async throws -> Pe
throw r
}
func apiSetUserUIThemes(userId: Int64, themes: ThemeModeOverrides?) async -> Bool {
let r = await chatSendCmd(.apiSetUserUIThemes(userId: userId, themes: themes))
if case .cmdOk = r { return true }
logger.error("apiSetUserUIThemes bad response: \(String(describing: r))")
return false
}
func apiSetChatUIThemes(chatId: ChatId, themes: ThemeModeOverrides?) async -> Bool {
let r = await chatSendCmd(.apiSetChatUIThemes(chatId: chatId, themes: themes))
if case .cmdOk = r { return true }
logger.error("apiSetChatUIThemes bad response: \(String(describing: r))")
return false
}
func apiCreateUserAddress() async throws -> String {
let userId = try currentUserId("apiCreateUserAddress")
let r = await chatSendCmd(.apiCreateMyAddress(userId: userId))
@@ -939,14 +948,19 @@ func standaloneFileInfo(url: String, ctrl: chat_ctrl? = nil) async -> MigrationF
}
}
func receiveFile(user: any UserLike, fileId: Int64, auto: Bool = false) async {
if let chatItem = await apiReceiveFile(fileId: fileId, encrypted: privacyEncryptLocalFilesGroupDefault.get(), auto: auto) {
func receiveFile(user: any UserLike, fileId: Int64, userApprovedRelays: Bool = false, auto: Bool = false) async {
if let chatItem = await apiReceiveFile(
fileId: fileId,
userApprovedRelays: userApprovedRelays || !privacyAskToApproveRelaysGroupDefault.get(),
encrypted: privacyEncryptLocalFilesGroupDefault.get(),
auto: auto
) {
await chatItemSimpleUpdate(user, chatItem)
}
}
func apiReceiveFile(fileId: Int64, encrypted: Bool, inline: Bool? = nil, auto: Bool = false) async -> AChatItem? {
let r = await chatSendCmd(.receiveFile(fileId: fileId, encrypted: encrypted, inline: inline))
func apiReceiveFile(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool, inline: Bool? = nil, auto: Bool = false) async -> AChatItem? {
let r = await chatSendCmd(.receiveFile(fileId: fileId, userApprovedRelays: userApprovedRelays, encrypted: encrypted, inline: inline))
let am = AlertManager.shared
if case let .rcvFileAccepted(_, chatItem) = r { return chatItem }
if case .rcvFileAcceptedSndCancelled = r {
@@ -959,19 +973,50 @@ func apiReceiveFile(fileId: Int64, encrypted: Bool, inline: Bool? = nil, auto: B
}
} else if let networkErrorAlert = networkErrorAlert(r) {
logger.error("apiReceiveFile network error: \(String(describing: r))")
am.showAlert(networkErrorAlert)
if !auto {
am.showAlert(networkErrorAlert)
}
} else {
switch chatError(r) {
case .fileCancelled:
logger.debug("apiReceiveFile ignoring fileCancelled error")
case .fileAlreadyReceiving:
logger.debug("apiReceiveFile ignoring fileAlreadyReceiving error")
case let .fileNotApproved(fileId, unknownServers):
logger.debug("apiReceiveFile fileNotApproved error")
if !auto {
let srvs = unknownServers.map { s in
if let srv = parseServerAddress(s), !srv.hostnames.isEmpty {
srv.hostnames[0]
} else {
serverHost(s)
}
}
am.showAlert(Alert(
title: Text("Unknown servers!"),
message: Text("Without Tor or VPN, your IP address will be visible to these XFTP relays: \(srvs.sorted().joined(separator: ", "))."),
primaryButton: .default(
Text("Download"),
action: {
Task {
logger.debug("apiReceiveFile fileNotApproved alert - in Task")
if let user = ChatModel.shared.currentUser {
await receiveFile(user: user, fileId: fileId, userApprovedRelays: true)
}
}
}
),
secondaryButton: .cancel()
))
}
default:
logger.error("apiReceiveFile error: \(String(describing: r))")
am.showAlertMsg(
title: "Error receiving file",
message: "Error: \(String(describing: r))"
)
if !auto {
am.showAlertMsg(
title: "Error receiving file",
message: "Error: \(String(describing: r))"
)
}
}
}
return nil
@@ -1241,7 +1286,7 @@ func filterMembersToAdd(_ ms: [GMember]) -> [Contact] {
let memberContactIds = ms.compactMap{ m in m.wrapped.memberCurrent ? m.wrapped.memberContactId : nil }
return ChatModel.shared.chats
.compactMap{ $0.chatInfo.contact }
.filter{ c in c.ready && c.active && !memberContactIds.contains(c.apiId) }
.filter{ c in c.sendMsgEnabled && !c.nextSendGrpInv && !memberContactIds.contains(c.apiId) }
.sorted{ $0.displayName.lowercased() < $1.displayName.lowercased() }
}
@@ -1317,10 +1362,8 @@ func initializeChat(start: Bool, confirmStart: Bool = false, dbKey: String? = ni
if encryptionStartedDefault.get() {
encryptionStartedDefault.set(false)
}
try apiSetTempFolder(tempFolder: getTempFilesDirectory().path)
try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path)
try apiSetAppFilePaths(filesFolder: getAppFilesDirectory().path, tempFolder: getTempFilesDirectory().path, assetsFolder: getWallpaperDirectory().deletingLastPathComponent().path)
try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get())
try apiSetPQEncryption(pqExperimentalEnabledDefault.get())
m.chatInitialized = true
m.currentUser = try apiGetActiveUser()
if m.currentUser == nil {
@@ -1404,8 +1447,7 @@ func startChatWithTemporaryDatabase(ctrl: chat_ctrl) throws -> User? {
logger.debug("startChatWithTemporaryDatabase")
let migrationActiveUser = try? apiGetActiveUser(ctrl: ctrl) ?? apiCreateActiveUser(Profile(displayName: "Temp", fullName: ""), ctrl: ctrl)
try setNetworkConfig(getNetCfg(), ctrl: ctrl)
try apiSetTempFolder(tempFolder: getMigrationTempFilesDirectory().path, ctrl: ctrl)
try apiSetFilesFolder(filesFolder: getMigrationTempFilesDirectory().path, ctrl: ctrl)
try apiSetAppFilePaths(filesFolder: getMigrationTempFilesDirectory().path, tempFolder: getMigrationTempFilesDirectory().path, assetsFolder: getWallpaperDirectory().deletingLastPathComponent().path, ctrl: ctrl)
_ = try apiStartChat(ctrl: ctrl)
return migrationActiveUser
}
@@ -1791,11 +1833,15 @@ func processReceivedMsg(_ res: ChatResponse) async {
if let aChatItem = aChatItem {
await chatItemSimpleUpdate(user, aChatItem)
}
case let .rcvFileError(user, aChatItem, _):
case let .rcvFileError(user, aChatItem, _, _):
if let aChatItem = aChatItem {
await chatItemSimpleUpdate(user, aChatItem)
Task { cleanupFile(aChatItem) }
}
case let .rcvFileWarning(user, aChatItem, _, _):
if let aChatItem = aChatItem {
await chatItemSimpleUpdate(user, aChatItem)
}
case let .sndFileStart(user, aChatItem, _):
await chatItemSimpleUpdate(user, aChatItem)
case let .sndFileComplete(user, aChatItem, _):
@@ -1812,11 +1858,15 @@ func processReceivedMsg(_ res: ChatResponse) async {
}
case let .sndFileCompleteXFTP(user, aChatItem, _):
await chatItemSimpleUpdate(user, aChatItem)
case let .sndFileError(user, aChatItem, _):
case let .sndFileError(user, aChatItem, _, _):
if let aChatItem = aChatItem {
await chatItemSimpleUpdate(user, aChatItem)
Task { cleanupFile(aChatItem) }
}
case let .sndFileWarning(user, aChatItem, _, _):
if let aChatItem = aChatItem {
await chatItemSimpleUpdate(user, aChatItem)
}
case let .callInvitation(invitation):
await MainActor.run {
m.callInvitations[invitation.contact.id] = invitation
@@ -1862,21 +1912,35 @@ func processReceivedMsg(_ res: ChatResponse) async {
}
case .chatSuspended:
chatSuspended()
case let .contactSwitch(_, contact, switchProgress):
await MainActor.run {
m.updateContactConnectionStats(contact, switchProgress.connectionStats)
case let .contactSwitch(user, contact, switchProgress):
if active(user) {
await MainActor.run {
m.updateContactConnectionStats(contact, switchProgress.connectionStats)
}
}
case let .groupMemberSwitch(_, groupInfo, member, switchProgress):
await MainActor.run {
m.updateGroupMemberConnectionStats(groupInfo, member, switchProgress.connectionStats)
case let .groupMemberSwitch(user, groupInfo, member, switchProgress):
if active(user) {
await MainActor.run {
m.updateGroupMemberConnectionStats(groupInfo, member, switchProgress.connectionStats)
}
}
case let .contactRatchetSync(_, contact, ratchetSyncProgress):
await MainActor.run {
m.updateContactConnectionStats(contact, ratchetSyncProgress.connectionStats)
case let .contactRatchetSync(user, contact, ratchetSyncProgress):
if active(user) {
await MainActor.run {
m.updateContactConnectionStats(contact, ratchetSyncProgress.connectionStats)
}
}
case let .groupMemberRatchetSync(_, groupInfo, member, ratchetSyncProgress):
await MainActor.run {
m.updateGroupMemberConnectionStats(groupInfo, member, ratchetSyncProgress.connectionStats)
case let .groupMemberRatchetSync(user, groupInfo, member, ratchetSyncProgress):
if active(user) {
await MainActor.run {
m.updateGroupMemberConnectionStats(groupInfo, member, ratchetSyncProgress.connectionStats)
}
}
case let .contactDisabled(user, contact):
if active(user) {
await MainActor.run {
m.updateContact(contact)
}
}
case let .remoteCtrlFound(remoteCtrl, ctrlAppInfo_, appVersion, compatible):
await MainActor.run {
@@ -1900,12 +1964,28 @@ func processReceivedMsg(_ res: ChatResponse) async {
let state = UIRemoteCtrlSessionState.connected(remoteCtrl: remoteCtrl, sessionCode: m.remoteCtrlSession?.sessionCode ?? "")
m.remoteCtrlSession = m.remoteCtrlSession?.updateState(state)
}
case .remoteCtrlStopped:
case let .remoteCtrlStopped(_, rcStopReason):
// This delay is needed to cancel the session that fails on network failure,
// e.g. when user did not grant permission to access local network yet.
if let sess = m.remoteCtrlSession {
await MainActor.run {
m.remoteCtrlSession = nil
dismissAllSheets() {
switch rcStopReason {
case .connectionFailed(.errorAgent(.RCP(.identity))):
AlertManager.shared.showAlertMsg(
title: "Connection with desktop stopped",
message: "This link was used with another mobile device, please create a new link on the desktop."
)
default:
AlertManager.shared.showAlert(Alert(
title: Text("Connection with desktop stopped"),
message: Text("Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers."),
primaryButton: .default(Text("Ok")),
secondaryButton: .default(Text("Copy error")) { UIPasteboard.general.string = String(describing: rcStopReason) }
))
}
}
}
if case .connected = sess.sessionState {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
+1
View File
@@ -39,6 +39,7 @@ struct SimpleXApp: App {
// so that it's computed by the time view renders, and not on event after rendering
ContentView(contentAccessAuthenticationExtended: !authenticationExpired())
.environmentObject(chatModel)
.environmentObject(AppTheme.shared)
.onOpenURL { url in
logger.debug("ContentView.onOpenURL: \(url)")
chatModel.appOpenUrl = url
+121
View File
@@ -0,0 +1,121 @@
//
// ThemeShared.swift
// SimpleX (iOS)
//
// Created by Avently on 14.06.2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import Foundation
import SwiftUI
import SimpleXChat
var CurrentColors: ThemeManager.ActiveTheme = ThemeManager.currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get()) {
didSet {
AppTheme.shared.name = CurrentColors.name
AppTheme.shared.base = CurrentColors.base
AppTheme.shared.colors.updateColorsFrom(CurrentColors.colors)
AppTheme.shared.appColors.updateColorsFrom(CurrentColors.appColors)
AppTheme.shared.wallpaper.updateWallpaperFrom(CurrentColors.wallpaper)
AppTheme.shared.objectWillChange.send()
}
}
var MenuTextColor: Color { if isInDarkTheme() { AppTheme.shared.colors.onBackground.opacity(0.8) } else { Color.black } }
var NoteFolderIconColor: Color { AppTheme.shared.appColors.primaryVariant2 }
func isInDarkTheme() -> Bool { !CurrentColors.colors.isLight }
class AppTheme: ObservableObject {
static let shared = AppTheme(name: CurrentColors.name, base: CurrentColors.base, colors: CurrentColors.colors, appColors: CurrentColors.appColors, wallpaper: CurrentColors.wallpaper)
var name: String
var base: DefaultTheme
@ObservedObject var colors: Colors
@ObservedObject var appColors: AppColors
@ObservedObject var wallpaper: AppWallpaper
init(name: String, base: DefaultTheme, colors: Colors, appColors: AppColors, wallpaper: AppWallpaper) {
self.name = name
self.base = base
self.colors = colors
self.appColors = appColors
self.wallpaper = wallpaper
}
}
struct ThemedBackground: ViewModifier {
@EnvironmentObject var theme: AppTheme
func body(content: Content) -> some View {
content
.background(
theme.base == DefaultTheme.SIMPLEX
? LinearGradient(
colors: [
theme.colors.background.lighter(0.4),
theme.colors.background.darker(0.4)
],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
: LinearGradient(
colors: [],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
.background(
theme.base == DefaultTheme.SIMPLEX
? Color.clear
: theme.colors.background
)
}
}
func reactOnDarkThemeChanges(_ isDark: Bool) {
systemInDarkThemeCurrently = isDark
//sceneDelegate.window?.overrideUserInterfaceStyle == .unspecified
if currentThemeDefault.get() == DefaultTheme.SYSTEM_THEME_NAME && CurrentColors.colors.isLight == isDark {
// Change active colors from light to dark and back based on system theme
ThemeManager.applyTheme(DefaultTheme.SYSTEM_THEME_NAME)
}
}
extension ThemeWallpaper {
public func importFromString() -> ThemeWallpaper {
if preset == nil, let image {
// Need to save image from string and to save its path
if let data = Data(base64Encoded: dropImagePrefix(image)),
let parsed = UIImage(data: data),
let filename = saveWallpaperFile(image: parsed) {
var copy = self
copy.image = nil
copy.imageFile = filename
return copy
} else {
return ThemeWallpaper()
}
} else {
return self
}
}
func withFilledWallpaperBase64() -> ThemeWallpaper {
let aw = toAppWallpaper()
let type = aw.type
let preset: String? = if case let WallpaperType.Preset(filename, _) = type { filename } else { nil }
let scale: Float? = if case let WallpaperType.Preset(_, scale) = type { scale } else { if case let WallpaperType.Image(_, scale, _) = type { scale } else { 1.0 } }
let scaleType: WallpaperScaleType? = if case let WallpaperType.Image(_, _, scaleType) = type { scaleType } else { nil }
let image: String? = if case WallpaperType.Image = type, let image = type.uiImage { resizeImageToStrSize(image, maxDataSize: 5_000_000) } else { nil }
return ThemeWallpaper (
preset: preset,
scale: scale,
scaleType: scaleType,
background: aw.background?.toReadableHex(),
tint: aw.tint?.toReadableHex(),
image: image,
imageFile: nil
)
}
}
+263
View File
@@ -0,0 +1,263 @@
//
// ThemeManager.swift
// SimpleX (iOS)
//
// Created by Avently on 03.06.2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import Foundation
import SwiftUI
import SimpleXChat
class ThemeManager {
struct ActiveTheme {
let name: String
let base: DefaultTheme
let colors: Colors
let appColors: AppColors
var wallpaper: AppWallpaper = AppWallpaper(background: nil, tint: nil, type: .Empty)
}
private static func systemDarkThemeColors() -> (Colors, DefaultTheme) {
switch systemDarkThemeDefault.get() {
case DefaultTheme.DARK.themeName: (DarkColorPalette, DefaultTheme.DARK)
case DefaultTheme.SIMPLEX.themeName: (SimplexColorPalette, DefaultTheme.SIMPLEX)
case DefaultTheme.BLACK.themeName: (BlackColorPalette, DefaultTheme.BLACK)
default: (SimplexColorPalette, DefaultTheme.SIMPLEX)
}
}
private static func nonSystemThemeName() -> String {
let themeName = currentThemeDefault.get()
return if themeName != DefaultTheme.SYSTEM_THEME_NAME {
themeName
} else {
systemInDarkThemeCurrently ? systemDarkThemeDefault.get() : DefaultTheme.LIGHT.themeName
}
}
static func defaultActiveTheme(_ appSettingsTheme: [ThemeOverrides]) -> ThemeOverrides? {
let nonSystemThemeName = nonSystemThemeName()
let defaultThemeId = currentThemeIdsDefault.get()[nonSystemThemeName]
return appSettingsTheme.getTheme(defaultThemeId)
}
static func defaultActiveTheme(_ perUserTheme: ThemeModeOverrides?, _ appSettingsTheme: [ThemeOverrides]) -> ThemeModeOverride {
let perUserTheme = !CurrentColors.colors.isLight ? perUserTheme?.dark : perUserTheme?.light
if let perUserTheme {
return perUserTheme
}
let defaultTheme = defaultActiveTheme(appSettingsTheme)
return ThemeModeOverride(mode: CurrentColors.base.mode, colors: defaultTheme?.colors ?? ThemeColors(), wallpaper: defaultTheme?.wallpaper)
}
static func currentColors(_ themeOverridesForType: WallpaperType?, _ perChatTheme: ThemeModeOverride?, _ perUserTheme: ThemeModeOverrides?, _ appSettingsTheme: [ThemeOverrides]) -> ActiveTheme {
let themeName = currentThemeDefault.get()
let nonSystemThemeName = nonSystemThemeName()
let defaultTheme = defaultActiveTheme(appSettingsTheme)
let baseTheme = switch nonSystemThemeName {
case DefaultTheme.LIGHT.themeName: ActiveTheme(name: DefaultTheme.LIGHT.themeName, base: DefaultTheme.LIGHT, colors: LightColorPalette.clone(), appColors: LightColorPaletteApp.clone(), wallpaper: AppWallpaper(background: nil, tint: nil, type: PresetWallpaper.school.toType(DefaultTheme.LIGHT)))
case DefaultTheme.DARK.themeName: ActiveTheme(name: DefaultTheme.DARK.themeName, base: DefaultTheme.DARK, colors: DarkColorPalette.clone(), appColors: DarkColorPaletteApp.clone(), wallpaper: AppWallpaper(background: nil, tint: nil, type: PresetWallpaper.school.toType(DefaultTheme.DARK)))
case DefaultTheme.SIMPLEX.themeName: ActiveTheme(name: DefaultTheme.SIMPLEX.themeName, base: DefaultTheme.SIMPLEX, colors: SimplexColorPalette.clone(), appColors: SimplexColorPaletteApp.clone(), wallpaper: AppWallpaper(background: nil, tint: nil, type: PresetWallpaper.school.toType(DefaultTheme.SIMPLEX)))
case DefaultTheme.BLACK.themeName: ActiveTheme(name: DefaultTheme.BLACK.themeName, base: DefaultTheme.BLACK, colors: BlackColorPalette.clone(), appColors: BlackColorPaletteApp.clone(), wallpaper: AppWallpaper(background: nil, tint: nil, type: PresetWallpaper.school.toType(DefaultTheme.BLACK)))
default: ActiveTheme(name: DefaultTheme.LIGHT.themeName, base: DefaultTheme.LIGHT, colors: LightColorPalette.clone(), appColors: LightColorPaletteApp.clone(), wallpaper: AppWallpaper(background: nil, tint: nil, type: PresetWallpaper.school.toType(DefaultTheme.LIGHT)))
}
let perUserTheme = baseTheme.colors.isLight ? perUserTheme?.light : perUserTheme?.dark
let theme = appSettingsTheme.sameTheme(themeOverridesForType ?? perChatTheme?.type ?? perUserTheme?.type ?? defaultTheme?.wallpaper?.toAppWallpaper().type, nonSystemThemeName) ?? defaultTheme
if theme == nil && perUserTheme == nil && perChatTheme == nil && themeOverridesForType == nil {
return ActiveTheme(name: themeName, base: baseTheme.base, colors: baseTheme.colors, appColors: baseTheme.appColors, wallpaper: baseTheme.wallpaper)
}
let presetWallpaperTheme: ThemeColors? = if let wallpaper = perChatTheme?.wallpaper {
if let preset = wallpaper.preset { PresetWallpaper.from(preset)?.colors[baseTheme.base] } else { nil }
} else if let wallpaper = perUserTheme?.wallpaper {
if let preset = wallpaper.preset { PresetWallpaper.from(preset)?.colors[baseTheme.base] } else { nil }
} else {
if let preset = theme?.wallpaper?.preset { PresetWallpaper.from(preset)?.colors[baseTheme.base] } else { nil }
}
let themeOrEmpty = theme ?? ThemeOverrides(base: baseTheme.base)
let colors = themeOrEmpty.toColors(themeOrEmpty.base, perChatTheme?.colors, perUserTheme?.colors, presetWallpaperTheme)
return ActiveTheme(
name: themeName,
base: baseTheme.base,
colors: colors,
appColors: themeOrEmpty.toAppColors(themeOrEmpty.base, perChatTheme?.colors, perChatTheme?.type, perUserTheme?.colors, perUserTheme?.type, presetWallpaperTheme),
wallpaper: themeOrEmpty.toAppWallpaper(themeOverridesForType, perChatTheme, perUserTheme, colors.background)
)
}
static func currentThemeOverridesForExport(_ perChatTheme: ThemeModeOverride?, _ perUserTheme: ThemeModeOverrides?) -> ThemeOverrides {
let current = currentColors(nil, perChatTheme, perUserTheme, themeOverridesDefault.get())
let wType = current.wallpaper.type
let wBackground = current.wallpaper.background
let wTint = current.wallpaper.tint
let w: ThemeWallpaper? = if case WallpaperType.Empty = wType {
nil
} else {
ThemeWallpaper.from(wType, wBackground?.toReadableHex(), wTint?.toReadableHex()).withFilledWallpaperBase64()
}
return ThemeOverrides(
themeId: "",
base: current.base,
colors: ThemeColors.from(current.colors, current.appColors),
wallpaper: w
)
}
static func applyTheme(_ theme: String) {
currentThemeDefault.set(theme)
CurrentColors = currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get())
SceneDelegate.windowStatic?.tintColor = UIColor(CurrentColors.colors.primary)
SceneDelegate.windowStatic?.backgroundColor = UIColor(CurrentColors.colors.background)
SceneDelegate.windowStatic?.overrideUserInterfaceStyle = switch currentThemeDefault.get() {
case DefaultTheme.LIGHT.themeName: .light
case DefaultTheme.SYSTEM_THEME_NAME: .unspecified
default: .dark
}
}
static func changeDarkTheme(_ theme: String) {
systemDarkThemeDefault.set(theme)
CurrentColors = currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get())
}
static func saveAndApplyThemeColor(_ baseTheme: DefaultTheme, _ name: ThemeColor, _ color: Color? = nil, _ pref: CodableDefault<[ThemeOverrides]>? = nil) {
let nonSystemThemeName = baseTheme.themeName
let pref = pref ?? themeOverridesDefault
let overrides = pref.get()
let themeId = currentThemeIdsDefault.get()[nonSystemThemeName]
let prevValue = overrides.getTheme(themeId) ?? ThemeOverrides(base: baseTheme)
pref.set(overrides.replace(prevValue.withUpdatedColor(name, color?.toReadableHex())))
var themeIds = currentThemeIdsDefault.get()
themeIds[nonSystemThemeName] = prevValue.themeId
currentThemeIdsDefault.set(themeIds)
CurrentColors = currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get())
}
static func applyThemeColor(name: ThemeColor, color: Color? = nil, pref: Binding<ThemeModeOverride>) {
pref.wrappedValue = pref.wrappedValue.withUpdatedColor(name, color?.toReadableHex())
}
static func saveAndApplyWallpaper(_ baseTheme: DefaultTheme, _ type: WallpaperType?, _ pref: CodableDefault<[ThemeOverrides]>?) {
let nonSystemThemeName = baseTheme.themeName
let pref = pref ?? themeOverridesDefault
let overrides = pref.get()
let theme = overrides.sameTheme(type, baseTheme.themeName)
var prevValue = theme ?? ThemeOverrides(base: baseTheme)
prevValue.wallpaper = if let type {
if case WallpaperType.Empty = type {
nil as ThemeWallpaper?
} else {
ThemeWallpaper.from(type, prevValue.wallpaper?.background, prevValue.wallpaper?.tint)
}
} else {
nil
}
pref.set(overrides.replace(prevValue))
var themeIds = currentThemeIdsDefault.get()
themeIds[nonSystemThemeName] = prevValue.themeId
currentThemeIdsDefault.set(themeIds)
CurrentColors = currentColors( nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get())
}
static func copyFromSameThemeOverrides(_ type: WallpaperType?, _ lowerLevelOverride: ThemeModeOverride?, _ pref: Binding<ThemeModeOverride>) -> Bool {
let overrides = themeOverridesDefault.get()
let sameWallpaper: ThemeWallpaper? = if let wallpaper = lowerLevelOverride?.wallpaper, lowerLevelOverride?.type?.sameType(type) == true {
wallpaper
} else {
overrides.sameTheme(type, CurrentColors.base.themeName)?.wallpaper
}
guard let sameWallpaper else {
if let type {
var w: ThemeWallpaper = ThemeWallpaper.from(type, nil, nil)
w.scale = nil
w.scaleType = nil
pref.wrappedValue = ThemeModeOverride(mode: CurrentColors.base.mode, wallpaper: w)
} else {
// Make an empty wallpaper to override any top level ones
pref.wrappedValue = ThemeModeOverride(mode: CurrentColors.base.mode, wallpaper: ThemeWallpaper())
}
return true
}
var type = sameWallpaper.toAppWallpaper().type
if case let WallpaperType.Image(filename, scale, scaleType) = type, sameWallpaper.imageFile == filename {
// same image file. Needs to be copied first in order to be able to remove the file once it's not needed anymore without affecting main theme override
if let filename = saveWallpaperFile(url: getWallpaperFilePath(filename)) {
type = WallpaperType.Image(filename, scale, scaleType)
} else {
logger.error("Error while copying wallpaper from global overrides to chat overrides")
return false
}
}
var prevValue = pref.wrappedValue
var w = ThemeWallpaper.from(type, nil, nil)
w.scale = nil
w.scaleType = nil
prevValue.colors = ThemeColors()
prevValue.wallpaper = w
pref.wrappedValue = prevValue
return true
}
static func applyWallpaper(_ type: WallpaperType?, _ pref: Binding<ThemeModeOverride>) {
var prevValue = pref.wrappedValue
prevValue.wallpaper = if let type {
ThemeWallpaper.from(type, prevValue.wallpaper?.background, prevValue.wallpaper?.tint)
} else {
nil
}
pref.wrappedValue = prevValue
}
static func saveAndApplyThemeOverrides(_ theme: ThemeOverrides, _ pref: CodableDefault<[ThemeOverrides]>? = nil) {
let wallpaper = theme.wallpaper?.importFromString()
let nonSystemThemeName = theme.base.themeName
let pref: CodableDefault<[ThemeOverrides]> = pref ?? themeOverridesDefault
let overrides = pref.get()
var prevValue = overrides.getTheme(nil, wallpaper?.toAppWallpaper().type, theme.base) ?? ThemeOverrides(base: theme.base)
if let imageFile = prevValue.wallpaper?.imageFile {
try? FileManager.default.removeItem(at: getWallpaperFilePath(imageFile))
}
prevValue.base = theme.base
prevValue.colors = theme.colors
prevValue.wallpaper = wallpaper
pref.set(overrides.replace(prevValue))
currentThemeDefault.set(nonSystemThemeName)
var currentThemeIds = currentThemeIdsDefault.get()
currentThemeIds[nonSystemThemeName] = prevValue.themeId
currentThemeIdsDefault.set(currentThemeIds)
CurrentColors = currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get())
}
static func resetAllThemeColors(_ pref: CodableDefault<[ThemeOverrides]>? = nil) {
let nonSystemThemeName = nonSystemThemeName()
let pref: CodableDefault<[ThemeOverrides]> = pref ?? themeOverridesDefault
let overrides = pref.get()
guard let themeId = currentThemeIdsDefault.get()[nonSystemThemeName],
var prevValue = overrides.getTheme(themeId)
else { return }
prevValue.colors = ThemeColors()
prevValue.wallpaper?.background = nil
prevValue.wallpaper?.tint = nil
pref.set(overrides.replace(prevValue))
CurrentColors = currentColors(nil, nil, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get())
}
static func resetAllThemeColors(_ pref: Binding<ThemeModeOverride>) {
var prevValue = pref.wrappedValue
prevValue.colors = ThemeColors()
prevValue.wallpaper?.background = nil
prevValue.wallpaper?.tint = nil
pref.wrappedValue = prevValue
}
static func removeTheme(_ themeId: String?) {
var themes = themeOverridesDefault.get().map { $0 }
themes.removeAll(where: { $0.themeId == themeId })
themeOverridesDefault.set(themes)
}
}
+42 -11
View File
@@ -110,8 +110,8 @@ struct ActiveCallView: View {
call.callState = .invitationSent
call.localCapabilities = capabilities
}
if call.supportsVideo {
try? AVAudioSession.sharedInstance().setCategory(.playback, options: .defaultToSpeaker)
if call.supportsVideo && !AVAudioSession.sharedInstance().hasExternalAudioDevice() {
try? AVAudioSession.sharedInstance().setCategory(.playback, options: [.allowBluetooth, .allowAirPlay, .allowBluetoothA2DP])
}
CallSoundsPlayer.shared.startConnectingCallSound()
activeCallWaitDeliveryReceipt()
@@ -139,7 +139,6 @@ struct ActiveCallView: View {
await MainActor.run {
call.callState = .negotiated
CallSoundsPlayer.shared.stop()
try? AVAudioSession.sharedInstance().setCategory(.soloAmbient)
}
}
case let .ice(iceCandidates):
@@ -236,6 +235,7 @@ struct ActiveCallOverlay: View {
@EnvironmentObject var chatModel: ChatModel
@ObservedObject var call: Call
var client: WebRTCClient
@ObservedObject private var deviceManager = CallAudioDeviceManager.shared
var body: some View {
VStack {
@@ -251,7 +251,16 @@ struct ActiveCallOverlay: View {
HStack {
toggleAudioButton()
Spacer()
Color.clear.frame(width: 40, height: 40)
if deviceManager.availableInputs.allSatisfy({ $0.portType == .builtInMic }) {
toggleSpeakerButton()
.frame(width: 40, height: 40)
} else if call.hasMedia {
AudioDevicePicker()
.scaleEffect(2)
.frame(maxWidth: 40, maxHeight: 40)
} else {
Color.clear.frame(width: 40, height: 40)
}
Spacer()
endCallButton()
Spacer()
@@ -277,9 +286,7 @@ struct ActiveCallOverlay: View {
.foregroundColor(.white.opacity(0.8))
}
VStack {
ProfileImage(imageStr: call.contact.profile.image)
.scaledToFit()
.frame(width: 192, height: 192)
ProfileImage(imageStr: call.contact.profile.image, size: 192)
audioCallInfoView(call)
}
.foregroundColor(.white)
@@ -294,14 +301,33 @@ struct ActiveCallOverlay: View {
toggleAudioButton()
.frame(maxWidth: .infinity, alignment: .leading)
endCallButton()
toggleSpeakerButton()
.frame(maxWidth: .infinity, alignment: .trailing)
// Check if the only input is microphone. And in this case show toggle button,
// If there are more inputs, it probably means something like bluetooth headphones are available
// and in this case show iOS button for choosing different output.
// There is no way to get available outputs, only inputs
if deviceManager.availableInputs.allSatisfy({ $0.portType == .builtInMic }) {
toggleSpeakerButton()
.frame(maxWidth: .infinity, alignment: .trailing)
} else if call.hasMedia {
AudioDevicePicker()
.scaleEffect(2)
.frame(maxWidth: 50, maxHeight: 40)
.frame(maxWidth: .infinity, alignment: .trailing)
} else {
Color.clear.frame(width: 50, height: 40)
}
}
.padding(.bottom, 60)
.padding(.horizontal, 48)
}
}
.frame(maxWidth: .infinity)
.onAppear {
deviceManager.start()
}
.onDisappear {
deviceManager.stop()
}
}
private func audioCallInfoView(_ call: Call) -> some View {
@@ -379,12 +405,17 @@ struct ActiveCallOverlay: View {
private func toggleSpeakerButton() -> some View {
controlButton(call, call.speakerEnabled ? "speaker.wave.2.fill" : "speaker.wave.1.fill") {
Task {
client.setSpeakerEnabledAndConfigureSession(!call.speakerEnabled)
let speakerEnabled = AVAudioSession.sharedInstance().currentRoute.outputs.first?.portType == .builtInSpeaker
client.setSpeakerEnabledAndConfigureSession(!speakerEnabled)
DispatchQueue.main.async {
call.speakerEnabled = !call.speakerEnabled
call.speakerEnabled = !speakerEnabled
}
}
}
.onAppear {
deviceManager.call = call
//call.speakerEnabled = AVAudioSession.sharedInstance().currentRoute.outputs.first?.portType == .builtInSpeaker
}
}
private func toggleVideoButton() -> some View {
@@ -0,0 +1,25 @@
//
// MPVolumeView.swift
// SimpleX (iOS)
//
// Created by Avently on 24.04.2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import Foundation
import SwiftUI
import UIKit
import AVKit
struct AudioDevicePicker: UIViewRepresentable {
func makeUIView(context: Context) -> some UIView {
let v = AVRoutePickerView(frame: .zero)
v.activeTintColor = .white
v.tintColor = .white
return v
}
func updateUIView(_ uiView: UIViewType, context: Context) {
}
}
@@ -0,0 +1,67 @@
//
// CallAudioDeviceManager.swift
// SimpleX (iOS)
//
// Created by Avently on 23.04.2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import Foundation
import SwiftUI
import SimpleXChat
import AVKit
import WebRTC
class CallAudioDeviceManager: ObservableObject {
static let shared = CallAudioDeviceManager()
let audioSession: AVAudioSession
let nc = NotificationCenter.default
var call: Call?
var timer: Timer? = nil
// Actually, only one output
@Published var outputs: [AVAudioSessionPortDescription]
@Published var currentDevice: AVAudioSessionPortDescription? = nil
// All devices that can record audio (the ones that can play audio are not included)
@Published var availableInputs: [AVAudioSessionPortDescription] = []
init(_ audioSession: AVAudioSession? = nil) {
self.audioSession = audioSession ?? RTCAudioSession.sharedInstance().session
self.outputs = self.audioSession.currentRoute.outputs
self.availableInputs = self.audioSession.availableInputs ?? []
}
func reloadDevices() {
outputs = audioSession.currentRoute.outputs
currentDevice = audioSession.currentRoute.outputs.first
availableInputs = audioSession.availableInputs ?? []
call?.speakerEnabled = currentDevice?.portType == .builtInSpeaker
// Workaround situation:
// have bluetooth device connected, choosing speaker, disconnecting bluetooth device. In this case iOS will not post notification, so do it manually
timer?.invalidate()
if availableInputs.contains(where: { $0.portType != .builtInReceiver && $0.portType != .builtInSpeaker }) {
timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: false) { t in
self.reloadDevices()
}
}
}
@objc func audioCallback(notification: Notification) {
reloadDevices()
logger.debug("Changes in devices, current audio devices: \(String(describing: self.availableInputs.map({ $0.portType.rawValue }))), output: \(String(describing: self.currentDevice?.portType.rawValue))")
}
func start() {
nc.addObserver(self, selector: #selector(audioCallback), name: AVAudioSession.routeChangeNotification, object: nil)
}
func stop() {
nc.removeObserver(self, name: AVAudioSession.routeChangeNotification, object: nil)
timer?.invalidate()
}
}
@@ -103,7 +103,23 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
RTCAudioSession.sharedInstance().audioSessionDidActivate(audioSession)
RTCAudioSession.sharedInstance().isAudioEnabled = true
do {
try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: .mixWithOthers)
let supportsVideo = ChatModel.shared.activeCall?.supportsVideo == true
if supportsVideo {
try audioSession.setCategory(.playAndRecord, mode: .videoChat, options: [.defaultToSpeaker, .mixWithOthers, .allowBluetooth, .allowAirPlay, .allowBluetoothA2DP])
} else {
try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: [.mixWithOthers, .allowBluetooth, .allowAirPlay, .allowBluetoothA2DP])
}
// Without any delay sound is not playing from speaker or external device in incoming call
Task {
for i in 0 ... 3 {
try? await Task.sleep(nanoseconds: UInt64(i) * 300_000000)
if let preferred = audioSession.preferredInputDevice() {
await MainActor.run { try? audioSession.setPreferredInput(preferred) }
} else if supportsVideo {
await MainActor.run { try? audioSession.overrideOutputAudioPort(.speaker) }
}
}
}
logger.debug("audioSession category set")
try audioSession.setActive(true)
logger.debug("audioSession activated")
@@ -11,6 +11,7 @@ import SimpleXChat
struct IncomingCallView: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@ObservedObject var cc = CallController.shared
var body: some View {
@@ -30,8 +31,7 @@ struct IncomingCallView: View {
VStack(alignment: .leading, spacing: 6) {
HStack {
if m.users.count > 1 {
ProfileImage(imageStr: invitation.user.image, color: .white)
.frame(width: 24, height: 24)
ProfileImage(imageStr: invitation.user.image, size: 24, color: .white)
}
Image(systemName: invitation.callType.media == .video ? "video.fill" : "phone.fill").foregroundColor(.green)
Text(invitation.callTypeText)
@@ -44,7 +44,7 @@ struct IncomingCallView: View {
cc.endCall(invitation: invitation)
}
callButton("Ignore", "multiply", .accentColor) {
callButton("Ignore", "multiply", theme.colors.primary) {
cc.activeCallInvitation = nil
}
@@ -64,7 +64,7 @@ struct IncomingCallView: View {
.padding(.horizontal, 16)
.padding(.vertical, 12)
.frame(maxWidth: .infinity)
.background(Color(uiColor: .tertiarySystemGroupedBackground))
.modifier(ThemedBackground())
.onAppear { dismissAllSheets() }
}
+28 -3
View File
@@ -605,9 +605,23 @@ extension WebRTCClient {
self.rtcAudioSession.unlockForConfiguration()
}
do {
try self.rtcAudioSession.setCategory(AVAudioSession.Category.playAndRecord.rawValue)
try self.rtcAudioSession.setMode(AVAudioSession.Mode.voiceChat.rawValue)
try self.rtcAudioSession.overrideOutputAudioPort(enabled ? .speaker : .none)
let hasExternalAudioDevice = self.rtcAudioSession.session.hasExternalAudioDevice()
if enabled {
try self.rtcAudioSession.setCategory(AVAudioSession.Category.playAndRecord.rawValue, with: [.defaultToSpeaker, .allowBluetooth, .allowAirPlay, .allowBluetoothA2DP])
try self.rtcAudioSession.setMode(AVAudioSession.Mode.videoChat.rawValue)
if hasExternalAudioDevice, let preferred = self.rtcAudioSession.session.preferredInputDevice() {
try self.rtcAudioSession.setPreferredInput(preferred)
} else {
try self.rtcAudioSession.overrideOutputAudioPort(.speaker)
}
} else {
try self.rtcAudioSession.setCategory(AVAudioSession.Category.playAndRecord.rawValue, with: [.allowBluetooth, .allowAirPlay, .allowBluetoothA2DP])
try self.rtcAudioSession.setMode(AVAudioSession.Mode.voiceChat.rawValue)
try self.rtcAudioSession.overrideOutputAudioPort(.none)
}
if hasExternalAudioDevice {
logger.debug("WebRTCClient: configuring session with external device available, skip configuring speaker")
}
try self.rtcAudioSession.setActive(true)
logger.debug("WebRTCClient: configuring session with speaker enabled \(enabled) success")
} catch let error {
@@ -658,6 +672,17 @@ extension WebRTCClient {
}
}
extension AVAudioSession {
func hasExternalAudioDevice() -> Bool {
availableInputs?.allSatisfy({ $0.portType == .builtInMic }) != true
}
func preferredInputDevice() -> AVAudioSessionPortDescription? {
// logger.debug("Preferred input device: \(String(describing: self.availableInputs?.filter({ $0.portType != .builtInMic })))")
return availableInputs?.filter({ $0.portType != .builtInMic }).last
}
}
struct CustomRTCSessionDescription: Codable {
public var type: RTCSdpType?
public var sdp: String?
@@ -25,11 +25,11 @@ struct ChatInfoToolbar: View {
}
ChatInfoImage(
chat: chat,
size: imageSize,
color: colorScheme == .dark
? chatImageColorDark
: chatImageColorLight
)
.frame(width: imageSize, height: imageSize)
.padding(.trailing, 4)
VStack {
let t = Text(cInfo.displayName).font(.headline)
+39 -56
View File
@@ -49,7 +49,7 @@ func localizedInfoRow(_ title: LocalizedStringKey, _ value: LocalizedStringKey)
}
}
private func serverHost(_ s: String) -> String {
func serverHost(_ s: String) -> String {
if let i = s.range(of: "@")?.lowerBound {
return String(s[i...].dropFirst())
} else {
@@ -90,6 +90,7 @@ enum SendReceipts: Identifiable, Hashable {
struct ChatInfoView: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@Environment(\.dismiss) var dismiss: DismissAction
@ObservedObject var chat: Chat
@State var contact: Contact
@@ -103,7 +104,6 @@ struct ChatInfoView: View {
@State private var sendReceipts = SendReceipts.userDefault(true)
@State private var sendReceiptsUserDefault = true
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@AppStorage(GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED, store: groupDefaults) private var pqExperimentalEnabled = false
enum ChatInfoViewAlert: Identifiable {
case clearChatAlert
@@ -111,7 +111,7 @@ struct ChatInfoView: View {
case switchAddressAlert
case abortSwitchAddressAlert
case syncConnectionForceAlert
case allowContactPQEncryptionAlert
case queueInfo(info: String)
case error(title: LocalizedStringKey, error: LocalizedStringKey = "")
var id: String {
@@ -121,7 +121,7 @@ struct ChatInfoView: View {
case .switchAddressAlert: return "switchAddressAlert"
case .abortSwitchAddressAlert: return "abortSwitchAddressAlert"
case .syncConnectionForceAlert: return "syncConnectionForceAlert"
case .allowContactPQEncryptionAlert: return "allowContactPQEncryptionAlert"
case let .queueInfo(info): return "queueInfo \(info)"
case let .error(title, _): return "error \(title)"
}
}
@@ -168,19 +168,9 @@ struct ChatInfoView: View {
}
.disabled(!contact.ready || !contact.active)
if pqExperimentalEnabled,
let conn = contact.activeConn {
if let conn = contact.activeConn {
Section {
infoRow(Text(String("E2E encryption")), conn.connPQEnabled ? "Quantum resistant" : "Standard")
if !conn.pqEncryption {
allowPQButton()
}
} header: {
Text(String("Quantum resistant E2E encryption"))
} footer: {
if !conn.pqEncryption {
Text(String("After allowing quantum resistant encryption, it will be enabled after several messages if your contact also allows it."))
}
}
}
@@ -237,9 +227,22 @@ struct ChatInfoView: View {
Section(header: Text("For console")) {
infoRow("Local name", chat.chatInfo.localDisplayName)
infoRow("Database ID", "\(chat.chatInfo.apiId)")
Button ("Debug delivery") {
Task {
do {
let info = queueInfoText(try await apiContactQueueInfo(chat.chatInfo.apiId))
await MainActor.run { alert = .queueInfo(info: info) }
} catch let e {
logger.error("apiContactQueueInfo error: \(responseError(e))")
let a = getErrorAlert(e, "Error")
await MainActor.run { alert = .error(title: a.title, error: a.message) }
}
}
}
}
}
}
.modifier(ThemedBackground())
.navigationBarHidden(true)
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
@@ -256,7 +259,7 @@ struct ChatInfoView: View {
case .switchAddressAlert: return switchAddressAlert(switchContactAddress)
case .abortSwitchAddressAlert: return abortSwitchAddressAlert(abortSwitchContactAddress)
case .syncConnectionForceAlert: return syncConnectionForceAlert({ syncContactConnection(force: true) })
case .allowContactPQEncryptionAlert: return allowContactPQEncryptionAlert()
case let .queueInfo(info): return queueInfoAlert(info)
case let .error(title, error): return mkAlert(title: title, message: error)
}
}
@@ -285,8 +288,7 @@ struct ChatInfoView: View {
private func contactInfoHeader() -> some View {
VStack {
let cInfo = chat.chatInfo
ChatInfoImage(chat: chat, color: Color(uiColor: .tertiarySystemFill))
.frame(width: 192, height: 192)
ChatInfoImage(chat: chat, size: 192, color: Color(uiColor: .tertiarySystemFill))
.padding(.top, 12)
.padding()
if contact.verified {
@@ -370,6 +372,7 @@ struct ChatInfoView: View {
)
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("Security code")
.modifier(ThemedBackground())
} label: {
Label(
contact.verified ? "View security code" : "Verify security code",
@@ -386,6 +389,7 @@ struct ChatInfoView: View {
currentFeaturesAllowed: contactUserPrefsToFeaturesAllowed(contact.mergedPreferences)
)
.navigationBarTitle("Contact preferences")
.modifier(ThemedBackground())
.navigationBarTitleDisplayMode(.large)
} label: {
Label("Contact preferences", systemImage: "switch.2")
@@ -430,20 +434,11 @@ struct ChatInfoView: View {
}
}
private func allowPQButton() -> some View {
Button {
alert = .allowContactPQEncryptionAlert
} label: {
Label(String("Allow PQ encryption"), systemImage: "exclamationmark.triangle")
.foregroundColor(.orange)
}
}
private func networkStatusRow() -> some View {
HStack {
Text("Network status")
Image(systemName: "info.circle")
.foregroundColor(.accentColor)
.foregroundColor(theme.colors.primary)
.font(.system(size: 14))
Spacer()
Text(chatModel.contactNetworkStatus(contact).statusString)
@@ -572,34 +567,6 @@ struct ChatInfoView: View {
}
}
}
private func allowContactPQEncryption() {
Task {
do {
let ct = try await apiSetContactPQ(contact.apiId, true)
contact = ct
await MainActor.run {
chatModel.updateContact(ct)
dismiss()
}
} catch let error {
logger.error("allowContactPQEncryption apiSetContactPQ error: \(responseError(error))")
let a = getErrorAlert(error, "Error allowing contact PQ encryption")
await MainActor.run {
alert = .error(title: a.title, error: a.message)
}
}
}
}
func allowContactPQEncryptionAlert() -> Alert {
Alert(
title: Text(String("Allow quantum resistant encryption?")),
message: Text(String("This is an experimental feature, it is not recommended to enable it for important chats.")),
primaryButton: .destructive(Text(String("Allow")), action: allowContactPQEncryption),
secondaryButton: .cancel()
)
}
}
func switchAddressAlert(_ switchAddress: @escaping () -> Void) -> Alert {
@@ -629,6 +596,22 @@ func syncConnectionForceAlert(_ syncConnectionForce: @escaping () -> Void) -> Al
)
}
func queueInfoText(_ info: (RcvMsgInfo?, QueueInfo)) -> String {
let (rcvMsgInfo, qInfo) = info
var msgInfo: String
if let rcvMsgInfo { msgInfo = encodeJSON(rcvMsgInfo) } else { msgInfo = "none" }
return String.localizedStringWithFormat(NSLocalizedString("server queue info: %@\n\nlast received msg: %@", comment: "queue info"), encodeJSON(qInfo), msgInfo)
}
func queueInfoAlert(_ info: String) -> Alert {
Alert(
title: Text("Message queue info"),
message: Text(info),
primaryButton: .default(Text("Ok")),
secondaryButton: .default(Text("Copy")) { UIPasteboard.general.string = info }
)
}
struct ChatInfoView_Previews: PreviewProvider {
static var previews: some View {
ChatInfoView(
@@ -11,6 +11,7 @@ import SimpleXChat
struct CICallItemView: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@ObservedObject var chat: Chat
var chatItem: ChatItem
var status: CICallStatus
@@ -35,9 +36,7 @@ struct CICallItemView: View {
case .error: missedCallIcon(sent).foregroundColor(.orange)
}
chatItem.timestampText
.font(.caption)
.foregroundColor(.secondary)
CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary, showStatus: false, showEdited: false)
.padding(.bottom, 8)
.padding(.horizontal, 12)
}
@@ -17,7 +17,7 @@ struct CIEventView: View {
.padding(.horizontal, 6)
.padding(.vertical, 4)
.textSelection(.disabled)
.lineLimit(3)
.lineLimit(4)
}
}
@@ -11,6 +11,7 @@ import SimpleXChat
struct CIFeaturePreferenceView: View {
@ObservedObject var chat: Chat
@EnvironmentObject var theme: AppTheme
var chatItem: ChatItem
var feature: ChatFeature
var allowed: FeatureAllowed
@@ -45,7 +46,7 @@ struct CIFeaturePreferenceView: View {
r = r
+ Text(acceptText)
.fontWeight(.medium)
.foregroundColor(.accentColor)
.foregroundColor(theme.colors.primary)
+ Text(" ")
}
r = r + chatItem.timestampText
@@ -12,6 +12,7 @@ import SimpleXChat
struct CIFileView: View {
@EnvironmentObject var m: ChatModel
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
let file: CIFile?
let edited: Bool
@@ -54,15 +55,18 @@ struct CIFileView: View {
switch (file.fileStatus) {
case .sndStored: return file.fileProtocol == .local
case .sndTransfer: return false
case .sndComplete: return false
case .sndComplete: return true
case .sndCancelled: return false
case .sndError: return false
case .sndError: return true
case .sndWarning: return true
case .rcvInvitation: return true
case .rcvAccepted: return true
case .rcvTransfer: return false
case .rcvAborted: return true
case .rcvComplete: return true
case .rcvCancelled: return false
case .rcvError: return false
case .rcvError: return true
case .rcvWarning: return true
case .invalid: return false
}
}
@@ -73,10 +77,10 @@ struct CIFileView: View {
logger.debug("CIFileView fileAction")
if let file = file {
switch (file.fileStatus) {
case .rcvInvitation:
case .rcvInvitation, .rcvAborted:
if fileSizeValid(file) {
Task {
logger.debug("CIFileView fileAction - in .rcvInvitation, in Task")
logger.debug("CIFileView fileAction - in .rcvInvitation, .rcvAborted, in Task")
if let user = m.currentUser {
await receiveFile(user: user, fileId: file.fileId)
}
@@ -107,11 +111,40 @@ struct CIFileView: View {
if let fileSource = getLoadedFileSource(file) {
saveCryptoFile(fileSource)
}
case let .rcvError(rcvFileError):
logger.debug("CIFileView fileAction - in .rcvError")
AlertManager.shared.showAlert(Alert(
title: Text("File error"),
message: Text(rcvFileError.errorInfo)
))
case let .rcvWarning(rcvFileError):
logger.debug("CIFileView fileAction - in .rcvWarning")
AlertManager.shared.showAlert(Alert(
title: Text("Temporary file error"),
message: Text(rcvFileError.errorInfo)
))
case .sndStored:
logger.debug("CIFileView fileAction - in .sndStored")
if file.fileProtocol == .local, let fileSource = getLoadedFileSource(file) {
saveCryptoFile(fileSource)
}
case .sndComplete:
logger.debug("CIFileView fileAction - in .sndComplete")
if let fileSource = getLoadedFileSource(file) {
saveCryptoFile(fileSource)
}
case let .sndError(sndFileError):
logger.debug("CIFileView fileAction - in .sndError")
AlertManager.shared.showAlert(Alert(
title: Text("File error"),
message: Text(sndFileError.errorInfo)
))
case let .sndWarning(sndFileError):
logger.debug("CIFileView fileAction - in .sndWarning")
AlertManager.shared.showAlert(Alert(
title: Text("Temporary file error"),
message: Text(sndFileError.errorInfo)
))
default: break
}
}
@@ -135,9 +168,10 @@ struct CIFileView: View {
case .sndComplete: fileIcon("doc.fill", innerIcon: "checkmark", innerIconSize: 10)
case .sndCancelled: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10)
case .sndError: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10)
case .sndWarning: fileIcon("doc.fill", innerIcon: "exclamationmark.triangle.fill", innerIconSize: 10)
case .rcvInvitation:
if fileSizeValid(file) {
fileIcon("arrow.down.doc.fill", color: .accentColor)
fileIcon("arrow.down.doc.fill", color: theme.colors.primary)
} else {
fileIcon("doc.fill", color: .orange, innerIcon: "exclamationmark", innerIconSize: 12)
}
@@ -148,9 +182,12 @@ struct CIFileView: View {
} else {
progressView()
}
case .rcvAborted:
fileIcon("doc.fill", color: theme.colors.primary, innerIcon: "exclamationmark.arrow.circlepath", innerIconSize: 12)
case .rcvComplete: fileIcon("doc.fill")
case .rcvCancelled: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10)
case .rcvError: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10)
case .rcvWarning: fileIcon("doc.fill", innerIcon: "exclamationmark.triangle.fill", innerIconSize: 10)
case .invalid: fileIcon("doc.fill", innerIcon: "questionmark", innerIconSize: 10)
}
} else {
@@ -11,7 +11,8 @@ import SimpleXChat
struct CIGroupInvitationView: View {
@EnvironmentObject var chatModel: ChatModel
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
@ObservedObject var chat: Chat
var chatItem: ChatItem
var groupInvitation: CIGroupInvitation
var memberRole: GroupMemberRole
@@ -20,6 +21,8 @@ struct CIGroupInvitationView: View {
@State private var inProgress = false
@State private var progressByTimeout = false
@AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = false
var body: some View {
let action = !chatItem.chatDir.sent && groupInvitation.status == .pending
let v = ZStack(alignment: .bottomTrailing) {
@@ -37,16 +40,22 @@ struct CIGroupInvitationView: View {
VStack(alignment: .leading, spacing: 2) {
groupInvitationText()
.overlay(DetermineWidth())
Text(chatIncognito ? "Tap to join incognito" : "Tap to join")
.foregroundColor(inProgress ? .secondary : chatIncognito ? .indigo : .accentColor)
.font(.callout)
.padding(.trailing, 60)
.overlay(DetermineWidth())
(
Text(chatIncognito ? "Tap to join incognito" : "Tap to join")
.foregroundColor(inProgress ? .secondary : chatIncognito ? .indigo : theme.colors.primary)
.font(.callout)
+ Text(" ")
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true, showStatus: false, showEdited: false, showViaProxy: showSentViaProxy)
)
.overlay(DetermineWidth())
}
} else {
groupInvitationText()
.padding(.trailing, 60)
.overlay(DetermineWidth())
(
groupInvitationText()
+ Text(" ")
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true, showStatus: false, showEdited: false, showViaProxy: showSentViaProxy)
)
.overlay(DetermineWidth())
}
}
.padding(.bottom, 2)
@@ -56,13 +65,11 @@ struct CIGroupInvitationView: View {
}
}
chatItem.timestampText
.font(.caption)
.foregroundColor(.secondary)
CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary, showStatus: false, showEdited: false)
}
.padding(.horizontal, 12)
.padding(.vertical, 6)
.background(chatItemFrameColor(chatItem, colorScheme))
.background(chatItemFrameColor(chatItem, theme))
.cornerRadius(18)
.textSelection(.disabled)
.onPreferenceChange(DetermineWidth.Key.self) { frameWidth = $0 }
@@ -92,7 +99,7 @@ struct CIGroupInvitationView: View {
private func groupInfoView(_ action: Bool) -> some View {
var color: Color
if action && !inProgress {
color = chatIncognito ? .indigo : .accentColor
color = chatIncognito ? .indigo : theme.colors.primary
} else {
color = Color(uiColor: .tertiaryLabel)
}
@@ -100,9 +107,9 @@ struct CIGroupInvitationView: View {
ProfileImage(
imageStr: groupInvitation.groupProfile.image,
iconName: "person.2.circle.fill",
size: 44,
color: color
)
.frame(width: 44, height: 44)
.padding(.trailing, 4)
VStack(alignment: .leading) {
let p = groupInvitation.groupProfile
@@ -115,7 +122,7 @@ struct CIGroupInvitationView: View {
}
}
private func groupInvitationText() -> some View {
private func groupInvitationText() -> Text {
Text(groupInvitationStr())
.font(.callout)
}
@@ -137,8 +144,8 @@ struct CIGroupInvitationView: View {
struct CIGroupInvitationView_Previews: PreviewProvider {
static var previews: some View {
Group {
CIGroupInvitationView(chatItem: ChatItem.getGroupInvitationSample(), groupInvitation: CIGroupInvitation.getSample(groupProfile: GroupProfile(displayName: "team", fullName: "team")), memberRole: .admin)
CIGroupInvitationView(chatItem: ChatItem.getGroupInvitationSample(), groupInvitation: CIGroupInvitation.getSample(status: .accepted), memberRole: .admin)
CIGroupInvitationView(chat: Chat.sampleData, chatItem: ChatItem.getGroupInvitationSample(), groupInvitation: CIGroupInvitation.getSample(groupProfile: GroupProfile(displayName: "team", fullName: "team")), memberRole: .admin)
CIGroupInvitationView(chat: Chat.sampleData, chatItem: ChatItem.getGroupInvitationSample(), groupInvitation: CIGroupInvitation.getSample(status: .accepted), memberRole: .admin)
}
}
}
@@ -17,7 +17,6 @@ struct CIImageView: View {
let maxWidth: CGFloat
@Binding var imgWidth: CGFloat?
@State var scrollProxy: ScrollViewProxy?
@State var metaColor: Color
@State private var showFullScreenImage = false
var body: some View {
@@ -38,7 +37,7 @@ struct CIImageView: View {
.onTapGesture {
if let file = file {
switch file.fileStatus {
case .rcvInvitation:
case .rcvInvitation, .rcvAborted:
Task {
if let user = m.currentUser {
await receiveFile(user: user, fileId: file.fileId)
@@ -61,6 +60,26 @@ struct CIImageView: View {
case .rcvTransfer: () // ?
case .rcvComplete: () // ?
case .rcvCancelled: () // TODO
case let .rcvError(rcvFileError):
AlertManager.shared.showAlert(Alert(
title: Text("File error"),
message: Text(rcvFileError.errorInfo)
))
case let .rcvWarning(rcvFileError):
AlertManager.shared.showAlert(Alert(
title: Text("Temporary file error"),
message: Text(rcvFileError.errorInfo)
))
case let .sndError(sndFileError):
AlertManager.shared.showAlert(Alert(
title: Text("File error"),
message: Text(sndFileError.errorInfo)
))
case let .sndWarning(sndFileError):
AlertManager.shared.showAlert(Alert(
title: Text("Temporary file error"),
message: Text(sndFileError.errorInfo)
))
default: ()
}
}
@@ -100,13 +119,16 @@ struct CIImageView: View {
case .sndComplete: fileIcon("checkmark", 10, 13)
case .sndCancelled: fileIcon("xmark", 10, 13)
case .sndError: fileIcon("xmark", 10, 13)
case .sndWarning: fileIcon("exclamationmark.triangle.fill", 10, 13)
case .rcvInvitation: fileIcon("arrow.down", 10, 13)
case .rcvAccepted: fileIcon("ellipsis", 14, 11)
case .rcvTransfer: progressView()
case .rcvAborted: fileIcon("exclamationmark.arrow.circlepath", 14, 11)
case .rcvComplete: EmptyView()
case .rcvCancelled: fileIcon("xmark", 10, 13)
case .rcvError: fileIcon("xmark", 10, 13)
case .rcvWarning: fileIcon("exclamationmark.triangle.fill", 10, 13)
case .invalid: fileIcon("questionmark", 10, 13)
default: EmptyView()
}
}
}
@@ -116,7 +138,7 @@ struct CIImageView: View {
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: size, height: size)
.foregroundColor(metaColor)
.foregroundColor(.white)
.padding(padding)
}
@@ -24,7 +24,7 @@ struct CIInvalidJSONView: View {
.cornerRadius(18)
.textSelection(.disabled)
.onTapGesture { showJSON = true }
.sheet(isPresented: $showJSON) {
.appSheet(isPresented: $showJSON) {
invalidJSONView(json)
}
}
@@ -44,6 +44,7 @@ func invalidJSONView(_ json: String) -> some View {
}
.frame(maxHeight: .infinity)
.padding()
.modifier(ThemedBackground())
}
struct CIInvalidJSONView_Previews: PreviewProvider {
@@ -11,6 +11,7 @@ import SimpleXChat
struct CIMemberCreatedContactView: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
var chatItem: ChatItem
var body: some View {
@@ -43,7 +44,7 @@ struct CIMemberCreatedContactView: View {
r = r
+ Text(openText)
.fontWeight(.medium)
.foregroundColor(.accentColor)
.foregroundColor(theme.colors.primary)
+ Text(" ")
}
r = r + chatItem.timestampText
@@ -11,9 +11,14 @@ import SimpleXChat
struct CIMetaView: View {
@ObservedObject var chat: Chat
@EnvironmentObject var theme: AppTheme
var chatItem: ChatItem
var metaColor = Color.secondary
var metaColor: Color
var paleMetaColor = Color(UIColor.tertiaryLabel)
var showStatus = true
var showEdited = true
@AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = false
var body: some View {
if chatItem.isDeletedContent {
@@ -25,24 +30,24 @@ struct CIMetaView: View {
switch meta.itemStatus {
case let .sndSent(sndProgress):
switch sndProgress {
case .complete: ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, sent: .sent)
case .partial: ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: paleMetaColor, sent: .sent)
case .complete: ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, sent: .sent, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy)
case .partial: ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: paleMetaColor, sent: .sent, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy)
}
case let .sndRcvd(_, sndProgress):
switch sndProgress {
case .complete:
ZStack {
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, sent: .rcvd1)
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, sent: .rcvd2)
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, sent: .rcvd1, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy)
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, sent: .rcvd2, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy)
}
case .partial:
ZStack {
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: paleMetaColor, sent: .rcvd1)
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: paleMetaColor, sent: .rcvd2)
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: paleMetaColor, sent: .rcvd1, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy)
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: paleMetaColor, sent: .rcvd2, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy)
}
}
default:
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor)
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy)
}
}
}
@@ -54,9 +59,20 @@ enum SentCheckmark {
case rcvd2
}
func ciMetaText(_ meta: CIMeta, chatTTL: Int?, encrypted: Bool?, color: Color = .clear, transparent: Bool = false, sent: SentCheckmark? = nil) -> Text {
func ciMetaText(
_ meta: CIMeta,
chatTTL: Int?,
encrypted: Bool?,
color: Color = .clear,
primaryColor: Color = .accentColor,
transparent: Bool = false,
sent: SentCheckmark? = nil,
showStatus: Bool = true,
showEdited: Bool = true,
showViaProxy: Bool
) -> Text {
var r = Text("")
if meta.itemEdited {
if showEdited, meta.itemEdited {
r = r + statusIconText("pencil", color)
}
if meta.disappearing {
@@ -67,19 +83,24 @@ func ciMetaText(_ meta: CIMeta, chatTTL: Int?, encrypted: Bool?, color: Color =
}
r = r + Text(" ")
}
if let (icon, statusColor) = meta.statusIcon(color) {
let t = Text(Image(systemName: icon)).font(.caption2)
let gap = Text(" ").kerning(-1.25)
let t1 = t.foregroundColor(transparent ? .clear : statusColor.opacity(0.67))
switch sent {
case nil: r = r + t1
case .sent: r = r + t1 + gap
case .rcvd1: r = r + t.foregroundColor(transparent ? .clear : statusColor.opacity(0.67)) + gap
case .rcvd2: r = r + gap + t1
if showViaProxy, meta.sentViaProxy == true {
r = r + statusIconText("arrow.forward", color.opacity(0.67)).font(.caption2)
}
if showStatus {
if let (icon, statusColor) = meta.statusIcon(color, primaryColor) {
let t = Text(Image(systemName: icon)).font(.caption2)
let gap = Text(" ").kerning(-1.25)
let t1 = t.foregroundColor(transparent ? .clear : statusColor.opacity(0.67))
switch sent {
case nil: r = r + t1
case .sent: r = r + t1 + gap
case .rcvd1: r = r + t.foregroundColor(transparent ? .clear : statusColor.opacity(0.67)) + gap
case .rcvd2: r = r + gap + t1
}
r = r + Text(" ")
} else if !meta.disappearing {
r = r + statusIconText("circlebadge.fill", .clear) + Text(" ")
}
r = r + Text(" ")
} else if !meta.disappearing {
r = r + statusIconText("circlebadge.fill", .clear) + Text(" ")
}
if let enc = encrypted {
r = r + statusIconText(enc ? "lock" : "lock.open", color) + Text(" ")
@@ -93,15 +114,16 @@ private func statusIconText(_ icon: String, _ color: Color) -> Text {
}
struct CIMetaView_Previews: PreviewProvider {
static let metaColor = Color.secondary
static var previews: some View {
Group {
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete)))
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .partial)))
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .ok, sndProgress: .complete)))
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .ok, sndProgress: .partial)))
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .badMsgHash, sndProgress: .complete)))
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete), itemEdited: true))
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getDeletedContentSample())
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete)), metaColor: metaColor)
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .partial)), metaColor: metaColor)
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .ok, sndProgress: .complete)), metaColor: metaColor)
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .ok, sndProgress: .partial)), metaColor: metaColor)
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndRcvd(msgRcptStatus: .badMsgHash, sndProgress: .complete)), metaColor: metaColor)
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getSample(2, .directSnd, .now, "https://simplex.chat", .sndSent(sndProgress: .complete), itemEdited: true), metaColor: metaColor)
CIMetaView(chat: Chat.sampleData, chatItem: ChatItem.getDeletedContentSample(), metaColor: metaColor)
}
.previewLayout(.fixed(width: 360, height: 100))
}
@@ -13,12 +13,15 @@ let decryptErrorReason: LocalizedStringKey = "It can happen when you or your con
struct CIRcvDecryptionError: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@ObservedObject var chat: Chat
var msgDecryptError: MsgDecryptError
var msgCount: UInt32
var chatItem: ChatItem
@State private var alert: CIRcvDecryptionErrorAlert?
@AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = false
enum CIRcvDecryptionErrorAlert: Identifiable {
case syncAllowedAlert(_ syncConnection: () -> Void)
case syncNotSupportedContactAlert
@@ -112,18 +115,18 @@ struct CIRcvDecryptionError: View {
}
(
Text(Image(systemName: "exclamationmark.arrow.triangle.2.circlepath"))
.foregroundColor(syncSupported ? .accentColor : .secondary)
.foregroundColor(syncSupported ? theme.colors.primary : theme.colors.secondary)
.font(.callout)
+ Text(" ")
+ Text("Fix connection")
.foregroundColor(syncSupported ? .accentColor : .secondary)
.foregroundColor(syncSupported ? theme.colors.primary : theme.colors.secondary)
.font(.callout)
+ Text(" ")
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true)
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true, showViaProxy: showSentViaProxy)
)
}
.padding(.horizontal, 12)
CIMetaView(chat: chat, chatItem: chatItem)
CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary)
.padding(.horizontal, 12)
}
.onTapGesture(perform: { onClick() })
@@ -140,10 +143,10 @@ struct CIRcvDecryptionError: View {
.foregroundColor(.red)
.italic()
+ Text(" ")
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true)
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true, showViaProxy: showSentViaProxy)
}
.padding(.horizontal, 12)
CIMetaView(chat: chat, chatItem: chatItem)
CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary)
.padding(.horizontal, 12)
}
.onTapGesture(perform: { onClick() })
@@ -69,7 +69,7 @@ struct CIVideoView: View {
.onTapGesture {
if let file = file {
switch file.fileStatus {
case .rcvInvitation:
case .rcvInvitation, .rcvAborted:
receiveFileIfValidSize(file: file, receiveFile: receiveFile)
case .rcvAccepted:
switch file.fileProtocol {
@@ -95,7 +95,7 @@ struct CIVideoView: View {
}
durationProgress()
}
if let file = file, case .rcvInvitation = file.fileStatus {
if let file = file, showDownloadButton(file.fileStatus) {
Button {
receiveFileIfValidSize(file: file, receiveFile: receiveFile)
} label: {
@@ -105,6 +105,14 @@ struct CIVideoView: View {
}
}
private func showDownloadButton(_ fileStatus: CIFileStatus) -> Bool {
switch fileStatus {
case .rcvInvitation: true
case .rcvAborted: true
default: false
}
}
private func videoViewEncrypted(_ file: CIFile, _ defaultPreview: UIImage, _ duration: Int) -> some View {
return ZStack(alignment: .topTrailing) {
ZStack(alignment: .center) {
@@ -184,7 +192,7 @@ struct CIVideoView: View {
.disabled(!canBePlayed)
}
}
loadingIndicator()
fileStatusIcon()
}
.onAppear {
addObserver(player, url)
@@ -250,11 +258,11 @@ struct CIVideoView: View {
.resizable()
.scaledToFit()
.frame(width: w)
loadingIndicator()
fileStatusIcon()
}
}
@ViewBuilder private func loadingIndicator() -> some View {
@ViewBuilder private func fileStatusIcon() -> some View {
if let file = chatItem.file {
switch file.fileStatus {
case .sndStored:
@@ -271,7 +279,22 @@ struct CIVideoView: View {
}
case .sndComplete: fileIcon("checkmark", 10, 13)
case .sndCancelled: fileIcon("xmark", 10, 13)
case .sndError: fileIcon("xmark", 10, 13)
case let .sndError(sndFileError):
fileIcon("xmark", 10, 13)
.onTapGesture {
AlertManager.shared.showAlert(Alert(
title: Text("File error"),
message: Text(sndFileError.errorInfo)
))
}
case let .sndWarning(sndFileError):
fileIcon("exclamationmark.triangle.fill", 10, 13)
.onTapGesture {
AlertManager.shared.showAlert(Alert(
title: Text("Temporary file error"),
message: Text(sndFileError.errorInfo)
))
}
case .rcvInvitation: fileIcon("arrow.down", 10, 13)
case .rcvAccepted: fileIcon("ellipsis", 14, 11)
case let .rcvTransfer(rcvProgress, rcvTotal):
@@ -280,10 +303,26 @@ struct CIVideoView: View {
} else {
progressView()
}
case .rcvAborted: fileIcon("exclamationmark.arrow.circlepath", 14, 11)
case .rcvComplete: EmptyView()
case .rcvCancelled: fileIcon("xmark", 10, 13)
case .rcvError: fileIcon("xmark", 10, 13)
case let .rcvError(rcvFileError):
fileIcon("xmark", 10, 13)
.onTapGesture {
AlertManager.shared.showAlert(Alert(
title: Text("File error"),
message: Text(rcvFileError.errorInfo)
))
}
case let .rcvWarning(rcvFileError):
fileIcon("exclamationmark.triangle.fill", 10, 13)
.onTapGesture {
AlertManager.shared.showAlert(Alert(
title: Text("Temporary file error"),
message: Text(rcvFileError.errorInfo)
))
}
case .invalid: fileIcon("questionmark", 10, 13)
default: EmptyView()
}
}
}
@@ -318,10 +357,10 @@ struct CIVideoView: View {
}
// TODO encrypt: where file size is checked?
private func receiveFileIfValidSize(file: CIFile, receiveFile: @escaping (User, Int64, Bool) async -> Void) {
private func receiveFileIfValidSize(file: CIFile, receiveFile: @escaping (User, Int64, Bool, Bool) async -> Void) {
Task {
if let user = m.currentUser {
await receiveFile(user, file.fileId, false)
await receiveFile(user, file.fileId, false, false)
}
}
}
@@ -11,6 +11,7 @@ import SimpleXChat
struct CIVoiceView: View {
@ObservedObject var chat: Chat
@EnvironmentObject var theme: AppTheme
var chatItem: ChatItem
let recordingFile: CIFile?
let duration: Int
@@ -92,7 +93,7 @@ struct CIVoiceView: View {
}
private func metaView() -> some View {
CIMetaView(chat: chat, chatItem: chatItem)
CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary)
}
}
@@ -118,7 +119,7 @@ struct VoiceMessagePlayerTime: View {
struct VoiceMessagePlayer: View {
@EnvironmentObject var chatModel: ChatModel
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
var chatItem: ChatItem
var recordingFile: CIFile?
var recordingTime: TimeInterval
@@ -134,17 +135,53 @@ struct VoiceMessagePlayer: View {
ZStack {
if let recordingFile = recordingFile {
switch recordingFile.fileStatus {
case .sndStored: playbackButton()
case .sndTransfer: playbackButton()
case .sndStored:
if recordingFile.fileProtocol == .local {
playbackButton()
} else {
loadingIcon()
}
case .sndTransfer: loadingIcon()
case .sndComplete: playbackButton()
case .sndCancelled: playbackButton()
case .sndError: playbackButton()
case .rcvInvitation: downloadButton(recordingFile)
case let .sndError(sndFileError):
fileStatusIcon("multiply", 14)
.onTapGesture {
AlertManager.shared.showAlert(Alert(
title: Text("File error"),
message: Text(sndFileError.errorInfo)
))
}
case let .sndWarning(sndFileError):
fileStatusIcon("exclamationmark.triangle.fill", 16)
.onTapGesture {
AlertManager.shared.showAlert(Alert(
title: Text("Temporary file error"),
message: Text(sndFileError.errorInfo)
))
}
case .rcvInvitation: downloadButton(recordingFile, "play.fill")
case .rcvAccepted: loadingIcon()
case .rcvTransfer: loadingIcon()
case .rcvAborted: downloadButton(recordingFile, "exclamationmark.arrow.circlepath")
case .rcvComplete: playbackButton()
case .rcvCancelled: playPauseIcon("play.fill", Color(uiColor: .tertiaryLabel))
case .rcvError: playPauseIcon("play.fill", Color(uiColor: .tertiaryLabel))
case let .rcvError(rcvFileError):
fileStatusIcon("multiply", 14)
.onTapGesture {
AlertManager.shared.showAlert(Alert(
title: Text("File error"),
message: Text(rcvFileError.errorInfo)
))
}
case let .rcvWarning(rcvFileError):
fileStatusIcon("exclamationmark.triangle.fill", 16)
.onTapGesture {
AlertManager.shared.showAlert(Alert(
title: Text("Temporary file error"),
message: Text(rcvFileError.errorInfo)
))
}
case .invalid: playPauseIcon("play.fill", Color(uiColor: .tertiaryLabel))
}
} else {
@@ -208,7 +245,7 @@ struct VoiceMessagePlayer: View {
.foregroundColor(color)
.padding(.leading, image == "play.fill" ? 4 : 0)
.frame(width: 56, height: 56)
.background(showBackground ? chatItemFrameColor(chatItem, colorScheme) : .clear)
.background(showBackground ? chatItemFrameColor(chatItem, theme) : .clear)
.clipShape(Circle())
if recordingTime > 0 {
ProgressCircle(length: recordingTime, progress: $playbackTime)
@@ -217,7 +254,7 @@ struct VoiceMessagePlayer: View {
}
}
private func downloadButton(_ recordingFile: CIFile) -> some View {
private func downloadButton(_ recordingFile: CIFile, _ icon: String) -> some View {
Button {
Task {
if let user = chatModel.currentUser {
@@ -225,11 +262,12 @@ struct VoiceMessagePlayer: View {
}
}
} label: {
playPauseIcon("play.fill")
playPauseIcon(icon)
}
}
private struct ProgressCircle: View {
@EnvironmentObject var theme: AppTheme
var length: TimeInterval
@Binding var progress: TimeInterval?
@@ -237,7 +275,7 @@ struct VoiceMessagePlayer: View {
Circle()
.trim(from: 0, to: ((progress ?? TimeInterval(0)) / length))
.stroke(
Color.accentColor,
theme.colors.primary,
style: StrokeStyle(lineWidth: 3)
)
.rotationEffect(.degrees(-90))
@@ -245,11 +283,22 @@ struct VoiceMessagePlayer: View {
}
}
private func fileStatusIcon(_ image: String, _ size: CGFloat) -> some View {
Image(systemName: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: size, height: size)
.foregroundColor(Color(uiColor: .tertiaryLabel))
.frame(width: 56, height: 56)
.background(showBackground ? chatItemFrameColor(chatItem, theme) : .clear)
.clipShape(Circle())
}
private func loadingIcon() -> some View {
ProgressView()
.frame(width: 30, height: 30)
.frame(width: 56, height: 56)
.background(showBackground ? chatItemFrameColor(chatItem, colorScheme) : .clear)
.background(showBackground ? chatItemFrameColor(chatItem, theme) : .clear)
.clipShape(Circle())
}
@@ -10,7 +10,7 @@ import SwiftUI
import SimpleXChat
struct DeletedItemView: View {
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
@ObservedObject var chat: Chat
var chatItem: ChatItem
@@ -19,12 +19,12 @@ struct DeletedItemView: View {
Text(chatItem.content.text)
.foregroundColor(.secondary)
.italic()
CIMetaView(chat: chat, chatItem: chatItem)
CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary)
.padding(.horizontal, 12)
}
.padding(.leading, 12)
.padding(.vertical, 6)
.background(chatItemFrameColor(chatItem, colorScheme))
.background(chatItemFrameColor(chatItem, theme))
.cornerRadius(18)
.textSelection(.disabled)
}
@@ -11,6 +11,7 @@ import SimpleXChat
struct EmojiItemView: View {
@ObservedObject var chat: Chat
@EnvironmentObject var theme: AppTheme
var chatItem: ChatItem
var body: some View {
@@ -18,7 +19,7 @@ struct EmojiItemView: View {
emojiText(chatItem.content.text)
.padding(.top, 8)
.padding(.horizontal, 6)
CIMetaView(chat: chat, chatItem: chatItem)
CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary)
.padding(.bottom, 8)
.padding(.horizontal, 12)
}
@@ -9,16 +9,9 @@
import SwiftUI
import SimpleXChat
let notesChatColorLight = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.21)
let notesChatColorDark = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.19)
let sentColorLight = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.12)
let sentColorDark = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.17)
private let sentQuoteColorLight = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.11)
private let sentQuoteColorDark = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.09)
struct FramedItemView: View {
@EnvironmentObject var m: ChatModel
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
@ObservedObject var chat: Chat
var chatItem: ChatItem
@Binding var revealed: Bool
@@ -83,16 +76,24 @@ struct FramedItemView: View {
.accessibilityLabel("")
}
}
.background(chatItemFrameColorMaybeImageOrVideo(chatItem, colorScheme))
.onAppear {
metaColor = metaColor == .secondary ? theme.colors.secondary : metaColor
}
.background(chatItemFrameColorMaybeImageOrVideo(chatItem, theme))
.cornerRadius(18)
.onPreferenceChange(DetermineWidth.Key.self) { msgWidth = $0 }
switch chatItem.meta.itemStatus {
case .sndErrorAuth:
v.onTapGesture { msgDeliveryError("Most likely this contact has deleted the connection with you.") }
case let .sndError(agentError):
v.onTapGesture { msgDeliveryError("Unexpected error: \(agentError)") }
default: v
if let (title, text) = chatItem.meta.itemStatus.statusInfo {
v.onTapGesture {
AlertManager.shared.showAlert(
Alert(
title: Text(title),
message: Text(text)
)
)
}
} else {
v
}
}
@@ -110,7 +111,7 @@ struct FramedItemView: View {
} else {
switch (chatItem.content.msgContent) {
case let .image(text, image):
CIImageView(chatItem: chatItem, image: image, maxWidth: maxWidth, imgWidth: $imgWidth, scrollProxy: scrollProxy, metaColor: metaColor)
CIImageView(chatItem: chatItem, image: image, maxWidth: maxWidth, imgWidth: $imgWidth, scrollProxy: scrollProxy)
.overlay(DetermineWidth())
if text == "" && !chatItem.meta.isLive {
Color.clear
@@ -157,13 +158,6 @@ struct FramedItemView: View {
}
}
}
private func msgDeliveryError(_ err: LocalizedStringKey) {
AlertManager.shared.showAlertMsg(
title: "Message delivery error",
message: err
)
}
@ViewBuilder func framedItemHeader(icon: String? = nil, caption: Text, pad: Bool = false) -> some View {
let v = HStack(spacing: 6) {
@@ -177,13 +171,13 @@ struct FramedItemView: View {
.font(.caption)
.lineLimit(1)
}
.foregroundColor(.secondary)
.foregroundColor(theme.colors.secondary)
.padding(.horizontal, 12)
.padding(.top, 6)
.padding(.bottom, pad || (chatItem.quotedItem == nil && chatItem.meta.itemForwarded == nil) ? 6 : 0)
.overlay(DetermineWidth())
.frame(minWidth: msgWidth, alignment: .leading)
.background(chatItemFrameContextColor(chatItem, colorScheme))
.background(chatItemFrameContextColor(chatItem, theme))
if let mediaWidth = maxMediaWidth(), mediaWidth < maxWidth {
v.frame(maxWidth: mediaWidth, alignment: .leading)
} else {
@@ -235,7 +229,7 @@ struct FramedItemView: View {
// if enable this always, size of the framed voice message item will be incorrect after end of playback
.overlay { if case .voice = chatItem.content.msgContent {} else { DetermineWidth() } }
.frame(minWidth: msgWidth, alignment: .leading)
.background(chatItemFrameContextColor(chatItem, colorScheme))
.background(chatItemFrameContextColor(chatItem, theme))
if let mediaWidth = maxMediaWidth(), mediaWidth < maxWidth {
v.frame(maxWidth: mediaWidth, alignment: .leading)
@@ -248,7 +242,10 @@ struct FramedItemView: View {
Group {
if let sender = qi.getSender(membership()) {
VStack(alignment: .leading, spacing: 2) {
Text(sender).font(.caption).foregroundColor(.secondary)
Text(sender)
.font(.caption)
.foregroundColor(theme.colors.secondary)
.lineLimit(1)
ciQuotedMsgTextView(qi, lines: 2)
}
} else {
@@ -362,22 +359,22 @@ func onlyImageOrVideo(_ ci: ChatItem) -> Bool {
return false
}
func chatItemFrameColorMaybeImageOrVideo(_ ci: ChatItem, _ colorScheme: ColorScheme) -> Color {
func chatItemFrameColorMaybeImageOrVideo(_ ci: ChatItem, _ theme: AppTheme) -> Color {
onlyImageOrVideo(ci)
? Color.clear
: chatItemFrameColor(ci, colorScheme)
: chatItemFrameColor(ci, theme)
}
func chatItemFrameColor(_ ci: ChatItem, _ colorScheme: ColorScheme) -> Color {
func chatItemFrameColor(_ ci: ChatItem, _ theme: AppTheme) -> Color {
ci.chatDir.sent
? (colorScheme == .light ? sentColorLight : sentColorDark)
: Color(uiColor: .tertiarySystemGroupedBackground)
? theme.appColors.sentMessage
: theme.appColors.receivedMessage
}
func chatItemFrameContextColor(_ ci: ChatItem, _ colorScheme: ColorScheme) -> Color {
func chatItemFrameContextColor(_ ci: ChatItem, _ theme: AppTheme) -> Color {
ci.chatDir.sent
? (colorScheme == .light ? sentQuoteColorLight : sentQuoteColorDark)
: Color(uiColor: .quaternarySystemFill)
? theme.appColors.sentQuote
: theme.appColors.receivedQuote
}
struct FramedItemView_Previews: PreviewProvider {
@@ -11,6 +11,7 @@ import SimpleXChat
struct IntegrityErrorItemView: View {
@ObservedObject var chat: Chat
@EnvironmentObject var theme: AppTheme
var msgError: MsgErrorType
var chatItem: ChatItem
@@ -54,6 +55,7 @@ struct IntegrityErrorItemView: View {
struct CIMsgError: View {
@ObservedObject var chat: Chat
@EnvironmentObject var theme: AppTheme
var chatItem: ChatItem
var onTap: () -> Void
@@ -62,7 +64,7 @@ struct CIMsgError: View {
Text(chatItem.content.text)
.foregroundColor(.red)
.italic()
CIMetaView(chat: chat, chatItem: chatItem)
CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary)
.padding(.horizontal, 12)
}
.padding(.leading, 12)
@@ -11,7 +11,7 @@ import SimpleXChat
struct MarkedDeletedItemView: View {
@EnvironmentObject var m: ChatModel
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
@ObservedObject var chat: Chat
var chatItem: ChatItem
@Binding var revealed: Bool
@@ -22,7 +22,7 @@ struct MarkedDeletedItemView: View {
.foregroundColor(.secondary)
.padding(.horizontal, 12)
.padding(.vertical, 6)
.background(chatItemFrameColor(chatItem, colorScheme))
.background(chatItemFrameColor(chatItem, theme))
.cornerRadius(18)
.textSelection(.disabled)
}
@@ -35,6 +35,8 @@ struct MsgContentView: View {
@State private var typingIdx = 0
@State private var timer: Timer?
@AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = false
var body: some View {
if meta?.isLive == true {
msgContentView()
@@ -81,7 +83,7 @@ struct MsgContentView: View {
}
private func reserveSpaceForMeta(_ mt: CIMeta) -> Text {
(rightToLeft ? Text("\n") : Text(" ")) + ciMetaText(mt, chatTTL: chat.chatInfo.timedMessagesTTL, encrypted: nil, transparent: true)
(rightToLeft ? Text("\n") : Text(" ")) + ciMetaText(mt, chatTTL: chat.chatInfo.timedMessagesTTL, encrypted: nil, transparent: true, showViaProxy: showSentViaProxy)
}
}
@@ -35,6 +35,7 @@ struct ChatItemForwardingView: View {
}
}
}
.modifier(ThemedBackground())
}
@ViewBuilder private func forwardListView() -> some View {
@@ -59,9 +60,13 @@ struct ChatItemForwardingView: View {
.cornerRadius(12)
.padding(.horizontal)
}
.background(Color(.systemGroupedBackground))
.modifier(ThemedBackground())
} else {
emptyList()
ZStack {
emptyList()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.modifier(ThemedBackground())
}
}
}
@@ -123,8 +128,7 @@ struct ChatItemForwardingView: View {
}
} label: {
HStack {
ChatInfoImage(chat: chat)
.frame(width: 30, height: 30)
ChatInfoImage(chat: chat, size: 30)
.padding(.trailing, 2)
Text(chat.chatInfo.chatViewName)
.foregroundColor(.primary)
@@ -12,11 +12,13 @@ import SimpleXChat
struct ChatItemInfoView: View {
@EnvironmentObject var chatModel: ChatModel
@Environment(\.dismiss) var dismiss
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
var ci: ChatItem
@Binding var chatItemInfo: ChatItemInfo?
@State private var selection: CIInfoTab = .history
@State private var alert: CIInfoViewAlert? = nil
@State private var messageStatusLimited: Bool = true
@State private var fileStatusLimited: Bool = true
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
enum CIInfoTab {
@@ -99,12 +101,14 @@ struct ChatItemInfoView: View {
Label("History", systemImage: "clock")
}
.tag(CIInfoTab.history)
.modifier(ThemedBackground())
if let qi = ci.quotedItem {
quoteTab(qi)
.tabItem {
Label("In reply to", systemImage: "arrowshape.turn.up.left")
}
.tag(CIInfoTab.quote)
.modifier(ThemedBackground())
}
if let forwardedFromItem = chatItemInfo?.forwardedFromChatItem {
forwardedFromTab(forwardedFromItem)
@@ -112,6 +116,7 @@ struct ChatItemInfoView: View {
Label(local ? "Saved" : "Forwarded", systemImage: "arrowshape.turn.up.forward")
}
.tag(CIInfoTab.forwarded)
.modifier(ThemedBackground())
}
}
.onAppear {
@@ -121,6 +126,7 @@ struct ChatItemInfoView: View {
}
} else {
historyTab()
.modifier(ThemedBackground())
}
}
@@ -157,6 +163,35 @@ struct ChatItemInfoView: View {
if developerTools {
infoRow("Database ID", "\(meta.itemId)")
infoRow("Record updated at", localTimestamp(meta.updatedAt))
let msv = infoRow("Message status", ci.meta.itemStatus.id)
Group {
if messageStatusLimited {
msv.lineLimit(1)
} else {
msv
}
}
.onTapGesture {
withAnimation {
messageStatusLimited.toggle()
}
}
if let file = ci.file {
let fsv = infoRow("File status", file.fileStatus.id)
Group {
if fileStatusLimited {
fsv.lineLimit(1)
} else {
fsv
}
}
.onTapGesture {
withAnimation {
fileStatusLimited.toggle()
}
}
}
}
}
}
@@ -196,7 +231,7 @@ struct ChatItemInfoView: View {
textBubble(itemVersion.msgContent.text, itemVersion.formattedText, nil)
.padding(.horizontal, 12)
.padding(.vertical, 6)
.background(chatItemFrameColor(ci, colorScheme))
.background(chatItemFrameColor(ci, theme))
.cornerRadius(18)
.contextMenu {
if itemVersion.msgContent.text != "" {
@@ -265,7 +300,7 @@ struct ChatItemInfoView: View {
textBubble(qi.text, qi.formattedText, qi.getSender(nil))
.padding(.horizontal, 12)
.padding(.vertical, 6)
.background(quotedMsgFrameColor(qi, colorScheme))
.background(quotedMsgFrameColor(qi, theme))
.cornerRadius(18)
.contextMenu {
if qi.text != "" {
@@ -289,10 +324,10 @@ struct ChatItemInfoView: View {
.frame(maxWidth: maxWidth, alignment: .leading)
}
func quotedMsgFrameColor(_ qi: CIQuote, _ colorScheme: ColorScheme) -> Color {
func quotedMsgFrameColor(_ qi: CIQuote, _ theme: AppTheme) -> Color {
(qi.chatDir?.sent ?? false)
? (colorScheme == .light ? sentColorLight : sentColorDark)
: Color(uiColor: .tertiarySystemGroupedBackground)
? theme.appColors.sentMessage
: theme.appColors.receivedMessage
}
@ViewBuilder private func forwardedFromTab(_ forwardedFromItem: AChatItem) -> some View {
@@ -334,8 +369,7 @@ struct ChatItemInfoView: View {
@ViewBuilder private func forwardedFromSender(_ forwardedFromItem: AChatItem) -> some View {
HStack {
ChatInfoImage(chat: Chat(chatInfo: forwardedFromItem.chatInfo))
.frame(width: 48, height: 48)
ChatInfoImage(chat: Chat(chatInfo: forwardedFromItem.chatInfo), size: 48)
.padding(.trailing, 6)
if forwardedFromItem.chatItem.chatDir.sent {
@@ -384,7 +418,7 @@ struct ChatItemInfoView: View {
let mss = membersStatuses(memberDeliveryStatuses)
if !mss.isEmpty {
ForEach(mss, id: \.0.groupMemberId) { memberStatus in
memberDeliveryStatusView(memberStatus.0, memberStatus.1)
memberDeliveryStatusView(memberStatus.0, memberStatus.1, memberStatus.2)
}
} else {
Text("No delivery information")
@@ -393,26 +427,29 @@ struct ChatItemInfoView: View {
}
}
private func membersStatuses(_ memberDeliveryStatuses: [MemberDeliveryStatus]) -> [(GroupMember, CIStatus)] {
private func membersStatuses(_ memberDeliveryStatuses: [MemberDeliveryStatus]) -> [(GroupMember, CIStatus, Bool?)] {
memberDeliveryStatuses.compactMap({ mds in
if let mem = chatModel.getGroupMember(mds.groupMemberId) {
return (mem.wrapped, mds.memberDeliveryStatus)
return (mem.wrapped, mds.memberDeliveryStatus, mds.sentViaProxy)
} else {
return nil
}
})
}
private func memberDeliveryStatusView(_ member: GroupMember, _ status: CIStatus) -> some View {
private func memberDeliveryStatusView(_ member: GroupMember, _ status: CIStatus, _ sentViaProxy: Bool?) -> some View {
HStack{
ProfileImage(imageStr: member.image)
.frame(width: 30, height: 30)
ProfileImage(imageStr: member.image, size: 30)
.padding(.trailing, 2)
Text(member.chatViewName)
.lineLimit(1)
Spacer()
if sentViaProxy == true {
Image(systemName: "arrow.forward")
.foregroundColor(.secondary).opacity(0.67)
}
let v = Group {
if let (icon, statusColor) = status.statusIcon(Color.secondary) {
if let (icon, statusColor) = status.statusIcon(theme.colors.secondary, theme.colors.primary) {
switch status {
case .sndRcvd:
ZStack(alignment: .trailing) {
@@ -471,8 +508,12 @@ struct ChatItemInfoView: View {
if developerTools {
shareText += [
String.localizedStringWithFormat(NSLocalizedString("Database ID: %d", comment: "copied message info"), meta.itemId),
String.localizedStringWithFormat(NSLocalizedString("Record updated at: %@", comment: "copied message info"), localTimestamp(meta.updatedAt))
String.localizedStringWithFormat(NSLocalizedString("Record updated at: %@", comment: "copied message info"), localTimestamp(meta.updatedAt)),
String.localizedStringWithFormat(NSLocalizedString("Message status: %@", comment: "copied message info"), meta.itemStatus.id)
]
if let file = ci.file {
shareText += [String.localizedStringWithFormat(NSLocalizedString("File status: %@", comment: "copied message info"), file.fileStatus.id)]
}
}
if let qi = ci.quotedItem {
shareText += ["", NSLocalizedString("## In reply to", comment: "copied message info")]
@@ -127,7 +127,7 @@ struct ChatItemContentView<Content: View>: View {
}
private func groupInvitationItemView(_ groupInvitation: CIGroupInvitation, _ memberRole: GroupMemberRole) -> some View {
CIGroupInvitationView(chatItem: chatItem, groupInvitation: groupInvitation, memberRole: memberRole, chatIncognito: chat.chatInfo.incognito)
CIGroupInvitationView(chat: chat, chatItem: chatItem, groupInvitation: groupInvitation, memberRole: memberRole, chatIncognito: chat.chatInfo.incognito)
}
private func eventItemView() -> some View {
+49 -12
View File
@@ -14,7 +14,7 @@ private let memberImageSize: CGFloat = 34
struct ChatView: View {
@EnvironmentObject var chatModel: ChatModel
@Environment(\.colorScheme) var colorScheme
@State var theme: AppTheme = buildTheme()
@Environment(\.dismiss) var dismiss
@Environment(\.presentationMode) var presentationMode
@Environment(\.scenePhase) var scenePhase
@@ -60,7 +60,16 @@ struct ChatView: View {
Divider()
}
ZStack(alignment: .trailing) {
let wallpaperImage = theme.wallpaper.type.image
let wallpaperType = theme.wallpaper.type
let backgroundColor = theme.wallpaper.background ?? wallpaperType.defaultBackgroundColor(theme.base, theme.colors.background)
let tintColor = theme.wallpaper.tint ?? wallpaperType.defaultTintColor(theme.base)
chatItemsList()
.if(wallpaperImage != nil) { view in
view.modifier(
ChatViewBackground(image: wallpaperImage!, imageType: wallpaperType, background: backgroundColor, tint: tintColor)
)
}
if let proxy = scrollProxy {
floatingButtons(proxy)
}
@@ -78,7 +87,9 @@ struct ChatView: View {
}
.padding(.top, 1)
.navigationTitle(cInfo.chatViewName)
.background(theme.colors.background)
.navigationBarTitleDisplayMode(.inline)
.environmentObject(theme)
.onAppear {
initChatView()
}
@@ -89,6 +100,7 @@ struct ChatView: View {
chat = c
}
initChatView()
theme = buildTheme()
} else {
dismiss()
}
@@ -131,7 +143,7 @@ struct ChatView: View {
} label: {
ChatInfoToolbar(chat: chat)
}
.sheet(isPresented: $showChatInfoSheet, onDismiss: {
.appSheet(isPresented: $showChatInfoSheet, onDismiss: {
connectionStats = nil
customUserProfile = nil
connectionCode = nil
@@ -384,7 +396,7 @@ struct ChatView: View {
circleButton {
unreadCountText(unreadAbove)
.font(.callout)
.foregroundColor(.accentColor)
.foregroundColor(theme.colors.primary)
}
.onTapGesture { scrollUp(proxy) }
.contextMenu {
@@ -404,13 +416,13 @@ struct ChatView: View {
circleButton {
unreadCountText(counts.unreadBelow)
.font(.callout)
.foregroundColor(.accentColor)
.foregroundColor(theme.colors.primary)
}
.onTapGesture { scrollToBottom(proxy) }
} else if counts.totalBelow > 16 {
circleButton {
Image(systemName: "chevron.down")
.foregroundColor(.accentColor)
.foregroundColor(theme.colors.primary)
}
.onTapGesture { scrollToBottom(proxy) }
}
@@ -513,7 +525,7 @@ struct ChatView: View {
}
}
}
@ViewBuilder private func chatItemView(_ ci: ChatItem, _ maxWidth: CGFloat) -> some View {
ChatItemWithMenu(
chat: chat,
@@ -528,7 +540,7 @@ struct ChatView: View {
private struct ChatItemWithMenu: View {
@EnvironmentObject var m: ChatModel
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
@ObservedObject var chat: Chat
var chatItem: ChatItem
var maxWidth: CGFloat
@@ -593,12 +605,12 @@ struct ChatView: View {
Text(memberNames(member, prevMember, memCount))
.font(.caption)
.foregroundStyle(.secondary)
.lineLimit(2)
.padding(.leading, memberImageSize + 14)
.padding(.top, 7)
}
HStack(alignment: .top, spacing: 8) {
ProfileImage(imageStr: member.memberProfile.image)
.frame(width: memberImageSize, height: memberImageSize)
ProfileImage(imageStr: member.memberProfile.image, size: memberImageSize, backgroundColor: theme.colors.background)
.onTapGesture {
if chatView.membersLoaded {
selectedMember = m.getGroupMember(member.groupMemberId)
@@ -662,7 +674,8 @@ struct ChatView: View {
playbackState: $playbackState,
playbackTime: $playbackTime
)
.uiKitContextMenu(hasImageOrVideo: ci.content.msgContent?.isImageOrVideo == true, maxWidth: maxWidth, itemWidth: $itemWidth, menu: uiMenu, allowMenu: $allowMenu)
.environmentObject(theme)
.uiKitContextMenu(hasImageOrVideo: ci.content.msgContent?.isImageOrVideo == true, maxWidth: maxWidth, itemWidth: $itemWidth, menu: uiMenu, allowMenu: $allowMenu, backgroundColor: theme.colors.background)
.accessibilityLabel("")
if ci.content.msgContent != nil && (ci.meta.itemDeleted == nil || revealed) && ci.reactions.count > 0 {
chatItemReactions(ci)
@@ -729,7 +742,7 @@ struct ChatView: View {
Text("\(r.totalReacted)")
.font(.caption)
.fontWeight(r.userReacted ? .bold : .light)
.foregroundColor(r.userReacted ? .accentColor : .secondary)
.foregroundColor(r.userReacted ? theme.colors.primary : theme.colors.secondary)
}
}
.padding(.horizontal, 6)
@@ -828,6 +841,9 @@ struct ChatView: View {
menu.append(deleteUIAction(ci))
} else if ci.mergeCategory != nil && ((range?.count ?? 0) > 1 || revealed) {
menu.append(revealed ? shrinkUIAction() : expandUIAction())
menu.append(deleteUIAction(ci))
} else if ci.showLocalDelete {
menu.append(deleteUIAction(ci))
}
return menu
}
@@ -1040,7 +1056,7 @@ struct ChatView: View {
image: UIImage(systemName: "trash"),
attributes: [.destructive]
) { _ in
if !revealed && ci.meta.itemDeleted != nil,
if !revealed,
let currIndex = m.getChatItemIndex(ci),
let ciCategory = ci.mergeCategory {
let (prevHidden, _) = m.getPrevShownChatItem(currIndex, ciCategory)
@@ -1216,6 +1232,27 @@ struct ChatView: View {
}
}
private func buildTheme() -> AppTheme {
if let cId = ChatModel.shared.chatId, let chat = ChatModel.shared.getChat(cId) {
let perChatTheme = if case let .direct(contact) = chat.chatInfo {
contact.uiThemes?.preferredMode(!AppTheme.shared.colors.isLight)
} else if case let .group(groupInfo) = chat.chatInfo {
groupInfo.uiThemes?.preferredMode(!AppTheme.shared.colors.isLight)
} else {
nil as ThemeModeOverride?
}
let overrides = if perChatTheme != nil {
ThemeManager.currentColors(nil, perChatTheme, ChatModel.shared.currentUser?.uiThemes, themeOverridesDefault.get())
} else {
nil as ThemeManager.ActiveTheme?
}
let theme = overrides ?? CurrentColors
return AppTheme(name: theme.name, base: theme.base, colors: theme.colors, appColors: theme.appColors, wallpaper: theme.wallpaper)
} else {
return AppTheme.shared
}
}
struct ToggleNtfsButton: View {
@ObservedObject var chat: Chat
@@ -9,7 +9,7 @@
import SwiftUI
struct ComposeFileView: View {
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
let fileName: String
let cancelFile: (() -> Void)
let cancelEnabled: Bool
@@ -33,7 +33,7 @@ struct ComposeFileView: View {
.padding(.vertical, 1)
.padding(.trailing, 12)
.frame(height: 50)
.background(colorScheme == .light ? sentColorLight : sentColorDark)
.background(theme.appColors.sentMessage)
.frame(maxWidth: .infinity)
.padding(.top, 8)
}
@@ -10,7 +10,7 @@ import SwiftUI
import SimpleXChat
struct ComposeImageView: View {
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
let images: [String]
let cancelImage: (() -> Void)
let cancelEnabled: Bool
@@ -48,7 +48,7 @@ struct ComposeImageView: View {
}
.padding(.vertical, 1)
.padding(.trailing, 12)
.background(colorScheme == .light ? sentColorLight : sentColorDark)
.background(theme.appColors.sentMessage)
.frame(maxWidth: .infinity)
.padding(.top, 8)
}
@@ -40,7 +40,7 @@ func getLinkPreview(url: URL, cb: @escaping (LinkPreview?) -> Void) {
}
struct ComposeLinkView: View {
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
let linkPreview: LinkPreview?
var cancelPreview: (() -> Void)? = nil
let cancelEnabled: Bool
@@ -62,7 +62,7 @@ struct ComposeLinkView: View {
}
.padding(.vertical, 1)
.padding(.trailing, 12)
.background(colorScheme == .light ? sentColorLight : sentColorDark)
.background(theme.appColors.sentMessage)
.frame(maxWidth: .infinity)
.padding(.top, 8)
}
@@ -254,6 +254,7 @@ enum UploadContent: Equatable {
struct ComposeView: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@ObservedObject var chat: Chat
@Binding var composeState: ComposeState
@Binding var keyboardVisible: Bool
@@ -353,10 +354,10 @@ struct ComposeView: View {
keyboardVisible: $keyboardVisible,
sendButtonColor: chat.chatInfo.incognito
? .indigo.opacity(colorScheme == .dark ? 1 : 0.7)
: .accentColor
: theme.colors.primary
)
.padding(.trailing, 12)
.background(.background)
.background(theme.colors.background)
.disabled(!chat.userCanSend)
if chat.userIsObserver {
@@ -712,9 +713,9 @@ struct ComposeView: View {
if chat.chatInfo.contact?.nextSendGrpInv ?? false {
await sendMemberContactInvitation()
} else if case let .forwardingItem(ci, fromChatInfo) = composeState.contextItem {
sent = await forwardItem(ci, fromChatInfo)
sent = await forwardItem(ci, fromChatInfo, ttl)
if !composeState.message.isEmpty {
sent = await send(checkLinkPreview(), quoted: sent?.id, live: false, ttl: nil)
sent = await send(checkLinkPreview(), quoted: sent?.id, live: false, ttl: ttl)
}
} else if case let .editingItem(ci) = composeState.contextItem {
sent = await updateMessage(ci, live: live)
@@ -890,13 +891,14 @@ struct ComposeView: View {
return nil
}
func forwardItem(_ forwardedItem: ChatItem, _ fromChatInfo: ChatInfo) async -> ChatItem? {
func forwardItem(_ forwardedItem: ChatItem, _ fromChatInfo: ChatInfo, _ ttl: Int?) async -> ChatItem? {
if let chatItem = await apiForwardChatItem(
toChatType: chat.chatInfo.chatType,
toChatId: chat.chatInfo.apiId,
fromChatType: fromChatInfo.chatType,
fromChatId: fromChatInfo.apiId,
itemId: forwardedItem.id
itemId: forwardedItem.id,
ttl: ttl
) {
await MainActor.run {
chatModel.addChatItem(chat.chatInfo, chatItem)
@@ -25,7 +25,7 @@ func voiceMessageTime_(_ time: TimeInterval?) -> String {
struct ComposeVoiceView: View {
@EnvironmentObject var chatModel: ChatModel
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
var recordingFileName: String
@Binding var recordingTime: TimeInterval?
@Binding var recordingState: VoiceMessageRecordingState
@@ -50,7 +50,7 @@ struct ComposeVoiceView: View {
}
.padding(.vertical, 1)
.frame(height: ComposeVoiceView.previewHeight)
.background(colorScheme == .light ? sentColorLight : sentColorDark)
.background(theme.appColors.sentMessage)
.frame(maxWidth: .infinity)
.padding(.top, 8)
}
@@ -80,7 +80,7 @@ struct ComposeVoiceView: View {
Button {
startPlayback()
} label: {
playPauseIcon("play.fill")
playPauseIcon("play.fill", theme.colors.primary)
}
Text(voiceMessageTime_(recordingTime))
case .playing:
@@ -88,7 +88,7 @@ struct ComposeVoiceView: View {
audioPlayer?.pause()
playbackState = .paused
} label: {
playPauseIcon("pause.fill")
playPauseIcon("pause.fill", theme.colors.primary)
}
Text(voiceMessageTime_(playbackTime))
case .paused:
@@ -96,7 +96,7 @@ struct ComposeVoiceView: View {
audioPlayer?.play()
playbackState = .playing
} label: {
playPauseIcon("play.fill")
playPauseIcon("play.fill", theme.colors.primary)
}
Text(voiceMessageTime_(playbackTime))
}
@@ -131,7 +131,7 @@ struct ComposeVoiceView: View {
}
}
private func playPauseIcon(_ image: String, _ color: Color = .accentColor) -> some View {
private func playPauseIcon(_ image: String, _ color: Color) -> some View {
Image(systemName: image)
.resizable()
.aspectRatio(contentMode: .fit)
@@ -162,6 +162,7 @@ struct ComposeVoiceView: View {
}
private struct ProgressBar: View {
@EnvironmentObject var theme: AppTheme
var length: TimeInterval
@Binding var progress: TimeInterval?
@@ -169,7 +170,7 @@ struct ComposeVoiceView: View {
GeometryReader { geometry in
ZStack {
Rectangle()
.fill(Color.accentColor)
.fill(theme.colors.primary)
.frame(width: min(CGFloat((progress ?? TimeInterval(0)) / length) * geometry.size.width, geometry.size.width), height: 4)
.animation(.linear, value: progress)
}
@@ -9,7 +9,7 @@
import SwiftUI
struct ContextInvitingContactMemberView: View {
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
var body: some View {
HStack {
@@ -20,7 +20,7 @@ struct ContextInvitingContactMemberView: View {
.padding(12)
.frame(minHeight: 50)
.frame(maxWidth: .infinity, alignment: .leading)
.background(colorScheme == .light ? sentColorLight : sentColorDark)
.background(theme.appColors.sentMessage)
.padding(.top, 8)
}
}
@@ -10,7 +10,7 @@ import SwiftUI
import SimpleXChat
struct ContextItemView: View {
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
@ObservedObject var chat: Chat
let contextItem: ChatItem
let contextIcon: String
@@ -44,7 +44,7 @@ struct ContextItemView: View {
.padding(12)
.frame(minHeight: 50)
.frame(maxWidth: .infinity)
.background(chatItemFrameColor(contextItem, colorScheme))
.background(chatItemFrameColor(contextItem, theme))
.padding(.top, 8)
}
@@ -28,6 +28,7 @@ struct NativeTextEditor: UIViewRepresentable {
func makeUIView(context: Context) -> UITextView {
let field = CustomUITextField(height: _height)
field.backgroundColor = .clear
field.text = text
field.textAlignment = alignment(text)
field.autocapitalizationType = .sentences
@@ -13,6 +13,7 @@ private let liveMsgInterval: UInt64 = 3000_000000
struct SendMessageView: View {
@Binding var composeState: ComposeState
@EnvironmentObject var theme: AppTheme
var sendMessage: (Int?) -> Void
var sendLiveMessage: (() async -> Void)? = nil
var updateLiveMessage: (() async -> Void)? = nil
@@ -224,8 +225,7 @@ struct SendMessageView: View {
@ViewBuilder private func sendButtonContextMenuItems() -> some View {
if composeState.liveMessage == nil,
!composeState.editing,
!composeState.forwarding {
!composeState.editing {
if case .noContextItem = composeState.contextItem,
!composeState.voicePreview,
let send = sendLiveMessage,
@@ -248,6 +248,7 @@ struct SendMessageView: View {
}
private struct RecordVoiceMessageButton: View {
@EnvironmentObject var theme: AppTheme
var startVoiceMessageRecording: (() -> Void)?
var finishVoiceMessageRecording: (() -> Void)?
@Binding var holdingVMR: Bool
@@ -257,7 +258,7 @@ struct SendMessageView: View {
var body: some View {
Button(action: {}) {
Image(systemName: "mic.fill")
.foregroundColor(.accentColor)
.foregroundColor(theme.colors.primary)
}
.disabled(disabled)
.frame(width: 29, height: 29)
@@ -324,7 +325,7 @@ struct SendMessageView: View {
Image(systemName: "multiply")
.resizable()
.scaledToFit()
.foregroundColor(.accentColor)
.foregroundColor(theme.colors.primary)
.frame(width: 15, height: 15)
}
.frame(width: 29, height: 29)
@@ -341,7 +342,7 @@ struct SendMessageView: View {
Image(systemName: "bolt.fill")
.resizable()
.scaledToFit()
.foregroundColor(.accentColor)
.foregroundColor(theme.colors.primary)
.frame(width: 20, height: 20)
}
.frame(width: 29, height: 29)
@@ -384,7 +385,7 @@ struct SendMessageView: View {
}
Task {
_ = try? await Task.sleep(nanoseconds: liveMsgInterval)
while composeState.liveMessage != nil {
while await composeState.liveMessage != nil {
await update()
_ = try? await Task.sleep(nanoseconds: liveMsgInterval)
}
@@ -395,7 +396,7 @@ struct SendMessageView: View {
private func finishVoiceMessageRecordingButton() -> some View {
Button(action: { finishVoiceMessageRecording?() }) {
Image(systemName: "stop.fill")
.foregroundColor(.accentColor)
.foregroundColor(theme.colors.primary)
}
.disabled(composeState.inProgress)
.frame(width: 29, height: 29)
@@ -21,6 +21,7 @@ struct AddGroupMembersView: View {
struct AddGroupMembersViewCommon: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
var chat: Chat
@State var groupInfo: GroupInfo
var creatingGroup: Bool = false
@@ -125,6 +126,7 @@ struct AddGroupMembersViewCommon: View {
.onChange(of: selectedContacts) { _ in
searchFocussed = false
}
.modifier(ThemedBackground())
}
private func inviteMembersButton() -> some View {
@@ -176,7 +178,7 @@ struct AddGroupMembersViewCommon: View {
} else {
if checked {
icon = "checkmark.circle.fill"
iconColor = .accentColor
iconColor = theme.colors.primary
} else {
icon = "circle"
iconColor = Color(uiColor: .tertiaryLabel)
@@ -194,8 +196,7 @@ struct AddGroupMembersViewCommon: View {
}
} label: {
HStack{
ProfileImage(imageStr: contact.image)
.frame(width: 30, height: 30)
ProfileImage(imageStr: contact.image, size: 30)
.padding(.trailing, 2)
Text(ChatInfo.direct(contact: contact).chatViewName)
.foregroundColor(prohibitedToInviteIncognito ? .secondary : .primary)
@@ -135,6 +135,7 @@ struct GroupChatInfoView: View {
}
}
}
.modifier(ThemedBackground())
.navigationBarHidden(true)
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
@@ -172,8 +173,7 @@ struct GroupChatInfoView: View {
private func groupInfoHeader() -> some View {
VStack {
let cInfo = chat.chatInfo
ChatInfoImage(chat: chat, color: Color(uiColor: .tertiarySystemFill))
.frame(width: 192, height: 192)
ChatInfoImage(chat: chat, size: 192, color: Color(uiColor: .tertiarySystemFill))
.padding(.top, 12)
.padding()
Text(cInfo.displayName)
@@ -211,14 +211,14 @@ struct GroupChatInfoView: View {
private struct MemberRowView: View {
var groupInfo: GroupInfo
@ObservedObject var groupMember: GMember
@EnvironmentObject var theme: AppTheme
var user: Bool = false
@Binding var alert: GroupChatInfoViewAlert?
var body: some View {
let member = groupMember.wrapped
let v = HStack{
ProfileImage(imageStr: member.image)
.frame(width: 38, height: 38)
ProfileImage(imageStr: member.image, size: 38)
.padding(.trailing, 2)
// TODO server connection status
VStack(alignment: .leading) {
@@ -284,7 +284,7 @@ struct GroupChatInfoView: View {
Button {
alert = .unblockMemberAlert(mem: member)
} label: {
Label("Unblock member", systemImage: "hand.raised.slash").foregroundColor(.accentColor)
Label("Unblock member", systemImage: "hand.raised.slash").foregroundColor(theme.colors.primary)
}
}
}
@@ -296,7 +296,7 @@ struct GroupChatInfoView: View {
Button {
alert = .unblockForAllAlert(mem: member)
} label: {
Label("Unblock for all", systemImage: "hand.raised.slash").foregroundColor(.accentColor)
Label("Unblock for all", systemImage: "hand.raised.slash").foregroundColor(theme.colors.primary)
}
} else {
Button {
@@ -335,6 +335,7 @@ struct GroupChatInfoView: View {
creatingGroup: false
)
.navigationBarTitle("Group link")
.modifier(ThemedBackground())
.navigationBarTitleDisplayMode(.large)
} label: {
if groupLink == nil {
@@ -352,6 +353,7 @@ struct GroupChatInfoView: View {
groupProfile: groupInfo.groupProfile
)
.navigationBarTitle("Group profile")
.modifier(ThemedBackground())
.navigationBarTitleDisplayMode(.large)
} label: {
Label("Edit group profile", systemImage: "pencil")
@@ -366,6 +368,7 @@ struct GroupChatInfoView: View {
welcomeText: groupInfo.groupProfile.description ?? ""
)
.navigationTitle("Welcome message")
.modifier(ThemedBackground())
.navigationBarTitleDisplayMode(.large)
} label: {
groupInfo.groupProfile.description == nil
@@ -520,6 +523,7 @@ func groupPreferencesButton(_ groupInfo: Binding<GroupInfo>, _ creatingGroup: Bo
creatingGroup: creatingGroup
)
.navigationBarTitle("Group preferences")
.modifier(ThemedBackground())
.navigationBarTitleDisplayMode(.large)
} label: {
if creatingGroup {
@@ -133,6 +133,7 @@ struct GroupLinkView: View {
shouldCreate = false
}
}
.modifier(ThemedBackground())
}
private func createGroupLink() {
@@ -35,6 +35,7 @@ struct GroupMemberInfoView: View {
case abortSwitchAddressAlert
case syncConnectionForceAlert
case planAndConnectAlert(alert: PlanAndConnectAlert)
case queueInfo(info: String)
case error(title: LocalizedStringKey, error: LocalizedStringKey)
var id: String {
@@ -49,6 +50,7 @@ struct GroupMemberInfoView: View {
case .abortSwitchAddressAlert: return "abortSwitchAddressAlert"
case .syncConnectionForceAlert: return "syncConnectionForceAlert"
case let .planAndConnectAlert(alert): return "planAndConnectAlert \(alert.id)"
case let .queueInfo(info): return "queueInfo \(info)"
case let .error(title, _): return "error \(title)"
}
}
@@ -178,6 +180,18 @@ struct GroupMemberInfoView: View {
Section("For console") {
infoRow("Local name", member.localDisplayName)
infoRow("Database ID", "\(member.groupMemberId)")
Button ("Debug delivery") {
Task {
do {
let info = queueInfoText(try await apiGroupMemberQueueInfo(groupInfo.apiId, member.groupMemberId))
await MainActor.run { alert = .queueInfo(info: info) }
} catch let e {
logger.error("apiContactQueueInfo error: \(responseError(e))")
let a = getErrorAlert(e, "Error")
await MainActor.run { alert = .error(title: a.title, error: a.message) }
}
}
}
}
}
}
@@ -223,6 +237,7 @@ struct GroupMemberInfoView: View {
case .abortSwitchAddressAlert: return abortSwitchAddressAlert(abortSwitchMemberAddress)
case .syncConnectionForceAlert: return syncConnectionForceAlert({ syncMemberConnection(force: true) })
case let .planAndConnectAlert(alert): return planAndConnectAlert(alert, dismiss: true)
case let .queueInfo(info): return queueInfoAlert(info)
case let .error(title, error): return Alert(title: Text(title), message: Text(error))
}
}
@@ -232,6 +247,7 @@ struct GroupMemberInfoView: View {
ProgressView().scaleEffect(2)
}
}
.modifier(ThemedBackground())
}
func connectViaAddressButton(_ contactLink: String) -> some View {
@@ -305,8 +321,7 @@ struct GroupMemberInfoView: View {
private func groupMemberInfoHeader(_ mem: GroupMember) -> some View {
VStack {
ProfileImage(imageStr: mem.image, color: Color(uiColor: .tertiarySystemFill))
.frame(width: 192, height: 192)
ProfileImage(imageStr: mem.image, size: 192, color: Color(uiColor: .tertiarySystemFill))
.padding(.top, 12)
.padding()
if mem.verified {
@@ -360,6 +375,7 @@ struct GroupMemberInfoView: View {
)
.navigationBarTitleDisplayMode(.inline)
.navigationTitle("Security code")
.modifier(ThemedBackground())
} label: {
Label(
member.verified ? "View security code" : "Verify security code",
@@ -34,8 +34,7 @@ struct GroupPreferencesView: View {
featureSection(.reactions, $preferences.reactions.enable)
featureSection(.voice, $preferences.voice.enable, $preferences.voice.role)
featureSection(.files, $preferences.files.enable, $preferences.files.role)
// TODO enable simplexLinks preference in 5.8
// featureSection(.simplexLinks, $preferences.simplexLinks.enable, $preferences.simplexLinks.role)
featureSection(.simplexLinks, $preferences.simplexLinks.enable, $preferences.simplexLinks.role)
featureSection(.history, $preferences.history.enable)
if groupInfo.canEdit {
@@ -102,8 +101,6 @@ struct GroupPreferencesView: View {
}
}
.frame(height: 36)
// remove in v5.8
.disabled(true)
}
} else {
settingsRow(icon, color: color) {
@@ -66,6 +66,7 @@ struct VerifyCodeView: View {
ScanCodeView(connectionVerified: $connectionVerified, verify: verify)
.navigationBarTitleDisplayMode(.large)
.navigationTitle("Scan code")
.modifier(ThemedBackground())
} label: {
Label("Scan code", systemImage: "qrcode")
}
@@ -26,6 +26,7 @@ private let rowHeights: [DynamicTypeSize: CGFloat] = [
struct ChatListNavLink: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@Environment(\.dynamicTypeSize) private var dynamicTypeSize
@ObservedObject var chat: Chat
@State private var showContactRequestDialog = false
@@ -224,7 +225,7 @@ struct ChatListNavLink: View {
} label: {
Label("Join", systemImage: chat.chatInfo.incognito ? "theatermasks" : "ipad.and.arrow.forward")
}
.tint(chat.chatInfo.incognito ? .indigo : .accentColor)
.tint(chat.chatInfo.incognito ? .indigo : theme.colors.primary)
}
@ViewBuilder private func markReadButton() -> some View {
@@ -234,14 +235,14 @@ struct ChatListNavLink: View {
} label: {
Label("Read", systemImage: "checkmark")
}
.tint(Color.accentColor)
.tint(theme.colors.primary)
} else {
Button {
Task { await markChatUnread(chat) }
} label: {
Label("Unread", systemImage: "circlebadge.fill")
}
.tint(Color.accentColor)
.tint(theme.colors.primary)
}
}
@@ -306,7 +307,7 @@ struct ChatListNavLink: View {
Button {
Task { await acceptContactRequest(incognito: false, contactRequest: contactRequest) }
} label: { Label("Accept", systemImage: "checkmark") }
.tint(.accentColor)
.tint(theme.colors.primary)
Button {
Task { await acceptContactRequest(incognito: true, contactRequest: contactRequest) }
} label: {
@@ -346,13 +347,16 @@ struct ChatListNavLink: View {
} label: {
Label("Name", systemImage: "pencil")
}
.tint(.accentColor)
.tint(theme.colors.primary)
}
.frame(height: rowHeights[dynamicTypeSize])
.sheet(isPresented: $showContactConnectionInfo) {
if case let .contactConnection(contactConnection) = chat.chatInfo {
ContactConnectionInfo(contactConnection: contactConnection)
.environment(\EnvironmentValues.refresh as! WritableKeyPath<EnvironmentValues, RefreshAction?>, nil)
.appSheet(isPresented: $showContactConnectionInfo) {
Group {
if case let .contactConnection(contactConnection) = chat.chatInfo {
ContactConnectionInfo(contactConnection: contactConnection)
.environment(\EnvironmentValues.refresh as! WritableKeyPath<EnvironmentValues, RefreshAction?>, nil)
.modifier(ThemedBackground())
}
}
}
.onTapGesture {
@@ -467,7 +471,7 @@ struct ChatListNavLink: View {
.padding(4)
.frame(height: rowHeights[dynamicTypeSize])
.onTapGesture { showInvalidJSON = true }
.sheet(isPresented: $showInvalidJSON) {
.appSheet(isPresented: $showInvalidJSON) {
invalidJSONView(json)
.environment(\EnvironmentValues.refresh as! WritableKeyPath<EnvironmentValues, RefreshAction?>, nil)
}
@@ -11,6 +11,7 @@ import SimpleXChat
struct ChatListView: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@Binding var showSettings: Bool
@State private var searchMode = false
@FocusState private var searchFocussed
@@ -86,14 +87,14 @@ struct ChatListView: View {
))
}
.listStyle(.plain)
.background(theme.colors.background)
.navigationBarTitleDisplayMode(.inline)
.navigationBarHidden(searchMode)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
let user = chatModel.currentUser ?? User.sampleData
ZStack(alignment: .topTrailing) {
ProfileImage(imageStr: user.image, color: Color(uiColor: .quaternaryLabel))
.frame(width: 32, height: 32)
ProfileImage(imageStr: user.image, size: 32, color: Color(uiColor: .quaternaryLabel))
.padding(.trailing, 4)
let allRead = chatModel.users
.filter { u in !u.user.activeUser && !u.user.hidden }
@@ -137,7 +138,7 @@ struct ChatListView: View {
showUnreadAndFavorites = !showUnreadAndFavorites
} label: {
Image(systemName: "line.3.horizontal.decrease.circle" + (showUnreadAndFavorites ? ".fill" : ""))
.foregroundColor(.accentColor)
.foregroundColor(theme.colors.primary)
}
}
@@ -155,12 +156,14 @@ struct ChatListView: View {
searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink
)
.listRowSeparator(.hidden)
.listRowBackground(Color.clear)
.frame(maxWidth: .infinity)
}
ForEach(cs, id: \.viewId) { chat in
ChatListNavLink(chat: chat)
.padding(.trailing, -16)
.disabled(chatModel.chatRunning != true || chatModel.deletedChats.contains(chat.chatInfo.id))
.listRowBackground(Color.clear)
}
.offset(x: -8)
}
@@ -180,7 +183,7 @@ struct ChatListView: View {
private func unreadBadge(_ text: Text? = Text(" "), size: CGFloat = 18) -> some View {
Circle()
.frame(width: size, height: size)
.foregroundColor(.accentColor)
.foregroundColor(theme.colors.primary)
}
private func onboardingButtons() -> some View {
@@ -191,7 +194,7 @@ struct ChatListView: View {
p.addLine(to: CGPoint(x: 0, y: 10))
p.addLine(to: CGPoint(x: 8, y: 0))
}
.fill(Color.accentColor)
.fill(theme.colors.primary)
.frame(width: 20, height: 10)
.padding(.trailing, 12)
@@ -214,7 +217,7 @@ struct ChatListView: View {
.padding(.vertical, 10)
.padding(.horizontal, 20)
}
.background(Color.accentColor)
.background(theme.colors.primary)
.foregroundColor(.white)
.clipShape(RoundedRectangle(cornerRadius: 16))
}
@@ -277,6 +280,7 @@ struct ChatListView: View {
struct ChatListSearchBar: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@Binding var searchMode: Bool
@FocusState.Binding var searchFocussed: Bool
@Binding var searchText: String
@@ -331,7 +335,7 @@ struct ChatListSearchBar: View {
if searchFocussed {
Text("Cancel")
.foregroundColor(.accentColor)
.foregroundColor(theme.colors.primary)
.onTapGesture {
searchText = ""
searchFocussed = false
@@ -11,6 +11,7 @@ import SimpleXChat
struct ChatPreviewView: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@ObservedObject var chat: Chat
@Binding var progressByTimeout: Bool
@State var deleting: Bool = false
@@ -23,8 +24,7 @@ struct ChatPreviewView: View {
let cItem = chat.chatItems.last
return HStack(spacing: 8) {
ZStack(alignment: .bottomTrailing) {
ChatInfoImage(chat: chat)
.frame(width: 63, height: 63)
ChatInfoImage(chat: chat, size: 63)
chatPreviewImageOverlayIcon()
.padding([.bottom, .trailing], 1)
}
@@ -95,7 +95,7 @@ struct ChatPreviewView: View {
case let .group(groupInfo):
let v = previewTitle(t)
switch (groupInfo.membership.memberStatus) {
case .memInvited: v.foregroundColor(deleting ? .secondary : chat.chatInfo.incognito ? .indigo : .accentColor)
case .memInvited: v.foregroundColor(deleting ? .secondary : chat.chatInfo.incognito ? .indigo : theme.colors.primary)
case .memAccepted: v.foregroundColor(.secondary)
default: if deleting { v.foregroundColor(.secondary) } else { v }
}
@@ -134,7 +134,7 @@ struct ChatPreviewView: View {
.foregroundColor(.white)
.padding(.horizontal, 4)
.frame(minWidth: 18, minHeight: 18)
.background(chat.chatInfo.ntfsEnabled || chat.chatInfo.chatType == .local ? Color.accentColor : Color.secondary)
.background(chat.chatInfo.ntfsEnabled || chat.chatInfo.chatType == .local ? theme.colors.primary : theme.colors.secondary)
.cornerRadius(10)
} else if !chat.chatInfo.ntfsEnabled && chat.chatInfo.chatType != .local {
Image(systemName: "speaker.slash.fill")
@@ -152,7 +152,7 @@ struct ChatPreviewView: View {
private func messageDraft(_ draft: ComposeState) -> Text {
let msg = draft.message
return image("rectangle.and.pencil.and.ellipsis", color: .accentColor)
return image("rectangle.and.pencil.and.ellipsis", color: theme.colors.primary)
+ attachment()
+ messageText(msg, parseSimpleXMarkdown(msg), nil, preview: true, showSecrets: false)
@@ -207,7 +207,7 @@ struct ChatPreviewView: View {
case let .direct(contact):
if contact.activeConn == nil && contact.profile.contactLink != nil {
chatPreviewInfoText("Tap to Connect")
.foregroundColor(.accentColor)
.foregroundColor(theme.colors.primary)
} else if !contact.ready && contact.activeConn != nil {
if contact.nextSendGrpInv {
chatPreviewInfoText("send direct message")
@@ -241,14 +241,14 @@ struct ChatPreviewView: View {
private func itemStatusMark(_ cItem: ChatItem) -> Text {
switch cItem.meta.itemStatus {
case .sndErrorAuth:
case .sndErrorAuth, .sndError:
return Text(Image(systemName: "multiply"))
.font(.caption)
.foregroundColor(.red) + Text(" ")
case .sndError:
case .sndWarning:
return Text(Image(systemName: "exclamationmark.triangle.fill"))
.font(.caption)
.foregroundColor(.yellow) + Text(" ")
.foregroundColor(.orange) + Text(" ")
default: return Text("")
}
}
@@ -11,6 +11,7 @@ import SimpleXChat
struct ContactConnectionInfo: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@Environment(\.dismiss) var dismiss: DismissAction
@State var contactConnection: PendingContactConnection
@State private var alert: CCInfoAlert?
@@ -82,6 +83,7 @@ struct ContactConnectionInfo: View {
}
}
}
.modifier(ThemedBackground())
if #available(iOS 16, *) {
v
} else {
@@ -149,7 +151,7 @@ struct ContactConnectionInfo: View {
HStack(spacing: 6) {
Text("Incognito")
Image(systemName: "info.circle")
.foregroundColor(.accentColor)
.foregroundColor(theme.colors.primary)
.font(.system(size: 14))
}
.onTapGesture {
@@ -178,6 +180,7 @@ private func oneTimeLinkLearnMoreButton() -> some View {
NavigationLink {
AddContactLearnMore(showTitle: false)
.navigationTitle("One-time invitation link")
.modifier(ThemedBackground())
.navigationBarTitleDisplayMode(.large)
} label: {
settingsRow("info.circle") {
@@ -11,20 +11,20 @@ import SimpleXChat
struct ContactRequestView: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
var contactRequest: UserContactRequest
@ObservedObject var chat: Chat
var body: some View {
HStack(spacing: 8) {
ChatInfoImage(chat: chat)
.frame(width: 63, height: 63)
ChatInfoImage(chat: chat, size: 63)
.padding(.leading, 4)
VStack(alignment: .leading, spacing: 0) {
HStack(alignment: .top) {
Text(contactRequest.chatViewName)
.font(.title3)
.fontWeight(.bold)
.foregroundColor(.accentColor)
.foregroundColor(theme.colors.primary)
.padding(.leading, 8)
.frame(alignment: .topLeading)
Spacer()
@@ -6,13 +6,11 @@
import SwiftUI
import SimpleXChat
private let fillColorDark = Color(uiColor: UIColor(red: 0.11, green: 0.11, blue: 0.11, alpha: 255))
private let fillColorLight = Color(uiColor: UIColor(red: 0.99, green: 0.99, blue: 0.99, alpha: 255))
struct UserPicker: View {
@EnvironmentObject var m: ChatModel
@Environment(\.colorScheme) var colorScheme
@Environment(\.scenePhase) var scenePhase
@EnvironmentObject var theme: AppTheme
@Binding var showSettings: Bool
@Binding var showConnectDesktop: Bool
@Binding var userPickerVisible: Bool
@@ -21,9 +19,6 @@ struct UserPicker: View {
private let menuButtonHeight: CGFloat = 68
@State var chatViewNameWidth: CGFloat = 0
var fillColor: Color {
colorScheme == .dark ? fillColorDark : fillColorLight
}
var body: some View {
VStack {
@@ -82,7 +77,7 @@ struct UserPicker: View {
.clipShape(RoundedRectangle(cornerRadius: 16))
.background(
Rectangle()
.fill(fillColor)
.fill(theme.colors.surface)
.cornerRadius(16)
.shadow(color: .black.opacity(0.12), radius: 24, x: 0, y: 0)
)
@@ -127,8 +122,7 @@ struct UserPicker: View {
}
}, label: {
HStack(spacing: 0) {
ProfileImage(imageStr: user.image, color: Color(uiColor: .tertiarySystemFill))
.frame(width: 44, height: 44)
ProfileImage(imageStr: user.image, size: 44, color: Color(uiColor: .tertiarySystemFill))
.padding(.trailing, 12)
Text(user.chatViewName)
.fontWeight(user.activeUser ? .medium : .regular)
@@ -138,7 +132,7 @@ struct UserPicker: View {
if user.activeUser {
Image(systemName: "checkmark")
} else if u.unreadCount > 0 {
unreadCounter(u.unreadCount, color: user.showNtfs ? .accentColor : .secondary)
unreadCounter(u.unreadCount, color: user.showNtfs ? theme.colors.primary : theme.colors.secondary)
} else if !user.showNtfs {
Image(systemName: "speaker.slash")
}
@@ -146,7 +140,7 @@ struct UserPicker: View {
.padding(.trailing)
.padding([.leading, .vertical], 12)
})
.buttonStyle(PressedButtonStyle(defaultColor: fillColor, pressedColor: Color(uiColor: .secondarySystemFill)))
.buttonStyle(PressedButtonStyle(defaultColor: theme.colors.surface, pressedColor: Color(uiColor: .secondarySystemFill)))
}
private func menuButton(_ title: LocalizedStringKey, icon: String, action: @escaping () -> Void) -> some View {
@@ -163,7 +157,7 @@ struct UserPicker: View {
.padding(.vertical, 22)
.frame(height: menuButtonHeight)
}
.buttonStyle(PressedButtonStyle(defaultColor: fillColor, pressedColor: Color(uiColor: .secondarySystemFill)))
.buttonStyle(PressedButtonStyle(defaultColor: theme.colors.surface, pressedColor: Color(uiColor: .secondarySystemFill)))
}
}
@@ -118,6 +118,7 @@ struct DatabaseView: View {
NavigationLink {
DatabaseEncryptionView(useKeychain: $useKeychain, migration: false)
.navigationTitle("Database passphrase")
.modifier(ThemedBackground())
} label: {
Text("Database passphrase")
}
@@ -144,6 +145,7 @@ struct DatabaseView: View {
NavigationLink {
ChatArchiveView(archiveName: archiveName)
.navigationTitle(title)
.modifier(ThemedBackground())
} label: {
Text(title)
}
@@ -355,6 +357,7 @@ struct DatabaseView: View {
Task {
do {
try await apiDeleteStorage()
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
do {
let config = ArchiveConfig(archivePath: archivePath.path)
let archiveErrors = try await apiImportArchive(config: config)
@@ -189,6 +189,7 @@ struct MigrateToAppGroupView: View {
Task {
do {
try apiSaveAppSettings(settings: AppSettings.current.prepareForExport())
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
try await apiExportArchive(config: config)
await MainActor.run { setV3DBMigration(.exported) }
} catch let error {
@@ -231,6 +232,7 @@ func exportChatArchive(_ storagePath: URL? = nil) async throws -> URL {
if !ChatModel.shared.chatDbChanged {
try apiSaveAppSettings(settings: AppSettings.current.prepareForExport())
}
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
try await apiExportArchive(config: config)
if storagePath == nil {
deleteOldArchive()
@@ -10,8 +10,9 @@ import SwiftUI
import SimpleXChat
struct ChatInfoImage: View {
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var theme: AppTheme
@ObservedObject var chat: Chat
var size: CGFloat
var color = Color(uiColor: .tertiarySystemGroupedBackground)
var body: some View {
@@ -23,11 +24,11 @@ struct ChatInfoImage: View {
case .contactRequest: iconName = "person.crop.circle.fill"
default: iconName = "circle.fill"
}
let notesColor = colorScheme == .light ? notesChatColorLight : notesChatColorDark
let iconColor = if case .local = chat.chatInfo { notesColor } else { color }
let iconColor = if case .local = chat.chatInfo { theme.appColors.primaryVariant2 } else { color }
return ProfileImage(
imageStr: chat.chatInfo.image,
iconName: iconName,
size: size,
color: iconColor
)
}
@@ -36,8 +37,9 @@ struct ChatInfoImage: View {
struct ChatInfoImage_Previews: PreviewProvider {
static var previews: some View {
ChatInfoImage(
chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: [])
, color: Color(red: 0.9, green: 0.9, blue: 0.9)
chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []),
size: 63,
color: Color(red: 0.9, green: 0.9, blue: 0.9)
)
.previewLayout(.fixed(width: 63, height: 63))
}
@@ -0,0 +1,91 @@
//
// ChatWallpaper.swift
// SimpleX (iOS)
//
// Created by Avently on 14.06.2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import Foundation
import SwiftUI
import SimpleXChat
struct ChatViewBackground: ViewModifier {
@EnvironmentObject var theme: AppTheme
var image: Image
var imageType: WallpaperType
var background: Color
var tint: Color
func body(content: Content) -> some View {
content.background(
Canvas { context, size in
var image = context.resolve(image)
image.shading = .color(tint)
let rect = CGRectMake(0, 0, size.width, size.height)
func repeatDraw(_ imageScale: CGFloat) {
let scale = imageScale
for h in 0 ... Int(size.height / image.size.height / scale) {
for w in 0 ... Int(size.width / image.size.width / scale) {
let rect = CGRectMake(CGFloat(w) * image.size.width * scale, CGFloat(h) * image.size.height * scale, image.size.width * scale, image.size.height * scale)
context.draw(image, in: rect, style: FillStyle())
}
}
}
context.fill(Path(rect), with: .color(background))
switch imageType {
case let WallpaperType.Preset(filename, scale): repeatDraw(CGFloat((scale ?? 1) * (PresetWallpaper.from(filename)?.scale ?? 1)))
case let WallpaperType.Image(_, scale, scaleType):
let scaleType = scaleType ?? WallpaperScaleType.fill
switch scaleType {
case WallpaperScaleType.repeat: repeatDraw(CGFloat(scale ?? 1))
case WallpaperScaleType.fill: fallthrough
case WallpaperScaleType.fit:
// let scale = scaleType.contentScale.computeScaleFactor(Size(image.width.toFloat(), image.height.toFloat()), Size(size.width, size.height))
// let scaledWidth = (image.width * scale.scaleX).roundToInt()
// let scaledHeight = (image.height * scale.scaleY).roundToInt()
// drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
// if (scaleType == WallpaperScaleType.FIT) {
// if (scaledWidth < size.width) {
// // has black lines at left and right sides
// var x = (size.width - scaledWidth) / 2
// while (x > 0) {
// drawImage(image, dstOffset = IntOffset(x = (x - scaledWidth).roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
// x -= scaledWidth
// }
// x = size.width - (size.width - scaledWidth) / 2
// while (x < size.width) {
// drawImage(image, dstOffset = IntOffset(x = x.roundToInt(), y = ((size.height - scaledHeight) / 2).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
// x += scaledWidth
// }
// } else {
// // has black lines at top and bottom sides
// var y = (size.height - scaledHeight) / 2
// while (y > 0) {
// drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = (y - scaledHeight).roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
// y -= scaledHeight
// }
// y = size.height - (size.height - scaledHeight) / 2
// while (y < size.height) {
// drawImage(image, dstOffset = IntOffset(x = ((size.width - scaledWidth) / 2).roundToInt(), y = y.roundToInt()), dstSize = IntSize(scaledWidth, scaledHeight), filterQuality = quality)
// y += scaledHeight
// }
// }
// }
context.fill(Path(rect), with: .color(tint))
}
case WallpaperType.Empty: ()
}
}
)
}
}
extension PresetWallpaper {
public func toType(_ base: DefaultTheme, _ scale: Float? = nil) -> WallpaperType {
WallpaperType.Preset(
filename,
scale ?? themeOverridesDefault.get().first { $0.wallpaper != nil && $0.wallpaper!.preset == filename && $0.base == base }?.wallpaper?.scale ?? 1
)
}
}
@@ -11,7 +11,7 @@ import UIKit
import SwiftUI
extension View {
func uiKitContextMenu(hasImageOrVideo: Bool, maxWidth: CGFloat, itemWidth: Binding<CGFloat>, menu: Binding<UIMenu>, allowMenu: Binding<Bool>) -> some View {
func uiKitContextMenu(hasImageOrVideo: Bool, maxWidth: CGFloat, itemWidth: Binding<CGFloat>, menu: Binding<UIMenu>, allowMenu: Binding<Bool>, backgroundColor: Color) -> some View {
Group {
if allowMenu.wrappedValue {
if hasImageOrVideo {
@@ -19,10 +19,10 @@ extension View {
self.environmentObject(ChatModel.shared)
.overlay(DetermineWidthImageVideoItem())
.onPreferenceChange(DetermineWidthImageVideoItem.Key.self) { itemWidth.wrappedValue = $0 == 0 ? maxWidth : $0 }
, maxWidth: maxWidth, itemWidth: itemWidth, menu: menu)
, maxWidth: maxWidth, itemWidth: itemWidth, menu: menu, backgroundColor: backgroundColor)
.frame(maxWidth: itemWidth.wrappedValue)
} else {
InteractionView(content: self.environmentObject(ChatModel.shared), maxWidth: maxWidth, itemWidth: itemWidth, menu: menu)
InteractionView(content: self.environmentObject(ChatModel.shared), maxWidth: maxWidth, itemWidth: itemWidth, menu: menu, backgroundColor: backgroundColor)
.fixedSize(horizontal: true, vertical: false)
}
} else {
@@ -42,11 +42,14 @@ struct InteractionView<Content: View>: UIViewRepresentable {
var maxWidth: CGFloat
var itemWidth: Binding<CGFloat>
@Binding var menu: UIMenu
var backgroundColor: Color
func makeUIView(context: Context) -> UIView {
let view = HostingViewHolder()
view.backgroundColor = .clear
let hostView = UIHostingController(rootView: content)
// hostView.view.backgroundColor = UIColor(backgroundColor)
hostView.view.backgroundColor = .clear
view.contentSize = hostView.view.intrinsicContentSize
hostView.view.translatesAutoresizingMaskIntoConstraints = false
let constraints = [
@@ -72,6 +75,7 @@ struct InteractionView<Content: View>: UIViewRepresentable {
if was != (uiView as! HostingViewHolder).contentSize {
uiView.invalidateIntrinsicContentSize()
}
//uiView.backgroundColor = UIColor(backgroundColor)
}
func makeCoordinator() -> Coordinator {
@@ -9,29 +9,53 @@
import SwiftUI
import SimpleXChat
let defaultProfileImageCorner = 22.5
struct ProfileImage: View {
var imageStr: String? = nil
var iconName: String = "person.crop.circle.fill"
var size: CGFloat
var color = Color(uiColor: .tertiarySystemGroupedBackground)
var backgroundColor: Color? = nil
@AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var radius = defaultProfileImageCorner
var body: some View {
if let image = imageStr,
let data = Data(base64Encoded: dropImagePrefix(image)),
let uiImage = UIImage(data: data) {
Image(uiImage: uiImage)
.resizable()
.clipShape(Circle())
clipProfileImage(Image(uiImage: uiImage), size: size, radius: radius)
} else {
Image(systemName: iconName)
.resizable()
.foregroundColor(color)
.frame(width: size, height: size)
.background(backgroundColor != nil ? backgroundColor! : .clear)
}
}
}
private let squareToCircleRatio = 0.935
private let radiusFactor = (1 - squareToCircleRatio) / 50
@ViewBuilder func clipProfileImage(_ img: Image, size: CGFloat, radius: Double) -> some View {
let v = img.resizable()
if radius >= 50 {
v.frame(width: size, height: size).clipShape(Circle())
} else if radius <= 0 {
let sz = size * squareToCircleRatio
v.frame(width: sz, height: sz).padding((size - sz) / 2)
} else {
let sz = size * (squareToCircleRatio + radius * radiusFactor)
v.frame(width: sz, height: sz)
.clipShape(RoundedRectangle(cornerRadius: sz * radius / 100, style: .continuous))
.padding((size - sz) / 2)
}
}
struct ProfileImage_Previews: PreviewProvider {
static var previews: some View {
ProfileImage(imageStr: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAKHGlDQ1BJQ0MgUHJvZmlsZQAASImFVgdUVNcWve9Nb7QZeu9NehtAem/Sq6gMQ28OQxWxgAQjEFFEREARNFQFg1KjiIhiIQgoYA9IEFBisCAq6OQNJNH4//r/zDpz9ttzz7n73ffWmg0A6QCDxYqD+QCIT0hmezlYywQEBsngngEYCAIy0AC6DGYSy8rDwxUg8Xf9d7wbAxC33tHgzvrP3/9nCISFJzEBgIIRTGey2MkILkawT1oyi4tnEUxjI6IQvMLFkauYqxjQQtewwuoaHy8bBNMBwJMZDHYkAERbhJdJZUYic4hhCNZOCItOQDB3vjkzioFwxLsIXhcRl5IOAImrRzs+fivCk7QRrIL0shAcwNUW+tX8yH/tFfrPXgxG5D84Pi6F+dc9ck+HHJ7g641UMSQlQATQBHEgBaQDGcACbLAVYaIRJhx5Dv+9j77aZ4OsZIFtSEc0iARRIBnpt/9qlvfqpGSQBhjImnCEcUU+NtxnujZy4fbqVEiU/wuXdQyA9S0cDqfzC+e2F4DzyLkSB79wyi0A8KoBcL2GmcJOXePQ3C8MIAJeQAOiQArIAxXuWwMMgSmwBHbAGbgDHxAINgMmojceUZUGMkEWyAX54AA4DMpAJTgJ6sAZ0ALawQVwGVwDt8AQGAUPwQSYBi/AAngHliEIwkEUiAqJQtKQIqQO6UJ0yByyg1whLygQCoEioQQoBcqE9kD5UBFUBlVB9dBPUCd0GboBDUP3oUloDnoNfYRRMBmmwZKwEqwF02Er2AX2gTfBkXAinAHnwPvhUrgaPg23wZfhW/AoPAG/gBdRAEVCCaFkURooOsoG5Y4KQkWg2KidqDxUCaoa1YTqQvWj7qAmUPOoD2gsmoqWQWugTdGOaF80E52I3okuQJeh69Bt6D70HfQkegH9GUPBSGDUMSYYJ0wAJhKThsnFlGBqMK2Yq5hRzDTmHRaLFcIqY42wjthAbAx2O7YAewzbjO3BDmOnsIs4HE4Up44zw7njGLhkXC7uKO407hJuBDeNe48n4aXxunh7fBA+AZ+NL8E34LvxI/gZ/DKBj6BIMCG4E8II2wiFhFOELsJtwjRhmchPVCaaEX2IMcQsYimxiXiV+Ij4hkQiyZGMSZ6kaNJuUinpLOk6aZL0gSxAViPbkIPJKeT95FpyD/k++Q2FQlGiWFKCKMmU/ZR6yhXKE8p7HiqPJo8TTxjPLp5ynjaeEZ6XvAReRV4r3s28GbwlvOd4b/PO8xH4lPhs+Bh8O/nK+Tr5xvkW+an8Ovzu/PH8BfwN/Df4ZwVwAkoCdgJhAjkCJwWuCExRUVR5qg2VSd1DPUW9Sp2mYWnKNCdaDC2fdoY2SFsQFBDUF/QTTBcsF7woOCGEElISchKKEyoUahEaE/ooLClsJRwuvE+4SXhEeElEXMRSJFwkT6RZZFTko6iMqJ1orOhB0XbRx2JoMTUxT7E0seNiV8XmxWnipuJM8TzxFvEHErCEmoSXxHaJkxIDEouSUpIOkizJo5JXJOelhKQspWKkiqW6peakqdLm0tHSxdKXpJ/LCMpYycTJlMr0ySzISsg6yqbIVskOyi7LKcv5ymXLNcs9lifK0+Uj5Ivle+UXFKQV3BQyFRoVHigSFOmKUYpHFPsVl5SUlfyV9iq1K80qiyg7KWcoNyo/UqGoWKgkqlSr3FXFqtJVY1WPqQ6pwWoGalFq5Wq31WF1Q/Vo9WPqw+sw64zXJayrXjeuQdaw0kjVaNSY1BTSdNXM1mzXfKmloBWkdVCrX+uztoF2nPYp7Yc6AjrOOtk6XTqvddV0mbrlunf1KHr2erv0OvRe6avrh+sf179nQDVwM9hr0GvwydDIkG3YZDhnpGAUYlRhNE6n0T3oBfTrxhhja+NdxheMP5gYmiSbtJj8YaphGmvaYDq7Xnl9+PpT66fM5MwYZlVmE+Yy5iHmJ8wnLGQtGBbVFk8t5S3DLGssZ6xUrWKsTlu9tNa2Zlu3Wi/ZmNjssOmxRdk62ObZDtoJ2Pnaldk9sZezj7RvtF9wMHDY7tDjiHF0cTzoOO4k6cR0qndacDZy3uHc50J28XYpc3nqqubKdu1yg92c3Q65PdqguCFhQ7s7cHdyP+T+2EPZI9HjZ0+sp4dnueczLx2vTK9+b6r3Fu8G73c+1j6FPg99VXxTfHv9eP2C/er9lvxt/Yv8JwK0AnYE3AoUC4wO7AjCBfkF1QQtbrTbeHjjdLBBcG7w2CblTembbmwW2xy3+eIW3i2MLedCMCH+IQ0hKwx3RjVjMdQptCJ0gWnDPMJ8EWYZVhw2F24WXhQ+E2EWURQxG2kWeShyLsoiqiRqPtomuiz6VYxjTGXMUqx7bG0sJ84/rjkeHx8S35kgkBCb0LdVamv61mGWOiuXNZFokng4cYHtwq5JgpI2JXUk05A/0oEUlZTvUiZTzVPLU9+n+aWdS+dPT0gf2Ka2bd+2mQz7jB+3o7czt/dmymZmZU7usNpRtRPaGbqzd5f8rpxd07sddtdlEbNis37J1s4uyn67x39PV45kzu6cqe8cvmvM5cll547vNd1b+T36++jvB/fp7Tu673NeWN7NfO38kvyVAmbBzR90fij9gbM/Yv9goWHh8QPYAwkHxg5aHKwr4i/KKJo65HaorVimOK/47eEth2+U6JdUHiEeSTkyUepa2nFU4eiBoytlUWWj5dblzRUSFfsqlo6FHRs5bnm8qVKyMr/y44noE/eqHKraqpWqS05iT6aefHbK71T/j/Qf62vEavJrPtUm1E7UedX11RvV1zdINBQ2wo0pjXOng08PnbE909Gk0VTVLNScfxacTTn7/KeQn8ZaXFp6z9HPNZ1XPF/RSm3Na4PatrUttEe1T3QEdgx3Onf2dpl2tf6s+XPtBdkL5RcFLxZ2E7tzujmXMi4t9rB65i9HXp7q3dL78ErAlbt9nn2DV12uXr9mf+1Kv1X/petm1y/cMLnReZN+s/2W4a22AYOB1l8MfmkdNBxsu210u2PIeKhreP1w94jFyOU7tneu3XW6e2t0w+jwmO/YvfHg8Yl7Yfdm78fdf/Ug9cHyw92PMI/yHvM9Lnki8aT6V9VfmycMJy5O2k4OPPV++nCKOfXit6TfVqZznlGelcxIz9TP6s5emLOfG3q+8fn0C9aL5fnc3/l/r3ip8vL8H5Z/DCwELEy/Yr/ivC54I/qm9q3+295Fj8Un7+LfLS/lvRd9X/eB/qH/o//HmeW0FdxK6SfVT12fXT4/4sRzOCwGm7FqBVBIwhERALyuBYASCAB1CPEPG9f8119+BvrK2fyNwVndL5jhvubRVsMQgCakeCFp04OsQ1LJEgAe5NodqT6WANbT+yf/iqQIPd21PXgaAcDJcjivtwJAQHLFgcNZ9uBwPlUgYhHf1z37f7V9g9e8ITewiP88wfWIYET6HPg21nzjV2fybQVcxfrg2/onng/F50lD/ccAAAA4ZVhJZk1NACoAAAAIAAGHaQAEAAAAAQAAABoAAAAAAAKgAgAEAAAAAQAAABigAwAEAAAAAQAAABgAAAAAwf1XlwAAAaNJREFUSA3FlT1LA0EQQBN/gYUYRTksJZVgEbCR/D+7QMr8ABtttBBCsLGzsLG2sxaxED/ie4d77u0dyaE5HHjczn7MzO7M7nU6/yXz+bwLhzCCjTQO+rZhDH3opuNLdRYN4RHe4RIKJ7R34Ro+4AEGSw2mE1iUwT18gpI74WvkGlccu4XNdH0jnYU7cAUacidn37qR23cOxc4aGU0nYUAn7iSWEHkz46w0ocdQu1X6B/AMQZ5o7KfBqNOfwRH8JB7FajGhnmcpKvQe3MEbvILiDm5gPXaCHnZr4vvFGMoEKudKn8YvQIOOe+YzCPop7dwJ3zRfJ7GDuso4YJGRa0yZgg4tUaNXdGrbuZWKKxzYYEJc2xp9AUUjGt8KC2jvgYadF8+10vJyDnNLXwbdiWUZi0fUK01Eoc+AZhCLZVzK4Vq6sDUdz+0dEcbbTTIOJmAyTVhx/WmvrExbv2jtPhWLKodjCtefZiEeZeVZWWSndgwj6fVf3XON8Qwq15++uoqrfYVrow6dGBpCq79ME291jaB0/Q2CPncyht/99MNO/vr9AqW/CGi8sJqbAAAAAElFTkSuQmCC")
ProfileImage(imageStr: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAKHGlDQ1BJQ0MgUHJvZmlsZQAASImFVgdUVNcWve9Nb7QZeu9NehtAem/Sq6gMQ28OQxWxgAQjEFFEREARNFQFg1KjiIhiIQgoYA9IEFBisCAq6OQNJNH4//r/zDpz9ttzz7n73ffWmg0A6QCDxYqD+QCIT0hmezlYywQEBsngngEYCAIy0AC6DGYSy8rDwxUg8Xf9d7wbAxC33tHgzvrP3/9nCISFJzEBgIIRTGey2MkILkawT1oyi4tnEUxjI6IQvMLFkauYqxjQQtewwuoaHy8bBNMBwJMZDHYkAERbhJdJZUYic4hhCNZOCItOQDB3vjkzioFwxLsIXhcRl5IOAImrRzs+fivCk7QRrIL0shAcwNUW+tX8yH/tFfrPXgxG5D84Pi6F+dc9ck+HHJ7g641UMSQlQATQBHEgBaQDGcACbLAVYaIRJhx5Dv+9j77aZ4OsZIFtSEc0iARRIBnpt/9qlvfqpGSQBhjImnCEcUU+NtxnujZy4fbqVEiU/wuXdQyA9S0cDqfzC+e2F4DzyLkSB79wyi0A8KoBcL2GmcJOXePQ3C8MIAJeQAOiQArIAxXuWwMMgSmwBHbAGbgDHxAINgMmojceUZUGMkEWyAX54AA4DMpAJTgJ6sAZ0ALawQVwGVwDt8AQGAUPwQSYBi/AAngHliEIwkEUiAqJQtKQIqQO6UJ0yByyg1whLygQCoEioQQoBcqE9kD5UBFUBlVB9dBPUCd0GboBDUP3oUloDnoNfYRRMBmmwZKwEqwF02Er2AX2gTfBkXAinAHnwPvhUrgaPg23wZfhW/AoPAG/gBdRAEVCCaFkURooOsoG5Y4KQkWg2KidqDxUCaoa1YTqQvWj7qAmUPOoD2gsmoqWQWugTdGOaF80E52I3okuQJeh69Bt6D70HfQkegH9GUPBSGDUMSYYJ0wAJhKThsnFlGBqMK2Yq5hRzDTmHRaLFcIqY42wjthAbAx2O7YAewzbjO3BDmOnsIs4HE4Up44zw7njGLhkXC7uKO407hJuBDeNe48n4aXxunh7fBA+AZ+NL8E34LvxI/gZ/DKBj6BIMCG4E8II2wiFhFOELsJtwjRhmchPVCaaEX2IMcQsYimxiXiV+Ij4hkQiyZGMSZ6kaNJuUinpLOk6aZL0gSxAViPbkIPJKeT95FpyD/k++Q2FQlGiWFKCKMmU/ZR6yhXKE8p7HiqPJo8TTxjPLp5ynjaeEZ6XvAReRV4r3s28GbwlvOd4b/PO8xH4lPhs+Bh8O/nK+Tr5xvkW+an8Ovzu/PH8BfwN/Df4ZwVwAkoCdgJhAjkCJwWuCExRUVR5qg2VSd1DPUW9Sp2mYWnKNCdaDC2fdoY2SFsQFBDUF/QTTBcsF7woOCGEElISchKKEyoUahEaE/ooLClsJRwuvE+4SXhEeElEXMRSJFwkT6RZZFTko6iMqJ1orOhB0XbRx2JoMTUxT7E0seNiV8XmxWnipuJM8TzxFvEHErCEmoSXxHaJkxIDEouSUpIOkizJo5JXJOelhKQspWKkiqW6peakqdLm0tHSxdKXpJ/LCMpYycTJlMr0ySzISsg6yqbIVskOyi7LKcv5ymXLNcs9lifK0+Uj5Ivle+UXFKQV3BQyFRoVHigSFOmKUYpHFPsVl5SUlfyV9iq1K80qiyg7KWcoNyo/UqGoWKgkqlSr3FXFqtJVY1WPqQ6pwWoGalFq5Wq31WF1Q/Vo9WPqw+sw64zXJayrXjeuQdaw0kjVaNSY1BTSdNXM1mzXfKmloBWkdVCrX+uztoF2nPYp7Yc6AjrOOtk6XTqvddV0mbrlunf1KHr2erv0OvRe6avrh+sf179nQDVwM9hr0GvwydDIkG3YZDhnpGAUYlRhNE6n0T3oBfTrxhhja+NdxheMP5gYmiSbtJj8YaphGmvaYDq7Xnl9+PpT66fM5MwYZlVmE+Yy5iHmJ8wnLGQtGBbVFk8t5S3DLGssZ6xUrWKsTlu9tNa2Zlu3Wi/ZmNjssOmxRdk62ObZDtoJ2Pnaldk9sZezj7RvtF9wMHDY7tDjiHF0cTzoOO4k6cR0qndacDZy3uHc50J28XYpc3nqqubKdu1yg92c3Q65PdqguCFhQ7s7cHdyP+T+2EPZI9HjZ0+sp4dnueczLx2vTK9+b6r3Fu8G73c+1j6FPg99VXxTfHv9eP2C/er9lvxt/Yv8JwK0AnYE3AoUC4wO7AjCBfkF1QQtbrTbeHjjdLBBcG7w2CblTembbmwW2xy3+eIW3i2MLedCMCH+IQ0hKwx3RjVjMdQptCJ0gWnDPMJ8EWYZVhw2F24WXhQ+E2EWURQxG2kWeShyLsoiqiRqPtomuiz6VYxjTGXMUqx7bG0sJ84/rjkeHx8S35kgkBCb0LdVamv61mGWOiuXNZFokng4cYHtwq5JgpI2JXUk05A/0oEUlZTvUiZTzVPLU9+n+aWdS+dPT0gf2Ka2bd+2mQz7jB+3o7czt/dmymZmZU7usNpRtRPaGbqzd5f8rpxd07sddtdlEbNis37J1s4uyn67x39PV45kzu6cqe8cvmvM5cll547vNd1b+T36++jvB/fp7Tu673NeWN7NfO38kvyVAmbBzR90fij9gbM/Yv9goWHh8QPYAwkHxg5aHKwr4i/KKJo65HaorVimOK/47eEth2+U6JdUHiEeSTkyUepa2nFU4eiBoytlUWWj5dblzRUSFfsqlo6FHRs5bnm8qVKyMr/y44noE/eqHKraqpWqS05iT6aefHbK71T/j/Qf62vEavJrPtUm1E7UedX11RvV1zdINBQ2wo0pjXOng08PnbE909Gk0VTVLNScfxacTTn7/KeQn8ZaXFp6z9HPNZ1XPF/RSm3Na4PatrUttEe1T3QEdgx3Onf2dpl2tf6s+XPtBdkL5RcFLxZ2E7tzujmXMi4t9rB65i9HXp7q3dL78ErAlbt9nn2DV12uXr9mf+1Kv1X/petm1y/cMLnReZN+s/2W4a22AYOB1l8MfmkdNBxsu210u2PIeKhreP1w94jFyOU7tneu3XW6e2t0w+jwmO/YvfHg8Yl7Yfdm78fdf/Ug9cHyw92PMI/yHvM9Lnki8aT6V9VfmycMJy5O2k4OPPV++nCKOfXit6TfVqZznlGelcxIz9TP6s5emLOfG3q+8fn0C9aL5fnc3/l/r3ip8vL8H5Z/DCwELEy/Yr/ivC54I/qm9q3+295Fj8Un7+LfLS/lvRd9X/eB/qH/o//HmeW0FdxK6SfVT12fXT4/4sRzOCwGm7FqBVBIwhERALyuBYASCAB1CPEPG9f8119+BvrK2fyNwVndL5jhvubRVsMQgCakeCFp04OsQ1LJEgAe5NodqT6WANbT+yf/iqQIPd21PXgaAcDJcjivtwJAQHLFgcNZ9uBwPlUgYhHf1z37f7V9g9e8ITewiP88wfWIYET6HPg21nzjV2fybQVcxfrg2/onng/F50lD/ccAAAA4ZVhJZk1NACoAAAAIAAGHaQAEAAAAAQAAABoAAAAAAAKgAgAEAAAAAQAAABigAwAEAAAAAQAAABgAAAAAwf1XlwAAAaNJREFUSA3FlT1LA0EQQBN/gYUYRTksJZVgEbCR/D+7QMr8ABtttBBCsLGzsLG2sxaxED/ie4d77u0dyaE5HHjczn7MzO7M7nU6/yXz+bwLhzCCjTQO+rZhDH3opuNLdRYN4RHe4RIKJ7R34Ro+4AEGSw2mE1iUwT18gpI74WvkGlccu4XNdH0jnYU7cAUacidn37qR23cOxc4aGU0nYUAn7iSWEHkz46w0ocdQu1X6B/AMQZ5o7KfBqNOfwRH8JB7FajGhnmcpKvQe3MEbvILiDm5gPXaCHnZr4vvFGMoEKudKn8YvQIOOe+YzCPop7dwJ3zRfJ7GDuso4YJGRa0yZgg4tUaNXdGrbuZWKKxzYYEJc2xp9AUUjGt8KC2jvgYadF8+10vJyDnNLXwbdiWUZi0fUK01Eoc+AZhCLZVzK4Vq6sDUdz+0dEcbbTTIOJmAyTVhx/WmvrExbv2jtPhWLKodjCtefZiEeZeVZWWSndgwj6fVf3XON8Qwq15++uoqrfYVrow6dGBpCq79ME291jaB0/Q2CPncyht/99MNO/vr9AqW/CGi8sJqbAAAAAElFTkSuQmCC", size: 63)
.previewLayout(.fixed(width: 63, height: 63))
.background(.black)
}
@@ -0,0 +1,19 @@
//
// ViewModifiers.swift
// SimpleX (iOS)
//
// Created by Avently on 12.06.2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import SwiftUI
extension View {
@ViewBuilder func `if`<Content: View>(_ condition: @autoclosure () -> Bool, transform: (Self) -> Content) -> some View {
if condition() {
transform(self)
} else {
self
}
}
}
@@ -140,7 +140,7 @@ struct PasscodeEntry: View {
ZStack {
Circle()
.frame(width: h, height: h)
.foregroundColor(Color(uiColor: .systemBackground))
.foregroundColor(AppTheme.shared.colors.background)
label()
}
}
@@ -29,7 +29,7 @@ struct PasscodeView: View {
}
.padding(.horizontal, 40)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(uiColor: .systemBackground))
.background(AppTheme.shared.colors.background)
}
private func verticalPasscodeView(_ g: GeometryProxy) -> some View {
@@ -53,6 +53,7 @@ private enum MigrateFromDeviceViewAlert: Identifiable {
struct MigrateFromDevice: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@Environment(\.dismiss) var dismiss: DismissAction
@Binding var showSettings: Bool
@Binding var showProgressOnSettings: Bool
@@ -215,7 +216,7 @@ struct MigrateFromDevice: View {
Section {
Button(action: { migrationState = .archiving }) {
settingsRow("tray.and.arrow.up") {
Text("Archive and upload").foregroundColor(.accentColor)
Text("Archive and upload").foregroundColor(theme.colors.primary)
}
}
} header: {
@@ -249,7 +250,7 @@ struct MigrateFromDevice: View {
}
}
let ratio = Float(uploadedBytes) / Float(totalBytes)
MigrateFromDevice.largeProgressView(ratio, "\(Int(ratio * 100))%", "\(ByteCountFormatter.string(fromByteCount: uploadedBytes, countStyle: .binary)) uploaded")
MigrateFromDevice.largeProgressView(ratio, "\(Int(ratio * 100))%", "\(ByteCountFormatter.string(fromByteCount: uploadedBytes, countStyle: .binary)) uploaded", theme.colors.primary)
}
.onAppear {
startUploading(totalBytes, archivePath)
@@ -263,7 +264,7 @@ struct MigrateFromDevice: View {
migrationState = .uploadProgress(uploadedBytes: 0, totalBytes: totalBytes, fileId: 0, archivePath: archivePath, ctrl: nil)
}) {
settingsRow("tray.and.arrow.up") {
Text("Repeat upload").foregroundColor(.accentColor)
Text("Repeat upload").foregroundColor(theme.colors.primary)
}
}
} header: {
@@ -299,7 +300,7 @@ struct MigrateFromDevice: View {
}
Button(action: { finishMigration(fileId, ctrl) }) {
settingsRow("checkmark") {
Text("Finalize migration").foregroundColor(.accentColor)
Text("Finalize migration").foregroundColor(theme.colors.primary)
}
}
} footer: {
@@ -340,7 +341,7 @@ struct MigrateFromDevice: View {
}
Button(action: { alert = .deleteChat() }) {
settingsRow("trash.fill") {
Text("Delete database from this device").foregroundColor(.accentColor)
Text("Delete database from this device").foregroundColor(theme.colors.primary)
}
}
} header: {
@@ -379,7 +380,7 @@ struct MigrateFromDevice: View {
.truncationMode(.middle)
}
static func largeProgressView(_ value: Float, _ title: String, _ description: LocalizedStringKey) -> some View {
static func largeProgressView(_ value: Float, _ title: String, _ description: LocalizedStringKey, _ primaryColor: Color) -> some View {
ZStack {
VStack {
Text(description)
@@ -389,7 +390,7 @@ struct MigrateFromDevice: View {
Text(title)
.font(.system(size: 54))
.bold()
.foregroundColor(.accentColor)
.foregroundColor(primaryColor)
Text(description)
.font(.title3)
@@ -398,7 +399,7 @@ struct MigrateFromDevice: View {
Circle()
.trim(from: 0, to: CGFloat(value))
.stroke(
Color.accentColor,
primaryColor,
style: StrokeStyle(lineWidth: 27)
)
.rotationEffect(.degrees(180))
@@ -91,6 +91,7 @@ private enum MigrateToDeviceViewAlert: Identifiable {
struct MigrateToDevice: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@Environment(\.dismiss) var dismiss: DismissAction
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@Binding var migrationState: MigrationToState?
@@ -243,7 +244,7 @@ struct MigrateToDevice: View {
}
}
let ratio = Float(downloadedBytes) / Float(max(totalBytes, 1))
MigrateFromDevice.largeProgressView(ratio, "\(Int(ratio * 100))%", "\(ByteCountFormatter.string(fromByteCount: downloadedBytes, countStyle: .binary)) downloaded")
MigrateFromDevice.largeProgressView(ratio, "\(Int(ratio * 100))%", "\(ByteCountFormatter.string(fromByteCount: downloadedBytes, countStyle: .binary)) downloaded", theme.colors.primary)
}
}
@@ -255,7 +256,7 @@ struct MigrateToDevice: View {
migrationState = .linkDownloading(link: link)
}) {
settingsRow("tray.and.arrow.down") {
Text("Repeat download").foregroundColor(.accentColor)
Text("Repeat download").foregroundColor(theme.colors.primary)
}
}
} header: {
@@ -293,7 +294,7 @@ struct MigrateToDevice: View {
migrationState = .archiveImport(archivePath: archivePath)
}) {
settingsRow("square.and.arrow.down") {
Text("Repeat import").foregroundColor(.accentColor)
Text("Repeat import").foregroundColor(theme.colors.primary)
}
}
} header: {
@@ -334,7 +335,7 @@ struct MigrateToDevice: View {
migrationState = .migration(passphrase: passphrase, confirmation: confirmation, useKeychain: useKeychain)
}) {
settingsRow("square.and.arrow.down") {
Text(button).foregroundColor(.accentColor)
Text(button).foregroundColor(theme.colors.primary)
}
}
} else {
@@ -364,6 +365,7 @@ struct MigrateToDevice: View {
}
struct OnionView: View {
@EnvironmentObject var theme: AppTheme
@State var appSettings: AppSettings
@State private var onionHosts: OnionHosts = .no
var finishMigration: (AppSettings) -> Void
@@ -381,7 +383,7 @@ struct MigrateToDevice: View {
finishMigration(appSettings)
}) {
settingsRow("checkmark") {
Text("Apply").foregroundColor(.accentColor)
Text("Apply").foregroundColor(theme.colors.primary)
}
}
} header: {
@@ -475,6 +477,7 @@ struct MigrateToDevice: View {
chatInitControllerRemovingDatabases()
}
try await apiDeleteStorage()
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
do {
let config = ArchiveConfig(archivePath: archivePath)
let archiveErrors = try await apiImportArchive(config: config)
@@ -30,6 +30,7 @@ struct AddContactLearnMore: View {
}
.listRowBackground(Color.clear)
}
.modifier(ThemedBackground())
}
}
@@ -11,6 +11,7 @@ import SimpleXChat
struct AddGroupView: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@Environment(\.dismiss) var dismiss: DismissAction
@AppStorage(GROUP_DEFAULT_INCOGNITO, store: groupDefaults) private var incognitoDefault = false
@State private var chat: Chat?
@@ -70,9 +71,7 @@ struct AddGroupView: View {
ZStack(alignment: .center) {
ZStack(alignment: .topTrailing) {
ProfileImage(imageStr: profile.image, color: Color(uiColor: .secondarySystemGroupedBackground))
.aspectRatio(1, contentMode: .fit)
.frame(maxWidth: 128, maxHeight: 128)
ProfileImage(imageStr: profile.image, size: 128, color: Color(uiColor: .secondarySystemGroupedBackground))
if profile.image != nil {
Button {
profile.image = nil
@@ -97,7 +96,7 @@ struct AddGroupView: View {
Section {
groupNameTextField()
Button(action: createGroup) {
settingsRow("checkmark", color: .accentColor) { Text("Create group") }
settingsRow("checkmark", color: theme.colors.primary) { Text("Create group") }
}
.disabled(!canCreateProfile())
IncognitoToggle(incognitoEnabled: $incognitoDefault)
@@ -146,6 +145,7 @@ struct AddGroupView: View {
profile.image = nil
}
}
.modifier(ThemedBackground())
}
func groupNameTextField() -> some View {
@@ -94,7 +94,7 @@ struct NewChatView: View {
.background(
// Rectangle is needed for swipe gesture to work on mostly empty views (creatingLinkProgressView and retryButton)
Rectangle()
.fill(Color(uiColor: .systemGroupedBackground))
.fill(AppTheme.shared.colors.background)
)
.animation(.easeInOut(duration: 0.3333), value: selection)
.gesture(DragGesture(minimumDistance: 20.0, coordinateSpace: .local)
@@ -113,7 +113,7 @@ struct NewChatView: View {
}
)
}
.background(Color(.systemGroupedBackground))
.modifier(ThemedBackground())
.onChange(of: invitationUsed) { used in
if used && !(m.showingInvitation?.connChatUsed ?? true) {
m.markShowingInvitationUsed()
@@ -367,11 +367,12 @@ struct ScannerInView: View {
@Binding var showQRCodeScanner: Bool
let processQRCode: (_ resp: Result<ScanResult, ScanError>) -> Void
@State private var cameraAuthorizationStatus: AVAuthorizationStatus?
var scanMode: ScanMode = .continuous
var body: some View {
Group {
if showQRCodeScanner, case .authorized = cameraAuthorizationStatus {
CodeScannerView(codeTypes: [.qr], scanMode: .continuous, completion: processQRCode)
CodeScannerView(codeTypes: [.qr], scanMode: scanMode, completion: processQRCode)
.aspectRatio(1, contentMode: .fit)
.cornerRadius(12)
.listRowBackground(Color.clear)
@@ -436,6 +437,7 @@ struct ScannerInView: View {
}
}
private func linkTextView(_ link: String) -> some View {
Text(link)
.lineLimit(1)
@@ -486,6 +488,7 @@ func strHasSingleSimplexLink(_ str: String) -> FormattedText? {
}
struct IncognitoToggle: View {
@EnvironmentObject var theme: AppTheme
@Binding var incognitoEnabled: Bool
@State private var showIncognitoSheet = false
@@ -499,7 +502,7 @@ struct IncognitoToggle: View {
HStack(spacing: 6) {
Text("Incognito")
Image(systemName: "info.circle")
.foregroundColor(.accentColor)
.foregroundColor(theme.colors.primary)
.font(.system(size: 14))
}
.onTapGesture {
@@ -66,6 +66,7 @@ struct CreateProfile: View {
}
}
.navigationTitle("Create your profile")
.modifier(ThemedBackground())
.alert(item: $alert) { a in userProfileAlert(a, $displayName) }
.onAppear() {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
@@ -78,6 +79,7 @@ struct CreateProfile: View {
struct CreateFirstProfile: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@Environment(\.dismiss) var dismiss
@State private var displayName: String = ""
@FocusState private var focusDisplayName
@@ -89,9 +91,9 @@ struct CreateFirstProfile: View {
.font(.largeTitle)
.bold()
Text("Your profile, contacts and delivered messages are stored on your device.")
.foregroundColor(.secondary)
.foregroundColor(theme.colors.secondary)
Text("The profile is only shared with your contacts.")
.foregroundColor(.secondary)
.foregroundColor(theme.colors.secondary)
.padding(.bottom)
}
.padding(.bottom)
@@ -44,6 +44,7 @@ struct HowItWorks: View {
.lineLimit(10)
.padding()
.frame(maxHeight: .infinity, alignment: .top)
.modifier(ThemedBackground())
}
}
@@ -78,6 +78,7 @@ struct SetNotificationsMode: View {
}
struct NtfModeSelector: View {
@EnvironmentObject var theme: AppTheme
var mode: NotificationsMode
@Binding var selection: NotificationsMode
@State private var tapped = false
@@ -87,7 +88,7 @@ struct NtfModeSelector: View {
VStack(alignment: .leading, spacing: 4) {
Text(mode.label)
.font(.headline)
.foregroundColor(selection == mode ? .accentColor : .secondary)
.foregroundColor(selection == mode ? theme.colors.primary : theme.colors.secondary)
Text(ntfModeDescription(mode))
.lineLimit(10)
.font(.subheadline)
@@ -95,11 +96,11 @@ struct NtfModeSelector: View {
.padding(12)
}
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(uiColor: tapped ? .secondarySystemFill : .systemBackground))
.background(tapped ? Color(uiColor: .secondarySystemFill) : theme.colors.background)
.clipShape(RoundedRectangle(cornerRadius: 18))
.overlay(
RoundedRectangle(cornerRadius: 18)
.stroke(selection == mode ? Color.accentColor : Color(uiColor: .secondarySystemFill), lineWidth: 2)
.stroke(selection == mode ? theme.colors.primary : Color(uiColor: .secondarySystemFill), lineWidth: 2)
)
._onButtonGesture { down in
tapped = down
@@ -79,7 +79,7 @@ struct SimpleXInfo: View {
MigrateToDevice(migrationState: $m.migrationState)
}
.navigationTitle("Migrate here")
.background(colorScheme == .light ? Color(uiColor: .tertiarySystemGroupedBackground) : .clear)
.modifier(ThemedBackground())
}
}
.sheet(isPresented: $showHowItWorks) {
@@ -375,7 +375,59 @@ private let versionDescriptions: [VersionDescription] = [
description: "Thanks to the users [contribute via Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!"
),
]
)
),
VersionDescription(
version: "v5.7",
post: URL(string: "https://simplex.chat/blog/20240426-simplex-legally-binding-transparency-v5-7-better-user-experience.html"),
features: [
FeatureDescription(
icon: "key",
title: "Quantum resistant encryption",
description: "Will be enabled in direct chats!"
),
FeatureDescription(
icon: "arrowshape.turn.up.forward",
title: "Forward and save messages",
description: "Message source remains private."
),
FeatureDescription(
icon: "music.note",
title: "In-call sounds",
description: "When connecting audio and video calls."
),
FeatureDescription(
icon: "person.crop.square",
title: "Shape profile images",
description: "Square, circle, or anything in between."
),
FeatureDescription(
icon: "antenna.radiowaves.left.and.right",
title: "Network management",
description: "More reliable network connection."
)
]
),
VersionDescription(
version: "v5.8",
post: URL(string: "https://simplex.chat/blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.html"),
features: [
FeatureDescription(
icon: "arrow.forward",
title: "Private message routing 🚀",
description: "Protect your IP address from the messaging relays chosen by your contacts.\nEnable in *Network & servers* settings."
),
FeatureDescription(
icon: "network.badge.shield.half.filled",
title: "Safely receive files",
description: "Confirm files from unknown servers."
),
FeatureDescription(
icon: "battery.50",
title: "Improved message delivery",
description: "With reduced battery usage."
)
]
),
]
private let lastVersion = versionDescriptions.last!.version
@@ -178,26 +178,32 @@ struct ConnectDesktopView: View {
}
}
.navigationTitle("Connect to desktop")
.modifier(ThemedBackground())
}
private func connectingDesktopView(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo?) -> some View {
List {
Section("Connecting to desktop") {
ctrlDeviceNameText(session, rc)
ctrlDeviceVersionText(session)
}
ZStack {
List {
Section("Connecting to desktop") {
ctrlDeviceNameText(session, rc)
ctrlDeviceVersionText(session)
}
if let sessCode = session.sessionCode {
Section("Session code") {
sessionCodeText(sessCode)
if let sessCode = session.sessionCode {
Section("Session code") {
sessionCodeText(sessCode)
}
}
Section {
disconnectButton()
}
}
.navigationTitle("Connecting to desktop")
Section {
disconnectButton()
}
ProgressView().scaleEffect(2)
}
.navigationTitle("Connecting to desktop")
.modifier(ThemedBackground())
}
private func searchingDesktopView() -> some View {
@@ -215,6 +221,7 @@ struct ConnectDesktopView: View {
}
}
.navigationTitle("Connecting to desktop")
.modifier(ThemedBackground())
}
@ViewBuilder private func foundDesktopView(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo, _ compatible: Bool) -> some View {
@@ -242,6 +249,7 @@ struct ConnectDesktopView: View {
}
}
.navigationTitle("Found desktop")
.modifier(ThemedBackground())
if compatible && connectRemoteViaMulticastAuto {
v.onAppear { confirmKnownDesktop(rc) }
@@ -271,6 +279,7 @@ struct ConnectDesktopView: View {
}
}
.navigationTitle("Verify connection")
.modifier(ThemedBackground())
}
private func ctrlDeviceNameText(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo?) -> Text {
@@ -311,6 +320,7 @@ struct ConnectDesktopView: View {
}
}
.navigationTitle("Connected to desktop")
.modifier(ThemedBackground())
}
private func sessionCodeText(_ code: String) -> some View {
@@ -332,13 +342,7 @@ struct ConnectDesktopView: View {
private func scanDesctopAddressView() -> some View {
Section("Scan QR code from desktop") {
CodeScannerView(codeTypes: [.qr], scanMode: .oncePerCode, completion: processDesktopQRCode)
.aspectRatio(1, contentMode: .fit)
.cornerRadius(12)
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.padding(.horizontal)
ScannerInView(showQRCodeScanner: $showQRCodeScanner, processQRCode: processDesktopQRCode, scanMode: .oncePerCode)
}
}
@@ -391,6 +395,7 @@ struct ConnectDesktopView: View {
}
}
.navigationTitle("Linked desktops")
.modifier(ThemedBackground())
}
private func remoteCtrlView(_ rc: RemoteCtrlInfo) -> some View {
+2
View File
@@ -106,6 +106,7 @@ struct TerminalView: View {
}
.navigationViewStyle(.stack)
.navigationTitle("Chat console")
.modifier(ThemedBackground())
}
func scrollToBottom(_ proxy: ScrollViewProxy, animation: Animation = .default) {
@@ -130,6 +131,7 @@ struct TerminalView: View {
}
}
.onDisappear { terminalItem = nil }
.modifier(ThemedBackground())
}
func consoleSendMessage() {
@@ -51,10 +51,10 @@ struct AdvancedNetworkSettings: View {
}
.disabled(currentNetCfg == NetCfg.proxyDefaults)
timeoutSettingPicker("TCP connection timeout", selection: $netCfg.tcpConnectTimeout, values: [5_000000, 7_500000, 10_000000, 15_000000, 20_000000, 30_000000, 40_000000], label: secondsLabel)
timeoutSettingPicker("TCP connection timeout", selection: $netCfg.tcpConnectTimeout, values: [10_000000, 15_000000, 20_000000, 30_000000, 45_000000, 60_000000, 90_000000], label: secondsLabel)
timeoutSettingPicker("Protocol timeout", selection: $netCfg.tcpTimeout, values: [5_000000, 7_000000, 10_000000, 15_000000, 20_000000, 30_000000], label: secondsLabel)
timeoutSettingPicker("Protocol timeout per KB", selection: $netCfg.tcpTimeoutPerKb, values: [2_500, 5_000, 10_000, 15_000, 20_000, 30_000], label: secondsLabel)
intSettingPicker("Receiving concurrency", selection: $netCfg.rcvConcurrency, values: [1, 2, 4, 8, 12, 16, 24], label: "")
// intSettingPicker("Receiving concurrency", selection: $netCfg.rcvConcurrency, values: [1, 2, 4, 8, 12, 16, 24], label: "")
timeoutSettingPicker("PING interval", selection: $netCfg.smpPingInterval, values: [120_000000, 300_000000, 600_000000, 1200_000000, 2400_000000, 3600_000000], label: secondsLabel)
intSettingPicker("PING count", selection: $netCfg.smpPingCount, values: [1, 2, 3, 5, 8], label: "")
Toggle("Enable TCP keep-alive", isOn: $enableKeepAlive)
@@ -23,6 +23,7 @@ extension AppSettings {
setNetCfg(val)
}
if let val = privacyEncryptLocalFiles { privacyEncryptLocalFilesGroupDefault.set(val) }
if let val = privacyAskToApproveRelays { privacyAskToApproveRelaysGroupDefault.set(val) }
if let val = privacyAcceptImages {
privacyAcceptImagesGroupDefault.set(val)
def.setValue(val, forKey: DEFAULT_PRIVACY_ACCEPT_IMAGES)
@@ -43,6 +44,11 @@ extension AppSettings {
if let val = androidCallOnLockScreen { def.setValue(val.rawValue, forKey: ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN) }
if let val = iosCallKitEnabled { callKitEnabledGroupDefault.set(val) }
if let val = iosCallKitCallsInRecents { def.setValue(val, forKey: DEFAULT_CALL_KIT_CALLS_IN_RECENTS) }
if let val = uiProfileImageCornerRadius { def.setValue(val, forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) }
if let val = uiColorScheme { def.setValue(val, forKey: DEFAULT_CURRENT_THEME) }
if let val = uiDarkColorScheme { def.setValue(val, forKey: DEFAULT_SYSTEM_DARK_THEME) }
if let val = uiCurrentThemeIds { def.setValue(val, forKey: DEFAULT_CURRENT_THEME_IDS) }
if let val = uiThemes { def.setValue(val.skipDuplicates(), forKey: DEFAULT_THEME_OVERRIDES) }
}
public static var current: AppSettings {
@@ -50,6 +56,7 @@ extension AppSettings {
var c = AppSettings.defaults
c.networkConfig = getNetCfg()
c.privacyEncryptLocalFiles = privacyEncryptLocalFilesGroupDefault.get()
c.privacyAskToApproveRelays = privacyAskToApproveRelaysGroupDefault.get()
c.privacyAcceptImages = privacyAcceptImagesGroupDefault.get()
c.privacyLinkPreviews = def.bool(forKey: DEFAULT_PRIVACY_LINK_PREVIEWS)
c.privacyShowChatPreviews = def.bool(forKey: DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS)
@@ -67,6 +74,11 @@ extension AppSettings {
c.androidCallOnLockScreen = AppSettingsLockScreenCalls(rawValue: def.string(forKey: ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN)!)
c.iosCallKitEnabled = callKitEnabledGroupDefault.get()
c.iosCallKitCallsInRecents = def.bool(forKey: DEFAULT_CALL_KIT_CALLS_IN_RECENTS)
c.uiProfileImageCornerRadius = def.float(forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS)
c.uiColorScheme = currentThemeDefault.get()
c.uiDarkColorScheme = systemDarkThemeDefault.get()
c.uiCurrentThemeIds = currentThemeIdsDefault.get()
c.uiThemes = themeOverridesDefault.get()
return c
}
}

Some files were not shown because too many files have changed in this diff Show More