Compare commits

...

336 Commits

Author SHA1 Message Date
Evgeny Poberezkin 85dc467f74 5.8.1: ios 225, android 221, desktop 54 2024-06-19 23:41:23 +01:00
spaced4ndy 89e65c2fc1 multiplatform: check preferences before share/forward (#4338) 2024-06-19 18:53:45 +04:00
Evgeny Poberezkin 0fa8d77214 ios: fix search disappearing in forward view (#4337)
* ios: fix search disappearing in forward view

* update

* diff

* refactor

* use List
2024-06-19 12:51:56 +01:00
spaced4ndy 6ba82c8c39 ios: check preferences before forward (#4336)
* ios: check preferences before forward

* rework

* clean up

* shorter message

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2024-06-19 13:49:44 +04:00
Evgeny Poberezkin e19c14b249 ios: update core library to 5.8.1 2024-06-19 10:12:22 +01:00
Evgeny Poberezkin d993381d31 Revert "web: glossary fix (#4298)"
This reverts commit d3274b28ba.
2024-06-18 23:44:58 +01:00
Evgeny Poberezkin 0198c1f110 core: 5.8.1.0 2024-06-18 23:12:27 +01:00
spaced4ndy 6c4c9b172c core: fix markdown check (#4334)
* core: fix markdown

* more tests

* enable tests

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2024-06-18 12:44:33 -07:00
spaced4ndy d9a61af401 core: fix preference application (#4330) 2024-06-18 10:37:45 +04:00
spaced4ndy a58522ef15 core: api to reconnect single server (#4317) 2024-06-17 11:52:55 +04:00
Evgeny Poberezkin bcd510c913 cli: option to confirm up migrations (#4150)
* cli: option to confirm up migrations

* fix test
2024-06-16 20:24:37 -07:00
M. Sarmad Qadeer c4083e00c5 web: update feed (#4255)
* web: update feed

* change date filter in feed.rss
2024-06-16 14:31:36 -07:00
M. Sarmad Qadeer 7cd35b9068 web: design fixes eg: blog titles & link wrap (#4305)
Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2024-06-16 14:22:53 -07:00
M. Sarmad Qadeer d3274b28ba web: glossary fix (#4298) 2024-06-16 08:23:40 -07:00
Narasimha-sc 40bd5dc2da web: update copyright year 2023->2024 (#4297)
* Update copyright year

* Update ar.json

* Update bg.json

* Update cs.json

* Update de.json

* Update es.json

* Update fr.json

* Update hu.json

* Update it.json

* Update ja.json

* Update nl.json

* Update pl.json

* Update pt_BR.json

* Update ru.json
2024-06-16 08:19:43 -07: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
Evgeny Poberezkin 8e3a713a49 ui: limit max size of events, sort members by role (#4058) 2024-04-21 10:57:54 +01:00
Evgeny Poberezkin 7cec79e79b 5.7-beta.1: ios 208, android 197, desktop 38 2024-04-20 22:07:04 +01:00
Evgeny Poberezkin e9db7e8e58 ios: export translations 2024-04-20 19:57:40 +01:00
Evgeny Poberezkin f53882049f ios: update core library 2024-04-20 19:54:47 +01:00
Evgeny Poberezkin 8f4c9a673a core: 5.7.0.1 2024-04-20 19:39:20 +01:00
Evgeny Poberezkin 02f980e968 core: update simplexmq (better newtork congestion handling, larger connection timeouts) (#4055)
* core: update simplexmq (better newtork congestion handling, larger connection timeouts)

* ui: update default TCP connect timeout, add receiving concurrency

* update simplexmq
2024-04-20 19:35:11 +01:00
Stanislav Dmitrenko 412f75219a android: select audio source (#4040)
* android, desktop: select audio source

* improvements

* fix possible crash

* changes
2024-04-20 19:19:37 +01:00
Evgeny Poberezkin c8c81a840b core: revert build to use two branches (#4052)
* Revert "scripts: ghc version update (#4010)"

This reverts commit 35b7f2cb1c.

* Revert "Force include hs_init_with_rtsopts (#4028)"

This reverts commit 76a33a3743.

* Revert "core: fix nix config (#4003)"

This reverts commit f8e6a78a3b.

* Revert "ci: build armv7a with 8.10.7 in the main branches (master, stable) (#3733)"

This reverts commit 0d7a32877f.
2024-04-19 21:17:22 +01:00
Stanislav Dmitrenko 925cb39397 desktop: using system sound in screenshare (#4035)
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-19 17:37:47 +01:00
Stanislav Dmitrenko f6ceb89c4b android, desktop: forward ui (#4039)
* android, desktop: forward ui

* changes

* attachment icon

* reorder lines

* reorder lines

* change

* inline content + formatted text

* fix header padding

* fix

* fix2

* Revert "fix header padding"

This reverts commit 63f2b15f02.

* broken default parameter

* changes

* more checks for file forwarding

* paddings

* padding

* padding and fix

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-04-19 17:36:32 +01:00
Evgeny Poberezkin a35d392f76 core: update simplexmq 2024-04-19 17:25:34 +01:00
spaced4ndy 1306c84938 ios: in item context menu don't show forward button if file doesn't exist, add download button (#4048) 2024-04-19 17:21:35 +01:00
Stanislav Dmitrenko 6484e36b82 ios: fix mute/unmute button state (#4046) 2024-04-19 17:20:31 +01:00
Stanislav Dmitrenko 918be31f2c desktop: fix freeze that happened when new chat items arrives while scrolling via scrollbar (#4050) 2024-04-19 15:57:00 +01:00
spaced4ndy 648884044f core: prohibit forwarding file if it's not found (#4047) 2024-04-19 11:31:44 +04:00
Stanislav Dmitrenko 505c6cb062 ios: prevent a crash when exiting the app (#4041)
* ios: prevent a crash when exiting the app

* log
2024-04-19 07:23:13 +01:00
Stanislav Dmitrenko 065c8f8861 ios: fix typing using keyboard suggestions (#4045) 2024-04-19 07:17:17 +01:00
spaced4ndy 6c509027f7 docs: IP address protection rfc (#4036) 2024-04-17 13:53:45 +04:00
Evgeny Poberezkin 4eafdc3723 blog: metadata in messengers (#4038)
* blog: metadata in messengers

* readme

* call.js

* picture

* revert call.js
2024-04-16 20:18:22 +01:00
spaced4ndy 77f5c678d5 ios: forward ui (#4024)
* wip

* wip

* fixes

* improvements

* info

* fix

* dont cleanup file

* info design, text

* text

* rework with LazyVStack

* delay

* refactor

* move delay

* fix

* refactor

* refactor 2

* keep current message

* forward with quote, context icons

* info

* preserve draft

* remove newline

* improve search

* info

* layout

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-16 09:28:39 +01:00
Stanislav Dmitrenko 5e71d00ace android, desktop: workaround of missing stuns support in calls (#4033)
* android, desktop: workaround of missing stuns support in calls

* const
2024-04-16 08:38:46 +01:00
Stanislav Dmitrenko c2169f7813 desktop: fix video calls in Firefox (#4034)
* desktop: fix video calls in Firefox

* one more play()
2024-04-16 08:22:40 +01:00
spaced4ndy 7ff4b05c90 core: remember new origin for received forwarded messages (#4032) 2024-04-15 17:19:37 +04:00
Evgeny Poberezkin d7f8bff957 Merge branch 'stable' 2024-04-15 11:16:33 +01:00
sh bda0917787 github: fix cla action (#4030) 2024-04-15 10:51:24 +01:00
Evgeny Poberezkin 0eae7f56f3 5.7-beta.0: ios 207, android 196, desktop 37 2024-04-14 21:14:15 +01:00
Stanislav Dmitrenko 24e3806b25 webrtc: updated credentials (#4011)
* webrtc: updated credentials

* no udp

* change

* stuns

* added stun back

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-14 19:18:12 +01:00
Evgeny Poberezkin f606e5742c ios: update library 2024-04-14 19:11:15 +01:00
Evgeny Poberezkin c6af2c285a core: 5.7.0.0 (#4027) 2024-04-14 17:31:56 +01:00
Stanislav Dmitrenko 35b7f2cb1c scripts: ghc version update (#4010) 2024-04-14 16:42:04 +01:00
Evgeny Poberezkin 76a33a3743 Force include hs_init_with_rtsopts (#4028)
Co-authored-by: Moritz Angermann <moritz.angermann@gmail.com>
2024-04-14 16:12:34 +01:00
Evgeny Poberezkin b6fd5e61b6 Merge branch 'stable' 2024-04-13 12:12:51 +01:00
artur fe639eed48 docs: Add Polish translation (#4001)
* docs: Add polish translation

* README: Add link to polish translation

* docs: Polish version of README - Fix links

* docs: Add links to polish translation, fix

* docs: Fix polish translation thanks to support team

* docs: Add links to polish translation in english version of docs

* fix links

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-13 12:12:21 +01:00
spaced4ndy 638fd3756a core: correctly populate data for items forwarded from groups (#4025) 2024-04-13 11:52:39 +01:00
Stanislav Dmitrenko de81434b36 android, desktop: role-based preferences, SimpleX links preference (#4023)
* android, desktop: role-based preferences, SimpleX links preference

* disable for now

* different way of disabling send button

* mic icon filled

* icons

* braces

* colors

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-13 11:51:10 +01:00
Evgeny Poberezkin 433b3652fb core: simplexmq 5.6.2.2 2024-04-13 09:33:37 +01:00
Alexander Bondarenko 86fe2212c3 chat: forward files with streaming re-encryption (#4022)
* chat: forward files with streaming re-encryption

* refactor
2024-04-13 09:32:43 +01:00
Evgeny Poberezkin 182225a5b1 ios: role-based preferences, SimpleX links preference (#4021)
* ios: role-based preferences, SimpleX links preference

* disable some options
2024-04-12 11:29:58 +01:00
spaced4ndy e10c63dc64 ui: deletable field (#4019) 2024-04-12 13:10:47 +04:00
spaced4ndy 29941729d2 ui: forwarded item header (#4017) 2024-04-12 12:56:09 +04:00
spaced4ndy ee53377070 core: fix forwarded item deletion (#4018) 2024-04-12 12:55:04 +04:00
spaced4ndy 0deb6a4eb1 ui: forward types (#4008)
* ios: forward types

* android

* move
2024-04-11 13:58:56 +01:00
Stanislav Dmitrenko b84ef0ce35 android: network observer (#3992)
* android: network observer

* removed unused code

* rename

* rename

* new line

* rename

* changes in logic

* adapted to ios logic

* change

* change

* change

* networkLost

* item in settings

* reorder

* changes

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-11 13:26:24 +01:00
Stanislav Dmitrenko 34dee32def android, desktop: sound prompts and vibration (#3997)
* android, desktop: sound prompts and vibration

* moved audio playing to js

* Revert "moved audio playing to js"

This reverts commit a9355c2d88.

* sound from speaker/earpiece

* better

* changes

* license of audio

* reorder lines

* loop fix

* awaiting call receipt

* desktop

* new line
2024-04-11 13:19:00 +01:00
Stanislav Dmitrenko 94851f177b ios: noRcvUser handling while downloading a file in migration process (#4012) 2024-04-11 12:05:01 +01:00
Stanislav Dmitrenko e560b49d14 ios: sound prompts and vibration during calls (#4005)
* ios: sound prompts and vibration

* awaiting call receipt

* update

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-11 12:02:41 +01:00
Stanislav Dmitrenko d8b52ee0d3 ios: network observer (#3996)
* ios: network observer

* update

* remove metered

* update tcpTimeoutPerKb

* network connection in settings

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-11 11:31:30 +01:00
Evgeny Poberezkin a9c0f356b4 ios: update core library 2024-04-11 11:25:06 +01:00
Evgeny Poberezkin 8a3710482b core: update simplexmq 2024-04-11 10:47:51 +01:00
Evgeny Poberezkin b3a4ea8e57 core: api to update user network state (#3998)
* core: api to update user network state

* simplexmq

* update simplexmq

* simplexmq
2024-04-10 23:32:55 +01:00
Evgeny Poberezkin 4be6c69633 core: update simplexmq 2024-04-10 08:49:06 +01:00
Evgeny Poberezkin 005d99ad50 docs: trasparency reports (#4007) 2024-04-09 21:45:48 +01:00
Narasimha-sc 36f86beafd docs: device linking info on downloads page #4004 2024-04-09 20:30:08 +01:00
Evgeny Poberezkin 1fa79a2654 android, desktop: fix incorrect event type for critical/internal error alerts 2024-04-09 18:18:11 +01:00
Evgeny Poberezkin 72611084d3 core: debug events (#4006) 2024-04-09 17:31:52 +01:00
Evgeny Poberezkin a5db36469d core: api to forward messages (#3968)
* wip

* wip

* test

* mute

* tests

* simplify (only bool flag)

* re-encrypt file

* tests

* more tests (wip)

* fix relative paths, refactor

* more tests

* more locks

* fix, tests

* more tests

* rework (revert from bool to ids)

* update schema

* more tests

* add to info

* ForwardedMsg container

* Revert "ForwardedMsg container"

This reverts commit bb57f12151.

* parser

* more tests

* rework api

* more locks

* test

* move

* remove from

* view

* prohibit editing

* item info view

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-04-09 16:02:59 +04:00
Evgeny Poberezkin f8e6a78a3b core: fix nix config (#4003)
* bump

* more compilers

* i686 32bit only 8.10.7

---------

Co-authored-by: Moritz Angermann <moritz.angermann@gmail.com>
2024-04-09 08:15:29 +01:00
spaced4ndy 5b0c8e0f76 core: improve some tests (#4000) 2024-04-08 18:16:29 +04:00
Evgeny Poberezkin 2968cd0460 rfc: protocol extensions for channels/super-peers (#3980) 2024-04-05 16:46:37 +01:00
Evgeny Poberezkin 8ffb4e500e Merge branch 'stable' 2024-04-05 15:30:09 +01:00
Stanislav Dmitrenko 6133993cfe android, desktop: fix build error related to JVM version (#3995) 2024-04-05 15:29:41 +01:00
Evgeny Poberezkin 0d7a32877f ci: build armv7a with 8.10.7 in the main branches (master, stable) (#3733)
Co-authored-by: Moritz Angermann <moritz.angermann@gmail.com>
2024-04-05 12:24:34 +01:00
Evgeny Poberezkin 18efc28d16 core: additional group preferences: prohibit SimpleX links, restrict some features to specific roles (#3964)
* core: additional group preferences: prohibit SimpleX links, restrict some features to specific roles

* add role to group preference items, tests
2024-04-04 20:41:56 +01:00
spaced4ndy 069395c2a0 core: entity locks (#3962)
* core: entity locks

* more locks

* update sha256map

* add delay

* clean up

* empty

* fix tests

* empty

* empty

* more delays

* empty

* comment delays

* Revert "comment delays"

This reverts commit 4245b545fb.

* Revert "Revert "comment delays""

This reverts commit f803386945.

* take lock in the beginning of processing loop

* empty

* empty

* remove lock

* rework file locks

* empty

* fix

* empty

* add connection locks

* empty

* fix test

* empty

* remove commented delays

* add to debug locks

* update

* refactor

* refactor

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-04 19:24:42 +01:00
Stanislav Dmitrenko a725d2efac desktop (windows): fix build (#3990) 2024-04-04 14:18:33 +01:00
Evgeny Poberezkin 5f9710e4bb blog: post 2024-04-04 13:17:20 +01:00
Evgeny Poberezkin ea6c5bfb0b 5.6.1: ios 206, android 193, desktop 36 2024-04-04 08:58:24 +01:00
Stanislav Dmitrenko 14c279d1e0 android: possibly, prevent TooManyRequests exception (#3989)
* android: possibly, prevent TooManyRequests exception

* new line
2024-04-03 15:15:44 +01:00
Stanislav Dmitrenko d2de81100d android: workaround of pager's bug (#3988) 2024-04-03 15:11:04 +01:00
Stanislav Dmitrenko 9b28ae6d9e desktop: remote connection host/port fix (#3987) 2024-04-03 11:58:32 +01:00
Evgeny Poberezkin ea862a8f34 core: 5.6.1.1 (simplexmq 5.6.2.1) 2024-04-03 11:43:42 +01:00
Evgeny Poberezkin 2bd1a82b7d core: revert "deps: switch to base64 via simplexmq (#3957)" (#3985)
* Revert "deps: switch to base64 via simplexmq (#3957)"

This reverts commit d65137882b.

* update simplexmq
2024-04-03 10:47:38 +01:00
Evgeny Poberezkin 97a37634ef ios: 5.6.1 build 205 2024-04-03 01:47:01 +01:00
Stanislav Dmitrenko 28fbc1cd84 desktop: correct height of a window (#3982) 2024-04-02 20:05:14 +01:00
Stanislav Dmitrenko b8ee2af5b7 deskop: show window icon (#3983)
* deskop: show window icon

* size
2024-04-02 18:57:23 +01:00
Stanislav Dmitrenko c234809894 ios: improvement of chat item context menu (#3981)
* ios: improvement of chat item context menu

* rename

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-02 17:00:24 +01:00
Evgeny Poberezkin e1a997877a rfc: groups based on super-peers (WIP) (#3917) 2024-04-01 15:04:13 +01:00
Evgeny Poberezkin 69218952c3 core: 5.6.1.0 2024-04-01 14:36:45 +01:00
Evgeny Poberezkin dc2591b0bf ios: build settings (#3978) 2024-04-01 13:55:26 +01:00
Evgeny Poberezkin 2272b77b53 ios: refactor notifications (#3976)
* ios: refactor notifications

* refactor
2024-04-01 13:52:28 +01:00
Evgeny Poberezkin d90e2f4436 core: remove mtl typeclasses to reduce overhead (#3975)
* core: remove mtl typeclasses to reduce overhead

* strict data, optimization

* update simplexmq, clean up

* un-unlift attachRevHTTP2Client

* remote

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
2024-04-01 13:34:45 +01:00
Evgeny Poberezkin 9b6ca23dcb ios: fix UI freezes on start and when exiting background (#3972)
* ios: fix UI freezes on start and when exiting background

* remove unused code

* comments
2024-03-29 22:58:36 +00:00
Evgeny Poberezkin 1410b4bd85 ios: prevent call to apiContactInfo after the app is suspended 2024-03-29 19:43:16 +00:00
Evgeny Poberezkin 42a35e8c72 ios: prevent call to listUsers after the app is suspended and database is closed 2024-03-29 18:58:55 +00:00
Evgeny Poberezkin 86fe28f1ed core: chat hooks allowing to extend or customize chat core (#3953)
* core: chat hooks allowing to extend or customize chat core

* update

* json

* custom response

* user in db queries
2024-03-29 18:30:17 +00:00
Evgeny Poberezkin 735359c279 android: strings for permissions 2024-03-29 18:28:54 +00:00
Stanislav Dmitrenko 3d20465662 android: more user-friendly way of granting permissions in call (#3945)
* android: more user-friendly way of granting permissions in call

* changes

* ask permissions without click at first

* changes in logic and UI

* enhancements

* show alert only when non in PiP

* change

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-03-29 18:19:36 +00:00
Stanislav Dmitrenko b64ae90850 ios: NotificationService refactoring (#3961)
* ios: NotificationService refactoring

* refactor

* removed unused code

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-03-28 17:28:10 +00:00
spaced4ndy 366b58ead5 core: don't save async commands records for ack (#3904) 2024-03-28 19:52:06 +04:00
spaced4ndy 32bcaad126 ui: add missing error types (#3925) 2024-03-28 17:49:09 +04:00
Alexander Bondarenko d65137882b deps: switch to base64 via simplexmq (#3957)
* deps: switch to base64 via simplexmq

* update error text

* update nix

* set simplexmq to master

* update paths
2024-03-28 13:09:04 +00:00
spaced4ndy 095d613f01 website: translations (#3960)
* Translated using Weblate (Polish)

Currently translated at 100.0% (254 of 254 strings)

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

* Translated using Weblate (Russian)

Currently translated at 100.0% (254 of 254 strings)

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

* Translated using Weblate (Hebrew)

Currently translated at 100.0% (254 of 254 strings)

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

* Translated using Weblate (Finnish)

Currently translated at 100.0% (254 of 254 strings)

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

* Translated using Weblate (Finnish)

Currently translated at 100.0% (254 of 254 strings)

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

---------

Co-authored-by: Jakub <fioletowy.temp@tutanota.com>
Co-authored-by: whoami11 <hvhdinik@gmail.com>
Co-authored-by: You and me <trash.93m78@slmail.me>
Co-authored-by: Jiri Grönroos <jiri.gronroos@iki.fi>
2024-03-28 12:30:31 +00:00
spaced4ndy f22beefbd4 ui: translations (#3959)
* Translated using Weblate (German)

Currently translated at 100.0% (1687 of 1687 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% (1687 of 1687 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% (1478 of 1478 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% (1478 of 1478 strings)

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

* Translated using Weblate (French)

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/fr/

* Translated using Weblate (French)

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/fr/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1687 of 1687 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% (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% (1687 of 1687 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 98.3% (1659 of 1687 strings)

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

* Translated using Weblate (Czech)

Currently translated at 98.2% (1657 of 1687 strings)

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

* Translated using Weblate (Arabic)

Currently translated at 95.9% (1619 of 1687 strings)

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

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (1687 of 1687 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% (1478 of 1478 strings)

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

* Translated using Weblate (Portuguese)

Currently translated at 0.3% (5 of 1478 strings)

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

* Translated using Weblate (Hebrew)

Currently translated at 100.0% (1687 of 1687 strings)

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

* Translated using Weblate (Hebrew)

Currently translated at 41.7% (617 of 1478 strings)

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

* Translated using Weblate (Turkish)

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/tr/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1687 of 1687 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 (Japanese)

Currently translated at 98.4% (1661 of 1687 strings)

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

* Translated using Weblate (Portuguese)

Currently translated at 45.2% (763 of 1687 strings)

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

* Translated using Weblate (Portuguese)

Currently translated at 3.7% (55 of 1478 strings)

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

* Translated using Weblate (Hebrew)

Currently translated at 100.0% (1687 of 1687 strings)

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

* Translated using Weblate (Japanese)

Currently translated at 99.4% (1678 of 1687 strings)

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

* Translated using Weblate (Portuguese)

Currently translated at 3.7% (56 of 1478 strings)

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

* Translated using Weblate (Dutch)

Currently translated at 100.0% (1687 of 1687 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% (1478 of 1478 strings)

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

* Translated using Weblate (Portuguese)

Currently translated at 7.3% (109 of 1478 strings)

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

* Translated using Weblate (Bulgarian)

Currently translated at 96.7% (1430 of 1478 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% (1687 of 1687 strings)

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

* Translated using Weblate (Vietnamese)

Currently translated at 5.6% (96 of 1687 strings)

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

* Translated using Weblate (Japanese)

Currently translated at 100.0% (1687 of 1687 strings)

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

* Translated using Weblate (German)

Currently translated at 100.0% (1688 of 1688 strings)

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

* Translated using Weblate (Dutch)

Currently translated at 100.0% (1688 of 1688 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 100.0% (1688 of 1688 strings)

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

* Translated using Weblate (Bulgarian)

Currently translated at 99.4% (1470 of 1478 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 99.9% (1687 of 1688 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% (1688 of 1688 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.0% (102 of 1688 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% (1688 of 1688 strings)

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

* android

* ios import

* ios export

---------

Co-authored-by: mlanp <github@lang.xyz>
Co-authored-by: subjection yogirt <subjectiongithub@yogirt.com>
Co-authored-by: whoami11 <hvhdinik@gmail.com>
Co-authored-by: khalidbelk <khalid.belkassmi-el-hafi@epitech.eu>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: Miyu Sakatsuki <miyu-sakatsuki@outlook.jp>
Co-authored-by: zenobit <zen@osowoso.xyz>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: You and me <trash.93m78@slmail.me>
Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
Co-authored-by: Andre <andrefeliciano@duck.com>
Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: elgratea <weblate@fastmail.com>
Co-authored-by: tuananh-ng <158744840+tuananh-ng@users.noreply.github.com>
2024-03-28 12:27:58 +00:00
spaced4ndy c9bf36291b ios: fix default (#3958) 2024-03-27 09:59:15 +00:00
spaced4ndy 42ebe8bc60 core: batch broadcast send db operations (#3956)
* core: batch broadcast send db operations

* refactor

* Update src/Simplex/Chat.hs

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
2024-03-27 11:49:14 +04:00
Stanislav Dmitrenko 96fb61ccfc desktop: show toast if failed to show notification (#3955)
* desktop: show toast if failed to show notification

* longer timeout

* returned old version of the lib
2024-03-26 12:43:45 +00:00
spaced4ndy 1d698c114f docs: communicate rejection rfc (#3946) 2024-03-26 11:29:28 +04:00
Stanislav Dmitrenko 154ce5843d android, desktop: update dependencies (#3954)
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-03-25 15:59:00 +00:00
Evgeny Poberezkin a5817d9feb core: fix cabal paths (#3952)
* fixed install cabal file paths.

* updated package.yaml

* fix

---------

Co-authored-by: Andor Kesselman <andor@magic-pro-3.local>
2024-03-25 08:24:46 +00:00
Evgeny Poberezkin 72082af139 docs: update version 2024-03-23 19:20:37 +00:00
Evgeny Poberezkin eb36704f59 Merge branch 'stable' 2024-03-23 18:42:53 +00:00
Evgeny Poberezkin 84fbff6148 blog: v5.6 release, non-profit governance (#3951)
* blog: v5.6 release

* images, preview

* corrections

* update image

* update readme
2024-03-23 18:42:18 +00:00
Evgeny Poberezkin dd75d30c08 blog: update PQ encryption post (#3948)
* blog: update pq encryption post

* correction
2024-03-23 18:29:05 +00:00
spaced4ndy 52803ae976 core: add delays to some failing tests (#3947) 2024-03-22 22:01:11 +04:00
spaced4ndy ec73a31cb5 directory: fix double logic inversion on roles check, incorrect error message (#3944) 2024-03-22 15:07:01 +04:00
Evgeny Poberezkin 6ad5048b58 5.6: ios 204, android 191, desktop 35 2024-03-21 22:30:30 +00:00
Stanislav Dmitrenko ca0ce4dfdd ios: making chat state .active when starting chat, correctly handle hidden users when deleting the last profile (#3936)
* ios: making chat state .active when starting chat

* changes

* refactor

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-03-21 19:36:05 +00:00
spaced4ndy d27502ec47 ios: restore reveal/hide animation (#3933)
* ios: restore reveal/hide animation

* remove comment

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-03-21 19:12:47 +00:00
spaced4ndy 655ad4d813 ui: translations (#3940)
* Translated using Weblate (Dutch)

Currently translated at 96.6% (1622 of 1679 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 96.2% (1624 of 1687 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 97.0% (1638 of 1687 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% (1687 of 1687 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 96.9% (1433 of 1478 strings)

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

* Translated using Weblate (Dutch)

Currently translated at 98.5% (1456 of 1478 strings)

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

* Translated using Weblate (Dutch)

Currently translated at 100.0% (1687 of 1687 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% (1478 of 1478 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 97.0% (1637 of 1687 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 96.6% (1631 of 1687 strings)

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

* Translated using Weblate (French)

Currently translated at 97.2% (1640 of 1687 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% (1687 of 1687 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 97.4% (1441 of 1478 strings)

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

* Translated using Weblate (Czech)

Currently translated at 95.2% (1607 of 1687 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 97.0% (1637 of 1687 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% (1687 of 1687 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 (French)

Currently translated at 100.0% (1687 of 1687 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% (1478 of 1478 strings)

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

* 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 (Czech)

Currently translated at 98.2% (1657 of 1687 strings)

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

* Translated using Weblate (Polish)

Currently translated at 99.9% (1686 of 1687 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 95.1% (1406 of 1478 strings)

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

* Translated using Weblate (Turkish)

Currently translated at 97.2% (1438 of 1478 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% (1687 of 1687 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 99.6% (1681 of 1687 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 99.6% (1681 of 1687 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% (1686 of 1687 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 (Hungarian)

Currently translated at 99.8% (1685 of 1687 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 99.8% (1476 of 1478 strings)

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

* Translated using Weblate (Arabic)

Currently translated at 95.8% (1617 of 1687 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% (1687 of 1687 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% (1687 of 1687 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% (1687 of 1687 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% (1478 of 1478 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% (1687 of 1687 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 95.9% (1619 of 1687 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 95.8% (1417 of 1478 strings)

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

* Translated using Weblate (Russian)

Currently translated at 100.0% (1687 of 1687 strings)

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

* Translated using Weblate (Russian)

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/ru/

* android

* import

* export

* ru corrections

* export/update ru

---------

Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
Co-authored-by: mlanp <github@lang.xyz>
Co-authored-by: Ophiushi <41908476+ishi-sama@users.noreply.github.com>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: zenobit <zen@osowoso.xyz>
Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: B.O.S.S <BxOxSxS@protonmail.com>
Co-authored-by: cervezagua <cervezagua@pm.me>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: No name <CertainBot@users.noreply.hosted.weblate.org>
Co-authored-by: khalidbelk <khalid.belkassmi-el-hafi@epitech.eu>
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-03-21 19:07:12 +00:00
Evgeny Poberezkin e0566599a4 core: 5.6.0.4 (#3939) 2024-03-21 19:00:19 +00:00
Evgeny Poberezkin 2546ad5351 Translated using Weblate (Czech) (#3941)
Currently translated at 99.2% (252 of 254 strings)

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

Co-authored-by: zenobit <zen@osowoso.xyz>
2024-03-21 17:37:56 +00:00
Stanislav Dmitrenko 0706e6e464 android: stopping chat after deleting the last user (#3937)
* android: stopping chat after deleting the last user

* fix for hidden users + onboarding
2024-03-21 14:02:38 +00:00
spaced4ndy c2adf40b5a ui: align finalize migration destructive/default button colors (#3938)
* ios: align finalize migration destructive/default button colors

* android
2024-03-21 14:00:27 +00:00
Stanislav Dmitrenko b220ea23e2 ios: stop chat after deleting the last profile (#3935) 2024-03-21 13:58:04 +00:00
spaced4ndy a16e3eebd1 ui: improve "allow PQ" button (#3932)
* ios: improve "allow PQ" button

* android
2024-03-21 09:13:18 +00:00
Stanislav Dmitrenko 6d54c21774 ios: chatitem height with menu workaround (#3930)
* ios: chatitem height with menu workaround

* turned on animation

* disabled animation in some places again
2024-03-20 17:49:53 +00:00
spaced4ndy d2e6313ce3 ui: swap finalize migration buttons (make destructive harder to reach, consistent order of primary and destructive with previous page) (#3931) 2024-03-20 20:20:10 +04:00
515 changed files with 41530 additions and 7363 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 }}
+9 -3
View File
@@ -5,14 +5,20 @@ on:
pull_request_target:
types: [opened, closed, synchronize]
permissions:
actions: write
contents: write
pull-requests: write
statuses: write
jobs:
CLAssistant:
runs-on: ubuntu-latest
steps:
- name: "CLA Assistant"
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request'
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
# Beta Release
uses: cla-assistant/github-action@v2.1.3-beta
uses: cla-assistant/github-action@v2.3.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# the below token should have repo scope and must be manually added by you in the repository's secret
@@ -33,4 +39,4 @@ jobs:
#custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA'
#custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.'
#lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true)
#use-dco-flag: true - If you are using DCO instead of CLA
#use-dco-flag: true - If you are using DCO instead of CLA
-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
+14 -7
View File
@@ -4,7 +4,7 @@
[![Join on Reddit](https://img.shields.io/reddit/subreddit-subscribers/SimpleXChat?style=social)](https://www.reddit.com/r/SimpleXChat)
<a rel="me" href="https://mastodon.social/@simplex">![Follow on Mastodon](https://img.shields.io/mastodon/follow/108619463746856738?domain=https%3A%2F%2Fmastodon.social&style=social)</a>
| 30/03/2023 | EN, [FR](/docs/lang/fr/README.md), [CZ](/docs/lang/cs/README.md) |
| 30/03/2023 | EN, [FR](/docs/lang/fr/README.md), [CZ](/docs/lang/cs/README.md), [PL](/docs/lang/pl/README.md) |
<img src="images/simplex-chat-logo.svg" alt="SimpleX logo" width="100%">
@@ -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,12 @@ 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)
[Jan 24, 2024. SimpleX Chat: free infrastructure from Linode, v5.5 released with private notes, group history and a simpler UX to connect.](./blog/20240124-simplex-chat-infrastructure-costs-v5-5-simplex-ux-private-notes-group-history.md)
@@ -377,10 +383,11 @@ Please also join [#simplex-devs](https://simplex.chat/contact#/?v=1-2&smp=smp%3A
- ✅ Using mobile profiles from the desktop app.
- ✅ 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.
- 🏗 Post-quantum resistant key exchange in double ratchet protocol.
- 🏗 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.
@@ -409,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
+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 {
+19
View File
@@ -103,6 +103,7 @@ final class ChatModel: ObservableObject {
// tracks keyboard height via subscription in AppDelegate
@Published var keyboardHeight: CGFloat = 0
@Published var pasteboardHasStrings: Bool = UIPasteboard.general.hasStrings
@Published var networkInfo = UserNetworkInfo(networkType: .other, online: true)
var messageDelivery: Dictionary<Int64, () -> Void> = [:]
@@ -778,6 +779,24 @@ final class Chat: ObservableObject, Identifiable {
var viewId: String { get { "\(chatInfo.id) \(created.timeIntervalSince1970)" } }
func groupFeatureEnabled(_ feature: GroupFeature) -> Bool {
if case let .group(groupInfo) = self.chatInfo {
let p = groupInfo.fullGroupPreferences
return switch feature {
case .timedMessages: p.timedMessages.on
case .directMessages: p.directMessages.on(for: groupInfo.membership)
case .fullDelete: p.fullDelete.on
case .reactions: p.reactions.on
case .voice: p.voice.on(for: groupInfo.membership)
case .files: p.files.on(for: groupInfo.membership)
case .simplexLinks: p.simplexLinks.on(for: groupInfo.membership)
case .history: p.history.on
}
} else {
return true
}
}
public static var sampleData: Chat = Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: [])
}
@@ -0,0 +1,73 @@
//
// NetworkObserver.swift
// SimpleX (iOS)
//
// Created by Avently on 05.04.2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import Foundation
import Network
import SimpleXChat
class NetworkObserver {
static let shared = NetworkObserver()
private let queue: DispatchQueue = DispatchQueue(label: "chat.simplex.app.NetworkObserver")
private var prevInfo: UserNetworkInfo? = nil
private var monitor: NWPathMonitor?
private let monitorLock: DispatchQueue = DispatchQueue(label: "chat.simplex.app.monitorLock")
func restartMonitor() {
monitorLock.sync {
monitor?.cancel()
let mon = NWPathMonitor()
mon.pathUpdateHandler = { [weak self] path in
self?.networkPathChanged(path: path)
}
mon.start(queue: queue)
monitor = mon
}
}
private func networkPathChanged(path: NWPath) {
let info = UserNetworkInfo(
networkType: networkTypeFromPath(path),
online: path.status == .satisfied
)
if (prevInfo != info) {
prevInfo = info
setNetworkInfo(info)
}
}
private func networkTypeFromPath(_ path: NWPath) -> UserNetworkType {
if path.usesInterfaceType(.wiredEthernet) {
.ethernet
} else if path.usesInterfaceType(.wifi) {
.wifi
} else if path.usesInterfaceType(.cellular) {
.cellular
} else if path.usesInterfaceType(.other) {
.other
} else {
.none
}
}
private static var networkObserver: NetworkObserver? = nil
private func setNetworkInfo(_ info: UserNetworkInfo) {
logger.debug("setNetworkInfo Network changed: \(String(describing: info))")
DispatchQueue.main.sync {
ChatModel.shared.networkInfo = info
}
if !hasChatCtrl() { return }
self.monitorLock.sync {
do {
try apiSetNetworkInfo(info)
} catch let err {
logger.error("setNetworkInfo error: \(responseError(err))")
}
}
}
}
+155 -84
View File
@@ -21,6 +21,8 @@ public let CURRENT_CHAT_VERSION: Int = 2
// version range that supports establishing direct connection with a group member (xGrpDirectInvVRange in core)
public let CREATE_MEMBER_CONTACT_VRANGE = VersionRange(minVersion: 2, maxVersion: CURRENT_CHAT_VERSION)
private let networkStatusesLock = DispatchQueue(label: "chat.simplex.app.network-statuses.lock")
enum TerminalItem: Identifiable {
case cmd(Date, ChatCommand)
case resp(Date, ChatResponse)
@@ -270,18 +272,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))
}
@@ -351,11 +341,20 @@ func apiGetChatItemInfo(type: ChatType, id: Int64, itemId: Int64) async throws -
throw r
}
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)
}
func apiSendMessage(type: ChatType, id: Int64, file: CryptoFile?, quotedItemId: Int64?, msg: MsgContent, live: Bool = false, ttl: Int? = nil) async -> ChatItem? {
let chatModel = ChatModel.shared
let cmd: ChatCommand = .apiSendMessage(type: type, id: id, file: file, quotedItemId: quotedItemId, msg: msg, live: live, ttl: ttl)
return await processSendMessageCmd(toChatType: type, cmd: cmd)
}
private func processSendMessageCmd(toChatType: ChatType, cmd: ChatCommand) async -> ChatItem? {
let chatModel = ChatModel.shared
let r: ChatResponse
if type == .direct {
if toChatType == .direct {
var cItem: ChatItem? = nil
let endTask = beginBGTask({
if let cItem = cItem {
@@ -395,7 +394,7 @@ func apiCreateChatItem(noteFolderId: Int64, file: CryptoFile?, msg: MsgContent)
}
private func sendMessageErrorAlert(_ r: ChatResponse) {
logger.error("apiSendMessage error: \(String(describing: r))")
logger.error("send message error: \(String(describing: r))")
AlertManager.shared.showAlertMsg(
title: "Error sending message",
message: "Error: \(String(describing: r))"
@@ -532,6 +531,12 @@ func setNetworkConfig(_ cfg: NetCfg, ctrl: chat_ctrl? = nil) throws {
throw r
}
func apiSetNetworkInfo(_ networkInfo: UserNetworkInfo) throws {
let r = chatSendCmdSync(.apiSetNetworkInfo(networkInfo: networkInfo))
if case .cmdOk = r { return }
throw r
}
func reconnectAllServers() async throws {
try await sendCommandOkResp(.reconnectAllServers)
}
@@ -556,6 +561,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 }
@@ -922,14 +939,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 {
@@ -942,19 +964,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
@@ -1224,7 +1277,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() }
}
@@ -1295,6 +1348,7 @@ func initializeChat(start: Bool, confirmStart: Bool = false, dbKey: String? = ni
defer { m.ctrlInitInProgress = false }
(m.chatDbEncrypted, m.chatDbStatus) = chatMigrateInit(dbKey, confirmMigrations: confirmMigrations)
if m.chatDbStatus != .ok { return }
NetworkObserver.shared.restartMonitor()
// If we migrated successfully means previous re-encryption process on database level finished successfully too
if encryptionStartedDefault.get() {
encryptionStartedDefault.set(false)
@@ -1302,7 +1356,6 @@ func initializeChat(start: Bool, confirmStart: Bool = false, dbKey: String? = ni
try apiSetTempFolder(tempFolder: getTempFilesDirectory().path)
try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path)
try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get())
try apiSetPQEncryption(pqExperimentalEnabledDefault.get())
m.chatInitialized = true
m.currentUser = try apiGetActiveUser()
if m.currentUser == nil {
@@ -1460,6 +1513,8 @@ class ChatReceiver {
private var receiveMessages = true
private var _lastMsgTime = Date.now
var messagesChannel: ((ChatResponse) -> Void)? = nil
static let shared = ChatReceiver()
var lastMsgTime: Date { get { _lastMsgTime } }
@@ -1477,6 +1532,9 @@ class ChatReceiver {
if let msg = await chatRecvMsg() {
self._lastMsgTime = .now
await processReceivedMsg(msg)
if let messagesChannel {
messagesChannel(msg)
}
}
_ = try? await Task.sleep(nanoseconds: 7_500_000)
}
@@ -1566,34 +1624,30 @@ func processReceivedMsg(_ res: ChatResponse) async {
m.removeChat(mergedContact.id)
}
}
case let .contactsSubscribed(_, contactRefs):
await updateContactsStatus(contactRefs, status: .connected)
case let .contactsDisconnected(_, contactRefs):
await updateContactsStatus(contactRefs, status: .disconnected)
case let .contactSubSummary(_, contactSubscriptions):
await MainActor.run {
for sub in contactSubscriptions {
// no need to update contact here, and it is slow
// if active(user) {
// m.updateContact(sub.contact)
// }
if let err = sub.contactError {
processContactSubError(sub.contact, err)
} else {
m.setContactNetworkStatus(sub.contact, .connected)
}
}
}
case let .networkStatus(status, connections):
await MainActor.run {
// dispatch queue to synchronize access
networkStatusesLock.sync {
var ns = m.networkStatuses
// slow loop is on the background thread
for cId in connections {
m.networkStatuses[cId] = status
ns[cId] = status
}
// fast model update is on the main thread
DispatchQueue.main.sync {
m.networkStatuses = ns
}
}
case let .networkStatuses(_, statuses): ()
await MainActor.run {
// dispatch queue to synchronize access
networkStatusesLock.sync {
var ns = m.networkStatuses
// slow loop is on the background thread
for s in statuses {
m.networkStatuses[s.agentConnId] = s.networkStatus
ns[s.agentConnId] = s.networkStatus
}
// fast model update is on the main thread
DispatchQueue.main.sync {
m.networkStatuses = ns
}
}
case let .newChatItem(user, aChatItem):
@@ -1772,11 +1826,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, _):
@@ -1793,12 +1851,15 @@ func processReceivedMsg(_ res: ChatResponse) async {
}
case let .sndFileCompleteXFTP(user, aChatItem, _):
await chatItemSimpleUpdate(user, aChatItem)
Task { cleanupFile(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
@@ -1844,21 +1905,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 {
@@ -1882,12 +1957,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) {
@@ -1944,26 +2035,6 @@ func chatItemSimpleUpdate(_ user: any UserLike, _ aChatItem: AChatItem) async {
}
}
func updateContactsStatus(_ contactRefs: [ContactRef], status: NetworkStatus) async {
let m = ChatModel.shared
await MainActor.run {
for c in contactRefs {
m.networkStatuses[c.agentConnId] = status
}
}
}
func processContactSubError(_ contact: Contact, _ chatError: ChatError) {
let m = ChatModel.shared
var err: String
switch chatError {
case .errorAgent(agentError: .BROKER(_, .NETWORK)): err = "network"
case .errorAgent(agentError: .SMP(smpErr: .AUTH)): err = "contact deleted"
default: err = String(describing: chatError)
}
m.setContactNetworkStatus(contact, .error(connectionError: err))
}
func refreshCallInvitations() throws {
let m = ChatModel.shared
let callInvitations = try justRefreshCallInvitations()
@@ -9,6 +9,7 @@
import SwiftUI
import WebKit
import SimpleXChat
import AVFoundation
struct ActiveCallView: View {
@EnvironmentObject var m: ChatModel
@@ -21,6 +22,7 @@ struct ActiveCallView: View {
@Binding var canConnectCall: Bool
@State var prevColorScheme: ColorScheme = .dark
@State var pipShown = false
@State var wasConnected = false
var body: some View {
ZStack(alignment: .topLeading) {
@@ -69,6 +71,11 @@ struct ActiveCallView: View {
Task { await m.callCommand.setClient(nil) }
AppDelegate.keepScreenOn(false)
client?.endCall()
CallSoundsPlayer.shared.stop()
try? AVAudioSession.sharedInstance().setCategory(.soloAmbient)
if (wasConnected) {
CallSoundsPlayer.shared.vibrate(long: true)
}
}
.background(m.activeCallViewIsCollapsed ? .clear : .black)
// Quite a big delay when opening/closing the view when a scheme changes (globally) this way. It's not needed when CallKit is used since status bar is green with white text on it
@@ -103,6 +110,11 @@ struct ActiveCallView: View {
call.callState = .invitationSent
call.localCapabilities = capabilities
}
if call.supportsVideo && !AVAudioSession.sharedInstance().hasExternalAudioDevice() {
try? AVAudioSession.sharedInstance().setCategory(.playback, options: [.allowBluetooth, .allowAirPlay, .allowBluetoothA2DP])
}
CallSoundsPlayer.shared.startConnectingCallSound()
activeCallWaitDeliveryReceipt()
}
case let .offer(offer, iceCandidates, capabilities):
Task {
@@ -126,6 +138,7 @@ struct ActiveCallView: View {
}
await MainActor.run {
call.callState = .negotiated
CallSoundsPlayer.shared.stop()
}
}
case let .ice(iceCandidates):
@@ -144,6 +157,10 @@ struct ActiveCallView: View {
: CallController.shared.reportIncomingCall(call: call, connectedAt: nil)
call.callState = .connected
call.connectedAt = .now
if !wasConnected {
CallSoundsPlayer.shared.vibrate(long: false)
wasConnected = true
}
}
if state.connectionState == "closed" {
closeCallView(client)
@@ -161,6 +178,10 @@ struct ActiveCallView: View {
call.callState = .connected
call.connectionInfo = connectionInfo
call.connectedAt = .now
if !wasConnected {
CallSoundsPlayer.shared.vibrate(long: false)
wasConnected = true
}
case .ended:
closeCallView(client)
call.callState = .ended
@@ -187,6 +208,22 @@ struct ActiveCallView: View {
}
}
private func activeCallWaitDeliveryReceipt() {
ChatReceiver.shared.messagesChannel = { msg in
guard let call = ChatModel.shared.activeCall, call.callState == .invitationSent else {
ChatReceiver.shared.messagesChannel = nil
return
}
if case let .chatItemStatusUpdated(_, msg) = msg,
msg.chatInfo.id == call.contact.id,
case .sndCall = msg.chatItem.content,
case .sndRcvd = msg.chatItem.meta.itemStatus {
CallSoundsPlayer.shared.startInCallSound()
ChatReceiver.shared.messagesChannel = nil
}
}
}
private func closeCallView(_ client: WebRTCClient) {
if m.activeCall != nil {
m.showCallView = false
@@ -198,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 {
@@ -213,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()
@@ -239,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)
@@ -256,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 {
@@ -341,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 Stanislav 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")
@@ -30,8 +30,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)
@@ -8,6 +8,7 @@
import Foundation
import AVFoundation
import UIKit
class SoundPlayer {
static let shared = SoundPlayer()
@@ -43,3 +44,63 @@ class SoundPlayer {
audioPlayer = nil
}
}
class CallSoundsPlayer {
static let shared = CallSoundsPlayer()
private var audioPlayer: AVAudioPlayer?
private var playerTask: Task = Task {}
private func start(_ soundName: String, delayMs: Double) {
audioPlayer?.stop()
playerTask.cancel()
logger.debug("start \(soundName)")
guard let path = Bundle.main.path(forResource: soundName, ofType: "mp3", inDirectory: "sounds") else {
logger.debug("start: file not found")
return
}
do {
let player = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
if player.prepareToPlay() {
audioPlayer = player
}
} catch {
logger.debug("start: AVAudioPlayer error \(error.localizedDescription)")
}
playerTask = Task {
while let player = audioPlayer {
player.play()
do {
try await Task.sleep(nanoseconds: UInt64((player.duration * 1_000_000_000) + delayMs * 1_000_000))
} catch {
break
}
}
}
}
func startConnectingCallSound() {
start("connecting_call", delayMs: 0)
}
func startInCallSound() {
// Taken from https://github.com/TelegramOrg/Telegram-Android
// https://github.com/TelegramOrg/Telegram-Android/blob/master/LICENSE
start("in_call", delayMs: 1000)
}
func stop() {
playerTask.cancel()
audioPlayer?.stop()
audioPlayer = nil
}
func vibrate(long: Bool) {
// iOS just don't want to vibrate more than once after a short period of time, and all 'styles' feel the same
if long {
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate)
} else {
UIImpactFeedbackGenerator(style: .heavy).impactOccurred()
}
}
}
+4 -3
View File
@@ -431,17 +431,18 @@ struct RTCIceServer: Codable, Equatable {
}
// the servers are expected in this format:
// stun:stun.simplex.im:443?transport=tcp
// turn:private:yleob6AVkiNI87hpR94Z@turn.simplex.im:443?transport=tcp
// stuns:stun.simplex.im:443?transport=tcp
// turns:private2:Hxuq2QxUjnhj96Zq2r4HjqHRj@turn.simplex.im:443?transport=tcp
func parseRTCIceServer(_ str: String) -> RTCIceServer? {
var s = replaceScheme(str, "stun:")
s = replaceScheme(s, "stuns:")
s = replaceScheme(s, "turn:")
s = replaceScheme(s, "turns:")
if let u: URL = URL(string: s),
let scheme = u.scheme,
let host = u.host,
let port = u.port,
u.path == "" && (scheme == "stun" || scheme == "turn" || scheme == "turns") {
u.path == "" && (scheme == "stun" || scheme == "stuns" || scheme == "turn" || scheme == "turns") {
let query = u.query == nil || u.query == "" ? "" : "?" + (u.query ?? "")
return RTCIceServer(
urls: ["\(scheme):\(host):\(port)\(query)"],
+33 -8
View File
@@ -49,7 +49,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
}
private let rtcAudioSession = RTCAudioSession.sharedInstance()
private let audioQueue = DispatchQueue(label: "audio")
private let audioQueue = DispatchQueue(label: "chat.simplex.app.audio")
private var sendCallResponse: (WVAPIMessage) async -> Void
var activeCall: Binding<Call?>
private var localRendererAspectRatio: Binding<CGFloat?>
@@ -65,14 +65,14 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
self.localRendererAspectRatio = localRendererAspectRatio
rtcAudioSession.useManualAudio = CallController.useCallKit()
rtcAudioSession.isAudioEnabled = !CallController.useCallKit()
logger.debug("WebRTCClient: rtcAudioSession has manual audio \(self.rtcAudioSession.useManualAudio) and audio enabled \(self.rtcAudioSession.isAudioEnabled)}")
logger.debug("WebRTCClient: rtcAudioSession has manual audio \(self.rtcAudioSession.useManualAudio) and audio enabled \(self.rtcAudioSession.isAudioEnabled)")
super.init()
}
let defaultIceServers: [WebRTC.RTCIceServer] = [
WebRTC.RTCIceServer(urlStrings: ["stun:stun.simplex.im:443"]),
WebRTC.RTCIceServer(urlStrings: ["turn:turn.simplex.im:443?transport=udp"], username: "private", credential: "yleob6AVkiNI87hpR94Z"),
WebRTC.RTCIceServer(urlStrings: ["turn:turn.simplex.im:443?transport=tcp"], username: "private", credential: "yleob6AVkiNI87hpR94Z"),
WebRTC.RTCIceServer(urlStrings: ["stuns:stun.simplex.im:443"]),
//WebRTC.RTCIceServer(urlStrings: ["turns:turn.simplex.im:443?transport=udp"], username: "private2", credential: "Hxuq2QxUjnhj96Zq2r4HjqHRj"),
WebRTC.RTCIceServer(urlStrings: ["turns:turn.simplex.im:443?transport=tcp"], username: "private2", credential: "Hxuq2QxUjnhj96Zq2r4HjqHRj"),
]
func initializeCall(_ iceServers: [WebRTC.RTCIceServer]?, _ mediaType: CallMediaType, _ aesKey: String?, _ relay: Bool?) -> Call {
@@ -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)
+34 -55
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 {
@@ -103,7 +103,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 +110,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 +120,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 +167,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,6 +226,18 @@ 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) }
}
}
}
}
}
}
@@ -256,7 +257,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 +286,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 {
@@ -430,15 +430,6 @@ 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")
@@ -572,34 +563,6 @@ struct ChatInfoView: View {
}
}
}
private func allowContactPQEncryption() {
Task {
do {
let ct = try await apiSetContactPQ(contact.apiId, true)
contact = ct
await MainActor.run {
chatModel.updateContact(contact)
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 +592,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(
@@ -35,9 +35,7 @@ struct CICallItemView: View {
case .error: missedCallIcon(sent).foregroundColor(.orange)
}
chatItem.timestampText
.font(.caption)
.foregroundColor(.secondary)
CIMetaView(chat: chat, chatItem: chatItem, showStatus: false, showEdited: false)
.padding(.bottom, 8)
.padding(.horizontal, 12)
}
@@ -11,6 +11,7 @@ import SimpleXChat
struct CIChatFeatureView: View {
@EnvironmentObject var m: ChatModel
@ObservedObject var chat: Chat
var chatItem: ChatItem
@Binding var revealed: Bool
var feature: Feature
@@ -18,7 +19,7 @@ struct CIChatFeatureView: View {
var iconColor: Color
var body: some View {
if !revealed, let fs = mergedFeautures() {
if !revealed, let fs = mergedFeatures() {
HStack {
ForEach(fs, content: featureIconView)
}
@@ -47,7 +48,7 @@ struct CIChatFeatureView: View {
}
}
private func mergedFeautures() -> [FeatureInfo]? {
private func mergedFeatures() -> [FeatureInfo]? {
var fs: [FeatureInfo] = []
var icons: Set<String> = []
if var i = m.getChatItemIndex(chatItem) {
@@ -67,8 +68,8 @@ struct CIChatFeatureView: View {
switch ci.content {
case let .rcvChatFeature(feature, enabled, param): FeatureInfo(feature, enabled.iconColor, param)
case let .sndChatFeature(feature, enabled, param): FeatureInfo(feature, enabled.iconColor, param)
case let .rcvGroupFeature(feature, preference, param): FeatureInfo(feature, preference.enable.iconColor, param)
case let .sndGroupFeature(feature, preference, param): FeatureInfo(feature, preference.enable.iconColor, param)
case let .rcvGroupFeature(feature, preference, param, role): FeatureInfo(feature, preference.enabled(role, for: chat.chatInfo.groupInfo?.membership).iconColor, param)
case let .sndGroupFeature(feature, preference, param, role): FeatureInfo(feature, preference.enabled(role, for: chat.chatInfo.groupInfo?.membership).iconColor, param)
default: nil
}
}
@@ -103,6 +104,6 @@ struct CIChatFeatureView: View {
struct CIChatFeatureView_Previews: PreviewProvider {
static var previews: some View {
let enabled = FeatureEnabled(forUser: false, forContact: false)
CIChatFeatureView(chatItem: ChatItem.getChatFeatureSample(.fullDelete, enabled), revealed: Binding.constant(true), feature: ChatFeature.fullDelete, iconColor: enabled.iconColor)
CIChatFeatureView(chat: Chat.sampleData, chatItem: ChatItem.getChatFeatureSample(.fullDelete, enabled), revealed: Binding.constant(true), feature: ChatFeature.fullDelete, iconColor: enabled.iconColor)
}
}
@@ -17,6 +17,7 @@ struct CIEventView: View {
.padding(.horizontal, 6)
.padding(.vertical, 4)
.textSelection(.disabled)
.lineLimit(4)
}
}
@@ -54,36 +54,32 @@ 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
}
}
return false
}
private func fileSizeValid() -> Bool {
if let file = file {
return file.fileSize <= getMaxFileSize(file.fileProtocol)
}
return false
}
private func fileAction() {
logger.debug("CIFileView fileAction")
if let file = file {
switch (file.fileStatus) {
case .rcvInvitation:
if fileSizeValid() {
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)
}
@@ -114,11 +110,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
}
}
@@ -142,8 +167,9 @@ 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() {
if fileSizeValid(file) {
fileIcon("arrow.down.doc.fill", color: .accentColor)
} else {
fileIcon("doc.fill", color: .orange, innerIcon: "exclamationmark", innerIconSize: 12)
@@ -155,9 +181,12 @@ struct CIFileView: View {
} else {
progressView()
}
case .rcvAborted:
fileIcon("doc.fill", color: .accentColor, 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 {
@@ -201,6 +230,13 @@ struct CIFileView: View {
}
}
func fileSizeValid(_ file: CIFile?) -> Bool {
if let file = file {
return file.fileSize <= getMaxFileSize(file.fileProtocol)
}
return false
}
func saveCryptoFile(_ fileSource: CryptoFile) {
if let cfArgs = fileSource.cryptoArgs {
let url = getAppFilePath(fileSource.filePath)
@@ -12,6 +12,7 @@ import SimpleXChat
struct CIGroupInvitationView: View {
@EnvironmentObject var chatModel: ChatModel
@Environment(\.colorScheme) var colorScheme
@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 : .accentColor)
.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,9 +65,7 @@ struct CIGroupInvitationView: View {
}
}
chatItem.timestampText
.font(.caption)
.foregroundColor(.secondary)
CIMetaView(chat: chat, chatItem: chatItem, showStatus: false, showEdited: false)
}
.padding(.horizontal, 12)
.padding(.vertical, 6)
@@ -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: ()
}
}
@@ -70,14 +89,14 @@ struct CIImageView: View {
}
private func imageView(_ img: UIImage) -> some View {
let w = img.size.width <= img.size.height ? maxWidth * 0.75 : img.imageData == nil ? .infinity : maxWidth
let w = img.size.width <= img.size.height ? maxWidth * 0.75 : maxWidth
DispatchQueue.main.async { imgWidth = w }
return ZStack(alignment: .topTrailing) {
if img.imageData == nil {
Image(uiImage: img)
.resizable()
.scaledToFit()
.frame(maxWidth: w)
.frame(width: w)
} else {
SwiftyGif(image: img)
.frame(width: w, height: w * img.size.height / img.size.width)
@@ -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)
}
}
@@ -14,6 +14,10 @@ struct CIMetaView: View {
var chatItem: ChatItem
var metaColor = Color.secondary
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 +29,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 +58,19 @@ 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,
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 +81,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) {
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(" ")
@@ -19,6 +19,8 @@ struct CIRcvDecryptionError: View {
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
@@ -119,7 +121,7 @@ struct CIRcvDecryptionError: View {
.foregroundColor(syncSupported ? .accentColor : .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)
@@ -140,7 +142,7 @@ 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)
@@ -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) {
@@ -126,7 +134,7 @@ struct CIVideoView: View {
if !decryptionInProgress {
Button {
decrypt(file: file) {
if let decrypted = urlDecrypted {
if urlDecrypted != nil {
videoPlaying = true
player?.play()
}
@@ -184,7 +192,7 @@ struct CIVideoView: View {
.disabled(!canBePlayed)
}
}
loadingIndicator()
fileStatusIcon()
}
.onAppear {
addObserver(player, url)
@@ -243,18 +251,18 @@ struct CIVideoView: View {
}
private func imageView(_ img: UIImage) -> some View {
let w = img.size.width <= img.size.height ? maxWidth * 0.75 : .infinity
let w = img.size.width <= img.size.height ? maxWidth * 0.75 : maxWidth
DispatchQueue.main.async { videoWidth = w }
return ZStack(alignment: .topTrailing) {
Image(uiImage: img)
.resizable()
.scaledToFit()
.frame(maxWidth: w)
loadingIndicator()
.frame(width: w)
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)
}
}
}
@@ -134,17 +134,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 {
@@ -217,7 +253,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,7 +261,7 @@ struct VoiceMessagePlayer: View {
}
}
} label: {
playPauseIcon("play.fill")
playPauseIcon(icon)
}
}
@@ -245,6 +281,17 @@ 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, colorScheme) : .clear)
.clipShape(Circle())
}
private func loadingIcon() -> some View {
ProgressView()
.frame(width: 30, height: 30)
@@ -65,6 +65,8 @@ struct FramedItemView: View {
}
}
}
} else if let itemForwarded = chatItem.meta.itemForwarded {
framedItemHeader(icon: "arrowshape.turn.up.forward", caption: Text(itemForwarded.text(chat.chatInfo.chatType)).italic(), pad: true)
}
ChatItemContentView(chat: chat, chatItem: chatItem, revealed: $revealed, msgContentView: framedMsgContentView)
@@ -85,12 +87,17 @@ struct FramedItemView: View {
.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
}
}
@@ -108,7 +115,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
@@ -155,15 +162,8 @@ 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) -> some View {
@ViewBuilder func framedItemHeader(icon: String? = nil, caption: Text, pad: Bool = false) -> some View {
let v = HStack(spacing: 6) {
if let icon = icon {
Image(systemName: icon)
@@ -178,7 +178,7 @@ struct FramedItemView: View {
.foregroundColor(.secondary)
.padding(.horizontal, 12)
.padding(.top, 6)
.padding(.bottom, chatItem.quotedItem == nil ? 6 : 0) // TODO think how to regroup
.padding(.bottom, pad || (chatItem.quotedItem == nil && chatItem.meta.itemForwarded == nil) ? 6 : 0)
.overlay(DetermineWidth())
.frame(minWidth: msgWidth, alignment: .leading)
.background(chatItemFrameContextColor(chatItem, colorScheme))
@@ -246,7 +246,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(.secondary)
.lineLimit(1)
ciQuotedMsgTextView(qi, lines: 2)
}
} else {
@@ -353,9 +356,9 @@ private struct MetaColorPreferenceKey: PreferenceKey {
func onlyImageOrVideo(_ ci: ChatItem) -> Bool {
if case let .image(text, _) = ci.content.msgContent {
return ci.meta.itemDeleted == nil && !ci.meta.isLive && ci.quotedItem == nil && text == ""
return ci.meta.itemDeleted == nil && !ci.meta.isLive && ci.quotedItem == nil && ci.meta.itemForwarded == nil && text == ""
} else if case let .video(text, _, _) = ci.content.msgContent {
return ci.meta.itemDeleted == nil && !ci.meta.isLive && ci.quotedItem == nil && text == ""
return ci.meta.itemDeleted == nil && !ci.meta.isLive && ci.quotedItem == nil && ci.meta.itemForwarded == nil && text == ""
}
return false
}
@@ -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)
}
}
@@ -0,0 +1,185 @@
//
// ChatItemForwardingView.swift
// SimpleX (iOS)
//
// Created by spaced4ndy on 12.04.2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import SwiftUI
import SimpleXChat
struct ChatItemForwardingView: View {
@EnvironmentObject var chatModel: ChatModel
@Environment(\.dismiss) var dismiss
var ci: ChatItem
var fromChatInfo: ChatInfo
@Binding var composeState: ComposeState
@State private var searchText: String = ""
@FocusState private var searchFocused
@State private var alert: SomeAlert?
@State private var hasSimplexLink_: Bool?
private let chatsToForwardTo = filterChatsToForwardTo()
var body: some View {
NavigationView {
forwardListView()
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Cancel") {
dismiss()
}
}
ToolbarItem(placement: .principal) {
Text("Forward")
.bold()
}
}
}
.alert(item: $alert) { $0.alert }
}
@ViewBuilder private func forwardListView() -> some View {
VStack(alignment: .leading) {
if !chatsToForwardTo.isEmpty {
List {
searchFieldView(text: $searchText, focussed: $searchFocused)
.padding(.leading, 2)
let s = searchText.trimmingCharacters(in: .whitespaces).localizedLowercase
let chats = s == "" ? chatsToForwardTo : chatsToForwardTo.filter { foundChat($0, s) }
ForEach(chats) { chat in
forwardListChatView(chat)
.disabled(chatModel.deletedChats.contains(chat.chatInfo.id))
}
}
} else {
emptyList()
}
}
}
private func foundChat(_ chat: Chat, _ searchStr: String) -> Bool {
let cInfo = chat.chatInfo
return switch cInfo {
case let .direct(contact):
viewNameContains(cInfo, searchStr) ||
contact.profile.displayName.localizedLowercase.contains(searchStr) ||
contact.fullName.localizedLowercase.contains(searchStr)
default:
viewNameContains(cInfo, searchStr)
}
func viewNameContains(_ cInfo: ChatInfo, _ s: String) -> Bool {
cInfo.chatViewName.localizedLowercase.contains(s)
}
}
private func prohibitedByPref(_ chat: Chat) -> Bool {
// preference checks should match checks in compose view
let simplexLinkProhibited = hasSimplexLink && !chat.groupFeatureEnabled(.simplexLinks)
let fileProhibited = (ci.content.msgContent?.isMediaOrFileAttachment ?? false) && !chat.groupFeatureEnabled(.files)
let voiceProhibited = (ci.content.msgContent?.isVoice ?? false) && !chat.chatInfo.featureEnabled(.voice)
return switch chat.chatInfo {
case .direct: voiceProhibited
case .group: simplexLinkProhibited || fileProhibited || voiceProhibited
case .local: false
case .contactRequest: false
case .contactConnection: false
case .invalidJSON: false
}
}
private var hasSimplexLink: Bool {
if let hasSimplexLink_ { return hasSimplexLink_ }
let r =
if let mcText = ci.content.msgContent?.text,
let parsedMsg = parseSimpleXMarkdown(mcText) {
parsedMsgHasSimplexLink(parsedMsg)
} else {
false
}
hasSimplexLink_ = r
return r
}
private func emptyList() -> some View {
Text("No filtered chats")
.foregroundColor(.secondary)
.frame(maxWidth: .infinity)
}
@ViewBuilder private func forwardListChatView(_ chat: Chat) -> some View {
let prohibited = prohibitedByPref(chat)
Button {
if prohibited {
alert = SomeAlert(
alert: mkAlert(
title: "Cannot forward message",
message: "Selected chat preferences prohibit this message."
),
id: "forward prohibited by preferences"
)
} else {
dismiss()
if chat.id == fromChatInfo.id {
composeState = ComposeState(
message: composeState.message,
preview: composeState.linkPreview != nil ? composeState.preview : .noPreview,
contextItem: .forwardingItem(chatItem: ci, fromChatInfo: fromChatInfo)
)
} else {
composeState = ComposeState.init(forwardingItem: ci, fromChatInfo: fromChatInfo)
chatModel.chatId = chat.id
}
}
} label: {
HStack {
ChatInfoImage(chat: chat, size: 30)
.padding(.trailing, 2)
Text(chat.chatInfo.chatViewName)
.foregroundColor(prohibited ? .secondary : .primary)
.lineLimit(1)
if chat.chatInfo.incognito {
Spacer()
Image(systemName: "theatermasks")
.resizable()
.scaledToFit()
.frame(width: 22, height: 22)
.foregroundColor(.secondary)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}
}
private func filterChatsToForwardTo() -> [Chat] {
var filteredChats = ChatModel.shared.chats.filter { c in
c.chatInfo.chatType != .local && canForwardToChat(c)
}
if let privateNotes = ChatModel.shared.chats.first(where: { $0.chatInfo.chatType == .local }) {
filteredChats.insert(privateNotes, at: 0)
}
return filteredChats
}
private func canForwardToChat(_ chat: Chat) -> Bool {
switch chat.chatInfo {
case let .direct(contact): contact.sendMsgEnabled && !contact.nextSendGrpInv
case let .group(groupInfo): groupInfo.sendMsgEnabled
case let .local(noteFolder): noteFolder.sendMsgEnabled
case .contactRequest: false
case .contactConnection: false
case .invalidJSON: false
}
}
#Preview {
ChatItemForwardingView(
ci: ChatItem.getSample(1, .directSnd, .now, "hello"),
fromChatInfo: .direct(contact: Contact.sampleData),
composeState: Binding.constant(ComposeState(message: "hello"))
)
}
@@ -11,16 +11,20 @@ import SimpleXChat
struct ChatItemInfoView: View {
@EnvironmentObject var chatModel: ChatModel
@Environment(\.dismiss) var dismiss
@Environment(\.colorScheme) var colorScheme
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 {
case history
case quote
case forwarded
case delivery
}
@@ -68,9 +72,20 @@ struct ChatItemInfoView: View {
if ci.quotedItem != nil {
numTabs += 1
}
if chatItemInfo?.forwardedFromChatItem != nil {
numTabs += 1
}
return numTabs
}
private var local: Bool {
switch ci.chatDir {
case .localSnd: true
case .localRcv: true
default: false
}
}
@ViewBuilder private func itemInfoView() -> some View {
if numTabs > 1 {
TabView(selection: $selection) {
@@ -93,6 +108,13 @@ struct ChatItemInfoView: View {
}
.tag(CIInfoTab.quote)
}
if let forwardedFromItem = chatItemInfo?.forwardedFromChatItem {
forwardedFromTab(forwardedFromItem)
.tabItem {
Label(local ? "Saved" : "Forwarded", systemImage: "arrowshape.turn.up.forward")
}
.tag(CIInfoTab.forwarded)
}
}
.onAppear {
if chatItemInfo?.memberDeliveryStatuses != nil {
@@ -137,6 +159,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()
}
}
}
}
}
}
@@ -275,6 +326,74 @@ struct ChatItemInfoView: View {
: Color(uiColor: .tertiarySystemGroupedBackground)
}
@ViewBuilder private func forwardedFromTab(_ forwardedFromItem: AChatItem) -> some View {
ScrollView {
VStack(alignment: .leading, spacing: 16) {
details()
Divider().padding(.vertical)
Text(local ? "Saved from" : "Forwarded from")
.font(.title2)
.padding(.bottom, 4)
forwardedFromView(forwardedFromItem)
}
.padding()
}
.frame(maxHeight: .infinity, alignment: .top)
}
private func forwardedFromView(_ forwardedFromItem: AChatItem) -> some View {
VStack(alignment: .leading, spacing: 8) {
Button {
Task {
await MainActor.run {
chatModel.chatId = forwardedFromItem.chatInfo.id
dismiss()
}
}
} label: {
forwardedFromSender(forwardedFromItem)
}
if !local {
Divider().padding(.top, 32)
Text("Recipient(s) can't see who this message is from.")
.font(.caption)
.foregroundColor(.secondary)
}
}
}
@ViewBuilder private func forwardedFromSender(_ forwardedFromItem: AChatItem) -> some View {
HStack {
ChatInfoImage(chat: Chat(chatInfo: forwardedFromItem.chatInfo), size: 48)
.padding(.trailing, 6)
if forwardedFromItem.chatItem.chatDir.sent {
VStack(alignment: .leading) {
Text("you")
.italic()
.foregroundColor(.primary)
Text(forwardedFromItem.chatInfo.chatViewName)
.foregroundColor(.secondary)
.lineLimit(1)
}
} else if case let .groupRcv(groupMember) = forwardedFromItem.chatItem.chatDir {
VStack(alignment: .leading) {
Text(groupMember.chatViewName)
.foregroundColor(.primary)
.lineLimit(1)
Text(forwardedFromItem.chatInfo.chatViewName)
.foregroundColor(.secondary)
.lineLimit(1)
}
} else {
Text(forwardedFromItem.chatInfo.chatViewName)
.foregroundColor(.primary)
.lineLimit(1)
}
}
}
@ViewBuilder private func deliveryTab(_ memberDeliveryStatuses: [MemberDeliveryStatus]) -> some View {
ScrollView {
VStack(alignment: .leading, spacing: 16) {
@@ -295,7 +414,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")
@@ -304,24 +423,27 @@ 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) {
switch status {
@@ -382,8 +504,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")]
@@ -46,7 +46,7 @@ struct ChatItemView: View {
let ci = chatItem
if chatItem.meta.itemDeleted != nil && (!revealed || chatItem.isDeletedContent) {
MarkedDeletedItemView(chat: chat, chatItem: chatItem, revealed: $revealed)
} else if ci.quotedItem == nil && ci.meta.itemDeleted == nil && !ci.meta.isLive {
} else if ci.quotedItem == nil && ci.meta.itemForwarded == nil && ci.meta.itemDeleted == nil && !ci.meta.isLive {
if let mc = ci.content.msgContent, mc.isText && isShortEmoji(ci.content.text) {
EmojiItemView(chat: chat, chatItem: ci)
} else if ci.content.text.isEmpty, case let .voice(_, duration) = ci.content.msgContent {
@@ -102,9 +102,9 @@ struct ChatItemContentView<Content: View>: View {
case let .rcvChatPreference(feature, allowed, param):
CIFeaturePreferenceView(chat: chat, chatItem: chatItem, feature: feature, allowed: allowed, param: param)
case let .sndChatPreference(feature, _, _):
CIChatFeatureView(chatItem: chatItem, revealed: $revealed, feature: feature, icon: feature.icon, iconColor: .secondary)
case let .rcvGroupFeature(feature, preference, _): chatFeatureView(feature, preference.enable.iconColor)
case let .sndGroupFeature(feature, preference, _): chatFeatureView(feature, preference.enable.iconColor)
CIChatFeatureView(chat: chat, chatItem: chatItem, revealed: $revealed, feature: feature, icon: feature.icon, iconColor: .secondary)
case let .rcvGroupFeature(feature, preference, _, role): chatFeatureView(feature, preference.enabled(role, for: chat.chatInfo.groupInfo?.membership).iconColor)
case let .sndGroupFeature(feature, preference, _, role): chatFeatureView(feature, preference.enabled(role, for: chat.chatInfo.groupInfo?.membership).iconColor)
case let .rcvChatFeatureRejected(feature): chatFeatureView(feature, .red)
case let .rcvGroupFeatureRejected(feature): chatFeatureView(feature, .red)
case .sndModerated: deletedItemView()
@@ -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 {
@@ -149,7 +149,7 @@ struct ChatItemContentView<Content: View>: View {
}
private func chatFeatureView(_ feature: Feature, _ iconColor: Color) -> some View {
CIChatFeatureView(chatItem: chatItem, revealed: $revealed, feature: feature, iconColor: iconColor)
CIChatFeatureView(chat: chat, chatItem: chatItem, revealed: $revealed, feature: feature, iconColor: iconColor)
}
private var mergedGroupEventText: Text? {
+85 -24
View File
@@ -17,6 +17,7 @@ struct ChatView: View {
@Environment(\.colorScheme) var colorScheme
@Environment(\.dismiss) var dismiss
@Environment(\.presentationMode) var presentationMode
@Environment(\.scenePhase) var scenePhase
@State @ObservedObject var chat: Chat
@State private var showChatInfoSheet: Bool = false
@State private var showAddMembersSheet: Bool = false
@@ -82,7 +83,11 @@ struct ChatView: View {
initChatView()
}
.onChange(of: chatModel.chatId) { cId in
if cId != nil {
showChatInfoSheet = false
if let cId {
if let c = chatModel.getChat(cId) {
chat = c
}
initChatView()
} else {
dismiss()
@@ -126,7 +131,7 @@ struct ChatView: View {
} label: {
ChatInfoToolbar(chat: chat)
}
.sheet(isPresented: $showChatInfoSheet, onDismiss: {
.appSheet(isPresented: $showChatInfoSheet, onDismiss: {
connectionStats = nil
customUserProfile = nil
connectionCode = nil
@@ -178,7 +183,7 @@ struct ChatView: View {
.disabled(!contact.ready || !contact.active)
}
searchButton()
toggleNtfsButton(chat)
ToggleNtfsButton(chat: chat)
.disabled(!contact.ready || !contact.active)
} label: {
Image(systemName: "ellipsis")
@@ -207,7 +212,7 @@ struct ChatView: View {
}
Menu {
searchButton()
toggleNtfsButton(chat)
ToggleNtfsButton(chat: chat)
} label: {
Image(systemName: "ellipsis")
}
@@ -234,7 +239,9 @@ struct ChatView: View {
private func initChatView() {
let cInfo = chat.chatInfo
if case let .direct(contact) = cInfo {
// This check prevents the call to apiContactInfo after the app is suspended, and the database is closed.
if case .active = scenePhase,
case let .direct(contact) = cInfo {
Task {
do {
let (stats, _) = try await apiContactInfo(chat.chatInfo.apiId)
@@ -248,7 +255,8 @@ struct ChatView: View {
}
}
}
if chatModel.draftChatId == cInfo.id, let draft = chatModel.draft {
if chatModel.draftChatId == cInfo.id && !composeState.forwarding,
let draft = chatModel.draft {
composeState = draft
}
if chat.chatStats.unreadChat {
@@ -293,7 +301,7 @@ struct ChatView: View {
}
private func voiceWithoutFrame(_ ci: ChatItem) -> Bool {
ci.content.msgContent?.isVoice == true && ci.content.text.count == 0 && ci.quotedItem == nil
ci.content.msgContent?.isVoice == true && ci.content.text.count == 0 && ci.quotedItem == nil && ci.meta.itemForwarded == nil
}
private func chatItemsList() -> some View {
@@ -339,8 +347,8 @@ struct ChatView: View {
.onChange(of: searchText) { _ in
loadChat(chat: chat, search: searchText)
}
.onChange(of: chatModel.chatId) { _ in
if let chatId = chatModel.chatId, let c = chatModel.getChat(chatId) {
.onChange(of: chatModel.chatId) { chatId in
if let chatId, let c = chatModel.getChat(chatId) {
chat = c
showChatInfoSheet = false
loadChat(chat: c)
@@ -511,6 +519,7 @@ struct ChatView: View {
chat: chat,
chatItem: ci,
maxWidth: maxWidth,
itemWidth: maxWidth,
composeState: $composeState,
selectedMember: $selectedMember,
chatView: self
@@ -523,6 +532,7 @@ struct ChatView: View {
@ObservedObject var chat: Chat
var chatItem: ChatItem
var maxWidth: CGFloat
@State var itemWidth: CGFloat
@Binding var composeState: ComposeState
@Binding var selectedMember: GMember?
var chatView: ChatView
@@ -534,6 +544,7 @@ struct ChatView: View {
@State private var revealed = false
@State private var showChatItemInfoSheet: Bool = false
@State private var chatItemInfo: ChatItemInfo?
@State private var showForwardingSheet: Bool = false
@State private var allowMenu: Bool = true
@@ -557,7 +568,12 @@ struct ChatView: View {
chatItemView(ci, nil, prev)
}
} else {
chatItemView(chatItem, range, prevItem)
// Switch branches just to work around context menu problem when 'revealed' changes but size of item isn't
if revealed {
chatItemView(chatItem, range, prevItem)
} else {
chatItemView(chatItem, range, prevItem)
}
}
}
}
@@ -577,12 +593,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)
.onTapGesture {
if chatView.membersLoaded {
selectedMember = m.getGroupMember(member.groupMemberId)
@@ -646,7 +662,7 @@ struct ChatView: View {
playbackState: $playbackState,
playbackTime: $playbackTime
)
.uiKitContextMenu(maxWidth: maxWidth, menu: uiMenu, allowMenu: $allowMenu)
.uiKitContextMenu(hasImageOrVideo: ci.content.msgContent?.isImageOrVideo == true, maxWidth: maxWidth, itemWidth: $itemWidth, menu: uiMenu, allowMenu: $allowMenu)
.accessibilityLabel("")
if ci.content.msgContent != nil && (ci.meta.itemDeleted == nil || revealed) && ci.reactions.count > 0 {
chatItemReactions(ci)
@@ -657,7 +673,7 @@ struct ChatView: View {
Button("Delete for me", role: .destructive) {
deleteMessage(.cidmInternal)
}
if let di = deletingItem, di.meta.editable && !di.localNote {
if let di = deletingItem, di.meta.deletable && !di.localNote {
Button(broadcastDeleteButtonText, role: .destructive) {
deleteMessage(.cidmBroadcast)
}
@@ -683,6 +699,14 @@ struct ChatView: View {
}) {
ChatItemInfoView(ci: ci, chatItemInfo: $chatItemInfo)
}
.sheet(isPresented: $showForwardingSheet) {
if #available(iOS 16.0, *) {
ChatItemForwardingView(ci: ci, fromChatInfo: chat.chatInfo, composeState: $composeState)
.presentationDetents([.fraction(0.8)])
} else {
ChatItemForwardingView(ci: ci, fromChatInfo: chat.chatInfo, composeState: $composeState)
}
}
}
private func showMemberImage(_ member: GroupMember, _ prevItem: ChatItem?) -> Bool {
@@ -761,10 +785,17 @@ struct ChatView: View {
} else {
menu.append(saveFileAction(fileSource))
}
} else if let file = ci.file, case .rcvInvitation = file.fileStatus, fileSizeValid(file) {
menu.append(downloadFileAction(file))
}
if ci.meta.editable && !mc.isVoice && !live {
menu.append(editAction(ci))
}
if ci.meta.itemDeleted == nil
&& (ci.file == nil || (fileSource != nil && fileExists))
&& !ci.isLiveDummy && !live {
menu.append(forwardUIAction(ci))
}
if !ci.isLiveDummy {
menu.append(viewInfoUIAction(ci))
}
@@ -797,6 +828,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
}
@@ -816,6 +850,15 @@ struct ChatView: View {
}
}
private func forwardUIAction(_ ci: ChatItem) -> UIAction {
UIAction(
title: NSLocalizedString("Forward", comment: "chat item action"),
image: UIImage(systemName: "arrowshape.turn.up.forward")
) { _ in
showForwardingSheet = true
}
}
private func reactionUIMenuPreiOS16(_ rs: [UIAction]) -> UIMenu {
UIMenu(
title: NSLocalizedString("React…", comment: "chat item menu"),
@@ -913,7 +956,21 @@ struct ChatView: View {
saveCryptoFile(fileSource)
}
}
private func downloadFileAction(_ file: CIFile) -> UIAction {
UIAction(
title: NSLocalizedString("Download", comment: "chat item action"),
image: UIImage(systemName: "arrow.down.doc")
) { _ in
Task {
logger.debug("ChatView downloadFileAction, in Task")
if let user = m.currentUser {
await receiveFile(user: user, fileId: file.fileId)
}
}
}
}
private func editAction(_ ci: ChatItem) -> UIAction {
UIAction(
title: NSLocalizedString("Edit", comment: "chat item action"),
@@ -986,7 +1043,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)
@@ -1162,14 +1219,18 @@ struct ChatView: View {
}
}
@ViewBuilder func toggleNtfsButton(_ chat: Chat) -> some View {
Button {
toggleNotifications(chat, enableNtfs: !chat.chatInfo.ntfsEnabled)
} label: {
if chat.chatInfo.ntfsEnabled {
Label("Mute", systemImage: "speaker.slash")
} else {
Label("Unmute", systemImage: "speaker.wave.2")
struct ToggleNtfsButton: View {
@ObservedObject var chat: Chat
var body: some View {
Button {
toggleNotifications(chat, enableNtfs: !chat.chatInfo.ntfsEnabled)
} label: {
if chat.chatInfo.ntfsEnabled {
Label("Mute", systemImage: "speaker.slash")
} else {
Label("Unmute", systemImage: "speaker.wave.2")
}
}
}
}
@@ -23,6 +23,7 @@ enum ComposeContextItem {
case noContextItem
case quotedItem(chatItem: ChatItem)
case editingItem(chatItem: ChatItem)
case forwardingItem(chatItem: ChatItem, fromChatInfo: ChatInfo)
}
enum VoiceMessageRecordingState {
@@ -72,6 +73,13 @@ struct ComposeState {
}
}
init(forwardingItem: ChatItem, fromChatInfo: ChatInfo) {
self.message = ""
self.preview = .noPreview
self.contextItem = .forwardingItem(chatItem: forwardingItem, fromChatInfo: fromChatInfo)
self.voiceMessageRecordingState = .noRecording
}
func copy(
message: String? = nil,
liveMessage: LiveMessage? = nil,
@@ -102,12 +110,19 @@ struct ComposeState {
}
}
var forwarding: Bool {
switch contextItem {
case .forwardingItem: return true
default: return false
}
}
var sendEnabled: Bool {
switch preview {
case let .mediaPreviews(media): return !media.isEmpty
case .voicePreview: return voiceMessageRecordingState == .finished
case .filePreview: return true
default: return !message.isEmpty || liveMessage != nil
default: return !message.isEmpty || forwarding || liveMessage != nil
}
}
@@ -153,7 +168,7 @@ struct ComposeState {
}
var attachmentDisabled: Bool {
if editing || liveMessage != nil || inProgress { return true }
if editing || forwarding || liveMessage != nil || inProgress { return true }
switch preview {
case .noPreview: return false
case .linkPreview: return false
@@ -161,6 +176,16 @@ struct ComposeState {
}
}
var attachmentPreview: Bool {
switch preview {
case .noPreview: false
case .linkPreview: false
case let .mediaPreviews(mediaPreviews): !mediaPreviews.isEmpty
case .voicePreview: false
case .filePreview: true
}
}
var empty: Bool {
message == "" && noPreview
}
@@ -234,6 +259,7 @@ struct ComposeView: View {
@Binding var keyboardVisible: Bool
@State var linkUrl: URL? = nil
@State var hasSimplexLink: Bool = false
@State var prevLinkUrl: URL? = nil
@State var pendingLinkUrl: URL? = nil
@State var cancelledLinks: Set<String> = []
@@ -260,6 +286,17 @@ struct ComposeView: View {
if chat.chatInfo.contact?.nextSendGrpInv ?? false {
ContextInvitingContactMemberView()
}
// preference checks should match checks in forwarding list
let simplexLinkProhibited = hasSimplexLink && !chat.groupFeatureEnabled(.simplexLinks)
let fileProhibited = composeState.attachmentPreview && !chat.groupFeatureEnabled(.files)
let voiceProhibited = composeState.voicePreview && !chat.chatInfo.featureEnabled(.voice)
if simplexLinkProhibited {
msgNotAllowedView("SimpleX links not allowed", icon: "link")
} else if fileProhibited {
msgNotAllowedView("Files and media not allowed", icon: "doc")
} else if voiceProhibited {
msgNotAllowedView("Voice messages not allowed", icon: "mic")
}
contextItemView()
switch (composeState.editing, composeState.preview) {
case (true, .filePreview): EmptyView()
@@ -278,7 +315,7 @@ struct ComposeView: View {
.padding(.bottom, 12)
.padding(.leading, 12)
if case let .group(g) = chat.chatInfo,
!g.fullGroupPreferences.files.on {
!g.fullGroupPreferences.files.on(for: g.membership) {
b.disabled(true).onTapGesture {
AlertManager.shared.showAlertMsg(
title: "Files and media prohibited!",
@@ -303,6 +340,7 @@ struct ComposeView: View {
},
nextSendGrpInv: chat.chatInfo.contact?.nextSendGrpInv ?? false,
voiceMessageAllowed: chat.chatInfo.featureEnabled(.voice),
disableSendButton: simplexLinkProhibited || fileProhibited || voiceProhibited,
showEnableVoiceMessagesAlert: chat.chatInfo.showEnableVoiceMessagesAlert,
startVoiceMessageRecording: {
Task {
@@ -337,13 +375,18 @@ struct ComposeView: View {
}
}
}
.onChange(of: composeState.message) { _ in
.onChange(of: composeState.message) { msg in
if composeState.linkPreviewAllowed {
if composeState.message.count > 0 {
showLinkPreview(composeState.message)
if msg.count > 0 {
showLinkPreview(msg)
} else {
resetLinkPreview()
hasSimplexLink = false
}
} else if msg.count > 0 && !chat.groupFeatureEnabled(.simplexLinks) {
(_, hasSimplexLink) = parseMessage(msg)
} else {
hasSimplexLink = false
}
}
.onChange(of: chat.userCanSend) { canSend in
@@ -610,6 +653,18 @@ struct ComposeView: View {
}
}
private func msgNotAllowedView(_ reason: LocalizedStringKey, icon: String) -> some View {
HStack {
Image(systemName: icon).foregroundColor(.secondary)
Text(reason).italic()
}
.padding(12)
.frame(minHeight: 50)
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(uiColor: .tertiarySystemGroupedBackground))
.padding(.top, 8)
}
@ViewBuilder private func contextItemView() -> some View {
switch composeState.contextItem {
case .noContextItem:
@@ -628,6 +683,14 @@ struct ComposeView: View {
contextIcon: "pencil",
cancelContextItem: { clearState() }
)
case let .forwardingItem(chatItem: forwardedItem, _):
ContextItemView(
chat: chat,
contextItem: forwardedItem,
contextIcon: "arrowshape.turn.up.forward",
cancelContextItem: { composeState = composeState.copy(contextItem: .noContextItem) },
showSender: false
)
}
}
@@ -649,6 +712,11 @@ 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, ttl)
if !composeState.message.isEmpty {
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)
} else if let liveMessage = liveMessage, liveMessage.sentMsg != nil {
@@ -694,7 +762,15 @@ struct ComposeView: View {
}
}
}
await MainActor.run { clearState(live: live) }
await MainActor.run {
let wasForwarding = composeState.forwarding
clearState(live: live)
if wasForwarding,
chatModel.draftChatId == chat.chatInfo.id,
let draft = chatModel.draft {
composeState = draft
}
}
return sent
func sending() async {
@@ -815,10 +891,27 @@ struct ComposeView: View {
return nil
}
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,
ttl: ttl
) {
await MainActor.run {
chatModel.addChatItem(chat.chatInfo, chatItem)
}
return chatItem
}
return nil
}
func checkLinkPreview() -> MsgContent {
switch (composeState.preview) {
case let .linkPreview(linkPreview: linkPreview):
if let url = parseMessage(msgText),
if let url = parseMessage(msgText).url,
let linkPreview = linkPreview,
url == linkPreview.uri {
return .link(text: msgText, preview: linkPreview)
@@ -947,7 +1040,7 @@ struct ComposeView: View {
private func showLinkPreview(_ s: String) {
prevLinkUrl = linkUrl
linkUrl = parseMessage(s)
(linkUrl, hasSimplexLink) = parseMessage(s)
if let url = linkUrl {
if url != composeState.linkPreview?.uri && url != pendingLinkUrl {
pendingLinkUrl = url
@@ -964,13 +1057,17 @@ struct ComposeView: View {
}
}
private func parseMessage(_ msg: String) -> URL? {
let parsedMsg = parseSimpleXMarkdown(msg)
let uri = parsedMsg?.first(where: { ft in
private func parseMessage(_ msg: String) -> (url: URL?, hasSimplexLink: Bool) {
guard let parsedMsg = parseSimpleXMarkdown(msg) else { return (nil, false) }
let url: URL? = if let uri = parsedMsg.first(where: { ft in
ft.format == .uri && !cancelledLinks.contains(ft.text) && !isSimplexLink(ft.text)
})
if let uri = uri { return URL(string: uri.text) }
else { return nil }
}) {
URL(string: uri.text)
} else {
nil
}
let simplexLink = parsedMsgHasSimplexLink(parsedMsg)
return (url, simplexLink)
}
private func isSimplexLink(_ link: String) -> Bool {
@@ -1009,6 +1106,10 @@ struct ComposeView: View {
}
}
func parsedMsgHasSimplexLink(_ parsedMsg: [FormattedText]) -> Bool {
parsedMsg.contains(where: { ft in ft.format?.isSimplexLink ?? false })
}
struct ComposeView_Previews: PreviewProvider {
static var previews: some View {
let chat = Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: [])
@@ -15,6 +15,7 @@ struct ContextItemView: View {
let contextItem: ChatItem
let contextIcon: String
let cancelContextItem: () -> Void
var showSender: Bool = true
var body: some View {
HStack {
@@ -23,7 +24,7 @@ struct ContextItemView: View {
.aspectRatio(contentMode: .fit)
.frame(width: 16, height: 16)
.foregroundColor(.secondary)
if let sender = contextItem.memberDisplayName {
if showSender, let sender = contextItem.memberDisplayName {
VStack(alignment: .leading, spacing: 4) {
Text(sender).font(.caption).foregroundColor(.secondary)
msgContentView(lines: 2)
@@ -48,14 +49,26 @@ struct ContextItemView: View {
}
private func msgContentView(lines: Int) -> some View {
MsgContentView(
chat: chat,
text: contextItem.text,
formattedText: contextItem.formattedText,
showSecrets: false
)
.multilineTextAlignment(isRightToLeft(contextItem.text) ? .trailing : .leading)
.lineLimit(lines)
contextMsgPreview()
.multilineTextAlignment(isRightToLeft(contextItem.text) ? .trailing : .leading)
.lineLimit(lines)
}
private func contextMsgPreview() -> Text {
return attachment() + messageText(contextItem.text, contextItem.formattedText, nil, preview: true, showSecrets: false)
func attachment() -> Text {
switch contextItem.content.msgContent {
case .file: return image("doc.fill")
case .image: return image("photo")
case .voice: return image("play.fill")
default: return Text("")
}
}
func image(_ s: String) -> Text {
Text(Image(systemName: s)).foregroundColor(Color(uiColor: .tertiaryLabel)) + Text(" ")
}
}
}
@@ -16,7 +16,6 @@ struct NativeTextEditor: UIViewRepresentable {
@Binding var disableEditing: Bool
@Binding var height: CGFloat
@Binding var focused: Bool
let alignment: TextAlignment
let onImagesAdded: ([UploadContent]) -> Void
private let minHeight: CGFloat = 37
@@ -30,13 +29,16 @@ struct NativeTextEditor: UIViewRepresentable {
func makeUIView(context: Context) -> UITextView {
let field = CustomUITextField(height: _height)
field.text = text
field.textAlignment = alignment == .leading ? .left : .right
field.textAlignment = alignment(text)
field.autocapitalizationType = .sentences
field.setOnTextChangedListener { newText, images in
if !disableEditing {
// Speed up the process of updating layout, reduce jumping content on screen
if !isShortEmoji(newText) { updateHeight(field) }
text = newText
field.textAlignment = alignment(text)
updateFont(field)
// Speed up the process of updating layout, reduce jumping content on screen
updateHeight(field)
self.height = field.frame.size.height
} else {
field.text = text
}
@@ -53,10 +55,12 @@ struct NativeTextEditor: UIViewRepresentable {
}
func updateUIView(_ field: UITextView, context: Context) {
field.text = text
field.textAlignment = alignment == .leading ? .left : .right
updateFont(field)
updateHeight(field)
if field.markedTextRange == nil && field.text != text {
field.text = text
field.textAlignment = alignment(text)
updateFont(field)
updateHeight(field)
}
}
private func updateHeight(_ field: UITextView) {
@@ -73,12 +77,19 @@ struct NativeTextEditor: UIViewRepresentable {
}
private func updateFont(_ field: UITextView) {
field.font = isShortEmoji(field.text)
let newFont = isShortEmoji(field.text)
? (field.text.count < 4 ? largeEmojiUIFont : mediumEmojiUIFont)
: UIFont.preferredFont(forTextStyle: .body)
if field.font != newFont {
field.font = newFont
}
}
}
private func alignment(_ text: String) -> NSTextAlignment {
isRightToLeft(text) ? .right : .left
}
private class CustomUITextField: UITextView, UITextViewDelegate {
var height: Binding<CGFloat>
var newHeight: CGFloat = 0
@@ -205,7 +216,6 @@ struct NativeTextEditor_Previews: PreviewProvider{
disableEditing: Binding.constant(false),
height: Binding.constant(100),
focused: Binding.constant(false),
alignment: TextAlignment.leading,
onImagesAdded: { _ in }
)
.fixedSize(horizontal: false, vertical: true)
@@ -20,6 +20,7 @@ struct SendMessageView: View {
var nextSendGrpInv: Bool = false
var showVoiceMessageButton: Bool = true
var voiceMessageAllowed: Bool = true
var disableSendButton = false
var showEnableVoiceMessagesAlert: ChatInfo.ShowEnableVoiceMessagesAlert = .other
var startVoiceMessageRecording: (() -> Void)? = nil
var finishVoiceMessageRecording: (() -> Void)? = nil
@@ -53,13 +54,11 @@ struct SendMessageView: View {
.padding(.vertical, 8)
.frame(maxWidth: .infinity)
} else {
let alignment: TextAlignment = isRightToLeft(composeState.message) ? .trailing : .leading
NativeTextEditor(
text: $composeState.message,
disableEditing: $composeState.inProgress,
height: $teHeight,
focused: $keyboardVisible,
alignment: alignment,
onImagesAdded: onMediaAdded
)
.allowsTightening(false)
@@ -109,6 +108,7 @@ struct SendMessageView: View {
} else if showVoiceMessageButton
&& composeState.message.isEmpty
&& !composeState.editing
&& !composeState.forwarding
&& composeState.liveMessage == nil
&& ((composeState.noPreview && vmrs == .noRecording)
|| (vmrs == .recording && holdingVMR)) {
@@ -184,7 +184,8 @@ struct SendMessageView: View {
!composeState.sendEnabled ||
composeState.inProgress ||
(!voiceMessageAllowed && composeState.voicePreview) ||
composeState.endLiveDisabled
composeState.endLiveDisabled ||
disableSendButton
)
.frame(width: 29, height: 29)
.contextMenu{
@@ -194,8 +194,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)
@@ -62,7 +62,7 @@ struct GroupChatInfoView: View {
NavigationView {
let members = chatModel.groupMembers
.filter { m in let status = m.wrapped.memberStatus; return status != .memLeft && status != .memRemoved }
.sorted { $0.displayName.lowercased() < $1.displayName.lowercased() }
.sorted { $0.wrapped.memberRole > $1.wrapped.memberRole }
List {
groupInfoHeader()
@@ -172,8 +172,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)
@@ -217,8 +216,7 @@ struct GroupChatInfoView: View {
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) {
@@ -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)"
}
}
@@ -83,7 +85,7 @@ struct GroupMemberInfoView: View {
Section {
if let contactId = member.memberContactId, let chat = knownDirectChat(contactId) {
knownDirectChatButton(chat)
} else if groupInfo.fullGroupPreferences.directMessages.on {
} else if groupInfo.fullGroupPreferences.directMessages.on(for: groupInfo.membership) {
if let contactId = member.memberContactId {
newDirectChatButton(contactId)
} else if member.activeConn?.peerChatVRange.isCompatibleRange(CREATE_MEMBER_CONTACT_VRANGE) ?? false {
@@ -110,7 +112,7 @@ struct GroupMemberInfoView: View {
Label("Share address", systemImage: "square.and.arrow.up")
}
if let contactId = member.memberContactId {
if knownDirectChat(contactId) == nil && !groupInfo.fullGroupPreferences.directMessages.on {
if knownDirectChat(contactId) == nil && !groupInfo.fullGroupPreferences.directMessages.on(for: groupInfo.membership) {
connectViaAddressButton(contactLink)
}
} else {
@@ -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))
}
}
@@ -305,8 +320,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 {
@@ -9,6 +9,12 @@
import SwiftUI
import SimpleXChat
private let featureRoles: [(role: GroupMemberRole?, text: LocalizedStringKey)] = [
(nil, "all members"),
(.admin, "admins"),
(.owner, "owners")
]
struct GroupPreferencesView: View {
@Environment(\.dismiss) var dismiss: DismissAction
@EnvironmentObject var chatModel: ChatModel
@@ -24,10 +30,11 @@ struct GroupPreferencesView: View {
List {
featureSection(.timedMessages, $preferences.timedMessages.enable)
featureSection(.fullDelete, $preferences.fullDelete.enable)
featureSection(.directMessages, $preferences.directMessages.enable)
featureSection(.directMessages, $preferences.directMessages.enable, $preferences.directMessages.role)
featureSection(.reactions, $preferences.reactions.enable)
featureSection(.voice, $preferences.voice.enable)
featureSection(.files, $preferences.files.enable)
featureSection(.voice, $preferences.voice.enable, $preferences.voice.role)
featureSection(.files, $preferences.files.enable, $preferences.files.role)
featureSection(.simplexLinks, $preferences.simplexLinks.enable, $preferences.simplexLinks.role)
featureSection(.history, $preferences.history.enable)
if groupInfo.canEdit {
@@ -64,7 +71,7 @@ struct GroupPreferencesView: View {
}
}
private func featureSection(_ feature: GroupFeature, _ enableFeature: Binding<GroupFeatureEnabled>) -> some View {
private func featureSection(_ feature: GroupFeature, _ enableFeature: Binding<GroupFeatureEnabled>, _ enableForRole: Binding<GroupMemberRole?>? = nil) -> some View {
Section {
let color: Color = enableFeature.wrappedValue == .on ? .green : .secondary
let icon = enableFeature.wrappedValue == .on ? feature.iconFilled : feature.icon
@@ -87,6 +94,14 @@ struct GroupPreferencesView: View {
)
.frame(height: 36)
}
if enableFeature.wrappedValue == .on, let enableForRole {
Picker("Enabled for", selection: enableForRole) {
ForEach(featureRoles, id: \.role) { fr in
Text(fr.text)
}
}
.frame(height: 36)
}
} else {
settingsRow(icon, color: color) {
infoRow(Text(feature.text), enableFeature.wrappedValue.text)
@@ -94,10 +109,26 @@ struct GroupPreferencesView: View {
if timedOn {
infoRow("Delete after", timeText(preferences.timedMessages.ttl))
}
if enableFeature.wrappedValue == .on, let enableForRole {
HStack {
Text("Enabled for").foregroundColor(.secondary)
Spacer()
Text(
featureRoles.first(where: { fr in fr.role == enableForRole.wrappedValue })?.text
?? "all members"
)
.foregroundColor(.secondary)
}
}
}
} footer: {
Text(feature.enableDescription(enableFeature.wrappedValue, groupInfo.canEdit))
}
.onChange(of: enableFeature.wrappedValue) { enabled in
if case .off = enabled {
enableForRole?.wrappedValue = nil
}
}
}
private func savePreferences() {
@@ -92,7 +92,7 @@ struct ChatListNavLink: View {
.swipeActions(edge: .leading, allowsFullSwipe: true) {
markReadButton()
toggleFavoriteButton()
toggleNtfsButton(chat)
ToggleNtfsButton(chat: chat)
}
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
if !chat.chatItems.isEmpty {
@@ -181,7 +181,7 @@ struct ChatListNavLink: View {
.swipeActions(edge: .leading, allowsFullSwipe: true) {
markReadButton()
toggleFavoriteButton()
toggleNtfsButton(chat)
ToggleNtfsButton(chat: chat)
}
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
if !chat.chatItems.isEmpty {
@@ -349,10 +349,12 @@ struct ChatListNavLink: View {
.tint(.accentColor)
}
.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)
}
}
}
.onTapGesture {
@@ -467,7 +469,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)
}
@@ -92,8 +92,7 @@ struct ChatListView: View {
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 }
@@ -23,8 +23,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)
}
@@ -241,14 +240,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("")
}
}
@@ -16,8 +16,7 @@ struct ContactRequestView: View {
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) {
@@ -12,6 +12,7 @@ private let fillColorLight = Color(uiColor: UIColor(red: 0.99, green: 0.99, blue
struct UserPicker: View {
@EnvironmentObject var m: ChatModel
@Environment(\.colorScheme) var colorScheme
@Environment(\.scenePhase) var scenePhase
@Binding var showSettings: Bool
@Binding var showConnectDesktop: Bool
@Binding var userPickerVisible: Bool
@@ -91,7 +92,10 @@ struct UserPicker: View {
.opacity(userPickerVisible ? 1.0 : 0.0)
.onAppear {
do {
m.users = try listUsers()
// This check prevents the call of listUsers after the app is suspended, and the database is closed.
if case .active = scenePhase {
m.users = try listUsers()
}
} catch let error {
logger.error("Error loading users \(responseError(error))")
}
@@ -123,8 +127,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)
@@ -12,6 +12,7 @@ import SimpleXChat
struct ChatInfoImage: View {
@Environment(\.colorScheme) var colorScheme
@ObservedObject var chat: Chat
var size: CGFloat
var color = Color(uiColor: .tertiarySystemGroupedBackground)
var body: some View {
@@ -28,6 +29,7 @@ struct ChatInfoImage: View {
return ProfileImage(
imageStr: chat.chatInfo.image,
iconName: iconName,
size: size,
color: iconColor
)
}
@@ -36,8 +38,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))
}
@@ -11,11 +11,20 @@ import UIKit
import SwiftUI
extension View {
func uiKitContextMenu(maxWidth: CGFloat, menu: Binding<UIMenu>, allowMenu: Binding<Bool>) -> some View {
func uiKitContextMenu(hasImageOrVideo: Bool, maxWidth: CGFloat, itemWidth: Binding<CGFloat>, menu: Binding<UIMenu>, allowMenu: Binding<Bool>) -> some View {
Group {
if allowMenu.wrappedValue {
InteractionView(content: self, maxWidth: maxWidth, menu: menu)
.fixedSize(horizontal: true, vertical: false)
if hasImageOrVideo {
InteractionView(content:
self.environmentObject(ChatModel.shared)
.overlay(DetermineWidthImageVideoItem())
.onPreferenceChange(DetermineWidthImageVideoItem.Key.self) { itemWidth.wrappedValue = $0 == 0 ? maxWidth : $0 }
, maxWidth: maxWidth, itemWidth: itemWidth, menu: menu)
.frame(maxWidth: itemWidth.wrappedValue)
} else {
InteractionView(content: self.environmentObject(ChatModel.shared), maxWidth: maxWidth, itemWidth: itemWidth, menu: menu)
.fixedSize(horizontal: true, vertical: false)
}
} else {
self
}
@@ -31,13 +40,14 @@ private class HostingViewHolder: UIView {
struct InteractionView<Content: View>: UIViewRepresentable {
let content: Content
var maxWidth: CGFloat
var itemWidth: Binding<CGFloat>
@Binding var menu: UIMenu
func makeUIView(context: Context) -> UIView {
let view = HostingViewHolder()
view.contentSize = CGSizeMake(maxWidth, .infinity)
view.backgroundColor = .clear
let hostView = UIHostingController(rootView: content)
view.contentSize = hostView.view.intrinsicContentSize
hostView.view.translatesAutoresizingMaskIntoConstraints = false
let constraints = [
hostView.view.topAnchor.constraint(equalTo: view.topAnchor),
@@ -57,7 +67,11 @@ struct InteractionView<Content: View>: UIViewRepresentable {
}
func updateUIView(_ uiView: UIView, context: Context) {
(uiView as! HostingViewHolder).contentSize = uiView.subviews[0].sizeThatFits(CGSizeMake(maxWidth, .infinity))
let was = (uiView as! HostingViewHolder).contentSize
(uiView as! HostingViewHolder).contentSize = uiView.subviews[0].sizeThatFits(CGSizeMake(itemWidth.wrappedValue, .infinity))
if was != (uiView as! HostingViewHolder).contentSize {
uiView.invalidateIntrinsicContentSize()
}
}
func makeCoordinator() -> Coordinator {
@@ -21,6 +21,19 @@ struct DetermineWidth: View {
}
}
struct DetermineWidthImageVideoItem: View {
typealias Key = MaximumWidthImageVideoPreferenceKey
var body: some View {
GeometryReader { proxy in
Color.clear
.preference(
key: MaximumWidthImageVideoPreferenceKey.self,
value: proxy.size.width
)
}
}
}
struct MaximumWidthPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
@@ -28,6 +41,13 @@ struct MaximumWidthPreferenceKey: PreferenceKey {
}
}
struct MaximumWidthImageVideoPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = max(value, nextValue())
}
}
struct DetermineWidth_Previews: PreviewProvider {
static var previews: some View {
DetermineWidth()
@@ -9,29 +9,51 @@
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)
@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)
}
}
}
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)
}
@@ -150,7 +150,7 @@ struct MigrateFromDevice: View {
return Alert(
title: Text(title),
message: Text(text),
primaryButton: .destructive(Text("Delete")) {
primaryButton: .default(Text("Delete")) {
deleteChatAndDismiss()
},
secondaryButton: .cancel()
@@ -333,16 +333,16 @@ struct MigrateFromDevice: View {
ZStack {
List {
Section {
Button(action: { alert = .deleteChat() }) {
settingsRow("trash.fill") {
Text("Delete database from this device").foregroundColor(.accentColor)
}
}
Button(action: { alert = .startChat() }) {
settingsRow("play.fill") {
Text("Start chat").foregroundColor(.red)
}
}
Button(action: { alert = .deleteChat() }) {
settingsRow("trash.fill") {
Text("Delete database from this device").foregroundColor(.accentColor)
}
}
} header: {
Text("Migration complete")
} footer: {
@@ -448,6 +448,9 @@ struct MigrateToDevice: View {
case .rcvFileError:
alert = .error(title: "Download failed", error: "File was deleted or link is invalid")
migrationState = .downloadFailed(totalBytes: totalBytes, link: link, archivePath: archivePath)
case .chatError(_, .error(.noRcvFileUser)):
alert = .error(title: "Download failed", error: "File was deleted or link is invalid")
migrationState = .downloadFailed(totalBytes: totalBytes, link: link, archivePath: archivePath)
default:
logger.debug("unsupported event: \(msg.responseType)")
}
@@ -70,9 +70,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
+12 -15
View File
@@ -11,14 +11,9 @@ import SimpleXChat
import CodeScanner
import AVFoundation
enum SomeAlert: Identifiable {
case someAlert(alert: Alert, id: String)
var id: String {
switch self {
case let .someAlert(_, id): return id
}
}
struct SomeAlert: Identifiable {
var alert: Alert
var id: String
}
private enum NewChatViewAlert: Identifiable {
@@ -142,8 +137,8 @@ struct NewChatView: View {
switch(a) {
case let .planAndConnectAlert(alert):
return planAndConnectAlert(alert, dismiss: true, cleanup: { pastedLink = "" })
case let .newChatSomeAlert(.someAlert(alert, _)):
return alert
case let .newChatSomeAlert(a):
return a.alert
}
}
}
@@ -181,7 +176,7 @@ struct NewChatView: View {
await MainActor.run {
creatingConnReq = false
if let apiAlert = apiAlert {
alert = .newChatSomeAlert(alert: .someAlert(alert: apiAlert, id: "createInvitation error"))
alert = .newChatSomeAlert(alert: SomeAlert(alert: apiAlert, id: "createInvitation error"))
}
}
}
@@ -315,7 +310,7 @@ private struct ConnectView: View {
// showQRCodeScanner = false
connect(pastedLink)
} else {
alert = .newChatSomeAlert(alert: .someAlert(
alert = .newChatSomeAlert(alert: SomeAlert(
alert: mkAlert(title: "Invalid link", message: "The text you pasted is not a SimpleX link."),
id: "pasteLinkView: code is not a SimpleX link"
))
@@ -338,14 +333,14 @@ private struct ConnectView: View {
if strIsSimplexLink(r.string) {
connect(link)
} else {
alert = .newChatSomeAlert(alert: .someAlert(
alert = .newChatSomeAlert(alert: SomeAlert(
alert: mkAlert(title: "Invalid QR code", message: "The code you scanned is not a SimpleX link QR code."),
id: "processQRCode: code is not a SimpleX link"
))
}
case let .failure(e):
logger.error("processQRCode QR code error: \(e.localizedDescription)")
alert = .newChatSomeAlert(alert: .someAlert(
alert = .newChatSomeAlert(alert: SomeAlert(
alert: mkAlert(title: "Invalid QR code", message: "Error scanning code: \(e.localizedDescription)"),
id: "processQRCode: failure"
))
@@ -367,11 +362,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 +432,7 @@ struct ScannerInView: View {
}
}
private func linkTextView(_ link: String) -> some View {
Text(link)
.lineLimit(1)
@@ -166,8 +166,10 @@ private func createProfile(_ displayName: String, showAlert: (UserProfileAlert)
)
let m = ChatModel.shared
do {
AppChatState.shared.set(.active)
m.currentUser = try apiCreateActiveUser(profile)
if m.users.isEmpty {
// .isEmpty check is redundant here, but it makes it clearer what is going on
if m.users.isEmpty || m.users.allSatisfy({ $0.user.hidden }) {
try startChat()
withAnimation {
onboardingStageDefault.set(.step3_CreateSimpleXAddress)
@@ -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
@@ -181,23 +181,27 @@ struct ConnectDesktopView: View {
}
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")
}
private func searchingDesktopView() -> some View {
@@ -329,16 +333,10 @@ 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)
}
}
@@ -51,9 +51,10 @@ struct AdvancedNetworkSettings: View {
}
.disabled(currentNetCfg == NetCfg.proxyDefaults)
timeoutSettingPicker("TCP connection timeout", selection: $netCfg.tcpConnectTimeout, values: [7_500000, 10_000000, 15_000000, 20_000000, 30_000000, 45_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: [15_000, 30_000, 45_000, 60_000, 90_000, 120_000], 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: "")
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)
@@ -50,6 +51,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)
@@ -17,11 +17,14 @@ let interfaceStyleNames: [LocalizedStringKey] = ["System", "Light", "Dark"]
let appSettingsURL = URL(string: UIApplication.openSettingsURLString)!
struct AppearanceSettings: View {
@EnvironmentObject var m: ChatModel
@Environment(\.colorScheme) var colorScheme
@EnvironmentObject var sceneDelegate: SceneDelegate
@State private var iconLightTapped = false
@State private var iconDarkTapped = false
@State private var userInterfaceStyle = getUserInterfaceStyleDefault()
@State private var uiTintColor = getUIAccentColorDefault()
@AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var profileImageCornerRadius = defaultProfileImageCorner
var body: some View {
VStack{
@@ -44,6 +47,23 @@ struct AppearanceSettings: View {
}
}
Section("Profile images") {
HStack(spacing: 16) {
if let img = m.currentUser?.image, img != "" {
ProfileImage(imageStr: img, size: 60)
} else {
clipProfileImage(Image(colorScheme == .light ? "icon-dark" : "icon-light"), size: 60, radius: profileImageCornerRadius)
}
Slider(
value: $profileImageCornerRadius,
in: 0...50,
step: 2.5
)
}
.foregroundColor(.secondary)
}
Section {
Picker("Theme", selection: $userInterfaceStyle) {
ForEach(interfaceStyles, id: \.self) { style in
@@ -93,7 +113,7 @@ struct AppearanceSettings: View {
}
._onButtonGesture { tapped.wrappedValue = $0 } perform: {}
.overlay(tapped.wrappedValue ? Color.secondary : Color.clear)
.cornerRadius(20)
.cornerRadius(13.5)
}
}
@@ -12,7 +12,6 @@ import SimpleXChat
struct DeveloperView: View {
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@AppStorage(GROUP_DEFAULT_CONFIRM_DB_UPGRADES, store: groupDefaults) private var confirmDatabaseUpgrades = false
@AppStorage(GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED, store: groupDefaults) private var pqExperimentalEnabled = false
@Environment(\.colorScheme) var colorScheme
var body: some View {
@@ -43,33 +42,9 @@ struct DeveloperView: View {
} footer: {
(developerTools ? Text("Show:") : Text("Hide:")) + Text(" ") + Text("Database IDs and Transport isolation option.")
}
if developerTools {
Section {
settingsRow("key") {
Toggle("Post-quantum E2EE", isOn: $pqExperimentalEnabled)
.onChange(of: pqExperimentalEnabled) {
setPQExperimentalEnabled($0)
}
}
} header: {
Text(String("Experimental"))
} footer: {
Text(String("In this version applies only to new contacts."))
}
}
}
}
}
private func setPQExperimentalEnabled(_ enable: Bool) {
do {
try apiSetPQEncryption(enable)
} catch let error {
let err = responseError(error)
logger.error("apiSetPQEncryption \(err)")
}
}
}
struct DeveloperView_Previews: PreviewProvider {
@@ -12,24 +12,32 @@ import SimpleXChat
private enum NetworkAlert: Identifiable {
case updateOnionHosts(hosts: OnionHosts)
case updateSessionMode(mode: TransportSessionMode)
case updateSMPProxyMode(proxyMode: SMPProxyMode)
case updateSMPProxyFallback(proxyFallback: SMPProxyFallback)
case error(err: String)
var id: String {
switch self {
case let .updateOnionHosts(hosts): return "updateOnionHosts \(hosts)"
case let .updateSessionMode(mode): return "updateSessionMode \(mode)"
case let .updateSMPProxyMode(proxyMode): return "updateSMPProxyMode \(proxyMode)"
case let .updateSMPProxyFallback(proxyFallback): return "updateSMPProxyFallback \(proxyFallback)"
case let .error(err): return "error \(err)"
}
}
}
struct NetworkAndServers: View {
@EnvironmentObject var m: ChatModel
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = true
@State private var cfgLoaded = false
@State private var currentNetCfg = NetCfg.defaults
@State private var netCfg = NetCfg.defaults
@State private var onionHosts: OnionHosts = .no
@State private var sessionMode: TransportSessionMode = .user
@State private var proxyMode: SMPProxyMode = .never
@State private var proxyFallback: SMPProxyFallback = .allow
@State private var alert: NetworkAlert?
var body: some View {
@@ -74,6 +82,30 @@ struct NetworkAndServers: View {
Text("Using .onion hosts requires compatible VPN provider.")
}
Section {
Picker("Private routing", selection: $proxyMode) {
ForEach(SMPProxyMode.values, id: \.self) { Text($0.text) }
}
.frame(height: 36)
Picker("Allow downgrade", selection: $proxyFallback) {
ForEach(SMPProxyFallback.values, id: \.self) { Text($0.text) }
}
.disabled(proxyMode == .never)
.frame(height: 36)
Toggle("Show message status", isOn: $showSentViaProxy)
} header: {
Text("Private message routing")
} footer: {
VStack(alignment: .leading) {
Text("To protect your IP address, private routing uses your SMP servers to deliver messages.")
if showSentViaProxy {
Text("Show → on messages sent via private routing.")
}
}
}
Section("Calls") {
NavigationLink {
RTCServers()
@@ -82,6 +114,14 @@ struct NetworkAndServers: View {
Text("WebRTC ICE servers")
}
}
Section("Network connection") {
HStack {
Text(m.networkInfo.networkType.text)
Spacer()
Image(systemName: "circle.fill").foregroundColor(m.networkInfo.online ? .green : .red)
}
}
}
}
.onAppear {
@@ -90,14 +130,24 @@ struct NetworkAndServers: View {
currentNetCfg = getNetCfg()
resetNetCfgView()
}
.onChange(of: onionHosts) { _ in
if onionHosts != OnionHosts(netCfg: currentNetCfg) {
alert = .updateOnionHosts(hosts: onionHosts)
.onChange(of: onionHosts) { hosts in
if hosts != OnionHosts(netCfg: currentNetCfg) {
alert = .updateOnionHosts(hosts: hosts)
}
}
.onChange(of: sessionMode) { _ in
if sessionMode != netCfg.sessionMode {
alert = .updateSessionMode(mode: sessionMode)
.onChange(of: sessionMode) { mode in
if mode != netCfg.sessionMode {
alert = .updateSessionMode(mode: mode)
}
}
.onChange(of: proxyMode) { mode in
if mode != netCfg.smpProxyMode {
alert = .updateSMPProxyMode(proxyMode: mode)
}
}
.onChange(of: proxyFallback) { fallbackMode in
if fallbackMode != netCfg.smpProxyFallback {
alert = .updateSMPProxyFallback(proxyFallback: fallbackMode)
}
}
.alert(item: $alert) { a in
@@ -128,6 +178,30 @@ struct NetworkAndServers: View {
resetNetCfgView()
}
)
case let .updateSMPProxyMode(proxyMode):
return Alert(
title: Text("Message routing mode"),
message: Text(proxyModeInfo(proxyMode)) + Text("\n") + Text("Updating this setting will re-connect the client to all servers."),
primaryButton: .default(Text("Ok")) {
netCfg.smpProxyMode = proxyMode
saveNetCfg()
},
secondaryButton: .cancel() {
resetNetCfgView()
}
)
case let .updateSMPProxyFallback(proxyFallback):
return Alert(
title: Text("Message routing fallback"),
message: Text(proxyFallbackInfo(proxyFallback)) + Text("\n") + Text("Updating this setting will re-connect the client to all servers."),
primaryButton: .default(Text("Ok")) {
netCfg.smpProxyFallback = proxyFallback
saveNetCfg()
},
secondaryButton: .cancel() {
resetNetCfgView()
}
)
case let .error(err):
return Alert(
title: Text("Error updating settings"),
@@ -157,6 +231,8 @@ struct NetworkAndServers: View {
netCfg = currentNetCfg
onionHosts = OnionHosts(netCfg: netCfg)
sessionMode = netCfg.sessionMode
proxyMode = netCfg.smpProxyMode
proxyFallback = netCfg.smpProxyFallback
}
private func onionHostsInfo(_ hosts: OnionHosts) -> LocalizedStringKey {
@@ -173,6 +249,23 @@ struct NetworkAndServers: View {
case .entity: return "A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail."
}
}
private func proxyModeInfo(_ mode: SMPProxyMode) -> LocalizedStringKey {
switch mode {
case .always: return "Always use private routing."
case .unknown: return "Use private routing with unknown servers."
case .unprotected: return "Use private routing with unknown servers when IP address is not protected."
case .never: return "Do NOT use private routing."
}
}
private func proxyFallbackInfo(_ proxyFallback: SMPProxyFallback) -> LocalizedStringKey {
switch proxyFallback {
case .allow: return "Send messages directly when your or destination server does not support private routing."
case .allowProtected: return "Send messages directly when IP address is protected and your or destination server does not support private routing."
case .prohibit: return "Do NOT send messages directly, even if your or destination server does not support private routing."
}
}
}
struct NetworkServersView_Previews: PreviewProvider {
@@ -16,6 +16,7 @@ struct PrivacySettings: View {
@AppStorage(DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS) private var showChatPreviews = true
@AppStorage(DEFAULT_PRIVACY_SAVE_LAST_DRAFT) private var saveLastDraft = true
@AppStorage(GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES, store: groupDefaults) private var encryptLocalFiles = true
@AppStorage(GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS, store: groupDefaults) private var askToApproveRelays = true
@State private var simplexLinkMode = privacySimplexLinkModeDefault.get()
@AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = false
@AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false
@@ -64,18 +65,6 @@ struct PrivacySettings: View {
}
Section {
settingsRow("lock.doc") {
Toggle("Encrypt local files", isOn: $encryptLocalFiles)
.onChange(of: encryptLocalFiles) {
setEncryptLocalFiles($0)
}
}
settingsRow("photo") {
Toggle("Auto-accept images", isOn: $autoAcceptImages)
.onChange(of: autoAcceptImages) {
privacyAcceptImagesGroupDefault.set($0)
}
}
settingsRow("network") {
Toggle("Send link previews", isOn: $useLinkPreviews)
}
@@ -108,6 +97,32 @@ struct PrivacySettings: View {
Text("Chats")
}
Section {
settingsRow("lock.doc") {
Toggle("Encrypt local files", isOn: $encryptLocalFiles)
.onChange(of: encryptLocalFiles) {
setEncryptLocalFiles($0)
}
}
settingsRow("photo") {
Toggle("Auto-accept images", isOn: $autoAcceptImages)
.onChange(of: autoAcceptImages) {
privacyAcceptImagesGroupDefault.set($0)
}
}
settingsRow("network.badge.shield.half.filled") {
Toggle("Protect IP address", isOn: $askToApproveRelays)
}
} header: {
Text("Files")
} footer: {
if askToApproveRelays {
Text("The app will ask to confirm downloads from unknown file servers (except .onion).")
} else {
Text("Without Tor or VPN, your IP address will be visible to file servers.")
}
}
Section {
settingsRow("person") {
Toggle("Contacts", isOn: $contactReceipts)
@@ -45,6 +45,7 @@ let DEFAULT_ACCENT_COLOR_RED = "accentColorRed"
let DEFAULT_ACCENT_COLOR_GREEN = "accentColorGreen"
let DEFAULT_ACCENT_COLOR_BLUE = "accentColorBlue"
let DEFAULT_USER_INTERFACE_STYLE = "userInterfaceStyle"
let DEFAULT_PROFILE_IMAGE_CORNER_RADIUS = "profileImageCornerRadius"
let DEFAULT_CONNECT_VIA_LINK_TAB = "connectViaLinkTab"
let DEFAULT_LIVE_MESSAGE_ALERT_SHOWN = "liveMessageAlertShown"
let DEFAULT_SHOW_HIDDEN_PROFILES_NOTICE = "showHiddenProfilesNotice"
@@ -59,6 +60,7 @@ let DEFAULT_DEVICE_NAME_FOR_REMOTE_ACCESS = "deviceNameForRemoteAccess"
let DEFAULT_CONFIRM_REMOTE_SESSIONS = "confirmRemoteSessions"
let DEFAULT_CONNECT_REMOTE_VIA_MULTICAST = "connectRemoteViaMulticast"
let DEFAULT_CONNECT_REMOTE_VIA_MULTICAST_AUTO = "connectRemoteViaMulticastAuto"
let DEFAULT_SHOW_SENT_VIA_RPOXY = "showSentViaProxy"
let ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN = "androidCallOnLockScreen"
@@ -87,6 +89,7 @@ let appDefaults: [String: Any] = [
DEFAULT_ACCENT_COLOR_GREEN: 0.533,
DEFAULT_ACCENT_COLOR_BLUE: 1.000,
DEFAULT_USER_INTERFACE_STYLE: 0,
DEFAULT_PROFILE_IMAGE_CORNER_RADIUS: defaultProfileImageCorner,
DEFAULT_CONNECT_VIA_LINK_TAB: ConnectViaLinkTab.scan.rawValue,
DEFAULT_LIVE_MESSAGE_ALERT_SHOWN: false,
DEFAULT_SHOW_HIDDEN_PROFILES_NOTICE: true,
@@ -97,6 +100,7 @@ let appDefaults: [String: Any] = [
DEFAULT_CONFIRM_REMOTE_SESSIONS: false,
DEFAULT_CONNECT_REMOTE_VIA_MULTICAST: true,
DEFAULT_CONNECT_REMOTE_VIA_MULTICAST_AUTO: true,
DEFAULT_SHOW_SENT_VIA_RPOXY: false,
ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN: AppSettingsLockScreenCalls.show.rawValue
]
@@ -425,8 +429,7 @@ struct ProfilePreview: View {
var body: some View {
HStack {
ProfileImage(imageStr: profileOf.image, color: color)
.frame(width: 44, height: 44)
ProfileImage(imageStr: profileOf.image, size: 44, color: color)
.padding(.trailing, 6)
.padding(.vertical, 6)
VStack(alignment: .leading) {
@@ -188,9 +188,7 @@ struct UserProfile: View {
}
func profileImageView(_ imageStr: String?) -> some View {
ProfileImage(imageStr: imageStr)
.aspectRatio(1, contentMode: .fit)
.frame(maxWidth: 192, maxHeight: 192)
ProfileImage(imageStr: imageStr, size: 192)
}
func editImageButton(action: @escaping () -> Void) -> some View {
@@ -123,7 +123,7 @@ struct UserProfilesView: View {
deleteModeButton("Profile and server connections", true)
deleteModeButton("Local profile data only", false)
}
.sheet(item: $selectedUser) { user in
.appSheet(item: $selectedUser) { user in
HiddenProfileView(user: user, profileHidden: $profileHidden)
}
.onChange(of: profileHidden) { _ in
@@ -131,7 +131,7 @@ struct UserProfilesView: View {
withAnimation { profileHidden = false }
}
}
.sheet(item: $profileAction) { action in
.appSheet(item: $profileAction) { action in
profileActionView(action)
}
.alert(item: $alert) { alert in
@@ -276,6 +276,7 @@ struct UserProfilesView: View {
// Deleting the last visible user while having hidden one(s)
try await deleteUser()
try await changeActiveUserAsync_(nil, viewPwd: nil)
try? await stopChatAsync()
await MainActor.run {
onboardingStageDefault.set(.step1_SimpleXInfo)
m.onboardingStage = .step1_SimpleXInfo
@@ -308,8 +309,7 @@ struct UserProfilesView: View {
}
} label: {
HStack {
ProfileImage(imageStr: user.image, color: Color(uiColor: .tertiarySystemFill))
.frame(width: 44, height: 44)
ProfileImage(imageStr: user.image, size: 44, color: Color(uiColor: .tertiarySystemFill))
.padding(.vertical, 4)
.padding(.trailing, 12)
Text(user.chatViewName)
File diff suppressed because it is too large Load Diff
@@ -690,6 +690,10 @@
<target>Povolte mizící zprávy, pouze pokud vám to váš kontakt dovolí.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow downgrade" xml:space="preserve">
<source>Allow downgrade</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow irreversible message deletion only if your contact allows it to you. (24 hours)" xml:space="preserve">
<source>Allow irreversible message deletion only if your contact allows it to you. (24 hours)</source>
<target>Povolte nevratné smazání zprávy pouze v případě, že vám to váš kontakt dovolí. (24 hodin)</target>
@@ -720,6 +724,10 @@
<target>Povolit nevratné smazání odeslaných zpráv. (24 hodin)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source>
<target>Povolit odesílání souborů a médii.</target>
@@ -778,6 +786,10 @@
<source>Already joining the group!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Always use private routing." xml:space="preserve">
<source>Always use private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Always use relay" xml:space="preserve">
<source>Always use relay</source>
<target>Spojení přes relé</target>
@@ -1043,6 +1055,14 @@
<target>Nelze přijmout soubor</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Capacity exceeded - recipient did not receive previously sent messages." xml:space="preserve">
<source>Capacity exceeded - recipient did not receive previously sent messages.</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve">
<source>Change</source>
<target>Změnit</target>
@@ -1230,6 +1250,10 @@
<target>Potvrdit aktualizaci databáze</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
<source>Confirm files from unknown servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm network settings" xml:space="preserve">
<source>Confirm network settings</source>
<note>No comment provided by engineer.</note>
@@ -1623,6 +1647,10 @@ This is your own one-time link!</source>
<target>Databáze bude přenesena po restartu aplikace</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Debug delivery" xml:space="preserve">
<source>Debug delivery</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Decentralized" xml:space="preserve">
<source>Decentralized</source>
<target>Decentralizované</target>
@@ -1862,6 +1890,10 @@ This cannot be undone!</source>
<source>Desktop devices</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Destination server error: %@" xml:space="preserve">
<source>Destination server error: %@</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Develop" xml:space="preserve">
<source>Develop</source>
<target>Vyvinout</target>
@@ -1965,11 +1997,19 @@ This cannot be undone!</source>
<source>Discover via local network</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do NOT send messages directly, even if your or destination server does not support private routing." xml:space="preserve">
<source>Do NOT send messages directly, even if your or destination server does not support private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
<source>Do NOT use SimpleX for emergency calls.</source>
<target>NEpoužívejte SimpleX pro tísňová volání.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do NOT use private routing." xml:space="preserve">
<source>Do NOT use private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do it later" xml:space="preserve">
<source>Do it later</source>
<target>Udělat později</target>
@@ -1999,6 +2039,10 @@ This cannot be undone!</source>
<target>Snížit a otevřít chat</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source>
<note>No comment provided by engineer.</note>
@@ -2104,6 +2148,10 @@ This cannot be undone!</source>
<target>Povolit sebedestrukční heslo</target>
<note>set passcode view</note>
</trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source>
<target>Šifrovat</target>
@@ -2247,10 +2295,6 @@ This cannot be undone!</source>
<target>Chyba přidávání člena(ů)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error allowing contact PQ encryption" xml:space="preserve">
<source>Error allowing contact PQ encryption</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error changing address" xml:space="preserve">
<source>Error changing address</source>
<target>Chuba změny adresy</target>
@@ -2507,7 +2551,7 @@ This cannot be undone!</source>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>Chyba: %@</target>
<note>No comment provided by engineer.</note>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -2596,6 +2640,10 @@ This cannot be undone!</source>
<target>Soubor: %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files" xml:space="preserve">
<source>Files</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files &amp; media" xml:space="preserve">
<source>Files &amp; media</source>
<target>Soubory a média</target>
@@ -2611,6 +2659,10 @@ This cannot be undone!</source>
<target>Soubory a média jsou zakázány v této skupině.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source>
<target>Soubory a média jsou zakázány!</target>
@@ -2674,6 +2726,32 @@ This cannot be undone!</source>
<target>Pro konzoli</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forward and save messages" xml:space="preserve">
<source>Forward and save messages</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarding server: %@&#10;Destination server error: %@" xml:space="preserve">
<source>Forwarding server: %1$@
Destination server error: %2$@</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Forwarding server: %@&#10;Error: %@" xml:space="preserve">
<source>Forwarding server: %1$@
Error: %2$@</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<note>No comment provided by engineer.</note>
@@ -2780,6 +2858,10 @@ This cannot be undone!</source>
<target>Členové skupiny mohou nevratně mazat odeslané zprávy. (24 hodin)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source>
<target>Členové skupiny mohou posílat přímé zprávy.</target>
@@ -3014,6 +3096,10 @@ This cannot be undone!</source>
<target>V odpovědi na</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="In-call sounds" xml:space="preserve">
<source>In-call sounds</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito" xml:space="preserve">
<source>Incognito</source>
<target>Inkognito</target>
@@ -3450,11 +3536,19 @@ This is your link for group %@!</source>
<target>Potvrzení o doručení zprávy!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message delivery warning" xml:space="preserve">
<source>Message delivery warning</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message draft" xml:space="preserve">
<source>Message draft</source>
<target>Návrh zprávy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reactions" xml:space="preserve">
<source>Message reactions</source>
<target>Reakce na zprávy</target>
@@ -3470,6 +3564,18 @@ This is your link for group %@!</source>
<target>Reakce na zprávy jsou v této skupině zakázány.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing mode" xml:space="preserve">
<source>Message routing mode</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message source remains private." xml:space="preserve">
<source>Message source remains private.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>Text zprávy</target>
@@ -3574,16 +3680,15 @@ This is your link for group %@!</source>
<target>Další vylepšení se chystají již brzy!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="More reliable network connection." xml:space="preserve">
<source>More reliable network connection.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Most likely this connection is deleted." xml:space="preserve">
<source>Most likely this connection is deleted.</source>
<target>Pravděpodobně je toto spojení smazáno.</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="Most likely this contact has deleted the connection with you." xml:space="preserve">
<source>Most likely this contact has deleted the connection with you.</source>
<target>Tento kontakt s největší pravděpodobností smazal spojení s vámi.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Multiple chat profiles" xml:space="preserve">
<source>Multiple chat profiles</source>
<target>Více chatovacích profilů</target>
@@ -3609,6 +3714,18 @@ This is your link for group %@!</source>
<target>Síť a servery</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network issues - message expired after many attempts to send it." xml:space="preserve">
<source>Network issues - message expired after many attempts to send it.</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Network management" xml:space="preserve">
<source>Network management</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source>
<target>Nastavení sítě</target>
@@ -3718,6 +3835,10 @@ This is your link for group %@!</source>
<target>Žádná historie</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>Nemáte oprávnění nahrávat hlasové zprávy</target>
@@ -3923,6 +4044,10 @@ This is your link for group %@!</source>
<source>Or show this code</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve">
<source>PING count</source>
<target>Počet PING</target>
@@ -4082,10 +4207,6 @@ Error: %@</source>
<target>Je možné, že otisk certifikátu v adrese serveru je nesprávný</target>
<note>server test error</note>
</trans-unit>
<trans-unit id="Post-quantum E2EE" xml:space="preserve">
<source>Post-quantum E2EE</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Preserve the last message draft, with attachments." xml:space="preserve">
<source>Preserve the last message draft, with attachments.</source>
<target>Zachování posledního návrhu zprávy s přílohami.</target>
@@ -4121,10 +4242,22 @@ Error: %@</source>
<target>Soukromé názvy souborů</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private message routing" xml:space="preserve">
<source>Private message routing</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private message routing 🚀" xml:space="preserve">
<source>Private message routing 🚀</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private notes" xml:space="preserve">
<source>Private notes</source>
<note>name of notes to self</note>
</trans-unit>
<trans-unit id="Private routing" xml:space="preserve">
<source>Private routing</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>Profil a připojení k serveru</target>
@@ -4135,6 +4268,10 @@ Error: %@</source>
<target>Profilový obrázek</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile images" xml:space="preserve">
<source>Profile images</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile name" xml:space="preserve">
<source>Profile name</source>
<note>No comment provided by engineer.</note>
@@ -4173,6 +4310,10 @@ Error: %@</source>
<target>Zakázat reakce na zprávy.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source>
<target>Zakázat odesílání přímých zpráv členům.</target>
@@ -4193,11 +4334,20 @@ Error: %@</source>
<target>Zakázat odesílání hlasových zpráv.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect IP address" xml:space="preserve">
<source>Protect IP address</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect app screen" xml:space="preserve">
<source>Protect app screen</source>
<target>Ochrana obrazovky aplikace</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts.&#10;Enable in *Network &amp; servers* settings." xml:space="preserve">
<source>Protect your IP address from the messaging relays chosen by your contacts.
Enable in *Network &amp; servers* settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
<source>Protect your chat profiles with a password!</source>
<target>Chraňte své chat profily heslem!</target>
@@ -4314,6 +4464,10 @@ Error: %@</source>
<source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source>
<target>Příjemci uvidí aktualizace během jejich psaní.</target>
@@ -4523,6 +4677,10 @@ Error: %@</source>
<target>SMP servery</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safely receive files" xml:space="preserve">
<source>Safely receive files</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safer groups" xml:space="preserve">
<source>Safer groups</source>
<note>No comment provided by engineer.</note>
@@ -4607,11 +4765,19 @@ Error: %@</source>
<target>Uložit uvítací zprávu?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source>
<target>Uložené servery WebRTC ICE budou odstraněny</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source>
<note>message info title</note>
@@ -4733,6 +4899,14 @@ Error: %@</source>
<target>Odeslat živou zprávu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when IP address is protected and your or destination server does not support private routing." xml:space="preserve">
<source>Send messages directly when IP address is protected and your or destination server does not support private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when your or destination server does not support private routing." xml:space="preserve">
<source>Send messages directly when your or destination server does not support private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send notifications" xml:space="preserve">
<source>Send notifications</source>
<target>Odeslat oznámení</target>
@@ -4837,6 +5011,10 @@ Error: %@</source>
<target>Odeslané zprávy se po uplynutí nastavené doby odstraní.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings." xml:space="preserve">
<source>Server address is incompatible with network settings.</source>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>Server vyžaduje autorizaci pro vytváření front, zkontrolujte heslo</target>
@@ -4852,6 +5030,10 @@ Error: %@</source>
<target>Test serveru se nezdařil!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings." xml:space="preserve">
<source>Server version is incompatible with network settings.</source>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>Servery</target>
@@ -4910,6 +5092,10 @@ Error: %@</source>
<target>Nastavení</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Shape profile images" xml:space="preserve">
<source>Shape profile images</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share" xml:space="preserve">
<source>Share</source>
<target>Sdílet</target>
@@ -4963,11 +5149,19 @@ Error: %@</source>
<target>Zobrazit poslední zprávy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show message status" xml:space="preserve">
<source>Show message status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Zobrazení náhledu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show → on messages sent via private routing." xml:space="preserve">
<source>Show → on messages sent via private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show:" xml:space="preserve">
<source>Show:</source>
<target>Zobrazit:</target>
@@ -5026,6 +5220,14 @@ Error: %@</source>
<trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source>
<target>Odkazy na SimpleX</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -5063,6 +5265,10 @@ Error: %@</source>
<target>Někdo</target>
<note>notification title</note>
</trans-unit>
<trans-unit id="Square, circle, or anything in between." xml:space="preserve">
<source>Square, circle, or anything in between.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Start chat" xml:space="preserve">
<source>Start chat</source>
<target>Začít chat</target>
@@ -5269,6 +5475,10 @@ Může se to stát kvůli nějaké chybě, nebo pokud je spojení kompromitován
<target>Aplikace vás může upozornit na přijaté zprávy nebo žádosti o kontakt - povolte to v nastavení.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The app will ask to confirm downloads from unknown file servers (except .onion)." xml:space="preserve">
<source>The app will ask to confirm downloads from unknown file servers (except .onion).</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The attempt to change database passphrase was not completed." xml:space="preserve">
<source>The attempt to change database passphrase was not completed.</source>
<target>Pokus o změnu přístupové fráze databáze nebyl dokončen.</target>
@@ -5445,6 +5655,10 @@ Může se to stát kvůli nějaké chybě, nebo pokud je spojení kompromitován
<target>K ochraně časového pásma používají obrazové/hlasové soubory UTC.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect your IP address, private routing uses your SMP servers to deliver messages." xml:space="preserve">
<source>To protect your IP address, private routing uses your SMP servers to deliver messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect your information, turn on SimpleX Lock.&#10;You will be prompted to complete authentication before this feature is enabled." xml:space="preserve">
<source>To protect your information, turn on SimpleX Lock.
You will be prompted to complete authentication before this feature is enabled.</source>
@@ -5531,11 +5745,6 @@ Před zapnutím této funkce budete vyzváni k dokončení ověření.</target>
<source>Unblock member?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unexpected error: %@" xml:space="preserve">
<source>Unexpected error: %@</source>
<target>Neočekávaná chyba: %@</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="Unexpected migration state" xml:space="preserve">
<source>Unexpected migration state</source>
<target>Neočekávaný stav přenášení</target>
@@ -5581,6 +5790,10 @@ Před zapnutím této funkce budete vyzváni k dokončení ověření.</target>
<target>Neznámá chyba</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unknown servers!" xml:space="preserve">
<source>Unknown servers!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions." xml:space="preserve">
<source>Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions.</source>
<target>Při nepoužívání rozhraní volání iOS, povolte režim Nerušit, abyste se vyhnuli vyrušování.</target>
@@ -5721,6 +5934,14 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
<source>Use only local notifications?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use private routing with unknown servers when IP address is not protected." xml:space="preserve">
<source>Use private routing with unknown servers when IP address is not protected.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use private routing with unknown servers." xml:space="preserve">
<source>Use private routing with unknown servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use server" xml:space="preserve">
<source>Use server</source>
<target>Použít server</target>
@@ -5828,6 +6049,10 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
<target>Hlasové zprávy jsou v této skupině zakázány.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source>
<target>Hlasové zprávy jsou zakázány!</target>
@@ -5895,6 +6120,10 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
<target>Když je k dispozici</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="When connecting audio and video calls." xml:space="preserve">
<source>When connecting audio and video calls.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="When people request to connect, you can accept or reject it." xml:space="preserve">
<source>When people request to connect, you can accept or reject it.</source>
<target>Když někdo požádá o připojení, můžete žádost přijmout nebo odmítnout.</target>
@@ -5905,6 +6134,18 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
<target>Pokud s někým sdílíte inkognito profil, bude tento profil použit pro skupiny, do kterých vás pozve.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Will be enabled in direct chats!" xml:space="preserve">
<source>Will be enabled in direct chats!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source>
<note>No comment provided by engineer.</note>
@@ -5918,11 +6159,23 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
<source>With reduced battery usage.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Without Tor or VPN, your IP address will be visible to file servers." xml:space="preserve">
<source>Without Tor or VPN, your IP address will be visible to file servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." xml:space="preserve">
<source>Without Tor or VPN, your IP address will be visible to these XFTP relays: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wrong database passphrase" xml:space="preserve">
<source>Wrong database passphrase</source>
<target>Špatná přístupová fráze k databázi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wrong key or unknown connection - most likely this connection is deleted." xml:space="preserve">
<source>Wrong key or unknown connection - most likely this connection is deleted.</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Wrong passphrase!" xml:space="preserve">
<source>Wrong passphrase!</source>
<target>Špatná přístupová fráze!</target>
@@ -6368,6 +6621,10 @@ Servery SimpleX nevidí váš profil.</target>
<target>správce</target>
<note>member role</note>
</trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source>
<target>povoluji šifrování pro %@…</target>
@@ -6378,6 +6635,10 @@ Servery SimpleX nevidí váš profil.</target>
<target>povoluji šifrování…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve">
<source>always</source>
<target>vždy</target>
@@ -6700,6 +6961,10 @@ Servery SimpleX nevidí váš profil.</target>
<source>event happened</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source>
<target>skupina smazána</target>
@@ -6906,6 +7171,10 @@ Servery SimpleX nevidí váš profil.</target>
<target>vlastník</target>
<note>member role</note>
</trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source>
<target>peer-to-peer</target>
@@ -6953,6 +7222,14 @@ Servery SimpleX nevidí váš profil.</target>
<target>odstranil vás</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve">
<source>sec</source>
<target>sek</target>
@@ -6978,6 +7255,12 @@ Servery SimpleX nevidí váš profil.</target>
<target>odeslat přímou zprávu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="server queue info: %@&#10;&#10;last received msg: %@" xml:space="preserve">
<source>server queue info: %1$@
last received msg: %2$@</source>
<note>queue info</note>
</trans-unit>
<trans-unit id="set new contact address" xml:space="preserve">
<source>set new contact address</source>
<note>profile update event chat item</note>
@@ -7014,10 +7297,18 @@ Servery SimpleX nevidí váš profil.</target>
<target>neznámý</target>
<note>connection info</note>
</trans-unit>
<trans-unit id="unknown relays" xml:space="preserve">
<source>unknown relays</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unknown status" xml:space="preserve">
<source>unknown status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unprotected" xml:space="preserve">
<source>unprotected</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="updated group profile" xml:space="preserve">
<source>updated group profile</source>
<target>aktualizoval profil skupiny</target>
@@ -7081,11 +7372,19 @@ Servery SimpleX nevidí váš profil.</target>
<target>týdnů</target>
<note>time unit</note>
</trans-unit>
<trans-unit id="when IP hidden" xml:space="preserve">
<source>when IP hidden</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="yes" xml:space="preserve">
<source>yes</source>
<target>ano</target>
<note>pref value</note>
</trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>jste pozváni do skupiny</target>
File diff suppressed because it is too large Load Diff
@@ -713,6 +713,11 @@
<target>Allow disappearing messages only if your contact allows it to you.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow downgrade" xml:space="preserve">
<source>Allow downgrade</source>
<target>Allow downgrade</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow irreversible message deletion only if your contact allows it to you. (24 hours)" xml:space="preserve">
<source>Allow irreversible message deletion only if your contact allows it to you. (24 hours)</source>
<target>Allow irreversible message deletion only if your contact allows it to you. (24 hours)</target>
@@ -743,6 +748,11 @@
<target>Allow to irreversibly delete sent messages. (24 hours)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<target>Allow to send SimpleX links.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source>
<target>Allow to send files and media.</target>
@@ -803,6 +813,11 @@
<target>Already joining the group!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Always use private routing." xml:space="preserve">
<source>Always use private routing.</source>
<target>Always use private routing.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Always use relay" xml:space="preserve">
<source>Always use relay</source>
<target>Always use relay</target>
@@ -1083,6 +1098,16 @@
<target>Cannot receive file</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Capacity exceeded - recipient did not receive previously sent messages." xml:space="preserve">
<source>Capacity exceeded - recipient did not receive previously sent messages.</source>
<target>Capacity exceeded - recipient did not receive previously sent messages.</target>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<target>Cellular</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve">
<source>Change</source>
<target>Change</target>
@@ -1274,6 +1299,11 @@
<target>Confirm database upgrades</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
<source>Confirm files from unknown servers.</source>
<target>Confirm files from unknown servers.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm network settings" xml:space="preserve">
<source>Confirm network settings</source>
<target>Confirm network settings</target>
@@ -1691,6 +1721,11 @@ This is your own one-time link!</target>
<target>Database will be migrated when the app restarts</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Debug delivery" xml:space="preserve">
<source>Debug delivery</source>
<target>Debug delivery</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Decentralized" xml:space="preserve">
<source>Decentralized</source>
<target>Decentralized</target>
@@ -1938,6 +1973,11 @@ This cannot be undone!</target>
<target>Desktop devices</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Destination server error: %@" xml:space="preserve">
<source>Destination server error: %@</source>
<target>Destination server error: %@</target>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Develop" xml:space="preserve">
<source>Develop</source>
<target>Develop</target>
@@ -2043,11 +2083,21 @@ This cannot be undone!</target>
<target>Discover via local network</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do NOT send messages directly, even if your or destination server does not support private routing." xml:space="preserve">
<source>Do NOT send messages directly, even if your or destination server does not support private routing.</source>
<target>Do NOT send messages directly, even if your or destination server does not support private routing.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
<source>Do NOT use SimpleX for emergency calls.</source>
<target>Do NOT use SimpleX for emergency calls.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do NOT use private routing." xml:space="preserve">
<source>Do NOT use private routing.</source>
<target>Do NOT use private routing.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do it later" xml:space="preserve">
<source>Do it later</source>
<target>Do it later</target>
@@ -2078,6 +2128,11 @@ This cannot be undone!</target>
<target>Downgrade and open chat</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<target>Download</target>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source>
<target>Download failed</target>
@@ -2188,6 +2243,11 @@ This cannot be undone!</target>
<target>Enable self-destruct passcode</target>
<note>set passcode view</note>
</trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<target>Enabled for</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source>
<target>Encrypt</target>
@@ -2338,11 +2398,6 @@ This cannot be undone!</target>
<target>Error adding member(s)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error allowing contact PQ encryption" xml:space="preserve">
<source>Error allowing contact PQ encryption</source>
<target>Error allowing contact PQ encryption</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error changing address" xml:space="preserve">
<source>Error changing address</source>
<target>Error changing address</target>
@@ -2606,7 +2661,7 @@ This cannot be undone!</target>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>Error: %@</target>
<note>No comment provided by engineer.</note>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -2698,6 +2753,11 @@ This cannot be undone!</target>
<target>File: %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files" xml:space="preserve">
<source>Files</source>
<target>Files</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files &amp; media" xml:space="preserve">
<source>Files &amp; media</source>
<target>Files &amp; media</target>
@@ -2713,6 +2773,11 @@ This cannot be undone!</target>
<target>Files and media are prohibited in this group.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<target>Files and media not allowed</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source>
<target>Files and media prohibited!</target>
@@ -2778,6 +2843,40 @@ This cannot be undone!</target>
<target>For console</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<target>Forward</target>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forward and save messages" xml:space="preserve">
<source>Forward and save messages</source>
<target>Forward and save messages</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<target>Forwarded</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<target>Forwarded from</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarding server: %@&#10;Destination server error: %@" xml:space="preserve">
<source>Forwarding server: %1$@
Destination server error: %2$@</source>
<target>Forwarding server: %1$@
Destination server error: %2$@</target>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Forwarding server: %@&#10;Error: %@" xml:space="preserve">
<source>Forwarding server: %1$@
Error: %2$@</source>
<target>Forwarding server: %1$@
Error: %2$@</target>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<target>Found desktop</target>
@@ -2888,6 +2987,11 @@ This cannot be undone!</target>
<target>Group members can irreversibly delete sent messages. (24 hours)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<target>Group members can send SimpleX links.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source>
<target>Group members can send direct messages.</target>
@@ -3128,6 +3232,11 @@ This cannot be undone!</target>
<target>In reply to</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="In-call sounds" xml:space="preserve">
<source>In-call sounds</source>
<target>In-call sounds</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito" xml:space="preserve">
<source>Incognito</source>
<target>Incognito</target>
@@ -3583,11 +3692,21 @@ This is your link for group %@!</target>
<target>Message delivery receipts!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message delivery warning" xml:space="preserve">
<source>Message delivery warning</source>
<target>Message delivery warning</target>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message draft" xml:space="preserve">
<source>Message draft</source>
<target>Message draft</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<target>Message queue info</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reactions" xml:space="preserve">
<source>Message reactions</source>
<target>Message reactions</target>
@@ -3603,6 +3722,21 @@ This is your link for group %@!</target>
<target>Message reactions are prohibited in this group.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<target>Message routing fallback</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing mode" xml:space="preserve">
<source>Message routing mode</source>
<target>Message routing mode</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message source remains private." xml:space="preserve">
<source>Message source remains private.</source>
<target>Message source remains private.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>Message text</target>
@@ -3718,16 +3852,16 @@ This is your link for group %@!</target>
<target>More improvements are coming soon!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="More reliable network connection." xml:space="preserve">
<source>More reliable network connection.</source>
<target>More reliable network connection.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Most likely this connection is deleted." xml:space="preserve">
<source>Most likely this connection is deleted.</source>
<target>Most likely this connection is deleted.</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="Most likely this contact has deleted the connection with you." xml:space="preserve">
<source>Most likely this contact has deleted the connection with you.</source>
<target>Most likely this contact has deleted the connection with you.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Multiple chat profiles" xml:space="preserve">
<source>Multiple chat profiles</source>
<target>Multiple chat profiles</target>
@@ -3753,6 +3887,21 @@ This is your link for group %@!</target>
<target>Network &amp; servers</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<target>Network connection</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network issues - message expired after many attempts to send it." xml:space="preserve">
<source>Network issues - message expired after many attempts to send it.</source>
<target>Network issues - message expired after many attempts to send it.</target>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Network management" xml:space="preserve">
<source>Network management</source>
<target>Network management</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source>
<target>Network settings</target>
@@ -3863,6 +4012,11 @@ This is your link for group %@!</target>
<target>No history</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<target>No network connection</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>No permission to record voice message</target>
@@ -4077,6 +4231,11 @@ This is your link for group %@!</target>
<target>Or show this code</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<target>Other</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve">
<source>PING count</source>
<target>PING count</target>
@@ -4244,11 +4403,6 @@ Error: %@</target>
<target>Possibly, certificate fingerprint in server address is incorrect</target>
<note>server test error</note>
</trans-unit>
<trans-unit id="Post-quantum E2EE" xml:space="preserve">
<source>Post-quantum E2EE</source>
<target>Post-quantum E2EE</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Preserve the last message draft, with attachments." xml:space="preserve">
<source>Preserve the last message draft, with attachments.</source>
<target>Preserve the last message draft, with attachments.</target>
@@ -4284,11 +4438,26 @@ Error: %@</target>
<target>Private filenames</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private message routing" xml:space="preserve">
<source>Private message routing</source>
<target>Private message routing</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private message routing 🚀" xml:space="preserve">
<source>Private message routing 🚀</source>
<target>Private message routing 🚀</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private notes" xml:space="preserve">
<source>Private notes</source>
<target>Private notes</target>
<note>name of notes to self</note>
</trans-unit>
<trans-unit id="Private routing" xml:space="preserve">
<source>Private routing</source>
<target>Private routing</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>Profile and server connections</target>
@@ -4299,6 +4468,11 @@ Error: %@</target>
<target>Profile image</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile images" xml:space="preserve">
<source>Profile images</source>
<target>Profile images</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile name" xml:space="preserve">
<source>Profile name</source>
<target>Profile name</target>
@@ -4339,6 +4513,11 @@ Error: %@</target>
<target>Prohibit messages reactions.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<target>Prohibit sending SimpleX links.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source>
<target>Prohibit sending direct messages to members.</target>
@@ -4359,11 +4538,23 @@ Error: %@</target>
<target>Prohibit sending voice messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect IP address" xml:space="preserve">
<source>Protect IP address</source>
<target>Protect IP address</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect app screen" xml:space="preserve">
<source>Protect app screen</source>
<target>Protect app screen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts.&#10;Enable in *Network &amp; servers* settings." xml:space="preserve">
<source>Protect your IP address from the messaging relays chosen by your contacts.
Enable in *Network &amp; servers* settings.</source>
<target>Protect your IP address from the messaging relays chosen by your contacts.
Enable in *Network &amp; servers* settings.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
<source>Protect your chat profiles with a password!</source>
<target>Protect your chat profiles with a password!</target>
@@ -4484,6 +4675,11 @@ Error: %@</target>
<target>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<target>Recipient(s) can't see who this message is from.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source>
<target>Recipients see updates as you type them.</target>
@@ -4699,6 +4895,11 @@ Error: %@</target>
<target>SMP servers</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safely receive files" xml:space="preserve">
<source>Safely receive files</source>
<target>Safely receive files</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safer groups" xml:space="preserve">
<source>Safer groups</source>
<target>Safer groups</target>
@@ -4784,11 +4985,21 @@ Error: %@</target>
<target>Save welcome message?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<target>Saved</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source>
<target>Saved WebRTC ICE servers will be removed</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<target>Saved from</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source>
<target>Saved message</target>
@@ -4914,6 +5125,16 @@ Error: %@</target>
<target>Send live message</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when IP address is protected and your or destination server does not support private routing." xml:space="preserve">
<source>Send messages directly when IP address is protected and your or destination server does not support private routing.</source>
<target>Send messages directly when IP address is protected and your or destination server does not support private routing.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when your or destination server does not support private routing." xml:space="preserve">
<source>Send messages directly when your or destination server does not support private routing.</source>
<target>Send messages directly when your or destination server does not support private routing.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send notifications" xml:space="preserve">
<source>Send notifications</source>
<target>Send notifications</target>
@@ -5019,6 +5240,11 @@ Error: %@</target>
<target>Sent messages will be deleted after set time.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings." xml:space="preserve">
<source>Server address is incompatible with network settings.</source>
<target>Server address is incompatible with network settings.</target>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>Server requires authorization to create queues, check password</target>
@@ -5034,6 +5260,11 @@ Error: %@</target>
<target>Server test failed!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings." xml:space="preserve">
<source>Server version is incompatible with network settings.</source>
<target>Server version is incompatible with network settings.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>Servers</target>
@@ -5094,6 +5325,11 @@ Error: %@</target>
<target>Settings</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Shape profile images" xml:space="preserve">
<source>Shape profile images</source>
<target>Shape profile images</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share" xml:space="preserve">
<source>Share</source>
<target>Share</target>
@@ -5149,11 +5385,21 @@ Error: %@</target>
<target>Show last messages</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show message status" xml:space="preserve">
<source>Show message status</source>
<target>Show message status</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Show preview</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show → on messages sent via private routing." xml:space="preserve">
<source>Show → on messages sent via private routing.</source>
<target>Show → on messages sent via private routing.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show:" xml:space="preserve">
<source>Show:</source>
<target>Show:</target>
@@ -5212,6 +5458,16 @@ Error: %@</target>
<trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source>
<target>SimpleX links</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<target>SimpleX links are prohibited in this group.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<target>SimpleX links not allowed</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -5249,6 +5505,11 @@ Error: %@</target>
<target>Somebody</target>
<note>notification title</note>
</trans-unit>
<trans-unit id="Square, circle, or anything in between." xml:space="preserve">
<source>Square, circle, or anything in between.</source>
<target>Square, circle, or anything in between.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Start chat" xml:space="preserve">
<source>Start chat</source>
<target>Start chat</target>
@@ -5461,6 +5722,11 @@ It can happen because of some bug or when the connection is compromised.</target
<target>The app can notify you when you receive messages or contact requests - please open settings to enable.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The app will ask to confirm downloads from unknown file servers (except .onion)." xml:space="preserve">
<source>The app will ask to confirm downloads from unknown file servers (except .onion).</source>
<target>The app will ask to confirm downloads from unknown file servers (except .onion).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The attempt to change database passphrase was not completed." xml:space="preserve">
<source>The attempt to change database passphrase was not completed.</source>
<target>The attempt to change database passphrase was not completed.</target>
@@ -5646,6 +5912,11 @@ It can happen because of some bug or when the connection is compromised.</target
<target>To protect timezone, image/voice files use UTC.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect your IP address, private routing uses your SMP servers to deliver messages." xml:space="preserve">
<source>To protect your IP address, private routing uses your SMP servers to deliver messages.</source>
<target>To protect your IP address, private routing uses your SMP servers to deliver messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect your information, turn on SimpleX Lock.&#10;You will be prompted to complete authentication before this feature is enabled." xml:space="preserve">
<source>To protect your information, turn on SimpleX Lock.
You will be prompted to complete authentication before this feature is enabled.</source>
@@ -5738,11 +6009,6 @@ You will be prompted to complete authentication before this feature is enabled.<
<target>Unblock member?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unexpected error: %@" xml:space="preserve">
<source>Unexpected error: %@</source>
<target>Unexpected error: %@</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="Unexpected migration state" xml:space="preserve">
<source>Unexpected migration state</source>
<target>Unexpected migration state</target>
@@ -5788,6 +6054,11 @@ You will be prompted to complete authentication before this feature is enabled.<
<target>Unknown error</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unknown servers!" xml:space="preserve">
<source>Unknown servers!</source>
<target>Unknown servers!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions." xml:space="preserve">
<source>Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions.</source>
<target>Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions.</target>
@@ -5935,6 +6206,16 @@ To connect, please ask your contact to create another connection link and check
<target>Use only local notifications?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use private routing with unknown servers when IP address is not protected." xml:space="preserve">
<source>Use private routing with unknown servers when IP address is not protected.</source>
<target>Use private routing with unknown servers when IP address is not protected.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use private routing with unknown servers." xml:space="preserve">
<source>Use private routing with unknown servers.</source>
<target>Use private routing with unknown servers.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use server" xml:space="preserve">
<source>Use server</source>
<target>Use server</target>
@@ -6050,6 +6331,11 @@ To connect, please ask your contact to create another connection link and check
<target>Voice messages are prohibited in this group.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<target>Voice messages not allowed</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source>
<target>Voice messages prohibited!</target>
@@ -6120,6 +6406,11 @@ To connect, please ask your contact to create another connection link and check
<target>When available</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="When connecting audio and video calls." xml:space="preserve">
<source>When connecting audio and video calls.</source>
<target>When connecting audio and video calls.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="When people request to connect, you can accept or reject it." xml:space="preserve">
<source>When people request to connect, you can accept or reject it.</source>
<target>When people request to connect, you can accept or reject it.</target>
@@ -6130,6 +6421,21 @@ To connect, please ask your contact to create another connection link and check
<target>When you share an incognito profile with somebody, this profile will be used for the groups they invite you to.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<target>WiFi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Will be enabled in direct chats!" xml:space="preserve">
<source>Will be enabled in direct chats!</source>
<target>Will be enabled in direct chats!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<target>Wired ethernet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source>
<target>With encrypted files and media.</target>
@@ -6145,11 +6451,26 @@ To connect, please ask your contact to create another connection link and check
<target>With reduced battery usage.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Without Tor or VPN, your IP address will be visible to file servers." xml:space="preserve">
<source>Without Tor or VPN, your IP address will be visible to file servers.</source>
<target>Without Tor or VPN, your IP address will be visible to file servers.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." xml:space="preserve">
<source>Without Tor or VPN, your IP address will be visible to these XFTP relays: %@.</source>
<target>Without Tor or VPN, your IP address will be visible to these XFTP relays: %@.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wrong database passphrase" xml:space="preserve">
<source>Wrong database passphrase</source>
<target>Wrong database passphrase</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wrong key or unknown connection - most likely this connection is deleted." xml:space="preserve">
<source>Wrong key or unknown connection - most likely this connection is deleted.</source>
<target>Wrong key or unknown connection - most likely this connection is deleted.</target>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Wrong passphrase!" xml:space="preserve">
<source>Wrong passphrase!</source>
<target>Wrong passphrase!</target>
@@ -6613,6 +6934,11 @@ SimpleX servers cannot see your profile.</target>
<target>admin</target>
<note>member role</note>
</trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<target>admins</target>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source>
<target>agreeing encryption for %@…</target>
@@ -6623,6 +6949,11 @@ SimpleX servers cannot see your profile.</target>
<target>agreeing encryption…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<target>all members</target>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve">
<source>always</source>
<target>always</target>
@@ -6953,6 +7284,11 @@ SimpleX servers cannot see your profile.</target>
<target>event happened</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<target>forwarded</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source>
<target>group deleted</target>
@@ -7160,6 +7496,11 @@ SimpleX servers cannot see your profile.</target>
<target>owner</target>
<note>member role</note>
</trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<target>owners</target>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source>
<target>peer-to-peer</target>
@@ -7210,6 +7551,16 @@ SimpleX servers cannot see your profile.</target>
<target>removed you</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<target>saved</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<target>saved from %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve">
<source>sec</source>
<target>sec</target>
@@ -7235,6 +7586,15 @@ SimpleX servers cannot see your profile.</target>
<target>send direct message</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="server queue info: %@&#10;&#10;last received msg: %@" xml:space="preserve">
<source>server queue info: %1$@
last received msg: %2$@</source>
<target>server queue info: %1$@
last received msg: %2$@</target>
<note>queue info</note>
</trans-unit>
<trans-unit id="set new contact address" xml:space="preserve">
<source>set new contact address</source>
<target>set new contact address</target>
@@ -7275,11 +7635,21 @@ SimpleX servers cannot see your profile.</target>
<target>unknown</target>
<note>connection info</note>
</trans-unit>
<trans-unit id="unknown relays" xml:space="preserve">
<source>unknown relays</source>
<target>unknown relays</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unknown status" xml:space="preserve">
<source>unknown status</source>
<target>unknown status</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unprotected" xml:space="preserve">
<source>unprotected</source>
<target>unprotected</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="updated group profile" xml:space="preserve">
<source>updated group profile</source>
<target>updated group profile</target>
@@ -7345,11 +7715,21 @@ SimpleX servers cannot see your profile.</target>
<target>weeks</target>
<note>time unit</note>
</trans-unit>
<trans-unit id="when IP hidden" xml:space="preserve">
<source>when IP hidden</source>
<target>when IP hidden</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="yes" xml:space="preserve">
<source>yes</source>
<target>yes</target>
<note>pref value</note>
</trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<target>you</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>you are invited to group</target>
File diff suppressed because it is too large Load Diff
@@ -685,6 +685,10 @@
<target>Salli katoavat viestit vain, jos kontaktisi sallii sen sinulle.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow downgrade" xml:space="preserve">
<source>Allow downgrade</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow irreversible message deletion only if your contact allows it to you. (24 hours)" xml:space="preserve">
<source>Allow irreversible message deletion only if your contact allows it to you. (24 hours)</source>
<target>Salli peruuttamaton viestien poisto vain, jos kontaktisi sallii ne sinulle. (24 tuntia)</target>
@@ -715,6 +719,10 @@
<target>Salli lähetettyjen viestien peruuttamaton poistaminen. (24 tuntia)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source>
<target>Salli tiedostojen ja median lähettäminen.</target>
@@ -773,6 +781,10 @@
<source>Already joining the group!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Always use private routing." xml:space="preserve">
<source>Always use private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Always use relay" xml:space="preserve">
<source>Always use relay</source>
<target>Käytä aina relettä</target>
@@ -1036,6 +1048,14 @@
<target>Tiedostoa ei voi vastaanottaa</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Capacity exceeded - recipient did not receive previously sent messages." xml:space="preserve">
<source>Capacity exceeded - recipient did not receive previously sent messages.</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve">
<source>Change</source>
<target>Muuta</target>
@@ -1223,6 +1243,10 @@
<target>Vahvista tietokannan päivitykset</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
<source>Confirm files from unknown servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm network settings" xml:space="preserve">
<source>Confirm network settings</source>
<note>No comment provided by engineer.</note>
@@ -1616,6 +1640,10 @@ This is your own one-time link!</source>
<target>Tietokanta siirretään, kun sovellus käynnistyy uudelleen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Debug delivery" xml:space="preserve">
<source>Debug delivery</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Decentralized" xml:space="preserve">
<source>Decentralized</source>
<target>Hajautettu</target>
@@ -1855,6 +1883,10 @@ This cannot be undone!</source>
<source>Desktop devices</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Destination server error: %@" xml:space="preserve">
<source>Destination server error: %@</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Develop" xml:space="preserve">
<source>Develop</source>
<target>Kehitä</target>
@@ -1958,11 +1990,19 @@ This cannot be undone!</source>
<source>Discover via local network</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do NOT send messages directly, even if your or destination server does not support private routing." xml:space="preserve">
<source>Do NOT send messages directly, even if your or destination server does not support private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
<source>Do NOT use SimpleX for emergency calls.</source>
<target>Älä käytä SimpleX-sovellusta hätäpuheluihin.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do NOT use private routing." xml:space="preserve">
<source>Do NOT use private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do it later" xml:space="preserve">
<source>Do it later</source>
<target>Tee myöhemmin</target>
@@ -1992,6 +2032,10 @@ This cannot be undone!</source>
<target>Alenna ja avaa keskustelu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source>
<note>No comment provided by engineer.</note>
@@ -2097,6 +2141,10 @@ This cannot be undone!</source>
<target>Ota itsetuhoava pääsykoodi käyttöön</target>
<note>set passcode view</note>
</trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source>
<target>Salaa</target>
@@ -2239,10 +2287,6 @@ This cannot be undone!</source>
<target>Virhe lisättäessä jäseniä</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error allowing contact PQ encryption" xml:space="preserve">
<source>Error allowing contact PQ encryption</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error changing address" xml:space="preserve">
<source>Error changing address</source>
<target>Virhe osoitteenvaihdossa</target>
@@ -2497,7 +2541,7 @@ This cannot be undone!</source>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>Virhe: %@</target>
<note>No comment provided by engineer.</note>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -2586,6 +2630,10 @@ This cannot be undone!</source>
<target>Tiedosto: %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files" xml:space="preserve">
<source>Files</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files &amp; media" xml:space="preserve">
<source>Files &amp; media</source>
<target>Tiedostot &amp; media</target>
@@ -2601,6 +2649,10 @@ This cannot be undone!</source>
<target>Tiedostot ja media ovat tässä ryhmässä kiellettyjä.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source>
<target>Tiedostot ja media kielletty!</target>
@@ -2664,6 +2716,32 @@ This cannot be undone!</source>
<target>Konsoliin</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forward and save messages" xml:space="preserve">
<source>Forward and save messages</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarding server: %@&#10;Destination server error: %@" xml:space="preserve">
<source>Forwarding server: %1$@
Destination server error: %2$@</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Forwarding server: %@&#10;Error: %@" xml:space="preserve">
<source>Forwarding server: %1$@
Error: %2$@</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<note>No comment provided by engineer.</note>
@@ -2770,6 +2848,10 @@ This cannot be undone!</source>
<target>Ryhmän jäsenet voivat poistaa lähetetyt viestit peruuttamattomasti. (24 tuntia)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source>
<target>Ryhmän jäsenet voivat lähettää suoraviestejä.</target>
@@ -3004,6 +3086,10 @@ This cannot be undone!</source>
<target>Vastauksena</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="In-call sounds" xml:space="preserve">
<source>In-call sounds</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito" xml:space="preserve">
<source>Incognito</source>
<target>Incognito</target>
@@ -3440,11 +3526,19 @@ This is your link for group %@!</source>
<target>Viestien toimituskuittaukset!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message delivery warning" xml:space="preserve">
<source>Message delivery warning</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message draft" xml:space="preserve">
<source>Message draft</source>
<target>Viestiluonnos</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reactions" xml:space="preserve">
<source>Message reactions</source>
<target>Viestireaktiot</target>
@@ -3460,6 +3554,18 @@ This is your link for group %@!</source>
<target>Viestireaktiot ovat kiellettyjä tässä ryhmässä.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing mode" xml:space="preserve">
<source>Message routing mode</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message source remains private." xml:space="preserve">
<source>Message source remains private.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>Viestin teksti</target>
@@ -3564,16 +3670,15 @@ This is your link for group %@!</source>
<target>Lisää parannuksia on tulossa pian!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="More reliable network connection." xml:space="preserve">
<source>More reliable network connection.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Most likely this connection is deleted." xml:space="preserve">
<source>Most likely this connection is deleted.</source>
<target>Todennäköisesti tämä yhteys on poistettu.</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="Most likely this contact has deleted the connection with you." xml:space="preserve">
<source>Most likely this contact has deleted the connection with you.</source>
<target>Todennäköisesti tämä kontakti on poistanut yhteyden sinuun.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Multiple chat profiles" xml:space="preserve">
<source>Multiple chat profiles</source>
<target>Useita keskusteluprofiileja</target>
@@ -3599,6 +3704,18 @@ This is your link for group %@!</source>
<target>Verkko ja palvelimet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network issues - message expired after many attempts to send it." xml:space="preserve">
<source>Network issues - message expired after many attempts to send it.</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Network management" xml:space="preserve">
<source>Network management</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source>
<target>Verkkoasetukset</target>
@@ -3707,6 +3824,10 @@ This is your link for group %@!</source>
<target>Ei historiaa</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>Ei lupaa ääniviestin tallentamiseen</target>
@@ -3911,6 +4032,10 @@ This is your link for group %@!</source>
<source>Or show this code</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve">
<source>PING count</source>
<target>PING-määrä</target>
@@ -4070,10 +4195,6 @@ Error: %@</source>
<target>Palvelimen osoitteen varmenteen sormenjälki on mahdollisesti virheellinen</target>
<note>server test error</note>
</trans-unit>
<trans-unit id="Post-quantum E2EE" xml:space="preserve">
<source>Post-quantum E2EE</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Preserve the last message draft, with attachments." xml:space="preserve">
<source>Preserve the last message draft, with attachments.</source>
<target>Säilytä viimeinen viestiluonnos liitteineen.</target>
@@ -4109,10 +4230,22 @@ Error: %@</source>
<target>Yksityiset tiedostonimet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private message routing" xml:space="preserve">
<source>Private message routing</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private message routing 🚀" xml:space="preserve">
<source>Private message routing 🚀</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private notes" xml:space="preserve">
<source>Private notes</source>
<note>name of notes to self</note>
</trans-unit>
<trans-unit id="Private routing" xml:space="preserve">
<source>Private routing</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>Profiili- ja palvelinyhteydet</target>
@@ -4123,6 +4256,10 @@ Error: %@</source>
<target>Profiilikuva</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile images" xml:space="preserve">
<source>Profile images</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile name" xml:space="preserve">
<source>Profile name</source>
<note>No comment provided by engineer.</note>
@@ -4161,6 +4298,10 @@ Error: %@</source>
<target>Estä viestireaktiot.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source>
<target>Estä suorien viestien lähettäminen jäsenille.</target>
@@ -4181,11 +4322,20 @@ Error: %@</source>
<target>Estä ääniviestien lähettäminen.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect IP address" xml:space="preserve">
<source>Protect IP address</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect app screen" xml:space="preserve">
<source>Protect app screen</source>
<target>Suojaa sovellusnäyttö</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts.&#10;Enable in *Network &amp; servers* settings." xml:space="preserve">
<source>Protect your IP address from the messaging relays chosen by your contacts.
Enable in *Network &amp; servers* settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
<source>Protect your chat profiles with a password!</source>
<target>Suojaa keskusteluprofiilisi salasanalla!</target>
@@ -4302,6 +4452,10 @@ Error: %@</source>
<source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source>
<target>Vastaanottajat näkevät päivitykset, kun kirjoitat niitä.</target>
@@ -4511,6 +4665,10 @@ Error: %@</source>
<target>SMP-palvelimet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safely receive files" xml:space="preserve">
<source>Safely receive files</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safer groups" xml:space="preserve">
<source>Safer groups</source>
<note>No comment provided by engineer.</note>
@@ -4595,11 +4753,19 @@ Error: %@</source>
<target>Tallenna tervetuloviesti?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source>
<target>Tallennetut WebRTC ICE -palvelimet poistetaan</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source>
<note>message info title</note>
@@ -4720,6 +4886,14 @@ Error: %@</source>
<target>Lähetä live-viesti</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when IP address is protected and your or destination server does not support private routing." xml:space="preserve">
<source>Send messages directly when IP address is protected and your or destination server does not support private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when your or destination server does not support private routing." xml:space="preserve">
<source>Send messages directly when your or destination server does not support private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send notifications" xml:space="preserve">
<source>Send notifications</source>
<target>Lähetys ilmoitukset</target>
@@ -4824,6 +4998,10 @@ Error: %@</source>
<target>Lähetetyt viestit poistetaan asetetun ajan kuluttua.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings." xml:space="preserve">
<source>Server address is incompatible with network settings.</source>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>Palvelin vaatii valtuutuksen jonojen luomiseen, tarkista salasana</target>
@@ -4839,6 +5017,10 @@ Error: %@</source>
<target>Palvelintesti epäonnistui!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings." xml:space="preserve">
<source>Server version is incompatible with network settings.</source>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>Palvelimet</target>
@@ -4897,6 +5079,10 @@ Error: %@</source>
<target>Asetukset</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Shape profile images" xml:space="preserve">
<source>Shape profile images</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share" xml:space="preserve">
<source>Share</source>
<target>Jaa</target>
@@ -4950,11 +5136,19 @@ Error: %@</source>
<target>Näytä viimeiset viestit</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show message status" xml:space="preserve">
<source>Show message status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Näytä esikatselu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show → on messages sent via private routing." xml:space="preserve">
<source>Show → on messages sent via private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show:" xml:space="preserve">
<source>Show:</source>
<target>Näytä:</target>
@@ -5013,6 +5207,14 @@ Error: %@</source>
<trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source>
<target>SimpleX-linkit</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -5049,6 +5251,10 @@ Error: %@</source>
<target>Joku</target>
<note>notification title</note>
</trans-unit>
<trans-unit id="Square, circle, or anything in between." xml:space="preserve">
<source>Square, circle, or anything in between.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Start chat" xml:space="preserve">
<source>Start chat</source>
<target>Aloita keskustelu</target>
@@ -5255,6 +5461,10 @@ Tämä voi johtua jostain virheestä tai siitä, että yhteys on vaarantunut.</t
<target>Sovellus voi ilmoittaa sinulle, kun saat viestejä tai yhteydenottopyyntöjä - avaa asetukset ottaaksesi ne käyttöön.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The app will ask to confirm downloads from unknown file servers (except .onion)." xml:space="preserve">
<source>The app will ask to confirm downloads from unknown file servers (except .onion).</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The attempt to change database passphrase was not completed." xml:space="preserve">
<source>The attempt to change database passphrase was not completed.</source>
<target>Tietokannan tunnuslauseen muuttamista ei suoritettu loppuun.</target>
@@ -5431,6 +5641,10 @@ Tämä voi johtua jostain virheestä tai siitä, että yhteys on vaarantunut.</t
<target>Aikavyöhykkeen suojaamiseksi kuva-/äänitiedostot käyttävät UTC:tä.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect your IP address, private routing uses your SMP servers to deliver messages." xml:space="preserve">
<source>To protect your IP address, private routing uses your SMP servers to deliver messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect your information, turn on SimpleX Lock.&#10;You will be prompted to complete authentication before this feature is enabled." xml:space="preserve">
<source>To protect your information, turn on SimpleX Lock.
You will be prompted to complete authentication before this feature is enabled.</source>
@@ -5516,11 +5730,6 @@ Sinua kehotetaan suorittamaan todennus loppuun, ennen kuin tämä ominaisuus ote
<source>Unblock member?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unexpected error: %@" xml:space="preserve">
<source>Unexpected error: %@</source>
<target>Odottamaton virhe: %@</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="Unexpected migration state" xml:space="preserve">
<source>Unexpected migration state</source>
<target>Odottamaton siirtotila</target>
@@ -5566,6 +5775,10 @@ Sinua kehotetaan suorittamaan todennus loppuun, ennen kuin tämä ominaisuus ote
<target>Tuntematon virhe</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unknown servers!" xml:space="preserve">
<source>Unknown servers!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions." xml:space="preserve">
<source>Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions.</source>
<target>Ellet käytä iOS:n puhelinkäyttöliittymää, ota Älä häiritse -tila käyttöön keskeytysten välttämiseksi.</target>
@@ -5706,6 +5919,14 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
<source>Use only local notifications?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use private routing with unknown servers when IP address is not protected." xml:space="preserve">
<source>Use private routing with unknown servers when IP address is not protected.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use private routing with unknown servers." xml:space="preserve">
<source>Use private routing with unknown servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use server" xml:space="preserve">
<source>Use server</source>
<target>Käytä palvelinta</target>
@@ -5813,6 +6034,10 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
<target>Ääniviestit ovat kiellettyjä tässä ryhmässä.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source>
<target>Ääniviestit kielletty!</target>
@@ -5880,6 +6105,10 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
<target>Kun saatavilla</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="When connecting audio and video calls." xml:space="preserve">
<source>When connecting audio and video calls.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="When people request to connect, you can accept or reject it." xml:space="preserve">
<source>When people request to connect, you can accept or reject it.</source>
<target>Kun ihmiset pyytävät yhteyden muodostamista, voit hyväksyä tai hylätä sen.</target>
@@ -5890,6 +6119,18 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
<target>Kun jaat inkognitoprofiilin jonkun kanssa, tätä profiilia käytetään ryhmissä, joihin tämä sinut kutsuu.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Will be enabled in direct chats!" xml:space="preserve">
<source>Will be enabled in direct chats!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source>
<note>No comment provided by engineer.</note>
@@ -5903,11 +6144,23 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
<source>With reduced battery usage.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Without Tor or VPN, your IP address will be visible to file servers." xml:space="preserve">
<source>Without Tor or VPN, your IP address will be visible to file servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." xml:space="preserve">
<source>Without Tor or VPN, your IP address will be visible to these XFTP relays: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wrong database passphrase" xml:space="preserve">
<source>Wrong database passphrase</source>
<target>Väärä tietokannan tunnuslause</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wrong key or unknown connection - most likely this connection is deleted." xml:space="preserve">
<source>Wrong key or unknown connection - most likely this connection is deleted.</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Wrong passphrase!" xml:space="preserve">
<source>Wrong passphrase!</source>
<target>Väärä tunnuslause!</target>
@@ -6353,6 +6606,10 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>ylläpitäjä</target>
<note>member role</note>
</trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source>
<target>salauksesta sovitaan %@:lle…</target>
@@ -6363,6 +6620,10 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>hyväksyy salausta…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve">
<source>always</source>
<target>aina</target>
@@ -6685,6 +6946,10 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>tapahtuma tapahtui</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source>
<target>ryhmä poistettu</target>
@@ -6891,6 +7156,10 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>omistaja</target>
<note>member role</note>
</trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source>
<target>vertais</target>
@@ -6938,6 +7207,14 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>poisti sinut</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve">
<source>sec</source>
<target>sek</target>
@@ -6962,6 +7239,12 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<source>send direct message</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="server queue info: %@&#10;&#10;last received msg: %@" xml:space="preserve">
<source>server queue info: %1$@
last received msg: %2$@</source>
<note>queue info</note>
</trans-unit>
<trans-unit id="set new contact address" xml:space="preserve">
<source>set new contact address</source>
<note>profile update event chat item</note>
@@ -6998,10 +7281,18 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>tuntematon</target>
<note>connection info</note>
</trans-unit>
<trans-unit id="unknown relays" xml:space="preserve">
<source>unknown relays</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unknown status" xml:space="preserve">
<source>unknown status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unprotected" xml:space="preserve">
<source>unprotected</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="updated group profile" xml:space="preserve">
<source>updated group profile</source>
<target>päivitetty ryhmäprofiili</target>
@@ -7065,11 +7356,19 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>viikkoa</target>
<note>time unit</note>
</trans-unit>
<trans-unit id="when IP hidden" xml:space="preserve">
<source>when IP hidden</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="yes" xml:space="preserve">
<source>yes</source>
<target>kyllä</target>
<note>pref value</note>
</trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>sinut on kutsuttu ryhmään</target>
File diff suppressed because it is too large Load Diff
@@ -5311,6 +5311,11 @@ SimpleX servers cannot see your profile.</source>
<target state="translated">ללא היסטוריה</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ and %@" xml:space="preserve" approved="no">
<source>%@ and %@</source>
<target state="translated">%@ ו-%@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
</body>
</file>
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="he" datatype="plaintext">
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -109,6 +109,7 @@
</trans-unit>
<trans-unit id="%@ downloaded" xml:space="preserve">
<source>%@ downloaded</source>
<target>%@ ダウンロード済</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ is connected!" xml:space="preserve">
@@ -133,6 +134,7 @@
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve">
<source>%@ uploaded</source>
<target>%@ アップロード済</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ wants to connect!" xml:space="preserve">
@@ -142,6 +144,7 @@
</trans-unit>
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
<source>%@, %@ and %lld members</source>
<target>%@や%@など%lld人のメンバー</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@, %@ and %lld other members connected" xml:space="preserve">
@@ -211,6 +214,7 @@
</trans-unit>
<trans-unit id="%lld group events" xml:space="preserve">
<source>%lld group events</source>
<target>%lld件のグループイベント</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld members" xml:space="preserve">
@@ -220,10 +224,12 @@
</trans-unit>
<trans-unit id="%lld messages blocked" xml:space="preserve">
<source>%lld messages blocked</source>
<target>%lld件のメッセージをブロック</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld messages blocked by admin" xml:space="preserve">
<source>%lld messages blocked by admin</source>
<target>%lldのメッセージが管理者によりブロック済</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld messages marked deleted" xml:space="preserve">
@@ -308,10 +314,12 @@
</trans-unit>
<trans-unit id="(new)" xml:space="preserve">
<source>(new)</source>
<target>(新規)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="(this device v%@)" xml:space="preserve">
<source>(this device v%@)</source>
<target>(このデバイス v%@)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id=")" xml:space="preserve">
@@ -321,6 +329,7 @@
</trans-unit>
<trans-unit id="**Add contact**: to create a new invitation link, or connect via a link you received." xml:space="preserve">
<source>**Add contact**: to create a new invitation link, or connect via a link you received.</source>
<target>**コンタクトの追加**: 新しい招待リンクを作成するか、受け取ったリンクから接続します。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve">
@@ -330,6 +339,7 @@
</trans-unit>
<trans-unit id="**Create group**: to create a new group." xml:space="preserve">
<source>**Create group**: to create a new group.</source>
<target>**グループ作成**: 新しいグループを作成する。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." xml:space="preserve">
@@ -344,6 +354,7 @@
</trans-unit>
<trans-unit id="**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." xml:space="preserve">
<source>**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection.</source>
<target>**注意**: 2つの端末で同じデータベースを使用すると、セキュリティ保護として、あなたが接続しているメッセージの復号化が解除されます。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**Please note**: you will NOT be able to recover or change passphrase if you lose it." xml:space="preserve">
@@ -363,16 +374,17 @@
</trans-unit>
<trans-unit id="**Warning**: the archive will be removed." xml:space="preserve">
<source>**Warning**: the archive will be removed.</source>
<target>**警告**: アーカイブデータは削除されます。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**e2e encrypted** audio call" xml:space="preserve">
<source>**e2e encrypted** audio call</source>
<target>**e2e 暗号化**音声通話</target>
<target>**エンドツーエンド暗号化済み**音声通話</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**e2e encrypted** video call" xml:space="preserve">
<source>**e2e encrypted** video call</source>
<target>**e2e暗号化**ビデオ通話</target>
<target>**エンドツーエンド暗号化済み**の テレビ電話 通話</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="*bold*" xml:space="preserve">
@@ -422,6 +434,7 @@
</trans-unit>
<trans-unit id="0 sec" xml:space="preserve">
<source>0 sec</source>
<target>0 秒</target>
<note>time to disappear</note>
</trans-unit>
<trans-unit id="0s" xml:space="preserve">
@@ -672,6 +685,7 @@
</trans-unit>
<trans-unit id="All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays." xml:space="preserve">
<source>All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays.</source>
<target>すべての連絡先、会話、ファイルは安全に暗号化され、設定されたXFTPリレーに分割でアップロードされます。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow" xml:space="preserve">
@@ -689,6 +703,10 @@
<target>連絡先が許可している場合のみ消えるメッセージを許可する。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow downgrade" xml:space="preserve">
<source>Allow downgrade</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow irreversible message deletion only if your contact allows it to you. (24 hours)" xml:space="preserve">
<source>Allow irreversible message deletion only if your contact allows it to you. (24 hours)</source>
<target>送信相手も永久メッセージ削除を許可する時のみに許可する。</target>
@@ -716,7 +734,12 @@
</trans-unit>
<trans-unit id="Allow to irreversibly delete sent messages. (24 hours)" xml:space="preserve">
<source>Allow to irreversibly delete sent messages. (24 hours)</source>
<target>送信済みメッセージの永久削除を許可する。</target>
<target>送信済みメッセージの永久削除を許可する。(24時間)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<target>SimpleXリンクの送信を許可。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve">
@@ -751,7 +774,7 @@
</trans-unit>
<trans-unit id="Allow your contacts to irreversibly delete sent messages. (24 hours)" xml:space="preserve">
<source>Allow your contacts to irreversibly delete sent messages. (24 hours)</source>
<target>送信相手が永久メッセージ削除するのを許可する。</target>
<target>送信相手が永久メッセージ削除するのを許可する。(24時間)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow your contacts to send disappearing messages." xml:space="preserve">
@@ -771,10 +794,16 @@
</trans-unit>
<trans-unit id="Already connecting!" xml:space="preserve">
<source>Already connecting!</source>
<target>既に接続中です!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Already joining the group!" xml:space="preserve">
<source>Already joining the group!</source>
<target>すでにグループに参加しています!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Always use private routing." xml:space="preserve">
<source>Always use private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Always use relay" xml:space="preserve">
@@ -799,6 +828,7 @@
</trans-unit>
<trans-unit id="App data migration" xml:space="preserve">
<source>App data migration</source>
<target>アプリデータの移行</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App encrypts new local files (except videos)." xml:space="preserve">
@@ -971,7 +1001,7 @@
</trans-unit>
<trans-unit id="Both you and your contact can irreversibly delete sent messages. (24 hours)" xml:space="preserve">
<source>Both you and your contact can irreversibly delete sent messages. (24 hours)</source>
<target>あなたと連絡相手が送信済みメッセージを永久削除できます。</target>
<target>あなたと連絡相手が送信済みメッセージを永久削除できます。(24時間)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Both you and your contact can make calls." xml:space="preserve">
@@ -1042,6 +1072,14 @@
<target>ファイル受信ができません</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Capacity exceeded - recipient did not receive previously sent messages." xml:space="preserve">
<source>Capacity exceeded - recipient did not receive previously sent messages.</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve">
<source>Change</source>
<target>変更</target>
@@ -1229,6 +1267,10 @@
<target>データベースのアップグレードを確認</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
<source>Confirm files from unknown servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm network settings" xml:space="preserve">
<source>Confirm network settings</source>
<note>No comment provided by engineer.</note>
@@ -1622,6 +1664,10 @@ This is your own one-time link!</source>
<target>データベースはアプリ再起動時に移行されます</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Debug delivery" xml:space="preserve">
<source>Debug delivery</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Decentralized" xml:space="preserve">
<source>Decentralized</source>
<target>分散型</target>
@@ -1861,6 +1907,10 @@ This cannot be undone!</source>
<source>Desktop devices</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Destination server error: %@" xml:space="preserve">
<source>Destination server error: %@</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Develop" xml:space="preserve">
<source>Develop</source>
<target>開発</target>
@@ -1964,11 +2014,19 @@ This cannot be undone!</source>
<source>Discover via local network</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do NOT send messages directly, even if your or destination server does not support private routing." xml:space="preserve">
<source>Do NOT send messages directly, even if your or destination server does not support private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
<source>Do NOT use SimpleX for emergency calls.</source>
<target>緊急通報にSimpleXを使用しないでください。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do NOT use private routing." xml:space="preserve">
<source>Do NOT use private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do it later" xml:space="preserve">
<source>Do it later</source>
<target>後で行う</target>
@@ -1998,6 +2056,10 @@ This cannot be undone!</source>
<target>ダウングレードしてチャットを開く</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source>
<note>No comment provided by engineer.</note>
@@ -2103,6 +2165,10 @@ This cannot be undone!</source>
<target>自己破壊パスコードを有効にする</target>
<note>set passcode view</note>
</trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source>
<target>暗号化する</target>
@@ -2246,10 +2312,6 @@ This cannot be undone!</source>
<target>メンバー追加にエラー発生</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error allowing contact PQ encryption" xml:space="preserve">
<source>Error allowing contact PQ encryption</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error changing address" xml:space="preserve">
<source>Error changing address</source>
<target>アドレス変更にエラー発生</target>
@@ -2504,7 +2566,7 @@ This cannot be undone!</source>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>エラー : %@</target>
<note>No comment provided by engineer.</note>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -2593,6 +2655,10 @@ This cannot be undone!</source>
<target>ファイル: %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files" xml:space="preserve">
<source>Files</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files &amp; media" xml:space="preserve">
<source>Files &amp; media</source>
<target>ファイルとメディア</target>
@@ -2608,6 +2674,10 @@ This cannot be undone!</source>
<target>このグループでは、ファイルとメディアは禁止されています。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source>
<target>ファイルとメディアは禁止されています!</target>
@@ -2671,6 +2741,32 @@ This cannot be undone!</source>
<target>コンソール</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forward and save messages" xml:space="preserve">
<source>Forward and save messages</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarding server: %@&#10;Destination server error: %@" xml:space="preserve">
<source>Forwarding server: %1$@
Destination server error: %2$@</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Forwarding server: %@&#10;Error: %@" xml:space="preserve">
<source>Forwarding server: %1$@
Error: %2$@</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<note>No comment provided by engineer.</note>
@@ -2774,7 +2870,11 @@ This cannot be undone!</source>
</trans-unit>
<trans-unit id="Group members can irreversibly delete sent messages. (24 hours)" xml:space="preserve">
<source>Group members can irreversibly delete sent messages. (24 hours)</source>
<target>グループのメンバーがメッセージを完全削除することができます。</target>
<target>グループのメンバーがメッセージを完全削除することができます。(24時間)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve">
@@ -3011,6 +3111,10 @@ This cannot be undone!</source>
<target>返信先</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="In-call sounds" xml:space="preserve">
<source>In-call sounds</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito" xml:space="preserve">
<source>Incognito</source>
<target>シークレットモード</target>
@@ -3446,11 +3550,19 @@ This is your link for group %@!</source>
<source>Message delivery receipts!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message delivery warning" xml:space="preserve">
<source>Message delivery warning</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message draft" xml:space="preserve">
<source>Message draft</source>
<target>メッセージの下書き</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reactions" xml:space="preserve">
<source>Message reactions</source>
<target>メッセージへのリアクション</target>
@@ -3466,6 +3578,18 @@ This is your link for group %@!</source>
<target>このグループではメッセージへのリアクションは禁止されています。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing mode" xml:space="preserve">
<source>Message routing mode</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message source remains private." xml:space="preserve">
<source>Message source remains private.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>メッセージ内容</target>
@@ -3570,16 +3694,15 @@ This is your link for group %@!</source>
<target>まだまだ改善してまいります!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="More reliable network connection." xml:space="preserve">
<source>More reliable network connection.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Most likely this connection is deleted." xml:space="preserve">
<source>Most likely this connection is deleted.</source>
<target>おそらく、この接続は削除されています。</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="Most likely this contact has deleted the connection with you." xml:space="preserve">
<source>Most likely this contact has deleted the connection with you.</source>
<target>恐らくこの連絡先があなたとの接続を削除しました。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Multiple chat profiles" xml:space="preserve">
<source>Multiple chat profiles</source>
<target>複数チャットのプロフィール</target>
@@ -3605,6 +3728,18 @@ This is your link for group %@!</source>
<target>ネットワークとサーバ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network issues - message expired after many attempts to send it." xml:space="preserve">
<source>Network issues - message expired after many attempts to send it.</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Network management" xml:space="preserve">
<source>Network management</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source>
<target>ネットワーク設定</target>
@@ -3714,6 +3849,10 @@ This is your link for group %@!</source>
<target>履歴はありません</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>音声メッセージを録音する権限がありません</target>
@@ -3818,7 +3957,7 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="Only you can irreversibly delete messages (your contact can mark them for deletion). (24 hours)" xml:space="preserve">
<source>Only you can irreversibly delete messages (your contact can mark them for deletion). (24 hours)</source>
<target>メッセージの完全削除はあなたにしかできません (あなたの連絡先は削除対象とすることができます)。</target>
<target>メッセージの完全削除はあなたにしかできません (あなたの連絡先は削除対象とすることができます)。(24時間)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only you can make calls." xml:space="preserve">
@@ -3843,7 +3982,7 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="Only your contact can irreversibly delete messages (you can mark them for deletion). (24 hours)" xml:space="preserve">
<source>Only your contact can irreversibly delete messages (you can mark them for deletion). (24 hours)</source>
<target>メッセージを完全削除できるのはあなたの連絡相手だけです (あなたは削除対象とすることができます)。</target>
<target>メッセージを完全削除できるのはあなたの連絡相手だけです (あなたは削除対象とすることができます)。(24時間)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only your contact can make calls." xml:space="preserve">
@@ -3919,6 +4058,10 @@ This is your link for group %@!</source>
<source>Or show this code</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve">
<source>PING count</source>
<target>PING回数</target>
@@ -4078,10 +4221,6 @@ Error: %@</source>
<target>サーバアドレスの証明証IDが正しくないかもしれません</target>
<note>server test error</note>
</trans-unit>
<trans-unit id="Post-quantum E2EE" xml:space="preserve">
<source>Post-quantum E2EE</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Preserve the last message draft, with attachments." xml:space="preserve">
<source>Preserve the last message draft, with attachments.</source>
<target>添付を含めて、下書きを保存する。</target>
@@ -4117,10 +4256,22 @@ Error: %@</source>
<target>プライベートなファイル名</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private message routing" xml:space="preserve">
<source>Private message routing</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private message routing 🚀" xml:space="preserve">
<source>Private message routing 🚀</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private notes" xml:space="preserve">
<source>Private notes</source>
<note>name of notes to self</note>
</trans-unit>
<trans-unit id="Private routing" xml:space="preserve">
<source>Private routing</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>プロフィールとサーバ接続</target>
@@ -4131,6 +4282,10 @@ Error: %@</source>
<target>プロフィール画像</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile images" xml:space="preserve">
<source>Profile images</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile name" xml:space="preserve">
<source>Profile name</source>
<note>No comment provided by engineer.</note>
@@ -4169,6 +4324,10 @@ Error: %@</source>
<target>メッセージへのリアクションは禁止されています。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source>
<target>メンバー間のダイレクトメッセージを使用禁止にする。</target>
@@ -4189,11 +4348,20 @@ Error: %@</source>
<target>音声メッセージを使用禁止にする。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect IP address" xml:space="preserve">
<source>Protect IP address</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect app screen" xml:space="preserve">
<source>Protect app screen</source>
<target>アプリ画面を守る</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts.&#10;Enable in *Network &amp; servers* settings." xml:space="preserve">
<source>Protect your IP address from the messaging relays chosen by your contacts.
Enable in *Network &amp; servers* settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
<source>Protect your chat profiles with a password!</source>
<target>チャットのプロフィールをパスワードで保護します!</target>
@@ -4309,6 +4477,10 @@ Error: %@</source>
<source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source>
<target>受信者には、入力時に更新内容が表示されます。</target>
@@ -4518,6 +4690,10 @@ Error: %@</source>
<target>SMPサーバ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safely receive files" xml:space="preserve">
<source>Safely receive files</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safer groups" xml:space="preserve">
<source>Safer groups</source>
<note>No comment provided by engineer.</note>
@@ -4602,11 +4778,19 @@ Error: %@</source>
<target>ウェルカムメッセージを保存しますか?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source>
<target>保存されたWebRTC ICEサーバは削除されます</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source>
<note>message info title</note>
@@ -4727,6 +4911,14 @@ Error: %@</source>
<target>ライブメッセージを送信</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when IP address is protected and your or destination server does not support private routing." xml:space="preserve">
<source>Send messages directly when IP address is protected and your or destination server does not support private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when your or destination server does not support private routing." xml:space="preserve">
<source>Send messages directly when your or destination server does not support private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send notifications" xml:space="preserve">
<source>Send notifications</source>
<target>通知を送信する</target>
@@ -4824,6 +5016,10 @@ Error: %@</source>
<target>一定時間が経ったら送信されたメッセージが削除されます。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings." xml:space="preserve">
<source>Server address is incompatible with network settings.</source>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>キューを作成するにはサーバーの認証が必要です。パスワードを確認してください</target>
@@ -4839,6 +5035,10 @@ Error: %@</source>
<target>サーバテスト失敗!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings." xml:space="preserve">
<source>Server version is incompatible with network settings.</source>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>サーバ</target>
@@ -4897,6 +5097,10 @@ Error: %@</source>
<target>設定</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Shape profile images" xml:space="preserve">
<source>Shape profile images</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share" xml:space="preserve">
<source>Share</source>
<target>共有する</target>
@@ -4950,11 +5154,19 @@ Error: %@</source>
<target>最新のメッセージを表示</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show message status" xml:space="preserve">
<source>Show message status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>プレビューを表示</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show → on messages sent via private routing." xml:space="preserve">
<source>Show → on messages sent via private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show:" xml:space="preserve">
<source>Show:</source>
<target>表示する:</target>
@@ -5013,6 +5225,14 @@ Error: %@</source>
<trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source>
<target>SimpleXリンク</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -5050,6 +5270,10 @@ Error: %@</source>
<target>誰か</target>
<note>notification title</note>
</trans-unit>
<trans-unit id="Square, circle, or anything in between." xml:space="preserve">
<source>Square, circle, or anything in between.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Start chat" xml:space="preserve">
<source>Start chat</source>
<target>チャットを開始する</target>
@@ -5256,6 +5480,10 @@ It can happen because of some bug or when the connection is compromised.</source
<target>アプリは、メッセージや連絡先のリクエストを受信したときに通知することができます - 設定を開いて有効にしてください。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The app will ask to confirm downloads from unknown file servers (except .onion)." xml:space="preserve">
<source>The app will ask to confirm downloads from unknown file servers (except .onion).</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The attempt to change database passphrase was not completed." xml:space="preserve">
<source>The attempt to change database passphrase was not completed.</source>
<target>データベースのパスフレーズ変更が完了してません。</target>
@@ -5431,6 +5659,10 @@ It can happen because of some bug or when the connection is compromised.</source
<target>時間帯を漏らさないために、画像と音声ファイルはUTCを使います。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect your IP address, private routing uses your SMP servers to deliver messages." xml:space="preserve">
<source>To protect your IP address, private routing uses your SMP servers to deliver messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect your information, turn on SimpleX Lock.&#10;You will be prompted to complete authentication before this feature is enabled." xml:space="preserve">
<source>To protect your information, turn on SimpleX Lock.
You will be prompted to complete authentication before this feature is enabled.</source>
@@ -5516,11 +5748,6 @@ You will be prompted to complete authentication before this feature is enabled.<
<source>Unblock member?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unexpected error: %@" xml:space="preserve">
<source>Unexpected error: %@</source>
<target>予期しないエラー: %@</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="Unexpected migration state" xml:space="preserve">
<source>Unexpected migration state</source>
<target>予期しない移行状態</target>
@@ -5566,6 +5793,10 @@ You will be prompted to complete authentication before this feature is enabled.<
<target>不明なエラー</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unknown servers!" xml:space="preserve">
<source>Unknown servers!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions." xml:space="preserve">
<source>Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions.</source>
<target>iOS 通話インターフェイスを使用しない場合は、中断を避けるために「おやすみモード」を有効にしてください。</target>
@@ -5706,6 +5937,14 @@ To connect, please ask your contact to create another connection link and check
<source>Use only local notifications?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use private routing with unknown servers when IP address is not protected." xml:space="preserve">
<source>Use private routing with unknown servers when IP address is not protected.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use private routing with unknown servers." xml:space="preserve">
<source>Use private routing with unknown servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use server" xml:space="preserve">
<source>Use server</source>
<target>サーバを使う</target>
@@ -5813,6 +6052,10 @@ To connect, please ask your contact to create another connection link and check
<target>このグループでは音声メッセージが使用禁止です。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source>
<target>音声メッセージは使用禁止です!</target>
@@ -5880,6 +6123,10 @@ To connect, please ask your contact to create another connection link and check
<target>利用可能時に</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="When connecting audio and video calls." xml:space="preserve">
<source>When connecting audio and video calls.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="When people request to connect, you can accept or reject it." xml:space="preserve">
<source>When people request to connect, you can accept or reject it.</source>
<target>接続が要求されたら、それを受け入れるか拒否するかを選択できます。</target>
@@ -5890,6 +6137,18 @@ To connect, please ask your contact to create another connection link and check
<target>連絡相手にシークレットモードのプロフィールを共有すると、その連絡相手に招待されたグループでも同じプロフィールが使われます。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Will be enabled in direct chats!" xml:space="preserve">
<source>Will be enabled in direct chats!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source>
<note>No comment provided by engineer.</note>
@@ -5903,11 +6162,23 @@ To connect, please ask your contact to create another connection link and check
<source>With reduced battery usage.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Without Tor or VPN, your IP address will be visible to file servers." xml:space="preserve">
<source>Without Tor or VPN, your IP address will be visible to file servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." xml:space="preserve">
<source>Without Tor or VPN, your IP address will be visible to these XFTP relays: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wrong database passphrase" xml:space="preserve">
<source>Wrong database passphrase</source>
<target>データベースのパスフレーズが違います</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wrong key or unknown connection - most likely this connection is deleted." xml:space="preserve">
<source>Wrong key or unknown connection - most likely this connection is deleted.</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Wrong passphrase!" xml:space="preserve">
<source>Wrong passphrase!</source>
<target>パスフレーズが違います!</target>
@@ -6353,6 +6624,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>管理者</target>
<note>member role</note>
</trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source>
<target>%@の暗号化に同意しています…</target>
@@ -6363,6 +6638,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>暗号化に同意しています…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve">
<source>always</source>
<target>常に</target>
@@ -6685,6 +6964,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>イベント発生</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source>
<target>グループ削除済み</target>
@@ -6891,6 +7174,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>オーナー</target>
<note>member role</note>
</trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source>
<target>P2P</target>
@@ -6938,6 +7225,14 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>あなたを除名しました</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve">
<source>sec</source>
<target>秒</target>
@@ -6962,6 +7257,12 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<source>send direct message</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="server queue info: %@&#10;&#10;last received msg: %@" xml:space="preserve">
<source>server queue info: %1$@
last received msg: %2$@</source>
<note>queue info</note>
</trans-unit>
<trans-unit id="set new contact address" xml:space="preserve">
<source>set new contact address</source>
<note>profile update event chat item</note>
@@ -6998,10 +7299,18 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>不明</target>
<note>connection info</note>
</trans-unit>
<trans-unit id="unknown relays" xml:space="preserve">
<source>unknown relays</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unknown status" xml:space="preserve">
<source>unknown status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unprotected" xml:space="preserve">
<source>unprotected</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="updated group profile" xml:space="preserve">
<source>updated group profile</source>
<target>グループプロフィールを更新しました</target>
@@ -7065,11 +7374,19 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>週</target>
<note>time unit</note>
</trans-unit>
<trans-unit id="when IP hidden" xml:space="preserve">
<source>when IP hidden</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="yes" xml:space="preserve">
<source>yes</source>
<target>はい</target>
<note>pref value</note>
</trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>グループ招待が届きました</target>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -15,32 +15,39 @@
Available in v5.1</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id=" " xml:space="preserve">
<trans-unit id=" " xml:space="preserve" approved="no">
<source> </source>
<target state="translated"> </target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id=" " xml:space="preserve">
<trans-unit id=" " xml:space="preserve" approved="no">
<source> </source>
<target state="translated"> </target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id=" " xml:space="preserve">
<trans-unit id=" " xml:space="preserve" approved="no">
<source> </source>
<target state="translated"> </target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id=" (" xml:space="preserve">
<trans-unit id=" (" xml:space="preserve" approved="no">
<source> (</source>
<target state="translated"> (</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id=" (can be copied)" xml:space="preserve">
<trans-unit id=" (can be copied)" xml:space="preserve" approved="no">
<source> (can be copied)</source>
<target state="translated"> .(pode ser copiado)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="!1 colored!" xml:space="preserve">
<trans-unit id="!1 colored!" xml:space="preserve" approved="no">
<source>!1 colored!</source>
<target state="translated">!1 colorido!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="#secret#" xml:space="preserve">
<trans-unit id="#secret#" xml:space="preserve" approved="no">
<source>#secret#</source>
<target state="translated">#secreto#</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@" xml:space="preserve">
@@ -55,48 +62,59 @@ Available in v5.1</source>
<source>%@ / %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ is connected!" xml:space="preserve">
<trans-unit id="%@ is connected!" xml:space="preserve" approved="no">
<source>%@ is connected!</source>
<target state="translated">%@ está conectado!</target>
<note>notification title</note>
</trans-unit>
<trans-unit id="%@ is not verified" xml:space="preserve">
<trans-unit id="%@ is not verified" xml:space="preserve" approved="no">
<source>%@ is not verified</source>
<target state="translated">%@ não foi verificado</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ is verified" xml:space="preserve">
<trans-unit id="%@ is verified" xml:space="preserve" approved="no">
<source>%@ is verified</source>
<target state="translated">%@ foi verificado</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ servers" xml:space="preserve">
<trans-unit id="%@ servers" xml:space="preserve" approved="no">
<source>%@ servers</source>
<target state="translated">%@ servidores</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ wants to connect!" xml:space="preserve">
<trans-unit id="%@ wants to connect!" xml:space="preserve" approved="no">
<source>%@ wants to connect!</source>
<target state="translated">%@ quer se conectar!</target>
<note>notification title</note>
</trans-unit>
<trans-unit id="%d days" xml:space="preserve">
<trans-unit id="%d days" xml:space="preserve" approved="no">
<source>%d days</source>
<target state="translated">%d dias</target>
<note>message ttl</note>
</trans-unit>
<trans-unit id="%d hours" xml:space="preserve">
<trans-unit id="%d hours" xml:space="preserve" approved="no">
<source>%d hours</source>
<target state="translated">%d horas</target>
<note>message ttl</note>
</trans-unit>
<trans-unit id="%d min" xml:space="preserve">
<trans-unit id="%d min" xml:space="preserve" approved="no">
<source>%d min</source>
<target state="translated">%d minuto</target>
<note>message ttl</note>
</trans-unit>
<trans-unit id="%d months" xml:space="preserve">
<trans-unit id="%d months" xml:space="preserve" approved="no">
<source>%d months</source>
<target state="translated">%d meses</target>
<note>message ttl</note>
</trans-unit>
<trans-unit id="%d sec" xml:space="preserve">
<trans-unit id="%d sec" xml:space="preserve" approved="no">
<source>%d sec</source>
<target state="translated">%d segundo</target>
<note>message ttl</note>
</trans-unit>
<trans-unit id="%d skipped message(s)" xml:space="preserve">
<trans-unit id="%d skipped message(s)" xml:space="preserve" approved="no">
<source>%d skipped message(s)</source>
<target state="translated">%d mensagem(s) ignorada(s)</target>
<note>integrity error chat item</note>
</trans-unit>
<trans-unit id="%lld" xml:space="preserve">
@@ -107,28 +125,34 @@ Available in v5.1</source>
<source>%lld %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld contact(s) selected" xml:space="preserve">
<trans-unit id="%lld contact(s) selected" xml:space="preserve" approved="no">
<source>%lld contact(s) selected</source>
<target state="translated">%lld contato(s) selecionado(s)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld file(s) with total size of %@" xml:space="preserve">
<trans-unit id="%lld file(s) with total size of %@" xml:space="preserve" approved="no">
<source>%lld file(s) with total size of %@</source>
<target state="translated">%lld arquivo(s) com tamanho total de %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld members" xml:space="preserve">
<trans-unit id="%lld members" xml:space="preserve" approved="no">
<source>%lld members</source>
<target state="translated">%lld membros</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld minutes" xml:space="preserve">
<trans-unit id="%lld minutes" xml:space="preserve" approved="no">
<source>%lld minutes</source>
<target state="translated">%lld minutos</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld second(s)" xml:space="preserve">
<trans-unit id="%lld second(s)" xml:space="preserve" approved="no">
<source>%lld second(s)</source>
<target state="translated">%lld segundo(s)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld seconds" xml:space="preserve">
<trans-unit id="%lld seconds" xml:space="preserve" approved="no">
<source>%lld seconds</source>
<target state="translated">%lld segundos</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lldd" xml:space="preserve">
@@ -159,12 +183,14 @@ Available in v5.1</source>
<source>%lldw</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%u messages failed to decrypt." xml:space="preserve">
<trans-unit id="%u messages failed to decrypt." xml:space="preserve" approved="no">
<source>%u messages failed to decrypt.</source>
<target state="translated">%u mensagens não foram descriptografadas.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%u messages skipped." xml:space="preserve">
<trans-unit id="%u messages skipped." xml:space="preserve" approved="no">
<source>%u messages skipped.</source>
<target state="translated">%u mensagens ignoradas.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="(" xml:space="preserve">
@@ -175,48 +201,56 @@ Available in v5.1</source>
<source>)</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve">
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve" approved="no">
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
<target state="translated">**Adicionar novo contato**: para criar seu QR Code único ou link para seu contato.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**Create link / QR code** for your contact to use." xml:space="preserve">
<source>**Create link / QR code** for your contact to use.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." xml:space="preserve">
<trans-unit id="**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." xml:space="preserve" approved="no">
<source>**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have.</source>
<target state="translated">**Mais privado**: verifique novas mensagens a cada 20 minutos. O token do dispositivo é compartilhado com o servidor SimpleX Chat, mas não com quantos contatos ou mensagens você possui.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." xml:space="preserve">
<trans-unit id="**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." xml:space="preserve" approved="no">
<source>**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app).</source>
<target state="translated">**Totalmente privado**: não use o servidor de notificações do SimpleX Chat, verifique as mensagens periodicamente em segundo plano (depende da frequência com que você usa o aplicativo).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**Paste received link** or open it in the browser and tap **Open in mobile app**." xml:space="preserve">
<source>**Paste received link** or open it in the browser and tap **Open in mobile app**.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**Please note**: you will NOT be able to recover or change passphrase if you lose it." xml:space="preserve">
<trans-unit id="**Please note**: you will NOT be able to recover or change passphrase if you lose it." xml:space="preserve" approved="no">
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
<target state="translated">**Atenção**: Você NÃO poderá recuperar ou alterar a senha caso a perca.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." xml:space="preserve">
<trans-unit id="**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from." xml:space="preserve" approved="no">
<source>**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from.</source>
<target state="translated">**Recomendado**: O token do dispositivo e as notificações são enviados ao servidor de notificação do SimpleX Chat, mas não o conteúdo, o tamanho da mensagem ou de quem ela é.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**Scan QR code**: to connect to your contact in person or via video call." xml:space="preserve">
<source>**Scan QR code**: to connect to your contact in person or via video call.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**Warning**: Instant push notifications require passphrase saved in Keychain." xml:space="preserve">
<trans-unit id="**Warning**: Instant push notifications require passphrase saved in Keychain." xml:space="preserve" approved="no">
<source>**Warning**: Instant push notifications require passphrase saved in Keychain.</source>
<target state="translated">**Aviso**: As notificações push instantâneas exigem uma senha salva nas Chaves.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**e2e encrypted** audio call" xml:space="preserve">
<trans-unit id="**e2e encrypted** audio call" xml:space="preserve" approved="no">
<source>**e2e encrypted** audio call</source>
<target state="translated">** Criptografado e2e** chamada de áudio</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**e2e encrypted** video call" xml:space="preserve">
<trans-unit id="**e2e encrypted** video call" xml:space="preserve" approved="no">
<source>**e2e encrypted** video call</source>
<target state="translated">**Criptografado e2e** chamada de vídeo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="*bold*" xml:space="preserve">
@@ -353,144 +387,175 @@ Available in v5.1</source>
<source>All group members will remain connected.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you." xml:space="preserve">
<trans-unit id="All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you." xml:space="preserve" approved="no">
<source>All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you.</source>
<target state="translated">Todas as mensagens serão apagadas isso não pode ser desfeito! As mensagens serão apagadas SOMENTE para você.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected" xml:space="preserve">
<source>All your contacts will remain connected</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow" xml:space="preserve">
<trans-unit id="Allow" xml:space="preserve" approved="no">
<source>Allow</source>
<target state="translated">Permitir</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow calls only if your contact allows them." xml:space="preserve">
<trans-unit id="Allow calls only if your contact allows them." xml:space="preserve" approved="no">
<source>Allow calls only if your contact allows them.</source>
<target state="translated">Permita chamadas somente se seu contato permitir.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow disappearing messages only if your contact allows it to you." xml:space="preserve">
<trans-unit id="Allow disappearing messages only if your contact allows it to you." xml:space="preserve" approved="no">
<source>Allow disappearing messages only if your contact allows it to you.</source>
<target state="translated">Permita o desaparecimento de mensagens somente se o seu contato permitir.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow irreversible message deletion only if your contact allows it to you." xml:space="preserve">
<source>Allow irreversible message deletion only if your contact allows it to you.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow sending direct messages to members." xml:space="preserve">
<trans-unit id="Allow sending direct messages to members." xml:space="preserve" approved="no">
<source>Allow sending direct messages to members.</source>
<target state="translated">Permitir o envio de mensagens diretas aos membros.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow sending disappearing messages." xml:space="preserve">
<trans-unit id="Allow sending disappearing messages." xml:space="preserve" approved="no">
<source>Allow sending disappearing messages.</source>
<target state="translated">Permitir o envio de mensagens que desaparecem.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to irreversibly delete sent messages." xml:space="preserve">
<source>Allow to irreversibly delete sent messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send voice messages." xml:space="preserve">
<trans-unit id="Allow to send voice messages." xml:space="preserve" approved="no">
<source>Allow to send voice messages.</source>
<target state="translated">Permitir enviar mensagens de voz.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow voice messages only if your contact allows them." xml:space="preserve">
<trans-unit id="Allow voice messages only if your contact allows them." xml:space="preserve" approved="no">
<source>Allow voice messages only if your contact allows them.</source>
<target state="translated">Permita mensagens de voz apenas se o seu contato permitir.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow voice messages?" xml:space="preserve">
<trans-unit id="Allow voice messages?" xml:space="preserve" approved="no">
<source>Allow voice messages?</source>
<target state="translated">Permitir mensagens de voz?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow your contacts to call you." xml:space="preserve">
<trans-unit id="Allow your contacts to call you." xml:space="preserve" approved="no">
<source>Allow your contacts to call you.</source>
<target state="translated">Permita que seus contatos liguem para você.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow your contacts to irreversibly delete sent messages." xml:space="preserve">
<source>Allow your contacts to irreversibly delete sent messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow your contacts to send disappearing messages." xml:space="preserve">
<trans-unit id="Allow your contacts to send disappearing messages." xml:space="preserve" approved="no">
<source>Allow your contacts to send disappearing messages.</source>
<target state="translated">Permita que seus contatos enviem mensagens que desaparecem.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow your contacts to send voice messages." xml:space="preserve">
<trans-unit id="Allow your contacts to send voice messages." xml:space="preserve" approved="no">
<source>Allow your contacts to send voice messages.</source>
<target state="translated">Permita que seus contatos enviem mensagens de voz.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Already connected?" xml:space="preserve">
<trans-unit id="Already connected?" xml:space="preserve" approved="no">
<source>Already connected?</source>
<target state="translated">Já conectado?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Always use relay" xml:space="preserve">
<trans-unit id="Always use relay" xml:space="preserve" approved="no">
<source>Always use relay</source>
<target state="translated">Sempre usar retransmissão</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Answer call" xml:space="preserve">
<trans-unit id="Answer call" xml:space="preserve" approved="no">
<source>Answer call</source>
<target state="translated">Atender chamada</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App build: %@" xml:space="preserve">
<trans-unit id="App build: %@" xml:space="preserve" approved="no">
<source>App build: %@</source>
<target state="translated">Versão do Aplicativo: %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App icon" xml:space="preserve">
<trans-unit id="App icon" xml:space="preserve" approved="no">
<source>App icon</source>
<target state="translated">Ícone do Aplicativo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App passcode" xml:space="preserve">
<trans-unit id="App passcode" xml:space="preserve" approved="no">
<source>App passcode</source>
<target state="translated">Senha do Aplicativo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App version" xml:space="preserve">
<trans-unit id="App version" xml:space="preserve" approved="no">
<source>App version</source>
<target state="translated">Versão do Aplicativo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App version: v%@" xml:space="preserve">
<trans-unit id="App version: v%@" xml:space="preserve" approved="no">
<source>App version: v%@</source>
<target state="translated">Versão do Aplicativo: v%@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Appearance" xml:space="preserve">
<trans-unit id="Appearance" xml:space="preserve" approved="no">
<source>Appearance</source>
<target state="translated">Aparência</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Attach" xml:space="preserve">
<trans-unit id="Attach" xml:space="preserve" approved="no">
<source>Attach</source>
<target state="translated">Anexar</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Audio &amp; video calls" xml:space="preserve">
<trans-unit id="Audio &amp; video calls" xml:space="preserve" approved="no">
<source>Audio &amp; video calls</source>
<target state="translated">Chamadas de áudio e vídeo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Audio and video calls" xml:space="preserve">
<trans-unit id="Audio and video calls" xml:space="preserve" approved="no">
<source>Audio and video calls</source>
<target state="translated">Chamadas de áudio e vídeo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Audio/video calls" xml:space="preserve">
<trans-unit id="Audio/video calls" xml:space="preserve" approved="no">
<source>Audio/video calls</source>
<target state="translated">Chamadas de áudio/vídeo</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="Audio/video calls are prohibited." xml:space="preserve">
<trans-unit id="Audio/video calls are prohibited." xml:space="preserve" approved="no">
<source>Audio/video calls are prohibited.</source>
<target state="translated">Chamadas de áudio/vídeo são proibidas.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Authentication cancelled" xml:space="preserve">
<trans-unit id="Authentication cancelled" xml:space="preserve" approved="no">
<source>Authentication cancelled</source>
<target state="translated">Autenticação cancelada</target>
<note>PIN entry</note>
</trans-unit>
<trans-unit id="Authentication failed" xml:space="preserve">
<trans-unit id="Authentication failed" xml:space="preserve" approved="no">
<source>Authentication failed</source>
<target state="translated">Falha na autenticação</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Authentication is required before the call is connected, but you may miss calls." xml:space="preserve">
<trans-unit id="Authentication is required before the call is connected, but you may miss calls." xml:space="preserve" approved="no">
<source>Authentication is required before the call is connected, but you may miss calls.</source>
<target state="translated">A autenticação é necessária antes que a chamada seja conectada, mas você pode perder chamadas.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Authentication unavailable" xml:space="preserve">
<trans-unit id="Authentication unavailable" xml:space="preserve" approved="no">
<source>Authentication unavailable</source>
<target state="translated">Autenticação indisponível</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Auto-accept contact requests" xml:space="preserve">
<trans-unit id="Auto-accept contact requests" xml:space="preserve" approved="no">
<source>Auto-accept contact requests</source>
<target state="translated">Aceitar solicitações de contato automaticamente</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Auto-accept images" xml:space="preserve">
@@ -545,160 +610,197 @@ Available in v5.1</source>
<source>Can't delete user profile!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't invite contact!" xml:space="preserve">
<trans-unit id="Can't invite contact!" xml:space="preserve" approved="no">
<source>Can't invite contact!</source>
<target state="translated">Não é possível convidar contato!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't invite contacts!" xml:space="preserve">
<trans-unit id="Can't invite contacts!" xml:space="preserve" approved="no">
<source>Can't invite contacts!</source>
<target state="translated">Não é possível convidar contatos!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cancel" xml:space="preserve">
<trans-unit id="Cancel" xml:space="preserve" approved="no">
<source>Cancel</source>
<target state="translated">Cancelar</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cannot access keychain to save database password" xml:space="preserve">
<trans-unit id="Cannot access keychain to save database password" xml:space="preserve" approved="no">
<source>Cannot access keychain to save database password</source>
<target state="translated">Não é possível acessar as chaves para salvar a senha do banco de dados</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cannot receive file" xml:space="preserve">
<trans-unit id="Cannot receive file" xml:space="preserve" approved="no">
<source>Cannot receive file</source>
<target state="translated">Não é possível receber o arquivo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve">
<trans-unit id="Change" xml:space="preserve" approved="no">
<source>Change</source>
<target state="translated">Mudar</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change passcode" xml:space="preserve">
<source>Change passcode</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change database passphrase?" xml:space="preserve">
<trans-unit id="Change database passphrase?" xml:space="preserve" approved="no">
<source>Change database passphrase?</source>
<target state="translated">Alterar a senha do banco de dados?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change lock mode" xml:space="preserve">
<trans-unit id="Change lock mode" xml:space="preserve" approved="no">
<source>Change lock mode</source>
<target state="translated">Alterar modo de bloqueio</target>
<note>authentication reason</note>
</trans-unit>
<trans-unit id="Change member role?" xml:space="preserve">
<trans-unit id="Change member role?" xml:space="preserve" approved="no">
<source>Change member role?</source>
<target state="translated">Alterar a função do membro?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change passcode" xml:space="preserve">
<trans-unit id="Change passcode" xml:space="preserve" approved="no">
<source>Change passcode</source>
<target state="translated">Alterar senha</target>
<note>authentication reason</note>
</trans-unit>
<trans-unit id="Change receiving address" xml:space="preserve">
<trans-unit id="Change receiving address" xml:space="preserve" approved="no">
<source>Change receiving address</source>
<target state="translated">Alterar endereço de recebimento</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change receiving address?" xml:space="preserve">
<trans-unit id="Change receiving address?" xml:space="preserve" approved="no">
<source>Change receiving address?</source>
<target state="translated">Alterar endereço de recebimento?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change role" xml:space="preserve">
<trans-unit id="Change role" xml:space="preserve" approved="no">
<source>Change role</source>
<target state="translated">Mudar função</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat archive" xml:space="preserve">
<trans-unit id="Chat archive" xml:space="preserve" approved="no">
<source>Chat archive</source>
<target state="translated">Arquivar conversa</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat console" xml:space="preserve">
<trans-unit id="Chat console" xml:space="preserve" approved="no">
<source>Chat console</source>
<target state="translated">Console da conversa</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database" xml:space="preserve">
<trans-unit id="Chat database" xml:space="preserve" approved="no">
<source>Chat database</source>
<target state="translated">Banco de Dados da conversa</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database deleted" xml:space="preserve">
<trans-unit id="Chat database deleted" xml:space="preserve" approved="no">
<source>Chat database deleted</source>
<target state="translated">Banco de Dados da conversa apagado</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database imported" xml:space="preserve">
<trans-unit id="Chat database imported" xml:space="preserve" approved="no">
<source>Chat database imported</source>
<target state="translated">Banco de Dados da conversa importado</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat is running" xml:space="preserve">
<trans-unit id="Chat is running" xml:space="preserve" approved="no">
<source>Chat is running</source>
<target state="translated">A conversa está em execução</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat is stopped" xml:space="preserve">
<source>Chat is stopped</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat preferences" xml:space="preserve">
<trans-unit id="Chat preferences" xml:space="preserve" approved="no">
<source>Chat preferences</source>
<target state="translated">Preferências da conversa</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
<trans-unit id="Chats" xml:space="preserve" approved="no">
<source>Chats</source>
<target state="translated">Conversas</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check server address and try again." xml:space="preserve">
<trans-unit id="Check server address and try again." xml:space="preserve" approved="no">
<source>Check server address and try again.</source>
<target state="translated">Verifique o endereço do servidor e tente novamente.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chinese and Spanish interface" xml:space="preserve">
<trans-unit id="Chinese and Spanish interface" xml:space="preserve" approved="no">
<source>Chinese and Spanish interface</source>
<target state="translated">Interface em Chinês e Espanhol</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Choose file" xml:space="preserve">
<trans-unit id="Choose file" xml:space="preserve" approved="no">
<source>Choose file</source>
<target state="translated">Escolher arquivo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Choose from library" xml:space="preserve">
<trans-unit id="Choose from library" xml:space="preserve" approved="no">
<source>Choose from library</source>
<target state="translated">Escolha da biblioteca</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Clear" xml:space="preserve">
<trans-unit id="Clear" xml:space="preserve" approved="no">
<source>Clear</source>
<target state="translated">Limpar</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Clear conversation" xml:space="preserve">
<trans-unit id="Clear conversation" xml:space="preserve" approved="no">
<source>Clear conversation</source>
<target state="translated">Limpar conversa</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Clear conversation?" xml:space="preserve">
<trans-unit id="Clear conversation?" xml:space="preserve" approved="no">
<source>Clear conversation?</source>
<target state="translated">Limpar conversa?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Clear verification" xml:space="preserve">
<trans-unit id="Clear verification" xml:space="preserve" approved="no">
<source>Clear verification</source>
<target state="translated">Limpar verificação</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Colors" xml:space="preserve">
<trans-unit id="Colors" xml:space="preserve" approved="no">
<source>Colors</source>
<target state="translated">Cores</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Compare file" xml:space="preserve">
<trans-unit id="Compare file" xml:space="preserve" approved="no">
<source>Compare file</source>
<target state="translated">Comparar arquivo</target>
<note>server test step</note>
</trans-unit>
<trans-unit id="Compare security codes with your contacts." xml:space="preserve">
<trans-unit id="Compare security codes with your contacts." xml:space="preserve" approved="no">
<source>Compare security codes with your contacts.</source>
<target state="translated">Compare os códigos de segurança com seus contatos.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configure ICE servers" xml:space="preserve">
<trans-unit id="Configure ICE servers" xml:space="preserve" approved="no">
<source>Configure ICE servers</source>
<target state="translated">Configurar servidores ICE</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm" xml:space="preserve">
<trans-unit id="Confirm" xml:space="preserve" approved="no">
<source>Confirm</source>
<target state="translated">Confirme</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm Passcode" xml:space="preserve">
<trans-unit id="Confirm Passcode" xml:space="preserve" approved="no">
<source>Confirm Passcode</source>
<target state="translated">Confirmar senha</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm database upgrades" xml:space="preserve">
<trans-unit id="Confirm database upgrades" xml:space="preserve" approved="no">
<source>Confirm database upgrades</source>
<target state="translated">Confirmar atualizações do banco de dados</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm new passphrase…" xml:space="preserve">
<trans-unit id="Confirm new passphrase…" xml:space="preserve" approved="no">
<source>Confirm new passphrase…</source>
<target state="translated">Confirme a nova senha…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm password" xml:space="preserve">
@@ -4162,6 +4264,282 @@ SimpleX servers cannot see your profile.</source>
<source>\~strike~</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="## In reply to" xml:space="preserve" approved="no">
<source>## In reply to</source>
<target state="translated">## Em resposta a</target>
<note>copied message info</note>
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve" approved="no">
<source>%@ uploaded</source>
<target state="translated">%@ enviado</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%d weeks" xml:space="preserve" approved="no">
<source>%d weeks</source>
<target state="translated">%d semanas</target>
<note>time interval</note>
</trans-unit>
<trans-unit id="%lld messages blocked by admin" xml:space="preserve" approved="no">
<source>%lld messages blocked by admin</source>
<target state="translated">%lld mensagens bloqueadas pelo administrador</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld new interface languages" xml:space="preserve" approved="no">
<source>%lld new interface languages</source>
<target state="translated">%lld novas interface de idiomas</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**Add contact**: to create a new invitation link, or connect via a link you received." xml:space="preserve" approved="no">
<source>**Add contact**: to create a new invitation link, or connect via a link you received.</source>
<target state="translated">**Adicionar contato**: para criar um novo link de convite ou conectar-se por meio de um link que você recebeu.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**Create group**: to create a new group." xml:space="preserve" approved="no">
<source>**Create group**: to create a new group.</source>
<target state="translated">**Criar grupo**: para criar um novo grupo.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." xml:space="preserve" approved="no">
<source>**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection.</source>
<target state="translated">**Observação**: usar o mesmo banco de dados em dois dispositivos interromperá a descriptografia de mensagens de suas conexões, como proteção de segurança.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="## History" xml:space="preserve" approved="no">
<source>## History</source>
<target state="translated">## Histórico</target>
<note>copied message info</note>
</trans-unit>
<trans-unit id="**Warning**: the archive will be removed." xml:space="preserve" approved="no">
<source>**Warning**: the archive will be removed.</source>
<target state="translated">**Atenção**: O arquivo será removido.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ and %@" xml:space="preserve" approved="no">
<source>%@ and %@</source>
<target state="translated">%@ e %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ and %@ connected" xml:space="preserve" approved="no">
<source>%@ and %@ connected</source>
<target state="translated">%@ e %@ conectado</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ at %@:" xml:space="preserve" approved="no">
<source>%1$@ at %2$@:</source>
<target state="translated">%1$@ em %2$@:</target>
<note>copied message info, &lt;sender&gt; at &lt;time&gt;</note>
</trans-unit>
<trans-unit id="%@ connected" xml:space="preserve" approved="no">
<source>%@ connected</source>
<target state="translated">%@ conectado</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@, %@ and %lld members" xml:space="preserve" approved="no">
<source>%@, %@ and %lld members</source>
<target state="translated">%@, %@ e %lld membros</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@, %@ and %lld other members connected" xml:space="preserve" approved="no">
<source>%@, %@ and %lld other members connected</source>
<target state="translated">%@, %@ e %lld outros membros conectados</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld group events" xml:space="preserve" approved="no">
<source>%lld group events</source>
<target state="translated">%lld eventos de grupo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld messages marked deleted" xml:space="preserve" approved="no">
<source>%lld messages marked deleted</source>
<target state="translated">%lld mensagens marcadas como excluídas</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld messages blocked" xml:space="preserve" approved="no">
<source>%lld messages blocked</source>
<target state="translated">%lld mensagens bloqueadas</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld messages moderated by %@" xml:space="preserve" approved="no">
<source>%lld messages moderated by %@</source>
<target state="translated">%lld mensagens moderadas por %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="(this device v%@)" xml:space="preserve" approved="no">
<source>(this device v%@)</source>
<target state="translated">(este dispositivo v%@)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All data is erased when it is entered." xml:space="preserve" approved="no">
<source>All data is erased when it is entered.</source>
<target state="translated">Todos os dados são apagados quando são inseridos.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow irreversible message deletion only if your contact allows it to you. (24 hours)" xml:space="preserve" approved="no">
<source>Allow irreversible message deletion only if your contact allows it to you. (24 hours)</source>
<target state="translated">Permita a exclusão irreversível de mensagens somente se o seu contato permitir. (24 horas)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Apply" xml:space="preserve" approved="no">
<source>Apply</source>
<target state="translated">Aplicar</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archive and upload" xml:space="preserve" approved="no">
<source>Archive and upload</source>
<target state="translated">Arquivar e fazer envio</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All new messages from %@ will be hidden!" xml:space="preserve" approved="no">
<source>All new messages from %@ will be hidden!</source>
<target state="translated">Todas as novas mensagens de %@ ficarão ocultas!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Already connecting!" xml:space="preserve" approved="no">
<source>Already connecting!</source>
<target state="translated">Já conectando!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App encrypts new local files (except videos)." xml:space="preserve" approved="no">
<source>App encrypts new local files (except videos).</source>
<target state="translated">O aplicativo criptografa novos arquivos locais (exceto vídeos).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App passcode is replaced with self-destruct passcode." xml:space="preserve" approved="no">
<source>App passcode is replaced with self-destruct passcode.</source>
<target state="translated">A senha do Aplicativo é substituída pela senha de autodestruição.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve" approved="no">
<source>All your contacts will remain connected.</source>
<target state="translated">Todos os seus contatos permanecerão conectados.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected. Profile update will be sent to your contacts." xml:space="preserve" approved="no">
<source>All your contacts will remain connected. Profile update will be sent to your contacts.</source>
<target state="translated">Todos os seus contatos permanecerão conectados. A atualização do perfil será enviada para seus contatos.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve" approved="no">
<source>Allow to send files and media.</source>
<target state="translated">Permitir o envio de arquivos e mídia.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="App data migration" xml:space="preserve" approved="no">
<source>App data migration</source>
<target state="translated">Migração de dados de aplicativos</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archiving database" xml:space="preserve" approved="no">
<source>Archiving database</source>
<target state="translated">Arquivando banco de dados</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All messages will be deleted - this cannot be undone!" xml:space="preserve" approved="no">
<source>All messages will be deleted - this cannot be undone!</source>
<target state="translated">Todas as mensagens serão apagadas isso não pode ser desfeito!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays." xml:space="preserve" approved="no">
<source>All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays.</source>
<target state="translated">Todos os seus contatos, conversas e arquivos serão criptografados com segurança e enviados em partes para retransmissões XFTP configuradas.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow message reactions only if your contact allows them." xml:space="preserve" approved="no">
<source>Allow message reactions only if your contact allows them.</source>
<target state="translated">Permita reações às mensagens somente se o seu contato permitir.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow message reactions." xml:space="preserve" approved="no">
<source>Allow message reactions.</source>
<target state="translated">Permitir reações às mensagens.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow your contacts to irreversibly delete sent messages. (24 hours)" xml:space="preserve" approved="no">
<source>Allow your contacts to irreversibly delete sent messages. (24 hours)</source>
<target state="translated">Permita que seus contatos apaguem irreversivelmente as mensagens enviadas. (24 horas)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Already joining the group!" xml:space="preserve" approved="no">
<source>Already joining the group!</source>
<target state="translated">Entrando no grupo!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="An empty chat profile with the provided name is created, and the app opens as usual." xml:space="preserve" approved="no">
<source>An empty chat profile with the provided name is created, and the app opens as usual.</source>
<target state="translated">Um perfil de conversa vazio com o nome fornecido é criado e o aplicativo abre normalmente.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Auto-accept" xml:space="preserve" approved="no">
<source>Auto-accept</source>
<target state="translated">Aceitar automaticamente</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow your contacts adding message reactions." xml:space="preserve" approved="no">
<source>Allow your contacts adding message reactions.</source>
<target state="translated">Permita que seus contatos adicionem reações às mensagens.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to irreversibly delete sent messages. (24 hours)" xml:space="preserve" approved="no">
<source>Allow to irreversibly delete sent messages. (24 hours)</source>
<target state="translated">Permitir apagar irreversivelmente as mensagens enviadas. (24 horas)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Choose _Migrate from another device_ on the new device and scan QR code." xml:space="preserve" approved="no">
<source>Choose _Migrate from another device_ on the new device and scan QR code.</source>
<target state="translated">Escolha _Migrar de outro dispositivo_ no novo dispositivo e leia o código QR.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cancel migration" xml:space="preserve" approved="no">
<source>Cancel migration</source>
<target state="translated">Cancelar migração</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Camera not available" xml:space="preserve" approved="no">
<source>Camera not available</source>
<target state="translated">Câmera não disponível</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change self-destruct passcode" xml:space="preserve" approved="no">
<source>Change self-destruct passcode</source>
<target state="translated">Alterar a senha de autodestruição</target>
<note>authentication reason
set passcode view</note>
</trans-unit>
<trans-unit id="Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." xml:space="preserve" approved="no">
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
<target state="translated">A conversa foi interrompida. Se você já usou esse banco de dados em outro dispositivo, deverá transferi-lo de volta antes de iniciar a conversa.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat migrated!" xml:space="preserve" approved="no">
<source>Chat migrated!</source>
<target state="translated">Conversa migrada!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Clear private notes?" xml:space="preserve" approved="no">
<source>Clear private notes?</source>
<target state="translated">Limpar notas privadas?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm network settings" xml:space="preserve" approved="no">
<source>Confirm network settings</source>
<target state="translated">Confirme as configurações de rede</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm that you remember database passphrase to migrate it." xml:space="preserve" approved="no">
<source>Confirm that you remember database passphrase to migrate it.</source>
<target state="translated">Confirme se você se lembra da senha do banco de dados para migrá-lo.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change self-destruct mode" xml:space="preserve" approved="no">
<source>Change self-destruct mode</source>
<target state="translated">Alterar modo de autodestruição</target>
<note>authentication reason</note>
</trans-unit>
<trans-unit id="Confirm upload" xml:space="preserve" approved="no">
<source>Confirm upload</source>
<target state="translated">Confirmar envio</target>
<note>No comment provided by engineer.</note>
</trans-unit>
</body>
</file>
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="pt" datatype="plaintext">
File diff suppressed because it is too large Load Diff
@@ -677,6 +677,10 @@
<target>อนุญาตให้ข้อความที่หายไปเฉพาะในกรณีที่ผู้ติดต่อของคุณอนุญาตเท่านั้น.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow downgrade" xml:space="preserve">
<source>Allow downgrade</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow irreversible message deletion only if your contact allows it to you. (24 hours)" xml:space="preserve">
<source>Allow irreversible message deletion only if your contact allows it to you. (24 hours)</source>
<target>อนุญาตให้ลบข้อความแบบถาวรเฉพาะในกรณีที่ผู้ติดต่อของคุณอนุญาตให้คุณเท่านั้น</target>
@@ -707,6 +711,10 @@
<target>อนุญาตให้ลบข้อความที่ส่งไปแล้วอย่างถาวร</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source>
<target>อนุญาตให้ส่งไฟล์และสื่อ</target>
@@ -765,6 +773,10 @@
<source>Already joining the group!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Always use private routing." xml:space="preserve">
<source>Always use private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Always use relay" xml:space="preserve">
<source>Always use relay</source>
<target>ใช้รีเลย์เสมอ</target>
@@ -1028,6 +1040,14 @@
<target>ไม่สามารถรับไฟล์ได้</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Capacity exceeded - recipient did not receive previously sent messages." xml:space="preserve">
<source>Capacity exceeded - recipient did not receive previously sent messages.</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve">
<source>Change</source>
<target>เปลี่ยน</target>
@@ -1215,6 +1235,10 @@
<target>ยืนยันการอัพเกรดฐานข้อมูล</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm files from unknown servers." xml:space="preserve">
<source>Confirm files from unknown servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm network settings" xml:space="preserve">
<source>Confirm network settings</source>
<note>No comment provided by engineer.</note>
@@ -1605,6 +1629,10 @@ This is your own one-time link!</source>
<target>ระบบจะย้ายฐานข้อมูลเมื่อแอปรีสตาร์ท</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Debug delivery" xml:space="preserve">
<source>Debug delivery</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Decentralized" xml:space="preserve">
<source>Decentralized</source>
<target>กระจายอำนาจแล้ว</target>
@@ -1843,6 +1871,10 @@ This cannot be undone!</source>
<source>Desktop devices</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Destination server error: %@" xml:space="preserve">
<source>Destination server error: %@</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Develop" xml:space="preserve">
<source>Develop</source>
<target>พัฒนา</target>
@@ -1945,11 +1977,19 @@ This cannot be undone!</source>
<source>Discover via local network</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do NOT send messages directly, even if your or destination server does not support private routing." xml:space="preserve">
<source>Do NOT send messages directly, even if your or destination server does not support private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
<source>Do NOT use SimpleX for emergency calls.</source>
<target>อย่าใช้ SimpleX สําหรับการโทรฉุกเฉิน</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do NOT use private routing." xml:space="preserve">
<source>Do NOT use private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do it later" xml:space="preserve">
<source>Do it later</source>
<target>ทำในภายหลัง</target>
@@ -1979,6 +2019,10 @@ This cannot be undone!</source>
<target>ปรับลดรุ่นและเปิดแชท</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source>
<note>No comment provided by engineer.</note>
@@ -2084,6 +2128,10 @@ This cannot be undone!</source>
<target>เปิดใช้งานรหัสผ่านแบบทําลายตัวเอง</target>
<note>set passcode view</note>
</trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source>
<target>Encrypt</target>
@@ -2225,10 +2273,6 @@ This cannot be undone!</source>
<target>เกิดข้อผิดพลาดในการเพิ่มสมาชิก</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error allowing contact PQ encryption" xml:space="preserve">
<source>Error allowing contact PQ encryption</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error changing address" xml:space="preserve">
<source>Error changing address</source>
<target>เกิดข้อผิดพลาดในการเปลี่ยนที่อยู่</target>
@@ -2482,7 +2526,7 @@ This cannot be undone!</source>
<trans-unit id="Error: %@" xml:space="preserve">
<source>Error: %@</source>
<target>ข้อผิดพลาด: % @</target>
<note>No comment provided by engineer.</note>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Error: URL is invalid" xml:space="preserve">
<source>Error: URL is invalid</source>
@@ -2571,6 +2615,10 @@ This cannot be undone!</source>
<target>ไฟล์: % @</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files" xml:space="preserve">
<source>Files</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files &amp; media" xml:space="preserve">
<source>Files &amp; media</source>
<target>ไฟล์และสื่อ</target>
@@ -2586,6 +2634,10 @@ This cannot be undone!</source>
<target>ไฟล์และสื่อเป็นสิ่งต้องห้ามในกลุ่มนี้</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source>
<target>ไฟล์และสื่อต้องห้าม!</target>
@@ -2649,6 +2701,32 @@ This cannot be undone!</source>
<target>สำหรับคอนโซล</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forward and save messages" xml:space="preserve">
<source>Forward and save messages</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarding server: %@&#10;Destination server error: %@" xml:space="preserve">
<source>Forwarding server: %1$@
Destination server error: %2$@</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Forwarding server: %@&#10;Error: %@" xml:space="preserve">
<source>Forwarding server: %1$@
Error: %2$@</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source>
<note>No comment provided by engineer.</note>
@@ -2755,6 +2833,10 @@ This cannot be undone!</source>
<target>สมาชิกกลุ่มสามารถลบข้อความที่ส่งแล้วอย่างถาวร</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source>
<target>สมาชิกกลุ่มสามารถส่งข้อความโดยตรงได้</target>
@@ -2989,6 +3071,10 @@ This cannot be undone!</source>
<target>ในการตอบกลับถึง</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="In-call sounds" xml:space="preserve">
<source>In-call sounds</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito" xml:space="preserve">
<source>Incognito</source>
<target>ไม่ระบุตัวตน</target>
@@ -3423,11 +3509,19 @@ This is your link for group %@!</source>
<target>ใบเสร็จการส่งข้อความ!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message delivery warning" xml:space="preserve">
<source>Message delivery warning</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message draft" xml:space="preserve">
<source>Message draft</source>
<target>ร่างข้อความ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reactions" xml:space="preserve">
<source>Message reactions</source>
<target>ปฏิกิริยาของข้อความ</target>
@@ -3443,6 +3537,18 @@ This is your link for group %@!</source>
<target>ปฏิกิริยาบนข้อความเป็นสิ่งต้องห้ามในกลุ่มนี้</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing mode" xml:space="preserve">
<source>Message routing mode</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message source remains private." xml:space="preserve">
<source>Message source remains private.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>ข้อความ</target>
@@ -3547,15 +3653,14 @@ This is your link for group %@!</source>
<target>การปรับปรุงเพิ่มเติมกำลังจะมาเร็ว ๆ นี้!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="More reliable network connection." xml:space="preserve">
<source>More reliable network connection.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Most likely this connection is deleted." xml:space="preserve">
<source>Most likely this connection is deleted.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="Most likely this contact has deleted the connection with you." xml:space="preserve">
<source>Most likely this contact has deleted the connection with you.</source>
<target>เป็นไปได้มากว่าผู้ติดต่อนี้ได้ลบการเชื่อมต่อกับคุณ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Multiple chat profiles" xml:space="preserve">
<source>Multiple chat profiles</source>
<target>โปรไฟล์การแชทหลายรายการ</target>
@@ -3581,6 +3686,18 @@ This is your link for group %@!</source>
<target>เครือข่ายและเซิร์ฟเวอร์</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network issues - message expired after many attempts to send it." xml:space="preserve">
<source>Network issues - message expired after many attempts to send it.</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Network management" xml:space="preserve">
<source>Network management</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source>
<target>การตั้งค่าเครือข่าย</target>
@@ -3688,6 +3805,10 @@ This is your link for group %@!</source>
<target>ไม่มีประวัติ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source>
<target>ไม่อนุญาตให้บันทึกข้อความเสียง</target>
@@ -3892,6 +4013,10 @@ This is your link for group %@!</source>
<source>Or show this code</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve">
<source>PING count</source>
<target>จํานวน PING</target>
@@ -4051,10 +4176,6 @@ Error: %@</source>
<target>อาจเป็นไปได้ว่าลายนิ้วมือของ certificate ในที่อยู่เซิร์ฟเวอร์ไม่ถูกต้อง</target>
<note>server test error</note>
</trans-unit>
<trans-unit id="Post-quantum E2EE" xml:space="preserve">
<source>Post-quantum E2EE</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Preserve the last message draft, with attachments." xml:space="preserve">
<source>Preserve the last message draft, with attachments.</source>
<target>เก็บข้อความที่ร่างไว้ล่าสุดพร้อมไฟล์แนบ</target>
@@ -4090,10 +4211,22 @@ Error: %@</source>
<target>ชื่อไฟล์ส่วนตัว</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private message routing" xml:space="preserve">
<source>Private message routing</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private message routing 🚀" xml:space="preserve">
<source>Private message routing 🚀</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private notes" xml:space="preserve">
<source>Private notes</source>
<note>name of notes to self</note>
</trans-unit>
<trans-unit id="Private routing" xml:space="preserve">
<source>Private routing</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>การเชื่อมต่อโปรไฟล์และเซิร์ฟเวอร์</target>
@@ -4104,6 +4237,10 @@ Error: %@</source>
<target>รูปโปรไฟล์</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile images" xml:space="preserve">
<source>Profile images</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile name" xml:space="preserve">
<source>Profile name</source>
<note>No comment provided by engineer.</note>
@@ -4142,6 +4279,10 @@ Error: %@</source>
<target>ห้ามแสดงปฏิกิริยาต่อข้อความ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source>
<target>ห้ามส่งข้อความโดยตรงถึงสมาชิก</target>
@@ -4162,11 +4303,20 @@ Error: %@</source>
<target>ห้ามส่งข้อความเสียง</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect IP address" xml:space="preserve">
<source>Protect IP address</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect app screen" xml:space="preserve">
<source>Protect app screen</source>
<target>ปกป้องหน้าจอแอป</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect your IP address from the messaging relays chosen by your contacts.&#10;Enable in *Network &amp; servers* settings." xml:space="preserve">
<source>Protect your IP address from the messaging relays chosen by your contacts.
Enable in *Network &amp; servers* settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protect your chat profiles with a password!" xml:space="preserve">
<source>Protect your chat profiles with a password!</source>
<target>ปกป้องโปรไฟล์การแชทของคุณด้วยรหัสผ่าน!</target>
@@ -4282,6 +4432,10 @@ Error: %@</source>
<source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source>
<target>ผู้รับจะเห็นการอัปเดตเมื่อคุณพิมพ์</target>
@@ -4490,6 +4644,10 @@ Error: %@</source>
<target>เซิร์ฟเวอร์ SMP</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safely receive files" xml:space="preserve">
<source>Safely receive files</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safer groups" xml:space="preserve">
<source>Safer groups</source>
<note>No comment provided by engineer.</note>
@@ -4574,11 +4732,19 @@ Error: %@</source>
<target>บันทึกข้อความต้อนรับ?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source>
<target>เซิร์ฟเวอร์ WebRTC ICE ที่บันทึกไว้จะถูกลบออก</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source>
<note>message info title</note>
@@ -4699,6 +4865,14 @@ Error: %@</source>
<target>ส่งข้อความสด</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when IP address is protected and your or destination server does not support private routing." xml:space="preserve">
<source>Send messages directly when IP address is protected and your or destination server does not support private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when your or destination server does not support private routing." xml:space="preserve">
<source>Send messages directly when your or destination server does not support private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send notifications" xml:space="preserve">
<source>Send notifications</source>
<target>ส่งการแจ้งเตือน</target>
@@ -4801,6 +4975,10 @@ Error: %@</source>
<target>ข้อความที่ส่งจะถูกลบหลังเกินเวลาที่กําหนด</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings." xml:space="preserve">
<source>Server address is incompatible with network settings.</source>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>เซิร์ฟเวอร์ต้องการการอนุญาตในการสร้างคิว โปรดตรวจสอบรหัสผ่าน</target>
@@ -4816,6 +4994,10 @@ Error: %@</source>
<target>การทดสอบเซิร์ฟเวอร์ล้มเหลว!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings." xml:space="preserve">
<source>Server version is incompatible with network settings.</source>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>เซิร์ฟเวอร์</target>
@@ -4874,6 +5056,10 @@ Error: %@</source>
<target>การตั้งค่า</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Shape profile images" xml:space="preserve">
<source>Shape profile images</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share" xml:space="preserve">
<source>Share</source>
<target>แชร์</target>
@@ -4926,11 +5112,19 @@ Error: %@</source>
<source>Show last messages</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show message status" xml:space="preserve">
<source>Show message status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>แสดงตัวอย่าง</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show → on messages sent via private routing." xml:space="preserve">
<source>Show → on messages sent via private routing.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show:" xml:space="preserve">
<source>Show:</source>
<target>แสดง:</target>
@@ -4989,6 +5183,14 @@ Error: %@</source>
<trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source>
<target>ลิงก์ SimpleX</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -5024,6 +5226,10 @@ Error: %@</source>
<target>ใครบางคน</target>
<note>notification title</note>
</trans-unit>
<trans-unit id="Square, circle, or anything in between." xml:space="preserve">
<source>Square, circle, or anything in between.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Start chat" xml:space="preserve">
<source>Start chat</source>
<target>เริ่มแชท</target>
@@ -5231,6 +5437,10 @@ It can happen because of some bug or when the connection is compromised.</source
<target>แอปสามารถแจ้งให้คุณทราบเมื่อคุณได้รับข้อความหรือคำขอติดต่อ - โปรดเปิดการตั้งค่าเพื่อเปิดใช้งาน</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The app will ask to confirm downloads from unknown file servers (except .onion)." xml:space="preserve">
<source>The app will ask to confirm downloads from unknown file servers (except .onion).</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The attempt to change database passphrase was not completed." xml:space="preserve">
<source>The attempt to change database passphrase was not completed.</source>
<target>ความพยายามในการเปลี่ยนรหัสผ่านของฐานข้อมูลไม่เสร็จสมบูรณ์</target>
@@ -5405,6 +5615,10 @@ It can happen because of some bug or when the connection is compromised.</source
<target>ไฟล์ภาพ/เสียงใช้ UTC เพื่อป้องกันเขตเวลา</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect your IP address, private routing uses your SMP servers to deliver messages." xml:space="preserve">
<source>To protect your IP address, private routing uses your SMP servers to deliver messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect your information, turn on SimpleX Lock.&#10;You will be prompted to complete authentication before this feature is enabled." xml:space="preserve">
<source>To protect your information, turn on SimpleX Lock.
You will be prompted to complete authentication before this feature is enabled.</source>
@@ -5490,11 +5704,6 @@ You will be prompted to complete authentication before this feature is enabled.<
<source>Unblock member?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unexpected error: %@" xml:space="preserve">
<source>Unexpected error: %@</source>
<target>ข้อผิดพลาดที่ไม่คาดคิด: %@</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="Unexpected migration state" xml:space="preserve">
<source>Unexpected migration state</source>
<target>สถานะการย้ายข้อมูลที่ไม่คาดคิด</target>
@@ -5540,6 +5749,10 @@ You will be prompted to complete authentication before this feature is enabled.<
<target>ข้อผิดพลาดที่ไม่รู้จัก</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unknown servers!" xml:space="preserve">
<source>Unknown servers!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions." xml:space="preserve">
<source>Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions.</source>
<target>ยกเว้นกรณีที่คุณใช้อินเทอร์เฟซการโทรของ iOS ให้เปิดใช้งานโหมดห้ามรบกวนเพื่อหลีกเลี่ยงการรบกวน</target>
@@ -5678,6 +5891,14 @@ To connect, please ask your contact to create another connection link and check
<source>Use only local notifications?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use private routing with unknown servers when IP address is not protected." xml:space="preserve">
<source>Use private routing with unknown servers when IP address is not protected.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use private routing with unknown servers." xml:space="preserve">
<source>Use private routing with unknown servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use server" xml:space="preserve">
<source>Use server</source>
<target>ใช้เซิร์ฟเวอร์</target>
@@ -5785,6 +6006,10 @@ To connect, please ask your contact to create another connection link and check
<target>ข้อความเสียงเป็นสิ่งต้องห้ามในกลุ่มนี้</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source>
<target>ห้ามข้อความเสียง!</target>
@@ -5852,6 +6077,10 @@ To connect, please ask your contact to create another connection link and check
<target>เมื่อพร้อมใช้งาน</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="When connecting audio and video calls." xml:space="preserve">
<source>When connecting audio and video calls.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="When people request to connect, you can accept or reject it." xml:space="preserve">
<source>When people request to connect, you can accept or reject it.</source>
<target>เมื่อมีคนขอเชื่อมต่อ คุณสามารถยอมรับหรือปฏิเสธได้</target>
@@ -5862,6 +6091,18 @@ To connect, please ask your contact to create another connection link and check
<target>เมื่อคุณแชร์โปรไฟล์ที่ไม่ระบุตัวตนกับใครสักคน โปรไฟล์นี้จะใช้สำหรับกลุ่มที่พวกเขาเชิญคุณ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Will be enabled in direct chats!" xml:space="preserve">
<source>Will be enabled in direct chats!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source>
<note>No comment provided by engineer.</note>
@@ -5875,11 +6116,23 @@ To connect, please ask your contact to create another connection link and check
<source>With reduced battery usage.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Without Tor or VPN, your IP address will be visible to file servers." xml:space="preserve">
<source>Without Tor or VPN, your IP address will be visible to file servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Without Tor or VPN, your IP address will be visible to these XFTP relays: %@." xml:space="preserve">
<source>Without Tor or VPN, your IP address will be visible to these XFTP relays: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wrong database passphrase" xml:space="preserve">
<source>Wrong database passphrase</source>
<target>รหัสผ่านฐานข้อมูลไม่ถูกต้อง</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wrong key or unknown connection - most likely this connection is deleted." xml:space="preserve">
<source>Wrong key or unknown connection - most likely this connection is deleted.</source>
<note>snd error text</note>
</trans-unit>
<trans-unit id="Wrong passphrase!" xml:space="preserve">
<source>Wrong passphrase!</source>
<target>รหัสผ่านผิด!</target>
@@ -6323,6 +6576,10 @@ SimpleX servers cannot see your profile.</source>
<target>ผู้ดูแลระบบ</target>
<note>member role</note>
</trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source>
<target>ยอมรับ encryption สำหรับ %@…</target>
@@ -6333,6 +6590,10 @@ SimpleX servers cannot see your profile.</source>
<target>เห็นด้วยกับการ encryption…</target>
<note>chat item text</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve">
<source>always</source>
<target>เสมอ</target>
@@ -6653,6 +6914,10 @@ SimpleX servers cannot see your profile.</source>
<source>event happened</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source>
<target>ลบกลุ่มแล้ว</target>
@@ -6859,6 +7124,10 @@ SimpleX servers cannot see your profile.</source>
<target>เจ้าของ</target>
<note>member role</note>
</trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source>
<target>เพื่อนต่อเพื่อน</target>
@@ -6906,6 +7175,14 @@ SimpleX servers cannot see your profile.</source>
<target>ลบคุณออกแล้ว</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve">
<source>sec</source>
<target>วินาที</target>
@@ -6930,6 +7207,12 @@ SimpleX servers cannot see your profile.</source>
<source>send direct message</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="server queue info: %@&#10;&#10;last received msg: %@" xml:space="preserve">
<source>server queue info: %1$@
last received msg: %2$@</source>
<note>queue info</note>
</trans-unit>
<trans-unit id="set new contact address" xml:space="preserve">
<source>set new contact address</source>
<note>profile update event chat item</note>
@@ -6966,10 +7249,18 @@ SimpleX servers cannot see your profile.</source>
<target>ไม่ทราบ</target>
<note>connection info</note>
</trans-unit>
<trans-unit id="unknown relays" xml:space="preserve">
<source>unknown relays</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unknown status" xml:space="preserve">
<source>unknown status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unprotected" xml:space="preserve">
<source>unprotected</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="updated group profile" xml:space="preserve">
<source>updated group profile</source>
<target>อัปเดตโปรไฟล์กลุ่มแล้ว</target>
@@ -7033,11 +7324,19 @@ SimpleX servers cannot see your profile.</source>
<target>สัปดาห์</target>
<note>time unit</note>
</trans-unit>
<trans-unit id="when IP hidden" xml:space="preserve">
<source>when IP hidden</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="yes" xml:space="preserve">
<source>yes</source>
<target>ใช่</target>
<note>pref value</note>
</trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source>
<target>คุณได้รับเชิญให้เข้าร่วมกลุ่ม</target>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,64 +0,0 @@
//
// ConcurrentQueue.swift
// SimpleX NSE
//
// Created by Evgeny on 08/12/2023.
// Copyright © 2023 SimpleX Chat. All rights reserved.
//
import Foundation
struct DequeueElement<T> {
var elementId: UUID?
var task: Task<T?, Never>
}
class ConcurrentQueue<T> {
private var queue: [T] = []
private var queueLock = DispatchQueue(label: "chat.simplex.app.SimpleX-NSE.concurrent-queue.lock.\(UUID())")
private var continuations = [(elementId: UUID, continuation: CheckedContinuation<T?, Never>)]()
func enqueue(_ el: T) {
resumeContinuation(el) { self.queue.append(el) }
}
func frontEnqueue(_ el: T) {
resumeContinuation(el) { self.queue.insert(el, at: 0) }
}
private func resumeContinuation(_ el: T, add: @escaping () -> Void) {
queueLock.sync {
if let (_, cont) = continuations.first {
continuations.remove(at: 0)
cont.resume(returning: el)
} else {
add()
}
}
}
func dequeue() -> DequeueElement<T> {
queueLock.sync {
if queue.isEmpty {
let elementId = UUID()
let task = Task {
await withCheckedContinuation { cont in
continuations.append((elementId, cont))
}
}
return DequeueElement(elementId: elementId, task: task)
} else {
let el = queue.remove(at: 0)
return DequeueElement(task: Task { el })
}
}
}
func cancelDequeue(_ elementId: UUID) {
queueLock.sync {
let cancelled = continuations.filter { $0.elementId == elementId }
continuations.removeAll { $0.elementId == elementId }
cancelled.forEach { $0.continuation.resume(returning: nil) }
}
}
}
+77 -136
View File
@@ -22,110 +22,6 @@ let nseSuspendSchedule: SuspendSchedule = (2, 4)
let fastNSESuspendSchedule: SuspendSchedule = (1, 1)
typealias NtfStream = ConcurrentQueue<NSENotification>
// Notifications are delivered via concurrent queues, as they are all received from chat controller in a single loop that
// writes to ConcurrentQueue and when notification is processed, the instance of Notification service extension reads from the queue.
// One queue per connection (entity) is used.
// The concurrent queues allow read cancellation, to ensure that notifications are not lost in case the current thread completes
// before expected notification is read (multiple notifications can be expected, because one notification can be delivered for several messages).
actor PendingNtfs {
static let shared = PendingNtfs()
private var ntfStreams: [String: NtfStream] = [:]
func createStream(_ id: String) async {
logger.debug("NotificationService PendingNtfs.createStream: \(id)")
if ntfStreams[id] == nil {
ntfStreams[id] = ConcurrentQueue()
logger.debug("NotificationService PendingNtfs.createStream: created ConcurrentQueue")
}
}
func readStream(_ id: String, for nse: NotificationService, ntfInfo: NtfMessages) async {
logger.debug("NotificationService PendingNtfs.readStream: \(id) \(ntfInfo.ntfMessages.count)")
if !ntfInfo.user.showNotifications {
nse.setBestAttemptNtf(.empty)
}
if let s = ntfStreams[id] {
logger.debug("NotificationService PendingNtfs.readStream: has stream")
var expected = Set(ntfInfo.ntfMessages.map { $0.msgId })
logger.debug("NotificationService PendingNtfs.readStream: expecting: \(expected)")
var readCancelled = false
var dequeued: DequeueElement<NSENotification>?
nse.cancelRead = {
readCancelled = true
if let elementId = dequeued?.elementId {
s.cancelDequeue(elementId)
}
}
while !readCancelled {
dequeued = s.dequeue()
if let ntf = await dequeued?.task.value {
if readCancelled {
logger.debug("NotificationService PendingNtfs.readStream: read cancelled, put ntf to queue front")
s.frontEnqueue(ntf)
break
} else if case let .msgInfo(info) = ntf {
let found = expected.remove(info.msgId)
if found != nil {
logger.debug("NotificationService PendingNtfs.readStream: msgInfo, last: \(expected.isEmpty)")
if expected.isEmpty { break }
} else if let msgTs = ntfInfo.msgTs, info.msgTs > msgTs {
logger.debug("NotificationService PendingNtfs.readStream: unexpected msgInfo")
s.frontEnqueue(ntf)
break
}
} else if ntfInfo.user.showNotifications {
logger.debug("NotificationService PendingNtfs.readStream: setting best attempt")
nse.setBestAttemptNtf(ntf)
if ntf.isCallInvitation { break }
}
} else {
break
}
}
nse.cancelRead = nil
logger.debug("NotificationService PendingNtfs.readStream: exiting")
}
}
func writeStream(_ id: String, _ ntf: NSENotification) async {
logger.debug("NotificationService PendingNtfs.writeStream: \(id)")
if let s = ntfStreams[id] {
logger.debug("NotificationService PendingNtfs.writeStream: writing ntf")
s.enqueue(ntf)
}
}
}
// The current implementation assumes concurrent notification delivery and uses semaphores
// to process only one notification per connection (entity) at a time.
class NtfStreamSemaphores {
static let shared = NtfStreamSemaphores()
private static let queue = DispatchQueue(label: "chat.simplex.app.SimpleX-NSE.notification-semaphores.lock")
private var semaphores: [String: DispatchSemaphore] = [:]
func waitForStream(_ id: String) {
streamSemaphore(id, value: 0)?.wait()
}
func signalStreamReady(_ id: String) {
streamSemaphore(id, value: 1)?.signal()
}
// this function returns nil if semaphore is just created, so passed value shoud be coordinated with the desired end value of the semaphore
private func streamSemaphore(_ id: String, value: Int) -> DispatchSemaphore? {
NtfStreamSemaphores.queue.sync {
if let s = semaphores[id] {
return s
} else {
semaphores[id] = DispatchSemaphore(value: value)
return nil
}
}
}
}
enum NSENotification {
case nse(UNMutableNotificationContent)
case callkit(RcvCallInvitation)
@@ -149,7 +45,7 @@ class NSEThreads {
static let shared = NSEThreads()
private static let queue = DispatchQueue(label: "chat.simplex.app.SimpleX-NSE.notification-threads.lock")
private var allThreads: Set<UUID> = []
private var activeThreads: Set<UUID> = []
private var activeThreads: [(UUID, NotificationService)] = []
func newThread() -> UUID {
NSEThreads.queue.sync {
@@ -158,19 +54,42 @@ class NSEThreads {
}
}
func startThread(_ t: UUID) {
func startThread(_ t: UUID, _ service: NotificationService) {
NSEThreads.queue.sync {
if allThreads.contains(t) {
_ = activeThreads.insert(t)
activeThreads.append((t, service))
} else {
logger.warning("NotificationService startThread: thread \(t) was removed before it started")
}
}
}
func processNotification(_ id: ChatId, _ ntf: NSENotification) async -> Void {
var waitTime: Int64 = 5_000_000000
while waitTime > 0 {
if let (_, nse) = rcvEntityThread(id),
nse.shouldProcessNtf && nse.processReceivedNtf(ntf) {
break
} else {
try? await Task.sleep(nanoseconds: 10_000000)
waitTime -= 10_000000
}
}
}
private func rcvEntityThread(_ id: ChatId) -> (UUID, NotificationService)? {
NSEThreads.queue.sync {
activeThreads.first(where: { (_, nse) in nse.receiveEntityId == id })
}
}
func endThread(_ t: UUID) -> Bool {
NSEThreads.queue.sync {
let tActive = activeThreads.remove(t)
let tActive: UUID? = if let index = activeThreads.firstIndex(where: { $0.0 == t }) {
activeThreads.remove(at: index).0
} else {
nil
}
let t = allThreads.remove(t)
if tActive != nil && activeThreads.isEmpty {
return true
@@ -198,8 +117,11 @@ class NotificationService: UNNotificationServiceExtension {
// thread is added to allThreads here - if thread did not start chat,
// chat does not need to be suspended but NSE state still needs to be set to "suspended".
var threadId: UUID? = NSEThreads.shared.newThread()
var notificationInfo: NtfMessages?
var receiveEntityId: String?
var cancelRead: (() -> Void)?
var expectedMessages: Set<String> = []
// return true if the message is taken - it prevents sending it to another NotificationService instance for processing
var shouldProcessNtf = false
var appSubscriber: AppSubscriber?
var returnedSuspension = false
@@ -265,7 +187,7 @@ class NotificationService: UNNotificationServiceExtension {
// check it here again
appStateGroupDefault.get().inactive {
// thread is added to activeThreads tracking set here - if thread started chat it needs to be suspended
if let t = threadId { NSEThreads.shared.startThread(t) }
if let t = threadId { NSEThreads.shared.startThread(t, self) }
let dbStatus = startChat()
if case .ok = dbStatus,
let ntfInfo = apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo) {
@@ -276,17 +198,11 @@ class NotificationService: UNNotificationServiceExtension {
? .nse(createConnectionEventNtf(ntfInfo.user, connEntity))
: .empty
)
if let id = connEntity.id {
if let id = connEntity.id, ntfInfo.msgTs != nil {
notificationInfo = ntfInfo
receiveEntityId = id
NtfStreamSemaphores.shared.waitForStream(id)
if receiveEntityId != nil {
Task {
logger.debug("NotificationService: receiveNtfMessages: in Task, connEntity id \(id)")
await PendingNtfs.shared.createStream(id)
await PendingNtfs.shared.readStream(id, for: self, ntfInfo: ntfInfo)
deliverBestAttemptNtf()
}
}
expectedMessages = Set(ntfInfo.ntfMessages.map { $0.msgId })
shouldProcessNtf = true
return
}
}
@@ -302,6 +218,38 @@ class NotificationService: UNNotificationServiceExtension {
deliverBestAttemptNtf(urgent: true)
}
func processReceivedNtf(_ ntf: NSENotification) -> Bool {
guard let ntfInfo = notificationInfo, let msgTs = ntfInfo.msgTs else { return false }
if !ntfInfo.user.showNotifications {
self.setBestAttemptNtf(.empty)
}
if case let .msgInfo(info) = ntf {
let found = expectedMessages.remove(info.msgId)
if found != nil {
logger.debug("NotificationService processNtf: msgInfo, last: \(self.expectedMessages.isEmpty)")
if expectedMessages.isEmpty {
self.deliverBestAttemptNtf()
}
return true
} else if info.msgTs > msgTs {
logger.debug("NotificationService processNtf: unexpected msgInfo, let other instance to process it, stopping this one")
self.deliverBestAttemptNtf()
return false
} else {
logger.debug("NotificationService processNtf: unknown message, let other instance to process it")
return false
}
} else if ntfInfo.user.showNotifications {
logger.debug("NotificationService processNtf: setting best attempt")
self.setBestAttemptNtf(ntf)
if ntf.isCallInvitation {
self.deliverBestAttemptNtf()
}
return true
}
return false
}
func setBadgeCount() {
badgeCount = ntfBadgeCountGroupDefault.get() + 1
ntfBadgeCountGroupDefault.set(badgeCount)
@@ -323,14 +271,9 @@ class NotificationService: UNNotificationServiceExtension {
private func deliverBestAttemptNtf(urgent: Bool = false) {
logger.debug("NotificationService.deliverBestAttemptNtf")
if let cancel = cancelRead {
cancelRead = nil
cancel()
}
if let id = receiveEntityId {
receiveEntityId = nil
NtfStreamSemaphores.shared.signalStreamReady(id)
}
// stop processing other messages
shouldProcessNtf = false
let suspend: Bool
if let t = threadId {
threadId = nil
@@ -572,7 +515,7 @@ func chatSuspended() {
}
// A single loop is used per Notification service extension process to receive and process all messages depending on the NSE state
// If the extension is not active yet, or suspended/suspending, or the app is running, the notifications will no be received.
// If the extension is not active yet, or suspended/suspending, or the app is running, the notifications will not be received.
func receiveMessages() async {
logger.debug("NotificationService receiveMessages")
while true {
@@ -591,8 +534,7 @@ func receiveMessages() async {
logger.debug("NotificationService receiveMsg: message")
if let (id, ntf) = await receivedMsgNtf(msg) {
logger.debug("NotificationService receiveMsg: notification")
await PendingNtfs.shared.createStream(id)
await PendingNtfs.shared.writeStream(id, ntf)
await NSEThreads.shared.processNotification(id, ntf)
}
}
}
@@ -644,9 +586,6 @@ func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? {
cleanupDirectFile(aChatItem)
}
return nil
case let .sndFileCompleteXFTP(_, aChatItem, _):
cleanupFile(aChatItem)
return nil
case let .callInvitation(invitation):
// Do not post it without CallKit support, iOS will stop launching the app without showing CallKit
return (
@@ -757,14 +696,16 @@ func apiGetNtfMessage(nonce: String, encNtfInfo: String) -> NtfMessages? {
}
func apiReceiveFile(fileId: Int64, encrypted: Bool, inline: Bool? = nil) -> AChatItem? {
let r = sendSimpleXCmd(.receiveFile(fileId: fileId, encrypted: encrypted, inline: inline))
let userApprovedRelays = !privacyAskToApproveRelaysGroupDefault.get()
let r = sendSimpleXCmd(.receiveFile(fileId: fileId, userApprovedRelays: userApprovedRelays, encrypted: encrypted, inline: inline))
if case let .rcvFileAccepted(_, chatItem) = r { return chatItem }
logger.error("receiveFile error: \(responseError(r))")
return nil
}
func apiSetFileToReceive(fileId: Int64, encrypted: Bool) {
let r = sendSimpleXCmd(.setFileToReceive(fileId: fileId, encrypted: encrypted))
let userApprovedRelays = !privacyAskToApproveRelaysGroupDefault.get()
let r = sendSimpleXCmd(.setFileToReceive(fileId: fileId, userApprovedRelays: userApprovedRelays, encrypted: encrypted))
if case .cmdOk = r { return }
logger.error("setFileToReceive error: \(responseError(r))")
}
@@ -5,5 +5,5 @@
"CFBundleName" = "SimpleX NSE";
/* Copyright (human-readable) */
"NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved.";
"NSHumanReadableCopyright" = "Copyright © 2024 SimpleX Chat. All rights reserved.";
+82 -37
View File
@@ -36,11 +36,6 @@
5C35CFC827B2782E00FB6C6D /* BGManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C35CFC727B2782E00FB6C6D /* BGManager.swift */; };
5C35CFCB27B2E91D00FB6C6D /* NtfManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C35CFCA27B2E91D00FB6C6D /* NtfManager.swift */; };
5C36027327F47AD5009F19D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C36027227F47AD5009F19D9 /* AppDelegate.swift */; };
5C371E6A2BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C371E652BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH.a */; };
5C371E6B2BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C371E662BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH-ghc9.6.3.a */; };
5C371E6C2BA9FDFA00100AD3 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C371E672BA9FDFA00100AD3 /* libgmpxx.a */; };
5C371E6D2BA9FDFA00100AD3 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C371E682BA9FDFA00100AD3 /* libffi.a */; };
5C371E6E2BA9FDFA00100AD3 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C371E692BA9FDFA00100AD3 /* libgmp.a */; };
5C3A88CE27DF50170060F1C2 /* DetermineWidth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3A88CD27DF50170060F1C2 /* DetermineWidth.swift */; };
5C3A88D127DF57800060F1C2 /* FramedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3A88D027DF57800060F1C2 /* FramedItemView.swift */; };
5C3CCFCC2AE6BD3100C3F0C3 /* ConnectDesktopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3CCFCB2AE6BD3100C3F0C3 /* ConnectDesktopView.swift */; };
@@ -144,7 +139,6 @@
5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; };
5CEBD7462A5C0A8F00665FE2 /* KeyboardPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */; };
5CEBD7482A5F115D00665FE2 /* SetDeliveryReceiptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */; };
5CF9371E2B23429500E1D781 /* ConcurrentQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF9371D2B23429500E1D781 /* ConcurrentQueue.swift */; };
5CF937202B24DE8C00E1D781 /* SharedFileSubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */; };
5CF937232B2503D000E1D781 /* NSESubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF937212B25034A00E1D781 /* NSESubscriber.swift */; };
5CFA59C42860BC6200863A68 /* MigrateToAppGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */; };
@@ -173,6 +167,7 @@
646BB38E283FDB6D001CE359 /* LocalAuthenticationUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 646BB38D283FDB6D001CE359 /* LocalAuthenticationUtils.swift */; };
647F090E288EA27B00644C40 /* GroupMemberInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647F090D288EA27B00644C40 /* GroupMemberInfoView.swift */; };
648010AB281ADD15009009B9 /* CIFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648010AA281ADD15009009B9 /* CIFileView.swift */; };
648679AB2BC96A74006456E7 /* ChatItemForwardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */; };
649BCDA0280460FD00C3A862 /* ComposeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCD9F280460FD00C3A862 /* ComposeImageView.swift */; };
649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCDA12805D6EF00C3A862 /* CIImageView.swift */; };
64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; };
@@ -188,12 +183,20 @@
8C69FE7D2B8C7D2700267E38 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */; };
8C7D949A2B88952700B7B9E1 /* MigrateToDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */; };
8C7DF3202B7CDB0A00C886D0 /* MigrateFromDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */; };
8C81482C2BD91CD4002CBEC3 /* AudioDevicePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C81482B2BD91CD4002CBEC3 /* AudioDevicePicker.swift */; };
8CC4ED902BD7B8530078AEE8 /* CallAudioDeviceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC4ED8F2BD7B8530078AEE8 /* CallAudioDeviceManager.swift */; };
8CC956EE2BC0041000412A11 /* NetworkObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC956ED2BC0041000412A11 /* NetworkObserver.swift */; };
D7197A1829AE89660055C05A /* WebRTC in Frameworks */ = {isa = PBXBuildFile; productRef = D7197A1729AE89660055C05A /* WebRTC */; };
D72A9088294BD7A70047C86D /* NativeTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72A9087294BD7A70047C86D /* NativeTextEditor.swift */; };
D741547829AF89AF0022400A /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D741547729AF89AF0022400A /* StoreKit.framework */; };
D741547A29AF90B00022400A /* PushKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D741547929AF90B00022400A /* PushKit.framework */; };
D77B92DC2952372200A5A1CC /* SwiftyGif in Frameworks */ = {isa = PBXBuildFile; productRef = D77B92DB2952372200A5A1CC /* SwiftyGif */; };
D7F0E33929964E7E0068AF69 /* LZString in Frameworks */ = {isa = PBXBuildFile; productRef = D7F0E33829964E7E0068AF69 /* LZString */; };
E5D68D3F2C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5D68D3A2C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c.a */; };
E5D68D402C22D78C00CBA347 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5D68D3B2C22D78C00CBA347 /* libffi.a */; };
E5D68D412C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5D68D3C2C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c-ghc9.6.3.a */; };
E5D68D422C22D78C00CBA347 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5D68D3D2C22D78C00CBA347 /* libgmp.a */; };
E5D68D432C22D78C00CBA347 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5D68D3E2C22D78C00CBA347 /* libgmpxx.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -291,11 +294,6 @@
5C371E4E2BA9AAA200100AD3 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = "<group>"; };
5C371E4F2BA9AB6400100AD3 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = "hu.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
5C371E502BA9AB6400100AD3 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/InfoPlist.strings; sourceTree = "<group>"; };
5C371E652BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH.a"; sourceTree = "<group>"; };
5C371E662BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH-ghc9.6.3.a"; sourceTree = "<group>"; };
5C371E672BA9FDFA00100AD3 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5C371E682BA9FDFA00100AD3 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5C371E692BA9FDFA00100AD3 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5C3A88CD27DF50170060F1C2 /* DetermineWidth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetermineWidth.swift; sourceTree = "<group>"; };
5C3A88D027DF57800060F1C2 /* FramedItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FramedItemView.swift; sourceTree = "<group>"; };
5C3CCFCB2AE6BD3100C3F0C3 /* ConnectDesktopView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectDesktopView.swift; sourceTree = "<group>"; };
@@ -437,7 +435,6 @@
5CEACCEC27DEA495000BD591 /* MsgContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MsgContentView.swift; sourceTree = "<group>"; };
5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardPadding.swift; sourceTree = "<group>"; };
5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDeliveryReceiptsView.swift; sourceTree = "<group>"; };
5CF9371D2B23429500E1D781 /* ConcurrentQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConcurrentQueue.swift; sourceTree = "<group>"; };
5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedFileSubscriber.swift; sourceTree = "<group>"; };
5CF937212B25034A00E1D781 /* NSESubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSESubscriber.swift; sourceTree = "<group>"; };
5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToAppGroupView.swift; sourceTree = "<group>"; };
@@ -465,6 +462,7 @@
646BB38D283FDB6D001CE359 /* LocalAuthenticationUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAuthenticationUtils.swift; sourceTree = "<group>"; };
647F090D288EA27B00644C40 /* GroupMemberInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupMemberInfoView.swift; sourceTree = "<group>"; };
648010AA281ADD15009009B9 /* CIFileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIFileView.swift; sourceTree = "<group>"; };
648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemForwardingView.swift; sourceTree = "<group>"; };
6493D667280ED77F007A76FB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
649BCD9F280460FD00C3A862 /* ComposeImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeImageView.swift; sourceTree = "<group>"; };
649BCDA12805D6EF00C3A862 /* CIImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIImageView.swift; sourceTree = "<group>"; };
@@ -482,10 +480,18 @@
8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToDevice.swift; sourceTree = "<group>"; };
8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateFromDevice.swift; sourceTree = "<group>"; };
8C81482B2BD91CD4002CBEC3 /* AudioDevicePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioDevicePicker.swift; sourceTree = "<group>"; };
8CC4ED8F2BD7B8530078AEE8 /* CallAudioDeviceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallAudioDeviceManager.swift; sourceTree = "<group>"; };
8CC956ED2BC0041000412A11 /* NetworkObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkObserver.swift; sourceTree = "<group>"; };
D72A9087294BD7A70047C86D /* NativeTextEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeTextEditor.swift; sourceTree = "<group>"; };
D741547729AF89AF0022400A /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/StoreKit.framework; sourceTree = DEVELOPER_DIR; };
D741547929AF90B00022400A /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/PushKit.framework; sourceTree = DEVELOPER_DIR; };
D7AA2C3429A936B400737B40 /* MediaEncryption.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = MediaEncryption.playground; path = Shared/MediaEncryption.playground; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
E5D68D3A2C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c.a"; sourceTree = "<group>"; };
E5D68D3B2C22D78C00CBA347 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
E5D68D3C2C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c-ghc9.6.3.a"; sourceTree = "<group>"; };
E5D68D3D2C22D78C00CBA347 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
E5D68D3E2C22D78C00CBA347 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -523,13 +529,13 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5C371E6A2BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH.a in Frameworks */,
5C371E6E2BA9FDFA00100AD3 /* libgmp.a in Frameworks */,
5C371E6D2BA9FDFA00100AD3 /* libffi.a in Frameworks */,
5C371E6C2BA9FDFA00100AD3 /* libgmpxx.a in Frameworks */,
5C371E6B2BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH-ghc9.6.3.a in Frameworks */,
E5D68D412C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c-ghc9.6.3.a in Frameworks */,
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
E5D68D3F2C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c.a in Frameworks */,
E5D68D422C22D78C00CBA347 /* libgmp.a in Frameworks */,
E5D68D402C22D78C00CBA347 /* libffi.a in Frameworks */,
E5D68D432C22D78C00CBA347 /* libgmpxx.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -547,6 +553,8 @@
5C55A922283CEDE600C4E99E /* SoundPlayer.swift */,
18415323A4082FC92887F906 /* WebRTCClient.swift */,
18415B08031E8FB0F7FC27F9 /* CallViewRenderers.swift */,
8CC4ED8F2BD7B8530078AEE8 /* CallAudioDeviceManager.swift */,
8C81482B2BD91CD4002CBEC3 /* AudioDevicePicker.swift */,
);
path = Call;
sourceTree = "<group>";
@@ -585,6 +593,7 @@
5CBE6C11294487F7002D9531 /* VerifyCodeView.swift */,
5CBE6C132944CC12002D9531 /* ScanCodeView.swift */,
64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */,
648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */,
);
path = Chat;
sourceTree = "<group>";
@@ -592,11 +601,11 @@
5C764E5C279C70B7000C6508 /* Libraries */ = {
isa = PBXGroup;
children = (
5C371E682BA9FDFA00100AD3 /* libffi.a */,
5C371E692BA9FDFA00100AD3 /* libgmp.a */,
5C371E672BA9FDFA00100AD3 /* libgmpxx.a */,
5C371E662BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH-ghc9.6.3.a */,
5C371E652BA9FDFA00100AD3 /* libHSsimplex-chat-5.6.0.3-4nskXXUBgQ3Bvo5v4RfruH.a */,
E5D68D3B2C22D78C00CBA347 /* libffi.a */,
E5D68D3D2C22D78C00CBA347 /* libgmp.a */,
E5D68D3E2C22D78C00CBA347 /* libgmpxx.a */,
E5D68D3C2C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c-ghc9.6.3.a */,
E5D68D3A2C22D78C00CBA347 /* libHSsimplex-chat-5.8.1.0-GEbUSGuGADZH0bnStuks0c.a */,
);
path = Libraries;
sourceTree = "<group>";
@@ -625,6 +634,7 @@
5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */,
5C93293E2928E0FD0090FFF9 /* AudioRecPlay.swift */,
5CBD2859295711D700EC2CF4 /* ImageUtils.swift */,
8CC956ED2BC0041000412A11 /* NetworkObserver.swift */,
);
path = Model;
sourceTree = "<group>";
@@ -800,7 +810,6 @@
isa = PBXGroup;
children = (
5CDCAD5128186DE400503DA2 /* SimpleX NSE.entitlements */,
5CF9371D2B23429500E1D781 /* ConcurrentQueue.swift */,
5CDCAD472818589900503DA2 /* NotificationService.swift */,
5CDCAD492818589900503DA2 /* Info.plist */,
5CB0BA862826CB3A00B3292C /* InfoPlist.strings */,
@@ -1161,6 +1170,7 @@
5C10D88828EED12E00E58BF0 /* ContactConnectionInfo.swift in Sources */,
5CBE6C12294487F7002D9531 /* VerifyCodeView.swift in Sources */,
3CDBCF4227FAE51000354CDD /* ComposeLinkView.swift in Sources */,
648679AB2BC96A74006456E7 /* ChatItemForwardingView.swift in Sources */,
64466DC829FC2B3B00E3D48D /* CreateSimpleXAddress.swift in Sources */,
3CDBCF4827FF621E00354CDD /* CILinkView.swift in Sources */,
5C7505A827B6D34800BE3227 /* ChatInfoToolbar.swift in Sources */,
@@ -1179,6 +1189,7 @@
5C9A5BDB2871E05400A5B906 /* SetNotificationsMode.swift in Sources */,
5CB0BA8E2827126500B3292C /* OnboardingView.swift in Sources */,
6442E0BE2880182D00CEC0F9 /* GroupChatInfoView.swift in Sources */,
8CC956EE2BC0041000412A11 /* NetworkObserver.swift in Sources */,
5C2E261227A30FEA00F70299 /* TerminalView.swift in Sources */,
5C9FD96E27A5D6ED0075386C /* SendMessageView.swift in Sources */,
5CA7DFC329302AF000F7FDDE /* AppSheet.swift in Sources */,
@@ -1232,6 +1243,7 @@
5CB346E52868AA7F001FD2EF /* SuspendChat.swift in Sources */,
5C9C2DA52894777E00CC63B1 /* GroupProfileView.swift in Sources */,
5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */,
8C81482C2BD91CD4002CBEC3 /* AudioDevicePicker.swift in Sources */,
5CCB939C297EFCB100399E78 /* NavStackCompat.swift in Sources */,
5C764E89279CBCB3000C6508 /* ChatModel.swift in Sources */,
5C971E1D27AEBEF600C8A3CE /* ChatInfoView.swift in Sources */,
@@ -1261,6 +1273,7 @@
18415B0585EB5A9A0A7CA8CD /* PressedButtonStyle.swift in Sources */,
1841560FD1CD447955474C1D /* UserProfilesView.swift in Sources */,
64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */,
8CC4ED902BD7B8530078AEE8 /* CallAudioDeviceManager.swift in Sources */,
18415C6C56DBCEC2CBBD2F11 /* WebRTCClient.swift in Sources */,
184152CEF68D2336FC2EBCB0 /* CallViewRenderers.swift in Sources */,
5CB634AD29E46CF70066AD6B /* LocalAuthView.swift in Sources */,
@@ -1285,7 +1298,6 @@
files = (
5CDCAD482818589900503DA2 /* NotificationService.swift in Sources */,
5CFE0922282EEAF60002594B /* ZoomableScrollView.swift in Sources */,
5CF9371E2B23429500E1D781 /* ConcurrentQueue.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1445,6 +1457,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@@ -1506,6 +1519,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -1534,12 +1548,16 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CLANG_ENABLE_MODULES = YES;
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 203;
CURRENT_PROJECT_VERSION = 225;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES;
GCC_OPTIMIZATION_LEVEL = s;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "SimpleX--iOS--Info.plist";
INFOPLIST_KEY_NSCameraUsageDescription = "SimpleX needs camera access to scan QR codes to connect to other users and for video calls.";
@@ -1558,11 +1576,13 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 5.6;
LLVM_LTO = YES_THIN;
MARKETING_VERSION = 5.8.1;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX;
SDKROOT = iphoneos;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};
@@ -1577,12 +1597,16 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CLANG_ENABLE_MODULES = YES;
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 203;
CURRENT_PROJECT_VERSION = 225;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_PREVIEWS = YES;
ENABLE_CODE_COVERAGE = NO;
ENABLE_PREVIEWS = NO;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "SimpleX--iOS--Info.plist";
INFOPLIST_KEY_NSCameraUsageDescription = "SimpleX needs camera access to scan QR codes to connect to other users and for video calls.";
@@ -1601,7 +1625,8 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 5.6;
LLVM_LTO = YES;
MARKETING_VERSION = 5.8.1;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX;
SDKROOT = iphoneos;
@@ -1657,12 +1682,15 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 203;
CURRENT_PROJECT_VERSION = 225;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GCC_OPTIMIZATION_LEVEL = s;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "SimpleX NSE/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = "SimpleX NSE";
@@ -1673,13 +1701,15 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 5.6;
LLVM_LTO = YES;
MARKETING_VERSION = 5.8.1;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Osize";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};
@@ -1689,12 +1719,15 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 203;
CURRENT_PROJECT_VERSION = 225;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_CODE_COVERAGE = NO;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "SimpleX NSE/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = "SimpleX NSE";
@@ -1705,13 +1738,15 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 5.6;
LLVM_LTO = YES;
MARKETING_VERSION = 5.8.1;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Osize";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
VALIDATE_PRODUCT = YES;
@@ -1723,14 +1758,17 @@
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES;
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 203;
CURRENT_PROJECT_VERSION = 225;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = NO;
GCC_OPTIMIZATION_LEVEL = s;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 SimpleX Chat. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@@ -1748,7 +1786,8 @@
"$(inherited)",
"$(PROJECT_DIR)/Libraries/sim",
);
MARKETING_VERSION = 5.6;
LLVM_LTO = YES;
MARKETING_VERSION = 5.8.1;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos;
@@ -1757,6 +1796,7 @@
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_INCLUDE_PATHS = "";
SWIFT_OBJC_BRIDGING_HEADER = ./SimpleXChat/SimpleX.h;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
@@ -1769,14 +1809,17 @@
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES;
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 203;
CURRENT_PROJECT_VERSION = 225;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = NO;
ENABLE_CODE_COVERAGE = NO;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2022 SimpleX Chat. All rights reserved.";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
@@ -1794,7 +1837,8 @@
"$(inherited)",
"$(PROJECT_DIR)/Libraries/sim",
);
MARKETING_VERSION = 5.6;
LLVM_LTO = YES;
MARKETING_VERSION = 5.8.1;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos;
@@ -1803,6 +1847,7 @@
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_INCLUDE_PATHS = "";
SWIFT_OBJC_BRIDGING_HEADER = ./SimpleXChat/SimpleX.h;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
+5
View File
@@ -87,6 +87,11 @@ public func chatInitControllerRemovingDatabases() {
public func chatCloseStore() {
// Prevent crash when exiting the app with already closed store (for example, after changing a database passpharase)
guard hasChatCtrl() else {
logger.error("chatCloseStore: already closed, chatCtrl is nil")
return
}
let err = fromCString(chat_close_store(getChatCtrl()))
if err != "" {
logger.error("chatCloseStore error: \(err)")
+175 -38
View File
@@ -32,8 +32,6 @@ public enum ChatCommand {
case setTempFolder(tempFolder: String)
case setFilesFolder(filesFolder: String)
case apiSetEncryptLocalFiles(enable: Bool)
case apiSetPQEncryption(enable: Bool)
case apiSetContactPQ(contactId: Int64, enable: Bool)
case apiExportArchive(config: ArchiveConfig)
case apiImportArchive(config: ArchiveConfig)
case apiDeleteStorage
@@ -50,6 +48,7 @@ public enum ChatCommand {
case apiDeleteChatItem(type: ChatType, id: Int64, itemId: Int64, mode: CIDeleteMode)
case apiDeleteMemberChatItem(groupId: Int64, groupMemberId: Int64, itemId: Int64)
case apiChatItemReaction(type: ChatType, id: Int64, itemId: Int64, add: Bool, reaction: MsgReaction)
case apiForwardChatItem(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemId: Int64, ttl: Int?)
case apiGetNtfToken
case apiRegisterToken(token: DeviceToken, notificationMode: NotificationsMode)
case apiVerifyToken(token: DeviceToken, nonce: String, code: String)
@@ -77,11 +76,14 @@ public enum ChatCommand {
case apiGetChatItemTTL(userId: Int64)
case apiSetNetworkConfig(networkConfig: NetCfg)
case apiGetNetworkConfig
case apiSetNetworkInfo(networkInfo: UserNetworkInfo)
case reconnectAllServers
case apiSetChatSettings(type: ChatType, id: Int64, chatSettings: ChatSettings)
case apiSetMemberSettings(groupId: Int64, groupMemberId: Int64, memberSettings: GroupMemberSettings)
case apiContactInfo(contactId: Int64)
case apiGroupMemberInfo(groupId: Int64, groupMemberId: Int64)
case apiContactQueueInfo(contactId: Int64)
case apiGroupMemberQueueInfo(groupId: Int64, groupMemberId: Int64)
case apiSwitchContact(contactId: Int64)
case apiSwitchGroupMember(groupId: Int64, groupMemberId: Int64)
case apiAbortSwitchContact(contactId: Int64)
@@ -123,8 +125,8 @@ public enum ChatCommand {
case apiGetNetworkStatuses
case apiChatRead(type: ChatType, id: Int64, itemRange: (Int64, Int64))
case apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool)
case receiveFile(fileId: Int64, encrypted: Bool?, inline: Bool?)
case setFileToReceive(fileId: Int64, encrypted: Bool?)
case receiveFile(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool?, inline: Bool?)
case setFileToReceive(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool?)
case cancelFile(fileId: Int64)
// remote desktop commands
case setLocalDeviceName(displayName: String)
@@ -170,8 +172,6 @@ public enum ChatCommand {
case let .setTempFolder(tempFolder): return "/_temp_folder \(tempFolder)"
case let .setFilesFolder(filesFolder): return "/_files_folder \(filesFolder)"
case let .apiSetEncryptLocalFiles(enable): return "/_files_encrypt \(onOff(enable))"
case let .apiSetPQEncryption(enable): return "/pq \(onOff(enable))"
case let .apiSetContactPQ(contactId, enable): return "/_pq @\(contactId) \(onOff(enable))"
case let .apiExportArchive(cfg): return "/_db export \(encodeJSON(cfg))"
case let .apiImportArchive(cfg): return "/_db import \(encodeJSON(cfg))"
case .apiDeleteStorage: return "/_db delete"
@@ -194,6 +194,9 @@ public enum ChatCommand {
case let .apiDeleteChatItem(type, id, itemId, mode): return "/_delete item \(ref(type, id)) \(itemId) \(mode.rawValue)"
case let .apiDeleteMemberChatItem(groupId, groupMemberId, itemId): return "/_delete member item #\(groupId) \(groupMemberId) \(itemId)"
case let .apiChatItemReaction(type, id, itemId, add, reaction): return "/_reaction \(ref(type, id)) \(itemId) \(onOff(add)) \(encodeJSON(reaction))"
case let .apiForwardChatItem(toChatType, toChatId, fromChatType, fromChatId, itemId, ttl):
let ttlStr = ttl != nil ? "\(ttl!)" : "default"
return "/_forward \(ref(toChatType, toChatId)) \(ref(fromChatType, fromChatId)) \(itemId) ttl=\(ttlStr)"
case .apiGetNtfToken: return "/_ntf get "
case let .apiRegisterToken(token, notificationMode): return "/_ntf register \(token.cmdString) \(notificationMode.rawValue)"
case let .apiVerifyToken(token, nonce, code): return "/_ntf verify \(token.cmdString) \(nonce) \(code)"
@@ -221,11 +224,14 @@ public enum ChatCommand {
case let .apiGetChatItemTTL(userId): return "/_ttl \(userId)"
case let .apiSetNetworkConfig(networkConfig): return "/_network \(encodeJSON(networkConfig))"
case .apiGetNetworkConfig: return "/network"
case let .apiSetNetworkInfo(networkInfo): return "/_network info \(encodeJSON(networkInfo))"
case .reconnectAllServers: return "/reconnect"
case let .apiSetChatSettings(type, id, chatSettings): return "/_settings \(ref(type, id)) \(encodeJSON(chatSettings))"
case let .apiSetMemberSettings(groupId, groupMemberId, memberSettings): return "/_member settings #\(groupId) \(groupMemberId) \(encodeJSON(memberSettings))"
case let .apiContactInfo(contactId): return "/_info @\(contactId)"
case let .apiGroupMemberInfo(groupId, groupMemberId): return "/_info #\(groupId) \(groupMemberId)"
case let .apiContactQueueInfo(contactId): return "/_queue info @\(contactId)"
case let .apiGroupMemberQueueInfo(groupId, groupMemberId): return "/_queue info #\(groupId) \(groupMemberId)"
case let .apiSwitchContact(contactId): return "/_switch @\(contactId)"
case let .apiSwitchGroupMember(groupId, groupMemberId): return "/_switch #\(groupId) \(groupMemberId)"
case let .apiAbortSwitchContact(contactId): return "/_abort switch @\(contactId)"
@@ -280,8 +286,8 @@ public enum ChatCommand {
case .apiGetNetworkStatuses: return "/_network_statuses"
case let .apiChatRead(type, id, itemRange: (from, to)): return "/_read chat \(ref(type, id)) from=\(from) to=\(to)"
case let .apiChatUnread(type, id, unreadChat): return "/_unread chat \(ref(type, id)) \(onOff(unreadChat))"
case let .receiveFile(fileId, encrypt, inline): return "/freceive \(fileId)\(onOffParam("encrypt", encrypt))\(onOffParam("inline", inline))"
case let .setFileToReceive(fileId, encrypt): return "/_set_file_to_receive \(fileId)\(onOffParam("encrypt", encrypt))"
case let .receiveFile(fileId, userApprovedRelays, encrypt, inline): return "/freceive \(fileId)\(onOffParam("approved_relays", userApprovedRelays))\(onOffParam("encrypt", encrypt))\(onOffParam("inline", inline))"
case let .setFileToReceive(fileId, userApprovedRelays, encrypt): return "/_set_file_to_receive \(fileId)\(onOffParam("approved_relays", userApprovedRelays))\(onOffParam("encrypt", encrypt))"
case let .cancelFile(fileId): return "/fcancel \(fileId)"
case let .setLocalDeviceName(displayName): return "/set device name \(displayName)"
case let .connectRemoteCtrl(xrcpInv): return "/connect remote ctrl \(xrcpInv)"
@@ -322,8 +328,6 @@ public enum ChatCommand {
case .setTempFolder: return "setTempFolder"
case .setFilesFolder: return "setFilesFolder"
case .apiSetEncryptLocalFiles: return "apiSetEncryptLocalFiles"
case .apiSetPQEncryption: return "apiSetPQEncryption"
case .apiSetContactPQ: return "apiSetContactPQ"
case .apiExportArchive: return "apiExportArchive"
case .apiImportArchive: return "apiImportArchive"
case .apiDeleteStorage: return "apiDeleteStorage"
@@ -341,6 +345,7 @@ public enum ChatCommand {
case .apiConnectContactViaAddress: return "apiConnectContactViaAddress"
case .apiDeleteMemberChatItem: return "apiDeleteMemberChatItem"
case .apiChatItemReaction: return "apiChatItemReaction"
case .apiForwardChatItem: return "apiForwardChatItem"
case .apiGetNtfToken: return "apiGetNtfToken"
case .apiRegisterToken: return "apiRegisterToken"
case .apiVerifyToken: return "apiVerifyToken"
@@ -368,11 +373,14 @@ public enum ChatCommand {
case .apiGetChatItemTTL: return "apiGetChatItemTTL"
case .apiSetNetworkConfig: return "apiSetNetworkConfig"
case .apiGetNetworkConfig: return "apiGetNetworkConfig"
case .apiSetNetworkInfo: return "apiSetNetworkInfo"
case .reconnectAllServers: return "reconnectAllServers"
case .apiSetChatSettings: return "apiSetChatSettings"
case .apiSetMemberSettings: return "apiSetMemberSettings"
case .apiContactInfo: return "apiContactInfo"
case .apiGroupMemberInfo: return "apiGroupMemberInfo"
case .apiContactQueueInfo: return "apiContactQueueInfo"
case .apiGroupMemberQueueInfo: return "apiGroupMemberQueueInfo"
case .apiSwitchContact: return "apiSwitchContact"
case .apiSwitchGroupMember: return "apiSwitchGroupMember"
case .apiAbortSwitchContact: return "apiAbortSwitchContact"
@@ -514,6 +522,7 @@ public enum ChatResponse: Decodable, Error {
case networkConfig(networkConfig: NetCfg)
case contactInfo(user: UserRef, contact: Contact, connectionStats_: ConnectionStats?, customUserProfile: Profile?)
case groupMemberInfo(user: UserRef, groupInfo: GroupInfo, member: GroupMember, connectionStats_: ConnectionStats?)
case queueInfo(user: UserRef, rcvMsgInfo: RcvMsgInfo?, queueInfo: QueueInfo)
case contactSwitchStarted(user: UserRef, contact: Contact, connectionStats: ConnectionStats)
case groupMemberSwitchStarted(user: UserRef, groupInfo: GroupInfo, member: GroupMember, connectionStats: ConnectionStats)
case contactSwitchAborted(user: UserRef, contact: Contact, connectionStats: ConnectionStats)
@@ -557,11 +566,6 @@ public enum ChatResponse: Decodable, Error {
case contactRequestRejected(user: UserRef)
case contactUpdated(user: UserRef, toContact: Contact)
case groupMemberUpdated(user: UserRef, groupInfo: GroupInfo, fromMember: GroupMember, toMember: GroupMember)
// TODO remove events below
case contactsSubscribed(server: String, contactRefs: [ContactRef])
case contactsDisconnected(server: String, contactRefs: [ContactRef])
case contactSubSummary(user: UserRef, contactSubscriptions: [ContactSubStatus])
// TODO remove events above
case networkStatus(networkStatus: NetworkStatus, connections: [String])
case networkStatuses(user_: UserRef?, networkStatuses: [ConnNetworkStatus])
case groupSubscribed(user: UserRef, groupInfo: GroupRef)
@@ -618,7 +622,8 @@ public enum ChatResponse: Decodable, Error {
case rcvStandaloneFileComplete(user: UserRef, targetPath: String, rcvFileTransfer: RcvFileTransfer)
case rcvFileCancelled(user: UserRef, chatItem_: AChatItem?, rcvFileTransfer: RcvFileTransfer)
case rcvFileSndCancelled(user: UserRef, chatItem: AChatItem, rcvFileTransfer: RcvFileTransfer)
case rcvFileError(user: UserRef, chatItem_: AChatItem?, rcvFileTransfer: RcvFileTransfer)
case rcvFileError(user: UserRef, chatItem_: AChatItem?, agentError: AgentErrorType, rcvFileTransfer: RcvFileTransfer)
case rcvFileWarning(user: UserRef, chatItem_: AChatItem?, agentError: AgentErrorType, rcvFileTransfer: RcvFileTransfer)
// sending file events
case sndFileStart(user: UserRef, chatItem: AChatItem, sndFileTransfer: SndFileTransfer)
case sndFileComplete(user: UserRef, chatItem: AChatItem, sndFileTransfer: SndFileTransfer)
@@ -631,7 +636,8 @@ public enum ChatResponse: Decodable, Error {
case sndFileCompleteXFTP(user: UserRef, chatItem: AChatItem, fileTransferMeta: FileTransferMeta)
case sndStandaloneFileComplete(user: UserRef, fileTransferMeta: FileTransferMeta, rcvURIs: [String])
case sndFileCancelledXFTP(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta)
case sndFileError(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta)
case sndFileError(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, errorMessage: String)
case sndFileWarning(user: UserRef, chatItem_: AChatItem?, fileTransferMeta: FileTransferMeta, errorMessage: String)
// call events
case callInvitation(callInvitation: RcvCallInvitation)
case callOffer(user: UserRef, contact: Contact, callType: CallType, offer: WebRTCSession, sharedKey: String?, askConfirmation: Bool)
@@ -644,6 +650,7 @@ public enum ChatResponse: Decodable, Error {
case ntfMessages(user_: User?, connEntity_: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo])
case ntfMessage(user: UserRef, connEntity: ConnectionEntity, ntfMessage: NtfMsgInfo)
case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection)
case contactDisabled(user: UserRef, contact: Contact)
// remote desktop responses/events
case remoteCtrlList(remoteCtrls: [RemoteCtrlInfo])
case remoteCtrlFound(remoteCtrl: RemoteCtrlInfo, ctrlAppInfo_: CtrlAppInfo?, appVersion: String, compatible: Bool)
@@ -652,7 +659,6 @@ public enum ChatResponse: Decodable, Error {
case remoteCtrlConnected(remoteCtrl: RemoteCtrlInfo)
case remoteCtrlStopped(rcsState: RemoteCtrlSessionState, rcStopReason: RemoteCtrlStopReason)
// pq
case contactPQAllowed(user: UserRef, contact: Contact, pqEncryption: Bool)
case contactPQEnabled(user: UserRef, contact: Contact, pqEnabled: Bool)
// misc
case versionInfo(versionInfo: CoreVersionInfo, chatMigrations: [UpMigration], agentMigrations: [UpMigration])
@@ -681,6 +687,7 @@ public enum ChatResponse: Decodable, Error {
case .networkConfig: return "networkConfig"
case .contactInfo: return "contactInfo"
case .groupMemberInfo: return "groupMemberInfo"
case .queueInfo: return "queueInfo"
case .contactSwitchStarted: return "contactSwitchStarted"
case .groupMemberSwitchStarted: return "groupMemberSwitchStarted"
case .contactSwitchAborted: return "contactSwitchAborted"
@@ -724,9 +731,6 @@ public enum ChatResponse: Decodable, Error {
case .contactRequestRejected: return "contactRequestRejected"
case .contactUpdated: return "contactUpdated"
case .groupMemberUpdated: return "groupMemberUpdated"
case .contactsSubscribed: return "contactsSubscribed"
case .contactsDisconnected: return "contactsDisconnected"
case .contactSubSummary: return "contactSubSummary"
case .networkStatus: return "networkStatus"
case .networkStatuses: return "networkStatuses"
case .groupSubscribed: return "groupSubscribed"
@@ -782,6 +786,7 @@ public enum ChatResponse: Decodable, Error {
case .rcvFileCancelled: return "rcvFileCancelled"
case .rcvFileSndCancelled: return "rcvFileSndCancelled"
case .rcvFileError: return "rcvFileError"
case .rcvFileWarning: return "rcvFileWarning"
case .sndFileStart: return "sndFileStart"
case .sndFileComplete: return "sndFileComplete"
case .sndFileCancelled: return "sndFileCancelled"
@@ -794,6 +799,7 @@ public enum ChatResponse: Decodable, Error {
case .sndStandaloneFileComplete: return "sndStandaloneFileComplete"
case .sndFileCancelledXFTP: return "sndFileCancelledXFTP"
case .sndFileError: return "sndFileError"
case .sndFileWarning: return "sndFileWarning"
case .callInvitation: return "callInvitation"
case .callOffer: return "callOffer"
case .callAnswer: return "callAnswer"
@@ -805,14 +811,14 @@ public enum ChatResponse: Decodable, Error {
case .ntfMessages: return "ntfMessages"
case .ntfMessage: return "ntfMessage"
case .contactConnectionDeleted: return "contactConnectionDeleted"
case .contactDisabled: return "contactDisabled"
case .remoteCtrlList: return "remoteCtrlList"
case .remoteCtrlFound: return "remoteCtrlFound"
case .remoteCtrlConnecting: return "remoteCtrlConnecting"
case .remoteCtrlSessionCode: return "remoteCtrlSessionCode"
case .remoteCtrlConnected: return "remoteCtrlConnected"
case .remoteCtrlStopped: return "remoteCtrlStopped"
case .contactPQAllowed: return "contactPQAllowed"
case .contactPQEnabled: return "contactPQAllowed"
case .contactPQEnabled: return "contactPQEnabled"
case .versionInfo: return "versionInfo"
case .cmdOk: return "cmdOk"
case .chatCmdError: return "chatCmdError"
@@ -842,6 +848,9 @@ public enum ChatResponse: Decodable, Error {
case let .networkConfig(networkConfig): return String(describing: networkConfig)
case let .contactInfo(u, contact, connectionStats_, customUserProfile): return withUser(u, "contact: \(String(describing: contact))\nconnectionStats_: \(String(describing: connectionStats_))\ncustomUserProfile: \(String(describing: customUserProfile))")
case let .groupMemberInfo(u, groupInfo, member, connectionStats_): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionStats_: \(String(describing: connectionStats_))")
case let .queueInfo(u, rcvMsgInfo, queueInfo):
let msgInfo = if let info = rcvMsgInfo { encodeJSON(rcvMsgInfo) } else { "none" }
return withUser(u, "rcvMsgInfo: \(msgInfo)\nqueueInfo: \(encodeJSON(queueInfo))")
case let .contactSwitchStarted(u, contact, connectionStats): return withUser(u, "contact: \(String(describing: contact))\nconnectionStats: \(String(describing: connectionStats))")
case let .groupMemberSwitchStarted(u, groupInfo, member, connectionStats): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionStats: \(String(describing: connectionStats))")
case let .contactSwitchAborted(u, contact, connectionStats): return withUser(u, "contact: \(String(describing: contact))\nconnectionStats: \(String(describing: connectionStats))")
@@ -885,9 +894,6 @@ public enum ChatResponse: Decodable, Error {
case .contactRequestRejected: return noDetails
case let .contactUpdated(u, toContact): return withUser(u, String(describing: toContact))
case let .groupMemberUpdated(u, groupInfo, fromMember, toMember): return withUser(u, "groupInfo: \(groupInfo)\nfromMember: \(fromMember)\ntoMember: \(toMember)")
case let .contactsSubscribed(server, contactRefs): return "server: \(server)\ncontacts:\n\(String(describing: contactRefs))"
case let .contactsDisconnected(server, contactRefs): return "server: \(server)\ncontacts:\n\(String(describing: contactRefs))"
case let .contactSubSummary(u, contactSubscriptions): return withUser(u, String(describing: contactSubscriptions))
case let .networkStatus(status, conns): return "networkStatus: \(String(describing: status))\nconnections: \(String(describing: conns))"
case let .networkStatuses(u, statuses): return withUser(u, String(describing: statuses))
case let .groupSubscribed(u, groupInfo): return withUser(u, String(describing: groupInfo))
@@ -942,7 +948,8 @@ public enum ChatResponse: Decodable, Error {
case let .rcvFileComplete(u, chatItem): return withUser(u, String(describing: chatItem))
case let .rcvFileCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem))
case let .rcvFileSndCancelled(u, chatItem, _): return withUser(u, String(describing: chatItem))
case let .rcvFileError(u, chatItem, _): return withUser(u, String(describing: chatItem))
case let .rcvFileError(u, chatItem, agentError, _): return withUser(u, "agentError: \(String(describing: agentError))\nchatItem: \(String(describing: chatItem))")
case let .rcvFileWarning(u, chatItem, agentError, _): return withUser(u, "agentError: \(String(describing: agentError))\nchatItem: \(String(describing: chatItem))")
case let .sndFileStart(u, chatItem, _): return withUser(u, String(describing: chatItem))
case let .sndFileComplete(u, chatItem, _): return withUser(u, String(describing: chatItem))
case let .sndFileCancelled(u, chatItem, _, _): return withUser(u, String(describing: chatItem))
@@ -954,7 +961,8 @@ public enum ChatResponse: Decodable, Error {
case let .sndFileCompleteXFTP(u, chatItem, _): return withUser(u, String(describing: chatItem))
case let .sndStandaloneFileComplete(u, _, rcvURIs): return withUser(u, String(rcvURIs.count))
case let .sndFileCancelledXFTP(u, chatItem, _): return withUser(u, String(describing: chatItem))
case let .sndFileError(u, chatItem, _): return withUser(u, String(describing: chatItem))
case let .sndFileError(u, chatItem, _, err): return withUser(u, "error: \(String(describing: err))\nchatItem: \(String(describing: chatItem))")
case let .sndFileWarning(u, chatItem, _, err): return withUser(u, "error: \(String(describing: err))\nchatItem: \(String(describing: chatItem))")
case let .callInvitation(inv): return String(describing: inv)
case let .callOffer(u, contact, callType, offer, sharedKey, askConfirmation): return withUser(u, "contact: \(contact.id)\ncallType: \(String(describing: callType))\nsharedKey: \(sharedKey ?? "")\naskConfirmation: \(askConfirmation)\noffer: \(String(describing: offer))")
case let .callAnswer(u, contact, answer): return withUser(u, "contact: \(contact.id)\nanswer: \(String(describing: answer))")
@@ -966,13 +974,13 @@ public enum ChatResponse: Decodable, Error {
case let .ntfMessages(u, connEntity, msgTs, ntfMessages): return withUser(u, "connEntity: \(String(describing: connEntity))\nmsgTs: \(String(describing: msgTs))\nntfMessages: \(String(describing: ntfMessages))")
case let .ntfMessage(u, connEntity, ntfMessage): return withUser(u, "connEntity: \(String(describing: connEntity))\nntfMessage: \(String(describing: ntfMessage))")
case let .contactConnectionDeleted(u, connection): return withUser(u, String(describing: connection))
case let .contactDisabled(u, contact): return withUser(u, String(describing: contact))
case let .remoteCtrlList(remoteCtrls): return String(describing: remoteCtrls)
case let .remoteCtrlFound(remoteCtrl, ctrlAppInfo_, appVersion, compatible): return "remoteCtrl:\n\(String(describing: remoteCtrl))\nctrlAppInfo_:\n\(String(describing: ctrlAppInfo_))\nappVersion: \(appVersion)\ncompatible: \(compatible)"
case let .remoteCtrlConnecting(remoteCtrl_, ctrlAppInfo, appVersion): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nctrlAppInfo:\n\(String(describing: ctrlAppInfo))\nappVersion: \(appVersion)"
case let .remoteCtrlSessionCode(remoteCtrl_, sessionCode): return "remoteCtrl_:\n\(String(describing: remoteCtrl_))\nsessionCode: \(sessionCode)"
case let .remoteCtrlConnected(remoteCtrl): return String(describing: remoteCtrl)
case .remoteCtrlStopped: return noDetails
case let .contactPQAllowed(u, contact, pqEncryption): return withUser(u, "contact: \(String(describing: contact))\npqEncryption: \(pqEncryption)")
case let .remoteCtrlStopped(rcsState, rcStopReason): return "rcsState: \(String(describing: rcsState))\nrcStopReason: \(String(describing: rcStopReason))"
case let .contactPQEnabled(u, contact, pqEnabled): return withUser(u, "contact: \(String(describing: contact))\npqEnabled: \(pqEnabled)")
case let .versionInfo(versionInfo, chatMigrations, agentMigrations): return "\(String(describing: versionInfo))\n\nchat migrations: \(chatMigrations.map(\.upName))\n\nagent migrations: \(agentMigrations.map(\.upName))"
case .cmdOk: return noDetails
@@ -1256,12 +1264,16 @@ public struct ServerAddress: Decodable {
public struct NetCfg: Codable, Equatable {
public var socksProxy: String? = nil
var socksMode: SocksMode = .always
public var hostMode: HostMode = .publicHost
public var requiredHostMode = true
public var sessionMode: TransportSessionMode
public var smpProxyMode: SMPProxyMode = .never
public var smpProxyFallback: SMPProxyFallback = .allow
public var tcpConnectTimeout: Int // microseconds
public var tcpTimeout: Int // microseconds
public var tcpTimeoutPerKb: Int // microseconds
public var rcvConcurrency: Int // pool size
public var tcpKeepAlive: KeepAliveOpts?
public var smpPingInterval: Int // microseconds
public var smpPingCount: Int // times
@@ -1270,9 +1282,10 @@ public struct NetCfg: Codable, Equatable {
public static let defaults: NetCfg = NetCfg(
socksProxy: nil,
sessionMode: TransportSessionMode.user,
tcpConnectTimeout: 20_000_000,
tcpConnectTimeout: 25_000_000,
tcpTimeout: 15_000_000,
tcpTimeoutPerKb: 45_000,
tcpTimeoutPerKb: 10_000,
rcvConcurrency: 12,
tcpKeepAlive: KeepAliveOpts.defaults,
smpPingInterval: 1200_000_000,
smpPingCount: 3,
@@ -1282,9 +1295,10 @@ public struct NetCfg: Codable, Equatable {
public static let proxyDefaults: NetCfg = NetCfg(
socksProxy: nil,
sessionMode: TransportSessionMode.user,
tcpConnectTimeout: 30_000_000,
tcpConnectTimeout: 35_000_000,
tcpTimeout: 20_000_000,
tcpTimeoutPerKb: 60_000,
tcpTimeoutPerKb: 15_000,
rcvConcurrency: 8,
tcpKeepAlive: KeepAliveOpts.defaults,
smpPingInterval: 1200_000_000,
smpPingCount: 3,
@@ -1300,6 +1314,49 @@ public enum HostMode: String, Codable {
case publicHost = "public"
}
public enum SocksMode: String, Codable {
case always = "always"
case onion = "onion"
}
public enum SMPProxyMode: String, Codable {
case always = "always"
case unknown = "unknown"
case unprotected = "unprotected"
case never = "never"
public var text: LocalizedStringKey {
switch self {
case .always: return "always"
case .unknown: return "unknown relays"
case .unprotected: return "unprotected"
case .never: return "never"
}
}
public var id: SMPProxyMode { self }
public static let values: [SMPProxyMode] = [.always, .unknown, .unprotected, .never]
}
public enum SMPProxyFallback: String, Codable {
case allow = "allow"
case allowProtected = "allowProtected"
case prohibit = "prohibit"
public var text: LocalizedStringKey {
switch self {
case .allow: return "yes"
case .allowProtected: return "when IP hidden"
case .prohibit: return "no"
}
}
public var id: SMPProxyFallback { self }
public static let values: [SMPProxyFallback] = [.allow, .allowProtected, .prohibit]
}
public enum OnionHosts: String, Identifiable {
case no
case prefer
@@ -1703,7 +1760,6 @@ public enum ChatErrorType: Decodable {
case groupMemberNotActive
case groupMemberUserRemoved
case groupMemberNotFound
case groupMemberIntroNotFound(contactName: ContactName)
case groupCantResendInvitation(groupInfo: GroupInfo, contactName: ContactName)
case groupInternal(message: String)
case fileNotFound(message: String)
@@ -1720,11 +1776,12 @@ public enum ChatErrorType: Decodable {
case fileImageType(filePath: String)
case fileImageSize(filePath: String)
case fileNotReceived(fileId: Int64)
// case xFTPRcvFile
// case xFTPSndFile
case fileNotApproved(fileId: Int64, unknownServers: [String])
case fallbackToSMPProhibited(fileId: Int64)
case inlineFileProhibited(fileId: Int64)
case invalidQuote
case invalidForward
case forwardNoFile
case invalidChatItemUpdate
case invalidChatItemDelete
case hasCurrentCall
@@ -1827,6 +1884,7 @@ public enum AgentErrorType: Decodable {
case BROKER(brokerAddress: String, brokerErr: BrokerErrorType)
case AGENT(agentErr: SMPAgentError)
case INTERNAL(internalErr: String)
case CRITICAL(offerRestart: Bool, criticalErr: String)
case INACTIVE
}
@@ -1878,6 +1936,8 @@ public enum XFTPErrorType: Decodable {
case NO_FILE
case HAS_FILE
case FILE_IO
case TIMEOUT
case REDIRECT(redirectError: String)
case INTERNAL
}
@@ -1885,6 +1945,8 @@ public enum RCErrorType: Decodable {
case `internal`(internalErr: String)
case identity
case noLocalAddress
case newController
case notDiscovered
case tlsStartFailed
case exception(exception: String)
case ctrlAuth
@@ -1910,6 +1972,7 @@ public enum ProtocolTransportError: Decodable {
case badBlock
case largeMsg
case badSession
case noServerAuth
case handshake(handshakeErr: SMPHandshakeError)
}
@@ -1917,6 +1980,7 @@ public enum SMPHandshakeError: Decodable {
case PARSE
case VERSION
case IDENTITY
case BAD_AUTH
}
public enum SMPAgentError: Decodable {
@@ -1938,10 +2002,13 @@ public enum RemoteCtrlError: Decodable {
case badState
case busy
case timeout
case noKnownControllers
case badController
case disconnected(remoteCtrlId: Int64, reason: String)
case badInvitation
case badVersion(appVersion: String)
// case protocolError(protocolError: RemoteProtocolError)
case hTTP2Error(http2Error: String)
case protocolError
}
public struct MigrationFileLinkData: Codable {
@@ -1986,6 +2053,7 @@ public struct MigrationFileLinkData: Codable {
public struct AppSettings: Codable, Equatable {
public var networkConfig: NetCfg? = nil
public var privacyEncryptLocalFiles: Bool? = nil
public var privacyAskToApproveRelays: Bool? = nil
public var privacyAcceptImages: Bool? = nil
public var privacyLinkPreviews: Bool? = nil
public var privacyShowChatPreviews: Bool? = nil
@@ -2009,6 +2077,7 @@ public struct AppSettings: Codable, Equatable {
let def = AppSettings.defaults
if networkConfig != def.networkConfig { empty.networkConfig = networkConfig }
if privacyEncryptLocalFiles != def.privacyEncryptLocalFiles { empty.privacyEncryptLocalFiles = privacyEncryptLocalFiles }
if privacyAskToApproveRelays != def.privacyAskToApproveRelays { empty.privacyAskToApproveRelays = privacyAskToApproveRelays }
if privacyAcceptImages != def.privacyAcceptImages { empty.privacyAcceptImages = privacyAcceptImages }
if privacyLinkPreviews != def.privacyLinkPreviews { empty.privacyLinkPreviews = privacyLinkPreviews }
if privacyShowChatPreviews != def.privacyShowChatPreviews { empty.privacyShowChatPreviews = privacyShowChatPreviews }
@@ -2033,6 +2102,7 @@ public struct AppSettings: Codable, Equatable {
AppSettings (
networkConfig: NetCfg.defaults,
privacyEncryptLocalFiles: true,
privacyAskToApproveRelays: true,
privacyAcceptImages: true,
privacyLinkPreviews: true,
privacyShowChatPreviews: true,
@@ -2087,3 +2157,70 @@ public enum AppSettingsLockScreenCalls: String, Codable {
case show
case accept
}
public struct UserNetworkInfo: Codable, Equatable {
public let networkType: UserNetworkType
public let online: Bool
public init(networkType: UserNetworkType, online: Bool) {
self.networkType = networkType
self.online = online
}
}
public enum UserNetworkType: String, Codable {
case none
case cellular
case wifi
case ethernet
case other
public var text: LocalizedStringKey {
switch self {
case .none: "No network connection"
case .cellular: "Cellular"
case .wifi: "WiFi"
case .ethernet: "Wired ethernet"
case .other: "Other"
}
}
}
public struct RcvMsgInfo: Codable {
var msgId: Int64
var msgDeliveryId: Int64
var msgDeliveryStatus: String
var agentMsgId: Int64
var agentMsgMeta: String
}
public struct QueueInfo: Codable {
var qiSnd: Bool
var qiNtf: Bool
var qiSub: QSub?
var qiSize: Int
var qiMsg: MsgInfo?
}
public struct QSub: Codable {
var qSubThread: QSubThread
var qDelivered: String?
}
public enum QSubThread: String, Codable {
case noSub
case subPending
case subThread
case prohibitSub
}
public struct MsgInfo: Codable {
var msgId: String
var msgTs: Date
var msgType: MsgType
}
public enum MsgType: String, Codable {
case message
case quota
}
+32 -3
View File
@@ -23,12 +23,16 @@ public let GROUP_DEFAULT_NTF_ENABLE_PERIODIC = "ntfEnablePeriodic" // no longer
let GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES = "privacyAcceptImages"
public let GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE = "privacyTransferImagesInline" // no longer used
public let GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES = "privacyEncryptLocalFiles"
public let GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS = "privacyAskToApproveRelays"
let GROUP_DEFAULT_NTF_BADGE_COUNT = "ntgBadgeCount"
let GROUP_DEFAULT_NETWORK_USE_ONION_HOSTS = "networkUseOnionHosts"
let GROUP_DEFAULT_NETWORK_SESSION_MODE = "networkSessionMode"
let GROUP_DEFAULT_NETWORK_SMP_PROXY_MODE = "networkSMPProxyMode"
let GROUP_DEFAULT_NETWORK_SMP_PROXY_FALLBACK = "networkSMPProxyFallback"
let GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT = "networkTCPConnectTimeout"
let GROUP_DEFAULT_NETWORK_TCP_TIMEOUT = "networkTCPTimeout"
let GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB = "networkTCPTimeoutPerKb"
let GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY = "networkRcvConcurrency"
let GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL = "networkSMPPingInterval"
let GROUP_DEFAULT_NETWORK_SMP_PING_COUNT = "networkSMPPingCount"
let GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE = "networkEnableKeepAlive"
@@ -40,7 +44,7 @@ let GROUP_DEFAULT_STORE_DB_PASSPHRASE = "storeDBPassphrase"
public let GROUP_DEFAULT_INITIAL_RANDOM_DB_PASSPHRASE = "initialRandomDBPassphrase"
public let GROUP_DEFAULT_CONFIRM_DB_UPGRADES = "confirmDBUpgrades"
public let GROUP_DEFAULT_CALL_KIT_ENABLED = "callKitEnabled"
public let GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED = "pqExperimentalEnabled"
public let GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED = "pqExperimentalEnabled" // no longer used
public let APP_GROUP_NAME = "group.chat.simplex.app"
@@ -52,9 +56,12 @@ public func registerGroupDefaults() {
GROUP_DEFAULT_NTF_ENABLE_PERIODIC: false,
GROUP_DEFAULT_NETWORK_USE_ONION_HOSTS: OnionHosts.no.rawValue,
GROUP_DEFAULT_NETWORK_SESSION_MODE: TransportSessionMode.user.rawValue,
GROUP_DEFAULT_NETWORK_SMP_PROXY_MODE: SMPProxyMode.never.rawValue,
GROUP_DEFAULT_NETWORK_SMP_PROXY_FALLBACK: SMPProxyFallback.allow.rawValue,
GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT: NetCfg.defaults.tcpConnectTimeout,
GROUP_DEFAULT_NETWORK_TCP_TIMEOUT: NetCfg.defaults.tcpTimeout,
GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB: NetCfg.defaults.tcpTimeoutPerKb,
GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY: NetCfg.defaults.rcvConcurrency,
GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL: NetCfg.defaults.smpPingInterval,
GROUP_DEFAULT_NETWORK_SMP_PING_COUNT: NetCfg.defaults.smpPingCount,
GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE: NetCfg.defaults.enableKeepAlive,
@@ -67,6 +74,7 @@ public func registerGroupDefaults() {
GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES: true,
GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE: false,
GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES: true,
GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS: true,
GROUP_DEFAULT_CONFIRM_DB_UPGRADES: false,
GROUP_DEFAULT_CALL_KIT_ENABLED: true,
GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED: false,
@@ -175,6 +183,8 @@ public let privacyAcceptImagesGroupDefault = BoolDefault(defaults: groupDefaults
public let privacyEncryptLocalFilesGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES)
public let privacyAskToApproveRelaysGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS)
public let ntfBadgeCountGroupDefault = IntDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_NTF_BADGE_COUNT)
public let networkUseOnionHostsGroupDefault = EnumDefault<OnionHosts>(
@@ -189,6 +199,18 @@ public let networkSessionModeGroupDefault = EnumDefault<TransportSessionMode>(
withDefault: .user
)
public let networkSMPProxyModeGroupDefault = EnumDefault<SMPProxyMode>(
defaults: groupDefaults,
forKey: GROUP_DEFAULT_NETWORK_SMP_PROXY_MODE,
withDefault: .never
)
public let networkSMPProxyFallbackGroupDefault = EnumDefault<SMPProxyFallback>(
defaults: groupDefaults,
forKey: GROUP_DEFAULT_NETWORK_SMP_PROXY_FALLBACK,
withDefault: .allow
)
public let storeDBPassphraseGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_STORE_DB_PASSPHRASE)
public let initialRandomDBPassphraseGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_INITIAL_RANDOM_DB_PASSPHRASE)
@@ -197,8 +219,6 @@ public let confirmDBUpgradesGroupDefault = BoolDefault(defaults: groupDefaults,
public let callKitEnabledGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_CALL_KIT_ENABLED)
public let pqExperimentalEnabledDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES)
public class DateDefault {
var defaults: UserDefaults
var key: String
@@ -275,9 +295,12 @@ public func getNetCfg() -> NetCfg {
let onionHosts = networkUseOnionHostsGroupDefault.get()
let (hostMode, requiredHostMode) = onionHosts.hostMode
let sessionMode = networkSessionModeGroupDefault.get()
let smpProxyMode = networkSMPProxyModeGroupDefault.get()
let smpProxyFallback = networkSMPProxyFallbackGroupDefault.get()
let tcpConnectTimeout = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT)
let tcpTimeout = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT)
let tcpTimeoutPerKb = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB)
let rcvConcurrency = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY)
let smpPingInterval = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL)
let smpPingCount = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_SMP_PING_COUNT)
let enableKeepAlive = groupDefaults.bool(forKey: GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE)
@@ -294,9 +317,12 @@ public func getNetCfg() -> NetCfg {
hostMode: hostMode,
requiredHostMode: requiredHostMode,
sessionMode: sessionMode,
smpProxyMode: smpProxyMode,
smpProxyFallback: smpProxyFallback,
tcpConnectTimeout: tcpConnectTimeout,
tcpTimeout: tcpTimeout,
tcpTimeoutPerKb: tcpTimeoutPerKb,
rcvConcurrency: rcvConcurrency,
tcpKeepAlive: tcpKeepAlive,
smpPingInterval: smpPingInterval,
smpPingCount: smpPingCount,
@@ -307,9 +333,12 @@ public func getNetCfg() -> NetCfg {
public func setNetCfg(_ cfg: NetCfg) {
networkUseOnionHostsGroupDefault.set(OnionHosts(netCfg: cfg))
networkSessionModeGroupDefault.set(cfg.sessionMode)
networkSMPProxyModeGroupDefault.set(cfg.smpProxyMode)
networkSMPProxyFallbackGroupDefault.set(cfg.smpProxyFallback)
groupDefaults.set(cfg.tcpConnectTimeout, forKey: GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT)
groupDefaults.set(cfg.tcpTimeout, forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT)
groupDefaults.set(cfg.tcpTimeoutPerKb, forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB)
groupDefaults.set(cfg.rcvConcurrency, forKey: GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY)
groupDefaults.set(cfg.smpPingInterval, forKey: GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL)
groupDefaults.set(cfg.smpPingCount, forKey: GROUP_DEFAULT_NETWORK_SMP_PING_COUNT)
if let tcpKeepAlive = cfg.tcpKeepAlive {
+285 -45
View File
@@ -543,6 +543,7 @@ public protocol Feature {
var iconFilled: String { get }
var iconScale: CGFloat { get }
var hasParam: Bool { get }
var hasRole: Bool { get }
var text: String { get }
}
@@ -569,6 +570,8 @@ public enum ChatFeature: String, Decodable, Feature {
}
}
public var hasRole: Bool { false }
public var text: String {
switch self {
case .timedMessages: return NSLocalizedString("Disappearing messages", comment: "chat feature")
@@ -694,6 +697,7 @@ public enum GroupFeature: String, Decodable, Feature {
case reactions
case voice
case files
case simplexLinks
case history
public var id: Self { self }
@@ -705,6 +709,19 @@ public enum GroupFeature: String, Decodable, Feature {
}
}
public var hasRole: Bool {
switch self {
case .timedMessages: false
case .directMessages: true
case .fullDelete: false
case .reactions: false
case .voice: true
case .files: true
case .simplexLinks: true
case .history: false
}
}
public var text: String {
switch self {
case .timedMessages: return NSLocalizedString("Disappearing messages", comment: "chat feature")
@@ -713,6 +730,7 @@ public enum GroupFeature: String, Decodable, Feature {
case .reactions: return NSLocalizedString("Message reactions", comment: "chat feature")
case .voice: return NSLocalizedString("Voice messages", comment: "chat feature")
case .files: return NSLocalizedString("Files and media", comment: "chat feature")
case .simplexLinks: return NSLocalizedString("SimpleX links", comment: "chat feature")
case .history: return NSLocalizedString("Visible history", comment: "chat feature")
}
}
@@ -725,6 +743,7 @@ public enum GroupFeature: String, Decodable, Feature {
case .reactions: return "face.smiling"
case .voice: return "mic"
case .files: return "doc"
case .simplexLinks: return "link.circle"
case .history: return "clock"
}
}
@@ -737,6 +756,7 @@ public enum GroupFeature: String, Decodable, Feature {
case .reactions: return "face.smiling.fill"
case .voice: return "mic.fill"
case .files: return "doc.fill"
case .simplexLinks: return "link.circle.fill"
case .history: return "clock.fill"
}
}
@@ -781,6 +801,11 @@ public enum GroupFeature: String, Decodable, Feature {
case .on: return "Allow to send files and media."
case .off: return "Prohibit sending files and media."
}
case .simplexLinks:
switch enabled {
case .on: return "Allow to send SimpleX links."
case .off: return "Prohibit sending SimpleX links."
}
case .history:
switch enabled {
case .on: return "Send up to 100 last messages to new members."
@@ -819,6 +844,11 @@ public enum GroupFeature: String, Decodable, Feature {
case .on: return "Group members can send files and media."
case .off: return "Files and media are prohibited in this group."
}
case .simplexLinks:
switch enabled {
case .on: return "Group members can send SimpleX links."
case .off: return "SimpleX links are prohibited in this group."
}
case .history:
switch enabled {
case .on: return "Up to 100 last messages are sent to new members."
@@ -958,20 +988,22 @@ public enum FeatureAllowed: String, Codable, Identifiable {
public struct FullGroupPreferences: Decodable, Equatable {
public var timedMessages: TimedMessagesGroupPreference
public var directMessages: GroupPreference
public var directMessages: RoleGroupPreference
public var fullDelete: GroupPreference
public var reactions: GroupPreference
public var voice: GroupPreference
public var files: GroupPreference
public var voice: RoleGroupPreference
public var files: RoleGroupPreference
public var simplexLinks: RoleGroupPreference
public var history: GroupPreference
public init(
timedMessages: TimedMessagesGroupPreference,
directMessages: GroupPreference,
directMessages: RoleGroupPreference,
fullDelete: GroupPreference,
reactions: GroupPreference,
voice: GroupPreference,
files: GroupPreference,
voice: RoleGroupPreference,
files: RoleGroupPreference,
simplexLinks: RoleGroupPreference,
history: GroupPreference
) {
self.timedMessages = timedMessages
@@ -980,36 +1012,40 @@ public struct FullGroupPreferences: Decodable, Equatable {
self.reactions = reactions
self.voice = voice
self.files = files
self.simplexLinks = simplexLinks
self.history = history
}
public static let sampleData = FullGroupPreferences(
timedMessages: TimedMessagesGroupPreference(enable: .off),
directMessages: GroupPreference(enable: .off),
directMessages: RoleGroupPreference(enable: .off, role: nil),
fullDelete: GroupPreference(enable: .off),
reactions: GroupPreference(enable: .on),
voice: GroupPreference(enable: .on),
files: GroupPreference(enable: .on),
voice: RoleGroupPreference(enable: .on, role: nil),
files: RoleGroupPreference(enable: .on, role: nil),
simplexLinks: RoleGroupPreference(enable: .on, role: nil),
history: GroupPreference(enable: .on)
)
}
public struct GroupPreferences: Codable {
public var timedMessages: TimedMessagesGroupPreference?
public var directMessages: GroupPreference?
public var directMessages: RoleGroupPreference?
public var fullDelete: GroupPreference?
public var reactions: GroupPreference?
public var voice: GroupPreference?
public var files: GroupPreference?
public var voice: RoleGroupPreference?
public var files: RoleGroupPreference?
public var simplexLinks: RoleGroupPreference?
public var history: GroupPreference?
public init(
timedMessages: TimedMessagesGroupPreference? = nil,
directMessages: GroupPreference? = nil,
directMessages: RoleGroupPreference? = nil,
fullDelete: GroupPreference? = nil,
reactions: GroupPreference? = nil,
voice: GroupPreference? = nil,
files: GroupPreference? = nil,
voice: RoleGroupPreference? = nil,
files: RoleGroupPreference? = nil,
simplexLinks: RoleGroupPreference? = nil,
history: GroupPreference? = nil
) {
self.timedMessages = timedMessages
@@ -1018,16 +1054,18 @@ public struct GroupPreferences: Codable {
self.reactions = reactions
self.voice = voice
self.files = files
self.simplexLinks = simplexLinks
self.history = history
}
public static let sampleData = GroupPreferences(
timedMessages: TimedMessagesGroupPreference(enable: .off),
directMessages: GroupPreference(enable: .off),
directMessages: RoleGroupPreference(enable: .off, role: nil),
fullDelete: GroupPreference(enable: .off),
reactions: GroupPreference(enable: .on),
voice: GroupPreference(enable: .on),
files: GroupPreference(enable: .on),
voice: RoleGroupPreference(enable: .on, role: nil),
files: RoleGroupPreference(enable: .on, role: nil),
simplexLinks: RoleGroupPreference(enable: .on, role: nil),
history: GroupPreference(enable: .on)
)
}
@@ -1040,6 +1078,7 @@ public func toGroupPreferences(_ fullPreferences: FullGroupPreferences) -> Group
reactions: fullPreferences.reactions,
voice: fullPreferences.voice,
files: fullPreferences.files,
simplexLinks: fullPreferences.simplexLinks,
history: fullPreferences.history
)
}
@@ -1051,11 +1090,37 @@ public struct GroupPreference: Codable, Equatable {
enable == .on
}
public func enabled(_ role: GroupMemberRole?, for m: GroupMember?) -> GroupFeatureEnabled {
switch enable {
case .off: .off
case .on:
if let role, let m {
m.memberRole >= role ? .on : .off
} else {
.on
}
}
}
public init(enable: GroupFeatureEnabled) {
self.enable = enable
}
}
public struct RoleGroupPreference: Codable, Equatable {
public var enable: GroupFeatureEnabled
public var role: GroupMemberRole?
public func on(for m: GroupMember) -> Bool {
enable == .on && m.memberRole >= (role ?? .observer)
}
public init(enable: GroupFeatureEnabled, role: GroupMemberRole?) {
self.enable = enable
self.role = role
}
}
public struct TimedMessagesGroupPreference: Codable, Equatable {
public var enable: GroupFeatureEnabled
public var ttl: Int?
@@ -1280,7 +1345,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat {
case .timedMessages: return prefs.timedMessages.on
case .fullDelete: return prefs.fullDelete.on
case .reactions: return prefs.reactions.on
case .voice: return prefs.voice.on
case .voice: return prefs.voice.on(for: groupInfo.membership)
case .calls: return false
}
case .local:
@@ -1323,7 +1388,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat {
return .other
}
case let .group(groupInfo):
if !groupInfo.fullGroupPreferences.voice.on {
if !groupInfo.fullGroupPreferences.voice.on(for: groupInfo.membership) {
return .groupOwnerCan
} else {
return .other
@@ -1445,7 +1510,12 @@ public struct Contact: Identifiable, Decodable, NamedChat {
public var ready: Bool { get { activeConn?.connStatus == .ready } }
public var active: Bool { get { contactStatus == .active } }
public var sendMsgEnabled: Bool { get {
(ready && active && !(activeConn?.connectionStats?.ratchetSyncSendProhibited ?? false))
(
ready
&& active
&& !(activeConn?.connectionStats?.ratchetSyncSendProhibited ?? false)
&& !(activeConn?.connDisabled ?? true)
)
|| nextSendGrpInv
} }
public var nextSendGrpInv: Bool { get { contactGroupMemberId != nil && !contactGrpInvSent } }
@@ -1536,15 +1606,20 @@ public struct Connection: Decodable {
public var pqEncryption: Bool
public var pqSndEnabled: Bool?
public var pqRcvEnabled: Bool?
public var authErrCounter: Int
public var connectionStats: ConnectionStats? = nil
private enum CodingKeys: String, CodingKey {
case connId, agentConnId, peerChatVRange, connStatus, connLevel, viaGroupLink, customUserProfileId, connectionCode, pqSupport, pqEncryption, pqSndEnabled, pqRcvEnabled
case connId, agentConnId, peerChatVRange, connStatus, connLevel, viaGroupLink, customUserProfileId, connectionCode, pqSupport, pqEncryption, pqSndEnabled, pqRcvEnabled, authErrCounter
}
public var id: ChatId { get { ":\(connId)" } }
public var connDisabled: Bool {
authErrCounter >= 10 // authErrDisableCount in core
}
public var connPQEnabled: Bool {
pqSndEnabled == true && pqRcvEnabled == true
}
@@ -1557,7 +1632,8 @@ public struct Connection: Decodable {
connLevel: 0,
viaGroupLink: false,
pqSupport: false,
pqEncryption: false
pqEncryption: false,
authErrCounter: 0
)
}
@@ -1975,7 +2051,7 @@ public struct GroupMemberIds: Decodable {
var groupId: Int64
}
public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Decodable {
public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Codable {
case observer = "observer"
case author = "author"
case member = "member"
@@ -2120,7 +2196,7 @@ public enum ConnectionEntity: Decodable {
public var id: String? {
switch self {
case let .rcvDirectMsgConnection(contact):
return contact?.id ?? nil
return contact?.id
case let .rcvGroupMsgConnection(_, groupMember):
return groupMember.id
case let .userContactConnection(userContact):
@@ -2372,10 +2448,20 @@ public struct ChatItem: Identifiable, Decodable {
}
}
public static func getSample (_ id: Int64, _ dir: CIDirection, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, quotedItem: CIQuote? = nil, file: CIFile? = nil, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, editable: Bool = true) -> ChatItem {
public var showLocalDelete: Bool {
switch content {
case .sndDirectE2EEInfo: return false
case .rcvDirectE2EEInfo: return false
case .sndGroupE2EEInfo: return false
case .rcvGroupE2EEInfo: return false
default: return true
}
}
public static func getSample (_ id: Int64, _ dir: CIDirection, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, quotedItem: CIQuote? = nil, file: CIFile? = nil, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, deletable: Bool = true, editable: Bool = true) -> ChatItem {
ChatItem(
chatDir: dir,
meta: CIMeta.getSample(id, ts, text, status, itemDeleted: itemDeleted, itemEdited: itemEdited, itemLive: itemLive, editable: editable),
meta: CIMeta.getSample(id, ts, text, status, itemDeleted: itemDeleted, itemEdited: itemEdited, itemLive: itemLive, deletable: deletable, editable: editable),
content: .sndMsgContent(msgContent: .text(text)),
quotedItem: quotedItem,
file: file
@@ -2466,6 +2552,7 @@ public struct ChatItem: Identifiable, Decodable {
itemDeleted: nil,
itemEdited: false,
itemLive: false,
deletable: false,
editable: false
),
content: .rcvDeleted(deleteMode: .cidmBroadcast),
@@ -2487,6 +2574,7 @@ public struct ChatItem: Identifiable, Decodable {
itemDeleted: nil,
itemEdited: false,
itemLive: true,
deletable: false,
editable: false
),
content: .sndMsgContent(msgContent: .text("")),
@@ -2544,12 +2632,15 @@ public struct CIMeta: Decodable {
public var itemTs: Date
var itemText: String
public var itemStatus: CIStatus
public var sentViaProxy: Bool?
public var createdAt: Date
public var updatedAt: Date
public var itemForwarded: CIForwardedFrom?
public var itemDeleted: CIDeleted?
public var itemEdited: Bool
public var itemTimed: CITimed?
public var itemLive: Bool?
public var deletable: Bool
public var editable: Bool
public var timestampText: Text { get { formatTimestampText(itemTs) } }
@@ -2566,7 +2657,7 @@ public struct CIMeta: Decodable {
itemStatus.statusIcon(metaColor)
}
public static func getSample(_ id: Int64, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, editable: Bool = true) -> CIMeta {
public static func getSample(_ id: Int64, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, deletable: Bool = true, editable: Bool = true) -> CIMeta {
CIMeta(
itemId: id,
itemTs: ts,
@@ -2577,6 +2668,7 @@ public struct CIMeta: Decodable {
itemDeleted: itemDeleted,
itemEdited: itemEdited,
itemLive: itemLive,
deletable: deletable,
editable: editable
)
}
@@ -2592,6 +2684,7 @@ public struct CIMeta: Decodable {
itemDeleted: nil,
itemEdited: false,
itemLive: false,
deletable: false,
editable: false
)
}
@@ -2629,18 +2722,20 @@ public enum CIStatus: Decodable {
case sndSent(sndProgress: SndCIStatusProgress)
case sndRcvd(msgRcptStatus: MsgReceiptStatus, sndProgress: SndCIStatusProgress)
case sndErrorAuth
case sndError(agentError: String)
case sndError(agentError: SndError)
case sndWarning(agentError: SndError)
case rcvNew
case rcvRead
case invalid(text: String)
var id: String {
public var id: String {
switch self {
case .sndNew: return "sndNew"
case .sndSent: return "sndSent"
case .sndRcvd: return "sndRcvd"
case .sndErrorAuth: return "sndErrorAuth"
case .sndError: return "sndError"
case .sndWarning: return "sndWarning"
case .rcvNew: return "rcvNew"
case .rcvRead: return "rcvRead"
case .invalid: return "invalid"
@@ -2657,7 +2752,8 @@ public enum CIStatus: Decodable {
case .badMsgHash: return ("checkmark", .red)
}
case .sndErrorAuth: return ("multiply", .red)
case .sndError: return ("exclamationmark.triangle.fill", .yellow)
case .sndError: return ("multiply", .red)
case .sndWarning: return ("exclamationmark.triangle.fill", .orange)
case .rcvNew: return ("circlebadge.fill", Color.accentColor)
case .rcvRead: return nil
case .invalid: return ("questionmark", metaColor)
@@ -2675,7 +2771,11 @@ public enum CIStatus: Decodable {
)
case let .sndError(agentError): return (
NSLocalizedString("Message delivery error", comment: "item status text"),
String.localizedStringWithFormat(NSLocalizedString("Unexpected error: %@", comment: "item status description"), agentError)
agentError.errorInfo
)
case let .sndWarning(agentError): return (
NSLocalizedString("Message delivery warning", comment: "item status text"),
agentError.errorInfo
)
case .rcvNew: return nil
case .rcvRead: return nil
@@ -2687,6 +2787,50 @@ public enum CIStatus: Decodable {
}
}
public enum SndError: Decodable {
case auth
case quota
case expired
case relay(srvError: SrvError)
case proxy(proxyServer: String, srvError: SrvError)
case proxyRelay(proxyServer: String, srvError: SrvError)
case other(sndError: String)
public var errorInfo: String {
switch self {
case .auth: NSLocalizedString("Wrong key or unknown connection - most likely this connection is deleted.", comment: "snd error text")
case .quota: NSLocalizedString("Capacity exceeded - recipient did not receive previously sent messages.", comment: "snd error text")
case .expired: NSLocalizedString("Network issues - message expired after many attempts to send it.", comment: "snd error text")
case let .relay(srvError): String.localizedStringWithFormat(NSLocalizedString("Destination server error: %@", comment: "snd error text"), srvError.errorInfo)
case let .proxy(proxyServer, srvError): String.localizedStringWithFormat(NSLocalizedString("Forwarding server: %@\nError: %@", comment: "snd error text"), proxyServer, srvError.errorInfo)
case let .proxyRelay(proxyServer, srvError): String.localizedStringWithFormat(NSLocalizedString("Forwarding server: %@\nDestination server error: %@", comment: "snd error text"), proxyServer, srvError.errorInfo)
case let .other(sndError): String.localizedStringWithFormat(NSLocalizedString("Error: %@", comment: "snd error text"), sndError)
}
}
}
public enum SrvError: Decodable, Equatable {
case host
case version
case other(srvError: String)
var id: String {
switch self {
case .host: return "host"
case .version: return "version"
case let .other(srvError): return "other \(srvError)"
}
}
public var errorInfo: String {
switch self {
case .host: NSLocalizedString("Server address is incompatible with network settings.", comment: "srv error text.")
case .version: NSLocalizedString("Server version is incompatible with network settings.", comment: "srv error text")
case let .other(srvError): srvError
}
}
}
public enum MsgReceiptStatus: String, Decodable {
case ok
case badMsgHash
@@ -2713,6 +2857,31 @@ public enum CIDeleted: Decodable {
}
}
public enum MsgDirection: String, Decodable {
case rcv = "rcv"
case snd = "snd"
}
public enum CIForwardedFrom: Decodable {
case unknown
case contact(chatName: String, msgDir: MsgDirection, contactId: Int64?, chatItemId: Int64?)
case group(chatName: String, msgDir: MsgDirection, groupId: Int64?, chatItemId: Int64?)
var chatName: String {
switch self {
case .unknown: ""
case let .contact(chatName, _, _, _): chatName
case let .group(chatName, _, _, _): chatName
}
}
public func text(_ chatType: ChatType) -> LocalizedStringKey {
chatType == .local
? (chatName == "" ? "saved" : "saved from \(chatName)")
: "forwarded"
}
}
public enum CIDeleteMode: String, Decodable {
case cidmBroadcast = "broadcast"
case cidmInternal = "internal"
@@ -2742,8 +2911,8 @@ public enum CIContent: Decodable, ItemContent {
case sndChatFeature(feature: ChatFeature, enabled: FeatureEnabled, param: Int?)
case rcvChatPreference(feature: ChatFeature, allowed: FeatureAllowed, param: Int?)
case sndChatPreference(feature: ChatFeature, allowed: FeatureAllowed, param: Int?)
case rcvGroupFeature(groupFeature: GroupFeature, preference: GroupPreference, param: Int?)
case sndGroupFeature(groupFeature: GroupFeature, preference: GroupPreference, param: Int?)
case rcvGroupFeature(groupFeature: GroupFeature, preference: GroupPreference, param: Int?, memberRole_: GroupMemberRole?)
case sndGroupFeature(groupFeature: GroupFeature, preference: GroupPreference, param: Int?, memberRole_: GroupMemberRole?)
case rcvChatFeatureRejected(feature: ChatFeature)
case rcvGroupFeatureRejected(groupFeature: GroupFeature)
case sndModerated
@@ -2777,8 +2946,8 @@ public enum CIContent: Decodable, ItemContent {
case let .sndChatFeature(feature, enabled, param): return CIContent.featureText(feature, enabled.text, param)
case let .rcvChatPreference(feature, allowed, param): return CIContent.preferenceText(feature, allowed, param)
case let .sndChatPreference(feature, allowed, param): return CIContent.preferenceText(feature, allowed, param)
case let .rcvGroupFeature(feature, preference, param): return CIContent.featureText(feature, preference.enable.text, param)
case let .sndGroupFeature(feature, preference, param): return CIContent.featureText(feature, preference.enable.text, param)
case let .rcvGroupFeature(feature, preference, param, role): return CIContent.featureText(feature, preference.enable.text, param, role)
case let .sndGroupFeature(feature, preference, param, role): return CIContent.featureText(feature, preference.enable.text, param, role)
case let .rcvChatFeatureRejected(feature): return String.localizedStringWithFormat("%@: received, prohibited", feature.text)
case let .rcvGroupFeatureRejected(groupFeature): return String.localizedStringWithFormat("%@: received, prohibited", groupFeature.text)
case .sndModerated: return NSLocalizedString("moderated", comment: "moderated chat item")
@@ -2803,10 +2972,25 @@ public enum CIContent: Decodable, ItemContent {
NSLocalizedString("This chat is protected by end-to-end encryption.", comment: "E2EE info chat item")
}
static func featureText(_ feature: Feature, _ enabled: String, _ param: Int?) -> String {
feature.hasParam
? "\(feature.text): \(timeText(param))"
: "\(feature.text): \(enabled)"
static func featureText(_ feature: Feature, _ enabled: String, _ param: Int?, _ role: GroupMemberRole? = nil) -> String {
(
feature.hasParam
? "\(feature.text): \(timeText(param))"
: "\(feature.text): \(enabled)"
) +
(
feature.hasRole && role != nil
? " (\(roleText(role)))"
: ""
)
}
private static func roleText(_ role: GroupMemberRole?) -> String {
switch role {
case .owner: NSLocalizedString("owners", comment: "feature role")
case .admin: NSLocalizedString("admins", comment: "feature role")
default: NSLocalizedString("all members", comment: "feature role")
}
}
public static func preferenceText(_ feature: Feature, _ allowed: FeatureAllowed, _ param: Int?) -> String {
@@ -2995,12 +3179,15 @@ public struct CIFile: Decodable {
case .sndComplete: return true
case .sndCancelled: return true
case .sndError: return true
case .sndWarning: return true
case .rcvInvitation: return false
case .rcvAccepted: return false
case .rcvTransfer: return false
case .rcvAborted: return false
case .rcvCancelled: return false
case .rcvComplete: return true
case .rcvError: return false
case .rcvWarning: return false
case .invalid: return false
}
}
@@ -3019,11 +3206,14 @@ public struct CIFile: Decodable {
}
case .sndCancelled: return nil
case .sndError: return nil
case .sndWarning: return sndCancelAction
case .rcvInvitation: return nil
case .rcvAccepted: return rcvCancelAction
case .rcvTransfer: return rcvCancelAction
case .rcvAborted: return nil
case .rcvCancelled: return nil
case .rcvComplete: return nil
case .rcvWarning: return rcvCancelAction
case .rcvError: return nil
case .invalid: return nil
}
@@ -3132,33 +3322,64 @@ public enum CIFileStatus: Decodable, Equatable {
case sndTransfer(sndProgress: Int64, sndTotal: Int64)
case sndComplete
case sndCancelled
case sndError
case sndError(sndFileError: FileError)
case sndWarning(sndFileError: FileError)
case rcvInvitation
case rcvAccepted
case rcvTransfer(rcvProgress: Int64, rcvTotal: Int64)
case rcvAborted
case rcvComplete
case rcvCancelled
case rcvError
case rcvError(rcvFileError: FileError)
case rcvWarning(rcvFileError: FileError)
case invalid(text: String)
var id: String {
public var id: String {
switch self {
case .sndStored: return "sndStored"
case let .sndTransfer(sndProgress, sndTotal): return "sndTransfer \(sndProgress) \(sndTotal)"
case .sndComplete: return "sndComplete"
case .sndCancelled: return "sndCancelled"
case .sndError: return "sndError"
case let .sndError(sndFileError): return "sndError \(sndFileError)"
case let .sndWarning(sndFileError): return "sndWarning \(sndFileError)"
case .rcvInvitation: return "rcvInvitation"
case .rcvAccepted: return "rcvAccepted"
case let .rcvTransfer(rcvProgress, rcvTotal): return "rcvTransfer \(rcvProgress) \(rcvTotal)"
case .rcvAborted: return "rcvAborted"
case .rcvComplete: return "rcvComplete"
case .rcvCancelled: return "rcvCancelled"
case .rcvError: return "rcvError"
case let .rcvError(rcvFileError): return "rcvError \(rcvFileError)"
case let .rcvWarning(rcvFileError): return "rcvWarning \(rcvFileError)"
case .invalid: return "invalid"
}
}
}
public enum FileError: Decodable, Equatable {
case auth
case noFile
case relay(srvError: SrvError)
case other(fileError: String)
var id: String {
switch self {
case .auth: return "auth"
case .noFile: return "noFile"
case let .relay(srvError): return "relay \(srvError)"
case let .other(fileError): return "other \(fileError)"
}
}
public var errorInfo: String {
switch self {
case .auth: NSLocalizedString("Wrong key or unknown file chunk address - most likely file is deleted.", comment: "file error text")
case .noFile: NSLocalizedString("File not found - most likely file was deleted or cancelled.", comment: "file error text")
case let .relay(srvError): String.localizedStringWithFormat(NSLocalizedString("File server error: %@", comment: "file error text"), srvError.errorInfo)
case let .other(fileError): String.localizedStringWithFormat(NSLocalizedString("Error: %@", comment: "file error text"), fileError)
}
}
}
public enum MsgContent: Equatable {
case text(String)
case link(text: String, preview: LinkPreview)
@@ -3209,6 +3430,23 @@ public enum MsgContent: Equatable {
}
}
public var isImageOrVideo: Bool {
switch self {
case .image: true
case .video: true
default: false
}
}
public var isMediaOrFileAttachment: Bool {
switch self {
case .image: true
case .video: true
case .file: true
default: false
}
}
var cmdString: String {
"json \(encodeJSON(self))"
}
@@ -3743,6 +3981,7 @@ public enum ChatItemTTL: Hashable, Identifiable, Comparable {
public struct ChatItemInfo: Decodable {
public var itemVersions: [ChatItemVersion]
public var memberDeliveryStatuses: [MemberDeliveryStatus]?
public var forwardedFromChatItem: AChatItem?
}
public struct ChatItemVersion: Decodable {
@@ -3756,4 +3995,5 @@ public struct ChatItemVersion: Decodable {
public struct MemberDeliveryStatus: Decodable {
public var groupMemberId: Int64
public var memberDeliveryStatus: CIStatus
public var sentViaProxy: Bool?
}
+335 -8
View File
@@ -85,6 +85,9 @@
/* No comment provided by engineer. */
"**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)." = "**Най-поверително**: не използвайте сървъра за известия SimpleX Chat, периодично проверявайте съобщенията във фонов режим (зависи от това колко често използвате приложението).";
/* No comment provided by engineer. */
"**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." = "**Моля, обърнете внимание**: използването на една и съща база данни на две устройства ще наруши декриптирането на съобщенията от вашите връзки като защита на сигурността.";
/* No comment provided by engineer. */
"**Please note**: you will NOT be able to recover or change passphrase if you lose it." = "**Моля, обърнете внимание**: НЯМА да можете да възстановите или промените паролата, ако я загубите.";
@@ -94,6 +97,9 @@
/* No comment provided by engineer. */
"**Warning**: Instant push notifications require passphrase saved in Keychain." = "**Внимание**: Незабавните push известия изискват парола, запазена в Keychain.";
/* No comment provided by engineer. */
"**Warning**: the archive will be removed." = "**Внимание**: архивът ще бъде изтрит.";
/* No comment provided by engineer. */
"*bold*" = "\\*удебелен*";
@@ -136,6 +142,9 @@
/* No comment provided by engineer. */
"%@ connected" = "%@ свързан";
/* No comment provided by engineer. */
"%@ downloaded" = "%@ изтеглено";
/* notification title */
"%@ is connected!" = "%@ е свързан!";
@@ -148,6 +157,9 @@
/* No comment provided by engineer. */
"%@ servers" = "%@ сървъри";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ качено";
/* notification title */
"%@ wants to connect!" = "%@ иска да се свърже!";
@@ -377,6 +389,12 @@
/* member role */
"admin" = "админ";
/* feature role */
"admins" = "администратори";
/* No comment provided by engineer. */
"Admins can block a member for all." = "Администраторите могат да блокират член за всички.";
/* No comment provided by engineer. */
"Admins can create the links to join groups." = "Админите могат да създадат линкове за присъединяване към групи.";
@@ -401,6 +419,9 @@
/* No comment provided by engineer. */
"All group members will remain connected." = "Всички членове на групата ще останат свързани.";
/* feature role */
"all members" = "всички членове";
/* No comment provided by engineer. */
"All messages will be deleted - this cannot be undone!" = "Всички съобщения ще бъдат изтрити - това не може да бъде отменено!";
@@ -416,6 +437,9 @@
/* No comment provided by engineer. */
"All your contacts will remain connected. Profile update will be sent to your contacts." = "Всички ваши контакти ще останат свързани. Актуализацията на профила ще бъде изпратена до вашите контакти.";
/* No comment provided by engineer. */
"All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays." = "Всички ваши контакти, разговори и файлове ще бъдат сигурно криптирани и качени на парчета в конфигурираните XFTP релета.";
/* No comment provided by engineer. */
"Allow" = "Позволи";
@@ -446,6 +470,9 @@
/* No comment provided by engineer. */
"Allow to send files and media." = "Позволи изпращане на файлове и медия.";
/* No comment provided by engineer. */
"Allow to send SimpleX links." = "Разрешаване на изпращане на SimpleX линкове.";
/* No comment provided by engineer. */
"Allow to send voice messages." = "Позволи изпращане на гласови съобщения.";
@@ -497,6 +524,9 @@
/* No comment provided by engineer. */
"App build: %@" = "Компилация на приложението: %@";
/* No comment provided by engineer. */
"App data migration" = "Миграция на данните от приложението";
/* No comment provided by engineer. */
"App encrypts new local files (except videos)." = "Приложението криптира нови локални файлове (с изключение на видеоклипове).";
@@ -518,6 +548,15 @@
/* No comment provided by engineer. */
"Appearance" = "Изглед";
/* No comment provided by engineer. */
"Apply" = "Приложи";
/* No comment provided by engineer. */
"Archive and upload" = "Архивиране и качване";
/* No comment provided by engineer. */
"Archiving database" = "Архивиране на база данни";
/* No comment provided by engineer. */
"Attach" = "Прикачи";
@@ -665,6 +704,9 @@
/* No comment provided by engineer. */
"Cancel" = "Отказ";
/* No comment provided by engineer. */
"Cancel migration" = "Отмени миграцията";
/* feature offered item */
"cancelled %@" = "отменен %@";
@@ -674,6 +716,9 @@
/* No comment provided by engineer. */
"Cannot receive file" = "Файлът не може да бъде получен";
/* No comment provided by engineer. */
"Cellular" = "Мобилна мрежа";
/* No comment provided by engineer. */
"Change" = "Промени";
@@ -744,6 +789,9 @@
/* No comment provided by engineer. */
"Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." = "Чатът е спрян. Ако вече сте използвали тази база данни на друго устройство, трябва да я прехвърлите обратно, преди да стартирате чата отново.";
/* No comment provided by engineer. */
"Chat migrated!" = "Чатът е мигриран!";
/* No comment provided by engineer. */
"Chat preferences" = "Чат настройки";
@@ -756,6 +804,9 @@
/* No comment provided by engineer. */
"Chinese and Spanish interface" = "Китайски и Испански интерфейс";
/* No comment provided by engineer. */
"Choose _Migrate from another device_ on the new device and scan QR code." = "Изберете _Мигриране от друго устройство_ на новото устройство и сканирайте QR кода.";
/* No comment provided by engineer. */
"Choose file" = "Избери файл";
@@ -801,6 +852,9 @@
/* No comment provided by engineer. */
"Confirm database upgrades" = "Потвърди актуализаациите на базата данни";
/* No comment provided by engineer. */
"Confirm network settings" = "Потвърди мрежовите настройки";
/* No comment provided by engineer. */
"Confirm new passphrase…" = "Потвърди новата парола…";
@@ -810,6 +864,12 @@
/* No comment provided by engineer. */
"Confirm password" = "Потвърди парола";
/* No comment provided by engineer. */
"Confirm that you remember database passphrase to migrate it." = "Потвърдете, че помните паролата на базата данни, преди да я мигрирате.";
/* No comment provided by engineer. */
"Confirm upload" = "Потвърди качването";
/* server test step */
"Connect" = "Свързване";
@@ -1008,6 +1068,9 @@
/* No comment provided by engineer. */
"Created on %@" = "Създаден на %@";
/* No comment provided by engineer. */
"Creating archive link" = "Създаване на архивен линк";
/* No comment provided by engineer. */
"Creating link…" = "Линкът се създава…";
@@ -1155,6 +1218,9 @@
/* No comment provided by engineer. */
"Delete database" = "Изтрий базата данни";
/* No comment provided by engineer. */
"Delete database from this device" = "Изтриване на базата данни от това устройство";
/* server test step */
"Delete file" = "Изтрий файл";
@@ -1347,9 +1413,21 @@
/* No comment provided by engineer. */
"Downgrade and open chat" = "Понижи версията и отвори чата";
/* chat item action */
"Download" = "Изтегли";
/* No comment provided by engineer. */
"Download failed" = "Неуспешно изтегляне";
/* server test step */
"Download file" = "Свали файл";
/* No comment provided by engineer. */
"Downloading archive" = "Архива се изтегля";
/* No comment provided by engineer. */
"Downloading link details" = "Подробности за линка се изтеглят";
/* No comment provided by engineer. */
"Duplicate display name!" = "Дублирано име!";
@@ -1383,6 +1461,9 @@
/* No comment provided by engineer. */
"Enable for all" = "Активиране за всички";
/* No comment provided by engineer. */
"Enable in direct chats (BETA)!" = "Активиране в личните чатове (БЕТА)!";
/* No comment provided by engineer. */
"Enable instant notifications?" = "Активирай незабавни известия?";
@@ -1410,6 +1491,9 @@
/* enabled status */
"enabled" = "активирано";
/* No comment provided by engineer. */
"Enabled for" = "Активирано за";
/* enabled status */
"enabled for contact" = "активирано за контакт";
@@ -1497,6 +1581,9 @@
/* No comment provided by engineer. */
"Enter Passcode" = "Въведете kодa за достъп";
/* No comment provided by engineer. */
"Enter passphrase" = "Въведи парола";
/* No comment provided by engineer. */
"Enter passphrase…" = "Въведи парола…";
@@ -1590,6 +1677,9 @@
/* No comment provided by engineer. */
"Error deleting user profile" = "Грешка при изтриване на потребителския профил";
/* No comment provided by engineer. */
"Error downloading the archive" = "Грешка при изтеглянето на архива";
/* No comment provided by engineer. */
"Error enabling delivery receipts!" = "Грешка при активирането на потвърждениeто за доставка!";
@@ -1635,6 +1725,9 @@
/* No comment provided by engineer. */
"Error saving passphrase to keychain" = "Грешка при запазване на парола в Кeychain";
/* when migrating */
"Error saving settings" = "Грешка при запазване на настройките";
/* No comment provided by engineer. */
"Error saving user password" = "Грешка при запазване на потребителска парола";
@@ -1678,9 +1771,15 @@
"Error updating user privacy" = "Грешка при актуализиране на поверителността на потребителя";
/* No comment provided by engineer. */
"Error: " = "Грешка: ";
"Error uploading the archive" = "Грешка при качването на архива";
/* No comment provided by engineer. */
"Error verifying passphrase:" = "Грешка при проверката на паролата:";
/* No comment provided by engineer. */
"Error: " = "Грешка: ";
/* snd error text */
"Error: %@" = "Грешка: %@";
/* No comment provided by engineer. */
@@ -1710,6 +1809,9 @@
/* No comment provided by engineer. */
"Exported database archive." = "Експортиран архив на базата данни.";
/* No comment provided by engineer. */
"Exported file doesn't exist" = "Експортираният файл не съществува";
/* No comment provided by engineer. */
"Exporting database archive…" = "Експортиране на архив на базата данни…";
@@ -1746,12 +1848,21 @@
/* No comment provided by engineer. */
"Files and media are prohibited in this group." = "Файловете и медията са забранени в тази група.";
/* No comment provided by engineer. */
"Files and media not allowed" = "Файлове и медия не са разрешени";
/* No comment provided by engineer. */
"Files and media prohibited!" = "Файловете и медията са забранени!";
/* No comment provided by engineer. */
"Filter unread and favorite chats." = "Филтрирайте непрочетените и любимите чатове.";
/* No comment provided by engineer. */
"Finalize migration" = "Завърши миграцията";
/* No comment provided by engineer. */
"Finalize migration on another device." = "Завършете миграцията на другото устройство.";
/* No comment provided by engineer. */
"Finally, we have them! 🚀" = "Най-накрая ги имаме! 🚀";
@@ -1779,6 +1890,21 @@
/* No comment provided by engineer. */
"For console" = "За конзолата";
/* chat item action */
"Forward" = "Препрати";
/* No comment provided by engineer. */
"Forward and save messages" = "Препращане и запазване на съобщения";
/* No comment provided by engineer. */
"forwarded" = "препратено";
/* No comment provided by engineer. */
"Forwarded" = "Препратено";
/* No comment provided by engineer. */
"Forwarded from" = "Препратено от";
/* No comment provided by engineer. */
"Found desktop" = "Намерено настолно устройство";
@@ -1857,6 +1983,9 @@
/* No comment provided by engineer. */
"Group members can send files and media." = "Членовете на групата могат да изпращат файлове и медия.";
/* No comment provided by engineer. */
"Group members can send SimpleX links." = "Членовете на групата могат да изпращат SimpleX линкове.";
/* No comment provided by engineer. */
"Group members can send voice messages." = "Членовете на групата могат да изпращат гласови съобщения.";
@@ -1935,6 +2064,9 @@
/* No comment provided by engineer. */
"How to use your servers" = "Как да използвате вашите сървъри";
/* No comment provided by engineer. */
"Hungarian interface" = "Унгарски интерфейс";
/* No comment provided by engineer. */
"ICE servers (one per line)" = "ICE сървъри (по един на ред)";
@@ -1974,6 +2106,12 @@
/* No comment provided by engineer. */
"Import database" = "Импортиране на база данни";
/* No comment provided by engineer. */
"Import failed" = "Неуспешно импортиране";
/* No comment provided by engineer. */
"Importing archive" = "Импортиране на архив";
/* No comment provided by engineer. */
"Improved message delivery" = "Подобрена доставка на съобщения";
@@ -1983,9 +2121,15 @@
/* No comment provided by engineer. */
"Improved server configuration" = "Подобрена конфигурация на сървъра";
/* No comment provided by engineer. */
"In order to continue, chat should be stopped." = "За да продължите, чатът трябва да бъде спрян.";
/* No comment provided by engineer. */
"In reply to" = "В отговор на";
/* No comment provided by engineer. */
"In-call sounds" = "Звуци по време на разговор";
/* No comment provided by engineer. */
"Incognito" = "Инкогнито";
@@ -2067,6 +2211,9 @@
/* No comment provided by engineer. */
"Invalid link" = "Невалиден линк";
/* No comment provided by engineer. */
"Invalid migration confirmation" = "Невалидно потвърждение за мигриране";
/* No comment provided by engineer. */
"Invalid name!" = "Невалидно име!";
@@ -2328,9 +2475,15 @@
/* notification */
"message received" = "получено съобщение";
/* No comment provided by engineer. */
"Message source remains private." = "Източникът на съобщението остава скрит.";
/* No comment provided by engineer. */
"Message text" = "Текст на съобщението";
/* No comment provided by engineer. */
"Message too large" = "Съобщението е твърде голямо";
/* No comment provided by engineer. */
"Messages" = "Съобщения";
@@ -2340,9 +2493,36 @@
/* No comment provided by engineer. */
"Messages from %@ will be shown!" = "Съобщенията от %@ ще бъдат показани!";
/* No comment provided by engineer. */
"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Съобщенията, файловете и разговорите са защитени чрез **криптиране от край до край** с перфектна секретност при препращане, правдоподобно опровержение и възстановяване при взлом.";
/* No comment provided by engineer. */
"Messages, files and calls are protected by **quantum resistant e2e encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Съобщенията, файловете и разговорите са защитени чрез **квантово устойчиво e2e криптиране** с перфектна секретност при препращане, правдоподобно опровержение и възстановяване при взлом.";
/* No comment provided by engineer. */
"Migrate device" = "Мигрирай устройството";
/* No comment provided by engineer. */
"Migrate from another device" = "Мигриране от друго устройство";
/* No comment provided by engineer. */
"Migrate here" = "Мигрирай тук";
/* No comment provided by engineer. */
"Migrate to another device" = "Миграция към друго устройство";
/* No comment provided by engineer. */
"Migrate to another device via QR code." = "Мигрирайте към друго устройство чрез QR код.";
/* No comment provided by engineer. */
"Migrating" = "Мигриране";
/* No comment provided by engineer. */
"Migrating database archive…" = "Архивът на базата данни се мигрира…";
/* No comment provided by engineer. */
"Migration complete" = "Миграцията е завършена";
/* No comment provided by engineer. */
"Migration error:" = "Грешка при мигриране:";
@@ -2382,12 +2562,12 @@
/* No comment provided by engineer. */
"More improvements are coming soon!" = "Очаквайте скоро още подобрения!";
/* No comment provided by engineer. */
"More reliable network connection." = "По-надеждна мрежова връзка.";
/* item status description */
"Most likely this connection is deleted." = "Най-вероятно тази връзка е изтрита.";
/* No comment provided by engineer. */
"Most likely this contact has deleted the connection with you." = "Най-вероятно този контакт е изтрил връзката с вас.";
/* No comment provided by engineer. */
"Multiple chat profiles" = "Множество профили за чат";
@@ -2403,6 +2583,12 @@
/* No comment provided by engineer. */
"Network & servers" = "Мрежа и сървъри";
/* No comment provided by engineer. */
"Network connection" = "Мрежова връзка";
/* No comment provided by engineer. */
"Network management" = "Управление на мрежата";
/* No comment provided by engineer. */
"Network settings" = "Мрежови настройки";
@@ -2481,6 +2667,9 @@
/* No comment provided by engineer. */
"No history" = "Няма история";
/* No comment provided by engineer. */
"No network connection" = "Няма мрежова връзка";
/* No comment provided by engineer. */
"No permission to record voice message" = "Няма разрешение за запис на гласово съобщение";
@@ -2600,6 +2789,9 @@
/* No comment provided by engineer. */
"Open group" = "Отвори група";
/* authentication reason */
"Open migration to another device" = "Отвори миграцията към друго устройство";
/* No comment provided by engineer. */
"Open Settings" = "Отвори настройки";
@@ -2612,15 +2804,27 @@
/* No comment provided by engineer. */
"Opening app…" = "Приложението се отваря…";
/* No comment provided by engineer. */
"Or paste archive link" = "Или постави архивен линк";
/* No comment provided by engineer. */
"Or scan QR code" = "Или сканирай QR код";
/* No comment provided by engineer. */
"Or securely share this file link" = "Или сигурно споделете този линк към файла";
/* No comment provided by engineer. */
"Or show this code" = "Или покажи този код";
/* No comment provided by engineer. */
"Other" = "Други";
/* member role */
"owner" = "собственик";
/* feature role */
"owners" = "собственици";
/* No comment provided by engineer. */
"Passcode" = "Код за достъп";
@@ -2666,6 +2870,9 @@
/* message decrypt error item */
"Permanent decryption error" = "Постоянна грешка при декриптиране";
/* No comment provided by engineer. */
"Picture-in-picture calls" = "Обаждания \"картина в картина\"";
/* No comment provided by engineer. */
"PING count" = "PING бройка";
@@ -2684,6 +2891,9 @@
/* No comment provided by engineer. */
"Please check yours and your contact preferences." = "Моля, проверете вашите настройки и тези вашия за контакт.";
/* No comment provided by engineer. */
"Please confirm that network settings are correct for this device." = "Моля, потвърдете, че мрежовите настройки са правилни за това устройство.";
/* No comment provided by engineer. */
"Please contact developers.\nError: %@" = "Моля, свържете се с разработчиците.\nГрешка: %@";
@@ -2747,6 +2957,9 @@
/* No comment provided by engineer. */
"Profile image" = "Профилно изображение";
/* No comment provided by engineer. */
"Profile images" = "Профилни изображения";
/* No comment provided by engineer. */
"Profile name" = "Име на профила";
@@ -2780,6 +2993,9 @@
/* No comment provided by engineer. */
"Prohibit sending files and media." = "Забрани изпращането на файлове и медия.";
/* No comment provided by engineer. */
"Prohibit sending SimpleX links." = "Забранете изпращането на SimpleX линкове.";
/* No comment provided by engineer. */
"Prohibit sending voice messages." = "Забрани изпращането на гласови съобщения.";
@@ -2798,6 +3014,15 @@
/* No comment provided by engineer. */
"Push notifications" = "Push известия";
/* No comment provided by engineer. */
"Push server" = "Push сървър";
/* chat item text */
"quantum resistant e2e encryption" = "квантово устойчиво e2e криптиране";
/* No comment provided by engineer. */
"Quantum resistant encryption" = "Квантово устойчиво криптиране";
/* No comment provided by engineer. */
"Rate the app" = "Оценете приложението";
@@ -2858,6 +3083,9 @@
/* No comment provided by engineer. */
"Recent history and improved [directory bot](simplex:/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)." = "Скорошна история и подобрен [bot за директория за групи](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPd jdLW3%23%2F%3Fv%3D1-2% 26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).";
/* No comment provided by engineer. */
"Recipient(s) can't see who this message is from." = "Получателят(ите) не могат да видят от кого е това съобщение.";
/* No comment provided by engineer. */
"Recipients see updates as you type them." = "Получателите виждат актуализации, докато ги въвеждате.";
@@ -2933,9 +3161,18 @@
/* No comment provided by engineer. */
"Repeat connection request?" = "Изпрати отново заявката за свързване?";
/* No comment provided by engineer. */
"Repeat download" = "Повтори изтеглянето";
/* No comment provided by engineer. */
"Repeat import" = "Повтори импортирането";
/* No comment provided by engineer. */
"Repeat join request?" = "Изпрати отново заявката за присъединяване?";
/* No comment provided by engineer. */
"Repeat upload" = "Повтори качването";
/* chat item action */
"Reply" = "Отговори";
@@ -2993,6 +3230,9 @@
/* No comment provided by engineer. */
"Run chat" = "Стартиране на чат";
/* No comment provided by engineer. */
"Safer groups" = "По-безопасни групи";
/* chat item action */
"Save" = "Запази";
@@ -3041,6 +3281,18 @@
/* No comment provided by engineer. */
"Save welcome message?" = "Запази съобщението при посрещане?";
/* No comment provided by engineer. */
"saved" = "запазено";
/* No comment provided by engineer. */
"Saved" = "Запазено";
/* No comment provided by engineer. */
"Saved from" = "Запазено от";
/* No comment provided by engineer. */
"saved from %@" = "запазено от %@";
/* message info title */
"Saved message" = "Запазено съобщение";
@@ -3233,6 +3485,9 @@
/* No comment provided by engineer. */
"Set passcode" = "Задай kод за достъп";
/* No comment provided by engineer. */
"Set passphrase" = "Задаване на парола";
/* No comment provided by engineer. */
"Set passphrase to export" = "Задай парола за експортиране";
@@ -3245,6 +3500,9 @@
/* No comment provided by engineer. */
"Settings" = "Настройки";
/* No comment provided by engineer. */
"Shape profile images" = "Променете формата на профилните изображения";
/* chat item action */
"Share" = "Сподели";
@@ -3278,6 +3536,9 @@
/* No comment provided by engineer. */
"Show preview" = "Показване на визуализация";
/* No comment provided by engineer. */
"Show QR code" = "Покажи QR код";
/* No comment provided by engineer. */
"Show:" = "Покажи:";
@@ -3299,9 +3560,15 @@
/* simplex link type */
"SimpleX group link" = "SimpleX групов линк";
/* No comment provided by engineer. */
/* chat feature */
"SimpleX links" = "SimpleX линкове";
/* No comment provided by engineer. */
"SimpleX links are prohibited in this group." = "SimpleX линкове са забранени в тази група.";
/* No comment provided by engineer. */
"SimpleX links not allowed" = "SimpleX линковете не са разрешени";
/* No comment provided by engineer. */
"SimpleX Lock" = "SimpleX заключване";
@@ -3338,6 +3605,12 @@
/* notification title */
"Somebody" = "Някой";
/* No comment provided by engineer. */
"Square, circle, or anything in between." = "Квадрат, кръг или нещо между тях.";
/* chat item text */
"standard end-to-end encryption" = "стандартно криптиране от край до край";
/* No comment provided by engineer. */
"Start chat" = "Започни чат";
@@ -3353,6 +3626,9 @@
/* No comment provided by engineer. */
"Stop" = "Спри";
/* No comment provided by engineer. */
"Stop chat" = "Спри чата";
/* No comment provided by engineer. */
"Stop chat to enable database actions" = "Спрете чата, за да активирате действията с базата данни";
@@ -3380,6 +3656,9 @@
/* authentication reason */
"Stop SimpleX" = "Спри SimpleX";
/* No comment provided by engineer. */
"Stopping chat" = "Спиране на чата";
/* No comment provided by engineer. */
"strike" = "зачеркнат";
@@ -3530,6 +3809,12 @@
/* No comment provided by engineer. */
"This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." = "Това действие не може да бъде отменено - вашият профил, контакти, съобщения и файлове ще бъдат безвъзвратно загубени.";
/* E2EE info chat item */
"This chat is protected by end-to-end encryption." = "Този чат е защитен чрез криптиране от край до край.";
/* E2EE info chat item */
"This chat is protected by quantum resistant end-to-end encryption." = "Този чат е защитен от квантово устойчиво криптиране от край до край.";
/* notification title */
"this contact" = "този контакт";
@@ -3629,9 +3914,6 @@
/* rcv group event chat item */
"unblocked %@" = "отблокиран %@";
/* item status description */
"Unexpected error: %@" = "Неочаквана грешка: %@";
/* No comment provided by engineer. */
"Unexpected migration state" = "Неочаквано състояние на миграция";
@@ -3722,9 +4004,15 @@
/* No comment provided by engineer. */
"Upgrade and open chat" = "Актуализирай и отвори чата";
/* No comment provided by engineer. */
"Upload failed" = "Неуспешно качване";
/* server test step */
"Upload file" = "Качи файл";
/* No comment provided by engineer. */
"Uploading archive" = "Архивът се качва";
/* No comment provided by engineer. */
"Use .onion hosts" = "Използвай .onion хостове";
@@ -3755,6 +4043,9 @@
/* No comment provided by engineer. */
"Use SimpleX Chat servers?" = "Използвай сървърите на SimpleX Chat?";
/* No comment provided by engineer. */
"Use the app while in the call." = "Използвайте приложението по време на разговора.";
/* No comment provided by engineer. */
"User profile" = "Потребителски профил";
@@ -3782,6 +4073,12 @@
/* No comment provided by engineer. */
"Verify connections" = "Потвърждение за свързване";
/* No comment provided by engineer. */
"Verify database passphrase" = "Проверете паролата на базата данни";
/* No comment provided by engineer. */
"Verify passphrase" = "Провери паролата";
/* No comment provided by engineer. */
"Verify security code" = "Потвърди кода за сигурност";
@@ -3836,6 +4133,9 @@
/* No comment provided by engineer. */
"Voice messages are prohibited in this group." = "Гласовите съобщения са забранени в тази група.";
/* No comment provided by engineer. */
"Voice messages not allowed" = "Гласовите съобщения не са разрешени";
/* No comment provided by engineer. */
"Voice messages prohibited!" = "Гласовите съобщения са забранени!";
@@ -3860,6 +4160,9 @@
/* No comment provided by engineer. */
"wants to connect to you!" = "иска да се свърже с вас!";
/* No comment provided by engineer. */
"Warning: starting chat on multiple devices is not supported and will cause message delivery failures" = "Внимание: стартирането на чата на множество устройства не се поддържа и ще доведе до неуспешно изпращане на съобщения";
/* No comment provided by engineer. */
"Warning: you may lose some data!" = "Предупреждение: Може да загубите някои данни!";
@@ -3875,18 +4178,33 @@
/* No comment provided by engineer. */
"Welcome message" = "Съобщение при посрещане";
/* No comment provided by engineer. */
"Welcome message is too long" = "Съобщението при посрещане е твърде дълго";
/* No comment provided by engineer. */
"What's new" = "Какво е новото";
/* No comment provided by engineer. */
"When available" = "Когато са налични";
/* No comment provided by engineer. */
"When connecting audio and video calls." = "При свързване на аудио и видео разговори.";
/* No comment provided by engineer. */
"When people request to connect, you can accept or reject it." = "Когато хората искат да се свържат с вас, можете да ги приемете или отхвърлите.";
/* No comment provided by engineer. */
"When you share an incognito profile with somebody, this profile will be used for the groups they invite you to." = "Когато споделяте инкогнито профил с някого, този профил ще се използва за групите, в които той ви кани.";
/* No comment provided by engineer. */
"WiFi" = "WiFi";
/* No comment provided by engineer. */
"Will be enabled in direct chats!" = "Ще бъде активирано в личните чатове!";
/* No comment provided by engineer. */
"Wired ethernet" = "Кабелен Ethernet";
/* No comment provided by engineer. */
"With encrypted files and media." = "С криптирани файлове и медия.";
@@ -3908,9 +4226,15 @@
/* pref value */
"yes" = "да";
/* No comment provided by engineer. */
"you" = "вие";
/* No comment provided by engineer. */
"You" = "Вие";
/* No comment provided by engineer. */
"You **must not** use the same database on two devices." = "**Не трябва** да използвате една и съща база данни на две устройства.";
/* No comment provided by engineer. */
"You accepted connection" = "Вие приехте връзката";
@@ -3971,6 +4295,9 @@
/* No comment provided by engineer. */
"You can enable them later via app Privacy & Security settings." = "Можете да ги активирате по-късно през настройките за \"Поверителност и сигурност\" на приложението.";
/* No comment provided by engineer. */
"You can give another try." = "Можете да опитате още веднъж.";
/* No comment provided by engineer. */
"You can hide or mute a user profile - swipe it to the right." = "Можете да скриете или заглушите известията за потребителски профил - плъзнете надясно.";
+2 -8
View File
@@ -1461,7 +1461,7 @@
/* No comment provided by engineer. */
"Error: " = "Chyba: ";
/* No comment provided by engineer. */
/* snd error text */
"Error: %@" = "Chyba: %@";
/* No comment provided by engineer. */
@@ -2082,9 +2082,6 @@
/* item status description */
"Most likely this connection is deleted." = "Pravděpodobně je toto spojení smazáno.";
/* No comment provided by engineer. */
"Most likely this contact has deleted the connection with you." = "Tento kontakt s největší pravděpodobností smazal spojení s vámi.";
/* No comment provided by engineer. */
"Multiple chat profiles" = "Více chatovacích profilů";
@@ -2903,7 +2900,7 @@
/* simplex link type */
"SimpleX group link" = "Skupinový odkaz SimpleX";
/* No comment provided by engineer. */
/* chat feature */
"SimpleX links" = "Odkazy na SimpleX";
/* No comment provided by engineer. */
@@ -3179,9 +3176,6 @@
/* No comment provided by engineer. */
"Unable to record voice message" = "Nelze nahrát hlasovou zprávu";
/* item status description */
"Unexpected error: %@" = "Neočekávaná chyba: %@";
/* No comment provided by engineer. */
"Unexpected migration state" = "Neočekávaný stav přenášení";
File diff suppressed because it is too large Load Diff

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