Compare commits

...

124 Commits

Author SHA1 Message Date
Avently 4c4794fe33 lib path 2024-12-13 10:31:34 -08:00
Avently f179db9545 package 2024-12-13 10:19:43 -08:00
Avently 52160f2602 change lib name 2024-12-13 10:14:37 -08:00
Avently 2a99a5235b changes 2024-12-13 19:57:06 +07:00
Avently 4fe7b674e6 rename 2024-12-13 16:50:22 +07:00
Avently a5e7fa2f9f nodejs: addon 2024-12-12 22:11:42 +07:00
spaced4ndy 708368c9c5 android, desktop: fix operators text (#5371) 2024-12-12 07:26:17 +00:00
Evgeny Poberezkin 5d8e11c3a2 blog: typo in screenshot 2024-12-10 18:39:14 +00:00
Evgeny Poberezkin df8ce2b06d blog: fix image 2024-12-10 18:13:11 +00:00
Evgeny 6ff5f31bee blog: v6.2 announcement (#5359)
* blog: v6.2 announcement

* update, images

* update

* readme

* correction

* update
2024-12-10 17:16:34 +00:00
Stanislav Dmitrenko 5e29cda27b desktop: start using continuous runtime for AppImage again (#5366) 2024-12-10 11:09:01 +00:00
Stanislav Dmitrenko 2e573662d5 android, desktop: hiding counters while scrolling (#5363)
* android, desktop: hiding counters while scrolling

* change
2024-12-10 10:07:49 +00:00
spaced4ndy 4075c26dd2 ui: offer to fix connection on call and message buttons (#5358) 2024-12-09 21:03:56 +04:00
Evgeny Poberezkin d64351b760 ui: update label in server statistics 2024-12-08 15:17:32 +00:00
sh b06211bd4e flatpak: update metainfo (#5353)
* flatpak: update metainfo

* make notes like in release

* simpler

* space

---------

Co-authored-by: Evgeny <evgeny@poberezkin.com>
2024-12-08 10:39:31 +00:00
Evgeny Poberezkin 33bc539e16 6.2: ios 254, android 259, desktop 82 2024-12-07 20:53:01 +00:00
Evgeny Poberezkin febea096db android, desktop: remove duplicate translation key 2024-12-07 18:53:18 +00:00
spaced4ndy f0781adbd3 desktop: fix opening operators on onboarding (#5351) 2024-12-07 18:51:23 +00:00
Evgeny Poberezkin df30bb99cb ui: add translations 2024-12-07 17:51:53 +00:00
Evgeny Poberezkin 7c86484978 ios: update library 2024-12-07 17:22:14 +00:00
Stanislav Dmitrenko 307211a47f android, desktop: landscape calls on Android and better local camera ratio management (#5124)
* android, desktop: landscape calls on Android and better local camera ratio management

The main thing is that now when exiting from CallActivity while in call
audio devices are not reset to default. It allows to have landscape mode
enabled

* styles

* fix changing calls
2024-12-07 17:09:00 +00:00
Evgeny 7d6c7c58d7 ui: translations (#5338)
* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (2211 of 2211 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 100.0% (2210 of 2210 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% (2210 of 2210 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 99.8% (2206 of 2210 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% (1931 of 1931 strings)

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

* Translated using Weblate (Italian)

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

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

* Translated using Weblate (Arabic)

Currently translated at 97.4% (2153 of 2210 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 99.8% (2206 of 2210 strings)

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

* Translated using Weblate (Arabic)

Currently translated at 98.3% (2174 of 2210 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% (2210 of 2210 strings)

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

* Translated using Weblate (Italian)

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

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

* Translated using Weblate (Arabic)

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

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

* Translated using Weblate (Hungarian)

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

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

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (2211 of 2211 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 100.0% (2210 of 2210 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% (2210 of 2210 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 99.8% (2206 of 2210 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% (1931 of 1931 strings)

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

* Translated using Weblate (Italian)

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

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

* Translated using Weblate (Arabic)

Currently translated at 97.4% (2153 of 2210 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 99.8% (2206 of 2210 strings)

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

* Translated using Weblate (Arabic)

Currently translated at 98.3% (2174 of 2210 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% (2210 of 2210 strings)

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

* Translated using Weblate (Italian)

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

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

* Translated using Weblate (Arabic)

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

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

* Translated using Weblate (Hungarian)

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

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

* Translated using Weblate (Dutch)

Currently translated at 99.8% (2208 of 2212 strings)

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

* Translated using Weblate (Arabic)

Currently translated at 100.0% (2212 of 2212 strings)

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

* Translated using Weblate (Dutch)

Currently translated at 100.0% (2212 of 2212 strings)

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

* Translated using Weblate (Arabic)

Currently translated at 100.0% (2212 of 2212 strings)

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

* Translated using Weblate (Arabic)

Currently translated at 100.0% (2212 of 2212 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 97.5% (2158 of 2212 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% (2212 of 2212 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% (1934 of 1934 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 98.7% (2185 of 2213 strings)

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

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2213 of 2213 strings)

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

* Translated using Weblate (Hungarian)

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

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

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (2213 of 2213 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% (2213 of 2213 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% (1934 of 1934 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% (2213 of 2213 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% (1934 of 1934 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% (2213 of 2213 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% (2213 of 2213 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% (1934 of 1934 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% (2214 of 2214 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% (1935 of 1935 strings)

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

* process localizations

---------

Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
Co-authored-by: J R <jr@simplex.chat>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: Bezruchenko Simon <worcposj44@gmail.com>
Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: mlanp <github@lang.xyz>
Co-authored-by: No name <CertainBot@users.noreply.hosted.weblate.org>
2024-12-07 16:52:34 +00:00
Evgeny 93319d947d website: translations (#5350)
* Translated using Weblate (Arabic)

Currently translated at 100.0% (258 of 258 strings)

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

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (258 of 258 strings)

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

* Translated using Weblate (Arabic)

Currently translated at 100.0% (258 of 258 strings)

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

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (258 of 258 strings)

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

* Translated using Weblate (German)

Currently translated at 100.0% (258 of 258 strings)

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

---------

Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: Bezruchenko Simon <worcposj44@gmail.com>
Co-authored-by: mlanp <github@lang.xyz>
2024-12-07 16:11:30 +00:00
Evgeny Poberezkin ea4927c9b0 core: 6.2.0.7 updated version 2024-12-07 16:01:57 +00:00
Evgeny fe0d811bf7 ui: operator information (#5343)
* ios: operator information

* android, desktop: operator information

* move texts, simplify navigation
2024-12-07 14:41:54 +00:00
Evgeny Poberezkin cbb3da8f83 core: 6.2.0.7 (simplexmq: 6.2.0.7) 2024-12-07 14:40:35 +00:00
Stanislav Dmitrenko 83f0bd9fd3 android, desktop: onboarding button multiline layout (#5348)
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-12-07 14:24:14 +00:00
Diogo 615c483912 android: onboarding small design adjustments (#5346)
* android: onboarding small design adjustments

* bigger

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-12-07 14:20:01 +00:00
spaced4ndy e0c2272fcb android, desktop: operators info on onboarding (#5341) 2024-12-06 21:35:10 +04:00
spaced4ndy 362581432c ios: export localizations, add translations (#5339)
* ios: export localizations, add translations

* import
2024-12-06 16:01:55 +00:00
Diogo df1a471c56 ios: remove all unsafe warnings in group preferences save (#5340) 2024-12-06 19:55:15 +04:00
Diogo 7d43a43e82 ios: ask for confirmation of save on contact preferences sheet dismiss (#5337) 2024-12-06 18:44:56 +04:00
spaced4ndy ae8ad5c639 ios: operators info on onboarding (#5336) 2024-12-06 17:49:57 +04:00
spaced4ndy 1408d75eb3 ios: use async getServerOperators api (#5334) 2024-12-06 17:21:55 +04:00
spaced4ndy f408988035 ios: fix oneHandUI setting becoming enabled on import (#5335) 2024-12-06 13:05:39 +00:00
spaced4ndy 945c5015d8 ui: improve pending connection texts (#5333)
* ui: improve contact request text

* android

* ternary

* shorter

* kotlin

* change

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-12-06 15:35:26 +04:00
Diogo 2e431c5afa ios: fix some real time updates in group members (#5332)
* ios: fix some real time updates in group members

* use chat instead of binding for group info updates
2024-12-06 11:10:52 +00:00
Diogo 924273191e ios: ask for confirmation of save on group preferences sheet dismiss (#5327)
* ios: ask for confirmation of save on group preferences sheet dismiss

* fix exit without saving temporary state and also apply fix on dismiss during group creation
2024-12-06 10:21:58 +00:00
Evgeny Poberezkin 9b82cc3303 core: fix feature items when updating preferences in business chats 2024-12-06 10:18:48 +00:00
Evgeny Poberezkin 19e2cebd68 android, desktop: remove footer in Run chat section when chat is running 2024-12-06 01:16:42 +00:00
Evgeny Poberezkin 5ef14ca95e 6.2-beta.6: ios 253, android 258, desktop 81 2024-12-05 23:30:05 +00:00
Evgeny Poberezkin 886dc56de8 ui: update business chat info type 2024-12-05 23:01:37 +00:00
Evgeny Poberezkin bd2ca74987 ui: update "server operators privacy" in onboarding 2024-12-05 22:22:03 +00:00
Evgeny 586671c307 website: translations (#5331)
* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (258 of 258 strings)

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

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (258 of 258 strings)

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

* Translated using Weblate (Italian)

Currently translated at 100.0% (258 of 258 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (258 of 258 strings)

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

---------

Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
2024-12-05 21:46:35 +00:00
Evgeny ff504702de ui: translations (#5330)
* Translated using Weblate (German)

Currently translated at 97.5% (2155 of 2209 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 93.2% (2060 of 2209 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 97.5% (2155 of 2209 strings)

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

* Translated using Weblate (Spanish)

Currently translated at 97.6% (2157 of 2209 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 97.5% (2154 of 2209 strings)

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

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 92.3% (2041 of 2209 strings)

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

* Translated using Weblate (Arabic)

Currently translated at 97.4% (2153 of 2209 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 97.5% (2155 of 2209 strings)

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

* Translated using Weblate (Polish)

Currently translated at 93.2% (2059 of 2209 strings)

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

* Translated using Weblate (Russian)

Currently translated at 93.2% (2061 of 2210 strings)

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

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 97.4% (2154 of 2210 strings)

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

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (2210 of 2210 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% (2210 of 2210 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% (1932 of 1932 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 100.0% (2210 of 2210 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% (1932 of 1932 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 98.9% (2187 of 2210 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 99.5% (2201 of 2210 strings)

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

* Translated using Weblate (Russian)

Currently translated at 100.0% (2210 of 2210 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 96.6% (1868 of 1932 strings)

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

* Translated using Weblate (Italian)

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

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

* Translated using Weblate (Indonesian)

Currently translated at 60.0% (1326 of 2210 strings)

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

* Translated using Weblate (Russian)

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

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

* Translated using Weblate (German)

Currently translated at 97.5% (2155 of 2209 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 93.2% (2060 of 2209 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 97.5% (2155 of 2209 strings)

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

* Translated using Weblate (Spanish)

Currently translated at 97.6% (2157 of 2209 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 97.5% (2154 of 2209 strings)

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

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 92.3% (2041 of 2209 strings)

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

* Translated using Weblate (Arabic)

Currently translated at 97.4% (2153 of 2209 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 97.5% (2155 of 2209 strings)

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

* Translated using Weblate (Polish)

Currently translated at 93.2% (2059 of 2209 strings)

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

* Translated using Weblate (Russian)

Currently translated at 93.2% (2061 of 2210 strings)

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

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 97.4% (2154 of 2210 strings)

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

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (2210 of 2210 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% (2210 of 2210 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% (1932 of 1932 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 100.0% (2210 of 2210 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% (1932 of 1932 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 98.9% (2187 of 2210 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 99.5% (2201 of 2210 strings)

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

* Translated using Weblate (Russian)

Currently translated at 100.0% (2210 of 2210 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 96.6% (1868 of 1932 strings)

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

* Translated using Weblate (Italian)

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

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

* Translated using Weblate (Indonesian)

Currently translated at 60.0% (1326 of 2210 strings)

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

* Translated using Weblate (Russian)

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

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

* process localizations

* update translations

---------

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: Rafi <rafimuhmad90@gmail.com>
2024-12-05 21:42:53 +00:00
spaced4ndy 69e23ad58f android, desktop: don't show unwanted notifications (#5328)
* android, desktop: don't show unwanted notifications

* format

* fix

* code style

---------

Co-authored-by: Avently <7953703+avently@users.noreply.github.com>
2024-12-05 18:45:19 +00:00
Evgeny Poberezkin e9bf229a9d core: 6.2.0.6 2024-12-05 18:36:07 +00:00
Evgeny 60e0e454e8 core: only update business chat preferences (#5325)
* core: only update business chat preferences

* rework

* migration

* test, bump protocol version

* fix tests
2024-12-05 18:32:00 +00:00
Stanislav Dmitrenko de76e271a8 android, desktop: displaying deleted message with file in chat list (#5329) 2024-12-05 18:10:36 +00:00
Diogo 5f66c29dbd ios: fix open from notification and connected directly chat item chat loading (#5326)
* ios: fix opening from notification and connected directly chat item chat loading

* better fix
2024-12-05 16:15:24 +00:00
spaced4ndy 97cd2682d7 core: take address lock before reading contact request data (to prevent possible race condition if user quickly accepts request several times in a row); android, desktop: show error context in agent CMD errors (#5324) 2024-12-05 20:10:44 +04:00
Stanislav Dmitrenko 4f8a70a6c1 android, desktop: allow to scan QR multiple times after fail (#5323) 2024-12-05 15:52:45 +00:00
Evgeny Poberezkin c1a0943448 6.2-beta.5: ios 251, android 256, desktop 80 2024-12-04 21:56:09 +00:00
Evgeny Poberezkin c1c17d1f19 core: 6.2.0.5 (simplexmq: 6.2.0.6) 2024-12-04 19:59:42 +00:00
spaced4ndy 009d13210f android: fix simplex chat team contact card opening empty page on connection (#5320) 2024-12-04 17:33:30 +00:00
spaced4ndy 892f6498be ios: fix contact cards opening empty page on connection 2 (#5319) 2024-12-04 17:33:12 +00:00
spaced4ndy 3fa1d7b07c core: fix cli servers override (#5317) 2024-12-04 16:50:19 +00:00
Stanislav Dmitrenko ee146cdc7b android, desktop, core: option to show toolbar in chat at the top in reachable UI (#5316)
* android, desktop: ability to show toolbar in chat at the top in reachable UI

* rename

* core AppSettings

* ios AppSettings

* rename

* strings, enable reachable chat toolbar when enabled reachable app toolbars

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-12-04 16:49:45 +00:00
Stanislav Dmitrenko 219381f941 android, desktop: don't load new messages when pressing quote while searching (#5315) 2024-12-04 16:33:14 +00:00
Evgeny 89f380400e core: update business chat profile (#5313)
* core: update business chat profile

* fix, test

* refactor

* test changing non-title member profile

* fix history
2024-12-04 16:32:01 +00:00
sh 4f1cf6e79f docs: business page, technical advice (#5314)
* docs/business: populate technical advice sections

* dev tools

* update

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-12-04 10:24:31 +00:00
spaced4ndy 6ff7d4a73c core: fix business chat state on accept (fixes icon) (#5312) 2024-12-04 12:12:30 +04:00
Evgeny Poberezkin 3acc69c6d8 6.2-beta.4: ios 250, android 255, desktop 79 2024-12-03 20:13:09 +00:00
Evgeny Poberezkin b9777c92a5 core: 6.2.0.4 (simplexmq: 6.2.0.5) 2024-12-03 18:52:06 +00:00
Evgeny Poberezkin f3be723cde ios: export localizations 2024-12-03 18:40:58 +00:00
Evgeny a182cf5730 ui, site: v6.2 whats new, business (#5309)
* ui, site: v6.2 whats new, business

* icon

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* business

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* typo

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* typo

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-12-03 18:23:24 +00:00
spaced4ndy 247d12fa40 android, desktop: make more texts different for groups and business chats; ios: preferences texts (#5308) 2024-12-03 21:44:06 +04:00
spaced4ndy 6593de89c2 ios: make more texts different for groups and business chats (#5307) 2024-12-03 15:25:15 +00:00
Evgeny a1e25620f7 website: translations (#5306)
* 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 (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 (Chinese (Simplified Han script))

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 (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 (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 (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 (Chinese (Simplified Han script))

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

---------

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: Max <Prototypem95@users.noreply.hosted.weblate.org>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: summoner001 <summoner@vivaldi.net>
2024-12-03 15:23:23 +00:00
Evgeny 29b9abf241 ui: translations (#5267)
* Translated using Weblate (German)

Currently translated at 95.6% (2087 of 2182 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 95.6% (2087 of 2182 strings)

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

* Translated using Weblate (French)

Currently translated at 95.6% (2087 of 2182 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 95.6% (2087 of 2182 strings)

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

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 95.6% (2087 of 2182 strings)

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

* Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 80.7% (1763 of 2182 strings)

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

* Translated using Weblate (Dutch)

Currently translated at 95.6% (2087 of 2182 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 88.6% (1935 of 2182 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 91.9% (2006 of 2182 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.6% (2087 of 2182 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 95.5% (2085 of 2182 strings)

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

* Translated using Weblate (Finnish)

Currently translated at 67.5% (1474 of 2182 strings)

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

* Translated using Weblate (Polish)

Currently translated at 95.6% (2086 of 2182 strings)

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

* Translated using Weblate (Portuguese)

Currently translated at 43.9% (959 of 2182 strings)

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

* Translated using Weblate (Hebrew)

Currently translated at 86.2% (1883 of 2182 strings)

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

* Translated using Weblate (Bulgarian)

Currently translated at 79.1% (1726 of 2182 strings)

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

* Translated using Weblate (Turkish)

Currently translated at 95.6% (2087 of 2182 strings)

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

* Translated using Weblate (Persian)

Currently translated at 83.3% (1819 of 2182 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 33.1% (723 of 2182 strings)

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

* Translated using Weblate (Vietnamese)

Currently translated at 61.5% (1342 of 2182 strings)

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

* Translated using Weblate (German)

Currently translated at 95.6% (2087 of 2182 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 95.6% (2087 of 2182 strings)

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

* Translated using Weblate (French)

Currently translated at 95.6% (2087 of 2182 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 95.6% (2087 of 2182 strings)

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

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 95.6% (2087 of 2182 strings)

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

* Translated using Weblate (Chinese (Traditional Han script))

Currently translated at 80.7% (1763 of 2182 strings)

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

* Translated using Weblate (Dutch)

Currently translated at 95.6% (2087 of 2182 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 88.6% (1935 of 2182 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 91.9% (2006 of 2182 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.6% (2087 of 2182 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 95.5% (2085 of 2182 strings)

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

* Translated using Weblate (Finnish)

Currently translated at 67.5% (1474 of 2182 strings)

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

* Translated using Weblate (Polish)

Currently translated at 95.6% (2086 of 2182 strings)

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

* Translated using Weblate (Portuguese)

Currently translated at 43.9% (959 of 2182 strings)

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

* Translated using Weblate (Hebrew)

Currently translated at 86.2% (1883 of 2182 strings)

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

* Translated using Weblate (Bulgarian)

Currently translated at 79.1% (1726 of 2182 strings)

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

* Translated using Weblate (Turkish)

Currently translated at 95.6% (2087 of 2182 strings)

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

* Translated using Weblate (Persian)

Currently translated at 83.3% (1819 of 2182 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 33.1% (723 of 2182 strings)

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

* Translated using Weblate (Vietnamese)

Currently translated at 61.5% (1342 of 2182 strings)

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

* Translated using Weblate (Chinese (Simplified Han script))

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

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

* Translated using Weblate (Turkish)

Currently translated at 96.1% (2099 of 2182 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% (2182 of 2182 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% (2182 of 2182 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% (1918 of 1918 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% (2182 of 2182 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% (1918 of 1918 strings)

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

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (2182 of 2182 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 98.4% (2148 of 2182 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% (2182 of 2182 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.0% (1900 of 1918 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% (2182 of 2182 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% (2182 of 2182 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% (2182 of 2182 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% (1918 of 1918 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2182 of 2182 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% (1918 of 1918 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 62.4% (1362 of 2182 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 99.0% (2161 of 2182 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% (1918 of 1918 strings)

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

* Translated using Weblate (Spanish)

Currently translated at 100.0% (2182 of 2182 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 99.5% (1910 of 1918 strings)

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

* Translated using Weblate (Spanish)

Currently translated at 99.6% (1911 of 1918 strings)

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

* Translated using Weblate (Spanish)

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

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

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2178 of 2178 strings)

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

* Translated using Weblate (Italian)

Currently translated at 100.0% (2178 of 2178 strings)

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

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (2178 of 2178 strings)

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

* Translated using Weblate (Korean)

Currently translated at 66.6% (1452 of 2178 strings)

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

* Translated using Weblate (Vietnamese)

Currently translated at 62.7% (1367 of 2178 strings)

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

* Translated using Weblate (Indonesian)

Currently translated at 59.3% (1293 of 2178 strings)

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

* Translated using Weblate (Spanish)

Currently translated at 100.0% (1918 of 1918 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 99.9% (2177 of 2178 strings)

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

* Translated using Weblate (Czech)

Currently translated at 91.9% (2002 of 2178 strings)

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

* Translated using Weblate (Vietnamese)

Currently translated at 62.9% (1372 of 2178 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 63.6% (1387 of 2178 strings)

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

* Translated using Weblate (German)

Currently translated at 100.0% (2178 of 2178 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 62.5% (1199 of 1918 strings)

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

* fix/process localizations

---------

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: Abdullah Koyuncu <wisewebworks@outlook.com>
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: No name <CertainBot@users.noreply.hosted.weblate.org>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: Max <Prototypem95@users.noreply.hosted.weblate.org>
Co-authored-by: Bezruchenko Simon <worcposj44@gmail.com>
Co-authored-by: tuananh-ng <158744840+tuananh-ng@users.noreply.github.com>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
Co-authored-by: Максим Горпиніч <mgorpinic2005@gmail.com>
Co-authored-by: translatorforkr <tsgjklvfdyu@onionmail.com>
Co-authored-by: Rafi <rafimuhmad90@gmail.com>
Co-authored-by: zenobit <zenobit@users.noreply.hosted.weblate.org>
Co-authored-by: Miyu Sakatsuki <miyu-sakatsuki@outlook.jp>
2024-12-03 15:22:41 +00:00
spaced4ndy a0b8cf62be android, desktop: support business addresses and chats (#5302) 2024-12-03 17:27:00 +04:00
Narasimha-sc 43fa4c43a2 docs: update FAQ (#5179)
* Update FAQ.md

Added:
- Why invite links use simplex.chat domain?
- I do not know my database passphrase

* Update FAQ.md

* Add flatpak directory

* corrections

* correction

* invitation

---------

Co-authored-by: Evgeny <evgeny@poberezkin.com>
2024-12-03 12:48:54 +00:00
Diogo 85e7a13dba desktop: show group message reaction on right click (#5304)
* desktop: show group message reaction on right click

* open on ctrl + click too
2024-12-03 12:26:50 +00:00
Evgeny 9d992735f4 core, ios: improve business address (connection plan, repeat requests, feature items) (#5303)
* core, ios: connection plan for business address

* core: store xcontact_id on business groups to prevent duplicate contact requests

* core: create feature items in new groups and in business groups

* fix tests

* error message
2024-12-03 12:11:38 +00:00
Stanislav Dmitrenko e61babdc8f android, desktop: preserving long message when failed to send (#5297)
* android, desktop: preserving long message when failed to send

* forwarding

* unused code

* strings

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-12-02 23:36:48 +00:00
Diogo c04e952620 desktop: onboarding improvements (#5294)
* consistent space to bottom on future of messaging

* consistent button suze on server operators

* updated setup database passphrase screen

* ability to cancel random passphrase

* reduce conditions padding to header

* show scrollbar in desktop

* EOLs

* EOL

* fix random passphrase param when deleting database and recreating new one

---------

Co-authored-by: Evgeny <evgeny@poberezkin.com>
Co-authored-by: Avently <7953703+avently@users.noreply.github.com>
2024-12-02 21:00:55 +00:00
Diogo 92967dfe0c ios: disable autocorrect add group member search (#5301) 2024-12-02 20:14:26 +00:00
spaced4ndy bc96000131 ios: support business addresses and chats (#5300)
* ios: support business addresses and chats

* improve

* words

* fix

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-12-02 17:40:22 +00:00
Stanislav Dmitrenko a588e7003d android: alert round corners (#5299)
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-12-02 16:33:22 +00:00
Stanislav Dmitrenko 665501026d android: show info for Xiaomi users about autostart restrictions (#5295)
* android: show info for Xiaomi users about autostart restrictions

* text and placement

* update strings

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-12-02 16:32:04 +00:00
spaced4ndy a62ce9168e core: fix names created for business request group and member (#5296) 2024-12-02 20:30:05 +04:00
Stanislav Dmitrenko f6b611aa30 android, desktop: check existence before deleting database (#5298) 2024-12-02 15:58:21 +00:00
Stanislav Dmitrenko 5a59fdd91c android, desktop: fix Can't represent a width ... and a height ... in Constraints (#5293) 2024-12-02 15:28:58 +00:00
Evgeny 5f01dc1a3f core: support business addresses and chats (#5272)
* core: support business addresses and chats

* types

* connect plan, add link type

* ios: toggle on address UI

* make compile

* todo

* fix migration

* types

* comments

* fix

* remove

* fix schema

* comment

* simplify

* remove diff

* comment

* comment

* diff

* acceptBusinessJoinRequestAsync wip

* comment

* update

* simplify types

* remove business

* wip

* read/write columns

* createBusinessRequestGroup

* remove comments

* read/write business_address column

* validate that business address is not set to be incognito

* replace contact card

* update simplexmq

* refactor

* event when accepting business address request

* sendGroupAutoReply

* delete contact request earlier

* test, fix

* refactor

* refactor2

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-12-02 14:01:23 +00:00
Evgeny Poberezkin c488c4fcd5 6.2-beta.3: ios 249, android 254, desktop 78 2024-12-01 19:09:53 +00:00
Evgeny Poberezkin 3143cc960e core: 6.2.0.3 2024-12-01 13:18:57 +00:00
Evgeny b8442d92a4 core: improve performance of marking chat items as read (#5290)
* core: improve performance of marking chat items as read

* fix tests
2024-12-01 13:11:30 +00:00
Evgeny Poberezkin 98a3437f43 6.2-beta.2: ios 248, android 253, desktop 77 2024-11-30 22:26:05 +00:00
Evgeny Poberezkin 8f32c6a61a core: 6.2.0.2 2024-11-30 20:54:39 +00:00
Evgeny 79d5573169 cli: fix option --yes-migrate to confirm up migrations automatically, closes #5200 (#5286)
* fix(cli): option to confirm up migrations didn't work

fix #5200

* diff

* import

---------

Co-authored-by: mervyn <6359152+reply2future@users.noreply.github.com>
2024-11-30 20:51:35 +00:00
Evgeny Poberezkin a9e7635e00 core: disable Flux XFTP servers to prevent unknown server warning for the previous version users 2024-11-30 20:15:11 +00:00
Evgeny Poberezkin 2c0de36439 ios: large Conditions screen heading during onboarding 2024-11-30 18:44:59 +00:00
Diogo 94377d0b7a android, desktop: bottom bar and update texts in onboarding (#5279)
* android, desktop: remove one hand ui bar from onboarding and design matching latest ios

* padding before text

* stop reserving space in conditions view

* notifications view

* revert unwanted

* update heading

* translations for new how it works

* how it works redone

* show create profile in how it works

* revert

* conditions of use same padding bottom

* unused str

* swapped instant and off notifications order

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-11-30 18:21:48 +00:00
Evgeny Poberezkin e9853fe3fc ios: update alert message for SimpleX address card 2024-11-30 18:06:36 +00:00
Stanislav Dmitrenko 4738286f4e android, desktop: start/stop chat toggle refactoring (#5261)
* android, desktop: start/stop chat toggle refactoring

* changes

* translations

* better

* better

* do not start chat after export, always show run toggle (#5283)

* update heading

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-11-30 16:33:38 +00:00
Stanislav Dmitrenko 961bdbfc59 ios: start/stop chat toggle refactoring (#5275)
* ios: start/stop chat toggle refactoring

* changes

* changes

* return back

* reduce diff

* better

* update button

* ios: do not start chat after export, always show run toggle (#5284)

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-11-30 16:29:27 +00:00
Evgeny 879c117269 Merge pull request #5259 from simplex-chat/reaction-members
view member reactions
2024-11-30 12:25:33 +00:00
Diogo 03bc4e5d01 ios: display reactions in groups by member (#5265)
* ios: display reactions in groups by member

* fetch data

* load on open

* wip

* fix text

* less api calls

* matching image sizes

* updates

* progress dump

* mostly works

* add member to list needed

* open member faster

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-11-30 12:23:51 +00:00
Evgeny Poberezkin 80bd4cd337 Merge branch 'master' into reaction-members 2024-11-29 19:40:58 +00:00
Diogo b0f3f0a523 ios: fix alignment on operators review later button and notice (#5280) 2024-11-29 20:04:29 +04:00
Diogo 9915e5572e desktop, android: show group reactions (#5277)
* desktop, android: show group reactions

* handle long names in single line

* swap ordering for composable item action

* Revert "swap ordering for composable item action"

This reverts commit 385825e7f2.

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-11-29 11:19:11 +00:00
spaced4ndy 9c6e0a7051 desktop: fix avatar crop (#5271)
* desktop: fix avatar crop wip

* fix
2024-11-28 13:37:52 +00:00
spaced4ndy 68be4b4ba5 core: return member records from apiGetReactionMembers (#5270) 2024-11-28 13:49:20 +04:00
spaced4ndy 13efdf2595 core: apiGetReactionMembers api implementation (#5263) 2024-11-28 11:24:29 +04:00
Evgeny c3991aad87 website: translations corrections
* Translated using Weblate (French)

Currently translated at 98.8% (254 of 257 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 98.8% (254 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 98.8% (254 of 257 strings)

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

* Translated using Weblate (Czech)

Currently translated at 98.8% (254 of 257 strings)

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

* Translated using Weblate (Arabic)

Currently translated at 98.8% (254 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 98.8% (254 of 257 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 98.8% (254 of 257 strings)

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

* Translated using Weblate (Ukrainian)

Currently translated at 98.8% (254 of 257 strings)

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

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 98.8% (254 of 257 strings)

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

* Translated using Weblate (Portuguese (Brazil))

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

* Translated using Weblate (Polish)

Currently translated at 98.8% (254 of 257 strings)

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

* Translated using Weblate (Japanese)

Currently translated at 98.8% (254 of 257 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 99.2% (255 of 257 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 98.8% (254 of 257 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 96.4% (248 of 257 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 98.8% (254 of 257 strings)

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

---------

Co-authored-by: Anonymous <noreply@weblate.org>
2024-11-28 00:12:53 +00:00
Evgeny 614846465f website: translations (#5266)
* ep/blog-v61

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

---------

Co-authored-by: summoner001 <summoner@vivaldi.net>
2024-11-27 23:51:51 +00:00
Evgeny 096dec2c7b ui: translations (#5264)
* Translated using Weblate (Spanish)

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

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

* Translated using Weblate (Czech)

Currently translated at 96.1% (2008 of 2089 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% (2089 of 2089 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% (1843 of 1843 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% (2089 of 2089 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% (1843 of 1843 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 100.0% (2089 of 2089 strings)

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

* Translated using Weblate (Vietnamese)

Currently translated at 44.0% (921 of 2089 strings)

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

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2089 of 2089 strings)

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

* Translated using Weblate (Hungarian)

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

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

* Translated using Weblate (Indonesian)

Currently translated at 12.7% (267 of 2089 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2089 of 2089 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% (1843 of 1843 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 45.7% (956 of 2089 strings)

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

* Translated using Weblate (Korean)

Currently translated at 46.2% (967 of 2089 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2089 of 2089 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% (1843 of 1843 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 89.4% (1869 of 2089 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% (2089 of 2089 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% (1843 of 1843 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 46.9% (980 of 2089 strings)

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

* Translated using Weblate (Indonesian)

Currently translated at 12.8% (268 of 2089 strings)

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

* Translated using Weblate (French)

Currently translated at 100.0% (2089 of 2089 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% (1843 of 1843 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% (2089 of 2089 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% (1843 of 1843 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 51.9% (1086 of 2089 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 52.8% (1104 of 2089 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% (2089 of 2089 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% (1843 of 1843 strings)

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

* Translated using Weblate (Ukrainian)

Currently translated at 96.6% (1782 of 1843 strings)

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

* Translated using Weblate (Italian)

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

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

* Translated using Weblate (Japanese)

Currently translated at 92.3% (1929 of 2089 strings)

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

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 98.9% (2068 of 2089 strings)

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

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 50.0% (923 of 1843 strings)

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

* Translated using Weblate (Greek)

Currently translated at 19.0% (397 of 2089 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2089 of 2089 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% (1843 of 1843 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 55.2% (1155 of 2089 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% (2089 of 2089 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% (1843 of 1843 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 56.2% (1175 of 2089 strings)

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

* Translated using Weblate (Korean)

Currently translated at 51.1% (1068 of 2089 strings)

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

* Translated using Weblate (Hungarian)

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

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

* Translated using Weblate (Korean)

Currently translated at 60.8% (1271 of 2089 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2089 of 2089 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% (1843 of 1843 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 58.0% (1213 of 2089 strings)

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

* Translated using Weblate (Korean)

Currently translated at 63.0% (1317 of 2089 strings)

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

* Translated using Weblate (Indonesian)

Currently translated at 16.1% (337 of 2089 strings)

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

* Translated using Weblate (Japanese)

Currently translated at 92.6% (1936 of 2089 strings)

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

* Translated using Weblate (Korean)

Currently translated at 66.7% (1394 of 2089 strings)

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

* Translated using Weblate (Vietnamese)

Currently translated at 59.8% (1251 of 2089 strings)

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

* Translated using Weblate (Indonesian)

Currently translated at 54.6% (1141 of 2089 strings)

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

* Translated using Weblate (Korean)

Currently translated at 66.7% (1395 of 2089 strings)

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

* Translated using Weblate (Indonesian)

Currently translated at 61.0% (1275 of 2089 strings)

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

* Translated using Weblate (Vietnamese)

Currently translated at 60.8% (1271 of 2089 strings)

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

* Translated using Weblate (Korean)

Currently translated at 67.6% (1414 of 2089 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2089 of 2089 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% (1843 of 1843 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 61.7% (1291 of 2089 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 62.6% (1308 of 2089 strings)

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

* Translated using Weblate (Lithuanian)

Currently translated at 83.6% (1747 of 2089 strings)

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

* Translated using Weblate (Vietnamese)

Currently translated at 62.7% (1310 of 2089 strings)

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

* Translated using Weblate (Indonesian)

Currently translated at 61.2% (1279 of 2089 strings)

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

* Translated using Weblate (Vietnamese)

Currently translated at 63.6% (1330 of 2089 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 64.3% (1344 of 2089 strings)

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

* process localizations

* ru

* export localizations

---------

Co-authored-by: No name <CertainBot@users.noreply.hosted.weblate.org>
Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: zenobit <zen@osowoso.xyz>
Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: tuananh-ng <158744840+tuananh-ng@users.noreply.github.com>
Co-authored-by: Bezruchenko Simon <worcposj44@gmail.com>
Co-authored-by: billy appetie <billy_appetie@users.noreply.hosted.weblate.org>
Co-authored-by: 장재원 <jaeone22@proton.me>
Co-authored-by: Miyu Sakatsuki <miyu-sakatsuki@outlook.jp>
Co-authored-by: Ophiushi <41908476+ishi-sama@users.noreply.github.com>
Co-authored-by: aquaticexpectancy <alexaratovskyi@gmail.com>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: Dampuzakura <dampuzakura@users.noreply.hosted.weblate.org>
Co-authored-by: Luis Henrique Rodrigues Dos Santos <luishenriquesc2020@gmail.com>
Co-authored-by: Dinos B <dinos.nng@gmail.com>
Co-authored-by: translatorforkr <tsgjklvfdyu@onionmail.com>
Co-authored-by: d4f5409d <d4f5409d-4b6a-4640-9ff3-155ebcdc7ab7@anonaddy.me>
Co-authored-by: Rafi <rafimuhmad90@gmail.com>
Co-authored-by: Vaclovas Intas <Gateway_31@protonmail.com>
2024-11-27 23:42:02 +00:00
Diogo d19708ed77 Merge branch 'master' into reaction-members 2024-11-27 22:09:58 +00:00
Diogo 22d7db89d8 ios: database error screens redesign (#5256)
* ios: database error screens redesign (wip)

* refactor

* remove code to simulate errors

* fix

* fix texts

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-11-27 20:32:18 +00:00
Evgeny ba7abcf6f7 ios: update onboarding texts (#5255)
* ios: update onboarding texts

* translations

* more translations

* more translations 2
2024-11-27 19:01:16 +00:00
spaced4ndy b512d3a743 Merge branch 'master' into reaction-members 2024-11-27 20:09:37 +04:00
spaced4ndy 9fa968a593 ui: fix marking chat read (don't use range api) (#5257) 2024-11-27 18:30:39 +04:00
spaced4ndy 41b7ad01f9 core: apiGetReactionMembers (#5258) 2024-11-27 15:51:35 +04:00
spaced4ndy 15fae29e5b android, desktop: offer to create 1-time link on address views (#5253) 2024-11-27 11:16:22 +04:00
Stanislav Dmitrenko 8c1abcccfb android, desktop: scroll to quoted item without known id (#5254) 2024-11-26 14:22:24 +00:00
Stanislav Dmitrenko 25893177d0 ios: view conditions as markdown (#5248)
* ios: view conditions as markdown

* changes

* removed Down

* refactor

* unused

* react on theme change
2024-11-26 13:00:39 +00:00
Diogo 345e0acdec ios: onboarding redesign (#5252)
* ios: onboarding redesign

* shorter texts

* updates

* more updates

* remove extra padding when focused

* strings

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-11-26 12:26:35 +00:00
spaced4ndy 1f04984a34 ios: offer to create 1-time link on address views (#5249) 2024-11-26 14:43:39 +04:00
Stanislav Dmitrenko 7a91ed2ab2 android, desktop: view conditions as markdown (#5247)
* android, desktop: view conditions as markdown

* better animation

* unused

* open chat links inside the app and removed divider, smaller font

* paddings
2024-11-25 16:20:02 +00:00
spaced4ndy d912fe07a1 core: fix pagination indexes (#5241) 2024-11-25 18:51:49 +04:00
Evgeny cfc21dfb51 ios: address or 1-time link (#5246) 2024-11-25 18:15:32 +04:00
spaced4ndy e5c83b20c9 android, desktop: fix operator disabled indication (#5242) 2024-11-25 15:52:30 +04:00
Evgeny 97b472fd9c blog: operators (#5240)
* blog: network operators (draft)

* update

* update

* ui: update whats new link

* fix file name

* update

* update

* update

* update
2024-11-25 09:24:12 +00:00
Stanislav Dmitrenko d40d690f86 desktop (Windows): fix linking with openssl 3 (#5238) 2024-11-24 08:27:58 +00:00
346 changed files with 27723 additions and 12635 deletions
+3
View File
@@ -79,3 +79,6 @@ website/package-lock.json
website/.cache
website/test/stubs-layout-cache/_includes/*.js
apps/android/app/release
packages/simplex-chat-nodejs/build
packages/simplex-chat-nodejs/.vscode
packages/simplex-chat-nodejs/libs
+5 -7
View File
@@ -233,6 +233,8 @@ You can use SimpleX with your own servers and still communicate with people usin
Recent and important updates:
[Dec 10, 2024. SimpleX network: preset servers operated by Flux, business chats and more with v6.2 of the apps](./20241210-simplex-network-v6-2-servers-by-flux-business-chats.md)
[Oct 14, 2024. SimpleX network: security review of protocols design by Trail of Bits, v6.1 released with better calls and user experience.](./blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.md)
[Aug 14, 2024. SimpleX network: the investment from Jack Dorsey and Asymmetric, v6.0 released with the new user experience and private message routing](./blog/20240814-simplex-chat-vision-funding-v6-private-routing-new-user-experience.md)
@@ -241,20 +243,14 @@ Recent and important updates:
[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)
[Nov 25, 2023. SimpleX Chat v5.4 released: link mobile and desktop apps via quantum resistant protocol, and much better groups](./blog/20231125-simplex-chat-v5-4-link-mobile-desktop-quantum-resistant-better-groups.md).
[Sep 25, 2023. SimpleX Chat v5.3 released: desktop app, local file encryption, improved groups and directory service](./blog/20230925-simplex-chat-v5-3-desktop-app-local-file-encryption-directory-service.md).
[Apr 22, 2023. SimpleX Chat: vision and funding, v5.0 released with videos and files up to 1gb](./blog/20230422-simplex-chat-vision-funding-v5-videos-files-passcode.md).
[Mar 1, 2023. SimpleX File Transfer Protocol send large files efficiently, privately and securely, soon to be integrated into SimpleX Chat apps.](./blog/20230301-simplex-file-transfer-protocol.md).
[Nov 8, 2022. Security audit by Trail of Bits, the new website and v4.2 released](./blog/20221108-simplex-chat-v4.2-security-audit-new-website.md).
[Sep 28, 2022. v4.0: encrypted local chat database and many other changes](./blog/20220928-simplex-chat-v4-encrypted-database.md).
[All updates](./blog)
## :zap: Quick installation of a terminal app
@@ -382,9 +378,11 @@ Please also join [#simplex-devs](https://simplex.chat/contact#/?v=1-2&smp=smp%3A
- ✅ 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).
- ✅ Support multiple network operators in the app.
- 🏗 Large groups, communities and public channels.
- 🏗 Short links to connect and join groups.
- 🏗 Improve stability and reduce battery usage.
- 🏗 Improve experience for the new users.
- 🏗 Large groups, communities and public channels.
- 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.
+1
View File
@@ -17,6 +17,7 @@ class AppDelegate: NSObject, UIApplicationDelegate {
application.registerForRemoteNotifications()
removePasscodesIfReinstalled()
prepareForLaunch()
deleteOldChatArchive()
return true
}
+2 -2
View File
@@ -845,7 +845,7 @@ final class ChatModel: ObservableObject {
}
func dismissConnReqView(_ id: String) {
if id == showingInvitation?.connId {
if id == showingInvitation?.pcc.id {
markShowingInvitationUsed()
dismissAllSheets()
}
@@ -898,7 +898,7 @@ final class ChatModel: ObservableObject {
}
struct ShowingInvitation {
var connId: String
var pcc: PendingContactConnection
var connChatUsed: Bool
}
+5 -1
View File
@@ -26,6 +26,7 @@ enum NtfCallAction {
class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject {
static let shared = NtfManager()
public var navigatingToChat = false
private var granted = false
private var prevNtfTime: Dictionary<ChatId, Date> = [:]
@@ -74,7 +75,10 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject {
}
} else {
if let chatId = content.targetContentIdentifier {
ItemsModel.shared.loadOpenChat(chatId)
self.navigatingToChat = true
ItemsModel.shared.loadOpenChat(chatId) {
self.navigatingToChat = false
}
}
}
}
+42 -20
View File
@@ -446,6 +446,13 @@ func apiChatItemReaction(type: ChatType, id: Int64, itemId: Int64, add: Bool, re
throw r
}
func apiGetReactionMembers(groupId: Int64, itemId: Int64, reaction: MsgReaction) async throws -> [MemberReaction] {
let userId = try currentUserId("apiGetReactionMemebers")
let r = await chatSendCmd(.apiGetReactionMembers(userId: userId, groupId: groupId, itemId: itemId, reaction: reaction ))
if case let .reactionMembers(_, memberReactions) = r { return memberReactions }
throw r
}
func apiDeleteChatItems(type: ChatType, id: Int64, itemIds: [Int64], mode: CIDeleteMode) async throws -> [ChatItemDeletion] {
let r = await chatSendCmd(.apiDeleteChatItem(type: type, id: id, itemIds: itemIds, mode: mode), bgDelay: msgDelay)
if case let .chatItemsDeleted(_, items, _) = r { return items }
@@ -512,7 +519,14 @@ func testProtoServer(server: String) async throws -> Result<(), ProtocolTestFail
throw r
}
func getServerOperators() throws -> ServerOperatorConditions {
func getServerOperators() async throws -> ServerOperatorConditions {
let r = await chatSendCmd(.apiGetServerOperators)
if case let .serverOperatorConditions(conditions) = r { return conditions }
logger.error("getServerOperators error: \(String(describing: r))")
throw r
}
func getServerOperatorsSync() throws -> ServerOperatorConditions {
let r = chatSendCmdSync(.apiGetServerOperators)
if case let .serverOperatorConditions(conditions) = r { return conditions }
logger.error("getServerOperators error: \(String(describing: r))")
@@ -1061,8 +1075,8 @@ func apiRejectContactRequest(contactReqId: Int64) async throws {
throw r
}
func apiChatRead(type: ChatType, id: Int64, itemRange: (Int64, Int64)) async throws {
try await sendCommandOkResp(.apiChatRead(type: type, id: id, itemRange: itemRange))
func apiChatRead(type: ChatType, id: Int64) async throws {
try await sendCommandOkResp(.apiChatRead(type: type, id: id))
}
func apiChatItemsRead(type: ChatType, id: Int64, itemIds: [Int64]) async throws {
@@ -1368,15 +1382,13 @@ func apiGetNetworkStatuses() throws -> [ConnNetworkStatus] {
throw r
}
func markChatRead(_ chat: Chat, aboveItem: ChatItem? = nil) async {
func markChatRead(_ chat: Chat) async {
do {
if chat.chatStats.unreadCount > 0 {
let minItemId = chat.chatStats.minUnreadItemId
let itemRange = (minItemId, aboveItem?.id ?? chat.chatItems.last?.id ?? minItemId)
let cInfo = chat.chatInfo
try await apiChatRead(type: cInfo.chatType, id: cInfo.apiId, itemRange: itemRange)
try await apiChatRead(type: cInfo.chatType, id: cInfo.apiId)
await MainActor.run {
withAnimation { ChatModel.shared.markChatItemsRead(cInfo, aboveItem: aboveItem) }
withAnimation { ChatModel.shared.markChatItemsRead(cInfo) }
}
}
if chat.chatStats.unreadChat {
@@ -1399,17 +1411,6 @@ func markChatUnread(_ chat: Chat, unreadChat: Bool = true) async {
}
}
func apiMarkChatItemRead(_ cInfo: ChatInfo, _ cItem: ChatItem) async {
do {
try await apiChatRead(type: cInfo.chatType, id: cInfo.apiId, itemRange: (cItem.id, cItem.id))
DispatchQueue.main.async {
ChatModel.shared.markChatItemsRead(cInfo, [cItem.id])
}
} catch {
logger.error("apiChatRead error: \(responseError(error))")
}
}
func apiMarkChatItemsRead(_ cInfo: ChatInfo, _ itemIds: [ChatItem.ID]) async {
do {
try await apiChatItemsRead(type: cInfo.chatType, id: cInfo.apiId, itemIds: itemIds)
@@ -1605,7 +1606,16 @@ func initializeChat(start: Bool, confirmStart: Bool = false, dbKey: String? = ni
try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get())
m.chatInitialized = true
m.currentUser = try apiGetActiveUser()
m.conditions = try getServerOperators()
m.conditions = try getServerOperatorsSync()
if shouldImportAppSettingsDefault.get() {
do {
let appSettings = try apiGetAppSettings(settings: AppSettings.current.prepareForExport())
appSettings.importIntoApp()
shouldImportAppSettingsDefault.set(false)
} catch {
logger.error("Error while importing app settings: \(error)")
}
}
if m.currentUser == nil {
onboardingStageDefault.set(.step1_SimpleXInfo)
privacyDeliveryReceiptsSet.set(true)
@@ -2011,6 +2021,18 @@ func processReceivedMsg(_ res: ChatResponse) async {
m.removeChat(hostConn.id)
}
}
case let .businessLinkConnecting(user, groupInfo, _, fromContact):
if !active(user) { return }
await MainActor.run {
m.updateGroup(groupInfo)
}
if m.chatId == fromContact.id {
ItemsModel.shared.loadOpenChat(groupInfo.id)
}
await MainActor.run {
m.removeChat(fromContact.id)
}
case let .joinedGroupMemberConnecting(user, groupInfo, _, member):
if active(user) {
await MainActor.run {
+2 -1
View File
@@ -143,7 +143,8 @@ struct SimpleXApp: App {
let chats = try await apiGetChatsAsync()
await MainActor.run { chatModel.updateChats(chats) }
if let id = chatModel.chatId,
let chat = chatModel.getChat(id) {
let chat = chatModel.getChat(id),
!NtfManager.shared.navigatingToChat {
Task { await loadChat(chat: chat, clearItems: false) }
}
if let ncr = chatModel.ntfContactRequest {
@@ -45,7 +45,7 @@ struct ChatInfoToolbar: View {
}
private var contactVerifiedShield: Text {
(Text(Image(systemName: "checkmark.shield")) + Text(" "))
(Text(Image(systemName: "checkmark.shield")) + textSpace)
.font(.caption)
.foregroundColor(theme.colors.secondary)
.baselineOffset(1)
+106 -25
View File
@@ -96,6 +96,8 @@ struct ChatInfoView: View {
@ObservedObject var chat: Chat
@State var contact: Contact
@State var localAlias: String
@State var featuresAllowed: ContactFeaturesAllowed
@State var currentFeaturesAllowed: ContactFeaturesAllowed
var onSearch: () -> Void
@State private var connectionStats: ConnectionStats? = nil
@State private var customUserProfile: Profile? = nil
@@ -154,8 +156,8 @@ struct ChatInfoView: View {
HStack(alignment: .center, spacing: 8) {
let buttonWidth = g.size.width / 4
searchButton(width: buttonWidth)
AudioCallButton(chat: chat, contact: contact, width: buttonWidth) { alert = .someAlert(alert: $0) }
VideoButton(chat: chat, contact: contact, width: buttonWidth) { alert = .someAlert(alert: $0) }
AudioCallButton(chat: chat, contact: contact, connectionStats: $connectionStats, width: buttonWidth) { alert = .someAlert(alert: $0) }
VideoButton(chat: chat, contact: contact, connectionStats: $connectionStats, width: buttonWidth) { alert = .someAlert(alert: $0) }
muteButton(width: buttonWidth)
}
}
@@ -312,7 +314,15 @@ struct ChatInfoView: View {
case .networkStatusAlert: return networkStatusAlert()
case .switchAddressAlert: return switchAddressAlert(switchContactAddress)
case .abortSwitchAddressAlert: return abortSwitchAddressAlert(abortSwitchContactAddress)
case .syncConnectionForceAlert: return syncConnectionForceAlert({ syncContactConnection(force: true) })
case .syncConnectionForceAlert:
return syncConnectionForceAlert({
Task {
if let stats = await syncContactConnection(contact, force: true, showAlert: { alert = .someAlert(alert: $0) }) {
connectionStats = stats
dismiss()
}
}
})
case let .queueInfo(info): return queueInfoAlert(info)
case let .someAlert(a): return a.alert
case let .error(title, error): return mkAlert(title: title, message: error)
@@ -327,6 +337,16 @@ struct ChatInfoView: View {
$0.content
}
}
.onDisappear {
if currentFeaturesAllowed != featuresAllowed {
showAlert(
title: NSLocalizedString("Save preferences?", comment: "alert title"),
buttonTitle: NSLocalizedString("Save and notify contact", comment: "alert button"),
buttonAction: { savePreferences() },
cancelButton: true
)
}
}
}
private func contactInfoHeader() -> some View {
@@ -339,7 +359,7 @@ struct ChatInfoView: View {
Text(Image(systemName: "checkmark.shield"))
.foregroundColor(theme.colors.secondary)
.font(.title2)
+ Text(" ")
+ textSpace
+ Text(contact.profile.displayName)
.font(.largeTitle)
)
@@ -447,8 +467,9 @@ struct ChatInfoView: View {
NavigationLink {
ContactPreferencesView(
contact: $contact,
featuresAllowed: contactUserPrefsToFeaturesAllowed(contact.mergedPreferences),
currentFeaturesAllowed: contactUserPrefsToFeaturesAllowed(contact.mergedPreferences)
featuresAllowed: $featuresAllowed,
currentFeaturesAllowed: $currentFeaturesAllowed,
savePreferences: savePreferences
)
.navigationBarTitle("Contact preferences")
.modifier(ThemedBackground(grouped: true))
@@ -480,7 +501,12 @@ struct ChatInfoView: View {
private func synchronizeConnectionButton() -> some View {
Button {
syncContactConnection(force: false)
Task {
if let stats = await syncContactConnection(contact, force: false, showAlert: { alert = .someAlert(alert: $0) }) {
connectionStats = stats
dismiss()
}
}
} label: {
Label("Fix connection", systemImage: "exclamationmark.arrow.triangle.2.circlepath")
.foregroundColor(.orange)
@@ -599,29 +625,50 @@ struct ChatInfoView: View {
}
}
private func syncContactConnection(force: Bool) {
private func savePreferences() {
Task {
do {
let stats = try apiSyncContactRatchet(contact.apiId, force)
connectionStats = stats
await MainActor.run {
chatModel.updateContactConnectionStats(contact, stats)
dismiss()
}
} catch let error {
logger.error("syncContactConnection apiSyncContactRatchet error: \(responseError(error))")
let a = getErrorAlert(error, "Error synchronizing connection")
await MainActor.run {
alert = .error(title: a.title, error: a.message)
let prefs = contactFeaturesAllowedToPrefs(featuresAllowed)
if let toContact = try await apiSetContactPrefs(contactId: contact.contactId, preferences: prefs) {
await MainActor.run {
contact = toContact
chatModel.updateContact(toContact)
currentFeaturesAllowed = featuresAllowed
}
}
} catch {
logger.error("ContactPreferencesView apiSetContactPrefs error: \(responseError(error))")
}
}
}
}
func syncContactConnection(_ contact: Contact, force: Bool, showAlert: (SomeAlert) -> Void) async -> ConnectionStats? {
do {
let stats = try apiSyncContactRatchet(contact.apiId, force)
await MainActor.run {
ChatModel.shared.updateContactConnectionStats(contact, stats)
}
return stats
} catch let error {
logger.error("syncContactConnection apiSyncContactRatchet error: \(responseError(error))")
let a = getErrorAlert(error, "Error synchronizing connection")
await MainActor.run {
showAlert(
SomeAlert(
alert: mkAlert(title: a.title, message: a.message),
id: "syncContactConnection error"
)
)
}
return nil
}
}
struct AudioCallButton: View {
var chat: Chat
var contact: Contact
@Binding var connectionStats: ConnectionStats?
var width: CGFloat
var showAlert: (SomeAlert) -> Void
@@ -629,6 +676,7 @@ struct AudioCallButton: View {
CallButton(
chat: chat,
contact: contact,
connectionStats: $connectionStats,
image: "phone.fill",
title: "call",
mediaType: .audio,
@@ -641,6 +689,7 @@ struct AudioCallButton: View {
struct VideoButton: View {
var chat: Chat
var contact: Contact
@Binding var connectionStats: ConnectionStats?
var width: CGFloat
var showAlert: (SomeAlert) -> Void
@@ -648,6 +697,7 @@ struct VideoButton: View {
CallButton(
chat: chat,
contact: contact,
connectionStats: $connectionStats,
image: "video.fill",
title: "video",
mediaType: .video,
@@ -660,6 +710,7 @@ struct VideoButton: View {
private struct CallButton: View {
var chat: Chat
var contact: Contact
@Binding var connectionStats: ConnectionStats?
var image: String
var title: LocalizedStringKey
var mediaType: CallMediaType
@@ -671,12 +722,40 @@ private struct CallButton: View {
InfoViewButton(image: image, title: title, disabledLook: !canCall, width: width) {
if canCall {
if CallController.useCallKit() {
CallController.shared.startCall(contact, mediaType)
} else {
// When CallKit is not used, colorscheme will be changed and it will be visible if not hiding sheets first
dismissAllSheets(animated: true) {
CallController.shared.startCall(contact, mediaType)
if let connStats = connectionStats {
if connStats.ratchetSyncState == .ok {
if CallController.useCallKit() {
CallController.shared.startCall(contact, mediaType)
} else {
// When CallKit is not used, colorscheme will be changed and it will be visible if not hiding sheets first
dismissAllSheets(animated: true) {
CallController.shared.startCall(contact, mediaType)
}
}
} else if connStats.ratchetSyncAllowed {
showAlert(SomeAlert(
alert: Alert(
title: Text("Fix connection?"),
message: Text("Connection requires encryption renegotiation."),
primaryButton: .default(Text("Fix")) {
Task {
if let stats = await syncContactConnection(contact, force: false, showAlert: showAlert) {
connectionStats = stats
}
}
},
secondaryButton: .cancel()
),
id: "can't call contact, fix connection"
))
} else {
showAlert(SomeAlert(
alert: mkAlert(
title: "Can't call contact",
message: "Encryption renegotiation in progress."
),
id: "can't call contact, encryption renegotiation in progress"
))
}
}
} else if contact.nextSendGrpInv {
@@ -1173,6 +1252,8 @@ struct ChatInfoView_Previews: PreviewProvider {
chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []),
contact: Contact.sampleData,
localAlias: "",
featuresAllowed: contactUserPrefsToFeaturesAllowed(Contact.sampleData.mergedPreferences),
currentFeaturesAllowed: contactUserPrefsToFeaturesAllowed(Contact.sampleData.mergedPreferences),
onSearch: {}
)
}
@@ -47,7 +47,7 @@ struct CIFeaturePreferenceView: View {
+ Text(acceptText)
.fontWeight(.medium)
.foregroundColor(theme.colors.primary)
+ Text(" ")
+ Text(verbatim: " ")
}
r = r + chatItem.timestampText
.fontWeight(.light)
@@ -45,7 +45,7 @@ struct CIGroupInvitationView: View {
Text(chatIncognito ? "Tap to join incognito" : "Tap to join")
.foregroundColor(inProgress ? theme.colors.secondary : chatIncognito ? .indigo : theme.colors.primary)
.font(.callout)
+ Text(" ")
+ Text(verbatim: " ")
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, colorMode: .transparent, showStatus: false, showEdited: false, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp)
)
.overlay(DetermineWidth())
@@ -53,7 +53,7 @@ struct CIGroupInvitationView: View {
} else {
(
groupInvitationText()
+ Text(" ")
+ Text(verbatim: " ")
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, colorMode: .transparent, showStatus: false, showEdited: false, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp)
)
.overlay(DetermineWidth())
@@ -23,7 +23,7 @@ struct CIMemberCreatedContactView: View {
.onTapGesture {
dismissAllSheets(animated: true)
DispatchQueue.main.async {
m.chatId = "@\(contactId)"
ItemsModel.shared.loadOpenChat("@\(contactId)")
}
}
} else {
@@ -45,7 +45,7 @@ struct CIMemberCreatedContactView: View {
+ Text(openText)
.fontWeight(.medium)
.foregroundColor(theme.colors.primary)
+ Text(" ")
+ Text(verbatim: " ")
}
r = r + chatItem.timestampText
.fontWeight(.light)
@@ -83,7 +83,7 @@ enum MetaColorMode {
? Image("checkmark.wide")
: Image(systemName: "circlebadge.fill")
).foregroundColor(.clear)
case .invertedMaterial: Text(" ").kerning(13)
case .invertedMaterial: textSpace.kerning(13)
}
}
}
@@ -120,7 +120,7 @@ func ciMetaText(
if ttl != chatTTL {
r = r + colored(Text(shortTimeText(ttl)), resolved)
}
space = Text(" ")
space = textSpace
}
if showViaProxy, meta.sentViaProxy == true {
appendSpace()
@@ -138,12 +138,12 @@ func ciMetaText(
} else if !meta.disappearing {
r = r + colorMode.statusSpacer(meta.itemStatus.sent)
}
space = Text(" ")
space = textSpace
}
if let enc = encrypted {
appendSpace()
r = r + statusIconText(enc ? "lock" : "lock.open", resolved)
space = Text(" ")
space = textSpace
}
if showTimesamp {
appendSpace()
@@ -121,11 +121,11 @@ struct CIRcvDecryptionError: View {
Text(Image(systemName: "exclamationmark.arrow.triangle.2.circlepath"))
.foregroundColor(syncSupported ? theme.colors.primary : theme.colors.secondary)
.font(.callout)
+ Text(" ")
+ textSpace
+ Text("Fix connection")
.foregroundColor(syncSupported ? theme.colors.primary : theme.colors.secondary)
.font(.callout)
+ Text(" ")
+ Text(verbatim: " ")
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, colorMode: .transparent, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp)
)
}
@@ -144,7 +144,7 @@ struct CIRcvDecryptionError: View {
Text(chatItem.content.text)
.foregroundColor(.red)
.italic()
+ Text(" ")
+ Text(verbatim: " ")
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, colorMode: .transparent, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp)
}
.padding(.horizontal, 12)
@@ -17,7 +17,7 @@ struct MarkedDeletedItemView: View {
var chatItem: ChatItem
var body: some View {
(Text(mergedMarkedDeletedText).italic() + Text(" ") + chatItem.timestampText)
(Text(mergedMarkedDeletedText).italic() + textSpace + chatItem.timestampText)
.font(.caption)
.foregroundColor(theme.colors.secondary)
.padding(.horizontal, 12)
@@ -11,7 +11,7 @@ import SimpleXChat
let uiLinkColor = UIColor(red: 0, green: 0.533, blue: 1, alpha: 1)
private let noTyping = Text(" ")
private let noTyping = Text(verbatim: " ")
private let typingIndicators: [Text] = [
(typing(.black) + typing() + typing()),
@@ -85,7 +85,7 @@ struct MsgContentView: View {
}
private func reserveSpaceForMeta(_ mt: CIMeta) -> Text {
(rightToLeft ? Text("\n") : Text(" ")) + ciMetaText(mt, chatTTL: chat.chatInfo.timedMessagesTTL, encrypted: nil, colorMode: .transparent, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp)
(rightToLeft ? Text("\n") : Text(verbatim: " ")) + ciMetaText(mt, chatTTL: chat.chatInfo.timedMessagesTTL, encrypted: nil, colorMode: .transparent, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp)
}
}
@@ -104,7 +104,7 @@ func messageText(_ text: String, _ formattedText: [FormattedText]?, _ sender: St
}
if let i = icon {
res = Text(Image(systemName: i)).foregroundColor(secondaryColor) + Text(" ") + res
res = Text(Image(systemName: i)).foregroundColor(secondaryColor) + textSpace + res
}
if let s = sender {
@@ -170,7 +170,7 @@ struct ChatItemContentView<Content: View>: View {
private func eventItemViewText(_ secondaryColor: Color) -> Text {
if !revealed, let t = mergedGroupEventText {
return chatEventText(t + Text(" ") + chatItem.timestampText, secondaryColor)
return chatEventText(t + textSpace + chatItem.timestampText, secondaryColor)
} else if let member = chatItem.memberDisplayName {
return Text(member + " ")
.font(.caption)
@@ -203,7 +203,7 @@ struct ChatItemContentView<Content: View>: View {
} else if ns.count == 0 {
Text("\(count) group events")
} else if count > ns.count {
Text(members) + Text(" ") + Text("and \(count - ns.count) other events")
Text(members) + textSpace + Text("and \(count - ns.count) other events")
} else {
Text(members)
}
@@ -234,7 +234,7 @@ func chatEventText(_ text: Text, _ secondaryColor: Color) -> Text {
}
func chatEventText(_ eventText: LocalizedStringKey, _ ts: Text, _ secondaryColor: Color) -> Text {
chatEventText(Text(eventText) + Text(" ") + ts, secondaryColor)
chatEventText(Text(eventText) + textSpace + ts, secondaryColor)
}
func chatEventText(_ ci: ChatItem, _ secondaryColor: Color) -> Text {
+130 -12
View File
@@ -133,7 +133,12 @@ struct ChatView: View {
.appSheet(item: $selectedMember) { member in
Group {
if case let .group(groupInfo) = chat.chatInfo {
GroupMemberInfoView(groupInfo: groupInfo, groupMember: member, navigation: true)
GroupMemberInfoView(
groupInfo: groupInfo,
chat: chat,
groupMember: member,
navigation: true
)
}
}
}
@@ -226,6 +231,8 @@ struct ChatView: View {
chat: chat,
contact: contact,
localAlias: chat.chatInfo.localAlias,
featuresAllowed: contactUserPrefsToFeaturesAllowed(contact.mergedPreferences),
currentFeaturesAllowed: contactUserPrefsToFeaturesAllowed(contact.mergedPreferences),
onSearch: { focusSearch() }
)
}
@@ -901,7 +908,7 @@ struct ChatView: View {
@State private var showChatItemInfoSheet: Bool = false
@State private var chatItemInfo: ChatItemInfo?
@State private var msgWidth: CGFloat = 0
@Binding var selectedChatItems: Set<Int64>?
@Binding var forwardedChatItems: [ChatItem]
@@ -987,7 +994,7 @@ struct ChatView: View {
}
} else if chatItem.isRcvNew {
waitToMarkRead {
await apiMarkChatItemRead(chat.chatInfo, chatItem)
await apiMarkChatItemsRead(chat.chatInfo, [chatItem.id])
}
}
}
@@ -1117,14 +1124,13 @@ struct ChatView: View {
HStack(alignment: .top, spacing: 10) {
MemberProfileImage(member, size: memberImageSize, backgroundColor: theme.colors.background)
.onTapGesture {
if let member = m.getGroupMember(member.groupMemberId) {
selectedMember = member
if let mem = m.getGroupMember(member.groupMemberId) {
selectedMember = mem
} else {
Task {
await m.loadGroupMembers(groupInfo) {
selectedMember = m.getGroupMember(member.groupMemberId)
}
}
let mem = GMember.init(member)
m.groupMembers.append(mem)
m.groupMembersIndexes[member.groupMemberId] = m.groupMembers.count - 1
selectedMember = mem
}
}
chatItemWithMenu(ci, range, maxWidth, itemSeparation)
@@ -1244,11 +1250,20 @@ struct ChatView: View {
}
.padding(.horizontal, 6)
.padding(.vertical, 4)
if chat.chatInfo.featureEnabled(.reactions) && (ci.allowAddReaction || r.userReacted) {
.if(chat.chatInfo.featureEnabled(.reactions) && (ci.allowAddReaction || r.userReacted)) { v in
v.onTapGesture {
setReaction(ci, add: !r.userReacted, reaction: r.reaction)
}
}
if case let .group(groupInfo) = chat.chatInfo {
v.contextMenu {
ReactionContextMenu(
groupInfo: groupInfo,
itemId: ci.id,
reactionCount: r,
selectedMember: $selectedMember
)
}
} else {
v
}
@@ -1838,6 +1853,109 @@ private func buildTheme() -> AppTheme {
}
}
struct ReactionContextMenu: View {
@EnvironmentObject var m: ChatModel
let groupInfo: GroupInfo
var itemId: Int64
var reactionCount: CIReactionCount
@Binding var selectedMember: GMember?
@State private var memberReactions: [MemberReaction] = []
@AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var radius = defaultProfileImageCorner
var body: some View {
groupMemberReactionList()
.task {
logger.debug("ReactionContextMenu task \(radius)")
await loadChatItemReaction()
}
}
@ViewBuilder private func groupMemberReactionList() -> some View {
if memberReactions.isEmpty {
ForEach(Array(repeating: 0, count: reactionCount.totalReacted), id: \.self) { _ in
Text(verbatim: " ")
}
} else {
ForEach(memberReactions, id: \.groupMember.groupMemberId) { mr in
let mem = mr.groupMember
let userMember = mem.groupMemberId == groupInfo.membership.groupMemberId
Button {
if let member = m.getGroupMember(mem.groupMemberId) {
selectedMember = member
} else {
let member = GMember.init(mem)
m.groupMembers.append(member)
m.groupMembersIndexes[member.groupMemberId] = m.groupMembers.count - 1
selectedMember = member
}
} label: {
HStack {
Text(mem.displayName)
if let img = cropImage(mem.image) {
Image(uiImage: img)
} else {
Image(systemName: "person.crop.circle")
}
}
}
.disabled(userMember)
}
}
}
private func cropImage(_ img: String?) -> UIImage? {
return if let originalImage = imageFromBase64(img) {
maskToCustomShape(originalImage, size: 30, radius: radius)
} else {
nil
}
}
private func loadChatItemReaction() async {
do {
let memberReactions = try await apiGetReactionMembers(
groupId: groupInfo.groupId,
itemId: itemId,
reaction: reactionCount.reaction
)
await MainActor.run {
self.memberReactions = memberReactions
}
} catch let error {
logger.error("apiGetReactionMembers error: \(responseError(error))")
}
}
}
func maskToCustomShape(_ image: UIImage, size: CGFloat, radius: CGFloat) -> UIImage {
let path = Path { path in
if radius >= 50 {
path.addEllipse(in: CGRect(x: 0, y: 0, width: size, height: size))
} else if radius <= 0 {
path.addRect(CGRect(x: 0, y: 0, width: size, height: size))
} else {
let cornerRadius = size * CGFloat(radius) / 100
path.addRoundedRect(
in: CGRect(x: 0, y: 0, width: size, height: size),
cornerSize: CGSize(width: cornerRadius, height: cornerRadius),
style: .continuous
)
}
}
return UIGraphicsImageRenderer(size: CGSize(width: size, height: size)).image { context in
context.cgContext.addPath(path.cgPath)
context.cgContext.clip()
let scale = size / max(image.size.width, image.size.height)
let imageSize = CGSize(width: image.size.width * scale, height: image.size.height * scale)
let imageOrigin = CGPoint(
x: (size - imageSize.width) / 2,
y: (size - imageSize.height) / 2
)
image.draw(in: CGRect(origin: imageOrigin, size: imageSize))
}
}
struct ToggleNtfsButton: View {
@ObservedObject var chat: Chat
@@ -85,7 +85,7 @@ struct ContextItemView: View {
}
func image(_ s: String) -> Text {
Text(Image(systemName: s)).foregroundColor(Color(uiColor: .tertiaryLabel)) + Text(" ")
Text(Image(systemName: s)).foregroundColor(Color(uiColor: .tertiaryLabel)) + textSpace
}
}
}
@@ -14,9 +14,10 @@ struct ContactPreferencesView: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@Binding var contact: Contact
@State var featuresAllowed: ContactFeaturesAllowed
@State var currentFeaturesAllowed: ContactFeaturesAllowed
@Binding var featuresAllowed: ContactFeaturesAllowed
@Binding var currentFeaturesAllowed: ContactFeaturesAllowed
@State private var showSaveDialogue = false
let savePreferences: () -> Void
var body: some View {
let user: User = chatModel.currentUser!
@@ -48,7 +49,10 @@ struct ContactPreferencesView: View {
savePreferences()
dismiss()
}
Button("Exit without saving") { dismiss() }
Button("Exit without saving") {
featuresAllowed = currentFeaturesAllowed
dismiss()
}
}
}
@@ -118,31 +122,15 @@ struct ContactPreferencesView: View {
private func featureFooter(_ feature: ChatFeature, _ enabled: FeatureEnabled) -> some View {
Text(feature.enabledDescription(enabled))
}
private func savePreferences() {
Task {
do {
let prefs = contactFeaturesAllowedToPrefs(featuresAllowed)
if let toContact = try await apiSetContactPrefs(contactId: contact.contactId, preferences: prefs) {
await MainActor.run {
contact = toContact
chatModel.updateContact(toContact)
currentFeaturesAllowed = featuresAllowed
}
}
} catch {
logger.error("ContactPreferencesView apiSetContactPrefs error: \(responseError(error))")
}
}
}
}
struct ContactPreferencesView_Previews: PreviewProvider {
static var previews: some View {
ContactPreferencesView(
contact: Binding.constant(Contact.sampleData),
featuresAllowed: ContactFeaturesAllowed.sampleData,
currentFeaturesAllowed: ContactFeaturesAllowed.sampleData
featuresAllowed: Binding.constant(ContactFeaturesAllowed.sampleData),
currentFeaturesAllowed: Binding.constant(ContactFeaturesAllowed.sampleData),
savePreferences: {}
)
}
}
@@ -78,7 +78,12 @@ struct AddGroupMembersViewCommon: View {
let count = selectedContacts.count
Section {
if creatingGroup {
groupPreferencesButton($groupInfo, true)
GroupPreferencesButton(
groupInfo: $groupInfo,
preferences: groupInfo.fullGroupPreferences,
currentPreferences: groupInfo.fullGroupPreferences,
creatingGroup: true
)
}
rolePicker()
inviteMembersButton()
@@ -140,12 +145,13 @@ struct AddGroupMembersViewCommon: View {
return dummy
}()
private func inviteMembersButton() -> some View {
@ViewBuilder private func inviteMembersButton() -> some View {
let label: LocalizedStringKey = groupInfo.businessChat == nil ? "Invite to group" : "Invite to chat"
Button {
inviteMembers()
} label: {
HStack {
Text("Invite to group")
Text(label)
Image(systemName: "checkmark")
}
}
@@ -231,6 +237,7 @@ func searchFieldView(text: Binding<String>, focussed: FocusState<Bool>.Binding,
.focused(focussed)
.foregroundColor(onBackgroundColor)
.frame(maxWidth: .infinity)
.autocorrectionDisabled(true)
Image(systemName: "xmark.circle.fill")
.resizable()
.scaledToFit()
@@ -81,13 +81,13 @@ struct GroupChatInfoView: View {
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
Section {
if groupInfo.canEdit {
if groupInfo.isOwner && groupInfo.businessChat == nil {
editGroupButton()
}
if groupInfo.groupProfile.description != nil || groupInfo.canEdit {
if groupInfo.groupProfile.description != nil || (groupInfo.isOwner && groupInfo.businessChat == nil) {
addOrEditWelcomeMessage()
}
groupPreferencesButton($groupInfo)
GroupPreferencesButton(groupInfo: $groupInfo, preferences: groupInfo.fullGroupPreferences, currentPreferences: groupInfo.fullGroupPreferences)
if members.filter({ $0.wrapped.memberCurrent }).count <= SMALL_GROUPS_RCPS_MEM_LIMIT {
sendReceiptsOption()
} else {
@@ -101,13 +101,20 @@ struct GroupChatInfoView: View {
} header: {
Text("")
} footer: {
Text("Only group owners can change group preferences.")
let label: LocalizedStringKey = (
groupInfo.businessChat == nil
? "Only group owners can change group preferences."
: "Only chat owners can change preferences."
)
Text(label)
.foregroundColor(theme.colors.secondary)
}
Section(header: Text("\(members.count + 1) members").foregroundColor(theme.colors.secondary)) {
if groupInfo.canAddMembers {
groupLinkButton()
if groupInfo.businessChat == nil {
groupLinkButton()
}
if (chat.chatInfo.incognito) {
Label("Invite members", systemImage: "plus")
.foregroundColor(Color(uiColor: .tertiaryLabel))
@@ -276,10 +283,15 @@ struct GroupChatInfoView: View {
}
private func addMembersButton() -> some View {
NavigationLink {
let label: LocalizedStringKey = switch groupInfo.businessChat?.chatType {
case .customer: "Add team members"
case .business: "Add friends"
case .none: "Invite members"
}
return NavigationLink {
addMembersDestinationView()
} label: {
Label("Invite members", systemImage: "plus")
Label(label, systemImage: "plus")
}
}
@@ -418,7 +430,7 @@ struct GroupChatInfoView: View {
}
private var memberVerifiedShield: Text {
(Text(Image(systemName: "checkmark.shield")) + Text(" "))
(Text(Image(systemName: "checkmark.shield")) + textSpace)
.font(.caption)
.baselineOffset(2)
.kerning(-2)
@@ -427,7 +439,7 @@ struct GroupChatInfoView: View {
}
private func memberInfoView(_ groupMember: GMember) -> some View {
GroupMemberInfoView(groupInfo: groupInfo, groupMember: groupMember)
GroupMemberInfoView(groupInfo: groupInfo, chat: chat, groupMember: groupMember)
.navigationBarHidden(false)
}
@@ -487,11 +499,12 @@ struct GroupChatInfoView: View {
}
}
private func deleteGroupButton() -> some View {
@ViewBuilder private func deleteGroupButton() -> some View {
let label: LocalizedStringKey = groupInfo.businessChat == nil ? "Delete group" : "Delete chat"
Button(role: .destructive) {
alert = .deleteGroupAlert
} label: {
Label("Delete group", systemImage: "trash")
Label(label, systemImage: "trash")
.foregroundColor(Color.red)
}
}
@@ -505,20 +518,22 @@ struct GroupChatInfoView: View {
}
}
private func leaveGroupButton() -> some View {
@ViewBuilder private func leaveGroupButton() -> some View {
let label: LocalizedStringKey = groupInfo.businessChat == nil ? "Leave group" : "Leave chat"
Button(role: .destructive) {
alert = .leaveGroupAlert
} label: {
Label("Leave group", systemImage: "rectangle.portrait.and.arrow.right")
Label(label, systemImage: "rectangle.portrait.and.arrow.right")
.foregroundColor(Color.red)
}
}
// TODO reuse this and clearChatAlert with ChatInfoView
private func deleteGroupAlert() -> Alert {
let label: LocalizedStringKey = groupInfo.businessChat == nil ? "Delete group?" : "Delete chat?"
return Alert(
title: Text("Delete group?"),
message: deleteGroupAlertMessage(),
title: Text(label),
message: deleteGroupAlertMessage(groupInfo),
primaryButton: .destructive(Text("Delete")) {
Task {
do {
@@ -537,10 +552,6 @@ struct GroupChatInfoView: View {
)
}
private func deleteGroupAlertMessage() -> Text {
groupInfo.membership.memberCurrent ? Text("Group will be deleted for all members - this cannot be undone!") : Text("Group will be deleted for you - this cannot be undone!")
}
private func clearChatAlert() -> Alert {
Alert(
title: Text("Clear conversation?"),
@@ -556,9 +567,15 @@ struct GroupChatInfoView: View {
}
private func leaveGroupAlert() -> Alert {
Alert(
title: Text("Leave group?"),
message: Text("You will stop receiving messages from this group. Chat history will be preserved."),
let titleLabel: LocalizedStringKey = groupInfo.businessChat == nil ? "Leave group?" : "Leave chat?"
let messageLabel: LocalizedStringKey = (
groupInfo.businessChat == nil
? "You will stop receiving messages from this group. Chat history will be preserved."
: "You will stop receiving messages from this chat. Chat history will be preserved."
)
return Alert(
title: Text(titleLabel),
message: Text(messageLabel),
primaryButton: .destructive(Text("Leave")) {
Task {
await leaveGroup(chat.chatInfo.apiId)
@@ -602,9 +619,14 @@ struct GroupChatInfoView: View {
}
private func removeMemberAlert(_ mem: GroupMember) -> Alert {
Alert(
let messageLabel: LocalizedStringKey = (
groupInfo.businessChat == nil
? "Member will be removed from group - this cannot be undone!"
: "Member will be removed from chat - this cannot be undone!"
)
return Alert(
title: Text("Remove member?"),
message: Text("Member will be removed from group - this cannot be undone!"),
message: Text(messageLabel),
primaryButton: .destructive(Text("Remove")) {
Task {
do {
@@ -624,26 +646,80 @@ struct GroupChatInfoView: View {
}
}
func groupPreferencesButton(_ groupInfo: Binding<GroupInfo>, _ creatingGroup: Bool = false) -> some View {
NavigationLink {
GroupPreferencesView(
groupInfo: groupInfo,
preferences: groupInfo.wrappedValue.fullGroupPreferences,
currentPreferences: groupInfo.wrappedValue.fullGroupPreferences,
creatingGroup: creatingGroup
)
.navigationBarTitle("Group preferences")
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(.large)
} label: {
if creatingGroup {
Text("Set group preferences")
} else {
Label("Group preferences", systemImage: "switch.2")
func deleteGroupAlertMessage(_ groupInfo: GroupInfo) -> Text {
groupInfo.businessChat == nil ? (
groupInfo.membership.memberCurrent ? Text("Group will be deleted for all members - this cannot be undone!") : Text("Group will be deleted for you - this cannot be undone!")
) : (
groupInfo.membership.memberCurrent ? Text("Chat will be deleted for all members - this cannot be undone!") : Text("Chat will be deleted for you - this cannot be undone!")
)
}
struct GroupPreferencesButton: View {
@Binding var groupInfo: GroupInfo
@State var preferences: FullGroupPreferences
@State var currentPreferences: FullGroupPreferences
var creatingGroup: Bool = false
private var label: LocalizedStringKey {
groupInfo.businessChat == nil ? "Group preferences" : "Chat preferences"
}
var body: some View {
NavigationLink {
GroupPreferencesView(
groupInfo: $groupInfo,
preferences: $preferences,
currentPreferences: currentPreferences,
creatingGroup: creatingGroup,
savePreferences: savePreferences
)
.navigationBarTitle(label)
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(.large)
.onDisappear {
let saveText = NSLocalizedString(
creatingGroup ? "Save" : "Save and notify group members",
comment: "alert button"
)
if groupInfo.fullGroupPreferences != preferences {
showAlert(
title: NSLocalizedString("Save preferences?", comment: "alert title"),
buttonTitle: saveText,
buttonAction: { savePreferences() },
cancelButton: true
)
}
}
} label: {
if creatingGroup {
Text("Set group preferences")
} else {
Label(label, systemImage: "switch.2")
}
}
}
private func savePreferences() {
Task {
do {
var gp = groupInfo.groupProfile
gp.groupPreferences = toGroupPreferences(preferences)
let gInfo = try await apiUpdateGroup(groupInfo.groupId, gp)
await MainActor.run {
groupInfo = gInfo
ChatModel.shared.updateGroup(gInfo)
currentPreferences = preferences
}
} catch {
logger.error("GroupPreferencesView apiUpdateGroup error: \(responseError(error))")
}
}
}
}
func cantInviteIncognitoAlert() -> Alert {
Alert(
title: Text("Can't invite contacts!"),
@@ -14,11 +14,15 @@ struct GroupMemberInfoView: View {
@EnvironmentObject var theme: AppTheme
@Environment(\.dismiss) var dismiss: DismissAction
@State var groupInfo: GroupInfo
@ObservedObject var chat: Chat
@ObservedObject var groupMember: GMember
var navigation: Bool = false
@State private var connectionStats: ConnectionStats? = nil
@State private var connectionCode: String? = nil
@State private var connectionLoaded: Bool = false
@State private var knownContactChat: Chat? = nil
@State private var knownContact: Contact? = nil
@State private var knownContactConnectionStats: ConnectionStats? = nil
@State private var newRole: GroupMemberRole = .member
@State private var alert: GroupMemberInfoViewAlert?
@State private var sheet: PlanAndConnectActionSheet?
@@ -118,8 +122,8 @@ struct GroupMemberInfoView: View {
} label: {
Label("Share address", systemImage: "square.and.arrow.up")
}
if let contactId = member.memberContactId {
if knownDirectChat(contactId) == nil && !groupInfo.fullGroupPreferences.directMessages.on(for: groupInfo.membership) {
if member.memberContactId != nil {
if knownContactChat == nil && !groupInfo.fullGroupPreferences.directMessages.on(for: groupInfo.membership) {
connectViaAddressButton(contactLink)
}
} else {
@@ -135,7 +139,8 @@ struct GroupMemberInfoView: View {
}
Section(header: Text("Member").foregroundColor(theme.colors.secondary)) {
infoRow("Group", groupInfo.displayName)
let label: LocalizedStringKey = groupInfo.businessChat == nil ? "Group" : "Chat"
infoRow(label, groupInfo.displayName)
if let roles = member.canChangeRoleTo(groupInfo: groupInfo) {
Picker("Change role", selection: $newRole) {
@@ -227,6 +232,18 @@ struct GroupMemberInfoView: View {
}
logger.error("apiGroupMemberInfo or apiGetGroupMemberCode error: \(responseError(error))")
}
if let contactId = member.memberContactId, let (contactChat, contact) = knownDirectChat(contactId) {
knownContactChat = contactChat
knownContact = contact
do {
let (stats, _) = try await apiContactInfo(contactChat.chatInfo.apiId)
await MainActor.run {
knownContactConnectionStats = stats
}
} catch let error {
logger.error("apiContactInfo error: \(responseError(error))")
}
}
}
.onChange(of: newRole) { newRole in
if newRole != member.memberRole {
@@ -260,6 +277,11 @@ struct GroupMemberInfoView: View {
ProgressView().scaleEffect(2)
}
}
.onChange(of: chat.chatInfo) { c in
if case let .group(gI) = chat.chatInfo {
groupInfo = gI
}
}
.modifier(ThemedBackground(grouped: true))
}
@@ -267,10 +289,10 @@ struct GroupMemberInfoView: View {
GeometryReader { g in
let buttonWidth = g.size.width / 4
HStack(alignment: .center, spacing: 8) {
if let contactId = member.memberContactId, let (chat, contact) = knownDirectChat(contactId) {
if let chat = knownContactChat, let contact = knownContact {
knownDirectChatButton(chat, width: buttonWidth)
AudioCallButton(chat: chat, contact: contact, width: buttonWidth) { alert = .someAlert(alert: $0) }
VideoButton(chat: chat, contact: contact, width: buttonWidth) { alert = .someAlert(alert: $0) }
AudioCallButton(chat: chat, contact: contact, connectionStats: $knownContactConnectionStats, width: buttonWidth) { alert = .someAlert(alert: $0) }
VideoButton(chat: chat, contact: contact, connectionStats: $knownContactConnectionStats, width: buttonWidth) { alert = .someAlert(alert: $0) }
} else if groupInfo.fullGroupPreferences.directMessages.on(for: groupInfo.membership) {
if let contactId = member.memberContactId {
newDirectChatButton(contactId, width: buttonWidth)
@@ -305,10 +327,15 @@ struct GroupMemberInfoView: View {
}
func showDirectMessagesProhibitedAlert(_ title: LocalizedStringKey) {
let messageLabel: LocalizedStringKey = (
groupInfo.businessChat == nil
? "Direct messages between members are prohibited."
: "Direct messages between members are prohibited in this chat."
)
alert = .someAlert(alert: SomeAlert(
alert: mkAlert(
title: title,
message: "Direct messages between members are prohibited in this group."
message: messageLabel
),
id: "can't message member, direct messages prohibited"
))
@@ -354,25 +381,49 @@ struct GroupMemberInfoView: View {
func createMemberContactButton(width: CGFloat) -> some View {
InfoViewButton(image: "message.fill", title: "message", width: width) {
progressIndicator = true
Task {
do {
let memberContact = try await apiCreateMemberContact(groupInfo.apiId, groupMember.groupMemberId)
await MainActor.run {
progressIndicator = false
chatModel.addChat(Chat(chatInfo: .direct(contact: memberContact)))
ItemsModel.shared.loadOpenChat(memberContact.id) {
dismissAllSheets(animated: true)
if let connStats = connectionStats {
if connStats.ratchetSyncState == .ok {
progressIndicator = true
Task {
do {
let memberContact = try await apiCreateMemberContact(groupInfo.apiId, groupMember.groupMemberId)
await MainActor.run {
progressIndicator = false
chatModel.addChat(Chat(chatInfo: .direct(contact: memberContact)))
ItemsModel.shared.loadOpenChat(memberContact.id) {
dismissAllSheets(animated: true)
}
NetworkModel.shared.setContactNetworkStatus(memberContact, .connected)
}
} catch let error {
logger.error("createMemberContactButton apiCreateMemberContact error: \(responseError(error))")
let a = getErrorAlert(error, "Error creating member contact")
await MainActor.run {
progressIndicator = false
alert = .error(title: a.title, error: a.message)
}
}
NetworkModel.shared.setContactNetworkStatus(memberContact, .connected)
}
} catch let error {
logger.error("createMemberContactButton apiCreateMemberContact error: \(responseError(error))")
let a = getErrorAlert(error, "Error creating member contact")
await MainActor.run {
progressIndicator = false
alert = .error(title: a.title, error: a.message)
}
} else if connStats.ratchetSyncAllowed {
alert = .someAlert(alert: SomeAlert(
alert: Alert(
title: Text("Fix connection?"),
message: Text("Connection requires encryption renegotiation."),
primaryButton: .default(Text("Fix")) {
syncMemberConnection(force: false)
},
secondaryButton: .cancel()
),
id: "can't message member, fix connection"
))
} else {
alert = .someAlert(alert: SomeAlert(
alert: mkAlert(
title: "Can't message member",
message: "Encryption renegotiation in progress."
),
id: "can't message contact, encryption renegotiation in progress"
))
}
}
}
@@ -388,7 +439,7 @@ struct GroupMemberInfoView: View {
Text(Image(systemName: "checkmark.shield"))
.foregroundColor(theme.colors.secondary)
.font(.title2)
+ Text(" ")
+ textSpace
+ Text(mem.displayName)
.font(.largeTitle)
)
@@ -537,9 +588,14 @@ struct GroupMemberInfoView: View {
}
private func removeMemberAlert(_ mem: GroupMember) -> Alert {
Alert(
let label: LocalizedStringKey = (
groupInfo.businessChat == nil
? "Member will be removed from group - this cannot be undone!"
: "Member will be removed from chat - this cannot be undone!"
)
return Alert(
title: Text("Remove member?"),
message: Text("Member will be removed from group - this cannot be undone!"),
message: Text(label),
primaryButton: .destructive(Text("Remove")) {
Task {
do {
@@ -562,7 +618,15 @@ struct GroupMemberInfoView: View {
private func changeMemberRoleAlert(_ mem: GroupMember) -> Alert {
Alert(
title: Text("Change member role?"),
message: mem.memberCurrent ? Text("Member role will be changed to \"\(newRole.text)\". All group members will be notified.") : Text("Member role will be changed to \"\(newRole.text)\". The member will receive a new invitation."),
message: (
mem.memberCurrent
? (
groupInfo.businessChat == nil
? Text("Member role will be changed to \"\(newRole.text)\". All group members will be notified.")
: Text("Member role will be changed to \"\(newRole.text)\". All chat members will be notified.")
)
: Text("Member role will be changed to \"\(newRole.text)\". The member will receive a new invitation.")
),
primaryButton: .default(Text("Change")) {
Task {
do {
@@ -570,7 +634,7 @@ struct GroupMemberInfoView: View {
await MainActor.run {
_ = chatModel.upsertGroupMember(groupInfo, updatedMember)
}
} catch let error {
newRole = mem.memberRole
logger.error("apiMemberRole error: \(responseError(error))")
@@ -739,6 +803,7 @@ struct GroupMemberInfoView_Previews: PreviewProvider {
static var previews: some View {
GroupMemberInfoView(
groupInfo: GroupInfo.sampleData,
chat: Chat.sampleData,
groupMember: GMember.sampleData
)
}
@@ -20,9 +20,10 @@ struct GroupPreferencesView: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@Binding var groupInfo: GroupInfo
@State var preferences: FullGroupPreferences
@State var currentPreferences: FullGroupPreferences
@Binding var preferences: FullGroupPreferences
var currentPreferences: FullGroupPreferences
let creatingGroup: Bool
let savePreferences: () -> Void
@State private var showSaveDialogue = false
var body: some View {
@@ -38,7 +39,7 @@ struct GroupPreferencesView: View {
featureSection(.simplexLinks, $preferences.simplexLinks.enable, $preferences.simplexLinks.role)
featureSection(.history, $preferences.history.enable)
if groupInfo.canEdit {
if groupInfo.isOwner {
Section {
Button("Reset") { preferences = currentPreferences }
Button(saveText) { savePreferences() }
@@ -68,7 +69,10 @@ struct GroupPreferencesView: View {
savePreferences()
dismiss()
}
Button("Exit without saving") { dismiss() }
Button("Exit without saving") {
preferences = currentPreferences
dismiss()
}
}
}
@@ -77,7 +81,7 @@ struct GroupPreferencesView: View {
let color: Color = enableFeature.wrappedValue == .on ? .green : theme.colors.secondary
let icon = enableFeature.wrappedValue == .on ? feature.iconFilled : feature.icon
let timedOn = feature == .timedMessages && enableFeature.wrappedValue == .on
if groupInfo.canEdit {
if groupInfo.isOwner {
let enable = Binding(
get: { enableFeature.wrappedValue == .on },
set: { on, _ in enableFeature.wrappedValue = on ? .on : .off }
@@ -123,7 +127,7 @@ struct GroupPreferencesView: View {
}
}
} footer: {
Text(feature.enableDescription(enableFeature.wrappedValue, groupInfo.canEdit))
Text(feature.enableDescription(enableFeature.wrappedValue, groupInfo.isOwner))
.foregroundColor(theme.colors.secondary)
}
.onChange(of: enableFeature.wrappedValue) { enabled in
@@ -132,32 +136,16 @@ struct GroupPreferencesView: View {
}
}
}
private func savePreferences() {
Task {
do {
var gp = groupInfo.groupProfile
gp.groupPreferences = toGroupPreferences(preferences)
let gInfo = try await apiUpdateGroup(groupInfo.groupId, gp)
await MainActor.run {
groupInfo = gInfo
chatModel.updateGroup(gInfo)
currentPreferences = preferences
}
} catch {
logger.error("GroupPreferencesView apiUpdateGroup error: \(responseError(error))")
}
}
}
}
struct GroupPreferencesView_Previews: PreviewProvider {
static var previews: some View {
GroupPreferencesView(
groupInfo: Binding.constant(GroupInfo.sampleData),
preferences: FullGroupPreferences.sampleData,
preferences: Binding.constant(FullGroupPreferences.sampleData),
currentPreferences: FullGroupPreferences.sampleData,
creatingGroup: false
creatingGroup: false,
savePreferences: {}
)
}
}
@@ -23,7 +23,7 @@ struct GroupWelcomeView: View {
var body: some View {
VStack {
if groupInfo.canEdit {
if groupInfo.isOwner && groupInfo.businessChat == nil {
editorView()
.modifier(BackButton(disabled: Binding.constant(false)) {
if welcomeTextUnchanged() {
@@ -42,7 +42,8 @@ struct ChatHelp: View {
Text("above, then choose:")
}
Text("**Add contact**: to create a new invitation link, or connect via a link you received.")
Text("**Create 1-time link**: to create and share a new invitation link.")
Text("**Scan / Paste link**: to connect via a link you received.")
Text("**Create group**: to create a new group.")
}
.padding(.top, 24)
@@ -404,8 +404,9 @@ struct ChatListNavLink: View {
}
private func deleteGroupAlert(_ groupInfo: GroupInfo) -> Alert {
Alert(
title: Text("Delete group?"),
let label: LocalizedStringKey = groupInfo.businessChat == nil ? "Delete group?" : "Delete chat?"
return Alert(
title: Text(label),
message: deleteGroupAlertMessage(groupInfo),
primaryButton: .destructive(Text("Delete")) {
Task { await deleteChat(chat) }
@@ -414,10 +415,6 @@ struct ChatListNavLink: View {
)
}
private func deleteGroupAlertMessage(_ groupInfo: GroupInfo) -> Text {
groupInfo.membership.memberCurrent ? Text("Group will be deleted for all members - this cannot be undone!") : Text("Group will be deleted for you - this cannot be undone!")
}
private func clearChatAlert() -> Alert {
Alert(
title: Text("Clear conversation?"),
@@ -441,9 +438,15 @@ struct ChatListNavLink: View {
}
private func leaveGroupAlert(_ groupInfo: GroupInfo) -> Alert {
Alert(
title: Text("Leave group?"),
message: Text("You will stop receiving messages from this group. Chat history will be preserved."),
let titleLabel: LocalizedStringKey = groupInfo.businessChat == nil ? "Leave group?" : "Leave chat?"
let messageLabel: LocalizedStringKey = (
groupInfo.businessChat == nil
? "You will stop receiving messages from this group. Chat history will be preserved."
: "You will stop receiving messages from this chat. Chat history will be preserved."
)
return Alert(
title: Text(titleLabel),
message: Text(messageLabel),
primaryButton: .destructive(Text("Leave")) {
Task { await leaveGroup(groupInfo.groupId) }
},
@@ -172,7 +172,7 @@ struct ChatPreviewView: View {
}
private var verifiedIcon: Text {
(Text(Image(systemName: "checkmark.shield")) + Text(" "))
(Text(Image(systemName: "checkmark.shield")) + textSpace)
.foregroundColor(theme.colors.secondary)
.baselineOffset(1)
.kerning(-2)
@@ -232,12 +232,12 @@ struct ChatPreviewView: View {
+ messageText(msg, parseSimpleXMarkdown(msg), nil, preview: true, showSecrets: false, secondaryColor: theme.colors.secondary)
func image(_ s: String, color: Color = Color(uiColor: .tertiaryLabel)) -> Text {
Text(Image(systemName: s)).foregroundColor(color) + Text(" ")
Text(Image(systemName: s)).foregroundColor(color) + textSpace
}
func attachment() -> Text {
switch draft.preview {
case let .filePreview(fileName, _): return image("doc.fill") + Text(fileName) + Text(" ")
case let .filePreview(fileName, _): return image("doc.fill") + Text(fileName) + textSpace
case .mediaPreviews: return image("photo")
case let .voicePreview(_, duration): return image("play.fill") + Text(durationText(duration))
default: return Text("")
@@ -367,11 +367,11 @@ struct ChatPreviewView: View {
case .sndErrorAuth, .sndError:
return Text(Image(systemName: "multiply"))
.font(.caption)
.foregroundColor(.red) + Text(" ")
.foregroundColor(.red) + textSpace
case .sndWarning:
return Text(Image(systemName: "exclamationmark.triangle.fill"))
.font(.caption)
.foregroundColor(.orange) + Text(" ")
.foregroundColor(.orange) + textSpace
default: return Text("")
}
}
@@ -587,7 +587,7 @@ struct SMPStatsView: View {
} header: {
Text("Statistics")
} footer: {
Text("Starting from \(localTimestamp(statsStartedAt)).") + Text("\n") + Text("All data is private to your device.")
Text("Starting from \(localTimestamp(statsStartedAt)).") + Text("\n") + Text("All data is kept private on your device.")
}
}
}
@@ -703,7 +703,7 @@ struct XFTPStatsView: View {
} header: {
Text("Statistics")
} footer: {
Text("Starting from \(localTimestamp(statsStartedAt)).") + Text("\n") + Text("All data is private to your device.")
Text("Starting from \(localTimestamp(statsStartedAt)).") + Text("\n") + Text("All data is kept private on your device.")
}
}
}
@@ -151,7 +151,7 @@ struct ContactListNavLink: View {
}
private var verifiedIcon: Text {
(Text(Image(systemName: "checkmark.shield")) + Text(" "))
(Text(Image(systemName: "checkmark.shield")) + textSpace)
.foregroundColor(.secondary)
.baselineOffset(1)
.kerning(-2)
@@ -1,68 +0,0 @@
//
// ChatArchiveView.swift
// SimpleXChat
//
// Created by Evgeny on 23/06/2022.
// Copyright © 2022 SimpleX Chat. All rights reserved.
//
import SwiftUI
import SimpleXChat
struct ChatArchiveView: View {
@EnvironmentObject var theme: AppTheme
var archiveName: String
@AppStorage(DEFAULT_CHAT_ARCHIVE_NAME) private var chatArchiveName: String?
@AppStorage(DEFAULT_CHAT_ARCHIVE_TIME) private var chatArchiveTime: Double = 0
@State private var showDeleteAlert = false
var body: some View {
let fileUrl = getDocumentsDirectory().appendingPathComponent(archiveName)
let fileTs = chatArchiveTimeDefault.get()
List {
Section {
settingsRow("square.and.arrow.up", color: theme.colors.secondary) {
Button {
showShareSheet(items: [fileUrl])
} label: {
Text("Save archive")
}
}
settingsRow("trash", color: theme.colors.secondary) {
Button {
showDeleteAlert = true
} label: {
Text("Delete archive").foregroundColor(.red)
}
}
} header: {
Text("Chat archive")
.foregroundColor(theme.colors.secondary)
} footer: {
Text("Created on \(fileTs)")
.foregroundColor(theme.colors.secondary)
}
}
.alert(isPresented: $showDeleteAlert) {
Alert(
title: Text("Delete chat archive?"),
primaryButton: .destructive(Text("Delete")) {
do {
try FileManager.default.removeItem(atPath: fileUrl.path)
chatArchiveName = nil
chatArchiveTime = 0
} catch let error {
logger.error("removeItem error \(String(describing: error))")
}
},
secondaryButton: .cancel()
)
}
}
}
struct ChatArchiveView_Previews: PreviewProvider {
static var previews: some View {
ChatArchiveView(archiveName: "")
}
}
@@ -48,6 +48,8 @@ struct DatabaseEncryptionView: View {
@State private var confirmNewKey = ""
@State private var currentKeyShown = false
let stopChatRunBlockStartChat: (Binding<Bool>, @escaping () async throws -> Bool) -> Void
var body: some View {
ZStack {
List {
@@ -134,46 +136,61 @@ struct DatabaseEncryptionView: View {
.onAppear {
if initialRandomDBPassphrase { currentKey = kcDatabasePassword.get() ?? "" }
}
.disabled(m.chatRunning != false)
.disabled(progressIndicator)
.alert(item: $alert) { item in databaseEncryptionAlert(item) }
}
private func encryptDatabase() {
progressIndicator = true
Task {
do {
encryptionStartedDefault.set(true)
encryptionStartedAtDefault.set(Date.now)
if !m.chatDbChanged {
try apiSaveAppSettings(settings: AppSettings.current.prepareForExport())
}
try await apiStorageEncryption(currentKey: currentKey, newKey: newKey)
encryptionStartedDefault.set(false)
initialRandomDBPassphraseGroupDefault.set(false)
if migration {
storeDBPassphraseGroupDefault.set(useKeychain)
}
if useKeychain {
if kcDatabasePassword.set(newKey) {
await resetFormAfterEncryption(true)
await operationEnded(.databaseEncrypted)
} else {
await resetFormAfterEncryption()
await operationEnded(.error(title: "Keychain error", error: "Error saving passphrase to keychain"))
}
} else {
if migration {
removePassphraseFromKeyChain()
}
await resetFormAfterEncryption()
private func encryptDatabaseAsync() async -> Bool {
await MainActor.run {
progressIndicator = true
}
do {
encryptionStartedDefault.set(true)
encryptionStartedAtDefault.set(Date.now)
if !m.chatDbChanged {
try apiSaveAppSettings(settings: AppSettings.current.prepareForExport())
}
try await apiStorageEncryption(currentKey: currentKey, newKey: newKey)
encryptionStartedDefault.set(false)
initialRandomDBPassphraseGroupDefault.set(false)
if migration {
storeDBPassphraseGroupDefault.set(useKeychain)
}
if useKeychain {
if kcDatabasePassword.set(newKey) {
await resetFormAfterEncryption(true)
await operationEnded(.databaseEncrypted)
}
} catch let error {
if case .chatCmdError(_, .errorDatabase(.errorExport(.errorNotADatabase))) = error as? ChatResponse {
await operationEnded(.currentPassphraseError)
} else {
await operationEnded(.error(title: "Error encrypting database", error: "\(responseError(error))"))
await resetFormAfterEncryption()
await operationEnded(.error(title: "Keychain error", error: "Error saving passphrase to keychain"))
}
} else {
if migration {
removePassphraseFromKeyChain()
}
await resetFormAfterEncryption()
await operationEnded(.databaseEncrypted)
}
return true
} catch let error {
if case .chatCmdError(_, .errorDatabase(.errorExport(.errorNotADatabase))) = error as? ChatResponse {
await operationEnded(.currentPassphraseError)
} else {
await operationEnded(.error(title: "Error encrypting database", error: "\(responseError(error))"))
}
return false
}
}
private func encryptDatabase() {
// it will try to stop and start the chat in case of: non-migration && successful encryption. In migration the chat will remain stopped
if migration {
Task {
await encryptDatabaseAsync()
}
} else {
stopChatRunBlockStartChat($progressIndicator) {
return await encryptDatabaseAsync()
}
}
}
@@ -371,6 +388,6 @@ func validKey(_ s: String) -> Bool {
struct DatabaseEncryptionView_Previews: PreviewProvider {
static var previews: some View {
DatabaseEncryptionView(useKeychain: Binding.constant(true), migration: false)
DatabaseEncryptionView(useKeychain: Binding.constant(true), migration: false, stopChatRunBlockStartChat: { _, _ in true })
}
}
@@ -11,6 +11,7 @@ import SimpleXChat
struct DatabaseErrorView: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@State var status: DBMigrationResult
@State private var dbKey = ""
@State private var storedDBKey = kcDatabasePassword.get()
@@ -28,23 +29,39 @@ struct DatabaseErrorView: View {
}
@ViewBuilder private func databaseErrorView() -> some View {
VStack(alignment: .leading, spacing: 16) {
VStack(alignment: .center, spacing: 20) {
switch status {
case let .errorNotADatabase(dbFile):
if useKeychain && storedDBKey != nil && storedDBKey != "" {
titleText("Wrong database passphrase")
Text("Database passphrase is different from saved in the keychain.")
.font(.callout)
.foregroundColor(theme.colors.secondary)
.multilineTextAlignment(.center)
.padding(.horizontal, 25)
databaseKeyField(onSubmit: saveAndRunChat)
saveAndOpenButton()
fileNameText(dbFile)
Spacer()
VStack(spacing: 10) {
saveAndOpenButton()
fileNameText(dbFile)
}
} else {
titleText("Encrypted database")
Text("Database passphrase is required to open chat.")
.font(.callout)
.foregroundColor(theme.colors.secondary)
.multilineTextAlignment(.center)
.padding(.horizontal, 25)
.padding(.bottom, 5)
if useKeychain {
databaseKeyField(onSubmit: saveAndRunChat)
Spacer()
saveAndOpenButton()
} else {
databaseKeyField(onSubmit: { runChat() })
Spacer()
openChatButton()
}
}
@@ -52,73 +69,105 @@ struct DatabaseErrorView: View {
switch migrationError {
case let .upgrade(upMigrations):
titleText("Database upgrade")
Button("Upgrade and open chat") { runChat(confirmMigrations: .yesUp) }
fileNameText(dbFile)
migrationsText(upMigrations.map(\.upName))
Spacer()
VStack(spacing: 10) {
Button("Upgrade and open chat") {
runChat(confirmMigrations: .yesUp)
}.buttonStyle(OnboardingButtonStyle(isDisabled: false))
fileNameText(dbFile)
}
case let .downgrade(downMigrations):
titleText("Database downgrade")
Text("Warning: you may lose some data!").bold()
Button("Downgrade and open chat") { runChat(confirmMigrations: .yesUpDown) }
fileNameText(dbFile)
Text("Warning: you may lose some data!")
.bold()
.padding(.horizontal, 25)
.multilineTextAlignment(.center)
migrationsText(downMigrations)
Spacer()
VStack(spacing: 10) {
Button("Downgrade and open chat") {
runChat(confirmMigrations: .yesUpDown)
}.buttonStyle(OnboardingButtonStyle(isDisabled: false))
fileNameText(dbFile)
}
case let .migrationError(mtrError):
titleText("Incompatible database version")
fileNameText(dbFile)
Text("Error: ") + Text(mtrErrorDescription(mtrError))
fileNameText(dbFile, font: .callout)
errorView(Text(mtrErrorDescription(mtrError)))
}
case let .errorSQL(dbFile, migrationSQLError):
titleText("Database error")
fileNameText(dbFile)
Text("Error: \(migrationSQLError)")
fileNameText(dbFile, font: .callout)
errorView(Text("Error: \(migrationSQLError)"))
case .errorKeychain:
titleText("Keychain error")
Text("Cannot access keychain to save database password")
errorView(Text("Cannot access keychain to save database password"))
case .invalidConfirmation:
// this can only happen if incorrect parameter is passed
Text(String("Invalid migration confirmation")).font(.title)
titleText("Invalid migration confirmation")
errorView()
case let .unknown(json):
titleText("Database error")
Text("Unknown database error: \(json)")
errorView(Text("Unknown database error: \(json)"))
case .ok:
EmptyView()
}
if showRestoreDbButton {
Spacer().frame(height: 10)
Spacer()
Text("The attempt to change database passphrase was not completed.")
.multilineTextAlignment(.center)
.padding(.horizontal, 25)
.font(.footnote)
restoreDbButton()
}
}
.padding()
.padding(.horizontal, 25)
.padding(.top, 75)
.padding(.bottom, 25)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.onAppear() { showRestoreDbButton = shouldShowRestoreDbButton() }
}
private func titleText(_ s: LocalizedStringKey) -> Text {
Text(s).font(.title)
private func titleText(_ s: LocalizedStringKey) -> some View {
Text(s).font(.largeTitle).bold().multilineTextAlignment(.center)
}
private func fileNameText(_ f: String) -> Text {
Text("File: \((f as NSString).lastPathComponent)")
private func fileNameText(_ f: String, font: Font = .caption) -> Text {
Text("File: \((f as NSString).lastPathComponent)").font(font)
}
private func migrationsText(_ ms: [String]) -> Text {
Text("Migrations: \(ms.joined(separator: ", "))")
private func migrationsText(_ ms: [String]) -> some View {
(Text("Migrations:").font(.subheadline) + Text(verbatim: "\n") + Text(ms.joined(separator: "\n")).font(.caption))
.multilineTextAlignment(.center)
.padding(.horizontal, 25)
}
private func databaseKeyField(onSubmit: @escaping () -> Void) -> some View {
PassphraseField(key: $dbKey, placeholder: "Enter passphrase…", valid: validKey(dbKey), onSubmit: onSubmit)
.padding(.vertical, 10)
.padding(.horizontal)
.background(
RoundedRectangle(cornerRadius: 10, style: .continuous)
.fill(Color(uiColor: .tertiarySystemFill))
)
}
private func saveAndOpenButton() -> some View {
Button("Save passphrase and open chat") {
saveAndRunChat()
}
.buttonStyle(OnboardingButtonStyle(isDisabled: false))
}
private func openChatButton() -> some View {
Button("Open chat") {
runChat()
}
.buttonStyle(OnboardingButtonStyle(isDisabled: false))
}
private func saveAndRunChat() {
@@ -192,8 +241,9 @@ struct DatabaseErrorView: View {
secondaryButton: .cancel()
))
} label: {
Text("Restore database backup").foregroundColor(.red)
Text("Restore database backup")
}
.buttonStyle(OnboardingButtonStyle(isDisabled: false))
}
private func restoreDb() {
@@ -208,6 +258,23 @@ struct DatabaseErrorView: View {
))
}
}
private func errorView(_ s: Text? = nil) -> some View {
VStack(spacing: 35) {
Image(systemName: "exclamationmark.triangle.fill")
.resizable()
.frame(width: 50, height: 50)
.foregroundColor(.red)
if let text = s {
text
.multilineTextAlignment(.center)
.font(.footnote)
}
}
.padding()
.frame(maxWidth: .infinity)
}
}
struct DatabaseErrorView_Previews: PreviewProvider {
+230 -126
View File
@@ -46,6 +46,7 @@ struct DatabaseView: View {
@EnvironmentObject var theme: AppTheme
let dismissSettingsSheet: DismissAction
@State private var runChat = false
@State private var stoppingChat = false
@State private var alert: DatabaseAlert? = nil
@State private var showFileImporter = false
@State private var importedArchivePath: URL?
@@ -57,6 +58,8 @@ struct DatabaseView: View {
@State private var useKeychain = storeDBPassphraseGroupDefault.get()
@State private var appFilesCountAndSize: (Int, Int)?
@State private var showDatabaseEncryptionView = false
@State var chatItemTTL: ChatItemTTL
@State private var currentChatItemTTL: ChatItemTTL = .none
@@ -69,7 +72,20 @@ struct DatabaseView: View {
}
}
@ViewBuilder
private func chatDatabaseView() -> some View {
NavigationLink(isActive: $showDatabaseEncryptionView) {
DatabaseEncryptionView(useKeychain: $useKeychain, migration: false, stopChatRunBlockStartChat: { progressIndicator, block in
stopChatRunBlockStartChat(false, progressIndicator, block)
})
.navigationTitle("Database passphrase")
.modifier(ThemedBackground(grouped: true))
} label: {
EmptyView()
}
.frame(width: 1, height: 1)
.hidden()
List {
let stopped = m.chatRunning == false
Section {
@@ -101,9 +117,10 @@ struct DatabaseView: View {
isOn: $runChat
)
.onChange(of: runChat) { _ in
if (runChat) {
startChat()
} else {
if runChat {
DatabaseView.startChat($runChat, $progressIndicator)
} else if !stoppingChat {
stoppingChat = false
alert = .stopChat
}
}
@@ -123,7 +140,9 @@ struct DatabaseView: View {
let color: Color = unencrypted ? .orange : theme.colors.secondary
settingsRow(unencrypted ? "lock.open" : useKeychain ? "key" : "lock", color: color) {
NavigationLink {
DatabaseEncryptionView(useKeychain: $useKeychain, migration: false)
DatabaseEncryptionView(useKeychain: $useKeychain, migration: false, stopChatRunBlockStartChat: { progressIndicator, block in
stopChatRunBlockStartChat(false, progressIndicator, block)
})
.navigationTitle("Database passphrase")
.modifier(ThemedBackground(grouped: true))
} label: {
@@ -133,9 +152,14 @@ struct DatabaseView: View {
settingsRow("square.and.arrow.up", color: theme.colors.secondary) {
Button("Export database") {
if initialRandomDBPassphraseGroupDefault.get() && !unencrypted {
alert = .exportProhibited
showDatabaseEncryptionView = true
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
alert = .exportProhibited
}
} else {
exportArchive()
stopChatRunBlockStartChat(stopped, $progressIndicator) {
await exportArchive()
}
}
}
}
@@ -144,20 +168,6 @@ struct DatabaseView: View {
showFileImporter = true
}
}
if let archiveName = chatArchiveName {
let title: LocalizedStringKey = chatArchiveTimeDefault.get() < chatLastStartGroupDefault.get()
? "Old database archive"
: "New database archive"
settingsRow("archivebox", color: theme.colors.secondary) {
NavigationLink {
ChatArchiveView(archiveName: archiveName)
.navigationTitle(title)
.modifier(ThemedBackground(grouped: true))
} label: {
Text(title)
}
}
}
settingsRow("trash.slash", color: theme.colors.secondary) {
Button("Delete database", role: .destructive) {
alert = .deleteChat
@@ -167,14 +177,10 @@ struct DatabaseView: View {
Text("Chat database")
.foregroundColor(theme.colors.secondary)
} footer: {
Text(
stopped
? "You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts."
: "Stop chat to enable database actions"
)
Text("You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts.")
.foregroundColor(theme.colors.secondary)
}
.disabled(!stopped)
.disabled(progressIndicator)
if case .group = dbContainer, legacyDatabase {
Section(header: Text("Old database").foregroundColor(theme.colors.secondary)) {
@@ -190,7 +196,7 @@ struct DatabaseView: View {
Button(m.users.count > 1 ? "Delete files for all chat profiles" : "Delete all files", role: .destructive) {
alert = .deleteFilesAndMedia
}
.disabled(!stopped || appFilesCountAndSize?.0 == 0)
.disabled(progressIndicator || appFilesCountAndSize?.0 == 0)
} header: {
Text("Files & media")
.foregroundColor(theme.colors.secondary)
@@ -255,7 +261,10 @@ struct DatabaseView: View {
title: Text("Import chat database?"),
message: Text("Your current chat database will be DELETED and REPLACED with the imported one.") + Text("This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost."),
primaryButton: .destructive(Text("Import")) {
importArchive(fileURL)
stopChatRunBlockStartChat(m.chatRunning == false, $progressIndicator) {
_ = await DatabaseView.importArchive(fileURL, $progressIndicator, $alert)
return true
}
},
secondaryButton: .cancel()
)
@@ -263,19 +272,15 @@ struct DatabaseView: View {
return Alert(title: Text("Error: no database file"))
}
case .archiveImported:
return Alert(
title: Text("Chat database imported"),
message: Text("Restart the app to use imported chat database")
)
let (title, message) = archiveImportedAlertText()
return Alert(title: Text(title), message: Text(message))
case let .archiveImportedWithErrors(errs):
return Alert(
title: Text("Chat database imported"),
message: Text("Restart the app to use imported chat database") + Text(verbatim: "\n") + Text("Some non-fatal errors occurred during import:") + archiveErrorsText(errs)
)
let (title, message) = archiveImportedWithErrorsAlertText(errs: errs)
return Alert(title: Text(title), message: Text(message))
case let .archiveExportedWithErrors(archivePath, errs):
return Alert(
title: Text("Chat database exported"),
message: Text("You may save the exported archive.") + Text(verbatim: "\n") + Text("Some file(s) were not exported:") + archiveErrorsText(errs),
message: Text("You may save the exported archive.") + Text(verbatim: "\n") + Text("Some file(s) were not exported:") + Text(archiveErrorsText(errs)),
dismissButton: .default(Text("Continue")) {
showShareSheet(items: [archivePath])
}
@@ -285,15 +290,17 @@ struct DatabaseView: View {
title: Text("Delete chat profile?"),
message: Text("This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost."),
primaryButton: .destructive(Text("Delete")) {
deleteChat()
let wasStopped = m.chatRunning == false
stopChatRunBlockStartChat(wasStopped, $progressIndicator) {
_ = await deleteChat()
return true
}
},
secondaryButton: .cancel()
)
case .chatDeleted:
return Alert(
title: Text("Chat database deleted"),
message: Text("Restart the app to create a new chat profile")
)
let (title, message) = chatDeletedAlertText()
return Alert(title: Text(title), message: Text(message))
case .deleteLegacyDatabase:
return Alert(
title: Text("Delete old database?"),
@@ -308,7 +315,10 @@ struct DatabaseView: View {
title: Text("Delete files and media?"),
message: Text("This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain."),
primaryButton: .destructive(Text("Delete")) {
deleteFiles()
stopChatRunBlockStartChat(m.chatRunning == false, $progressIndicator) {
deleteFiles()
return true
}
},
secondaryButton: .cancel()
)
@@ -328,95 +338,180 @@ struct DatabaseView: View {
}
}
private func authStopChat() {
private func authStopChat(_ onStop: (() -> Void)? = nil) {
if UserDefaults.standard.bool(forKey: DEFAULT_PERFORM_LA) {
authenticate(reason: NSLocalizedString("Stop SimpleX", comment: "authentication reason")) { laResult in
switch laResult {
case .success: stopChat()
case .unavailable: stopChat()
case .success: stopChat(onStop)
case .unavailable: stopChat(onStop)
case .failed: withAnimation { runChat = true }
}
}
} else {
stopChat()
stopChat(onStop)
}
}
private func stopChat() {
private func stopChat(_ onStop: (() -> Void)? = nil) {
Task {
do {
try await stopChatAsync()
onStop?()
} catch let error {
await MainActor.run {
runChat = true
alert = .error(title: "Error stopping chat", error: responseError(error))
showAlert("Error stopping chat", message: responseError(error))
}
}
}
}
private func exportArchive() {
progressIndicator = true
Task {
do {
let (archivePath, archiveErrors) = try await exportChatArchive()
if archiveErrors.isEmpty {
showShareSheet(items: [archivePath])
await MainActor.run { progressIndicator = false }
} else {
await MainActor.run {
alert = .archiveExportedWithErrors(archivePath: archivePath, archiveErrors: archiveErrors)
progressIndicator = false
func stopChatRunBlockStartChat(
_ stopped: Bool,
_ progressIndicator: Binding<Bool>,
_ block: @escaping () async throws -> Bool
) {
// if the chat was running, the sequence is: stop chat, run block, start chat.
// Otherwise, just run block and do nothing - the toggle will be visible anyway and the user can start the chat or not
if stopped {
Task {
do {
_ = try await block()
} catch {
logger.error("Error while executing block: \(error)")
}
}
} else {
authStopChat {
stoppingChat = true
runChat = false
Task {
// if it throws, let's start chat again anyway
var canStart = false
do {
canStart = try await block()
} catch {
logger.error("Error executing block: \(error)")
canStart = true
}
if canStart {
await MainActor.run {
DatabaseView.startChat($runChat, $progressIndicator)
}
}
}
}
}
}
static func startChat(_ runChat: Binding<Bool>, _ progressIndicator: Binding<Bool>) {
progressIndicator.wrappedValue = true
let m = ChatModel.shared
if m.chatDbChanged {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
resetChatCtrl()
do {
let hadDatabase = hasDatabase()
try initializeChat(start: true)
m.chatDbChanged = false
AppChatState.shared.set(.active)
if m.chatDbStatus != .ok || !hadDatabase {
// Hide current view and show `DatabaseErrorView`
dismissAllSheets(animated: true)
}
} catch let error {
fatalError("Error starting chat \(responseError(error))")
}
progressIndicator.wrappedValue = false
}
} else {
do {
_ = try apiStartChat()
runChat.wrappedValue = true
m.chatRunning = true
ChatReceiver.shared.start()
chatLastStartGroupDefault.set(Date.now)
AppChatState.shared.set(.active)
} catch let error {
runChat.wrappedValue = false
showAlert(NSLocalizedString("Error starting chat", comment: ""), message: responseError(error))
}
progressIndicator.wrappedValue = false
}
}
private func exportArchive() async -> Bool {
await MainActor.run {
progressIndicator = true
}
do {
let (archivePath, archiveErrors) = try await exportChatArchive()
if archiveErrors.isEmpty {
showShareSheet(items: [archivePath])
await MainActor.run { progressIndicator = false }
} else {
await MainActor.run {
alert = .error(title: "Error exporting chat database", error: responseError(error))
alert = .archiveExportedWithErrors(archivePath: archivePath, archiveErrors: archiveErrors)
progressIndicator = false
}
}
} catch let error {
await MainActor.run {
alert = .error(title: "Error exporting chat database", error: responseError(error))
progressIndicator = false
}
}
return false
}
private func importArchive(_ archivePath: URL) {
static func importArchive(
_ archivePath: URL,
_ progressIndicator: Binding<Bool>,
_ alert: Binding<DatabaseAlert?>
) async -> Bool {
if archivePath.startAccessingSecurityScopedResource() {
progressIndicator = true
Task {
do {
try await apiDeleteStorage()
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
do {
let config = ArchiveConfig(archivePath: archivePath.path)
let archiveErrors = try await apiImportArchive(config: config)
_ = kcDatabasePassword.remove()
if archiveErrors.isEmpty {
await operationEnded(.archiveImported)
} else {
await operationEnded(.archiveImportedWithErrors(archiveErrors: archiveErrors))
}
} catch let error {
await operationEnded(.error(title: "Error importing chat database", error: responseError(error)))
}
} catch let error {
await operationEnded(.error(title: "Error deleting chat database", error: responseError(error)))
}
archivePath.stopAccessingSecurityScopedResource()
await MainActor.run {
progressIndicator.wrappedValue = true
}
do {
try await apiDeleteStorage()
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
do {
let config = ArchiveConfig(archivePath: archivePath.path)
let archiveErrors = try await apiImportArchive(config: config)
shouldImportAppSettingsDefault.set(true)
_ = kcDatabasePassword.remove()
if archiveErrors.isEmpty {
await operationEnded(.archiveImported, progressIndicator, alert)
} else {
await operationEnded(.archiveImportedWithErrors(archiveErrors: archiveErrors), progressIndicator, alert)
}
return true
} catch let error {
await operationEnded(.error(title: "Error importing chat database", error: responseError(error)), progressIndicator, alert)
}
} catch let error {
await operationEnded(.error(title: "Error deleting chat database", error: responseError(error)), progressIndicator, alert)
}
archivePath.stopAccessingSecurityScopedResource()
} else {
alert = .error(title: "Error accessing database file")
showAlert("Error accessing database file")
}
return false
}
private func deleteChat() {
progressIndicator = true
Task {
do {
try await deleteChatAsync()
await operationEnded(.chatDeleted)
appFilesCountAndSize = directoryFileCountAndSize(getAppFilesDirectory())
} catch let error {
await operationEnded(.error(title: "Error deleting database", error: responseError(error)))
}
private func deleteChat() async -> Bool {
await MainActor.run {
progressIndicator = true
}
do {
try await deleteChatAsync()
appFilesCountAndSize = directoryFileCountAndSize(getAppFilesDirectory())
await DatabaseView.operationEnded(.chatDeleted, $progressIndicator, $alert)
return true
} catch let error {
await DatabaseView.operationEnded(.error(title: "Error deleting database", error: responseError(error)), $progressIndicator, $alert)
return false
}
}
@@ -428,39 +523,28 @@ struct DatabaseView: View {
}
}
private func operationEnded(_ dbAlert: DatabaseAlert) async {
private static func operationEnded(_ dbAlert: DatabaseAlert, _ progressIndicator: Binding<Bool>, _ alert: Binding<DatabaseAlert?>) async {
await MainActor.run {
let m = ChatModel.shared
m.chatDbChanged = true
m.chatInitialized = false
progressIndicator = false
alert = dbAlert
progressIndicator.wrappedValue = false
}
}
private func startChat() {
if m.chatDbChanged {
dismissSettingsSheet()
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
resetChatCtrl()
do {
try initializeChat(start: true)
m.chatDbChanged = false
AppChatState.shared.set(.active)
} catch let error {
fatalError("Error starting chat \(responseError(error))")
}
}
} else {
do {
_ = try apiStartChat()
runChat = true
m.chatRunning = true
ChatReceiver.shared.start()
chatLastStartGroupDefault.set(Date.now)
AppChatState.shared.set(.active)
} catch let error {
runChat = false
alert = .error(title: "Error starting chat", error: responseError(error))
await withCheckedContinuation { cont in
let okAlertActionWaiting = UIAlertAction(title: NSLocalizedString("Ok", comment: "alert button"), style: .default, handler: { _ in cont.resume() })
// show these alerts globally so they are visible when all sheets will be hidden
if case .archiveImported = dbAlert {
let (title, message) = archiveImportedAlertText()
showAlert(title, message: message, actions: { [okAlertActionWaiting] })
} else if case .archiveImportedWithErrors(let errs) = dbAlert {
let (title, message) = archiveImportedWithErrorsAlertText(errs: errs)
showAlert(title, message: message, actions: { [okAlertActionWaiting] })
} else if case .chatDeleted = dbAlert {
let (title, message) = chatDeletedAlertText()
showAlert(title, message: message, actions: { [okAlertActionWaiting] })
} else {
alert.wrappedValue = dbAlert
cont.resume()
}
}
}
@@ -503,8 +587,28 @@ struct DatabaseView: View {
}
}
func archiveErrorsText(_ errs: [ArchiveError]) -> Text {
return Text("\n" + errs.map(showArchiveError).joined(separator: "\n"))
private func archiveImportedAlertText() -> (String, String) {
(
NSLocalizedString("Chat database imported", comment: ""),
NSLocalizedString("Restart the app to use imported chat database", comment: "")
)
}
private func archiveImportedWithErrorsAlertText(errs: [ArchiveError]) -> (String, String) {
(
NSLocalizedString("Chat database imported", comment: ""),
NSLocalizedString("Restart the app to use imported chat database", comment: "") + "\n" + NSLocalizedString("Some non-fatal errors occurred during import:", comment: "") + archiveErrorsText(errs)
)
}
private func chatDeletedAlertText() -> (String, String) {
(
NSLocalizedString("Chat database deleted", comment: ""),
NSLocalizedString("Restart the app to create a new chat profile", comment: "")
)
}
func archiveErrorsText(_ errs: [ArchiveError]) -> String {
return "\n" + errs.map(showArchiveError).joined(separator: "\n")
func showArchiveError(_ err: ArchiveError) -> String {
switch err {
@@ -117,7 +117,7 @@ struct MigrateToAppGroupView: View {
setV3DBMigration(.migration_error)
migrationError = "Error starting chat: \(responseError(error))"
}
deleteOldArchive()
deleteOldChatArchive()
} label: {
Text("Start chat")
.font(.title)
@@ -235,14 +235,16 @@ func exportChatArchive(_ storagePath: URL? = nil) async throws -> (URL, [Archive
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
let errs = try await apiExportArchive(config: config)
if storagePath == nil {
deleteOldArchive()
deleteOldChatArchive()
UserDefaults.standard.set(archiveName, forKey: DEFAULT_CHAT_ARCHIVE_NAME)
chatArchiveTimeDefault.set(archiveTime)
}
return (archivePath, errs)
}
func deleteOldArchive() {
/// Deprecated. Remove in the end of 2025. All unused archives should be deleted for the most users til then.
/// Remove DEFAULT_CHAT_ARCHIVE_NAME and DEFAULT_CHAT_ARCHIVE_TIME as well
func deleteOldChatArchive() {
let d = UserDefaults.standard
if let archiveName = d.string(forKey: DEFAULT_CHAT_ARCHIVE_NAME) {
do {
@@ -177,7 +177,7 @@ struct MigrateFromDevice: View {
case let .archiveExportedWithErrors(archivePath, errs):
return Alert(
title: Text("Chat database exported"),
message: Text("You may migrate the exported database.") + Text(verbatim: "\n") + Text("Some file(s) were not exported:") + archiveErrorsText(errs),
message: Text("You may migrate the exported database.") + Text(verbatim: "\n") + Text("Some file(s) were not exported:") + Text(archiveErrorsText(errs)),
dismissButton: .default(Text("Continue")) {
Task { await uploadArchive(path: archivePath) }
}
@@ -222,7 +222,8 @@ struct MigrateFromDevice: View {
}
private func passphraseNotSetView() -> some View {
DatabaseEncryptionView(useKeychain: $useKeychain, migration: true)
DatabaseEncryptionView(useKeychain: $useKeychain, migration: true, stopChatRunBlockStartChat: { _, _ in
})
.onChange(of: initialRandomDBPassphrase) { initial in
if !initial {
migrationState = .uploadConfirmation
@@ -103,6 +103,9 @@ struct MigrateToDevice: View {
@State private var showQRCodeScanner: Bool = true
@State private var pasteboardHasStrings = UIPasteboard.general.hasStrings
@State private var importingArchiveFromFileProgressIndicator = false
@State private var showFileImporter = false
var body: some View {
VStack {
switch migrationState {
@@ -200,6 +203,12 @@ struct MigrateToDevice: View {
Section(header: Text("Or paste archive link").foregroundColor(theme.colors.secondary)) {
pasteLinkView()
}
Section(header: Text("Or import archive file").foregroundColor(theme.colors.secondary)) {
archiveImportFromFileView()
}
}
if importingArchiveFromFileProgressIndicator {
progressView()
}
}
}
@@ -220,6 +229,34 @@ struct MigrateToDevice: View {
.frame(maxWidth: .infinity, alignment: .center)
}
private func archiveImportFromFileView() -> some View {
Button {
showFileImporter = true
} label: {
Label("Import database", systemImage: "square.and.arrow.down")
}
.disabled(importingArchiveFromFileProgressIndicator)
.fileImporter(
isPresented: $showFileImporter,
allowedContentTypes: [.zip],
allowsMultipleSelection: false
) { result in
if case let .success(files) = result, let fileURL = files.first {
Task {
let success = await DatabaseView.importArchive(fileURL, $importingArchiveFromFileProgressIndicator, Binding.constant(nil))
if success {
DatabaseView.startChat(
Binding.constant(false),
$importingArchiveFromFileProgressIndicator
)
hideView()
}
}
}
}
}
private func linkDownloadingView(_ link: String) -> some View {
ZStack {
List {
@@ -18,7 +18,6 @@ struct NewChatMenuButton: View {
// @EnvironmentObject var chatModel: ChatModel
@State private var showNewChatSheet = false
@State private var alert: SomeAlert? = nil
@State private var pendingConnection: PendingContactConnection? = nil
var body: some View {
Button {
@@ -30,12 +29,8 @@ struct NewChatMenuButton: View {
.frame(width: 24, height: 24)
}
.appSheet(isPresented: $showNewChatSheet) {
NewChatSheet(pendingConnection: $pendingConnection)
NewChatSheet()
.environment(\EnvironmentValues.refresh as! WritableKeyPath<EnvironmentValues, RefreshAction?>, nil)
.onDisappear {
alert = cleanupPendingConnection(contactConnection: pendingConnection)
pendingConnection = nil
}
}
.alert(item: $alert) { a in
return a.alert
@@ -55,7 +50,6 @@ struct NewChatSheet: View {
@State private var searchShowingSimplexLink = false
@State private var searchChatFilteredBySimplexLink: String? = nil
@State private var alert: SomeAlert?
@Binding var pendingConnection: PendingContactConnection?
// Sheet height management
@State private var isAddContactActive = false
@@ -110,17 +104,17 @@ struct NewChatSheet: View {
if (searchText.isEmpty) {
Section {
NavigationLink(isActive: $isAddContactActive) {
NewChatView(selection: .invite, parentAlert: $alert, contactConnection: $pendingConnection)
NewChatView(selection: .invite)
.navigationTitle("New chat")
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(.large)
} label: {
navigateOnTap(Label("Add contact", systemImage: "link.badge.plus")) {
navigateOnTap(Label("Create 1-time link", systemImage: "link.badge.plus")) {
isAddContactActive = true
}
}
NavigationLink(isActive: $isScanPasteLinkActive) {
NewChatView(selection: .connect, showQRCodeScanner: true, parentAlert: $alert, contactConnection: $pendingConnection)
NewChatView(selection: .connect, showQRCodeScanner: true)
.navigationTitle("New chat")
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(.large)
+41 -32
View File
@@ -45,32 +45,33 @@ enum NewChatOption: Identifiable {
var id: Self { self }
}
func cleanupPendingConnection(contactConnection: PendingContactConnection?) -> SomeAlert? {
var alert: SomeAlert? = nil
if !(ChatModel.shared.showingInvitation?.connChatUsed ?? true),
let conn = contactConnection {
alert = SomeAlert(
alert: Alert(
title: Text("Keep unused invitation?"),
message: Text("You can view invitation link again in connection details."),
primaryButton: .default(Text("Keep")) {},
secondaryButton: .destructive(Text("Delete")) {
Task {
await deleteChat(Chat(
chatInfo: .contactConnection(contactConnection: conn),
chatItems: []
))
func showKeepInvitationAlert() {
if let showingInvitation = ChatModel.shared.showingInvitation,
!showingInvitation.connChatUsed {
showAlert(
NSLocalizedString("Keep unused invitation?", comment: "alert title"),
message: NSLocalizedString("You can view invitation link again in connection details.", comment: "alert message"),
actions: {[
UIAlertAction(
title: NSLocalizedString("Keep", comment: "alert action"),
style: .default
),
UIAlertAction(
title: NSLocalizedString("Delete", comment: "alert action"),
style: .destructive,
handler: { _ in
Task {
await deleteChat(Chat(
chatInfo: .contactConnection(contactConnection: showingInvitation.pcc),
chatItems: []
))
}
}
}
),
id: "keepUnusedInvitation"
)
]}
)
}
ChatModel.shared.showingInvitation = nil
return alert
}
struct NewChatView: View {
@@ -84,13 +85,12 @@ struct NewChatView: View {
@State var choosingProfile = false
@State private var pastedLink: String = ""
@State private var alert: NewChatViewAlert?
@Binding var parentAlert: SomeAlert?
@Binding var contactConnection: PendingContactConnection?
@State private var contactConnection: PendingContactConnection? = nil
var body: some View {
VStack(alignment: .leading) {
Picker("New chat", selection: $selection) {
Label("Add contact", systemImage: "link")
Label("1-time link", systemImage: "link")
.tag(NewChatOption.invite)
Label("Connect via link", systemImage: "qrcode")
.tag(NewChatOption.connect)
@@ -157,7 +157,7 @@ struct NewChatView: View {
}
.onDisappear {
if !choosingProfile {
parentAlert = cleanupPendingConnection(contactConnection: contactConnection)
showKeepInvitationAlert()
contactConnection = nil
}
}
@@ -197,7 +197,7 @@ struct NewChatView: View {
if let (connReq, pcc) = r {
await MainActor.run {
m.updateContactConnection(pcc)
m.showingInvitation = ShowingInvitation(connId: pcc.id, connChatUsed: false)
m.showingInvitation = ShowingInvitation(pcc: pcc, connChatUsed: false)
connReqInvitation = connReq
contactConnection = pcc
}
@@ -916,11 +916,17 @@ func planAndConnectAlert(_ alert: PlanAndConnectAlert, dismiss: Bool, cleanup: (
)
case let .groupLinkConnecting(_, groupInfo):
if let groupInfo = groupInfo {
return Alert(
return groupInfo.businessChat == nil
? Alert(
title: Text("Group already exists!"),
message: Text("You are already joining the group \(groupInfo.displayName)."),
dismissButton: .default(Text("OK")) { cleanup?() }
)
: Alert(
title: Text("Chat already exists!"),
message: Text("You are already connecting to \(groupInfo.displayName)."),
dismissButton: .default(Text("OK")) { cleanup?() }
)
} else {
return Alert(
title: Text("Already joining the group!"),
@@ -1237,10 +1243,15 @@ func contactAlreadyConnectingAlert(_ contact: Contact) -> Alert {
}
func groupAlreadyExistsAlert(_ groupInfo: GroupInfo) -> Alert {
mkAlert(
groupInfo.businessChat == nil
? mkAlert(
title: "Group already exists",
message: "You are already in group \(groupInfo.displayName)."
)
: mkAlert(
title: "Chat already exists",
message: "You are already connected with \(groupInfo.displayName)."
)
}
enum ConnReqType: Equatable {
@@ -1278,9 +1289,7 @@ struct NewChatView_Previews: PreviewProvider {
@State var contactConnection: PendingContactConnection? = nil
NewChatView(
selection: .invite,
parentAlert: $parentAlert,
contactConnection: $contactConnection
selection: .invite
)
}
}
@@ -34,13 +34,7 @@ struct AddressCreationCard: View {
Text("Your SimpleX address")
.font(.title3)
Spacer()
HStack(alignment: .center) {
Text("How to use it")
VStack {
Image(systemName: "info.circle")
.foregroundColor(theme.colors.secondary)
}
}
Text("How to use it") + textSpace + Text(Image(systemName: "info.circle")).foregroundColor(theme.colors.secondary)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
@@ -68,7 +62,7 @@ struct AddressCreationCard: View {
.alert(isPresented: $showAddressCreationAlert) {
Alert(
title: Text("SimpleX address"),
message: Text("You can create it in user picker."),
message: Text("Tap Create SimpleX address in the menu to create it later."),
dismissButton: .default(Text("Ok")) {
withAnimation {
addressCreationCardShown = true
@@ -87,8 +81,8 @@ struct AddressCreationCard: View {
.sheet(isPresented: $showAddressInfoSheet) {
NavigationView {
UserAddressLearnMore(showCreateAddressButton: true)
.navigationTitle("SimpleX address")
.navigationBarTitleDisplayMode(.large)
.navigationTitle("Address or 1-time link?")
.navigationBarTitleDisplayMode(.inline)
.modifier(ThemedBackground(grouped: true))
}
}
@@ -62,7 +62,6 @@ struct ChooseServerOperators: View {
var onboarding: Bool
@State private var serverOperators: [ServerOperator] = []
@State private var selectedOperatorIds = Set<Int64>()
@State private var reviewConditionsNavLinkActive = false
@State private var sheetItem: ChooseServerOperatorsSheet? = nil
@State private var notificationsModeNavLinkActive = false
@State private var justOpened = true
@@ -73,24 +72,33 @@ struct ChooseServerOperators: View {
GeometryReader { g in
ScrollView {
VStack(alignment: .leading, spacing: 20) {
if !onboarding {
Text("Choose operators")
.font(.largeTitle)
.bold()
let title = Text("Server operators")
.font(.largeTitle)
.bold()
.frame(maxWidth: .infinity, alignment: .center)
if onboarding {
title.padding(.top, 25)
} else {
title
}
infoText()
.frame(maxWidth: .infinity, alignment: .center)
Spacer()
ForEach(serverOperators) { srvOperator in
operatorCheckView(srvOperator)
}
Text("You can configure servers via settings.")
.font(.footnote)
.multilineTextAlignment(.center)
.frame(maxWidth: .infinity, alignment: .center)
.padding(.horizontal, 32)
VStack {
Text("SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app.").padding(.bottom, 8)
Text("You can configure servers via settings.")
}
.font(.footnote)
.multilineTextAlignment(.center)
.frame(maxWidth: .infinity, alignment: .center)
.padding(.horizontal, 16)
Spacer()
@@ -117,24 +125,24 @@ struct ChooseServerOperators: View {
.foregroundColor(.clear)
}
}
.font(.callout)
.padding(.top)
.font(.system(size: 17, weight: .semibold))
.frame(minHeight: 40)
}
}
.padding(.bottom)
if !onboarding && !reviewForOperators.isEmpty {
VStack(spacing: 8) {
reviewLaterButton()
(
Text("Conditions will be accepted for enabled operators after 30 days.")
+ Text(" ")
+ textSpace
+ Text("You can configure operators in Network & servers settings.")
)
.multilineTextAlignment(.center)
.font(.footnote)
.padding(.horizontal, 32)
}
.frame(maxWidth: .infinity)
.disabled(!canReviewLater)
.padding(.bottom)
}
@@ -160,23 +168,18 @@ struct ChooseServerOperators: View {
.modifier(ThemedBackground(grouped: true))
}
}
.frame(maxHeight: .infinity, alignment: .top)
}
.frame(maxHeight: .infinity)
.padding()
.frame(maxHeight: .infinity, alignment: .top)
.padding(onboarding ? 25 : 16)
}
private func infoText() -> some View {
HStack(spacing: 12) {
Image(systemName: "info.circle")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.foregroundColor(theme.colors.primary)
.onTapGesture {
sheetItem = .showInfo
}
Text("Select network operators to use.")
Button {
sheetItem = .showInfo
} label: {
Label("How it helps privacy", systemImage: "info.circle")
.font(.headline)
}
}
@@ -214,23 +217,15 @@ struct ChooseServerOperators: View {
}
private func reviewConditionsButton() -> some View {
ZStack {
Button {
reviewConditionsNavLinkActive = true
} label: {
Text("Review conditions")
}
.buttonStyle(OnboardingButtonStyle(isDisabled: selectedOperatorIds.isEmpty))
.disabled(selectedOperatorIds.isEmpty)
NavigationLink(isActive: $reviewConditionsNavLinkActive) {
reviewConditionsDestinationView()
} label: {
EmptyView()
}
.frame(width: 1, height: 1)
.hidden()
NavigationLink("Review conditions") {
reviewConditionsView()
.navigationTitle("Conditions of use")
.navigationBarTitleDisplayMode(.large)
.toolbar { ToolbarItem(placement: .navigationBarTrailing, content: conditionsLinkButton) }
.modifier(ThemedBackground(grouped: true))
}
.buttonStyle(OnboardingButtonStyle(isDisabled: selectedOperatorIds.isEmpty))
.disabled(selectedOperatorIds.isEmpty)
}
private func setOperatorsButton() -> some View {
@@ -305,37 +300,27 @@ struct ChooseServerOperators: View {
private func notificationsModeDestinationView() -> some View {
SetNotificationsMode()
.navigationTitle("Push notifications")
.navigationBarTitleDisplayMode(.large)
.navigationBarBackButtonHidden(true)
.modifier(ThemedBackground())
}
private func reviewConditionsDestinationView() -> some View {
reviewConditionsView()
.navigationTitle("Conditions of use")
.navigationBarTitleDisplayMode(.large)
.toolbar { ToolbarItem(placement: .navigationBarTrailing, content: conditionsLinkButton) }
.modifier(ThemedBackground(grouped: true))
}
@ViewBuilder private func reviewConditionsView() -> some View {
let operatorsWithConditionsAccepted = ChatModel.shared.conditions.serverOperators.filter { $0.conditionsAcceptance.conditionsAccepted }
let acceptForOperators = selectedOperators.filter { !$0.conditionsAcceptance.conditionsAccepted }
VStack(alignment: .leading, spacing: 20) {
if !operatorsWithConditionsAccepted.isEmpty {
Text("Conditions are already accepted for following operator(s): **\(operatorsWithConditionsAccepted.map { $0.legalName_ }.joined(separator: ", "))**.")
Text("Same conditions will apply to operator(s): **\(acceptForOperators.map { $0.legalName_ }.joined(separator: ", "))**.")
Text("Conditions are already accepted for these operator(s): **\(operatorsWithConditionsAccepted.map { $0.legalName_ }.joined(separator: ", "))**.")
Text("The same conditions will apply to operator(s): **\(acceptForOperators.map { $0.legalName_ }.joined(separator: ", "))**.")
} else {
Text("Conditions will be accepted for operator(s): **\(acceptForOperators.map { $0.legalName_ }.joined(separator: ", "))**.")
}
ConditionsTextView()
.frame(maxHeight: .infinity)
acceptConditionsButton()
.padding(.bottom)
.padding(.bottom)
}
.padding(.horizontal)
.frame(maxHeight: .infinity)
.padding(.horizontal, 25)
}
private func acceptConditionsButton() -> some View {
@@ -408,26 +393,56 @@ struct ChooseServerOperators: View {
}
}
let operatorsPostLink = URL(string: "https://simplex.chat/blog/20241125-servers-operated-by-flux-true-privacy-and-decentralization-for-all-users.html")!
struct ChooseServerOperatorsInfoView: View {
@Environment(\.colorScheme) var colorScheme: ColorScheme
@EnvironmentObject var theme: AppTheme
var body: some View {
VStack(alignment: .leading) {
Text("Network operators")
.font(.largeTitle)
.bold()
.padding(.vertical)
ScrollView {
VStack(alignment: .leading) {
Group {
Text("When more than one network operator is enabled, the app will use the servers of different operators for each conversation.")
Text("For example, if you receive messages via SimpleX Chat server, the app will use one of Flux servers for private routing.")
NavigationView {
List {
VStack(alignment: .leading, spacing: 12) {
Text("The app protects your privacy by using different operators in each conversation.")
Text("When more than one operator is enabled, none of them has metadata to learn who communicates with whom.")
Text("For example, if your contact receives messages via a SimpleX Chat server, your app will deliver them via a Flux server.")
}
.fixedSize(horizontal: false, vertical: true)
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.padding(.top)
Section {
ForEach(ChatModel.shared.conditions.serverOperators) { op in
operatorInfoNavLinkView(op)
}
.padding(.bottom)
} header: {
Text("About operators")
.foregroundColor(theme.colors.secondary)
}
}
.navigationTitle("Server operators")
.navigationBarTitleDisplayMode(.large)
.modifier(ThemedBackground(grouped: true))
}
}
private func operatorInfoNavLinkView(_ op: ServerOperator) -> some View {
NavigationLink() {
OperatorInfoView(serverOperator: op)
.navigationBarTitle("Network operator")
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(.large)
} label: {
HStack {
Image(op.logo(colorScheme))
.resizable()
.scaledToFit()
.frame(width: 24, height: 24)
Text(op.tradeName)
}
}
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
.modifier(ThemedBackground())
}
}
@@ -119,49 +119,66 @@ struct CreateFirstProfile: View {
var body: some View {
VStack(alignment: .leading, spacing: 20) {
Text("Your profile, contacts and delivered messages are stored on your device.")
.font(.callout)
.foregroundColor(theme.colors.secondary)
Text("The profile is only shared with your contacts.")
.font(.callout)
.foregroundColor(theme.colors.secondary)
VStack(alignment: .center, spacing: 20) {
Text("Create your profile")
.font(.largeTitle)
.bold()
.multilineTextAlignment(.center)
Text("Your profile, contacts and delivered messages are stored on your device.")
.font(.callout)
.foregroundColor(theme.colors.secondary)
.multilineTextAlignment(.center)
Text("The profile is only shared with your contacts.")
.font(.callout)
.foregroundColor(theme.colors.secondary)
.multilineTextAlignment(.center)
}
.frame(maxWidth: .infinity) // Ensures it takes up the full width
.padding(.horizontal, 10)
HStack {
let name = displayName.trimmingCharacters(in: .whitespaces)
let validName = mkValidName(name)
ZStack {
ZStack(alignment: .trailing) {
TextField("Enter your name…", text: $displayName)
.focused($focusDisplayName)
.padding(.horizontal)
.padding(.vertical, 10)
.background(
RoundedRectangle(cornerRadius: 10, style: .continuous)
.fill(Color(uiColor: .tertiarySystemFill))
)
if name != validName {
Button {
showAlert(.invalidNameError(validName: validName))
} label: {
Image(systemName: "exclamationmark.circle").foregroundColor(.red)
Image(systemName: "exclamationmark.circle")
.foregroundColor(.red)
.padding(.horizontal, 10)
}
} else {
Image(systemName: "exclamationmark.circle").foregroundColor(.clear)
Image(systemName: "pencil").foregroundColor(theme.colors.secondary)
}
}
TextField("Enter your name…", text: $displayName)
.focused($focusDisplayName)
.padding(.horizontal)
.padding(.vertical, 10)
.background(
RoundedRectangle(cornerRadius: 10, style: .continuous)
.fill(Color(uiColor: .tertiarySystemFill))
)
}
.padding(.top)
Spacer()
createProfileButton()
.padding(.bottom)
VStack(spacing: 10) {
createProfileButton()
if !focusDisplayName {
onboardingButtonPlaceholder()
}
}
}
.onAppear() {
focusDisplayName = true
setLastVersionDefault()
}
.padding()
.padding(.horizontal, 25)
.padding(.top, 10)
.padding(.bottom, 25)
.frame(maxWidth: .infinity, alignment: .leading)
}
@@ -191,8 +208,6 @@ struct CreateFirstProfile: View {
private func nextStepDestinationView() -> some View {
ChooseServerOperators(onboarding: true)
.navigationTitle("Choose operators")
.navigationBarTitleDisplayMode(.large)
.navigationBarBackButtonHidden(true)
.modifier(ThemedBackground())
}
@@ -23,13 +23,10 @@ struct HowItWorks: View {
ScrollView {
VStack(alignment: .leading) {
Group {
Text("Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*")
Text("To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts.")
Text("You control through which server(s) **to receive** the messages, your contacts the servers you use to message them.")
Text("Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**.")
if onboarding {
Text("Read more in our GitHub repository.")
} else {
Text("To protect your privacy, SimpleX uses separate IDs for each of your contacts.")
Text("Only client devices store user profiles, contacts, groups, and messages.")
Text("All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages.")
if !onboarding {
Text("Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme).")
}
}
@@ -40,12 +37,14 @@ struct HowItWorks: View {
Spacer()
if onboarding {
createFirstProfileButton()
.padding(.bottom)
VStack(spacing: 10) {
createFirstProfileButton()
onboardingButtonPlaceholder()
}
}
}
.lineLimit(10)
.padding()
.padding(onboarding ? 25 : 16)
.frame(maxHeight: .infinity, alignment: .top)
.modifier(ThemedBackground())
}
@@ -24,14 +24,10 @@ struct OnboardingView: View {
CreateSimpleXAddress()
case .step3_ChooseServerOperators:
ChooseServerOperators(onboarding: true)
.navigationTitle("Choose operators")
.navigationBarTitleDisplayMode(.large)
.navigationBarBackButtonHidden(true)
.modifier(ThemedBackground())
case .step4_SetNotificationsMode:
SetNotificationsMode()
.navigationTitle("Push notifications")
.navigationBarTitleDisplayMode(.large)
.navigationBarBackButtonHidden(true)
.modifier(ThemedBackground())
case .onboardingComplete: EmptyView()
@@ -40,6 +36,10 @@ struct OnboardingView: View {
}
}
func onboardingButtonPlaceholder() -> some View {
Spacer().frame(height: 40)
}
enum OnboardingStage: String, Identifiable {
case step1_SimpleXInfo
case step2_CreateProfile // deprecated
@@ -13,41 +13,55 @@ struct SetNotificationsMode: View {
@EnvironmentObject var m: ChatModel
@State private var notificationMode = NotificationsMode.instant
@State private var showAlert: NotificationAlert?
@State private var showInfo: Bool = false
var body: some View {
GeometryReader { g in
ScrollView {
VStack(alignment: .leading, spacing: 20) {
Text("Send notifications:")
VStack(alignment: .center, spacing: 20) {
Text("Push notifications")
.font(.largeTitle)
.bold()
.padding(.top, 25)
infoText()
Spacer()
ForEach(NotificationsMode.values) { mode in
NtfModeSelector(mode: mode, selection: $notificationMode)
}
Spacer()
Button {
if let token = m.deviceToken {
setNotificationsMode(token, notificationMode)
} else {
AlertManager.shared.showAlertMsg(title: "No device token!")
}
onboardingStageDefault.set(.onboardingComplete)
m.onboardingStage = .onboardingComplete
} label: {
if case .off = notificationMode {
Text("Use chat")
} else {
Text("Enable notifications")
VStack(spacing: 10) {
Button {
if let token = m.deviceToken {
setNotificationsMode(token, notificationMode)
} else {
AlertManager.shared.showAlertMsg(title: "No device token!")
}
onboardingStageDefault.set(.onboardingComplete)
m.onboardingStage = .onboardingComplete
} label: {
if case .off = notificationMode {
Text("Use chat")
} else {
Text("Enable notifications")
}
}
.buttonStyle(OnboardingButtonStyle())
onboardingButtonPlaceholder()
}
.buttonStyle(OnboardingButtonStyle())
.padding(.bottom)
}
.padding()
.padding(25)
.frame(minHeight: g.size.height)
}
}
.frame(maxHeight: .infinity)
.sheet(isPresented: $showInfo) {
NotificationsInfoView()
}
}
private func setNotificationsMode(_ token: DeviceToken, _ mode: NotificationsMode) {
@@ -73,6 +87,15 @@ struct SetNotificationsMode: View {
}
}
}
private func infoText() -> some View {
Button {
showInfo = true
} label: {
Label("How it affects privacy", systemImage: "info.circle")
.font(.headline)
}
}
}
struct NtfModeSelector: View {
@@ -83,15 +106,25 @@ struct NtfModeSelector: View {
var body: some View {
ZStack {
VStack(alignment: .leading, spacing: 4) {
Text(mode.label)
.font(.headline)
HStack(spacing: 16) {
Image(systemName: mode.icon)
.resizable()
.scaledToFill()
.frame(width: mode.icon == "bolt" ? 14 : 18, height: 18)
.foregroundColor(selection == mode ? theme.colors.primary : theme.colors.secondary)
Text(ntfModeDescription(mode))
.lineLimit(10)
.font(.subheadline)
VStack(alignment: .leading, spacing: 4) {
Text(mode.label)
.font(.headline)
.foregroundColor(selection == mode ? theme.colors.primary : theme.colors.secondary)
Text(ntfModeShortDescription(mode))
.lineLimit(2)
.font(.callout)
.fixedSize(horizontal: false, vertical: true)
}
}
.padding(12)
.padding(.vertical, 12)
.padding(.trailing, 12)
.padding(.leading, 16)
}
.frame(maxWidth: .infinity, alignment: .leading)
.background(tapped ? Color(uiColor: .secondarySystemFill) : theme.colors.background)
@@ -107,6 +140,37 @@ struct NtfModeSelector: View {
}
}
struct NotificationsInfoView: View {
var body: some View {
VStack(alignment: .leading) {
Text("Notifications privacy")
.font(.largeTitle)
.bold()
.padding(.vertical)
ScrollView {
VStack(alignment: .leading) {
Group {
ForEach(NotificationsMode.values) { mode in
VStack(alignment: .leading, spacing: 4) {
(Text(Image(systemName: mode.icon)) + textSpace + Text(mode.label))
.font(.headline)
.foregroundColor(.secondary)
Text(ntfModeDescription(mode))
.lineLimit(10)
.font(.callout)
}
}
}
.padding(.bottom)
}
}
}
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
.modifier(ThemedBackground())
}
}
struct NotificationsModeView_Previews: PreviewProvider {
static var previews: some View {
SetNotificationsMode()
@@ -19,51 +19,52 @@ struct SimpleXInfo: View {
var body: some View {
GeometryReader { g in
ScrollView {
VStack(alignment: .leading, spacing: 20) {
Image(colorScheme == .light ? "logo" : "logo-light")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: g.size.width * 0.67)
.padding(.bottom, 8)
.frame(maxWidth: .infinity, minHeight: 48, alignment: .top)
VStack(alignment: .leading) {
VStack(alignment: .center, spacing: 10) {
Image(colorScheme == .light ? "logo" : "logo-light")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: g.size.width * 0.67)
.padding(.bottom, 8)
.padding(.leading, 4)
.frame(maxWidth: .infinity, minHeight: 48, alignment: .top)
Button {
showHowItWorks = true
} label: {
Label("The future of messaging", systemImage: "info.circle")
.font(.headline)
}
}
Spacer()
VStack(alignment: .leading) {
Text("The next generation of private messaging")
.font(.title2)
.padding(.bottom, 30)
.padding(.horizontal, 40)
.frame(maxWidth: .infinity)
.multilineTextAlignment(.center)
infoRow("privacy", "Privacy redefined",
"The 1st platform without any user identifiers private by design.", width: 48)
infoRow("shield", "Immune to spam and abuse",
"People can connect to you only via the links you share.", width: 46)
infoRow(colorScheme == .light ? "decentralized" : "decentralized-light", "Decentralized",
"Open-source protocol and code anybody can run the servers.", width: 44)
onboardingInfoRow("privacy", "Privacy redefined",
"No user identifiers.", width: 48)
onboardingInfoRow("shield", "Immune to spam",
"You decide who can connect.", width: 46)
onboardingInfoRow(colorScheme == .light ? "decentralized" : "decentralized-light", "Decentralized",
"Anybody can host servers.", width: 46)
}
.padding(.leading, 16)
Spacer()
if onboarding {
createFirstProfileButton()
VStack(spacing: 10) {
createFirstProfileButton()
Button {
m.migrationState = .pasteOrScanLink
} label: {
Label("Migrate from another device", systemImage: "tray.and.arrow.down")
.font(.subheadline)
Button {
m.migrationState = .pasteOrScanLink
} label: {
Label("Migrate from another device", systemImage: "tray.and.arrow.down")
.font(.system(size: 17, weight: .semibold))
.frame(minHeight: 40)
}
.frame(maxWidth: .infinity)
}
.frame(maxWidth: .infinity)
}
Button {
showHowItWorks = true
} label: {
Label("How it works", systemImage: "info.circle")
.font(.subheadline)
}
.frame(maxWidth: .infinity)
.padding(.bottom)
}
.frame(minHeight: g.size.height)
}
@@ -89,26 +90,29 @@ struct SimpleXInfo: View {
}
}
.frame(maxHeight: .infinity)
.padding()
.padding(.horizontal, 25)
.padding(.top, 75)
.padding(.bottom, 25)
}
private func infoRow(_ image: String, _ title: LocalizedStringKey, _ text: LocalizedStringKey, width: CGFloat) -> some View {
private func onboardingInfoRow(_ image: String, _ title: LocalizedStringKey, _ text: LocalizedStringKey, width: CGFloat) -> some View {
HStack(alignment: .top) {
Image(image)
.resizable()
.scaledToFit()
.frame(width: width, height: 54)
.frame(width: 54)
.padding(.top, 4)
.padding(.leading, 4)
.padding(.trailing, 10)
VStack(alignment: .leading, spacing: 4) {
Text(title).font(.headline)
Text(text).frame(minHeight: 40, alignment: .top)
.font(.callout)
.lineLimit(3)
.fixedSize(horizontal: false, vertical: true)
}
.padding(.top, 4)
}
.padding(.bottom, 20)
.padding(.trailing, 6)
.padding(.bottom, 12)
}
private func createFirstProfileButton() -> some View {
@@ -121,7 +125,7 @@ struct SimpleXInfo: View {
.buttonStyle(OnboardingButtonStyle(isDisabled: false))
NavigationLink(isActive: $createProfileNavLinkActive) {
createProfileDestinationView()
CreateFirstProfile()
} label: {
EmptyView()
}
@@ -129,15 +133,10 @@ struct SimpleXInfo: View {
.hidden()
}
}
private func createProfileDestinationView() -> some View {
CreateFirstProfile()
.navigationTitle("Create your profile")
.navigationBarTitleDisplayMode(.large)
.modifier(ThemedBackground())
}
}
let textSpace = Text(verbatim: " ")
struct SimpleXInfo_Previews: PreviewProvider {
static var previews: some View {
SimpleXInfo(onboarding: true)
@@ -520,14 +520,19 @@ private let versionDescriptions: [VersionDescription] = [
]
),
VersionDescription(
version: "v6.2 (beta.1)",
post: URL(string: "https://simplex.chat/blog/20241125-servers-operated-by-flux-true-privacy-and-decentralization-for-all-users.html"),
version: "v6.2",
post: URL(string: "https://simplex.chat/blog/20241210-simplex-network-v6-2-servers-by-flux-business-chats.html"),
features: [
.view(FeatureView(
icon: nil,
title: "Network decentralization",
view: { NewOperatorsView() }
)),
.feature(Description(
icon: "briefcase",
title: "Business chats",
description: "Privacy for your customers."
)),
.feature(Description(
icon: "bolt",
title: "More reliable notifications",
@@ -268,7 +268,7 @@ struct ConnectDesktopView: View {
private func ctrlDeviceNameText(_ session: RemoteCtrlSession, _ rc: RemoteCtrlInfo?) -> Text {
var t = Text(rc?.deviceViewName ?? session.ctrlAppInfo?.deviceName ?? "")
if (rc == nil) {
t = t + Text(" ") + Text("(new)").italic()
t = t + textSpace + Text("(new)").italic()
}
return t
}
@@ -277,7 +277,7 @@ struct ConnectDesktopView: View {
let v = session.ctrlAppInfo?.appVersionRange.maxVersion
var t = Text("v\(v ?? "")")
if v != session.appVersion {
t = t + Text(" ") + Text("(this device v\(session.appVersion))").italic()
t = t + textSpace + Text("(this device v\(session.appVersion))").italic()
}
return t
}
@@ -65,6 +65,7 @@ extension AppSettings {
if let val = uiCurrentThemeIds { currentThemeIdsDefault.set(val) }
if let val = uiThemes { themeOverridesDefault.set(val.skipDuplicates()) }
if let val = oneHandUI { groupDefaults.setValue(val, forKey: GROUP_DEFAULT_ONE_HAND_UI) }
if let val = chatBottomBar { groupDefaults.setValue(val, forKey: GROUP_DEFAULT_CHAT_BOTTOM_BAR) }
}
public static var current: AppSettings {
@@ -100,6 +101,7 @@ extension AppSettings {
c.uiCurrentThemeIds = currentThemeIdsDefault.get()
c.uiThemes = themeOverridesDefault.get()
c.oneHandUI = groupDefaults.bool(forKey: GROUP_DEFAULT_ONE_HAND_UI)
c.chatBottomBar = groupDefaults.bool(forKey: GROUP_DEFAULT_CHAT_BOTTOM_BAR)
return c
}
}
@@ -45,7 +45,7 @@ struct DeveloperView: View {
} header: {
Text("")
} footer: {
((developerTools ? Text("Show:") : Text("Hide:")) + Text(" ") + Text("Database IDs and Transport isolation option."))
((developerTools ? Text("Show:") : Text("Hide:")) + textSpace + Text("Database IDs and Transport isolation option."))
.foregroundColor(theme.colors.secondary)
}
@@ -0,0 +1,83 @@
//
// ConditionsWebView.swift
// SimpleX (iOS)
//
// Created by Stanislav Dmitrenko on 26.11.2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import SwiftUI
import WebKit
struct ConditionsWebView: UIViewRepresentable {
@State var html: String
@EnvironmentObject var theme: AppTheme
@State var pageLoaded = false
func makeUIView(context: Context) -> WKWebView {
let view = WKWebView()
view.backgroundColor = .clear
view.isOpaque = false
view.navigationDelegate = context.coordinator
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
// just to make sure that even if updateUIView will not be called for any reason, the page
// will be rendered anyway
if !pageLoaded {
loadPage(view)
}
}
return view
}
func updateUIView(_ view: WKWebView, context: Context) {
loadPage(view)
}
private func loadPage(_ webView: WKWebView) {
let styles = """
<style>
body {
color: \(theme.colors.onBackground.toHTMLHex());
font-family: Helvetica;
}
a {
color: \(theme.colors.primary.toHTMLHex());
}
code, pre {
font-family: Menlo;
background: \(theme.colors.secondary.opacity(theme.colors.isLight ? 0.2 : 0.3).toHTMLHex());
}
</style>
"""
let head = "<head><meta name='viewport' content='width=device-width, initial-scale=1.0, minimum-scale=1.0, user-scalable=no'>\(styles)</head>"
webView.loadHTMLString(head + html, baseURL: nil)
DispatchQueue.main.async {
pageLoaded = true
}
}
func makeCoordinator() -> Cordinator {
Cordinator()
}
class Cordinator: NSObject, WKNavigationDelegate {
func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
guard let url = navigationAction.request.url else { return decisionHandler(.allow) }
switch navigationAction.navigationType {
case .linkActivated:
decisionHandler(.cancel)
if url.absoluteString.starts(with: "https://simplex.chat/contact#") {
ChatModel.shared.appOpenUrl = url
} else {
UIApplication.shared.open(url)
}
default:
decisionHandler(.allow)
}
}
}
}
@@ -269,6 +269,7 @@ struct UsageConditionsView: View {
}
.padding(.bottom)
.padding(.bottom)
case let .accepted(operators):
Text("Conditions are accepted for the operator(s): **\(operators.map { $0.legalName_ }.joined(separator: ", "))**.")
@@ -277,7 +278,7 @@ struct UsageConditionsView: View {
.padding(.bottom)
}
}
.padding(.horizontal)
.padding(.horizontal, 25)
.frame(maxHeight: .infinity)
}
@@ -8,6 +8,7 @@
import SwiftUI
import SimpleXChat
import Ink
struct OperatorView: View {
@Environment(\.dismiss) var dismiss: DismissAction
@@ -330,9 +331,12 @@ struct OperatorInfoView: View {
Text(d)
}
}
Link(serverOperator.info.website.absoluteString, destination: serverOperator.info.website)
}
Section {
Link("\(serverOperator.info.website)", destination: URL(string: serverOperator.info.website)!)
if let selfhost = serverOperator.info.selfhost {
Section {
Link(selfhost.text, destination: selfhost.link)
}
}
}
}
@@ -342,6 +346,7 @@ struct OperatorInfoView: View {
struct ConditionsTextView: View {
@State private var conditionsData: (UsageConditions, String?, UsageConditions?)?
@State private var failedToLoad: Bool = false
@State private var conditionsHTML: String? = nil
let defaultConditionsLink = "https://github.com/simplex-chat/simplex-chat/blob/stable/PRIVACY.md"
@@ -350,7 +355,18 @@ struct ConditionsTextView: View {
.frame(maxWidth: .infinity, maxHeight: .infinity)
.task {
do {
conditionsData = try await getUsageConditions()
let conditions = try await getUsageConditions()
let conditionsText = conditions.1
let parentLink = "https://github.com/simplex-chat/simplex-chat/blob/\(conditions.0.conditionsCommit)"
let preparedText: String?
if let conditionsText {
let prepared = prepareMarkdown(conditionsText.trimmingCharacters(in: .whitespacesAndNewlines), parentLink)
conditionsHTML = MarkdownParser().html(from: prepared)
preparedText = prepared
} else {
preparedText = nil
}
conditionsData = (conditions.0, preparedText, conditions.2)
} catch let error {
logger.error("ConditionsTextView getUsageConditions error: \(responseError(error))")
failedToLoad = true
@@ -358,18 +374,16 @@ struct ConditionsTextView: View {
}
}
// TODO Markdown & diff rendering
// TODO Diff rendering
@ViewBuilder private func viewBody() -> some View {
if let (usageConditions, conditionsText, acceptedConditions) = conditionsData {
if let conditionsText = conditionsText {
ScrollView {
Text(conditionsText.trimmingCharacters(in: .whitespacesAndNewlines))
.padding()
}
.background(
RoundedRectangle(cornerRadius: 12, style: .continuous)
.fill(Color(uiColor: .secondarySystemGroupedBackground))
)
if let (usageConditions, _, _) = conditionsData {
if let conditionsHTML {
ConditionsWebView(html: conditionsHTML)
.padding(6)
.background(
RoundedRectangle(cornerRadius: 12, style: .continuous)
.fill(Color(uiColor: .secondarySystemGroupedBackground))
)
} else {
let conditionsLink = "https://github.com/simplex-chat/simplex-chat/blob/\(usageConditions.conditionsCommit)/PRIVACY.md"
conditionsLinkView(conditionsLink)
@@ -391,6 +405,16 @@ struct ConditionsTextView: View {
}
}
}
private func prepareMarkdown(_ text: String, _ parentLink: String) -> String {
let localLinkRegex = try! NSRegularExpression(pattern: "\\[([^\\(]*)\\]\\(#.*\\)")
let h1Regex = try! NSRegularExpression(pattern: "^# ")
var text = localLinkRegex.stringByReplacingMatches(in: text, options: [], range: NSRange(location: 0, length: text.utf16.count), withTemplate: "$1")
text = h1Regex.stringByReplacingMatches(in: text, options: [], range: NSRange(location: 0, length: text.utf16.count), withTemplate: "")
return text
.replacingOccurrences(of: "](/", with: "](\(parentLink)/")
.replacingOccurrences(of: "](./", with: "](\(parentLink)/")
}
}
struct SingleOperatorUsageConditionsView: View {
@@ -400,7 +424,6 @@ struct SingleOperatorUsageConditionsView: View {
@Binding var userServers: [UserOperatorServers]
@Binding var serverErrors: [UserServersError]
var operatorIndex: Int
@State private var usageConditionsNavLinkActive: Bool = false
var body: some View {
viewBody()
@@ -412,52 +435,45 @@ struct SingleOperatorUsageConditionsView: View {
// In current UI implementation this branch doesn't get shown - as conditions can't be opened from inside operator once accepted
VStack(alignment: .leading, spacing: 20) {
Group {
viewHeader()
ConditionsTextView()
.padding(.bottom)
.padding(.bottom)
}
.padding(.horizontal)
viewHeader()
ConditionsTextView()
}
.padding(.bottom)
.padding(.bottom)
.padding(.horizontal)
.frame(maxHeight: .infinity)
} else if !operatorsWithConditionsAccepted.isEmpty {
NavigationView {
VStack(alignment: .leading, spacing: 20) {
Group {
viewHeader()
Text("Conditions are already accepted for following operator(s): **\(operatorsWithConditionsAccepted.map { $0.legalName_ }.joined(separator: ", "))**.")
Text("Same conditions will apply to operator **\(userServers[operatorIndex].operator_.legalName_)**.")
conditionsAppliedToOtherOperatorsText()
usageConditionsNavLinkButton()
viewHeader()
Text("Conditions are already accepted for these operator(s): **\(operatorsWithConditionsAccepted.map { $0.legalName_ }.joined(separator: ", "))**.")
Text("The same conditions will apply to operator **\(userServers[operatorIndex].operator_.legalName_)**.")
conditionsAppliedToOtherOperatorsText()
Spacer()
Spacer()
acceptConditionsButton()
.padding(.bottom)
.padding(.bottom)
}
.padding(.horizontal)
acceptConditionsButton()
usageConditionsNavLinkButton()
}
.padding(.bottom)
.padding(.bottom)
.padding(.horizontal)
.frame(maxHeight: .infinity)
}
} else {
VStack(alignment: .leading, spacing: 20) {
Group {
viewHeader()
Text("To use the servers of **\(userServers[operatorIndex].operator_.legalName_)**, accept conditions of use.")
conditionsAppliedToOtherOperatorsText()
ConditionsTextView()
acceptConditionsButton()
.padding(.bottom)
.padding(.bottom)
}
.padding(.horizontal)
viewHeader()
Text("To use the servers of **\(userServers[operatorIndex].operator_.legalName_)**, accept conditions of use.")
conditionsAppliedToOtherOperatorsText()
ConditionsTextView()
acceptConditionsButton()
.padding(.bottom)
.padding(.bottom)
}
.padding(.horizontal)
.frame(maxHeight: .infinity)
}
@@ -524,31 +540,16 @@ struct SingleOperatorUsageConditionsView: View {
}
private func usageConditionsNavLinkButton() -> some View {
ZStack {
Button {
usageConditionsNavLinkActive = true
} label: {
Text("View conditions")
}
NavigationLink(isActive: $usageConditionsNavLinkActive) {
usageConditionsDestinationView()
} label: {
EmptyView()
}
.frame(width: 1, height: 1)
.hidden()
NavigationLink("View conditions") {
ConditionsTextView()
.padding()
.navigationTitle("Conditions of use")
.navigationBarTitleDisplayMode(.large)
.toolbar { ToolbarItem(placement: .navigationBarTrailing, content: conditionsLinkButton) }
.modifier(ThemedBackground(grouped: true))
}
}
private func usageConditionsDestinationView() -> some View {
ConditionsTextView()
.padding()
.padding(.bottom)
.navigationTitle("Conditions of use")
.navigationBarTitleDisplayMode(.large)
.toolbar { ToolbarItem(placement: .navigationBarTrailing, content: conditionsLinkButton) }
.modifier(ThemedBackground(grouped: true))
.font(.callout)
.frame(maxWidth: .infinity, alignment: .center)
}
}
@@ -237,9 +237,17 @@ struct NotificationsView: View {
func ntfModeDescription(_ mode: NotificationsMode) -> LocalizedStringKey {
switch mode {
case .off: return "**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app)."
case .periodic: return "**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."
case .instant: return "**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from."
case .off: return "**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app."
case .periodic: return "**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata."
case .instant: return "**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from."
}
}
func ntfModeShortDescription(_ mode: NotificationsMode) -> LocalizedStringKey {
switch mode {
case .off: return "Check messages when allowed."
case .periodic: return "Check messages every 20 min."
case .instant: return "E2E encrypted notifications."
}
}
@@ -39,6 +39,7 @@ let DEFAULT_EXPERIMENTAL_CALLS = "experimentalCalls"
let DEFAULT_CHAT_ARCHIVE_NAME = "chatArchiveName"
let DEFAULT_CHAT_ARCHIVE_TIME = "chatArchiveTime"
let DEFAULT_CHAT_V3_DB_MIGRATION = "chatV3DBMigration"
let DEFAULT_SHOULD_IMPORT_APP_SETTINGS = "shouldImportAppSettings"
let DEFAULT_DEVELOPER_TOOLS = "developerTools"
let DEFAULT_ENCRYPTION_STARTED = "encryptionStarted"
let DEFAULT_ENCRYPTION_STARTED_AT = "encryptionStartedAt"
@@ -192,6 +193,8 @@ let customDisappearingMessageTimeDefault = IntDefault(defaults: UserDefaults.sta
let showDeleteConversationNoticeDefault = BoolDefault(defaults: UserDefaults.standard, forKey: DEFAULT_SHOW_DELETE_CONVERSATION_NOTICE)
let showDeleteContactNoticeDefault = BoolDefault(defaults: UserDefaults.standard, forKey: DEFAULT_SHOW_DELETE_CONTACT_NOTICE)
/// after importing new database, this flag will be set and unset only after importing app settings in `initializeChat` */
let shouldImportAppSettingsDefault = BoolDefault(defaults: UserDefaults.standard, forKey: DEFAULT_SHOULD_IMPORT_APP_SETTINGS)
let currentThemeDefault = StringDefault(defaults: UserDefaults.standard, forKey: DEFAULT_CURRENT_THEME, withDefault: DefaultTheme.SYSTEM_THEME_NAME)
let systemDarkThemeDefault = StringDefault(defaults: UserDefaults.standard, forKey: DEFAULT_SYSTEM_DARK_THEME, withDefault: DefaultTheme.DARK.themeName)
let currentThemeIdsDefault = CodableDefault<[String: String]>(defaults: UserDefaults.standard, forKey: DEFAULT_CURRENT_THEME_IDS, withDefault: [:] )
@@ -11,25 +11,50 @@ import SwiftUI
struct UserAddressLearnMore: View {
@State var showCreateAddressButton = false
@State private var createAddressLinkActive = false
@State private var createOneTimeLinkActive = false
var body: some View {
VStack {
List {
VStack(alignment: .leading, spacing: 18) {
Text("You can share your address as a link or QR code - anybody can connect to you.")
VStack(alignment: .leading, spacing: 12) {
(Text(Image(systemName: "envelope")).foregroundColor(.secondary) + textSpace + Text("Share address publicly").bold().font(.title2))
Text("Share SimpleX address on social media.")
Text("You won't lose your contacts if you later delete your address.")
Text("When people request to connect, you can accept or reject it.")
Text("Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address).")
(Text(Image(systemName: "link.badge.plus")).foregroundColor(.secondary) + textSpace + Text("Share 1-time link with a friend").font(.title2).bold())
.padding(.top)
Text("1-time link can be used *with one contact only* - share in person or via any messenger.")
Text("You can set connection name, to remember who the link was shared with.")
if !showCreateAddressButton {
(Text(Image(systemName: "shield")).foregroundColor(.secondary) + textSpace + Text("Connection security").font(.title2).bold())
.padding(.top)
Text("SimpleX address and 1-time links are safe to share via any messenger.")
Text("To protect against your link being replaced, you can compare contact security codes.")
Text("Read more in [User Guide](https://simplex.chat/docs/guide/making-connections.html#comparison-of-1-time-invitation-links-and-simplex-contact-addresses).")
.padding(.top)
}
}
.listRowBackground(Color.clear)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
}
.frame(maxHeight: .infinity)
.frame(maxHeight: .infinity, alignment: .top)
Spacer()
if showCreateAddressButton {
addressCreationButton()
.padding()
VStack {
addressCreationButton()
.padding(.bottom)
createOneTimeLinkButton()
}
.padding()
}
}
.frame(maxHeight: .infinity, alignment: .top)
}
private func addressCreationButton() -> some View {
@@ -52,6 +77,28 @@ struct UserAddressLearnMore: View {
.hidden()
}
}
private func createOneTimeLinkButton() -> some View {
ZStack {
Button {
createOneTimeLinkActive = true
} label: {
Text("Create 1-time link")
.font(.callout)
}
NavigationLink(isActive: $createOneTimeLinkActive) {
NewChatView(selection: .invite)
.navigationTitle("New chat")
.navigationBarTitleDisplayMode(.large)
.modifier(ThemedBackground(grouped: true))
} label: {
EmptyView()
}
.frame(width: 1, height: 1)
.hidden()
}
}
}
struct UserAddressLearnMore_Previews: PreviewProvider {
@@ -18,43 +18,28 @@ struct UserAddressView: View {
@State var autoCreate = false
@State private var aas = AutoAcceptState()
@State private var savedAAS = AutoAcceptState()
@State private var ignoreShareViaProfileChange = false
@State private var showMailView = false
@State private var mailViewResult: Result<MFMailComposeResult, Error>? = nil
@State private var alert: UserAddressAlert?
@State private var progressIndicator = false
@FocusState private var keyboardVisible: Bool
private enum UserAddressAlert: Identifiable {
case deleteAddress
case profileAddress(on: Bool)
case shareOnCreate
case error(title: LocalizedStringKey, error: LocalizedStringKey?)
var id: String {
switch self {
case .deleteAddress: return "deleteAddress"
case let .profileAddress(on): return "profileAddress \(on)"
case .shareOnCreate: return "shareOnCreate"
case let .error(title, _): return "error \(title)"
}
}
}
var body: some View {
ZStack {
userAddressScrollView()
.onDisappear {
if savedAAS != aas {
showAlert(
title: NSLocalizedString("Auto-accept settings", comment: "alert title"),
message: NSLocalizedString("Settings were changed.", comment: "alert message"),
buttonTitle: NSLocalizedString("Save", comment: "alert button"),
buttonAction: saveAAS,
cancelButton: true
)
}
}
userAddressView()
if progressIndicator {
ZStack {
@@ -75,23 +60,6 @@ struct UserAddressView: View {
}
}
@Namespace private var bottomID
private func userAddressScrollView() -> some View {
ScrollViewReader { proxy in
userAddressView()
.onChange(of: keyboardVisible) { _ in
if keyboardVisible {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
withAnimation {
proxy.scrollTo(bottomID, anchor: .top)
}
}
}
}
}
}
private func userAddressView() -> some View {
List {
if let userAddress = chatModel.userAddress {
@@ -100,14 +68,18 @@ struct UserAddressView: View {
aas = AutoAcceptState(userAddress: userAddress)
savedAAS = aas
}
.onChange(of: aas.enable) { _ in
if !aas.enable { aas = AutoAcceptState() }
}
} else {
Section {
createAddressButton()
} footer: {
Text("Create an address to let people connect with you.")
} header: {
Text("For social media")
.foregroundColor(theme.colors.secondary)
}
Section {
createOneTimeLinkButton()
} header: {
Text("Or to share privately")
.foregroundColor(theme.colors.secondary)
}
@@ -123,8 +95,8 @@ struct UserAddressView: View {
title: Text("Delete address?"),
message:
shareViaProfile
? Text("All your contacts will remain connected. Profile update will be sent to your contacts.")
: Text("All your contacts will remain connected."),
? Text("All your contacts will remain connected. Profile update will be sent to your contacts.")
: Text("All your contacts will remain connected."),
primaryButton: .destructive(Text("Delete")) {
progressIndicator = true
Task {
@@ -134,7 +106,6 @@ struct UserAddressView: View {
chatModel.userAddress = nil
chatModel.updateUser(u)
if shareViaProfile {
ignoreShareViaProfileChange = true
shareViaProfile = false
}
}
@@ -147,37 +118,12 @@ struct UserAddressView: View {
}
}, secondaryButton: .cancel()
)
case let .profileAddress(on):
if on {
return Alert(
title: Text("Share address with contacts?"),
message: Text("Profile update will be sent to your contacts."),
primaryButton: .default(Text("Share")) {
setProfileAddress(on)
}, secondaryButton: .cancel() {
ignoreShareViaProfileChange = true
shareViaProfile = !on
}
)
} else {
return Alert(
title: Text("Stop sharing address?"),
message: Text("Profile update will be sent to your contacts."),
primaryButton: .default(Text("Stop sharing")) {
setProfileAddress(on)
}, secondaryButton: .cancel() {
ignoreShareViaProfileChange = true
shareViaProfile = !on
}
)
}
case .shareOnCreate:
return Alert(
title: Text("Share address with contacts?"),
message: Text("Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts."),
primaryButton: .default(Text("Share")) {
setProfileAddress(true)
ignoreShareViaProfileChange = true
setProfileAddress($progressIndicator, true)
shareViaProfile = true
}, secondaryButton: .cancel()
)
@@ -192,19 +138,39 @@ struct UserAddressView: View {
SimpleXLinkQRCode(uri: userAddress.connReqContact)
.id("simplex-contact-address-qrcode-\(userAddress.connReqContact)")
shareQRCodeButton(userAddress)
if MFMailComposeViewController.canSendMail() {
shareViaEmailButton(userAddress)
// if MFMailComposeViewController.canSendMail() {
// shareViaEmailButton(userAddress)
// }
settingsRow("briefcase", color: theme.colors.secondary) {
Toggle("Business address", isOn: $aas.business)
.onChange(of: aas.business) { ba in
if ba {
aas.enable = true
aas.incognito = false
}
saveAAS($aas, $savedAAS)
}
}
shareWithContactsButton()
autoAcceptToggle()
learnMoreButton()
addressSettingsButton(userAddress)
} header: {
Text("Address")
Text("For social media")
.foregroundColor(theme.colors.secondary)
} footer: {
if aas.business {
Text("Add your team members to the conversations.")
.foregroundColor(theme.colors.secondary)
}
}
Section {
createOneTimeLinkButton()
} header: {
Text("Or to share privately")
.foregroundColor(theme.colors.secondary)
}
if aas.enable {
autoAcceptSection()
Section {
learnMoreButton()
}
Section {
@@ -213,7 +179,6 @@ struct UserAddressView: View {
Text("Your contacts will remain connected.")
.foregroundColor(theme.colors.secondary)
}
.id(bottomID)
}
private func createAddressButton() -> some View {
@@ -223,7 +188,7 @@ struct UserAddressView: View {
Label("Create SimpleX address", systemImage: "qrcode")
}
}
private func createAddress() {
progressIndicator = true
Task {
@@ -243,6 +208,18 @@ struct UserAddressView: View {
}
}
private func createOneTimeLinkButton() -> some View {
NavigationLink {
NewChatView(selection: .invite)
.navigationTitle("New chat")
.navigationBarTitleDisplayMode(.large)
.modifier(ThemedBackground(grouped: true))
} label: {
Label("Create 1-time link", systemImage: "link.badge.plus")
.foregroundColor(theme.colors.primary)
}
}
private func deleteAddressButton() -> some View {
Button(role: .destructive) {
alert = .deleteAddress
@@ -292,24 +269,140 @@ struct UserAddressView: View {
}
}
private func autoAcceptToggle() -> some View {
settingsRow("checkmark", color: theme.colors.secondary) {
Toggle("Auto-accept", isOn: $aas.enable)
.onChange(of: aas.enable) { _ in
saveAAS()
}
private func addressSettingsButton(_ userAddress: UserContactLink) -> some View {
NavigationLink {
UserAddressSettingsView(shareViaProfile: $shareViaProfile)
.navigationTitle("Address settings")
.navigationBarTitleDisplayMode(.large)
.modifier(ThemedBackground(grouped: true))
} label: {
Text("Address settings")
}
}
private func learnMoreButton() -> some View {
NavigationLink {
UserAddressLearnMore()
.navigationTitle("SimpleX address")
.navigationTitle("Address or 1-time link?")
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(.large)
.navigationBarTitleDisplayMode(.inline)
} label: {
settingsRow("info.circle", color: theme.colors.secondary) {
Text("About SimpleX address")
Text("SimpleX address or 1-time link?")
}
}
}
}
private struct AutoAcceptState: Equatable {
var enable = false
var incognito = false
var business = false
var welcomeText = ""
init(enable: Bool = false, incognito: Bool = false, business: Bool = false, welcomeText: String = "") {
self.enable = enable
self.incognito = incognito
self.business = business
self.welcomeText = welcomeText
}
init(userAddress: UserContactLink) {
if let aa = userAddress.autoAccept {
enable = true
incognito = aa.acceptIncognito
business = aa.businessAddress
if let msg = aa.autoReply {
welcomeText = msg.text
} else {
welcomeText = ""
}
} else {
enable = false
incognito = false
business = false
welcomeText = ""
}
}
var autoAccept: AutoAccept? {
if enable {
var autoReply: MsgContent? = nil
let s = welcomeText.trimmingCharacters(in: .whitespacesAndNewlines)
if s != "" { autoReply = .text(s) }
return AutoAccept(businessAddress: business, acceptIncognito: incognito, autoReply: autoReply)
}
return nil
}
}
private func setProfileAddress(_ progressIndicator: Binding<Bool>, _ on: Bool) {
progressIndicator.wrappedValue = true
Task {
do {
if let u = try await apiSetProfileAddress(on: on) {
DispatchQueue.main.async {
ChatModel.shared.updateUser(u)
}
}
await MainActor.run { progressIndicator.wrappedValue = false }
} catch let error {
logger.error("apiSetProfileAddress: \(responseError(error))")
await MainActor.run { progressIndicator.wrappedValue = false }
}
}
}
struct UserAddressSettingsView: View {
@Environment(\.dismiss) var dismiss: DismissAction
@EnvironmentObject var theme: AppTheme
@Binding var shareViaProfile: Bool
@State private var aas = AutoAcceptState()
@State private var savedAAS = AutoAcceptState()
@State private var ignoreShareViaProfileChange = false
@State private var progressIndicator = false
@FocusState private var keyboardVisible: Bool
var body: some View {
ZStack {
if let userAddress = ChatModel.shared.userAddress {
userAddressSettingsView()
.onAppear {
aas = AutoAcceptState(userAddress: userAddress)
savedAAS = aas
}
.onChange(of: aas.enable) { aasEnabled in
if !aasEnabled { aas = AutoAcceptState() }
}
.onDisappear {
if savedAAS != aas {
showAlert(
title: NSLocalizedString("Auto-accept settings", comment: "alert title"),
message: NSLocalizedString("Settings were changed.", comment: "alert message"),
buttonTitle: NSLocalizedString("Save", comment: "alert button"),
buttonAction: { saveAAS($aas, $savedAAS) },
cancelButton: true
)
}
}
} else {
Text(String("Error opening address settings"))
}
if progressIndicator {
ProgressView().scaleEffect(2)
}
}
}
private func userAddressSettingsView() -> some View {
List {
Section {
shareWithContactsButton()
autoAcceptToggle().disabled(aas.business)
}
if aas.enable {
autoAcceptSection()
}
}
}
@@ -321,70 +414,70 @@ struct UserAddressView: View {
if ignoreShareViaProfileChange {
ignoreShareViaProfileChange = false
} else {
alert = .profileAddress(on: on)
if on {
showAlert(
NSLocalizedString("Share address with contacts?", comment: "alert title"),
message: NSLocalizedString("Profile update will be sent to your contacts.", comment: "alert message"),
actions: {[
UIAlertAction(
title: NSLocalizedString("Cancel", comment: "alert action"),
style: .default,
handler: { _ in
ignoreShareViaProfileChange = true
shareViaProfile = !on
}
),
UIAlertAction(
title: NSLocalizedString("Share", comment: "alert action"),
style: .default,
handler: { _ in
setProfileAddress($progressIndicator, on)
}
)
]}
)
} else {
showAlert(
NSLocalizedString("Stop sharing address?", comment: "alert title"),
message: NSLocalizedString("Profile update will be sent to your contacts.", comment: "alert message"),
actions: {[
UIAlertAction(
title: NSLocalizedString("Cancel", comment: "alert action"),
style: .default,
handler: { _ in
ignoreShareViaProfileChange = true
shareViaProfile = !on
}
),
UIAlertAction(
title: NSLocalizedString("Stop sharing", comment: "alert action"),
style: .default,
handler: { _ in
setProfileAddress($progressIndicator, on)
}
)
]}
)
}
}
}
}
}
private func setProfileAddress(_ on: Bool) {
progressIndicator = true
Task {
do {
if let u = try await apiSetProfileAddress(on: on) {
DispatchQueue.main.async {
chatModel.updateUser(u)
}
private func autoAcceptToggle() -> some View {
settingsRow("checkmark", color: theme.colors.secondary) {
Toggle("Auto-accept", isOn: $aas.enable)
.onChange(of: aas.enable) { _ in
saveAAS($aas, $savedAAS)
}
await MainActor.run { progressIndicator = false }
} catch let error {
logger.error("UserAddressView apiSetProfileAddress: \(responseError(error))")
await MainActor.run { progressIndicator = false }
}
}
}
private struct AutoAcceptState: Equatable {
var enable = false
var incognito = false
var welcomeText = ""
init(enable: Bool = false, incognito: Bool = false, welcomeText: String = "") {
self.enable = enable
self.incognito = incognito
self.welcomeText = welcomeText
}
init(userAddress: UserContactLink) {
if let aa = userAddress.autoAccept {
enable = true
incognito = aa.acceptIncognito
if let msg = aa.autoReply {
welcomeText = msg.text
} else {
welcomeText = ""
}
} else {
enable = false
incognito = false
welcomeText = ""
}
}
var autoAccept: AutoAccept? {
if enable {
var autoReply: MsgContent? = nil
let s = welcomeText.trimmingCharacters(in: .whitespacesAndNewlines)
if s != "" { autoReply = .text(s) }
return AutoAccept(acceptIncognito: incognito, autoReply: autoReply)
}
return nil
}
}
@ViewBuilder private func autoAcceptSection() -> some View {
private func autoAcceptSection() -> some View {
Section {
acceptIncognitoToggle()
if !aas.business {
acceptIncognitoToggle()
}
welcomeMessageEditor()
saveAASButton()
.disabled(aas == savedAAS)
@@ -424,22 +517,24 @@ struct UserAddressView: View {
private func saveAASButton() -> some View {
Button {
keyboardVisible = false
saveAAS()
saveAAS($aas, $savedAAS)
} label: {
Text("Save")
}
}
}
private func saveAAS() {
Task {
do {
if let address = try await userAddressAutoAccept(aas.autoAccept) {
chatModel.userAddress = address
savedAAS = aas
private func saveAAS(_ aas: Binding<AutoAcceptState>, _ savedAAS: Binding<AutoAcceptState>) {
Task {
do {
if let address = try await userAddressAutoAccept(aas.wrappedValue.autoAccept) {
await MainActor.run {
ChatModel.shared.userAddress = address
savedAAS.wrappedValue = aas.wrappedValue
}
} catch let error {
logger.error("userAddressAutoAccept error: \(responseError(error))")
}
} catch let error {
logger.error("userAddressAutoAccept error: \(responseError(error))")
}
}
}
@@ -197,7 +197,7 @@ struct UserProfilesView: View {
action()
} else {
authenticate(
reason: NSLocalizedString("Change user profiles", comment: "authentication reason")
reason: NSLocalizedString("Change chat profiles", comment: "authentication reason")
) { laResult in
switch laResult {
case .success, .unavailable:
@@ -187,23 +187,18 @@
<target state="needs-translation">)</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" approved="no">
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
<target state="translated">** إضافة جهة اتصال جديدة **: لإنشاء رمز QR لمرة واحدة أو رابط جهة الاتصال الخاصة بكم.</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" approved="no">
<source>**Create link / QR code** for your contact to use.</source>
<target state="translated">** أنشئ رابطًا / رمز QR ** لتستخدمه جهة الاتصال الخاصة بك.</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" 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>
<trans-unit id="**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." xml:space="preserve" approved="no">
<source>**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata.</source>
<target state="translated">** المزيد من الخصوصية **: تحققوا من الرسائل الجديدة كل 20 دقيقة. تتم مشاركة رمز الجهاز مع خادم SimpleX Chat ، ولكن ليس عدد جهات الاتصال أو الرسائل لديكم.</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" 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>
<trans-unit id="**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." xml:space="preserve" approved="no">
<source>**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app.</source>
<target state="translated">** الأكثر خصوصية **: لا تستخدم خادم إشعارات SimpleX Chat ، وتحقق من الرسائل بشكل دوري في الخلفية (يعتمد على عدد مرات استخدامكم للتطبيق).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -217,8 +212,8 @@
<target state="translated">** يرجى ملاحظة **: لن تتمكنوا من استعادة أو تغيير عبارة المرور إذا فقدتموها.</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" 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>
<trans-unit id="**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." xml:space="preserve" approved="no">
<source>**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from.</source>
<target state="translated">** موصى به **: يتم إرسال رمز الجهاز والإشعارات إلى خادم إشعارات SimpleX Chat ، ولكن ليس محتوى الرسالة أو حجمها أو مصدرها.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -1048,8 +1043,8 @@
<source>Direct messages</source>
<note>chat feature</note>
</trans-unit>
<trans-unit id="Direct messages between members are prohibited in this group." xml:space="preserve">
<source>Direct messages between members are prohibited in this group.</source>
<trans-unit id="Direct messages between members are prohibited." xml:space="preserve">
<source>Direct messages between members are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disable SimpleX Lock" xml:space="preserve">
@@ -1064,8 +1059,8 @@
<source>Disappearing messages are prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disappearing messages are prohibited in this group." xml:space="preserve">
<source>Disappearing messages are prohibited in this group.</source>
<trans-unit id="Disappearing messages are prohibited." xml:space="preserve">
<source>Disappearing messages are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disconnect" xml:space="preserve">
@@ -1428,16 +1423,16 @@
<source>Group members can irreversibly delete sent messages.</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>
<trans-unit id="Members can send direct messages." xml:space="preserve">
<source>Members can send direct messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send disappearing messages." xml:space="preserve">
<source>Group members can send disappearing messages.</source>
<trans-unit id="Members can send disappearing messages." xml:space="preserve">
<source>Members can send disappearing messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send voice messages." xml:space="preserve">
<source>Group members can send voice messages.</source>
<trans-unit id="Members can send voice messages." xml:space="preserve">
<source>Members can send voice messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group message:" xml:space="preserve">
@@ -1528,8 +1523,8 @@
<source>Image will be received when your contact is online, please wait or check later!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Immune to spam and abuse" xml:space="preserve">
<source>Immune to spam and abuse</source>
<trans-unit id="Immune to spam" xml:space="preserve">
<source>Immune to spam</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Import" xml:space="preserve">
@@ -1625,8 +1620,8 @@
<source>Irreversible message deletion is prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Irreversible message deletion is prohibited in this group." xml:space="preserve">
<source>Irreversible message deletion is prohibited in this group.</source>
<trans-unit id="Irreversible message deletion is prohibited." xml:space="preserve">
<source>Irreversible message deletion is prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It allows having many anonymous connections without any shared data between them in a single chat profile." xml:space="preserve">
@@ -1926,8 +1921,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>Onion hosts will not be used.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**.</source>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
@@ -1978,8 +1973,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>Open user profiles</source>
<note>authentication reason</note>
</trans-unit>
<trans-unit id="Open-source protocol and code anybody can run the servers." xml:space="preserve">
<source>Open-source protocol and code anybody can run the servers.</source>
<trans-unit id="Anybody can host servers." xml:space="preserve">
<source>Anybody can host servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Opening the link in the browser may reduce connection privacy and security. Untrusted SimpleX links will be red." xml:space="preserve">
@@ -2010,8 +2005,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>Paste the link you received into the box below to connect with your contact.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
<source>People can connect to you only via the links you share.</source>
<trans-unit id="You decide who can connect." xml:space="preserve">
<source>You decide who can connect.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Periodically" xml:space="preserve">
@@ -2590,8 +2585,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>Thanks to the users contribute via Weblate!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The 1st platform without any user identifiers private by design." xml:space="preserve">
<source>The 1st platform without any user identifiers private by design.</source>
<trans-unit id="No user identifiers." xml:space="preserve">
<source>No user identifiers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The app can notify you when you receive messages or contact requests - please open settings to enable." xml:space="preserve">
@@ -2622,8 +2617,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>The microphone does not work when the app is in the background.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The next generation of private messaging" xml:space="preserve">
<source>The next generation of private messaging</source>
<trans-unit id="The future of messaging" xml:space="preserve">
<source>The future of messaging</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The old database was not removed during the migration, it can be deleted." xml:space="preserve">
@@ -2686,8 +2681,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>To prevent the call interruption, enable Do Not Disturb mode.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." xml:space="preserve">
<source>To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts.</source>
<trans-unit id="To protect your privacy, SimpleX uses separate IDs for each of your contacts." xml:space="preserve">
<source>To protect your privacy, SimpleX uses separate IDs for each of your contacts.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect timezone, image/voice files use UTC." xml:space="preserve">
@@ -2860,8 +2855,8 @@ To connect, please ask your contact to create another connection link and check
<source>Voice messages are prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages are prohibited in this group." xml:space="preserve">
<source>Voice messages are prohibited in this group.</source>
<trans-unit id="Voice messages are prohibited." xml:space="preserve">
<source>Voice messages are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
@@ -2972,10 +2967,6 @@ To connect, please ask your contact to create another connection link and check
<source>You can use markdown to format messages:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You control through which server(s) **to receive** the messages, your contacts the servers you use to message them." xml:space="preserve">
<source>You control through which server(s) **to receive** the messages, your contacts the servers you use to message them.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You could not be verified; please try again." xml:space="preserve">
<source>You could not be verified; please try again.</source>
<note>No comment provided by engineer.</note>
@@ -3752,8 +3743,8 @@ SimpleX servers cannot see your profile.</source>
<source>%u messages skipped.</source>
<target state="translated">%u تم تخطي الرسائل.</target>
</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>
<trans-unit id="**Create 1-time link**: to create and share a new invitation link." xml:space="preserve" approved="no">
<source>**Create 1-time link**: to create and share a new invitation link.</source>
<target state="translated">**إضافة جهة اتصال**: لإنشاء رابط دعوة جديد، أو الاتصال عبر الرابط الذي تلقيتوهم.</target>
</trans-unit>
<trans-unit id="**Create group**: to create a new group." xml:space="preserve" approved="no">
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,6 @@
/* No comment provided by engineer. */
"_italic_" = "\\_italic_";
/* No comment provided by engineer. */
"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact.";
/* No comment provided by engineer. */
"*bold*" = "\\*bold*";
@@ -27,4 +24,3 @@
/* No comment provided by engineer. */
"No group!" = "Group not found!";
@@ -193,20 +193,16 @@
<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">
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
<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">
<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>
<trans-unit id="**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." xml:space="preserve">
<source>**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata.</source>
<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">
<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>
<trans-unit id="**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." xml:space="preserve">
<source>**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app.</source>
<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">
@@ -217,8 +213,8 @@
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
<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">
<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>
<trans-unit id="**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." xml:space="preserve">
<source>**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from.</source>
<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">
@@ -1251,8 +1247,8 @@
<source>Direct messages</source>
<note>chat feature</note>
</trans-unit>
<trans-unit id="Direct messages between members are prohibited in this group." xml:space="preserve">
<source>Direct messages between members are prohibited in this group.</source>
<trans-unit id="Direct messages between members are prohibited." xml:space="preserve">
<source>Direct messages between members are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disable SimpleX Lock" xml:space="preserve">
@@ -1271,8 +1267,8 @@
<source>Disappearing messages are prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disappearing messages are prohibited in this group." xml:space="preserve">
<source>Disappearing messages are prohibited in this group.</source>
<trans-unit id="Disappearing messages are prohibited." xml:space="preserve">
<source>Disappearing messages are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disappears at" xml:space="preserve">
@@ -1751,24 +1747,24 @@
<source>Group links</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can add message reactions." xml:space="preserve">
<source>Group members can add message reactions.</source>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can irreversibly delete sent messages." xml:space="preserve">
<source>Group members can irreversibly delete sent messages.</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>
<trans-unit id="Members can send direct messages." xml:space="preserve">
<source>Members can send direct messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send disappearing messages." xml:space="preserve">
<source>Group members can send disappearing messages.</source>
<trans-unit id="Members can send disappearing messages." xml:space="preserve">
<source>Members can send disappearing messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send voice messages." xml:space="preserve">
<source>Group members can send voice messages.</source>
<trans-unit id="Members can send voice messages." xml:space="preserve">
<source>Members can send voice messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group message:" xml:space="preserve">
@@ -1899,8 +1895,8 @@
<source>Immediately</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Immune to spam and abuse" xml:space="preserve">
<source>Immune to spam and abuse</source>
<trans-unit id="Immune to spam" xml:space="preserve">
<source>Immune to spam</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Import" xml:space="preserve">
@@ -2020,8 +2016,8 @@
<source>Irreversible message deletion is prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Irreversible message deletion is prohibited in this group." xml:space="preserve">
<source>Irreversible message deletion is prohibited in this group.</source>
<trans-unit id="Irreversible message deletion is prohibited." xml:space="preserve">
<source>Irreversible message deletion is prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It allows having many anonymous connections without any shared data between them in a single chat profile." xml:space="preserve">
@@ -2207,8 +2203,8 @@
<source>Message reactions are prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reactions are prohibited in this group." xml:space="preserve">
<source>Message reactions are prohibited in this group.</source>
<trans-unit id="Message reactions are prohibited." xml:space="preserve">
<source>Message reactions are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
@@ -2239,8 +2235,8 @@
<source>Migration is completed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Migrations: %@" xml:space="preserve">
<source>Migrations: %@</source>
<trans-unit id="Migrations:" xml:space="preserve">
<source>Migrations:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Moderate" xml:space="preserve">
@@ -2409,8 +2405,8 @@
<source>Onion hosts will not be used.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**.</source>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
@@ -2477,8 +2473,8 @@
<source>Open user profiles</source>
<note>authentication reason</note>
</trans-unit>
<trans-unit id="Open-source protocol and code anybody can run the servers." xml:space="preserve">
<source>Open-source protocol and code anybody can run the servers.</source>
<trans-unit id="Anybody can host servers." xml:space="preserve">
<source>Anybody can host servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Opening database…" xml:space="preserve">
@@ -2537,8 +2533,8 @@
<source>Paste the link you received into the box below to connect with your contact.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
<source>People can connect to you only via the links you share.</source>
<trans-unit id="You decide who can connect." xml:space="preserve">
<source>You decide who can connect.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Periodically" xml:space="preserve">
@@ -3373,8 +3369,8 @@
<source>Thanks to the users contribute via Weblate!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The 1st platform without any user identifiers private by design." xml:space="preserve">
<source>The 1st platform without any user identifiers private by design.</source>
<trans-unit id="No user identifiers." xml:space="preserve">
<source>No user identifiers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The ID of the next message is incorrect (less or equal to the previous).&#10;It can happen because of some bug or when the connection is compromised." xml:space="preserve">
@@ -3418,8 +3414,8 @@ It can happen because of some bug or when the connection is compromised.</source
<source>The message will be marked as moderated for all members.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The next generation of private messaging" xml:space="preserve">
<source>The next generation of private messaging</source>
<trans-unit id="The future of messaging" xml:space="preserve">
<source>The future of messaging</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The old database was not removed during the migration, it can be deleted." xml:space="preserve">
@@ -3490,8 +3486,8 @@ It can happen because of some bug or when the connection is compromised.</source
<source>To make a new connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." xml:space="preserve">
<source>To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts.</source>
<trans-unit id="To protect your privacy, SimpleX uses separate IDs for each of your contacts." xml:space="preserve">
<source>To protect your privacy, SimpleX uses separate IDs for each of your contacts.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect timezone, image/voice files use UTC." xml:space="preserve">
@@ -3724,8 +3720,8 @@ To connect, please ask your contact to create another connection link and check
<source>Voice messages are prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages are prohibited in this group." xml:space="preserve">
<source>Voice messages are prohibited in this group.</source>
<trans-unit id="Voice messages are prohibited." xml:space="preserve">
<source>Voice messages are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
@@ -3876,10 +3872,6 @@ To connect, please ask your contact to create another connection link and check
<source>You can't send messages!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You control through which server(s) **to receive** the messages, your contacts the servers you use to message them." xml:space="preserve">
<source>You control through which server(s) **to receive** the messages, your contacts the servers you use to message them.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You could not be verified; please try again." xml:space="preserve">
<source>You could not be verified; please try again.</source>
<note>No comment provided by engineer.</note>
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,6 @@
/* No comment provided by engineer. */
"_italic_" = "\\_italic_";
/* No comment provided by engineer. */
"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact.";
/* No comment provided by engineer. */
"*bold*" = "\\*bold*";
@@ -27,4 +24,3 @@
/* No comment provided by engineer. */
"No group!" = "Group not found!";
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,6 @@
/* No comment provided by engineer. */
"_italic_" = "\\_italic_";
/* No comment provided by engineer. */
"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact.";
/* No comment provided by engineer. */
"*bold*" = "\\*bold*";
@@ -27,4 +24,3 @@
/* No comment provided by engineer. */
"No group!" = "Group not found!";
@@ -186,20 +186,16 @@ 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">
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
<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">
<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>
<trans-unit id="**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." xml:space="preserve">
<source>**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata.</source>
<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">
<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>
<trans-unit id="**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." xml:space="preserve">
<source>**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app.</source>
<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">
@@ -210,8 +206,8 @@ Available in v5.1</source>
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
<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">
<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>
<trans-unit id="**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." xml:space="preserve">
<source>**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from.</source>
<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">
@@ -1128,8 +1124,8 @@ Available in v5.1</source>
<source>Direct messages</source>
<note>chat feature</note>
</trans-unit>
<trans-unit id="Direct messages between members are prohibited in this group." xml:space="preserve">
<source>Direct messages between members are prohibited in this group.</source>
<trans-unit id="Direct messages between members are prohibited." xml:space="preserve">
<source>Direct messages between members are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disable SimpleX Lock" xml:space="preserve">
@@ -1144,8 +1140,8 @@ Available in v5.1</source>
<source>Disappearing messages are prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disappearing messages are prohibited in this group." xml:space="preserve">
<source>Disappearing messages are prohibited in this group.</source>
<trans-unit id="Disappearing messages are prohibited." xml:space="preserve">
<source>Disappearing messages are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disconnect" xml:space="preserve">
@@ -1580,16 +1576,16 @@ Available in v5.1</source>
<source>Group members can irreversibly delete sent messages.</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>
<trans-unit id="Members can send direct messages." xml:space="preserve">
<source>Members can send direct messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send disappearing messages." xml:space="preserve">
<source>Group members can send disappearing messages.</source>
<trans-unit id="Members can send disappearing messages." xml:space="preserve">
<source>Members can send disappearing messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send voice messages." xml:space="preserve">
<source>Group members can send voice messages.</source>
<trans-unit id="Members can send voice messages." xml:space="preserve">
<source>Members can send voice messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group message:" xml:space="preserve">
@@ -1708,8 +1704,8 @@ Available in v5.1</source>
<source>Immediately</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Immune to spam and abuse" xml:space="preserve">
<source>Immune to spam and abuse</source>
<trans-unit id="Immune to spam" xml:space="preserve">
<source>Immune to spam</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Import" xml:space="preserve">
@@ -1821,8 +1817,8 @@ Available in v5.1</source>
<source>Irreversible message deletion is prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Irreversible message deletion is prohibited in this group." xml:space="preserve">
<source>Irreversible message deletion is prohibited in this group.</source>
<trans-unit id="Irreversible message deletion is prohibited." xml:space="preserve">
<source>Irreversible message deletion is prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It allows having many anonymous connections without any shared data between them in a single chat profile." xml:space="preserve">
@@ -2016,8 +2012,8 @@ Available in v5.1</source>
<source>Migration is completed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Migrations: %@" xml:space="preserve">
<source>Migrations: %@</source>
<trans-unit id="Migrations:" xml:space="preserve">
<source>Migrations:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Moderate" xml:space="preserve">
@@ -2174,8 +2170,8 @@ Available in v5.1</source>
<source>Onion hosts will not be used.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**.</source>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
@@ -2234,8 +2230,8 @@ Available in v5.1</source>
<source>Open user profiles</source>
<note>authentication reason</note>
</trans-unit>
<trans-unit id="Open-source protocol and code anybody can run the servers." xml:space="preserve">
<source>Open-source protocol and code anybody can run the servers.</source>
<trans-unit id="Anybody can host servers." xml:space="preserve">
<source>Anybody can host servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Opening the link in the browser may reduce connection privacy and security. Untrusted SimpleX links will be red." xml:space="preserve">
@@ -2290,8 +2286,8 @@ Available in v5.1</source>
<source>Paste the link you received into the box below to connect with your contact.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
<source>People can connect to you only via the links you share.</source>
<trans-unit id="You decide who can connect." xml:space="preserve">
<source>You decide who can connect.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Periodically" xml:space="preserve">
@@ -2994,8 +2990,8 @@ Available in v5.1</source>
<source>Thanks to the users contribute via Weblate!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The 1st platform without any user identifiers private by design." xml:space="preserve">
<source>The 1st platform without any user identifiers private by design.</source>
<trans-unit id="No user identifiers." xml:space="preserve">
<source>No user identifiers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The ID of the next message is incorrect (less or equal to the previous).&#10;It can happen because of some bug or when the connection is compromised." xml:space="preserve">
@@ -3039,8 +3035,8 @@ It can happen because of some bug or when the connection is compromised.</source
<source>The message will be marked as moderated for all members.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The next generation of private messaging" xml:space="preserve">
<source>The next generation of private messaging</source>
<trans-unit id="The future of messaging" xml:space="preserve">
<source>The future of messaging</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The old database was not removed during the migration, it can be deleted." xml:space="preserve">
@@ -3111,8 +3107,8 @@ It can happen because of some bug or when the connection is compromised.</source
<source>To make a new connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." xml:space="preserve">
<source>To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts.</source>
<trans-unit id="To protect your privacy, SimpleX uses separate IDs for each of your contacts." xml:space="preserve">
<source>To protect your privacy, SimpleX uses separate IDs for each of your contacts.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect timezone, image/voice files use UTC." xml:space="preserve">
@@ -3337,8 +3333,8 @@ To connect, please ask your contact to create another connection link and check
<source>Voice messages are prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages are prohibited in this group." xml:space="preserve">
<source>Voice messages are prohibited in this group.</source>
<trans-unit id="Voice messages are prohibited." xml:space="preserve">
<source>Voice messages are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
@@ -3478,10 +3474,6 @@ SimpleX Lock must be enabled.</source>
<source>You can't send messages!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You control through which server(s) **to receive** the messages, your contacts the servers you use to message them." xml:space="preserve">
<source>You control through which server(s) **to receive** the messages, your contacts the servers you use to message them.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You could not be verified; please try again." xml:space="preserve">
<source>You could not be verified; please try again.</source>
<note>No comment provided by engineer.</note>
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,6 @@
/* No comment provided by engineer. */
"_italic_" = "\\_italic_";
/* No comment provided by engineer. */
"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact.";
/* No comment provided by engineer. */
"*bold*" = "\\*bold*";
@@ -27,4 +24,3 @@
/* No comment provided by engineer. */
"No group!" = "Group not found!";
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,6 @@
/* No comment provided by engineer. */
"_italic_" = "\\_italic_";
/* No comment provided by engineer. */
"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact.";
/* No comment provided by engineer. */
"*bold*" = "\\*bold*";
@@ -27,4 +24,3 @@
/* No comment provided by engineer. */
"No group!" = "Group not found!";
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,6 @@
/* No comment provided by engineer. */
"_italic_" = "\\_italic_";
/* No comment provided by engineer. */
"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact.";
/* No comment provided by engineer. */
"*bold*" = "\\*bold*";
@@ -27,4 +24,3 @@
/* No comment provided by engineer. */
"No group!" = "Group not found!";
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,6 @@
/* No comment provided by engineer. */
"_italic_" = "\\_italic_";
/* No comment provided by engineer. */
"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact.";
/* No comment provided by engineer. */
"*bold*" = "\\*bold*";
@@ -27,4 +24,3 @@
/* No comment provided by engineer. */
"No group!" = "Group not found!";
@@ -217,23 +217,18 @@ Available in v5.1</source>
<target state="translated">)</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" approved="no">
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
<target state="translated">**הוסיפו איש קשר חדש**: ליצירת קוד QR או קישור חד־פעמיים עבור איש הקשר שלכם.</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" approved="no">
<source>**Create link / QR code** for your contact to use.</source>
<target state="translated">**צור קישור / קוד QR** לשימוש איש הקשר שלך.</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" 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>
<trans-unit id="**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." xml:space="preserve" approved="no">
<source>**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata.</source>
<target state="translated">**יותר פרטי**: בדוק הודעות חדשות כל 20 דקות. אסימון המכשיר משותף עם שרת SimpleX Chat, אך לא כמה אנשי קשר או הודעות יש לך.</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" 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>
<trans-unit id="**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." xml:space="preserve" approved="no">
<source>**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app.</source>
<target state="translated">**הכי פרטי**: אל תשתמש בשרת ההתראות של SimpleX Chat, בדוק הודעות מעת לעת ברקע (תלוי בתדירות השימוש באפליקציה).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -247,8 +242,8 @@ Available in v5.1</source>
<target state="translated">**שימו לב**: לא ניתן יהיה לשחזר או לשנות את הסיסמה אם תאבדו אותה.</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" 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>
<trans-unit id="**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." xml:space="preserve" approved="no">
<source>**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from.</source>
<target state="translated">**מומלץ**: אסימון מכשיר והתראות נשלחים לשרת ההתראות של SimpleX Chat, אך לא תוכן ההודעה, גודלה או ממי היא.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -1391,8 +1386,8 @@ Available in v5.1</source>
<target state="translated">הודעות ישירות</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="Direct messages between members are prohibited in this group." xml:space="preserve" approved="no">
<source>Direct messages between members are prohibited in this group.</source>
<trans-unit id="Direct messages between members are prohibited." xml:space="preserve" approved="no">
<source>Direct messages between members are prohibited.</source>
<target state="translated">הודעות ישירות בין חברי קבוצה אסורות בקבוצה זו.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -1411,8 +1406,8 @@ Available in v5.1</source>
<target state="translated">הודעות נעלמות אסורות בצ׳אט זה.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disappearing messages are prohibited in this group." xml:space="preserve" approved="no">
<source>Disappearing messages are prohibited in this group.</source>
<trans-unit id="Disappearing messages are prohibited." xml:space="preserve" approved="no">
<source>Disappearing messages are prohibited.</source>
<target state="translated">הודעות נעלמות אסורות בקבוצה זו.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -1956,18 +1951,18 @@ Available in v5.1</source>
<target state="translated">חברי הקבוצה יכולים למחוק באופן בלתי הפיך הודעות שנשלחו.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve" approved="no">
<source>Group members can send direct messages.</source>
<trans-unit id="Members can send direct messages." xml:space="preserve" approved="no">
<source>Members can send direct messages.</source>
<target state="translated">חברי הקבוצה יכולים לשלוח הודעות ישירות.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send disappearing messages." xml:space="preserve" approved="no">
<source>Group members can send disappearing messages.</source>
<trans-unit id="Members can send disappearing messages." xml:space="preserve" approved="no">
<source>Members can send disappearing messages.</source>
<target state="translated">חברי הקבוצה יכולים לשלוח הודעות נעלמות.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send voice messages." xml:space="preserve" approved="no">
<source>Group members can send voice messages.</source>
<trans-unit id="Members can send voice messages." xml:space="preserve" approved="no">
<source>Members can send voice messages.</source>
<target state="translated">חברי הקבוצה יכולים לשלוח הודעות קוליות.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -2115,8 +2110,8 @@ Available in v5.1</source>
<target state="translated">מיד</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Immune to spam and abuse" xml:space="preserve" approved="no">
<source>Immune to spam and abuse</source>
<trans-unit id="Immune to spam" xml:space="preserve" approved="no">
<source>Immune to spam</source>
<target state="translated">חסין מפני ספאם ושימוש לרעה</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -2257,8 +2252,8 @@ Available in v5.1</source>
<target state="translated">מחיקה בלתי הפיכה של הודעות אסורה בצ׳אט זה.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Irreversible message deletion is prohibited in this group." xml:space="preserve" approved="no">
<source>Irreversible message deletion is prohibited in this group.</source>
<trans-unit id="Irreversible message deletion is prohibited." xml:space="preserve" approved="no">
<source>Irreversible message deletion is prohibited.</source>
<target state="translated">מחיקה בלתי הפיכה של הודעות אסורה בקבוצה זו.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -2502,9 +2497,9 @@ Available in v5.1</source>
<target state="translated">ההעברה הושלמה</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Migrations: %@" xml:space="preserve" approved="no">
<source>Migrations: %@</source>
<target state="translated">העברות: %@</target>
<trans-unit id="Migrations:" xml:space="preserve" approved="no">
<source>Migrations:</source>
<target state="translated">העברות:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Moderate" xml:space="preserve" approved="no">
@@ -2701,8 +2696,8 @@ Available in v5.1</source>
<target state="translated">לא ייעשה שימוש במארחי Onion.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**.</source>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
@@ -2761,8 +2756,8 @@ Available in v5.1</source>
<source>Open user profiles</source>
<note>authentication reason</note>
</trans-unit>
<trans-unit id="Open-source protocol and code anybody can run the servers." xml:space="preserve">
<source>Open-source protocol and code anybody can run the servers.</source>
<trans-unit id="Anybody can host servers." xml:space="preserve">
<source>Anybody can host servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Opening the link in the browser may reduce connection privacy and security. Untrusted SimpleX links will be red." xml:space="preserve">
@@ -2817,8 +2812,8 @@ Available in v5.1</source>
<source>Paste the link you received into the box below to connect with your contact.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
<source>People can connect to you only via the links you share.</source>
<trans-unit id="You decide who can connect." xml:space="preserve">
<source>You decide who can connect.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Periodically" xml:space="preserve">
@@ -3521,8 +3516,8 @@ Available in v5.1</source>
<source>Thanks to the users contribute via Weblate!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The 1st platform without any user identifiers private by design." xml:space="preserve">
<source>The 1st platform without any user identifiers private by design.</source>
<trans-unit id="No user identifiers." xml:space="preserve">
<source>No user identifiers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The ID of the next message is incorrect (less or equal to the previous).&#10;It can happen because of some bug or when the connection is compromised." xml:space="preserve">
@@ -3566,8 +3561,8 @@ It can happen because of some bug or when the connection is compromised.</source
<source>The message will be marked as moderated for all members.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The next generation of private messaging" xml:space="preserve">
<source>The next generation of private messaging</source>
<trans-unit id="The future of messaging" xml:space="preserve">
<source>The future of messaging</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The old database was not removed during the migration, it can be deleted." xml:space="preserve">
@@ -3638,8 +3633,8 @@ It can happen because of some bug or when the connection is compromised.</source
<source>To make a new connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." xml:space="preserve">
<source>To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts.</source>
<trans-unit id="To protect your privacy, SimpleX uses separate IDs for each of your contacts." xml:space="preserve">
<source>To protect your privacy, SimpleX uses separate IDs for each of your contacts.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect timezone, image/voice files use UTC." xml:space="preserve">
@@ -3864,8 +3859,8 @@ To connect, please ask your contact to create another connection link and check
<source>Voice messages are prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages are prohibited in this group." xml:space="preserve">
<source>Voice messages are prohibited in this group.</source>
<trans-unit id="Voice messages are prohibited." xml:space="preserve">
<source>Voice messages are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
@@ -4005,10 +4000,6 @@ SimpleX Lock must be enabled.</source>
<source>You can't send messages!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You control through which server(s) **to receive** the messages, your contacts the servers you use to message them." xml:space="preserve">
<source>You control through which server(s) **to receive** the messages, your contacts the servers you use to message them.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You could not be verified; please try again." xml:space="preserve">
<source>You could not be verified; please try again.</source>
<note>No comment provided by engineer.</note>
@@ -4967,8 +4958,8 @@ SimpleX servers cannot see your profile.</source>
<target state="translated">נמחק</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media are prohibited in this group." xml:space="preserve" approved="no">
<source>Files and media are prohibited in this group.</source>
<trans-unit id="Files and media are prohibited." xml:space="preserve" approved="no">
<source>Files and media are prohibited.</source>
<target state="translated">קבצים ומדיה אסורים בקבוצה זו.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -5027,13 +5018,13 @@ SimpleX servers cannot see your profile.</source>
<target state="translated">הזמן חברים</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can add message reactions." xml:space="preserve" approved="no">
<source>Group members can add message reactions.</source>
<trans-unit id="Members can add message reactions." xml:space="preserve" approved="no">
<source>Members can add message reactions.</source>
<target state="translated">חברי הקבוצה יכולים להוסיף תגובות אמוג׳י להודעות.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send files and media." xml:space="preserve" approved="no">
<source>Group members can send files and media.</source>
<trans-unit id="Members can send files and media." xml:space="preserve" approved="no">
<source>Members can send files and media.</source>
<target state="translated">חברי הקבוצה יכולים לשלוח קבצים ומדיה.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -5231,8 +5222,8 @@ SimpleX servers cannot see your profile.</source>
<target state="translated">תגובות אמוג׳י להודעות אסורות בצ׳אט זה.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reactions are prohibited in this group." xml:space="preserve" approved="no">
<source>Message reactions are prohibited in this group.</source>
<trans-unit id="Message reactions are prohibited." xml:space="preserve" approved="no">
<source>Message reactions are prohibited.</source>
<target state="translated">תגובות אמוג׳י להודעות אסורות בקבוצה זו.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -181,23 +181,18 @@
<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" approved="no">
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
<target state="needs-translation">**Dodajte novi kontakt**: da biste stvorili svoj jednokratni QR kôd ili vezu za svoj kontakt.</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" approved="no">
<source>**Create link / QR code** for your contact to use.</source>
<target state="needs-translation">**Stvorite vezu / QR kôd** za vaš kontakt.</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" 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>
<trans-unit id="**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." xml:space="preserve" approved="no">
<source>**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata.</source>
<target state="needs-translation">**Privatnije**: provjeravajte nove poruke svakih 20 minuta. Token uređaja dijeli se s SimpleX Chat poslužiteljem, ali ne i s brojem kontakata ili poruka koje imate.</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" 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>
<trans-unit id="**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." xml:space="preserve" approved="no">
<source>**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app.</source>
<target state="needs-translation">**Najprivatniji**: nemojte koristiti SimpleX Chat poslužitelj obavijesti, povremeno provjeravajte poruke u pozadini (ovisi o tome koliko često koristite aplikaciju).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -211,8 +206,8 @@
<target state="needs-translation">**Imajte na umu**: NEĆETE moći oporaviti ili promijeniti pristupni izraz ako ga izgubite.</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" 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>
<trans-unit id="**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." xml:space="preserve" approved="no">
<source>**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from.</source>
<target state="needs-translation">**Preporučeno**: token uređaja i obavijesti šalju se na poslužitelj obavijesti SimpleX Chata, ali ne i sadržaj poruke, veličinu ili od koga je.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -1039,8 +1034,8 @@
<source>Direct messages</source>
<note>chat feature</note>
</trans-unit>
<trans-unit id="Direct messages between members are prohibited in this group." xml:space="preserve">
<source>Direct messages between members are prohibited in this group.</source>
<trans-unit id="Direct messages between members are prohibited." xml:space="preserve">
<source>Direct messages between members are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disable SimpleX Lock" xml:space="preserve">
@@ -1055,8 +1050,8 @@
<source>Disappearing messages are prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disappearing messages are prohibited in this group." xml:space="preserve">
<source>Disappearing messages are prohibited in this group.</source>
<trans-unit id="Disappearing messages are prohibited." xml:space="preserve">
<source>Disappearing messages are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disconnect" xml:space="preserve">
@@ -1419,16 +1414,16 @@
<source>Group members can irreversibly delete sent messages.</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>
<trans-unit id="Members can send direct messages." xml:space="preserve">
<source>Members can send direct messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send disappearing messages." xml:space="preserve">
<source>Group members can send disappearing messages.</source>
<trans-unit id="Members can send disappearing messages." xml:space="preserve">
<source>Members can send disappearing messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send voice messages." xml:space="preserve">
<source>Group members can send voice messages.</source>
<trans-unit id="Members can send voice messages." xml:space="preserve">
<source>Members can send voice messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group message:" xml:space="preserve">
@@ -1519,8 +1514,8 @@
<source>Image will be received when your contact is online, please wait or check later!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Immune to spam and abuse" xml:space="preserve">
<source>Immune to spam and abuse</source>
<trans-unit id="Immune to spam" xml:space="preserve">
<source>Immune to spam</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Import" xml:space="preserve">
@@ -1616,8 +1611,8 @@
<source>Irreversible message deletion is prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Irreversible message deletion is prohibited in this group." xml:space="preserve">
<source>Irreversible message deletion is prohibited in this group.</source>
<trans-unit id="Irreversible message deletion is prohibited." xml:space="preserve">
<source>Irreversible message deletion is prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It allows having many anonymous connections without any shared data between them in a single chat profile." xml:space="preserve">
@@ -1917,8 +1912,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>Onion hosts will not be used.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**.</source>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
@@ -1965,8 +1960,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>Open chat console</source>
<note>authentication reason</note>
</trans-unit>
<trans-unit id="Open-source protocol and code anybody can run the servers." xml:space="preserve">
<source>Open-source protocol and code anybody can run the servers.</source>
<trans-unit id="Anybody can host servers." xml:space="preserve">
<source>Anybody can host servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Opening the link in the browser may reduce connection privacy and security. Untrusted SimpleX links will be red." xml:space="preserve">
@@ -1997,8 +1992,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>Paste the link you received into the box below to connect with your contact.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
<source>People can connect to you only via the links you share.</source>
<trans-unit id="You decide who can connect." xml:space="preserve">
<source>You decide who can connect.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Periodically" xml:space="preserve">
@@ -2577,8 +2572,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>Thanks to the users contribute via Weblate!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The 1st platform without any user identifiers private by design." xml:space="preserve">
<source>The 1st platform without any user identifiers private by design.</source>
<trans-unit id="No user identifiers." xml:space="preserve">
<source>No user identifiers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The app can notify you when you receive messages or contact requests - please open settings to enable." xml:space="preserve">
@@ -2609,8 +2604,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>The microphone does not work when the app is in the background.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The next generation of private messaging" xml:space="preserve">
<source>The next generation of private messaging</source>
<trans-unit id="The future of messaging" xml:space="preserve">
<source>The future of messaging</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The old database was not removed during the migration, it can be deleted." xml:space="preserve">
@@ -2673,8 +2668,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>To prevent the call interruption, enable Do Not Disturb mode.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." xml:space="preserve">
<source>To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts.</source>
<trans-unit id="To protect your privacy, SimpleX uses separate IDs for each of your contacts." xml:space="preserve">
<source>To protect your privacy, SimpleX uses separate IDs for each of your contacts.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect timezone, image/voice files use UTC." xml:space="preserve">
@@ -2847,8 +2842,8 @@ To connect, please ask your contact to create another connection link and check
<source>Voice messages are prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages are prohibited in this group." xml:space="preserve">
<source>Voice messages are prohibited in this group.</source>
<trans-unit id="Voice messages are prohibited." xml:space="preserve">
<source>Voice messages are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
@@ -2959,10 +2954,6 @@ To connect, please ask your contact to create another connection link and check
<source>You can use markdown to format messages:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You control through which server(s) **to receive** the messages, your contacts the servers you use to message them." xml:space="preserve">
<source>You control through which server(s) **to receive** the messages, your contacts the servers you use to message them.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You could not be verified; please try again." xml:space="preserve">
<source>You could not be verified; please try again.</source>
<note>No comment provided by engineer.</note>
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,6 @@
/* No comment provided by engineer. */
"_italic_" = "\\_italic_";
/* No comment provided by engineer. */
"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact.";
/* No comment provided by engineer. */
"*bold*" = "\\*bold*";
@@ -27,4 +24,3 @@
/* No comment provided by engineer. */
"No group!" = "Group not found!";
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,6 @@
/* No comment provided by engineer. */
"_italic_" = "\\_italic_";
/* No comment provided by engineer. */
"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact.";
/* No comment provided by engineer. */
"*bold*" = "\\*bold*";
@@ -27,4 +24,3 @@
/* No comment provided by engineer. */
"No group!" = "Group not found!";
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,6 @@
/* No comment provided by engineer. */
"_italic_" = "\\_italic_";
/* No comment provided by engineer. */
"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact.";
/* No comment provided by engineer. */
"*bold*" = "\\*bold*";
@@ -27,4 +24,3 @@
/* No comment provided by engineer. */
"No group!" = "Group not found!";
@@ -152,20 +152,16 @@
<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">
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
<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">
<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>
<trans-unit id="**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." xml:space="preserve">
<source>**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata.</source>
<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">
<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>
<trans-unit id="**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." xml:space="preserve">
<source>**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app.</source>
<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">
@@ -176,8 +172,8 @@
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
<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">
<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>
<trans-unit id="**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." xml:space="preserve">
<source>**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from.</source>
<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">
@@ -1013,8 +1009,8 @@
<source>Direct messages</source>
<note>chat feature</note>
</trans-unit>
<trans-unit id="Direct messages between members are prohibited in this group." xml:space="preserve">
<source>Direct messages between members are prohibited in this group.</source>
<trans-unit id="Direct messages between members are prohibited." xml:space="preserve">
<source>Direct messages between members are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disable SimpleX Lock" xml:space="preserve">
@@ -1029,8 +1025,8 @@
<source>Disappearing messages are prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disappearing messages are prohibited in this group." xml:space="preserve">
<source>Disappearing messages are prohibited in this group.</source>
<trans-unit id="Disappearing messages are prohibited." xml:space="preserve">
<source>Disappearing messages are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disconnect" xml:space="preserve">
@@ -1421,16 +1417,16 @@
<source>Group members can irreversibly delete sent messages.</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>
<trans-unit id="Members can send direct messages." xml:space="preserve">
<source>Members can send direct messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send disappearing messages." xml:space="preserve">
<source>Group members can send disappearing messages.</source>
<trans-unit id="Members can send disappearing messages." xml:space="preserve">
<source>Members can send disappearing messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send voice messages." xml:space="preserve">
<source>Group members can send voice messages.</source>
<trans-unit id="Members can send voice messages." xml:space="preserve">
<source>Members can send voice messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group message:" xml:space="preserve">
@@ -1537,8 +1533,8 @@
<source>Image will be received when your contact is online, please wait or check later!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Immune to spam and abuse" xml:space="preserve">
<source>Immune to spam and abuse</source>
<trans-unit id="Immune to spam" xml:space="preserve">
<source>Immune to spam</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Import" xml:space="preserve">
@@ -1642,8 +1638,8 @@
<source>Irreversible message deletion is prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Irreversible message deletion is prohibited in this group." xml:space="preserve">
<source>Irreversible message deletion is prohibited in this group.</source>
<trans-unit id="Irreversible message deletion is prohibited." xml:space="preserve">
<source>Irreversible message deletion is prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It allows having many anonymous connections without any shared data between them in a single chat profile." xml:space="preserve">
@@ -1961,8 +1957,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>Onion hosts will not be used.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**.</source>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
@@ -2013,8 +2009,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>Open user profiles</source>
<note>authentication reason</note>
</trans-unit>
<trans-unit id="Open-source protocol and code anybody can run the servers." xml:space="preserve">
<source>Open-source protocol and code anybody can run the servers.</source>
<trans-unit id="Anybody can host servers." xml:space="preserve">
<source>Anybody can host servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Opening the link in the browser may reduce connection privacy and security. Untrusted SimpleX links will be red." xml:space="preserve">
@@ -2049,8 +2045,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>Paste the link you received into the box below to connect with your contact.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
<source>People can connect to you only via the links you share.</source>
<trans-unit id="You decide who can connect." xml:space="preserve">
<source>You decide who can connect.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Periodically" xml:space="preserve">
@@ -2670,8 +2666,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>Thanks to the users contribute via Weblate!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The 1st platform without any user identifiers private by design." xml:space="preserve">
<source>The 1st platform without any user identifiers private by design.</source>
<trans-unit id="No user identifiers." xml:space="preserve">
<source>No user identifiers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The app can notify you when you receive messages or contact requests - please open settings to enable." xml:space="preserve">
@@ -2706,8 +2702,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>The message will be marked as moderated for all members.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The next generation of private messaging" xml:space="preserve">
<source>The next generation of private messaging</source>
<trans-unit id="The future of messaging" xml:space="preserve">
<source>The future of messaging</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The old database was not removed during the migration, it can be deleted." xml:space="preserve">
@@ -2774,8 +2770,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>To make a new connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." xml:space="preserve">
<source>To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts.</source>
<trans-unit id="To protect your privacy, SimpleX uses separate IDs for each of your contacts." xml:space="preserve">
<source>To protect your privacy, SimpleX uses separate IDs for each of your contacts.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect timezone, image/voice files use UTC." xml:space="preserve">
@@ -2968,8 +2964,8 @@ To connect, please ask your contact to create another connection link and check
<source>Voice messages are prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages are prohibited in this group." xml:space="preserve">
<source>Voice messages are prohibited in this group.</source>
<trans-unit id="Voice messages are prohibited." xml:space="preserve">
<source>Voice messages are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
@@ -3093,10 +3089,6 @@ SimpleX Lock must be enabled.</source>
<source>You can't send messages!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You control through which server(s) **to receive** the messages, your contacts the servers you use to message them." xml:space="preserve">
<source>You control through which server(s) **to receive** the messages, your contacts the servers you use to message them.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You could not be verified; please try again." xml:space="preserve">
<source>You could not be verified; please try again.</source>
<note>No comment provided by engineer.</note>
@@ -162,20 +162,16 @@
<target state="translated">)</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">
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
<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">
<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>
<trans-unit id="**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." xml:space="preserve">
<source>**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata.</source>
<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">
<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>
<trans-unit id="**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." xml:space="preserve">
<source>**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app.</source>
<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">
@@ -187,8 +183,8 @@
<target state="translated">**Turėkite omenyje**: jeigu prarasite slaptafrazę, NEBEGALĖSITE jos atkurti ar pakeisti.</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">
<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>
<trans-unit id="**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." xml:space="preserve">
<source>**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from.</source>
<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">
@@ -1033,8 +1029,8 @@
<source>Direct messages</source>
<note>chat feature</note>
</trans-unit>
<trans-unit id="Direct messages between members are prohibited in this group." xml:space="preserve">
<source>Direct messages between members are prohibited in this group.</source>
<trans-unit id="Direct messages between members are prohibited." xml:space="preserve">
<source>Direct messages between members are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disable SimpleX Lock" xml:space="preserve">
@@ -1049,8 +1045,8 @@
<source>Disappearing messages are prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disappearing messages are prohibited in this group." xml:space="preserve">
<source>Disappearing messages are prohibited in this group.</source>
<trans-unit id="Disappearing messages are prohibited." xml:space="preserve">
<source>Disappearing messages are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disconnect" xml:space="preserve">
@@ -1417,16 +1413,16 @@
<source>Group members can irreversibly delete sent messages.</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>
<trans-unit id="Members can send direct messages." xml:space="preserve">
<source>Members can send direct messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send disappearing messages." xml:space="preserve">
<source>Group members can send disappearing messages.</source>
<trans-unit id="Members can send disappearing messages." xml:space="preserve">
<source>Members can send disappearing messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send voice messages." xml:space="preserve">
<source>Group members can send voice messages.</source>
<trans-unit id="Members can send voice messages." xml:space="preserve">
<source>Members can send voice messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group message:" xml:space="preserve">
@@ -1513,8 +1509,8 @@
<source>Image will be received when your contact is online, please wait or check later!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Immune to spam and abuse" xml:space="preserve">
<source>Immune to spam and abuse</source>
<trans-unit id="Immune to spam" xml:space="preserve">
<source>Immune to spam</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Import" xml:space="preserve">
@@ -1614,8 +1610,8 @@
<source>Irreversible message deletion is prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Irreversible message deletion is prohibited in this group." xml:space="preserve">
<source>Irreversible message deletion is prohibited in this group.</source>
<trans-unit id="Irreversible message deletion is prohibited." xml:space="preserve">
<source>Irreversible message deletion is prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It allows having many anonymous connections without any shared data between them in a single chat profile." xml:space="preserve">
@@ -1919,8 +1915,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>Onion hosts will not be used.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**.</source>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
@@ -1971,8 +1967,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>Open user profiles</source>
<note>authentication reason</note>
</trans-unit>
<trans-unit id="Open-source protocol and code anybody can run the servers." xml:space="preserve">
<source>Open-source protocol and code anybody can run the servers.</source>
<trans-unit id="Anybody can host servers." xml:space="preserve">
<source>Anybody can host servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Opening the link in the browser may reduce connection privacy and security. Untrusted SimpleX links will be red." xml:space="preserve">
@@ -2003,8 +1999,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>Paste the link you received into the box below to connect with your contact.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
<source>People can connect to you only via the links you share.</source>
<trans-unit id="You decide who can connect." xml:space="preserve">
<source>You decide who can connect.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Periodically" xml:space="preserve">
@@ -2591,8 +2587,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>Thanks to the users contribute via Weblate!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The 1st platform without any user identifiers private by design." xml:space="preserve">
<source>The 1st platform without any user identifiers private by design.</source>
<trans-unit id="No user identifiers." xml:space="preserve">
<source>No user identifiers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The app can notify you when you receive messages or contact requests - please open settings to enable." xml:space="preserve">
@@ -2627,8 +2623,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>The message will be marked as moderated for all members.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The next generation of private messaging" xml:space="preserve">
<source>The next generation of private messaging</source>
<trans-unit id="The future of messaging" xml:space="preserve">
<source>The future of messaging</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The old database was not removed during the migration, it can be deleted." xml:space="preserve">
@@ -2687,8 +2683,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>To make a new connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." xml:space="preserve">
<source>To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts.</source>
<trans-unit id="To protect your privacy, SimpleX uses separate IDs for each of your contacts." xml:space="preserve">
<source>To protect your privacy, SimpleX uses separate IDs for each of your contacts.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect timezone, image/voice files use UTC." xml:space="preserve">
@@ -2873,8 +2869,8 @@ To connect, please ask your contact to create another connection link and check
<source>Voice messages are prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages are prohibited in this group." xml:space="preserve">
<source>Voice messages are prohibited in this group.</source>
<trans-unit id="Voice messages are prohibited." xml:space="preserve">
<source>Voice messages are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
@@ -2993,10 +2989,6 @@ To connect, please ask your contact to create another connection link and check
<source>You can't send messages!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You control through which server(s) **to receive** the messages, your contacts the servers you use to message them." xml:space="preserve">
<source>You control through which server(s) **to receive** the messages, your contacts the servers you use to message them.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You could not be verified; please try again." xml:space="preserve">
<source>You could not be verified; please try again.</source>
<note>No comment provided by engineer.</note>
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,6 @@
/* No comment provided by engineer. */
"_italic_" = "\\_italic_";
/* No comment provided by engineer. */
"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact.";
/* No comment provided by engineer. */
"*bold*" = "\\*bold*";
@@ -27,4 +24,3 @@
/* No comment provided by engineer. */
"No group!" = "Group not found!";
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,6 @@
/* No comment provided by engineer. */
"_italic_" = "\\_italic_";
/* No comment provided by engineer. */
"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact.";
/* No comment provided by engineer. */
"*bold*" = "\\*bold*";
@@ -27,4 +24,3 @@
/* No comment provided by engineer. */
"No group!" = "Group not found!";
@@ -187,23 +187,18 @@
<target state="translated">)</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" 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 ou link único 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" approved="no">
<source>**Create link / QR code** for your contact to use.</source>
<target state="translated">**Crie um link / QR code** para seu contato usar.</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" 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>
<trans-unit id="**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." xml:space="preserve" approved="no">
<source>**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata.</source>
<target state="translated">**Mais privado**: verifique as novas mensagens a cada 20 minutos. O token do dispositivo é compartilhado com o servidor do SimpleX Chat, mas não quantos contatos ou mensagens você tem.</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" 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>
<trans-unit id="**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." xml:space="preserve" approved="no">
<source>**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app.</source>
<target state="translated">**Mais 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>
@@ -217,8 +212,8 @@
<target state="translated">**Observação**: NÃO será possível recuperar ou alterar a frase secreta se você a perder.</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" 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>
<trans-unit id="**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." xml:space="preserve" approved="no">
<source>**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see 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 para o servidor de notificações do SimpleX Chat, mas não o conteúdo, o tamanho ou o remetente da mensagem.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -1209,8 +1204,8 @@
<target state="translated">Mensagens diretas</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="Direct messages between members are prohibited in this group." xml:space="preserve" approved="no">
<source>Direct messages between members are prohibited in this group.</source>
<trans-unit id="Direct messages between members are prohibited." xml:space="preserve" approved="no">
<source>Direct messages between members are prohibited.</source>
<target state="translated">Mensagens diretas entre membros são proibidas neste grupo.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -1229,8 +1224,8 @@
<target state="translated">Mensagens temporárias são proibidas nesse chat.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disappearing messages are prohibited in this group." xml:space="preserve" approved="no">
<source>Disappearing messages are prohibited in this group.</source>
<trans-unit id="Disappearing messages are prohibited." xml:space="preserve" approved="no">
<source>Disappearing messages are prohibited.</source>
<target state="translated">Mensagens que temporárias são proibidas neste grupo.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -1643,18 +1638,18 @@
<target state="translated">Os membros do grupo podem excluir mensagens enviadas de forma irreversível.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve" approved="no">
<source>Group members can send direct messages.</source>
<trans-unit id="Members can send direct messages." xml:space="preserve" approved="no">
<source>Members can send direct messages.</source>
<target state="translated">Os membros do grupo podem enviar DMs.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send disappearing messages." xml:space="preserve" approved="no">
<source>Group members can send disappearing messages.</source>
<trans-unit id="Members can send disappearing messages." xml:space="preserve" approved="no">
<source>Members can send disappearing messages.</source>
<target state="translated">Os membros do grupo podem enviar mensagens que desaparecem.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send voice messages." xml:space="preserve" approved="no">
<source>Group members can send voice messages.</source>
<trans-unit id="Members can send voice messages." xml:space="preserve" approved="no">
<source>Members can send voice messages.</source>
<target state="translated">Os membros do grupo podem enviar mensagens de voz.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -1761,8 +1756,8 @@
<target state="translated">A imagem será recebida quando seu contato estiver online, aguarde ou verifique mais tarde!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Immune to spam and abuse" xml:space="preserve" approved="no">
<source>Immune to spam and abuse</source>
<trans-unit id="Immune to spam" xml:space="preserve" approved="no">
<source>Immune to spam</source>
<target state="translated">Imune a spam e abuso</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -1878,8 +1873,8 @@
<target state="translated">A exclusão irreversível de mensagens é proibida neste chat.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Irreversible message deletion is prohibited in this group." xml:space="preserve" approved="no">
<source>Irreversible message deletion is prohibited in this group.</source>
<trans-unit id="Irreversible message deletion is prohibited." xml:space="preserve" approved="no">
<source>Irreversible message deletion is prohibited.</source>
<target state="translated">A exclusão irreversível de mensagens é proibida neste grupo.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -2209,8 +2204,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<target state="translated">Hosts Onion não serão usados.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**.</source>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve" approved="no">
@@ -2267,8 +2262,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<target state="translated">Abrir console de chat</target>
<note>authentication reason</note>
</trans-unit>
<trans-unit id="Open-source protocol and code anybody can run the servers." xml:space="preserve" approved="no">
<source>Open-source protocol and code anybody can run the servers.</source>
<trans-unit id="Anybody can host servers." xml:space="preserve" approved="no">
<source>Anybody can host servers.</source>
<target state="translated">Protocolo de código aberto qualquer um pode executar os servidores.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -2306,8 +2301,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<target state="translated">Cole o link que você recebeu na caixa abaixo para conectar com o seu contato.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve" approved="no">
<source>People can connect to you only via the links you share.</source>
<trans-unit id="You decide who can connect." xml:space="preserve" approved="no">
<source>You decide who can connect.</source>
<target state="translated">Pessoas podem se conectar com você somente via links compartilhados.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -2961,8 +2956,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>Thank you for installing SimpleX Chat!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The 1st platform without any user identifiers private by design." xml:space="preserve" approved="no">
<source>The 1st platform without any user identifiers private by design.</source>
<trans-unit id="No user identifiers." xml:space="preserve" approved="no">
<source>No user identifiers.</source>
<target state="translated">A 1ª plataforma sem nenhum identificador de usuário privada por design.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -2998,8 +2993,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>The microphone does not work when the app is in the background.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The next generation of private messaging" xml:space="preserve" approved="no">
<source>The next generation of private messaging</source>
<trans-unit id="The future of messaging" xml:space="preserve" approved="no">
<source>The future of messaging</source>
<target state="translated">A próxima geração de mensageiros privados</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -3071,8 +3066,8 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>To prevent the call interruption, enable Do Not Disturb mode.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." xml:space="preserve">
<source>To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts.</source>
<trans-unit id="To protect your privacy, SimpleX uses separate IDs for each of your contacts." xml:space="preserve">
<source>To protect your privacy, SimpleX uses separate IDs for each of your contacts.</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" approved="no">
@@ -3274,8 +3269,8 @@ Para se conectar, peça ao seu contato para criar outro link de conexão e verif
<target state="translated">Mensagens de voz são proibidas neste chat.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages are prohibited in this group." xml:space="preserve" approved="no">
<source>Voice messages are prohibited in this group.</source>
<trans-unit id="Voice messages are prohibited." xml:space="preserve" approved="no">
<source>Voice messages are prohibited.</source>
<target state="translated">Mensagens de voz são proibidas neste grupo.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -3402,10 +3397,6 @@ Para se conectar, peça ao seu contato para criar outro link de conexão e verif
<target state="translated">Você pode usar markdown para formatar mensagens:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You control through which server(s) **to receive** the messages, your contacts the servers you use to message them." xml:space="preserve">
<source>You control through which server(s) **to receive** the messages, your contacts the servers you use to message them.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You could not be verified; please try again." xml:space="preserve" approved="no">
<source>You could not be verified; please try again.</source>
<target state="translated">Você não pôde ser verificado; por favor, tente novamente.</target>
@@ -5434,8 +5425,8 @@ Isso pode acontecer por causa de algum bug ou quando a conexão está comprometi
<source>Advanced settings</source>
<target state="translated">Configurações avançadas</target>
</trans-unit>
<trans-unit id="All data is private to your device." xml:space="preserve" approved="no">
<source>All data is private to your device.</source>
<trans-unit id="All data is kept private on your device." xml:space="preserve" approved="no">
<source>All data is kept private on your device.</source>
<target state="translated">Toda informação é privada em seu dispositivo.</target>
</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">
@@ -5482,8 +5473,8 @@ Isso pode acontecer por causa de algum bug ou quando a conexão está comprometi
<source>(this device v%@)</source>
<target state="translated">este dispositivo</target>
</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>
<trans-unit id="**Create 1-time link**: to create and share a new invitation link." xml:space="preserve" approved="no">
<source>**Create 1-time link**: to create and share a new invitation link.</source>
<target state="translated">**Adicionar contato**: criar um novo link de convite ou conectar via um link que você recebeu.</target>
</trans-unit>
<trans-unit id="**Create group**: to create a new group." xml:space="preserve" approved="no">
@@ -5570,6 +5561,90 @@ Isso pode acontecer por causa de algum bug ou quando a conexão está comprometi
<source>Capacity exceeded - recipient did not receive previously sent messages.</source>
<target state="translated">Capacidade excedida - o destinatário não recebeu as mensagens enviadas anteriormente.</target>
</trans-unit>
<trans-unit id="Chat migrated!" xml:space="preserve" approved="no">
<source>Chat migrated!</source>
<target state="translated">Conversa migrada!</target>
</trans-unit>
<trans-unit id="Auto-accept settings" xml:space="preserve" approved="no">
<source>Auto-accept settings</source>
<target state="translated">Aceitar automaticamente configurações</target>
</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 videos).</target>
</trans-unit>
<trans-unit id="App session" xml:space="preserve" approved="no">
<source>App session</source>
<target state="translated">Sessão do aplicativo</target>
</trans-unit>
<trans-unit id="Acknowledged" xml:space="preserve" approved="no">
<source>Acknowledged</source>
<target state="translated">Reconhecido</target>
</trans-unit>
<trans-unit id="Acknowledgement errors" xml:space="preserve" approved="no">
<source>Acknowledgement errors</source>
<target state="translated">Erros conhecidos</target>
</trans-unit>
<trans-unit id="Chat list" xml:space="preserve" approved="no">
<source>Chat list</source>
<target state="translated">Lista de conversas</target>
</trans-unit>
<trans-unit id="Chat database exported" xml:space="preserve" approved="no">
<source>Chat database exported</source>
<target state="translated">Banco de dados da conversa exportado</target>
</trans-unit>
<trans-unit id="Chat preferences were changed." xml:space="preserve" approved="no">
<source>Chat preferences were changed.</source>
<target state="translated">As preferências de bate-papo foram alteradas.</target>
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve" approved="no">
<source>Chat theme</source>
<target state="translated">Tema da conversa</target>
</trans-unit>
<trans-unit id="Better calls" xml:space="preserve" approved="no">
<source>Better calls</source>
<target state="translated">Chamadas melhores</target>
</trans-unit>
<trans-unit id="Better user experience" xml:space="preserve" approved="no">
<source>Better user experience</source>
<target state="translated">Melhor experiência do usuário</target>
</trans-unit>
<trans-unit id="Allow downgrade" xml:space="preserve" approved="no">
<source>Allow downgrade</source>
<target state="translated">Permitir redução</target>
</trans-unit>
<trans-unit id="Additional secondary" xml:space="preserve" approved="no">
<source>Additional secondary</source>
<target state="translated">Secundária adicional</target>
</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 do aplicativo</target>
</trans-unit>
<trans-unit id="Archive and upload" xml:space="preserve" approved="no">
<source>Archive and upload</source>
<target state="translated">Arquivar e enviar</target>
</trans-unit>
<trans-unit id="Background" xml:space="preserve" approved="no">
<source>Background</source>
<target state="translated">Fundo</target>
</trans-unit>
<trans-unit id="Better message dates." xml:space="preserve" approved="no">
<source>Better message dates.</source>
<target state="translated">Datas de mensagens melhores.</target>
</trans-unit>
<trans-unit id="Better notifications" xml:space="preserve" approved="no">
<source>Better notifications</source>
<target state="translated">Notificações melhores</target>
</trans-unit>
<trans-unit id="Better security ✅" xml:space="preserve" approved="no">
<source>Better security ✅</source>
<target state="translated">Melhor segurança ✅</target>
</trans-unit>
<trans-unit id="Chat profile" xml:space="preserve" approved="no">
<source>Chat profile</source>
<target state="translated">Perfil da conversa</target>
</trans-unit>
</body>
</file>
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="pt-BR" datatype="plaintext">
@@ -214,22 +214,17 @@ 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" 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" 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>
<trans-unit id="**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata." xml:space="preserve" approved="no">
<source>**More private**: check new messages every 20 minutes. Only device token is shared with our push server. It doesn't see how many contacts you have, or any message metadata.</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" 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>
<trans-unit id="**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending on how often you use the app." xml:space="preserve" approved="no">
<source>**Most private**: do not use SimpleX Chat push server. The app will check messages in background, when the system allows it, depending 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>
@@ -242,8 +237,8 @@ Available in v5.1</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" 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>
<trans-unit id="**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see the message content, size or who it is from." xml:space="preserve" approved="no">
<source>**Recommended**: device token and end-to-end encrypted notifications are sent to SimpleX Chat push server, but it does not see 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>
@@ -1232,8 +1227,8 @@ Available in v5.1</source>
<source>Direct messages</source>
<note>chat feature</note>
</trans-unit>
<trans-unit id="Direct messages between members are prohibited in this group." xml:space="preserve">
<source>Direct messages between members are prohibited in this group.</source>
<trans-unit id="Direct messages between members are prohibited." xml:space="preserve">
<source>Direct messages between members are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disable SimpleX Lock" xml:space="preserve">
@@ -1248,8 +1243,8 @@ Available in v5.1</source>
<source>Disappearing messages are prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disappearing messages are prohibited in this group." xml:space="preserve">
<source>Disappearing messages are prohibited in this group.</source>
<trans-unit id="Disappearing messages are prohibited." xml:space="preserve">
<source>Disappearing messages are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disconnect" xml:space="preserve">
@@ -1684,16 +1679,16 @@ Available in v5.1</source>
<source>Group members can irreversibly delete sent messages.</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>
<trans-unit id="Members can send direct messages." xml:space="preserve">
<source>Members can send direct messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send disappearing messages." xml:space="preserve">
<source>Group members can send disappearing messages.</source>
<trans-unit id="Members can send disappearing messages." xml:space="preserve">
<source>Members can send disappearing messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send voice messages." xml:space="preserve">
<source>Group members can send voice messages.</source>
<trans-unit id="Members can send voice messages." xml:space="preserve">
<source>Members can send voice messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group message:" xml:space="preserve">
@@ -1812,8 +1807,8 @@ Available in v5.1</source>
<source>Immediately</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Immune to spam and abuse" xml:space="preserve">
<source>Immune to spam and abuse</source>
<trans-unit id="Immune to spam" xml:space="preserve">
<source>Immune to spam</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Import" xml:space="preserve">
@@ -1925,8 +1920,8 @@ Available in v5.1</source>
<source>Irreversible message deletion is prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Irreversible message deletion is prohibited in this group." xml:space="preserve">
<source>Irreversible message deletion is prohibited in this group.</source>
<trans-unit id="Irreversible message deletion is prohibited." xml:space="preserve">
<source>Irreversible message deletion is prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It allows having many anonymous connections without any shared data between them in a single chat profile." xml:space="preserve">
@@ -2120,8 +2115,8 @@ Available in v5.1</source>
<source>Migration is completed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Migrations: %@" xml:space="preserve">
<source>Migrations: %@</source>
<trans-unit id="Migrations:" xml:space="preserve">
<source>Migrations:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Moderate" xml:space="preserve">
@@ -2278,8 +2273,8 @@ Available in v5.1</source>
<source>Onion hosts will not be used.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**.</source>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
@@ -2338,8 +2333,8 @@ Available in v5.1</source>
<source>Open user profiles</source>
<note>authentication reason</note>
</trans-unit>
<trans-unit id="Open-source protocol and code anybody can run the servers." xml:space="preserve">
<source>Open-source protocol and code anybody can run the servers.</source>
<trans-unit id="Anybody can host servers." xml:space="preserve">
<source>Anybody can host servers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Opening the link in the browser may reduce connection privacy and security. Untrusted SimpleX links will be red." xml:space="preserve">
@@ -2394,8 +2389,8 @@ Available in v5.1</source>
<source>Paste the link you received into the box below to connect with your contact.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
<source>People can connect to you only via the links you share.</source>
<trans-unit id="You decide who can connect." xml:space="preserve">
<source>You decide who can connect.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Periodically" xml:space="preserve">
@@ -3098,8 +3093,8 @@ Available in v5.1</source>
<source>Thanks to the users contribute via Weblate!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The 1st platform without any user identifiers private by design." xml:space="preserve">
<source>The 1st platform without any user identifiers private by design.</source>
<trans-unit id="No user identifiers." xml:space="preserve">
<source>No user identifiers.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The ID of the next message is incorrect (less or equal to the previous).&#10;It can happen because of some bug or when the connection is compromised." xml:space="preserve">
@@ -3143,8 +3138,8 @@ It can happen because of some bug or when the connection is compromised.</source
<source>The message will be marked as moderated for all members.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The next generation of private messaging" xml:space="preserve">
<source>The next generation of private messaging</source>
<trans-unit id="The future of messaging" xml:space="preserve">
<source>The future of messaging</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The old database was not removed during the migration, it can be deleted." xml:space="preserve">
@@ -3215,8 +3210,8 @@ It can happen because of some bug or when the connection is compromised.</source
<source>To make a new connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." xml:space="preserve">
<source>To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts.</source>
<trans-unit id="To protect your privacy, SimpleX uses separate IDs for each of your contacts." xml:space="preserve">
<source>To protect your privacy, SimpleX uses separate IDs for each of your contacts.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect timezone, image/voice files use UTC." xml:space="preserve">
@@ -3441,8 +3436,8 @@ To connect, please ask your contact to create another connection link and check
<source>Voice messages are prohibited in this chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages are prohibited in this group." xml:space="preserve">
<source>Voice messages are prohibited in this group.</source>
<trans-unit id="Voice messages are prohibited." xml:space="preserve">
<source>Voice messages are prohibited.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
@@ -3582,10 +3577,6 @@ SimpleX Lock must be enabled.</source>
<source>You can't send messages!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You control through which server(s) **to receive** the messages, your contacts the servers you use to message them." xml:space="preserve">
<source>You control through which server(s) **to receive** the messages, your contacts the servers you use to message them.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You could not be verified; please try again." xml:space="preserve">
<source>You could not be verified; please try again.</source>
<note>No comment provided by engineer.</note>
@@ -4302,8 +4293,8 @@ SimpleX servers cannot see your profile.</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>
<trans-unit id="**Create 1-time link**: to create and share a new invitation link." xml:space="preserve" approved="no">
<source>**Create 1-time link**: to create and share a new invitation link.</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>
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,6 @@
/* No comment provided by engineer. */
"_italic_" = "\\_italic_";
/* No comment provided by engineer. */
"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact.";
/* No comment provided by engineer. */
"*bold*" = "\\*bold*";
@@ -27,4 +24,3 @@
/* No comment provided by engineer. */
"No group!" = "Group not found!";
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,6 @@
/* No comment provided by engineer. */
"_italic_" = "\\_italic_";
/* No comment provided by engineer. */
"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact.";
/* No comment provided by engineer. */
"*bold*" = "\\*bold*";
@@ -27,4 +24,3 @@
/* No comment provided by engineer. */
"No group!" = "Group not found!";
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,6 @@
/* No comment provided by engineer. */
"_italic_" = "\\_italic_";
/* No comment provided by engineer. */
"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact.";
/* No comment provided by engineer. */
"*bold*" = "\\*bold*";
@@ -27,4 +24,3 @@
/* No comment provided by engineer. */
"No group!" = "Group not found!";
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,6 @@
/* No comment provided by engineer. */
"_italic_" = "\\_italic_";
/* No comment provided by engineer. */
"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact.";
/* No comment provided by engineer. */
"*bold*" = "\\*bold*";
@@ -27,4 +24,3 @@
/* No comment provided by engineer. */
"No group!" = "Group not found!";
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