Compare commits

..

369 Commits

Author SHA1 Message Date
Evgeny Poberezkin 05cc300231 Merge tag 'v6.0.5' into stable-android 2024-10-09 08:28:29 +01:00
Evgeny Poberezkin 2f730d54e9 6.0.5: ios 239, android 241, desktop 68 2024-09-24 21:48:30 +01:00
Evgeny Poberezkin fe0013c4a9 ios: update core library 2024-09-24 17:51:34 +01:00
Stanislav Dmitrenko 5261886b31 android, desktop: proxy configuration includes credentials (#4892)
* android, desktop: proxy configuration includes credentials

* migration

* changes for disabled socks

* migration

* port

* new logic

* migration

* check validity of fields

* validity of host

* import changes proxy just in case

* send port always

* non-nullable

* Revert "send port always"

This reverts commit 14dd066d80.

* string

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-09-24 17:07:39 +01:00
Evgeny 93ab3076d4 ios: SOCKS proxy UI (#4893)
* ios: SOCKS proxy UI

* update network config

* proxy

* adapt

* move, dont default to localhost:9050

* move socks proxy to defaults

* sock proxy preference

* rename

* rename

* fix

* fix

---------

Co-authored-by: Avently <7953703+avently@users.noreply.github.com>
2024-09-24 17:06:00 +01:00
Stanislav Dmitrenko 3b88ddbd4f desktop: fix vlc dependency (2) (#4869) 2024-09-24 17:04:08 +01:00
Evgeny Poberezkin cd65e4dbb9 Merge branch 'stable' into stable-android 2024-09-24 12:42:55 +01:00
Evgeny Poberezkin d6dc35738e core: 6.0.5.0 (simplexmq 6.0.5.0) 2024-09-24 12:42:22 +01:00
Evgeny 5b3aba9db2 ci: dont build when files in core do not change (#4797) 2024-09-24 12:40:58 +01:00
Stanislav Dmitrenko 981cbb8bf9 android: target API level 34 (Android 14) (#4697) 2024-09-23 09:05:14 +01:00
Stanislav Dmitrenko 25fb099c44 android, desktop: fix always connecting state in call (#4911) 2024-09-19 18:00:08 +01:00
Evgeny 40e93cc61e core: reduce max message sizes (#4882)
* core: reduce max message sizes

* reduce

* comment
2024-09-16 18:05:09 +01:00
Evgeny c22d23750f core: support different SOCKS proxy authentication modes (#4886)
* core: support different SOCKS proxy authentication modes

* use defaultSocksProxyWithAuth

* hostMode CLI option

* simplexmq
2024-09-16 07:33:48 +01:00
Stanislav Dmitrenko 2539255957 android: remove Worker provider's interface implementation (#4874) 2024-09-13 12:35:12 +01:00
spaced4ndy 3ea8279451 multiplatform: fix delete messages alert (#4862) 2024-09-11 13:04:59 +01:00
Evgeny 0cb568d206 fix incorrect error of migration to device (#4852)
* fix incorrect error of migration to device

* alert to finish migration, ios fix

* simplexmq

* catching exception and stopping chat

* text

---------

Co-authored-by: Avently <7953703+avently@users.noreply.github.com>
2024-09-09 15:01:18 +01:00
Evgeny 351cfcbcbc core: allow deleting user when user record in agent database was deleted (#4851) 2024-09-08 23:01:31 +01:00
Henrique Albuquerque 691cd489ea blog: fix grammar error (#4846) 2024-09-08 12:56:15 +01:00
Stanislav Dmitrenko bccbb9900f android: fix initializing WorkManager (#4833) 2024-09-06 12:39:23 +01:00
Diogo e002f33c53 multiplatform: fix deleted contacts with conversations being identified as contact cards (#4828)
* multiplatform: fix deleted contacts with conversations being identified as contact cards

* fix in ios

* Revert "fix in ios"

This reverts commit 9b8c6bc125.

* fix for ios

* same check as ios for android and desktop
2024-09-04 18:12:41 +01:00
Stanislav Dmitrenko 6407d5de63 android, desktop: making list items unique (#4812) 2024-09-03 14:38:42 +01:00
Stanislav Dmitrenko 9cba96082d android, desktop: fix crash when window is small in chat view (#4819) 2024-09-03 14:37:04 +01:00
Stanislav Dmitrenko 2089fd8539 android, desktop: remove slash from strings (#4818) 2024-09-03 14:36:41 +01:00
Stanislav Dmitrenko 31c4ff2705 android, desktop: group info remembers scroll position and search (#4817) 2024-09-03 14:36:01 +01:00
sh fe20a43232 flatpak: update metainfo (#4811) 2024-09-02 15:15:16 +01:00
Stanislav Dmitrenko 087519c39d desktop: fix vlc dependency (#4809) 2024-09-02 13:08:09 +01:00
Evgeny fff29f8548 faq: private message routing (#4807)
* faq: private message routing

* readme

* corrections
2024-08-31 11:41:21 +01:00
Evgeny 8cc03b6c21 docs: FAQ on deletion of sent messages and read receipts (#4470)
* docs: FAQ on deletion of sent messages and read receipts

* update
2024-08-31 11:41:06 +01:00
Evgeny Poberezkin f5d5bd62ed Merge branch 'stable' into stable-android 2024-08-30 20:00:44 +01:00
Evgeny Poberezkin 6adf8f29b0 6.0.4: ios 236, android 237, desktop 65 2024-08-30 19:59:40 +01:00
Stanislav Dmitrenko 4ca1b57e1b android, desktop: small enhancements to new chat sheet (#4803)
* android, desktop: small enhancements to new chat sheet

* padding

* normal view matching stable

* fix one hand layout

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
Co-authored-by: Diogo <diogofncunha@gmail.com>
2024-08-30 18:34:53 +01:00
Evgeny Poberezkin 9432a5e5cd ios: update core library 2024-08-30 17:09:49 +01:00
Evgeny Poberezkin d2f9888195 Merge branch 'stable' into stable-android 2024-08-30 13:40:03 +01:00
Evgeny Poberezkin a9ec1f9ec1 core: 6.0.4.0 (simplexmq 6.0.3.0) 2024-08-30 13:39:35 +01:00
Stanislav Dmitrenko 122387d180 android, desktop: fix loading chat items when search was not empty (#4802) 2024-08-30 11:11:26 +01:00
Stanislav Dmitrenko 6edea46dad android, desktop: improvement to a lock UI (#4769)
* android, desktop: improvement to a lock UI

* oneTime passcode screen which allows to pass verification while in call

* change

* unused line

* don't ask to set up auth if already has

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-29 13:15:11 +01:00
Diogo 2fe3acf4df fix android simulator build (#4795) 2024-08-29 12:01:29 +01:00
Stanislav Dmitrenko dfe16991d0 ios: make CallKit calls fire in time after cold start (#4787)
* ios: make CallKit calls fire in time after cold start

* longer wait period

* uncomment

* change

* change

* removed commented code

* ios: update core library

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-28 15:49:11 +01:00
Stanislav Dmitrenko acb372a4ce core: call uuid (#4777)
* core: call uuid

* fix

* text

* android, desktop

* ios

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-28 15:31:02 +01:00
sh 121eaf6073 flatpak: update metainfo (#4784)
* flatpak: update metainfo

* flatpak: change release link and ol to ul
2024-08-28 10:39:28 +01:00
Stanislav Dmitrenko 76cb9013f5 desktop: show only AppImage download option for those who running AppImage (#4774) 2024-08-27 11:21:00 +01:00
Stanislav Dmitrenko f1e8c65aa1 android, desktop: using SemVer when checking for updates (#4768)
* android, desktop: using SemVer when checking for updates

* simplify

* simplify

* no comment

* simplify

* change

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-26 21:06:21 +01:00
Stanislav Dmitrenko 0118e64ab4 android, desktop: items padding and min height (#4767) 2024-08-26 16:59:57 +01:00
Evgeny 4552860345 ios: remove unnecessary protocols (#4763) 2024-08-25 14:33:06 +01:00
Evgeny 4d18174b11 ui: fix Debug delivery (#4757) 2024-08-24 19:10:30 +01:00
Evgeny Poberezkin 5ec5826642 Merge branch 'stable' into stable-android 2024-08-24 16:49:03 +01:00
Evgeny Poberezkin defd095a4f 6.0.3: ios 235, android 235, desktop 64 2024-08-24 16:32:32 +01:00
Evgeny Poberezkin ed60f28e56 ios: update core library 2024-08-24 15:44:08 +01:00
Evgeny Poberezkin 29155ab73a Merge branch 'stable' into stable-android 2024-08-24 15:10:05 +01:00
Evgeny Poberezkin f0b889ffcf core: 6.0.3.0 (simplexmq 6.0.2.0) 2024-08-24 15:07:32 +01:00
Evgeny Poberezkin a95415fa1a 6.0.2: ios 234, android 234, desktop 63 2024-08-22 12:22:28 +01:00
Evgeny 8d48c4b14c ui: translations (#4740)
* Translated using Weblate (Italian)

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

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

* Translated using Weblate (Chinese (Traditional))

Currently translated at 64.6% (1314 of 2032 strings)

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

* Translated using Weblate (Chinese (Traditional))

Currently translated at 62.8% (1121 of 1785 strings)

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

* Translated using Weblate (Spanish)

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

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

* Translated using Weblate (Vietnamese)

Currently translated at 33.5% (682 of 2032 strings)

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

* Translated using Weblate (Chinese (Traditional))

Currently translated at 81.6% (1659 of 2032 strings)

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

* Translated using Weblate (Chinese (Traditional))

Currently translated at 81.6% (1659 of 2032 strings)

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

* Translated using Weblate (Chinese (Traditional))

Currently translated at 62.8% (1121 of 1785 strings)

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

* Translated using Weblate (Spanish)

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

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

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 49.6% (887 of 1785 strings)

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

* Translated using Weblate (Polish)

Currently translated at 99.9% (2031 of 2032 strings)

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

* Translated using Weblate (Polish)

Currently translated at 100.0% (1785 of 1785 strings)

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

* Translated using Weblate (Indonesian)

Currently translated at 5.3% (108 of 2032 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 34.2% (696 of 2032 strings)

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

* Translated using Weblate (Chinese (Traditional))

Currently translated at 85.4% (1736 of 2032 strings)

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

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (1785 of 1785 strings)

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

* Translated using Weblate (Chinese (Traditional))

Currently translated at 86.8% (1764 of 2032 strings)

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

* Translated using Weblate (Czech)

Currently translated at 88.6% (1801 of 2032 strings)

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

* Translated using Weblate (Indonesian)

Currently translated at 5.8% (118 of 2032 strings)

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

* Update translation files

Updated by "Remove blank strings" hook in Weblate.

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

* Translated using Weblate (Vietnamese)

Currently translated at 34.4% (701 of 2032 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 34.9% (710 of 2032 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% (2032 of 2032 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% (1785 of 1785 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% (2032 of 2032 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 35.6% (725 of 2032 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% (2032 of 2032 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% (1785 of 1785 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% (2032 of 2032 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% (1785 of 1785 strings)

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

* Translated using Weblate (Spanish)

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

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

* process localizations

* ru

* ru export

* hu

---------

Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: kumo <cloud_029@icloud.com>
Co-authored-by: No name <CertainBot@users.noreply.hosted.weblate.org>
Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: tuananh-ng <158744840+tuananh-ng@users.noreply.github.com>
Co-authored-by: Jiaa <jorden2895@gmail.com>
Co-authored-by: axmfs <axmfs@proton.me>
Co-authored-by: B.O.S.S <BxOxSxS@protonmail.com>
Co-authored-by: billy appetie <billy_appetie@users.noreply.hosted.weblate.org>
Co-authored-by: Max <Prototypem95@users.noreply.hosted.weblate.org>
Co-authored-by: Reza Almanda <rezaalmanda27@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
2024-08-22 11:40:54 +01:00
Stanislav Dmitrenko 9f44242e4c ios: fix applying theme when no global theme edited yet (#4738)
* ios: fix applying theme when no global theme editer yet

* base color

* added check for base

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-22 08:34:17 +01:00
Evgeny Poberezkin 7e7d93c596 ios: update library 2024-08-21 22:43:24 +01:00
Stanislav Dmitrenko 073818db55 android, desktop: fix applying theme when no global theme edited yet (#4735) 2024-08-21 22:05:50 +01:00
Stanislav Dmitrenko 519dd9e219 android, desktop: fix onboarding layout (#4734) 2024-08-21 22:03:49 +01:00
Evgeny Poberezkin 94218a1a7e core: 6.0.2.0 (simplexmq 6.0.1.0) 2024-08-21 19:20:20 +01:00
Arturs Krumins 996c6efddd ios: prevent hangs when opening app from background with async api calls (#4730)
* ios: async api calls on entering foreground

* rename

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-21 19:16:57 +01:00
Arturs Krumins fd9c080103 ios: fix last message being hidden on load (#4733) 2024-08-21 17:04:12 +01:00
Stanislav Dmitrenko 5cb8badb22 android, desktop: layout changes for settings items (#4732)
* android, desktop: layout changes for settings items

* section paddings

* toggle

* padding and border

* padding

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-21 16:46:42 +01:00
Arturs Krumins 0438f35539 ios: fix notifying the recipient when hanging up a call in ios15 (#4731)
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-21 15:05:59 +01:00
Diogo d5eb7b7811 core: api to change user of pending connections (#4681)
* core: add api that enables change of owner user id for pending connections

* old user sends request, incognito handling and coverage

* call agent inside set connection api

* only set user id if servers match

* simplify

* reduce test noise

* return invitation when a newone is created

* add test for profile on different server

* refactor namings

* update simplexmq

* refactor

* test improvements and simplify

* remove fdescribes

* simplify and reduce vars scope

* put if back

* refactor, change error

* refactor view

* refactor

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-08-21 13:27:58 +04:00
Evgeny Poberezkin e04f74738e Revert "ios: asynchronous api calls when entering foreground (#4710)"
This reverts commit b52dfee078.
2024-08-20 18:24:53 +01:00
Stanislav Dmitrenko 5f0ccb9f17 ios: fix interface color without callKit (#4727)
* ios: fix interface color without callKit

* call area

* better hiding sheet when making a call without CallKit

* invert condition
2024-08-20 17:04:00 +01:00
Arturs Krumins b2d18f6960 ios: fix visual artifacts when opening a chat on iOS15 (#4726)
* ios: fix visual artifacts when opening a chat on iOS15

* fix separators

* cleanup
2024-08-20 13:54:52 +01:00
Arturs Krumins b52dfee078 ios: asynchronous api calls when entering foreground (#4710)
* ios: get call invitations asynchronously

* async update chats

* async user list on appear

* move model changes to main thread

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-20 11:28:06 +01:00
Evgeny 70991debfd ios: optimize performance of updating chats (#4723)
* ios: optimize performance of updating chats

* even simpler

* always use updateChats

* make chats readonly from outside of model
2024-08-20 09:29:52 +01:00
Stanislav Dmitrenko 885aa9cfa5 android, desktop: scrolling moves title to app bar (#4703)
* android, desktop: scrolling moves title to app bar

* one place should be without padding

* scroll related changes for both platforms

* adapt code to universal ColumnWithScrollBar

* show in center

* small adjustments

* new chat sheet fix

* divider + mix background color for desktop

* coerce

* different transition

* desktop title starts from left

* host starts from left too

* different coefficient

* settings title
2024-08-19 19:43:54 +01:00
Evgeny 75a468434c core: only start message delivery workers when there are pending messages (#4713)
* core: use threads instead of async (reduce memory)

* simplexmq

* core: only start message delivery workers when there are pending messages (#4714)

* core: only start message delivery workers when there are pending messages

* update tls

* simplexmq

* update ios, simplexmq

* simplexmq
2024-08-18 23:00:34 +01:00
Arturs Krumins 3b98032371 ios: filter agent subscriptions from chat console (#4708) 2024-08-17 15:52:35 +01:00
Arturs Krumins 3a0921c093 ios: asychronous subscription updates (#4707)
* ios: asychronous subscription updates

* cleanup
2024-08-17 13:26:56 +01:00
Evgeny 3740805125 core: batch connection subscription transactions (#4701)
* core: batch connection subscription transactions

* simplexmq
2024-08-16 11:55:22 +01:00
Arturs Krumins b0e0b0beb8 remove text slection context menu from chat item (#4699) 2024-08-15 20:08:51 +01:00
Arturs Krumins c823a4fa6c extend chat view material behind keyboard (#4698)
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-15 18:43:30 +01:00
Arturs Krumins 3c694e2841 upgrade code scanner (#4650)
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-15 18:21:22 +01:00
Arturs Krumins c159c2ede3 ios: fix pending connection sheet styling when opened via icon; fix tappable area of pending connections and contact requests (#4694)
* fix new contact sheet styling

* fix contact request tappable area

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-08-15 14:15:24 +04:00
spaced4ndy 1d0d7bbd01 core: batch send file descriptions (#4684)
* core: batch send file descriptions

* fix useMember

* fix result interpretation

* remove comment

* refactor

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-15 13:43:57 +04:00
Evgeny Poberezkin 82c4d77c73 6.0.1: ios 233, android 232, desktop 62 2024-08-14 23:10:06 +01:00
Evgeny 25938af62f ui: translations (#4654)
* Translated using Weblate (Dutch)

Currently translated at 100.0% (2030 of 2030 strings)

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

* Translated using Weblate (Vietnamese)

Currently translated at 28.5% (580 of 2030 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 5.2% (106 of 2030 strings)

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

* Translated using Weblate (Dutch)

Currently translated at 100.0% (2030 of 2030 strings)

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

* Translated using Weblate (Vietnamese)

Currently translated at 28.5% (580 of 2030 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 5.2% (106 of 2030 strings)

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

* Translated using Weblate (German)

Currently translated at 100.0% (1785 of 1785 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2030 of 2030 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% (1785 of 1785 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 29.3% (595 of 2030 strings)

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

* Translated using Weblate (Spanish)

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

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

* Translated using Weblate (Vietnamese)

Currently translated at 29.9% (607 of 2030 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 30.5% (621 of 2030 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% (2032 of 2032 strings)

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

* Translated using Weblate (French)

Currently translated at 97.4% (1981 of 2032 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 97.8% (1989 of 2032 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 98.7% (2007 of 2032 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% (2032 of 2032 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% (1785 of 1785 strings)

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

* Translated using Weblate (Arabic)

Currently translated at 100.0% (2032 of 2032 strings)

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

* Translated using Weblate (German)

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

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

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2032 of 2032 strings)

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

* Translated using Weblate (Italian)

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

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

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2032 of 2032 strings)

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

* Translated using Weblate (Vietnamese)

Currently translated at 31.3% (638 of 2032 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 5.2% (107 of 2032 strings)

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

* process localizations

* update ru

---------

Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: tuananh-ng <158744840+tuananh-ng@users.noreply.github.com>
Co-authored-by: billy appetie <billy_appetie@users.noreply.hosted.weblate.org>
Co-authored-by: mlanp <github@lang.xyz>
Co-authored-by: No name <CertainBot@users.noreply.hosted.weblate.org>
Co-authored-by: Ophiushi <41908476+ishi-sama@users.noreply.github.com>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: Bezruchenko Simon <worcposj44@gmail.com>
2024-08-14 21:12:50 +01:00
Evgeny edfcced1fa website: translation (#4692)
* 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/

* space

---------

Co-authored-by: Bezruchenko Simon <worcposj44@gmail.com>
2024-08-14 21:01:28 +01:00
Evgeny Poberezkin 5bff5a855e core: 6.0.1.0 2024-08-14 20:30:11 +01:00
Arturs Krumins 46a60d979b ios: fix messages reseting position, when app is brought from background to foreground (#4686)
* ios: fix messages reseting position, when app is brought back to foreground

* minor
2024-08-14 20:14:40 +01:00
Arturs Krumins f6ef57534f ios: add text to share extension link preview (#4683)
* ios: add text to share extension link preview

* remove maxHeight contraint
2024-08-14 19:29:13 +01:00
Stanislav Dmitrenko 1e4479f736 android, desktop: fix loading more items when fast switching chats (#4685)
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-14 19:08:08 +01:00
Stanislav Dmitrenko 789c762c81 android: hide keyboard when not needed (#4689)
* android: hide keyboard when not needed

* revert some parts

* more places

* unused

* delay

* comment
2024-08-14 18:59:53 +01:00
Evgeny e9baeba31f blog: v6 announcement (#4598)
* blog: v6 announcement

* update
2024-08-14 14:10:14 +01:00
Arturs Krumins 2d5bbcdd61 ios: fix merged item order (#4682) 2024-08-14 07:59:58 +01:00
Arturs Krumins c3f67aff69 ios: speed up network status handling (#4678)
* move network status into a separate model

* reduce network model observation scope (#4679)

* dont pass chat

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-13 21:08:04 +01:00
Arturs Krumins 7cb3a499b2 ios: optimise chat switching (#4663)
* ios: shooth chat switching

* debug button

* navigation timeout

* fix scroll crash

* fix merge

* whitespace

* wip

* add spinner; extract load and nav logic

* cleanup

* direct chat button

* cleanup

* showLoadingProgress

* reverse rename

* rename

* spinner layout

* move all programmatic navigation to `openLoadChat`

* remove access restriction

* fix scroll on item added regression

* print

* fix page load regression

* fix member sheet disappearing

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-13 19:37:48 +01:00
Stanislav Dmitrenko 32e7fd72d3 android, desktop: searching in different screens using the same data (#4673)
* android, desktop: searching in different screens using the same data

* don't filter if not needed
2024-08-13 19:15:54 +01:00
Stanislav Dmitrenko 915bbed400 android, desktop: fix chatView state propagation (#4676) 2024-08-13 19:11:04 +01:00
Stanislav Dmitrenko 2ae5a8bffd android, desktop: padding for RTL layout and remembering prefered layout (#4675)
* android, desktop: padding for RTL layout and remembering prefered layout

* refactor

* changes
2024-08-13 19:07:03 +01:00
Stanislav Dmitrenko cd1550a14d android, desktop: fix crash on marking chat read (#4671) 2024-08-13 18:56:47 +01:00
Stanislav Dmitrenko c72c461306 android: replacing a crash with an alert when opening broken Uri (#4674)
* android: replacing a crash with an alert when opening broken Uri

* strings

---------

Co-authored-by: Evgeny <evgeny@poberezkin.com>
2024-08-13 18:42:33 +01:00
spaced4ndy 38fa4c231f ui: improve remote controller stop reason (#4670) 2024-08-13 15:05:13 +04:00
spaced4ndy cb683d0706 ui: disable subs indicator when chat is stopped (#4672) 2024-08-13 13:14:27 +04:00
Diogo 84ae39b012 android: fix group creation chat redirection showing attachments view (#4669) 2024-08-13 12:18:06 +04:00
Stanislav Dmitrenko 6a12f2dec8 ios: fix updating chat wallpaper while app is in background (#4661) 2024-08-12 21:57:04 +01:00
Diogo d1f704d160 multiplatform: mark chat non deleted only on send/receive (keep chatDeleted flag on open) (#4664) 2024-08-12 19:50:36 +04:00
Diogo 9871ebb3b1 ios: mark chat non deleted only on send/receive (keep chatDeleted flag on open) (#4659)
* ios: navigation to delete chat working for group members

* modify

* Revert "modify"

This reverts commit fc811bbb84.

* don't mark non deleted on open, mark on send

* simplify

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-08-12 18:49:34 +04:00
Evgeny 02c404593c cli: do not log events for hidden user profiles (#4658)
* cli: do not log events for hidden user profiles

* fix
2024-08-12 08:45:11 +01:00
sh 1d9c5b7a0b flatpak: update metainfo (#4660) 2024-08-12 11:42:48 +04:00
Evgeny Poberezkin e20a4e54c3 6.0: ios 232, android 230, desktop 61 2024-08-11 14:31:00 +01:00
Stanislav Dmitrenko 1f8c69ec23 ios: fix applying chat theme (#4656) 2024-08-11 13:11:11 +01:00
Arturs Krumins 35c37263b8 ios: fix group replaced in the list of chats with direct chat (#4655)
* add viewbuilder

* fix

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-11 12:06:25 +01:00
Arturs Krumins 9b70599cc5 ios: scroll chat list to bottom, when items are added (#4651)
* scroll chat list to bottom

* simpler

* is really near bottom

* reduce thresholds

* comment

* itemAdded

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-11 10:07:45 +01:00
Evgeny a4a11f99d2 ios: fix switching to another chat prevents marking items as unread (#4652) 2024-08-11 07:18:01 +01:00
Evgeny Poberezkin 7c8955fcdb ios: core library 6.0.0.7 2024-08-10 22:49:47 +01:00
Arturs Krumins f922064f5c iOS: fix chat list temporarily navigating to an empty view (#4647)
* add two way binding for chatList navigation

* style

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-10 22:23:56 +01:00
Evgeny 110839627f ios: new message sheet detents (#4639)
* ios: new message sheet detents

* animation

* use isActive

* fix sheet rendering

* minor

* increase onTap area

* cleanup

* cleanup

* refactor, dont allow reducing sheet once opened

---------

Co-authored-by: Levitating Pineapple <noreply@levitatingpineapple.com>
2024-08-10 22:18:49 +01:00
Evgeny Poberezkin c90b71120f core: 6.0.0.7 2024-08-10 21:53:21 +01:00
Evgeny 3ec029e489 core: fix auto-reply to the previous version clients (#4649)
* core: fix auto-reply to the previous version clients

* add condition, refactor
2024-08-10 21:52:21 +01:00
Evgeny f4c799aced ui: translations (#4648)
* Translated using Weblate (German)

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

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

* Translated using Weblate (Dutch)

Currently translated at 100.0% (2030 of 2030 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% (2030 of 2030 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% (1785 of 1785 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% (2030 of 2030 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% (1785 of 1785 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 28.0% (569 of 2030 strings)

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

* Translated using Weblate (Spanish)

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

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

* Translated using Weblate (Chinese (Simplified))

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

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

* Translated using Weblate (Arabic)

Currently translated at 100.0% (2030 of 2030 strings)

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

* Translated using Weblate (Russian)

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

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

* process localizations, update RU

* remove quotes

---------

Co-authored-by: mlanp <github@lang.xyz>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: tuananh-ng <158744840+tuananh-ng@users.noreply.github.com>
Co-authored-by: No name <CertainBot@users.noreply.hosted.weblate.org>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
2024-08-10 15:51:29 +01:00
Evgeny d970470702 ui: fix throttled chat ordering (#4645)
* ios: fix throttled chat ordering

* optimize

* account for added chats

* revert kotlin change

* dont pop chat that is already on top, unify with addChat

* android, desktop: fix chat ordering

* update

* clear

* fix ios

* refactor sorting
2024-08-10 14:04:37 +01:00
Evgeny Poberezkin 9ee74bd36e core: 6.0.0.6 (simplexmq 6.0.0.8) 2024-08-09 23:16:39 +01:00
Evgeny 0a4ff2e35f core: reduce usage of STM transactions for better performance and memory usage (#4636)
* core: reduce usage of STM transactions for better performance and memory usage

* simplexmq

* fix test crash

* enable all tests

* simplexmq
2024-08-09 22:30:30 +01:00
Stanislav Dmitrenko a3550df893 android, desktop: small layout changes (#4643)
* android, desktop: small layout changes

* padding

* blue theme handling

* themedBackground on onboarding

* status bar in call

* disabled elevation on attachments sheet to prevent seeing shadow from the bottom

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-09 21:51:40 +01:00
Evgeny Poberezkin 3778698a6e ios: set default toolbar opacity to regular, show dropdown 2024-08-09 17:26:57 +01:00
Arturs Krumins cf7a16e857 ios: translucent bars in chat view (#4641)
* extend reverse list; disable clipping

* wallpaper - ignore safe area

* minor

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-08-09 17:23:57 +01:00
Evgeny bef1597fa1 ui: fix when moderation for multiple items is enabled (#4642)
* ios: fix when moderation for multiple items is enabled

* same on Android

* same

---------

Co-authored-by: Avently <7953703+avently@users.noreply.github.com>
2024-08-09 19:56:19 +04:00
spaced4ndy f06118835d android: don't show local authentication notice on first start (#4640) 2024-08-09 18:29:14 +04:00
Evgeny Poberezkin fa9e50a904 desktop: correct ru translation 2024-08-09 13:28:39 +01:00
Evgeny Poberezkin 932a65630c desktop, android: remove duplicate localization string 2024-08-09 13:17:14 +01:00
Evgeny b649b64999 ui: translations (#4638)
* Translated using Weblate (German)

Currently translated at 97.8% (1986 of 2029 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 99.2% (2014 of 2029 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 97.4% (1978 of 2029 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 99.1% (2012 of 2029 strings)

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

* Translated using Weblate (Chinese (Simplified))

Currently translated at 99.1% (2012 of 2029 strings)

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

* Translated using Weblate (Chinese (Traditional))

Currently translated at 59.0% (1198 of 2029 strings)

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

* Translated using Weblate (Spanish)

Currently translated at 99.0% (2010 of 2029 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 99.1% (2012 of 2029 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% (1799 of 2029 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 88.7% (1801 of 2029 strings)

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

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 79.7% (1618 of 2029 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 99.0% (2009 of 2029 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 89.1% (1809 of 2029 strings)

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

* Translated using Weblate (Lithuanian)

Currently translated at 86.0% (1745 of 2029 strings)

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

* Translated using Weblate (Finnish)

Currently translated at 72.6% (1474 of 2029 strings)

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

* Translated using Weblate (Korean)

Currently translated at 45.7% (929 of 2029 strings)

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

* Translated using Weblate (Polish)

Currently translated at 95.7% (1943 of 2029 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 45.5% (925 of 2029 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 92.8% (1883 of 2029 strings)

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

* Translated using Weblate (Thai)

Currently translated at 63.7% (1293 of 2029 strings)

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

* Translated using Weblate (Bulgarian)

Currently translated at 85.0% (1726 of 2029 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 89.1% (1809 of 2029 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 99.1% (2012 of 2029 strings)

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

* Translated using Weblate (Persian)

Currently translated at 89.7% (1821 of 2029 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.0% (670 of 2029 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 27.4% (556 of 2029 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.2% (2013 of 2029 strings)

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

* Translated using Weblate (Hungarian)

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

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

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2029 of 2029 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% (2029 of 2029 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% (1786 of 1786 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% (2029 of 2029 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.7% (1781 of 1786 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% (2029 of 2029 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% (1786 of 1786 strings)

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

* process localizations

* ru: translate New message

---------

Co-authored-by: Anonymous <noreply@weblate.org>
Co-authored-by: No name <CertainBot@users.noreply.hosted.weblate.org>
Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: mlanp <github@lang.xyz>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
2024-08-09 13:07:19 +01:00
Evgeny e94ead3a4f ui: rename sheet to "New message" (#4637)
* ui: rename sheet to "New message"

* export localizations
2024-08-09 12:40:39 +01:00
spaced4ndy 42d7c20a47 ios: fix appSheet screen protection for ios 16 (#4635)
* ios: fix app sheet screen protection for ios 16

* comment
2024-08-09 11:43:42 +01:00
Evgeny Poberezkin cf8fd2ea11 6.0-beta.4: ios 231, android 229, desktop 60 2024-08-08 21:43:23 +01:00
Evgeny 5006742577 android: move reachable toolbar card above Private notes for the new users (#4633) 2024-08-08 20:49:00 +01:00
Stanislav Dmitrenko 38d46891a1 android, desktop: onboarding design changes (#4631)
* android, desktop: onboarding design changes

* changes

* onboardingState + status bar color

* paddings, update texts

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-08 20:25:56 +01:00
Evgeny 6a7e573b42 ios: rename group default for SimpleX Lock to protect share extension without unnecessarily locking the app (#4632) 2024-08-08 18:27:46 +01:00
Stanislav Dmitrenko b31a74567d ios: fix appSheet (#4627)
* ios: fix appSheet

* old method

* refactor

* refactor

* Revert "refactor"

This reverts commit 32333a13d3.

* Revert "refactor"

This reverts commit da42bd9ecf.

* Revert "old method"

This reverts commit a9cd219479.

* refactor

* remove

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-08-08 21:12:55 +04:00
Evgeny b3f0e71ded ios: toolbar opacity (#4630)
* ios: toolbar material

* top toolbar too
2024-08-08 17:25:45 +01:00
Evgeny ed50623ef5 ui: translations (#4626)
* Translated using Weblate (Russian)

Currently translated at 100.0% (2020 of 2020 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 86.3% (1537 of 1780 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% (2020 of 2020 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% (1780 of 1780 strings)

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

* Translated using Weblate (Dutch)

Currently translated at 100.0% (2020 of 2020 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% (1780 of 1780 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 89.5% (1808 of 2020 strings)

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

* Translated using Weblate (Russian)

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

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

* Translated using Weblate (Arabic)

Currently translated at 100.0% (2020 of 2020 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 98.8% (1997 of 2020 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 98.8% (1997 of 2020 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 98.9% (1998 of 2020 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 98.9% (1998 of 2020 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 99.0% (2000 of 2020 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 99.0% (2000 of 2020 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 99.2% (2004 of 2020 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 99.2% (2004 of 2020 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% (2020 of 2020 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% (2020 of 2020 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% (1780 of 1780 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% (2020 of 2020 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% (1780 of 1780 strings)

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

* Translated using Weblate (Italian)

Currently translated at 100.0% (2021 of 2021 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 98.9% (1999 of 2021 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 93.6% (1667 of 1780 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% (2021 of 2021 strings)

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

* Translated using Weblate (Spanish)

Currently translated at 99.5% (2011 of 2021 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.7% (2015 of 2021 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% (2021 of 2021 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 94.8% (1689 of 1780 strings)

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

* Translated using Weblate (Italian)

Currently translated at 100.0% (2023 of 2023 strings)

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

* Translated using Weblate (Chinese (Simplified))

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

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

* Translated using Weblate (Hebrew)

Currently translated at 93.4% (1890 of 2023 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2023 of 2023 strings)

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

* import/export localizations

* ru: corrections

* export localizations

---------

Co-authored-by: J R <jr@simplex.chat>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: zenobit <zen@osowoso.xyz>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
Co-authored-by: No name <CertainBot@users.noreply.hosted.weblate.org>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: הצל השחור וואהה!!!! <ty79k9+5pm8c50cjgpm4@sharklasers.com>
2024-08-08 15:27:36 +01:00
Evgeny 53368713a0 Translated using Weblate (Russian) (#4629)
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/ru/

Co-authored-by: J R <jr@simplex.chat>
2024-08-08 15:26:00 +01:00
Evgeny Poberezkin 5739b94808 core: 6.0.0.5 (simplexmq 6.0.0.7) 2024-08-08 13:18:49 +01:00
spaced4ndy 0782408682 multiplatform: improve info views actions buttons design (#4622)
* multiplatform: improve info views actions buttons design

* spaces

* rework

* fix width
2024-08-08 13:10:13 +01:00
Evgeny 41576f80e7 ui: update whats new in 6.0 (#4625)
* ui: update whats new in 6.0

* update

* export localization

* android whats new
2024-08-08 13:07:55 +01:00
Arturs Krumins 2503a86f07 ios: tapping chat list bottom bar scrolls to search input (#4623)
* ios: tapping bottom bar scrolls to search input

* disable for iOS15
2024-08-07 23:00:16 +01:00
spaced4ndy dc713268e8 ios: fix search color in simplex dark theme (#4611)
* ios: fix search color in simplex dark theme

* paddings, colors, group icon

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-07 22:39:05 +01:00
Stanislav Dmitrenko d0c52e43a2 android, desktop: throttle items moving around in chat list too often (#4617)
* android, desktop: throttle items moving around in chat list too often

* test

* Revert "test"

This reverts commit 82db198ed9.

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-07 20:34:51 +01:00
Diogo 2e8e6cef7e android: reachable toolbar card on start (#4608)
* android: reachable toolbar card on start

* same padding for all elements

* show alert with instruction when dismissed

* reset tip, fix for variable font size

* layout, rename

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-08-07 20:20:12 +01:00
Arturs Krumins 239c815f3e ios: fix chat list bottom bar background appearance (#4612)
* ios: fix chat list bottom bar background appearance

* push up bottom bar, when no home indicator is present; tapable chats

* smaller toolbar

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-07 16:24:15 +01:00
Stanislav Dmitrenko 1a6245fe51 android, desktop: prevent migration when network conf wasn't applied (#4615)
* android, desktop: prevent migration when network conf wasn't applied

* name of param
2024-08-07 15:42:31 +01:00
Evgeny b3d15f97f0 core: stop stats when chat is stopped (#4616)
* core: stop stats when chat is stopped

* rename field

* simplexmq
2024-08-07 14:49:58 +01:00
Evgeny c0b8cfb3e2 ui: reset hints in dev tools (#4613)
* ui: reset hints in dev tools

* fix disabled, icon, remove damage

* icon
2024-08-07 11:01:23 +01:00
Arturs Krumins 818161c6ed ios: fix chat list swipe action localisation regression (#4610) 2024-08-07 08:36:52 +01:00
Evgeny Poberezkin 4c53620dfa 6.0-beta.3: ios 230, android 228, desktop 59 2024-08-06 23:33:59 +01:00
spaced4ndy ea5afb28d3 ios: one hand UI (#4589)
* ios: fix bottom toolbar for one hand ui (#4585)

* fix chat list toolbars forhandUI

* add TODO

* cleanup

* fix safe top safe area

* format

---------

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

* fix sheet layout; move user picker (#4592)

* ios: invert swipe actions in oneHandUI mode (#4596)

* add swipe label

* minor

* adjust font

* dynamic type

* limit use to oneHandUI

* icon size

* fix offset

* change font style

---------

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

* ios: reachable toolbar card on start (#4594)

* ios: reachable toolbar card on start

* rename toggle

* move to one-hand UI default to app group

* clean up

* remove tap gesture on toolbar

* "fix" iOS 15

---------

Co-authored-by: Arturs Krumins <auth@levitatingpineapple.com>
Co-authored-by: Evgeny <evgeny@poberezkin.com>
Co-authored-by: Levitating Pineapple <noreply@levitatingpineapple.com>
2024-08-06 22:33:48 +01:00
spaced4ndy 060760675d ios: fix local authentication becoming enabled by default on app update and for new installations (#4595) 2024-08-06 22:06:07 +01:00
Diogo 75101ac885 desktop: dismiss chat sheet when contact connects while qrcode is open (#4607) 2024-08-06 21:58:31 +01:00
Evgeny Poberezkin 159f66e812 ios: update library 2024-08-06 21:24:41 +01:00
Evgeny Poberezkin 6d19b48979 core: 6.0.0.4 (simplexmq 6.0.0.6) 2024-08-06 19:39:09 +01:00
Evgeny ce73a15787 ui: translations (#4600)
* Translated using Weblate (Dutch)

Currently translated at 98.2% (1969 of 2004 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.4% (1992 of 2004 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.6% (1996 of 2004 strings)

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

* Translated using Weblate (Dutch)

Currently translated at 99.8% (2001 of 2004 strings)

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

* Translated using Weblate (Dutch)

Currently translated at 99.9% (2002 of 2004 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% (2004 of 2004 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% (1746 of 1746 strings)

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

* Translated using Weblate (Russian)

Currently translated at 91.9% (1842 of 2004 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2004 of 2004 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 99.7% (1741 of 1746 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% (2004 of 2004 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% (1746 of 1746 strings)

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

* Translated using Weblate (Hungarian)

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

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

* Translated using Weblate (Italian)

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

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

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (2004 of 2004 strings)

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

* export/import localizations

* ru: typos

* export localizations

---------

Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: GameBoyNoob <game.boy.new26@gmail.com>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
Co-authored-by: mlanp <github@lang.xyz>
Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
2024-08-06 19:35:04 +01:00
Evgeny e6545a1747 ui: whats new in v6.0, rename "Deleted chats" to "Archived contacts" (#4599)
* ios: whats new in v6.0

* android, rename Deleted chats to Archive contacts
2024-08-06 19:20:54 +01:00
Diogo 601b081cab android: one hand UI fixes and improvements (#4597)
* fix bottom toolbar in share one hand ui

* rename one hand ui label to reachable chat toolbar

* one hand ui to be android default

* dumb if remove

* make one hand ui always false when outside android

* override set of one hand ui for imports on desktop

* no need to override current

* always default one hand to true

* one hand ui without using mirrors

* remove unused vars

* added space on multiplication

* clean subscription to prop and param spread

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-08-06 18:27:57 +01:00
spaced4ndy d31dedf132 ui: revert to always show subscriptions indicator, but make it light blue instead of gray for new users (fresh installation with no chats) (#4604) 2024-08-06 20:31:45 +04:00
Evgeny 7441ed9892 core: choose random servers for the first user profile, use the same servers for other profiles (#4584)
* core: choose random servers for the first user profile, use the same servers for other profiles

* update ui clients
2024-08-06 16:13:36 +01:00
Stanislav Dmitrenko f6ee6338c4 android: status bar color fix in non-oneHandUI (#4603) 2024-08-06 16:12:30 +01:00
Stanislav Dmitrenko 5a42c0c1d2 android, desktop: show database import/export errors (#4601)
* android, desktop: show database import/export errors

* no line
2024-08-06 15:37:55 +01:00
Stanislav Dmitrenko 240131a023 android, desktop: move some network settings to advanced network settings (#4583)
* android, desktop: move some network settings to advanced network settings

* strings

* icon

* string

* fix

* change

* icon and footer

* paddings

* revert debug lines
2024-08-06 15:08:47 +01:00
spaced4ndy 37e275c3ca core: change simplex contact cards order (#4593) 2024-08-06 12:58:05 +04:00
spaced4ndy f893ad15de ui: only show subsription summary indicator after any chat is created; ios: fix servers summary sheet dismissal, screen protection (#4590) 2024-08-06 12:03:25 +04:00
Evgeny a0763b3a43 ios: same size of action buttons in chat info sheets (#4587) 2024-08-06 08:12:42 +01:00
Arturs Krumins e6ba82c8ff ios: improve performance, when receiving many messages in a single chat (#4586)
* use UnreadCollector for new messages

* less confusing

* collect removed items to reduce unread counts

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-05 23:36:33 +01:00
spaced4ndy a8bdf1555f ios: new chat sheet & info views action buttons improvements (#4582) 2024-08-05 21:22:09 +04:00
Evgeny d278b3dbcd core: resume message subscriptions as soon as client is moved to foreground (iOS) or network connection appears (iOS and Android) (#4577)
* core: resume subscriptions as soon as iOS client is moved to foreground

* simplexmq

* simplexmq
2024-08-05 16:42:29 +01:00
spaced4ndy ac4b1e9406 multiplatform: improve deleted chats design (#4581) 2024-08-05 19:39:16 +04:00
Stanislav Dmitrenko b04f159970 android, desktop: blur images of blocked group members (#4579)
* android, desktop: blur images of blocked group members

* simplify

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-05 15:01:50 +01:00
Diogo 55331289d3 ios: improve new and existing chat interactions - new chat sheet, one hand ui, info views action buttons; new modes of contact deletion (keep conversation, only delete conversation) (#4427)
* ios: added delete contacts, one hand ui, and contact action buttons

* remove unused, rework info buttons wip

* ios: moved existing buttons to new chat sheet

* ios: add basic list of contacts to new chat sheet

* ios: add deleted chats section to new chat sheet

* group chat info navigation

* fix spacing of group info buttons

* remove comment

* unify spacing logic across info views

* info button alerts wip

* calls alerts wip

* call buttons alerts

* fix call button to correctly update on preference change while in view

* refactor

* fix alert ids

* contact list wip

* more contact list actions

* open chat wip

* fix contact list elements clickability

* ios: search functionality on new chat sheet

* ios: white bg for search box on new chat sheet

* ios: don't show empty list when pasted contact is not known

* ios: add search and nav title to deleted chats

* navigation links wip

* fix refreshable

* ios: empty states for lists

* ios: hide contact cards from chat list

* ios: make search bar icon sizes consistent

* ios: fix deleted conversation dissapearing from chat list on back

* fix pending invitation cleanup in chat sheet

* rename search label from open to search

* make cleanup alert work on sheet and on dismiss

* dismiss all sheets after creation of groups

* fix double toolbar on group invite members

* fix double toolbar on group link invitation screen

* dismiss all on group creation error

* comment

* show alert in dismissAllSheets completion

* fix sheet dismissal on known group

* rework contact list with buttons (fixes dark mode)

* fix dark mode on new chat view

* fix search dark mode

* increase search padding

* improve new chat title and info button placing

* info view background

* improve create group title placement

* refactor

* fix delete dialogue in light mode

* change icon

* archivebox on contact list

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-05 15:58:24 +04:00
Stanislav Dmitrenko b2b1519aea android, desktop: multiple messages deletion (#4559)
* android, desktop: multiple messages deletion

* icons

* icon
2024-08-05 10:26:27 +01:00
Evgeny e769abf14a ui: translations (#4575)
* Deleted translation using Weblate (Hindi)

* Deleted translation using Weblate (Malayalam)

* Translated using Weblate (Dutch)

Currently translated at 100.0% (1963 of 1963 strings)

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

* Translated using Weblate (Dutch)

Currently translated at 97.1% (1695 of 1745 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% (1963 of 1963 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% (1963 of 1963 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 99.6% (1739 of 1745 strings)

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

* Translated using Weblate (French)

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

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

* Translated using Weblate (Dutch)

Currently translated at 99.0% (1728 of 1745 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 2.5% (44 of 1745 strings)

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

* Translated using Weblate (Hebrew)

Currently translated at 91.8% (1803 of 1963 strings)

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

* Translated using Weblate (Hungarian)

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

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

* Translated using Weblate (Italian)

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

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

* Translated using Weblate (Hebrew)

Currently translated at 96.5% (1895 of 1963 strings)

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

* Translated using Weblate (Hebrew)

Currently translated at 38.9% (680 of 1745 strings)

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

* Update translation files

Updated by "Remove blank strings" hook in Weblate.

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

* Translated using Weblate (Arabic)

Currently translated at 3.4% (61 of 1745 strings)

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

* Translated using Weblate (Arabic)

Currently translated at 3.8% (67 of 1745 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 99.5% (1987 of 1996 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 4.5% (80 of 1745 strings)

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

* Translated using Weblate (Hungarian)

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

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

* Translated using Weblate (Italian)

Currently translated at 100.0% (1996 of 1996 strings)

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

* Translated using Weblate (French)

Currently translated at 100.0% (1996 of 1996 strings)

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

* Translated using Weblate (Arabic)

Currently translated at 100.0% (1996 of 1996 strings)

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

* Translated using Weblate (Portuguese)

Currently translated at 46.8% (935 of 1996 strings)

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

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1996 of 1996 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% (1745 of 1745 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 5.5% (96 of 1745 strings)

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

* Translated using Weblate (Portuguese)

Currently translated at 46.9% (937 of 1996 strings)

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

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1996 of 1996 strings)

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

* Translated using Weblate (German)

Currently translated at 100.0% (1996 of 1996 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 99.9% (1744 of 1745 strings)

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

* Translated using Weblate (Vietnamese)

Currently translated at 28.0% (559 of 1996 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% (1996 of 1996 strings)

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

* import/export localizations

* update strings

---------

Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
Co-authored-by: Ophiushi <41908476+ishi-sama@users.noreply.github.com>
Co-authored-by: Muhammad AbdelMajeed <Muhammad.offic@proton.me>
Co-authored-by: shit face <shitface@users.noreply.hosted.weblate.org>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: הצל השחור וואהה!!!! <ty79k9+5pm8c50cjgpm4@sharklasers.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
Co-authored-by: Fábio Ferreira <ffcfpten@gmail.com>
Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: Pixelcode <pixelcode@dismail.de>
Co-authored-by: tuananh-ng <158744840+tuananh-ng@users.noreply.github.com>
Co-authored-by: mlanp <github@lang.xyz>
2024-08-05 10:19:32 +01:00
Evgeny 9ca7031f82 Translated using Weblate (German) (#4574)
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/

Co-authored-by: Pixelcode <pixelcode@dismail.de>
2024-08-05 09:36:49 +01:00
Evgeny 7a418918d6 ios: blur images of blocked group members (#4573)
* ios: blur images of blocked group members

* refactor
2024-08-04 22:24:08 +01:00
Stanislav Dmitrenko 8f1302e1c6 android, desktop: chat preview, compose message, new chat button (#4576)
* android, desktop: chat preview, compose message, new chat button

* padding on desktop

* multiplier

* no placeholder in console

* sheet elevation zero when hidden

* divider

* padding

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-04 21:57:23 +01:00
Evgeny ee7c5d2aac android: smaller new chat button in 1-hand UI mode, line on the correct side of the bar (#4569)
* android: smaller new chat button in 1-hand UI mode, line on the correct side of the bar (wrong color in 1-hand UI mode)

* remove comment

* desktop: open new chat information buttons on start modal

* remove no longer applicable comment

* desktop, android: move to chat when accepting from chat list and snsReady

* android: keep search visible if keyboard is open on new chat sheet

* android: keep search visibile if keyboard is open on chat list

* android: scroll modal header on new chat sheet

* android: added divider between search and toolbar in one hand ui

* make one hand ui toolbar more extensible by using scafold

* android: remove tiny paddings around one hand ui toolbars

* android: hide toolbar when searching on one hand ui

* avoid passing one hand ui as param everywhere

* make paddings match in new chat sheet action buttons

* flip animation

* refactor and divider fix

* fix padding

* bigger padding

* appPrefs

---------

Co-authored-by: Diogo Cunha <diogofncunha@gmail.com>
Co-authored-by: Avently <7953703+avently@users.noreply.github.com>
2024-08-04 21:37:20 +01:00
Evgeny bf697c722a ios: update messages in share extension alert when message sending is slow (#4578) 2024-08-04 20:48:54 +01:00
Evgeny f30dfa0be7 ios: move onion and private routing to advanced network settings, enable private routing by default (#4571)
* ios: move onion and private routing to advanced network settings, enable private routing by default

* update

* update labels

* update localizations
2024-08-04 12:01:09 +01:00
Evgeny Poberezkin 9c73ef9769 android, desktop: search placeholder color and size 2024-08-03 10:21:54 +01:00
Stanislav Dmitrenko e38db7fb44 android: status bar and navigation bar colors from theme (#4568)
* android: status bar and navigation bar colors from theme

* padding

* background on desktop

* useless code

* colors

* removed unused param

* one more place
2024-08-02 23:48:41 +01:00
Evgeny Poberezkin cb76c8079c core: fix archive export when some filename is not compatible with zip (#4561)
* core: fix archive export when some filename is not compatible with zip

* update

* core, ios

* update kotlin apis, ios: add alert to migrate from device
2024-08-02 20:23:54 +01:00
Stanislav Dmitrenko 8fbba16f53 android, desktop: enhancements to chat switching (#4567) 2024-08-02 16:01:21 +01:00
Arturs Krumins 5384e2826d ios: throttle items moving around in chat list too often (#4564)
* ios: add throttling for incoming messages

* cleanup

* throttle, update unread

* dont pop the first chat

* move chats every 3 seconds

* fix

* optimize

* better updateChats

* remove file

* diff

* restore special case for the current chat

* ios: simpler item throttler

* minor

* minor

* refactor

* sort by key

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-02 14:08:10 +01:00
Evgeny Poberezkin 31782600cb Merge branch 'stable' into stable-android 2024-06-01 09:28:13 +01:00
Evgeny Poberezkin db392037ea Merge branch 'stable' into stable-android 2024-05-31 23:35:46 +01:00
Evgeny Poberezkin 6ad7189c53 Merge branch 'stable' into stable-android 2024-05-25 17:17:18 +01:00
Evgeny Poberezkin 15d8444c74 Merge branch 'stable' into stable-android 2024-05-25 11:19:14 +01:00
Evgeny Poberezkin 2d31b853ac Merge branch 'stable' into stable-android 2024-05-22 20:30:46 +01:00
Evgeny Poberezkin 3af0108b0d Merge branch 'stable' into stable-android 2024-04-13 11:59:14 +01:00
Evgeny Poberezkin 746dd33728 Merge branch 'stable' into stable-android 2024-03-22 21:21:10 +00:00
Evgeny Poberezkin 2fceecab37 Merge branch 'stable' into stable-android 2024-03-04 21:38:09 +00:00
Evgeny Poberezkin e094ec5cf5 Merge branch 'stable' into stable-android 2024-02-27 07:19:21 +00:00
Evgeny Poberezkin fbd6012611 Merge branch 'stable' into stable-android 2024-02-21 19:06:13 +00:00
Evgeny Poberezkin 913ce4ebfd Merge branch 'stable' into stable-android 2024-02-21 14:30:35 +00:00
Evgeny Poberezkin 1e01a961fe Merge branch 'stable' into stable-android 2024-02-21 08:43:40 +00:00
Evgeny Poberezkin 27f4a71146 Merge branch 'stable' into stable-android 2024-02-10 23:35:40 +00:00
Evgeny Poberezkin 282bca4d81 Merge branch 'stable' into stable-android 2024-02-03 18:45:28 +00:00
Evgeny Poberezkin e5011ad150 Merge branch 'stable' into stable-android 2024-01-31 09:47:35 +00:00
Evgeny Poberezkin 0d4d08e52b core: unify dependencies for GHC 8.10.7 and 9.6.3 (#3774) 2024-01-30 23:18:37 +00:00
Evgeny Poberezkin 7f8c7b2d99 fix merge 2024-01-24 22:46:56 +00:00
Evgeny Poberezkin f5b666ae6d Merge branch 'stable' into stable-android 2024-01-24 22:42:04 +00:00
Evgeny Poberezkin 1cfe51af45 Merge branch 'stable' into stable-android 2024-01-18 20:58:52 +00:00
Evgeny Poberezkin 68af58bda1 Merge branch 'stable' into stable-android 2024-01-10 12:17:06 +00:00
Evgeny Poberezkin f51796b81c Merge branch 'stable' into stable-android 2024-01-09 20:20:37 +00:00
Evgeny Poberezkin 995618cf1c Merge branch 'stable' into stable-android 2024-01-09 10:40:34 +00:00
Evgeny Poberezkin 68baa18af8 Merge branch 'stable' into stable-android 2024-01-06 11:48:49 +00:00
Evgeny Poberezkin a4f1c9c915 Merge branch 'stable' into stable-android 2023-12-30 10:21:07 +00:00
Evgeny Poberezkin b8141c3555 Merge branch 'master-android' into stable-android 2023-12-28 09:18:12 +00:00
Evgeny Poberezkin 47214e33ea Merge branch 'master-ghc8107' into master-android 2023-12-27 21:02:49 +00:00
Evgeny Poberezkin ff239d81e5 Merge branch 'master' into master-ghc8107 2023-12-27 21:02:11 +00:00
Evgeny Poberezkin 0a6e47dd61 Merge branch 'master' into master-ghc8107 2023-12-27 20:57:25 +00:00
Evgeny Poberezkin ee75219ed3 core: 5.4.2.1 (simplexmq 5.5.0.2) 2023-12-27 20:57:05 +00:00
Evgeny Poberezkin 2e337a753f Merge branch 'master-ghc8107' into master-android 2023-12-27 14:09:42 +00:00
Evgeny Poberezkin 9918d91def Merge branch 'master' into master-ghc8107 2023-12-27 14:09:23 +00:00
Evgeny Poberezkin 82dd5751c1 Merge branch 'master-ghc8107' into master-android 2023-12-26 21:27:06 +00:00
Evgeny Poberezkin de637fab50 Merge branch 'master' into master-ghc8107 2023-12-26 21:12:36 +00:00
Evgeny Poberezkin cb49f6f01d Merge branch 'master-ghc8107' into master-android 2023-12-23 19:57:27 +00:00
Evgeny Poberezkin 6d0a83aa58 Merge branch 'master' into master-ghc8107 2023-12-23 19:57:08 +00:00
Evgeny Poberezkin 05f57e98ad Merge branch 'master-ghc8107' into master-android 2023-12-23 17:25:14 +00:00
Evgeny Poberezkin 6a66525927 Merge branch 'master' into master-ghc8107 2023-12-23 17:24:47 +00:00
Evgeny Poberezkin 9530a6055a Merge branch 'master-ghc8107' into master-android 2023-12-23 15:12:49 +00:00
Evgeny Poberezkin f5ed8debcc Merge branch 'master' into master-ghc8107 2023-12-23 15:11:38 +00:00
Evgeny Poberezkin 355d2449c5 fix for GHC 8.10.7 2023-12-23 15:05:36 +00:00
Evgeny Poberezkin f7382cdd6f Merge branch 'master' into master-ghc8107 2023-12-23 13:52:44 +00:00
Evgeny Poberezkin dc8d10d068 core: limit process package version to 1.6.17 2023-12-21 10:54:54 +00:00
Evgeny Poberezkin 951245d33f Merge branch 'master-ghc8107' into master-android 2023-12-21 10:00:28 +00:00
Evgeny Poberezkin 521b901cc9 Merge branch 'master' into master-ghc8107 (without changes for iPhone7) 2023-12-21 09:59:25 +00:00
Evgeny Poberezkin 6311ba451b Merge branch 'master-ghc8107' into master-android 2023-12-21 00:45:32 +00:00
Evgeny Poberezkin 754c76d6fd Merge branch 'master' into master-ghc8107 2023-12-21 00:45:10 +00:00
Evgeny Poberezkin 545f110fb8 nix: fix direct-sqlcipher content hash 2023-12-13 20:29:36 +00:00
Evgeny Poberezkin 4c6387c854 nix: fix script 2023-12-13 20:28:32 +00:00
Evgeny Poberezkin aea7ff1c89 nix: fix script 2023-12-13 20:27:58 +00:00
Evgeny Poberezkin 77ac972e09 Merge branch 'master-ghc8107' into master-android 2023-12-13 12:36:06 +00:00
Evgeny Poberezkin 256f85024f Merge branch 'master' into master-ghc8107 2023-12-13 12:35:37 +00:00
Evgeny Poberezkin 870f9e42dd Merge branch 'master' into master-ghc8107 2023-12-12 20:09:39 +00:00
Evgeny Poberezkin b3544cfbc3 Merge branch 'stable' into stable-android 2023-12-12 10:12:42 +00:00
Evgeny Poberezkin 0a4a3a24e1 Merge branch 'master-ghc8107' into master-android 2023-12-11 17:36:51 +00:00
Evgeny Poberezkin 8a66390a78 fix for GHC 8.10.7 2023-12-11 16:14:56 +00:00
Evgeny Poberezkin 3d48eded3d Merge branch 'master-ghc8107' into master-android 2023-12-11 14:56:47 +00:00
Evgeny Poberezkin 6546426ec0 Merge branch 'master' into master-ghc8107 2023-12-11 14:56:25 +00:00
spaced4ndy 4a7ceb00fb Merge branch 'master-ghc8107' into master-android 2023-12-11 17:53:08 +04:00
spaced4ndy 53560378bb Merge branch 'master' into master-ghc8107 2023-12-11 17:52:48 +04:00
Evgeny Poberezkin bfa8717ed4 Merge branch 'master-android' into stable-android 2023-12-09 20:43:21 +00:00
Evgeny Poberezkin cdb3b6aafd Merge branch 'master-ghc8107' into master-android 2023-12-07 15:12:49 +00:00
Evgeny Poberezkin 9f3d3e8ba4 Merge branch 'master' into master-ghc8107 2023-12-07 15:00:23 +00:00
Evgeny Poberezkin 047aad592e Merge branch 'master-ghc8107' into master-android 2023-12-04 12:32:05 +00:00
Evgeny Poberezkin 087acd9180 changes to support GHC 8.10.7 (#3512)
* Revert "raise lower bound on mtl to a real version (#3499)"

This reverts commit f94c0311c1.

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

This reverts commit 9a1c7f41f7.

* update simplexmq

* remove netword-transport fork

* compatibility with GHC 8.10.7

* simplexmq

* fix test

* simplexmq, deps

* update sqlcipher deps in sha256nix

* fix index-state in cabal.project

* index-state

* remove import

* add cabal.project.freeze

* simplexmq

* remove freeze

* simplexmq

* bytestring,simplexmq

* template-haskell, simplexmq

* simplexmq

* simplexmq

* simplexmq

* mtl

* simplexmq

* remove duplicate index-state
2023-12-04 12:29:49 +00:00
Evgeny Poberezkin 0b822e4a5c Merge branch 'master' into master-ghc8107 2023-12-04 10:07:16 +00:00
Evgeny Poberezkin f8a469488e Merge branch 'master' into master-ghc8107 2023-12-02 12:30:36 +00:00
Evgeny Poberezkin 3b5e806418 Merge branch 'master' into master-ghc8107 2023-12-01 17:46:12 +00:00
spaced4ndy 79e208193a Merge branch 'master-ghc8107' into master-android 2023-11-30 21:11:25 +04:00
spaced4ndy ef5c13b1c1 Merge branch 'master' into master-ghc8107 2023-11-30 21:10:51 +04:00
spaced4ndy 38533213d2 Merge branch 'master' into master-ghc8107 2023-11-30 20:56:51 +04:00
spaced4ndy 5f1aa6fa9d Merge branch 'master-ghc8107' into master-android 2023-11-27 19:18:22 +04:00
spaced4ndy b0002fe07d Merge branch 'master' into master-ghc8107 2023-11-27 19:17:39 +04:00
Evgeny Poberezkin ef21fd1d26 Merge branch 'master-ghc8107' into master-android 2023-11-26 19:59:46 +00:00
Evgeny Poberezkin 4a311b9578 fix for ghc 8.10.7 2023-11-26 19:34:39 +00:00
Evgeny Poberezkin b8da5e225b Merge branch 'master' into master-ghc8107 2023-11-26 18:53:40 +00:00
Evgeny Poberezkin 8b3300e197 Merge branch 'master-android' into stable-android 2023-11-26 13:14:27 +00:00
Evgeny Poberezkin f27de052cf Merge branch 'master' into master-android 2023-11-26 11:51:16 +00:00
Evgeny Poberezkin 5cc537f14c Merge branch 'master' into master-ghc8107 2023-11-26 11:50:50 +00:00
Evgeny Poberezkin dc8ca4cf89 Merge tag 'v5.4.0' into master-android 2023-11-25 11:23:23 +00:00
Evgeny Poberezkin b62dd801f1 Merge branch 'master-ghc8107' into master-android 2023-11-24 20:02:05 +00:00
Evgeny Poberezkin 0c096e2c89 Merge branch 'master' into master-ghc8107 2023-11-24 19:00:30 +00:00
Evgeny Poberezkin cc127e56fe Merge branch 'master' into master-android 2023-11-23 16:23:02 +00:00
Evgeny Poberezkin 1781495ee3 Merge branch 'master' into master-ghc8107 2023-11-23 16:22:46 +00:00
Evgeny Poberezkin 831231d8e6 Merge branch 'master-ghc8107' into master-android 2023-11-21 21:16:04 +00:00
Evgeny Poberezkin 45102442f4 Merge branch 'master' into master-ghc8107 2023-11-21 21:15:10 +00:00
spaced4ndy f323c8e112 Merge branch 'master-ghc8107' into master-android 2023-11-21 19:42:28 +04:00
spaced4ndy 3bdc6b5e28 Merge branch 'master' into master-ghc8107 2023-11-21 19:41:06 +04:00
Evgeny Poberezkin d8373262bc Merge branch 'master-ghc8107' into master-android 2023-11-21 00:01:20 +00:00
Evgeny Poberezkin 3597d34716 Merge branch 'master' into master-ghc8107 2023-11-21 00:00:59 +00:00
Evgeny Poberezkin bd4259e89e update hpack 2023-11-20 14:43:20 +00:00
Evgeny Poberezkin 55ead740cc update hpack 2023-11-20 14:43:05 +00:00
Evgeny Poberezkin 5ef0eda2d7 Merge branch 'master-ghc8107' into master-android 2023-11-20 14:30:27 +00:00
Evgeny Poberezkin 49a9b0e7d6 update hpack version 2023-11-20 14:30:10 +00:00
Evgeny Poberezkin 45ada450a2 Merge branch 'master-ghc8107' into master-android 2023-11-20 13:24:07 +00:00
Evgeny Poberezkin 307a1b3c5e fix for ghc 8.10.7 2023-11-20 13:23:45 +00:00
Evgeny Poberezkin ed6b3bbead Merge branch 'master' into master-ghc8107 2023-11-20 13:01:22 +00:00
spaced4ndy 901610eec5 Merge branch 'master-ghc8107' into master-android 2023-11-20 14:08:10 +04:00
spaced4ndy 7d4127c51d Merge branch 'master' into master-ghc8107 2023-11-20 14:07:08 +04:00
Evgeny Poberezkin 13215d91d7 Merge branch 'master-ghc8107' into master-android 2023-11-20 00:07:12 +00:00
Evgeny Poberezkin e1a8099474 fix for GHC 8.10.7 2023-11-20 00:06:45 +00:00
Evgeny Poberezkin daa8d9bb21 Merge branch 'master' into master-ghc8107 2023-11-19 23:42:13 +00:00
Evgeny Poberezkin 5fcbade1bc Merge branch 'master-ghc8107' into master-android 2023-11-17 11:49:35 +00:00
Evgeny Poberezkin 3937ffa9a6 Merge branch 'master' into master-ghc8107 2023-11-17 11:47:52 +00:00
Evgeny Poberezkin 80ddb50e1c Merge branch 'master-ghc8107' into master-android 2023-11-16 18:55:17 +00:00
Evgeny Poberezkin f6e66f1c53 Merge branch 'master' into master-ghc8107 2023-11-16 18:13:02 +00:00
Evgeny Poberezkin 0c23ff9ae3 Merge branch 'master-ghc8107' into master-android 2023-11-11 13:57:32 +00:00
Evgeny Poberezkin 1570bc2b99 Merge branch 'master' into master-ghc8107 2023-11-11 13:56:53 +00:00
Evgeny Poberezkin 1e2104cabf Merge branch 'master-ghc8107' into master-android 2023-11-11 09:52:07 +00:00
Evgeny Poberezkin f3014f258d Merge branch 'master' into master-ghc8107 2023-11-11 09:51:42 +00:00
Evgeny Poberezkin f0991cc0ba Merge branch 'master-ghc8107' into master-android 2023-11-10 21:22:19 +00:00
Evgeny Poberezkin 74b78a8d7b Merge branch 'master' into master-ghc8107 2023-11-10 21:11:08 +00:00
Evgeny Poberezkin 82cd70a75c Merge branch 'master-ghc8107' into master-android 2023-11-10 21:08:11 +00:00
Evgeny Poberezkin fe4eb7b5af Merge branch 'master' into master-ghc8107 without changes, to skip update for ghc 9.6.3 2023-11-10 21:04:20 +00:00
Evgeny Poberezkin c459e71d02 Merge branch 'master-ghc8107' into master-android 2023-11-06 11:26:40 +00:00
Evgeny Poberezkin 2516d5a393 Merge branch 'master' into master-ghc8107 2023-11-06 11:26:22 +00:00
spaced4ndy 477d98d75a Merge branch 'master-ghc8107' into master-android 2023-11-06 11:42:39 +04:00
spaced4ndy 4253cd7fb9 Merge branch 'master' into master-ghc8107 2023-11-06 11:41:55 +04:00
Evgeny Poberezkin ca78958667 Merge branch 'master-ghc8107' into master-android 2023-11-04 13:39:35 +00:00
Evgeny Poberezkin 1f5b80d560 fix for ghc8107 2023-11-04 13:37:25 +00:00
Evgeny Poberezkin 2de111e76c Merge branch 'master' into master-ghc8107 2023-11-04 13:02:08 +00:00
spaced4ndy 8343285d93 Merge branch 'master-ghc8107' into master-android 2023-10-30 21:01:16 +04:00
spaced4ndy 5dbe2b2745 Merge branch 'master' into master-ghc8107 2023-10-30 21:00:11 +04:00
Evgeny Poberezkin fb9485190d Merge branch 'master-ghc8107' into master-android 2023-10-29 18:27:00 +00:00
Evgeny Poberezkin 6881600e06 Merge branch 'master' into master-ghc8107 2023-10-29 18:24:13 +00:00
spaced4ndy 9ed723bafa Merge branch 'master-ghc8107' into master-android 2023-10-25 10:48:37 +04:00
spaced4ndy 9ded1c9821 Merge branch 'master' into master-ghc8107 2023-10-25 10:47:35 +04:00
spaced4ndy bb374c68b1 Merge branch 'master-ghc8107' into master-android 2023-10-24 18:14:22 +04:00
spaced4ndy c3e82a6a4e Merge branch 'master' into master-ghc8107 2023-10-24 18:13:56 +04:00
spaced4ndy 7c12e82042 Merge branch 'master-ghc8107' into master-android 2023-10-24 17:41:19 +04:00
spaced4ndy e7e66ff873 Merge branch 'master' into master-ghc8107 2023-10-24 17:40:55 +04:00
Evgeny Poberezkin c4d7e5307c Merge branch 'master-ghc8107' into master-android 2023-10-22 15:35:55 +01:00
Evgeny Poberezkin d6b9a45a39 Merge branch 'master' into master-ghc8107 2023-10-22 15:10:33 +01:00
Evgeny Poberezkin 7fd3b4d6ba Merge branch 'master-ghc8107' into master-android 2023-10-18 22:45:11 +01:00
Evgeny Poberezkin 4004aafbc5 Merge branch 'master' into master-ghc8107 2023-10-18 22:44:27 +01:00
spaced4ndy 95008eeeaf Merge branch 'master-ghc8107' into master-android 2023-10-16 20:05:49 +04:00
spaced4ndy c7a8992043 core: fix compilation for ghc 8.10.7 2023-10-16 20:05:13 +04:00
spaced4ndy ea2b5f2ccf Merge branch 'master-ghc8107' into master-android 2023-10-16 19:28:48 +04:00
spaced4ndy ed9f277421 Merge branch 'master' into master-ghc8107 2023-10-16 19:28:06 +04:00
Evgeny Poberezkin 5c14c3b349 Merge branch 'master-ghc8107' into master-android 2023-10-15 18:54:16 +01:00
Evgeny Poberezkin d8fb31f167 Merge branch 'master' into master-ghc8107 2023-10-15 18:53:23 +01:00
Evgeny Poberezkin 02db38ffd3 Merge branch 'master-ghc8107' into master-android 2023-10-11 21:58:16 +01:00
Evgeny Poberezkin 7692195bfa core: fix for ghc 8.10.7 2023-10-11 21:57:53 +01:00
Evgeny Poberezkin c435cbdc7b Merge branch 'master-ghc8107' into master-android 2023-10-11 21:28:43 +01:00
Evgeny Poberezkin effc281271 Merge branch 'master' into master-ghc8107 2023-10-11 21:27:21 +01:00
spaced4ndy 41eb2e5689 Merge branch 'master-ghc8107' into master-android 2023-10-11 13:22:30 +04:00
spaced4ndy 67d74a0a27 Merge branch 'master' into master-ghc8107 2023-10-11 13:21:46 +04:00
Evgeny Poberezkin c60af078d7 Merge branch 'stable' into stable-android 2023-10-09 19:55:51 +01:00
Evgeny Poberezkin f66405e79b Merge branch 'master-ghc8107' into master-android 2023-10-09 17:31:56 +01:00
Evgeny Poberezkin 74d186af16 Merge branch 'master' into master-ghc8107 2023-10-09 17:31:27 +01:00
Evgeny Poberezkin e4fbe66d95 Merge branch 'stable' into stable-android 2023-10-09 14:36:03 +01:00
Evgeny Poberezkin 187fef0c5a Merge branch 'master-ghc8107' into master-android 2023-10-09 14:05:21 +01:00
Evgeny Poberezkin 4782cab507 Merge branch 'master' into master-ghc8107 2023-10-09 14:05:04 +01:00
Evgeny Poberezkin cb52d75ff0 Merge branch 'stable' into stable-android 2023-10-08 17:31:23 +01:00
Evgeny Poberezkin bcbee67709 Merge branch 'master-ghc8107' into master-android 2023-10-08 08:38:40 +01:00
Evgeny Poberezkin 2501cbe55d Merge branch 'master' into master-ghc8107 2023-10-08 08:38:02 +01:00
Evgeny Poberezkin 2bd049db87 Merge branch 'master-ghc8107' into master-android 2023-10-07 21:10:22 +01:00
Evgeny Poberezkin 6b8b9ab4fd Merge branch 'master' into master-ghc8107 2023-10-07 19:06:38 +01:00
Evgeny Poberezkin 30db24265e Merge branch 'master-ghc8107' into master-android 2023-10-02 23:04:44 +01:00
Evgeny Poberezkin 316d605899 Merge branch 'master' into master-ghc8107 2023-10-02 23:04:13 +01:00
Evgeny Poberezkin b4257f7767 Merge branch 'master-ghc8107' into master-android 2023-10-01 13:21:50 +01:00
Evgeny Poberezkin a3f2d5c919 Merge branch 'master' into master-ghc8107 2023-10-01 13:20:06 +01:00
Evgeny Poberezkin cf46469cd5 Merge branch 'master-ghc8107' into master-android 2023-10-01 11:19:50 +01:00
Evgeny Poberezkin 0312fde818 Merge branch 'master' into master-ghc8107 2023-10-01 11:19:27 +01:00
Evgeny Poberezkin 9defa44f0c Merge branch 'master-ghc8107' into master-android 2023-09-29 13:15:23 +01:00
Evgeny Poberezkin 915b53054c Merge branch 'master' into master-ghc8107 2023-09-29 13:14:57 +01:00
Evgeny Poberezkin f81557b4fd Merge branch 'master-ghc8107' into master-android 2023-09-27 22:10:06 +01:00
Evgeny Poberezkin e273bd1239 Merge branch 'master' into master-ghc8107 2023-09-27 22:04:00 +01:00
spaced4ndy a63caf4640 Merge branch 'master-ghc8107' into master-android 2023-09-27 20:38:36 +04:00
spaced4ndy e7f0234134 Merge branch 'master' into master-ghc8107 2023-09-27 20:11:39 +04:00
Evgeny Poberezkin 340552321e Merge branch 'master-ghc8107' into master-android 2023-09-27 16:07:24 +01:00
Evgeny Poberezkin 98a3fc214d Merge branch 'master' into master-ghc8107 2023-09-27 16:04:25 +01:00
Evgeny Poberezkin 6a578cfe3c Merge branch 'master-ghc8107' into master-android 2023-09-25 16:53:04 +01:00
Evgeny Poberezkin dacc075fe8 Merge branch 'master' into master-ghc8107 2023-09-25 16:52:33 +01:00
spaced4ndy 55418e2bc0 Merge branch 'master-ghc8107' into master-android 2023-09-25 17:43:59 +04:00
spaced4ndy f2b5c0f3a8 Merge branch 'master' into master-ghc8107 2023-09-25 17:43:37 +04:00
spaced4ndy 5ebdf5dba9 Merge branch 'master-ghc8107' into master-android 2023-09-25 17:07:14 +04:00
spaced4ndy 8e045764df Merge branch 'master' into master-ghc8107 2023-09-25 16:40:08 +04:00
Evgeny Poberezkin 503d3d77e6 Merge branch 'master' into master-ghc8107 2023-09-23 08:47:28 +01:00
Evgeny Poberezkin 81bd7d97c5 Merge branch 'master' into master-ghc8107 2023-09-22 17:21:54 +01:00
Evgeny Poberezkin 8f57925067 compatibility with GHC 8.10.7 2023-09-22 14:01:25 +01:00
Evgeny Poberezkin 9bf99db82e Merge branch 'master' into master-ghc8107 2023-09-22 13:46:50 +01:00
Evgeny Poberezkin 5615cdbf1a Merge branch 'master' into master-android 2023-09-21 17:04:47 +01:00
Evgeny Poberezkin d802ae0058 Merge branch 'master' into master-android 2023-09-21 12:06:10 +01:00
Evgeny Poberezkin 8f2278198c Merge branch 'master' into master-android 2023-09-20 14:55:25 +01:00
spaced4ndy 10937a5a4e Merge branch 'master' into master-android 2023-09-20 17:36:53 +04:00
Evgeny Poberezkin 6aff6e9804 Merge branch 'master-ghc9' into master-ghc8107 2023-09-18 21:56:35 +01:00
Evgeny Poberezkin 95477cae7e core: use commit from simplexmq branch master-ghc8107 2023-09-18 21:45:50 +01:00
562 changed files with 24012 additions and 18113 deletions
+16 -4
View File
@@ -5,12 +5,22 @@ on:
branches:
- master
- stable
- users
tags:
- "v*"
- "!*-fdroid"
- "!*-armv7a"
pull_request:
paths-ignore:
- "apps/ios"
- "apps/multiplatform"
- "blog"
- "docs"
- "fastlane"
- "images"
- "packages"
- "website"
- "README.md"
- "PRIVACY.md"
jobs:
prepare-release:
@@ -92,7 +102,7 @@ jobs:
uses: actions/checkout@v3
- name: Setup Haskell
uses: haskell-actions/setup@v2
uses: haskell/actions/setup@v2
with:
ghc-version: ${{ matrix.ghc }}
cabal-version: "3.10.1.0"
@@ -220,7 +230,7 @@ jobs:
APPLE_SIMPLEX_NOTARIZATION_APPLE_ID: ${{ secrets.APPLE_SIMPLEX_NOTARIZATION_APPLE_ID }}
APPLE_SIMPLEX_NOTARIZATION_PASSWORD: ${{ secrets.APPLE_SIMPLEX_NOTARIZATION_PASSWORD }}
run: |
scripts/ci/build-desktop-mac.sh
scripts/build-desktop-mac.sh
path=$(echo $PWD/apps/multiplatform/release/main/dmg/SimpleX-*.dmg)
echo "package_path=$path" >> $GITHUB_OUTPUT
echo "package_hash=$(echo SHA2-512\(${{ matrix.desktop_asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
@@ -300,7 +310,9 @@ jobs:
# Unix /
# / Windows
# rm -rf dist-newstyle/src/direct-sq* is here because of the bug in cabal's dependency which prevents second build from finishing
# * In powershell multiline commands do not fail if individual commands fail - https://github.community/t/multiline-commands-on-windows-do-not-fail-if-individual-commands-fail/16753
# * And GitHub Actions does not support parameterizing shell in a matrix job - https://github.community/t/using-matrix-to-specify-shell-is-it-possible/17065
- name: 'Setup MSYS2'
if: matrix.os == 'windows-latest'
+22 -18
View File
@@ -18,7 +18,7 @@
2. ↔️ [Connect to the team](#connect-to-the-team), [join user groups](#join-user-groups) and [follow our updates](#follow-our-updates).
3. 🤝 [Make a private connection](#make-a-private-connection) with a friend.
4. 🔤 [Help translating SimpleX Chat](#help-translating-simplex-chat).
5. ⚡️ [Contribute](#contribute) and [help us with donations](#help-us-with-donations).
5. ⚡️ [Contribute](#contribute) and [support us with donations](#please-support-us-with-your-donations).
[Learn more about SimpleX Chat](#contents).
@@ -150,7 +150,7 @@ We would love to have you join the development! You can help us with:
- contributing to SimpleX Chat knowledge-base.
- developing features - please connect to us via chat so we can help you get started.
## Help us with donations
## Please support us with your donations
Huge thank you to everybody who donated to SimpleX Chat!
@@ -233,6 +233,8 @@ You can use SimpleX with your own servers and still communicate with people usin
Recent and important updates:
[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)
[Jun 4, 2024. SimpleX network: private message routing, v5.8 released with IP address protection and chat themes](./blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.md)
[Apr 26, 2024. SimpleX network: legally binding transparency, v5.7 released with better calls and messages.](./blog/20240426-simplex-legally-binding-transparency-v5-7-better-user-experience.md)
@@ -298,25 +300,27 @@ What is already implemented:
1. Instead of user profile identifiers used by all other platforms, even the most private ones, SimpleX uses [pairwise per-queue identifiers](./docs/GLOSSARY.md#pairwise-pseudonymous-identifier) (2 addresses for each unidirectional message queue, with an optional 3rd address for push notifications on iOS, 2 queues in each connection between the users). It makes observing the network graph on the application level more difficult, as for `n` users there can be up to `n * (n-1)` message queues.
2. [End-to-end encryption](./docs/GLOSSARY.md#end-to-end-encryption) in each message queue using [NaCl cryptobox](https://nacl.cr.yp.to/box.html). This is added to allow redundancy in the future (passing each message via several servers), to avoid having the same ciphertext in different queues (that would only be visible to the attacker if TLS is compromised). The encryption keys used for this encryption are not rotated, instead we are planning to rotate the queues. Curve25519 keys are used for key negotiation.
3. [Double ratchet](./docs/GLOSSARY.md#double-ratchet-algorithm) end-to-end encryption in each conversation between two users (or group members). This is the same algorithm that is used in Signal and many other messaging apps; it provides OTR messaging with [forward secrecy](./docs/GLOSSARY.md#forward-secrecy) (each message is encrypted by its own ephemeral key) and [break-in recovery](./docs/GLOSSARY.md#post-compromise-security) (the keys are frequently re-negotiated as part of the message exchange). Two pairs of Curve448 keys are used for the initial [key agreement](./docs/GLOSSARY.md#key-agreement-protocol), initiating party passes these keys via the connection link, accepting side - in the header of the confirmation message.
4. Additional layer of encryption using NaCL cryptobox for the messages delivered from the server to the recipient. This layer avoids having any ciphertext in common between sent and received traffic of the server inside TLS (and there are no identifiers in common as well).
5. Several levels of [content padding](./docs/GLOSSARY.md#message-padding) to frustrate message size attacks.
6. All message metadata, including the time when the message was received by the server (rounded to a second) is sent to the recipients inside an encrypted envelope, so even if TLS is compromised it cannot be observed.
7. Only TLS 1.2/1.3 are allowed for client-server connections, limited to cryptographic algorithms: CHACHA20POLY1305_SHA256, Ed25519/Ed448, Curve25519/Curve448.
8. To protect against replay attacks SimpleX servers require [tlsunique channel binding](https://www.rfc-editor.org/rfc/rfc5929.html) as session ID in each client command signed with per-queue ephemeral key.
9. To protect your IP address all SimpleX Chat clients support accessing messaging servers via Tor - see [v3.1 release announcement](./blog/20220808-simplex-chat-v3.1-chat-groups.md) for more details.
10. Local database encryption with passphrase - your contacts, groups and all sent and received messages are stored encrypted. If you used SimpleX Chat before v4.0 you need to enable the encryption via the app settings.
11. Transport isolation - different TCP connections and Tor circuits are used for traffic of different user profiles, optionally - for different contacts and group member connections.
12. Manual messaging queue rotations to move conversation to another SMP relay.
13. Sending end-to-end encrypted files using [XFTP protocol](https://simplex.chat/blog/20230301-simplex-file-transfer-protocol.html).
14. Local files encryption.
4. [Post-quantum resistant key exchange](./docs/GLOSSARY.md#post-quantum-cryptography) in double ratchet protocol *on every ratchet step*. Read more in [this post](./blog/20240314-simplex-chat-v5-6-quantum-resistance-signal-double-ratchet-algorithm.md) and also see this [publication by Apple]( https://security.apple.com/blog/imessage-pq3/) explaining the need for post-quantum key rotation.
5. Additional layer of encryption using NaCL cryptobox for the messages delivered from the server to the recipient. This layer avoids having any ciphertext in common between sent and received traffic of the server inside TLS (and there are no identifiers in common as well).
6. Several levels of [content padding](./docs/GLOSSARY.md#message-padding) to frustrate message size attacks.
7. All message metadata, including the time when the message was received by the server (rounded to a second) is sent to the recipients inside an encrypted envelope, so even if TLS is compromised it cannot be observed.
8. Only TLS 1.2/1.3 are allowed for client-server connections, limited to cryptographic algorithms: CHACHA20POLY1305_SHA256, Ed25519/Ed448, Curve25519/Curve448.
9. To protect against replay attacks SimpleX servers require [tlsunique channel binding](https://www.rfc-editor.org/rfc/rfc5929.html) as session ID in each client command signed with per-queue ephemeral key.
10. To protect your IP address from unknown messaging relays, and for per-message transport anonymity (compared with Tor/VPN per-connection anonymity), from v6.0 all SimpleX Chat clients use private message routing by default. Read more in [this post](./blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.md#private-message-routing).
11. To protect your IP address from unknown file relays, when SOCKS proxy is not enabled SimpleX Chat clients ask for a confirmation before downloading the files from unknown servers.
12. To protect your IP address from known servers all SimpleX Chat clients support accessing messaging servers via Tor - see [v3.1 release announcement](./blog/20220808-simplex-chat-v3.1-chat-groups.md) for more details.
13. Local database encryption with passphrase - your contacts, groups and all sent and received messages are stored encrypted. If you used SimpleX Chat before v4.0 you need to enable the encryption via the app settings.
14. Transport isolation - different TCP connections and Tor circuits are used for traffic of different user profiles, optionally - for different contacts and group member connections.
15. Manual messaging queue rotations to move conversation to another SMP relay.
16. Sending end-to-end encrypted files using [XFTP protocol](https://simplex.chat/blog/20230301-simplex-file-transfer-protocol.html).
17. Local files encryption.
We plan to add:
1. Senders' SMP relays and recipients' XFTP relays to reduce traffic and conceal IP addresses from the relays chosen, and potentially controlled, by another party.
2. Post-quantum resistant key exchange in double ratchet protocol.
3. Automatic message queue rotation and redundancy. Currently the queues created between two users are used until the queue is manually changed by the user or contact is deleted. We are planning to add automatic queue rotation to make these identifiers temporary and rotate based on some schedule TBC (e.g., every X messages, or every X hours/days).
4. Message "mixing" - adding latency to message delivery, to protect against traffic correlation by message time.
5. Reproducible builds – this is the limitation of the development stack, but we will be investing into solving this problem. Users can still build all applications and services from the source code.
1. Automatic message queue rotation and redundancy. Currently the queues created between two users are used until the queue is manually changed by the user or contact is deleted. We are planning to add automatic queue rotation to make these identifiers temporary and rotate based on some schedule TBC (e.g., every X messages, or every X hours/days).
2. Message "mixing" - adding latency to message delivery, to protect against traffic correlation by message time.
3. Reproducible builds – this is the limitation of the development stack, but we will be investing into solving this problem. Users can still build all applications and services from the source code.
4. Recipients' XFTP relays to reduce traffic and conceal IP addresses from the relays chosen, and potentially controlled, by another party.
## For developers
-24
View File
@@ -15,35 +15,11 @@ class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
logger.debug("AppDelegate: didFinishLaunchingWithOptions")
application.registerForRemoteNotifications()
if #available(iOS 17.0, *) { trackKeyboard() }
NotificationCenter.default.addObserver(self, selector: #selector(pasteboardChanged), name: UIPasteboard.changedNotification, object: nil)
removePasscodesIfReinstalled()
prepareForLaunch()
return true
}
@available(iOS 17.0, *)
private func trackKeyboard() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}
@available(iOS 17.0, *)
@objc func keyboardWillShow(_ notification: Notification) {
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
ChatModel.shared.keyboardHeight = keyboardFrame.cgRectValue.height
}
}
@available(iOS 17.0, *)
@objc func keyboardWillHide(_ notification: Notification) {
ChatModel.shared.keyboardHeight = 0
}
@objc func pasteboardChanged() {
ChatModel.shared.pasteboardHasStrings = UIPasteboard.general.hasStrings
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.map { String(format: "%02hhx", $0) }.joined()
logger.debug("AppDelegate: didRegisterForRemoteNotificationsWithDeviceToken \(token)")
+4 -4
View File
@@ -36,7 +36,7 @@ struct ContentView: View {
@State private var waitingForOrPassedAuth = true
@State private var chatListActionSheet: ChatListActionSheet? = nil
private let callTopPadding: CGFloat = 50
private let callTopPadding: CGFloat = 40
private enum ChatListActionSheet: Identifiable {
case planAndConnectSheet(sheet: PlanAndConnectActionSheet)
@@ -151,12 +151,12 @@ struct ContentView: View {
}
}
.onAppear {
reactOnDarkThemeChanges()
reactOnDarkThemeChanges(systemInDarkThemeCurrently)
}
.onChange(of: colorScheme) { scheme in
// It's needed to update UI colors when iOS wants to make screenshot after going to background,
// so when a user changes his global theme from dark to light or back, the app will adapt to it
reactOnDarkThemeChanges()
reactOnDarkThemeChanges(scheme == .dark)
}
.onChange(of: theme.name) { _ in
ThemeManager.adjustWindowStyle()
@@ -207,7 +207,7 @@ struct ContentView: View {
CallDuration(call: call)
}
.padding(.horizontal)
.frame(height: callTopPadding - 10)
.frame(height: callTopPadding)
.background(Color(uiColor: UIColor(red: 47/255, green: 208/255, blue: 88/255, alpha: 1)))
.onTapGesture {
chatModel.activeCallViewIsCollapsed = false
+182 -73
View File
@@ -50,12 +50,77 @@ class ItemsModel: ObservableObject {
var reversedChatItems: [ChatItem] = [] {
willSet { publisher.send() }
}
var itemAdded = false {
willSet { publisher.send() }
}
// Publishes directly to `objectWillChange` publisher,
// this will cause reversedChatItems to be rendered without throttling
@Published var isLoading = false
@Published var showLoadingProgress = false
init() {
publisher
.throttle(for: 0.25, scheduler: DispatchQueue.main, latest: true)
.sink { self.objectWillChange.send() }
.store(in: &bag)
}
func loadOpenChat(_ chatId: ChatId, willNavigate: @escaping () -> Void = {}) {
let navigationTimeout = Task {
do {
try await Task.sleep(nanoseconds: 250_000000)
await MainActor.run {
willNavigate()
ChatModel.shared.chatId = chatId
}
} catch {}
}
let progressTimeout = Task {
do {
try await Task.sleep(nanoseconds: 1500_000000)
await MainActor.run { showLoadingProgress = true }
} catch {}
}
Task {
if let chat = ChatModel.shared.getChat(chatId) {
await MainActor.run { self.isLoading = true }
// try? await Task.sleep(nanoseconds: 5000_000000)
await loadChat(chat: chat)
navigationTimeout.cancel()
progressTimeout.cancel()
await MainActor.run {
self.isLoading = false
self.showLoadingProgress = false
willNavigate()
ChatModel.shared.chatId = chatId
}
}
}
}
}
class NetworkModel: ObservableObject {
// map of connections network statuses, key is agent connection id
@Published var networkStatuses: Dictionary<String, NetworkStatus> = [:]
static let shared = NetworkModel()
private init() { }
func setContactNetworkStatus(_ contact: Contact, _ status: NetworkStatus) {
if let conn = contact.activeConn {
networkStatuses[conn.agentConnId] = status
}
}
func contactNetworkStatus(_ contact: Contact) -> NetworkStatus {
if let conn = contact.activeConn {
networkStatuses[conn.agentConnId] ?? .unknown
} else {
.unknown
}
}
}
final class ChatModel: ObservableObject {
@@ -78,10 +143,8 @@ final class ChatModel: ObservableObject {
@Published var contentViewAccessAuthenticated: Bool = false
@Published var laRequest: LocalAuthRequest?
// list of chat "previews"
@Published var chats: [Chat] = []
@Published private(set) var chats: [Chat] = []
@Published var deletedChats: Set<String> = []
// map of connections network statuses, key is agent connection id
@Published var networkStatuses: Dictionary<String, NetworkStatus> = [:]
// current chat
@Published var chatId: String?
var chatItemStatuses: Dictionary<Int64, CIStatus> = [:]
@@ -120,9 +183,6 @@ final class ChatModel: ObservableObject {
@Published var stopPreviousRecPlay: URL? = nil // coordinates currently playing source
@Published var draft: ComposeState?
@Published var draftChatId: String?
// tracks keyboard height via subscription in AppDelegate
@Published var keyboardHeight: CGFloat = 0
@Published var pasteboardHasStrings: Bool = UIPasteboard.general.hasStrings
@Published var networkInfo = UserNetworkInfo(networkType: .other, online: true)
var messageDelivery: Dictionary<Int64, () -> Void> = [:]
@@ -228,10 +288,17 @@ final class ChatModel: ObservableObject {
chats.firstIndex(where: { $0.id == id })
}
func addChat(_ chat: Chat, at position: Int = 0) {
withAnimation {
chats.insert(chat, at: position)
func addChat(_ chat: Chat) {
if chatId == nil {
withAnimation { addChat_(chat, at: 0) }
} else {
addChat_(chat, at: 0)
}
popChatCollector.throttlePopChat(chat.chatInfo.id, currentPosition: 0)
}
func addChat_(_ chat: Chat, at position: Int = 0) {
chats.insert(chat, at: position)
}
func updateChatInfo(_ cInfo: ChatInfo) {
@@ -289,26 +356,10 @@ final class ChatModel: ObservableObject {
}
}
func updateChats(with newChats: [ChatData]) {
for i in 0..<newChats.count {
let c = newChats[i]
if let j = getChatIndex(c.id) {
let chat = chats[j]
chat.chatInfo = c.chatInfo
chat.chatItems = c.chatItems
chat.chatStats = c.chatStats
if i != j {
if chatId != c.chatInfo.id {
popChat_(j, to: i)
} else if i == 0 {
chatToTop = c.chatInfo.id
}
}
} else {
addChat(Chat(c), at: i)
}
}
func updateChats(_ newChats: [ChatData]) {
chats = newChats.map { Chat($0) }
NtfManager.shared.setNtfBadgeCount(totalUnreadCountForAllUsers())
popChatCollector.clear()
}
// func addGroup(_ group: SimpleXChat.Group) {
@@ -316,6 +367,12 @@ final class ChatModel: ObservableObject {
// }
func addChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) {
// mark chat non deleted
if case let .direct(contact) = cInfo, contact.chatDeleted {
var updatedContact = contact
updatedContact.chatDeleted = false
updateContact(updatedContact)
}
// update previews
if let i = getChatIndex(cInfo.id) {
chats[i].chatItems = switch cInfo {
@@ -333,18 +390,9 @@ final class ChatModel: ObservableObject {
[cItem]
}
if case .rcvNew = cItem.meta.itemStatus {
chats[i].chatStats.unreadCount = chats[i].chatStats.unreadCount + 1
increaseUnreadCounter(user: currentUser!)
}
if i > 0 {
if chatId == nil {
withAnimation { popChat_(i) }
} else if chatId == cInfo.id {
chatToTop = cInfo.id
} else {
popChat_(i)
}
unreadCollector.changeUnreadCounter(cInfo.id, by: 1)
}
popChatCollector.throttlePopChat(cInfo.id, currentPosition: i)
} else {
addChat(Chat(chatInfo: cInfo, chatItems: [cItem]))
}
@@ -390,6 +438,7 @@ final class ChatModel: ObservableObject {
ci.meta.itemStatus = status
}
im.reversedChatItems.insert(ci, at: hasLiveDummy ? 1 : 0)
im.itemAdded = true
}
return true
}
@@ -422,8 +471,8 @@ final class ChatModel: ObservableObject {
}
func removeChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) {
if cItem.isRcvNew, let chatIndex = getChatIndex(cInfo.id) {
decreaseUnreadCounter(chatIndex)
if cItem.isRcvNew {
unreadCollector.changeUnreadCounter(cInfo.id, by: -1)
}
// update previews
if let chat = getChat(cInfo.id) {
@@ -484,6 +533,7 @@ final class ChatModel: ObservableObject {
let cItem = ChatItem.liveDummy(chatInfo.chatType)
withAnimation {
im.reversedChatItems.insert(cItem, at: 0)
im.itemAdded = true
}
return cItem
}
@@ -572,14 +622,13 @@ final class ChatModel: ObservableObject {
func markChatItemRead(_ cInfo: ChatInfo, _ cItem: ChatItem) async {
if chatId == cInfo.id,
let itemIndex = getChatItemIndex(cItem),
let chatIndex = getChatIndex(cInfo.id),
im.reversedChatItems[itemIndex].isRcvNew {
await MainActor.run {
withTransaction(Transaction()) {
// update current chat
markChatItemRead_(itemIndex)
// update preview
unreadCollector.decreaseUnreadCounter(chatIndex)
unreadCollector.changeUnreadCounter(cInfo.id, by: -1)
}
}
}
@@ -588,26 +637,92 @@ final class ChatModel: ObservableObject {
private let unreadCollector = UnreadCollector()
class UnreadCollector {
private let subject = PassthroughSubject<Int, Never>()
private let subject = PassthroughSubject<Void, Never>()
private var bag = Set<AnyCancellable>()
private var dictionary = Dictionary<Int, Int>()
private var unreadCounts: [ChatId: Int] = [:]
init() {
subject
.debounce(for: 1, scheduler: DispatchQueue.main)
.sink { _ in
self.dictionary.forEach { key, value in
ChatModel.shared.decreaseUnreadCounter(key, by: value)
.sink {
let m = ChatModel.shared
for (chatId, count) in self.unreadCounts {
if let i = m.getChatIndex(chatId) {
m.changeUnreadCounter(i, by: count)
}
}
self.dictionary = Dictionary<Int, Int>()
self.unreadCounts = [:]
}
.store(in: &bag)
}
func changeUnreadCounter(_ chatId: ChatId, by count: Int) {
DispatchQueue.main.async {
self.unreadCounts[chatId] = (self.unreadCounts[chatId] ?? 0) + count
}
subject.send()
}
}
let popChatCollector = PopChatCollector()
class PopChatCollector {
private let subject = PassthroughSubject<Void, Never>()
private var bag = Set<AnyCancellable>()
private var chatsToPop: [ChatId: Date] = [:]
private let popTsComparator = KeyPathComparator<Chat>(\.popTs, order: .reverse)
init() {
subject
.throttle(for: 2, scheduler: DispatchQueue.main, latest: true)
.sink { self.popCollectedChats() }
.store(in: &bag)
}
// Only call from main thread
func decreaseUnreadCounter(_ chatIndex: Int) {
dictionary[chatIndex] = (dictionary[chatIndex] ?? 0) + 1
subject.send(chatIndex)
func throttlePopChat(_ chatId: ChatId, currentPosition: Int) {
let m = ChatModel.shared
if currentPosition > 0 && m.chatId == chatId {
m.chatToTop = chatId
}
if currentPosition > 0 || !chatsToPop.isEmpty {
chatsToPop[chatId] = Date.now
subject.send()
}
}
func clear() {
chatsToPop = [:]
}
func popCollectedChats() {
let m = ChatModel.shared
var ixs: IndexSet = []
var chs: [Chat] = []
// collect chats that received updates
for (chatId, popTs) in self.chatsToPop {
// Currently opened chat is excluded, removing it from the list would navigate out of it
// It will be popped to top later when user exits from the list.
if m.chatId != chatId, let i = m.getChatIndex(chatId) {
ixs.insert(i)
let ch = m.chats[i]
ch.popTs = popTs
chs.append(ch)
}
}
let removeInsert = {
m.chats.remove(atOffsets: ixs)
// sort chats by pop timestamp in descending order
m.chats.insert(contentsOf: chs.sorted(using: self.popTsComparator), at: 0)
}
if m.chatId == nil {
withAnimation { removeInsert() }
} else {
removeInsert()
}
self.chatsToPop = [:]
}
}
@@ -622,25 +737,24 @@ final class ChatModel: ObservableObject {
}
}
func decreaseUnreadCounter(_ chatIndex: Int, by count: Int = 1) {
chats[chatIndex].chatStats.unreadCount = chats[chatIndex].chatStats.unreadCount - count
decreaseUnreadCounter(user: currentUser!, by: count)
func changeUnreadCounter(_ chatIndex: Int, by count: Int) {
chats[chatIndex].chatStats.unreadCount = chats[chatIndex].chatStats.unreadCount + count
changeUnreadCounter(user: currentUser!, by: count)
}
func increaseUnreadCounter(user: any UserLike) {
changeUnreadCounter(user: user, by: 1)
NtfManager.shared.incNtfBadgeCount()
}
func decreaseUnreadCounter(user: any UserLike, by: Int = 1) {
changeUnreadCounter(user: user, by: -by)
NtfManager.shared.decNtfBadgeCount(by: by)
}
private func changeUnreadCounter(user: any UserLike, by: Int) {
if let i = users.firstIndex(where: { $0.user.userId == user.userId }) {
users[i].unreadCount += by
}
NtfManager.shared.changeNtfBadgeCount(by: by)
}
func totalUnreadCountForAllUsers() -> Int {
@@ -706,6 +820,7 @@ final class ChatModel: ObservableObject {
func popChat(_ id: String) {
if let i = getChatIndex(id) {
// no animation here, for it not to look like it just moved when leaving the chat
popChat_(i)
}
}
@@ -778,7 +893,13 @@ final class ChatModel: ObservableObject {
}
i += 1
}
return UnreadChatItemCounts(isNearBottom: totalBelow < 16, unreadBelow: unreadBelow)
return UnreadChatItemCounts(
// TODO these thresholds account for the fact that items are still "visible" while
// covered by compose area, they should be replaced with the actual height in pixels below the screen.
isNearBottom: totalBelow < 15,
isReallyNearBottom: totalBelow < 2,
unreadBelow: unreadBelow
)
}
func topItemInView(itemsInView: Set<String>) -> ChatItem? {
@@ -789,20 +910,6 @@ final class ChatModel: ObservableObject {
while i < maxIx && inView(i) { i += 1 }
return im.reversedChatItems[min(i - 1, maxIx)]
}
func setContactNetworkStatus(_ contact: Contact, _ status: NetworkStatus) {
if let conn = contact.activeConn {
networkStatuses[conn.agentConnId] = status
}
}
func contactNetworkStatus(_ contact: Contact) -> NetworkStatus {
if let conn = contact.activeConn {
networkStatuses[conn.agentConnId] ?? .unknown
} else {
.unknown
}
}
}
struct ShowingInvitation {
@@ -817,6 +924,7 @@ struct NTFContactRequest {
struct UnreadChatItemCounts: Equatable {
var isNearBottom: Bool
var isReallyNearBottom: Bool
var unreadBelow: Int
}
@@ -825,6 +933,7 @@ final class Chat: ObservableObject, Identifiable, ChatLike {
@Published var chatItems: [ChatItem]
@Published var chatStats: ChatStats
var created = Date.now
fileprivate var popTs: Date?
init(_ cData: ChatData) {
self.chatInfo = cData.chatInfo
+5 -7
View File
@@ -57,7 +57,9 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject {
chatModel.ntfCallInvitationAction = (chatId, ntfAction)
}
} else {
chatModel.chatId = content.targetContentIdentifier
if let chatId = content.targetContentIdentifier {
ItemsModel.shared.loadOpenChat(chatId)
}
}
handler()
}
@@ -238,12 +240,8 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject {
ntfBadgeCountGroupDefault.set(count)
}
func decNtfBadgeCount(by count: Int = 1) {
setNtfBadgeCount(max(0, UIApplication.shared.applicationIconBadgeNumber - count))
}
func incNtfBadgeCount(by count: Int = 1) {
setNtfBadgeCount(UIApplication.shared.applicationIconBadgeNumber + count)
func changeNtfBadgeCount(by count: Int = 1) {
setNtfBadgeCount(max(0, UIApplication.shared.applicationIconBadgeNumber + count))
}
private func addNotification(_ content: UNMutableNotificationContent) {
+136 -54
View File
@@ -112,9 +112,9 @@ func chatSendCmdSync(_ cmd: ChatCommand, bgTask: Bool = true, bgDelay: Double? =
return resp
}
func chatSendCmd(_ cmd: ChatCommand, bgTask: Bool = true, bgDelay: Double? = nil, _ ctrl: chat_ctrl? = nil) async -> ChatResponse {
func chatSendCmd(_ cmd: ChatCommand, bgTask: Bool = true, bgDelay: Double? = nil, _ ctrl: chat_ctrl? = nil, log: Bool = true) async -> ChatResponse {
await withCheckedContinuation { cont in
cont.resume(returning: chatSendCmdSync(cmd, bgTask: bgTask, bgDelay: bgDelay, ctrl))
cont.resume(returning: chatSendCmdSync(cmd, bgTask: bgTask, bgDelay: bgDelay, ctrl, log: log))
}
}
@@ -137,8 +137,8 @@ func apiGetActiveUser(ctrl: chat_ctrl? = nil) throws -> User? {
}
}
func apiCreateActiveUser(_ p: Profile?, sameServers: Bool = false, pastTimestamp: Bool = false, ctrl: chat_ctrl? = nil) throws -> User {
let r = chatSendCmdSync(.createActiveUser(profile: p, sameServers: sameServers, pastTimestamp: pastTimestamp), ctrl)
func apiCreateActiveUser(_ p: Profile?, pastTimestamp: Bool = false, ctrl: chat_ctrl? = nil) throws -> User {
let r = chatSendCmdSync(.createActiveUser(profile: p, pastTimestamp: pastTimestamp), ctrl)
if case let .activeUser(user) = r { return user }
throw r
}
@@ -279,8 +279,10 @@ func apiGetAppSettings(settings: AppSettings) throws -> AppSettings {
throw r
}
func apiExportArchive(config: ArchiveConfig) async throws {
try await sendCommandOkResp(.apiExportArchive(config: config))
func apiExportArchive(config: ArchiveConfig) async throws -> [ArchiveError] {
let r = await chatSendCmd(.apiExportArchive(config: config))
if case let .archiveExported(archiveErrors) = r { return archiveErrors }
throw r
}
func apiImportArchive(config: ArchiveConfig) async throws -> [ArchiveError] {
@@ -318,8 +320,8 @@ private func apiChatsResponse(_ r: ChatResponse) throws -> [ChatData] {
let loadItemsPerPage = 50
func apiGetChat(type: ChatType, id: Int64, search: String = "") throws -> Chat {
let r = chatSendCmdSync(.apiGetChat(type: type, id: id, pagination: .last(count: loadItemsPerPage), search: search))
func apiGetChat(type: ChatType, id: Int64, search: String = "") async throws -> Chat {
let r = await chatSendCmd(.apiGetChat(type: type, id: id, pagination: .last(count: loadItemsPerPage), search: search))
if case let .apiChat(_, chat) = r { return Chat.init(chat) }
throw r
}
@@ -330,16 +332,20 @@ func apiGetChatItems(type: ChatType, id: Int64, pagination: ChatPagination, sear
throw r
}
func loadChat(chat: Chat, search: String = "") {
func loadChat(chat: Chat, search: String = "", clearItems: Bool = true) async {
do {
let cInfo = chat.chatInfo
let m = ChatModel.shared
let im = ItemsModel.shared
m.chatItemStatuses = [:]
im.reversedChatItems = []
let chat = try apiGetChat(type: cInfo.chatType, id: cInfo.apiId, search: search)
m.updateChatInfo(chat.chatInfo)
im.reversedChatItems = chat.chatItems.reversed()
if clearItems {
await MainActor.run { im.reversedChatItems = [] }
}
let chat = try await apiGetChat(type: cInfo.chatType, id: cInfo.apiId, search: search)
await MainActor.run {
im.reversedChatItems = chat.chatItems.reversed()
m.updateChatInfo(chat.chatInfo)
}
} catch let error {
logger.error("loadChat error: \(responseError(error))")
}
@@ -576,13 +582,13 @@ func apiGroupMemberInfo(_ groupId: Int64, _ groupMemberId: Int64) throws -> (Gro
throw r
}
func apiContactQueueInfo(_ contactId: Int64) async throws -> (RcvMsgInfo?, QueueInfo) {
func apiContactQueueInfo(_ contactId: Int64) async throws -> (RcvMsgInfo?, ServerQueueInfo) {
let r = await chatSendCmd(.apiContactQueueInfo(contactId: contactId))
if case let .queueInfo(_, rcvMsgInfo, queueInfo) = r { return (rcvMsgInfo, queueInfo) }
throw r
}
func apiGroupMemberQueueInfo(_ groupId: Int64, _ groupMemberId: Int64) async throws -> (RcvMsgInfo?, QueueInfo) {
func apiGroupMemberQueueInfo(_ groupId: Int64, _ groupMemberId: Int64) async throws -> (RcvMsgInfo?, ServerQueueInfo) {
let r = await chatSendCmd(.apiGroupMemberQueueInfo(groupId: groupId, groupMemberId: groupMemberId))
if case let .queueInfo(_, rcvMsgInfo, queueInfo) = r { return (rcvMsgInfo, queueInfo) }
throw r
@@ -699,7 +705,7 @@ func apiConnect_(incognito: Bool, connReq: String) async -> ((ConnReqType, Pendi
return ((.contact, connection), nil)
case let .contactAlreadyExists(_, contact):
if let c = m.getContactChat(contact.contactId) {
await MainActor.run { m.chatId = c.id }
ItemsModel.shared.loadOpenChat(c.id)
}
let alert = contactAlreadyExistsAlert(contact)
return (nil, alert)
@@ -759,22 +765,38 @@ func apiConnectContactViaAddress(incognito: Bool, contactId: Int64) async -> (Co
return (nil, alert)
}
func apiDeleteChat(type: ChatType, id: Int64, notify: Bool? = nil) async throws {
func apiDeleteChat(type: ChatType, id: Int64, chatDeleteMode: ChatDeleteMode = .full(notify: true)) async throws {
let chatId = type.rawValue + id.description
DispatchQueue.main.async { ChatModel.shared.deletedChats.insert(chatId) }
defer { DispatchQueue.main.async { ChatModel.shared.deletedChats.remove(chatId) } }
let r = await chatSendCmd(.apiDeleteChat(type: type, id: id, notify: notify), bgTask: false)
let r = await chatSendCmd(.apiDeleteChat(type: type, id: id, chatDeleteMode: chatDeleteMode), bgTask: false)
if case .direct = type, case .contactDeleted = r { return }
if case .contactConnection = type, case .contactConnectionDeleted = r { return }
if case .group = type, case .groupDeletedUser = r { return }
throw r
}
func deleteChat(_ chat: Chat, notify: Bool? = nil) async {
func apiDeleteContact(id: Int64, chatDeleteMode: ChatDeleteMode = .full(notify: true)) async throws -> Contact {
let type: ChatType = .direct
let chatId = type.rawValue + id.description
if case .full = chatDeleteMode {
DispatchQueue.main.async { ChatModel.shared.deletedChats.insert(chatId) }
}
defer {
if case .full = chatDeleteMode {
DispatchQueue.main.async { ChatModel.shared.deletedChats.remove(chatId) }
}
}
let r = await chatSendCmd(.apiDeleteChat(type: type, id: id, chatDeleteMode: chatDeleteMode), bgTask: false)
if case let .contactDeleted(_, contact) = r { return contact }
throw r
}
func deleteChat(_ chat: Chat, chatDeleteMode: ChatDeleteMode = .full(notify: true)) async {
do {
let cInfo = chat.chatInfo
try await apiDeleteChat(type: cInfo.chatType, id: cInfo.apiId, notify: notify)
DispatchQueue.main.async { ChatModel.shared.removeChat(cInfo.id) }
try await apiDeleteChat(type: cInfo.chatType, id: cInfo.apiId, chatDeleteMode: chatDeleteMode)
await MainActor.run { ChatModel.shared.removeChat(cInfo.id) }
} catch let error {
logger.error("deleteChat apiDeleteChat error: \(responseError(error))")
AlertManager.shared.showAlertMsg(
@@ -784,6 +806,39 @@ func deleteChat(_ chat: Chat, notify: Bool? = nil) async {
}
}
func deleteContactChat(_ chat: Chat, chatDeleteMode: ChatDeleteMode = .full(notify: true)) async -> Alert? {
do {
let cInfo = chat.chatInfo
let ct = try await apiDeleteContact(id: cInfo.apiId, chatDeleteMode: chatDeleteMode)
await MainActor.run {
switch chatDeleteMode {
case .full:
ChatModel.shared.removeChat(cInfo.id)
case .entity:
ChatModel.shared.removeChat(cInfo.id)
ChatModel.shared.addChat(Chat(
chatInfo: .direct(contact: ct),
chatItems: chat.chatItems
))
case .messages:
ChatModel.shared.removeChat(cInfo.id)
ChatModel.shared.addChat(Chat(
chatInfo: .direct(contact: ct),
chatItems: []
))
}
}
} catch let error {
logger.error("deleteContactChat apiDeleteContact error: \(responseError(error))")
return mkAlert(
title: "Error deleting chat!",
message: "Error: \(responseError(error))"
)
}
return nil
}
func apiClearChat(type: ChatType, id: Int64) async throws -> ChatInfo {
let r = await chatSendCmd(.apiClearChat(type: type, id: id), bgTask: false)
if case let .chatCleared(_, updatedChatInfo) = r { return updatedChatInfo }
@@ -1112,9 +1167,16 @@ func networkErrorAlert(_ r: ChatResponse) -> Alert? {
func acceptContactRequest(incognito: Bool, contactRequest: UserContactRequest) async {
if let contact = await apiAcceptContactRequest(incognito: incognito, contactReqId: contactRequest.apiId) {
let chat = Chat(chatInfo: ChatInfo.direct(contact: contact), chatItems: [])
DispatchQueue.main.async {
await MainActor.run {
ChatModel.shared.replaceChat(contactRequest.id, chat)
ChatModel.shared.setContactNetworkStatus(contact, .connected)
NetworkModel.shared.setContactNetworkStatus(contact, .connected)
}
if contact.sndReady {
DispatchQueue.main.async {
dismissAllSheets(animated: true) {
ItemsModel.shared.loadOpenChat(chat.id)
}
}
}
}
}
@@ -1156,12 +1218,18 @@ func apiEndCall(_ contact: Contact) async throws {
try await sendCommandOkResp(.apiEndCall(contact: contact))
}
func apiGetCallInvitations() throws -> [RcvCallInvitation] {
func apiGetCallInvitationsSync() throws -> [RcvCallInvitation] {
let r = chatSendCmdSync(.apiGetCallInvitations)
if case let .callInvitations(invs) = r { return invs }
throw r
}
func apiGetCallInvitations() async throws -> [RcvCallInvitation] {
let r = await chatSendCmd(.apiGetCallInvitations)
if case let .callInvitations(invs) = r { return invs }
throw r
}
func apiCallStatus(_ contact: Contact, _ status: String) async throws {
if let callStatus = WebRTCCallStatus.init(rawValue: status) {
try await sendCommandOkResp(.apiCallStatus(contact: contact, callStatus: callStatus))
@@ -1358,9 +1426,9 @@ func apiGetVersion() throws -> CoreVersionInfo {
throw r
}
func getAgentSubsTotal() throws -> (SMPServerSubs, Bool) {
func getAgentSubsTotal() async throws -> (SMPServerSubs, Bool) {
let userId = try currentUserId("getAgentSubsTotal")
let r = chatSendCmdSync(.getAgentSubsTotal(userId: userId), log: false)
let r = await chatSendCmd(.getAgentSubsTotal(userId: userId), log: false)
if case let .agentSubsTotal(_, subsTotal, hasSession) = r { return (subsTotal, hasSession) }
logger.error("getAgentSubsTotal error: \(String(describing: r))")
throw r
@@ -1455,7 +1523,7 @@ func startChat(refreshInvitations: Bool = true) throws {
try getUserChatData()
NtfManager.shared.setNtfBadgeCount(m.totalUnreadCountForAllUsers())
if (refreshInvitations) {
try refreshCallInvitations()
Task { try await refreshCallInvitations() }
}
(m.savedToken, m.tokenStatus, m.notificationMode, m.notificationServer) = apiGetNtfToken()
_ = try apiStartChat()
@@ -1529,7 +1597,7 @@ func getUserChatData() throws {
m.userAddress = try apiGetUserAddress()
m.chatItemTTL = try getChatItemTTL()
let chats = try apiGetChats()
m.chats = chats.map { Chat.init($0) }
m.updateChats(chats)
}
private func getUserChatDataAsync() async throws {
@@ -1541,12 +1609,12 @@ private func getUserChatDataAsync() async throws {
await MainActor.run {
m.userAddress = userAddress
m.chatItemTTL = chatItemTTL
m.chats = chats.map { Chat.init($0) }
m.updateChats(chats)
}
} else {
await MainActor.run {
m.userAddress = nil
m.chats = []
m.updateChats([])
}
}
}
@@ -1596,6 +1664,7 @@ func processReceivedMsg(_ res: ChatResponse) async {
await TerminalItems.shared.add(.resp(.now, res))
}
let m = ChatModel.shared
let n = NetworkModel.shared
logger.debug("processReceivedMsg: \(res.responseType)")
switch res {
case let .contactDeletedByContact(user, contact):
@@ -1618,7 +1687,7 @@ func processReceivedMsg(_ res: ChatResponse) async {
NtfManager.shared.notifyContactConnected(user, contact)
}
await MainActor.run {
m.setContactNetworkStatus(contact, .connected)
n.setContactNetworkStatus(contact, .connected)
}
case let .contactConnecting(user, contact):
if active(user) && contact.directOrUsed {
@@ -1641,7 +1710,7 @@ func processReceivedMsg(_ res: ChatResponse) async {
}
}
await MainActor.run {
m.setContactNetworkStatus(contact, .connected)
n.setContactNetworkStatus(contact, .connected)
}
case let .receivedContactRequest(user, contactRequest):
if active(user) {
@@ -1675,7 +1744,7 @@ func processReceivedMsg(_ res: ChatResponse) async {
if active(user) && m.hasChat(mergedContact.id) {
await MainActor.run {
if m.chatId == mergedContact.id {
m.chatId = intoContact.id
ItemsModel.shared.loadOpenChat(mergedContact.id)
}
m.removeChat(mergedContact.id)
}
@@ -1683,27 +1752,27 @@ func processReceivedMsg(_ res: ChatResponse) async {
case let .networkStatus(status, connections):
// dispatch queue to synchronize access
networkStatusesLock.sync {
var ns = m.networkStatuses
var ns = n.networkStatuses
// slow loop is on the background thread
for cId in connections {
ns[cId] = status
}
// fast model update is on the main thread
DispatchQueue.main.sync {
m.networkStatuses = ns
n.networkStatuses = ns
}
}
case let .networkStatuses(_, statuses): ()
// dispatch queue to synchronize access
networkStatusesLock.sync {
var ns = m.networkStatuses
var ns = n.networkStatuses
// slow loop is on the background thread
for s in statuses {
ns[s.agentConnId] = s.networkStatus
}
// fast model update is on the main thread
DispatchQueue.main.sync {
m.networkStatuses = ns
n.networkStatuses = ns
}
}
case let .newChatItem(user, aChatItem):
@@ -1844,7 +1913,7 @@ func processReceivedMsg(_ res: ChatResponse) async {
}
if let contact = memberContact {
await MainActor.run {
m.setContactNetworkStatus(contact, .connected)
n.setContactNetworkStatus(contact, .connected)
}
}
case let .groupUpdated(user, toGroup):
@@ -2025,6 +2094,8 @@ func processReceivedMsg(_ res: ChatResponse) async {
m.remoteCtrlSession = nil
dismissAllSheets() {
switch rcStopReason {
case .disconnected:
()
case .connectionFailed(.errorAgent(.RCP(.identity))):
AlertManager.shared.showAlertMsg(
title: "Connection with desktop stopped",
@@ -2067,12 +2138,13 @@ func processReceivedMsg(_ res: ChatResponse) async {
func switchToLocalSession() {
let m = ChatModel.shared
let n = NetworkModel.shared
m.remoteCtrlSession = nil
do {
m.users = try listUsers()
try getUserChatData()
let statuses = (try apiGetNetworkStatuses()).map { s in (s.agentConnId, s.networkStatus) }
m.networkStatuses = Dictionary(uniqueKeysWithValues: statuses)
n.networkStatuses = Dictionary(uniqueKeysWithValues: statuses)
} catch let error {
logger.debug("error updating chat data: \(responseError(error))")
}
@@ -2095,32 +2167,42 @@ func chatItemSimpleUpdate(_ user: any UserLike, _ aChatItem: AChatItem) async {
}
}
func refreshCallInvitations() throws {
func refreshCallInvitations() async throws {
let m = ChatModel.shared
let callInvitations = try justRefreshCallInvitations()
if let (chatId, ntfAction) = m.ntfCallInvitationAction,
let invitation = m.callInvitations.removeValue(forKey: chatId) {
m.ntfCallInvitationAction = nil
CallController.shared.callAction(invitation: invitation, action: ntfAction)
} else if let invitation = callInvitations.last(where: { $0.user.showNotifications }) {
activateCall(invitation)
let callInvitations = try await apiGetCallInvitations()
await MainActor.run {
m.callInvitations = callsByChat(callInvitations)
if let (chatId, ntfAction) = m.ntfCallInvitationAction,
let invitation = m.callInvitations.removeValue(forKey: chatId) {
m.ntfCallInvitationAction = nil
CallController.shared.callAction(invitation: invitation, action: ntfAction)
} else if let invitation = callInvitations.last(where: { $0.user.showNotifications }) {
activateCall(invitation)
}
}
}
func justRefreshCallInvitations() throws -> [RcvCallInvitation] {
let m = ChatModel.shared
let callInvitations = try apiGetCallInvitations()
m.callInvitations = callInvitations.reduce(into: [ChatId: RcvCallInvitation]()) { result, inv in result[inv.contact.id] = inv }
return callInvitations
func justRefreshCallInvitations() async throws {
let callInvitations = try apiGetCallInvitationsSync()
await MainActor.run {
ChatModel.shared.callInvitations = callsByChat(callInvitations)
}
}
private func callsByChat(_ callInvitations: [RcvCallInvitation]) -> [ChatId: RcvCallInvitation] {
callInvitations.reduce(into: [ChatId: RcvCallInvitation]()) {
result, inv in result[inv.contact.id] = inv
}
}
func activateCall(_ callInvitation: RcvCallInvitation) {
if !callInvitation.user.showNotifications { return }
let m = ChatModel.shared
logger.debug("reportNewIncomingCall activeCallUUID \(String(describing: m.activeCall?.callUUID)) invitationUUID \(String(describing: callInvitation.callUUID))")
if !callInvitation.user.showNotifications || m.activeCall?.callUUID == callInvitation.callUUID { return }
CallController.shared.reportNewIncomingCall(invitation: callInvitation) { error in
if let error = error {
DispatchQueue.main.async {
m.callInvitations[callInvitation.contact.id]?.callkitUUID = nil
m.callInvitations[callInvitation.contact.id]?.callUUID = nil
}
logger.error("reportNewIncomingCall error: \(error.localizedDescription)")
} else {
+13 -10
View File
@@ -58,6 +58,7 @@ struct SimpleXApp: App {
}
.onChange(of: scenePhase) { phase in
logger.debug("scenePhase was \(String(describing: scenePhase)), now \(String(describing: phase))")
AppSheetState.shared.scenePhaseActive = phase == .active
switch (phase) {
case .background:
// --- authentication
@@ -82,9 +83,11 @@ struct SimpleXApp: App {
if appState != .stopped {
startChatAndActivate {
if appState.inactive && chatModel.chatRunning == true {
updateChats()
if !chatModel.showCallView && !CallController.shared.hasActiveCalls() {
updateCallInvitations()
Task {
await updateChats()
if !chatModel.showCallView && !CallController.shared.hasActiveCalls() {
await updateCallInvitations()
}
}
}
}
@@ -129,16 +132,16 @@ struct SimpleXApp: App {
}
}
private func updateChats() {
private func updateChats() async {
do {
let chats = try apiGetChats()
chatModel.updateChats(with: chats)
let chats = try await apiGetChatsAsync()
await MainActor.run { chatModel.updateChats(chats) }
if let id = chatModel.chatId,
let chat = chatModel.getChat(id) {
loadChat(chat: chat)
Task { await loadChat(chat: chat, clearItems: false) }
}
if let ncr = chatModel.ntfContactRequest {
chatModel.ntfContactRequest = nil
await MainActor.run { chatModel.ntfContactRequest = nil }
if case let .contactRequest(contactRequest) = chatModel.getChat(ncr.chatId)?.chatInfo {
Task { await acceptContactRequest(incognito: ncr.incognito, contactRequest: contactRequest) }
}
@@ -148,9 +151,9 @@ struct SimpleXApp: App {
}
}
private func updateCallInvitations() {
private func updateCallInvitations() async {
do {
try refreshCallInvitations()
try await refreshCallInvitations()
} catch let error {
logger.error("apiGetCallInvitations: cannot update call invitations \(responseError(error))")
}
+2 -2
View File
@@ -91,8 +91,8 @@ var systemInDarkThemeCurrently: Bool {
return UITraitCollection.current.userInterfaceStyle == .dark
}
func reactOnDarkThemeChanges() {
if currentThemeDefault.get() == DefaultTheme.SYSTEM_THEME_NAME && CurrentColors.colors.isLight == systemInDarkThemeCurrently {
func reactOnDarkThemeChanges(_ inDarkNow: Bool) {
if currentThemeDefault.get() == DefaultTheme.SYSTEM_THEME_NAME && CurrentColors.colors.isLight == inDarkNow {
// Change active colors from light to dark and back based on system theme
ThemeManager.applyTheme(DefaultTheme.SYSTEM_THEME_NAME)
}
+1 -1
View File
@@ -53,7 +53,7 @@ class ThemeManager {
return perUserTheme
}
let defaultTheme = defaultActiveTheme(appSettingsTheme)
return ThemeModeOverride(mode: CurrentColors.base.mode, colors: defaultTheme?.colors ?? ThemeColors(), wallpaper: defaultTheme?.wallpaper)
return ThemeModeOverride(mode: CurrentColors.base.mode, colors: defaultTheme?.colors ?? ThemeColors(), wallpaper: defaultTheme?.wallpaper ?? ThemeWallpaper.from(PresetWallpaper.school.toType(CurrentColors.base), nil, nil))
}
static func currentColors(_ themeOverridesForType: WallpaperType?, _ perChatTheme: ThemeModeOverride?, _ perUserTheme: ThemeModeOverrides?, _ appSettingsTheme: [ThemeOverrides]) -> ActiveTheme {
@@ -185,7 +185,7 @@ struct ActiveCallView: View {
case .ended:
closeCallView(client)
call.callState = .ended
if let uuid = call.callkitUUID {
if let uuid = call.callUUID {
CallController.shared.endCall(callUUID: uuid)
}
case .ok:
@@ -382,7 +382,7 @@ struct ActiveCallOverlay: View {
private func endCallButton() -> some View {
let cc = CallController.shared
return callButton("phone.down.fill", width: 60, height: 60) {
if let uuid = call.callkitUUID {
if let uuid = call.callUUID {
cc.endCall(callUUID: uuid)
} else {
cc.endCall(call: call) {}
@@ -462,9 +462,9 @@ struct ActiveCallOverlay: View {
struct ActiveCallOverlay_Previews: PreviewProvider {
static var previews: some View {
Group{
ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callkitUUID: UUID(), callState: .offerSent, localMedia: .video), client: WebRTCClient(Binding.constant(nil), { _ in }, Binding.constant(nil)))
ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callUUID: UUID().uuidString.lowercased(), callState: .offerSent, localMedia: .video), client: WebRTCClient(Binding.constant(nil), { _ in }, Binding.constant(nil)))
.background(.black)
ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callkitUUID: UUID(), callState: .offerSent, localMedia: .audio), client: WebRTCClient(Binding.constant(nil), { _ in }, Binding.constant(nil)))
ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callUUID: UUID().uuidString.lowercased(), callState: .offerSent, localMedia: .audio), client: WebRTCClient(Binding.constant(nil), { _ in }, Binding.constant(nil)))
.background(.black)
}
}
+96 -44
View File
@@ -51,7 +51,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
logger.debug("CallController.provider CXStartCallAction")
if callManager.startOutgoingCall(callUUID: action.callUUID) {
if callManager.startOutgoingCall(callUUID: action.callUUID.uuidString.lowercased()) {
action.fulfill()
provider.reportOutgoingCall(with: action.callUUID, startedConnectingAt: nil)
} else {
@@ -61,12 +61,30 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
logger.debug("CallController.provider CXAnswerCallAction")
if callManager.answerIncomingCall(callUUID: action.callUUID) {
// WebRTC call should be in connected state to fulfill.
// Otherwise no audio and mic working on lockscreen
fulfillOnConnect = action
} else {
action.fail()
Task {
let chatIsReady = await waitUntilChatStarted(timeoutMs: 30_000, stepMs: 500)
logger.debug("CallController chat started \(chatIsReady) \(ChatModel.shared.chatInitialized) \(ChatModel.shared.chatRunning == true) \(String(describing: AppChatState.shared.value))")
if !chatIsReady {
action.fail()
return
}
if !ChatModel.shared.callInvitations.values.contains(where: { inv in inv.callUUID == action.callUUID.uuidString.lowercased() }) {
try? await justRefreshCallInvitations()
logger.debug("CallController: updated call invitations chat")
}
await MainActor.run {
logger.debug("CallController.provider will answer on call")
if callManager.answerIncomingCall(callUUID: action.callUUID.uuidString.lowercased()) {
logger.debug("CallController.provider answered on call")
// WebRTC call should be in connected state to fulfill.
// Otherwise no audio and mic working on lockscreen
fulfillOnConnect = action
} else {
logger.debug("CallController.provider will fail the call")
action.fail()
}
}
}
}
@@ -75,7 +93,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
// Should be nil here if connection was in connected state
fulfillOnConnect?.fail()
fulfillOnConnect = nil
callManager.endCall(callUUID: action.callUUID) { ok in
callManager.endCall(callUUID: action.callUUID.uuidString.lowercased()) { ok in
if ok {
action.fulfill()
} else {
@@ -86,7 +104,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
}
func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
if callManager.enableMedia(media: .audio, enable: !action.isMuted, callUUID: action.callUUID) {
if callManager.enableMedia(media: .audio, enable: !action.isMuted, callUUID: action.callUUID.uuidString.lowercased()) {
action.fulfill()
} else {
action.fail()
@@ -156,6 +174,19 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
}
}
private func waitUntilChatStarted(timeoutMs: UInt64, stepMs: UInt64) async -> Bool {
logger.debug("CallController waiting until chat started")
var t: UInt64 = 0
repeat {
if ChatModel.shared.chatInitialized, ChatModel.shared.chatRunning == true, case .active = AppChatState.shared.value {
return true
}
_ = try? await Task.sleep(nanoseconds: stepMs * 1000000)
t += stepMs
} while t < timeoutMs
return false
}
@objc(pushRegistry:didUpdatePushCredentials:forType:)
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
logger.debug("CallController: didUpdate push credentials for type \(type.rawValue)")
@@ -171,32 +202,19 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
self.reportExpiredCall(payload: payload, completion)
return
}
if (!ChatModel.shared.chatInitialized) {
logger.debug("CallController: initializing chat")
do {
try initializeChat(start: true, refreshInvitations: false)
} catch let error {
logger.error("CallController: initializing chat error: \(error)")
self.reportExpiredCall(payload: payload, completion)
return
}
}
logger.debug("CallController: initialized chat")
startChatForCall()
logger.debug("CallController: started chat")
self.shouldSuspendChat = true
// There are no invitations in the model, as it was processed by NSE
_ = try? justRefreshCallInvitations()
logger.debug("CallController: updated call invitations chat")
// logger.debug("CallController justRefreshCallInvitations: \(String(describing: m.callInvitations))")
// Extract the call information from the push notification payload
let m = ChatModel.shared
if let contactId = payload.dictionaryPayload["contactId"] as? String,
let invitation = m.callInvitations[contactId] {
let update = self.cxCallUpdate(invitation: invitation)
if let uuid = invitation.callkitUUID {
let displayName = payload.dictionaryPayload["displayName"] as? String,
let callUUID = payload.dictionaryPayload["callUUID"] as? String,
let uuid = UUID(uuidString: callUUID),
let callTsInterval = payload.dictionaryPayload["callTs"] as? TimeInterval,
let mediaStr = payload.dictionaryPayload["media"] as? String,
let media = CallMediaType(rawValue: mediaStr) {
let update = self.cxCallUpdate(contactId, displayName, media)
let callTs = Date(timeIntervalSince1970: callTsInterval)
if callTs.timeIntervalSinceNow >= -180 {
logger.debug("CallController: report pushkit call via CallKit")
let update = self.cxCallUpdate(invitation: invitation)
self.provider.reportNewIncomingCall(with: uuid, update: update) { error in
if error != nil {
m.callInvitations.removeValue(forKey: contactId)
@@ -205,11 +223,31 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
completion()
}
} else {
logger.debug("CallController will expire call 1")
self.reportExpiredCall(update: update, completion)
}
} else {
logger.debug("CallController will expire call 2")
self.reportExpiredCall(payload: payload, completion)
}
//DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
if (!ChatModel.shared.chatInitialized) {
logger.debug("CallController: initializing chat")
do {
try initializeChat(start: true, refreshInvitations: false)
} catch let error {
logger.error("CallController: initializing chat error: \(error)")
if let call = ChatModel.shared.activeCall {
self.endCall(call: call, completed: completion)
}
return
}
}
logger.debug("CallController: initialized chat")
startChatForCall()
logger.debug("CallController: started chat")
self.shouldSuspendChat = true
}
// This function fulfils the requirement to always report a call when PushKit notification is received,
@@ -239,8 +277,8 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
}
func reportNewIncomingCall(invitation: RcvCallInvitation, completion: @escaping (Error?) -> Void) {
logger.debug("CallController.reportNewIncomingCall, UUID=\(String(describing: invitation.callkitUUID))")
if CallController.useCallKit(), let uuid = invitation.callkitUUID {
logger.debug("CallController.reportNewIncomingCall, UUID=\(String(describing: invitation.callUUID))")
if CallController.useCallKit(), let callUUID = invitation.callUUID, let uuid = UUID(uuidString: callUUID) {
if invitation.callTs.timeIntervalSinceNow >= -180 {
let update = cxCallUpdate(invitation: invitation)
provider.reportNewIncomingCall(with: uuid, update: update, completion: completion)
@@ -261,6 +299,14 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
return update
}
private func cxCallUpdate(_ contactId: String, _ displayName: String, _ media: CallMediaType) -> CXCallUpdate {
let update = CXCallUpdate()
update.remoteHandle = CXHandle(type: .generic, value: contactId)
update.hasVideo = media == .video
update.localizedCallerName = displayName
return update
}
func reportIncomingCall(call: Call, connectedAt dateConnected: Date?) {
logger.debug("CallController: reporting incoming call connected")
if CallController.useCallKit() {
@@ -272,14 +318,14 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
func reportOutgoingCall(call: Call, connectedAt dateConnected: Date?) {
logger.debug("CallController: reporting outgoing call connected")
if CallController.useCallKit(), let uuid = call.callkitUUID {
if CallController.useCallKit(), let callUUID = call.callUUID, let uuid = UUID(uuidString: callUUID) {
provider.reportOutgoingCall(with: uuid, connectedAt: dateConnected)
}
}
func reportCallRemoteEnded(invitation: RcvCallInvitation) {
logger.debug("CallController: reporting remote ended")
if CallController.useCallKit(), let uuid = invitation.callkitUUID {
if CallController.useCallKit(), let callUUID = invitation.callUUID, let uuid = UUID(uuidString: callUUID) {
provider.reportCall(with: uuid, endedAt: nil, reason: .remoteEnded)
} else if invitation.contact.id == activeCallInvitation?.contact.id {
activeCallInvitation = nil
@@ -288,14 +334,17 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
func reportCallRemoteEnded(call: Call) {
logger.debug("CallController: reporting remote ended")
if CallController.useCallKit(), let uuid = call.callkitUUID {
if CallController.useCallKit(), let callUUID = call.callUUID, let uuid = UUID(uuidString: callUUID) {
provider.reportCall(with: uuid, endedAt: nil, reason: .remoteEnded)
}
}
func startCall(_ contact: Contact, _ media: CallMediaType) {
logger.debug("CallController.startCall")
let uuid = callManager.newOutgoingCall(contact, media)
let callUUID = callManager.newOutgoingCall(contact, media)
guard let uuid = UUID(uuidString: callUUID) else {
return
}
if CallController.useCallKit() {
let handle = CXHandle(type: .generic, value: contact.id)
let action = CXStartCallAction(call: uuid, handle: handle)
@@ -307,8 +356,8 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
update.localizedCallerName = contact.displayName
self.provider.reportCall(with: uuid, updated: update)
}
} else if callManager.startOutgoingCall(callUUID: uuid) {
if callManager.startOutgoingCall(callUUID: uuid) {
} else if callManager.startOutgoingCall(callUUID: callUUID) {
if callManager.startOutgoingCall(callUUID: callUUID) {
logger.debug("CallController.startCall: call started")
} else {
logger.error("CallController.startCall: no active call")
@@ -318,8 +367,8 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
func answerCall(invitation: RcvCallInvitation) {
logger.debug("CallController: answering a call")
if CallController.useCallKit(), let callUUID = invitation.callkitUUID {
requestTransaction(with: CXAnswerCallAction(call: callUUID))
if CallController.useCallKit(), let callUUID = invitation.callUUID, let uuid = UUID(uuidString: callUUID) {
requestTransaction(with: CXAnswerCallAction(call: uuid))
} else {
callManager.answerIncomingCall(invitation: invitation)
}
@@ -328,10 +377,13 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
}
}
func endCall(callUUID: UUID) {
logger.debug("CallController: ending the call with UUID \(callUUID.uuidString)")
func endCall(callUUID: String) {
let uuid = UUID(uuidString: callUUID)
logger.debug("CallController: ending the call with UUID \(callUUID)")
if CallController.useCallKit() {
requestTransaction(with: CXEndCallAction(call: callUUID))
if let uuid {
requestTransaction(with: CXEndCallAction(call: uuid))
}
} else {
callManager.endCall(callUUID: callUUID) { ok in
if ok {
+13 -13
View File
@@ -10,17 +10,17 @@ import Foundation
import SimpleXChat
class CallManager {
func newOutgoingCall(_ contact: Contact, _ media: CallMediaType) -> UUID {
let uuid = UUID()
let call = Call(direction: .outgoing, contact: contact, callkitUUID: uuid, callState: .waitCapabilities, localMedia: media)
func newOutgoingCall(_ contact: Contact, _ media: CallMediaType) -> String {
let uuid = UUID().uuidString.lowercased()
let call = Call(direction: .outgoing, contact: contact, callUUID: uuid, callState: .waitCapabilities, localMedia: media)
call.speakerEnabled = media == .video
ChatModel.shared.activeCall = call
return uuid
}
func startOutgoingCall(callUUID: UUID) -> Bool {
func startOutgoingCall(callUUID: String) -> Bool {
let m = ChatModel.shared
if let call = m.activeCall, call.callkitUUID == callUUID {
if let call = m.activeCall, call.callUUID == callUUID {
m.showCallView = true
Task { await m.callCommand.processCommand(.capabilities(media: call.localMedia)) }
return true
@@ -28,7 +28,7 @@ class CallManager {
return false
}
func answerIncomingCall(callUUID: UUID) -> Bool {
func answerIncomingCall(callUUID: String) -> Bool {
if let invitation = getCallInvitation(callUUID) {
answerIncomingCall(invitation: invitation)
return true
@@ -42,7 +42,7 @@ class CallManager {
let call = Call(
direction: .incoming,
contact: invitation.contact,
callkitUUID: invitation.callkitUUID,
callUUID: invitation.callUUID,
callState: .invitationAccepted,
localMedia: invitation.callType.media,
sharedKey: invitation.sharedKey
@@ -68,8 +68,8 @@ class CallManager {
}
}
func enableMedia(media: CallMediaType, enable: Bool, callUUID: UUID) -> Bool {
if let call = ChatModel.shared.activeCall, call.callkitUUID == callUUID {
func enableMedia(media: CallMediaType, enable: Bool, callUUID: String) -> Bool {
if let call = ChatModel.shared.activeCall, call.callUUID == callUUID {
let m = ChatModel.shared
Task { await m.callCommand.processCommand(.media(media: media, enable: enable)) }
return true
@@ -77,8 +77,8 @@ class CallManager {
return false
}
func endCall(callUUID: UUID, completed: @escaping (Bool) -> Void) {
if let call = ChatModel.shared.activeCall, call.callkitUUID == callUUID {
func endCall(callUUID: String, completed: @escaping (Bool) -> Void) {
if let call = ChatModel.shared.activeCall, call.callUUID == callUUID {
endCall(call: call) { completed(true) }
} else if let invitation = getCallInvitation(callUUID) {
endCall(invitation: invitation) { completed(true) }
@@ -126,8 +126,8 @@ class CallManager {
}
}
private func getCallInvitation(_ callUUID: UUID) -> RcvCallInvitation? {
if let (_, invitation) = ChatModel.shared.callInvitations.first(where: { (_, inv) in inv.callkitUUID == callUUID }) {
private func getCallInvitation(_ callUUID: String) -> RcvCallInvitation? {
if let (_, invitation) = ChatModel.shared.callInvitations.first(where: { (_, inv) in inv.callUUID == callUUID }) {
return invitation
}
return nil
+3 -3
View File
@@ -18,7 +18,7 @@ class Call: ObservableObject, Equatable {
var direction: CallDirection
var contact: Contact
var callkitUUID: UUID?
var callUUID: String?
var localMedia: CallMediaType
@Published var callState: CallState
@Published var localCapabilities: CallCapabilities?
@@ -33,14 +33,14 @@ class Call: ObservableObject, Equatable {
init(
direction: CallDirection,
contact: Contact,
callkitUUID: UUID?,
callUUID: String?,
callState: CallState,
localMedia: CallMediaType,
sharedKey: String? = nil
) {
self.direction = direction
self.contact = contact
self.callkitUUID = callkitUUID
self.callUUID = callUUID
self.callState = callState
self.localMedia = localMedia
self.sharedKey = sharedKey
@@ -411,6 +411,15 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
}
func endCall() {
if #available(iOS 16.0, *) {
_endCall()
} else {
// Fixes `connection.close()` getting locked up in iOS15
DispatchQueue.global(qos: .utility).async { self._endCall() }
}
}
private func _endCall() {
guard let call = activeCall.wrappedValue else { return }
logger.debug("WebRTCClient: ending the call")
activeCall.wrappedValue = nil
+480 -78
View File
@@ -92,19 +92,23 @@ struct ChatInfoView: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@Environment(\.dismiss) var dismiss: DismissAction
@ObservedObject var networkModel = NetworkModel.shared
@ObservedObject var chat: Chat
@State var contact: Contact
@Binding var connectionStats: ConnectionStats?
@Binding var customUserProfile: Profile?
@State var localAlias: String
@Binding var connectionCode: String?
var onSearch: () -> Void
@State private var connectionStats: ConnectionStats? = nil
@State private var customUserProfile: Profile? = nil
@State private var connectionCode: String? = nil
@FocusState private var aliasTextFieldFocused: Bool
@State private var alert: ChatInfoViewAlert? = nil
@State private var showDeleteContactActionSheet = false
@State private var actionSheet: SomeActionSheet? = nil
@State private var sheet: SomeSheet<AnyView>? = nil
@State private var showConnectContactViaAddressDialog = false
@State private var sendReceipts = SendReceipts.userDefault(true)
@State private var sendReceiptsUserDefault = true
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
enum ChatInfoViewAlert: Identifiable {
case clearChatAlert
case networkStatusAlert
@@ -112,6 +116,7 @@ struct ChatInfoView: View {
case abortSwitchAddressAlert
case syncConnectionForceAlert
case queueInfo(info: String)
case someAlert(alert: SomeAlert)
case error(title: LocalizedStringKey, error: LocalizedStringKey?)
var id: String {
@@ -122,11 +127,12 @@ struct ChatInfoView: View {
case .abortSwitchAddressAlert: return "abortSwitchAddressAlert"
case .syncConnectionForceAlert: return "syncConnectionForceAlert"
case let .queueInfo(info): return "queueInfo \(info)"
case let .someAlert(alert): return "chatInfoSomeAlert \(alert.id)"
case let .error(title, _): return "error \(title)"
}
}
}
var body: some View {
NavigationView {
List {
@@ -136,12 +142,29 @@ struct ChatInfoView: View {
.onTapGesture {
aliasTextFieldFocused = false
}
Group {
localAliasTextEdit()
}
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
.padding(.bottom, 18)
GeometryReader { g in
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) }
muteButton(width: buttonWidth)
}
}
.padding(.trailing)
.frame(maxWidth: .infinity)
.frame(height: infoViewActionButtonHeight)
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 8))
if let customUserProfile = customUserProfile {
Section(header: Text("Incognito").foregroundColor(theme.colors.secondary)) {
@@ -153,7 +176,7 @@ struct ChatInfoView: View {
}
}
}
Section {
Group {
if let code = connectionCode { verifyCodeButton(code) }
@@ -173,14 +196,18 @@ struct ChatInfoView: View {
} label: {
Label("Chat theme", systemImage: "photo")
}
// } else if developerTools {
// synchronizeConnectionButtonForce()
// }
}
.disabled(!contact.ready || !contact.active)
if let conn = contact.activeConn {
Section {
infoRow(Text(String("E2E encryption")), conn.connPQEnabled ? "Quantum resistant" : "Standard")
}
}
if let contactLink = contact.contactLink {
Section {
SimpleXLinkQRCode(uri: contactLink)
@@ -197,7 +224,7 @@ struct ChatInfoView: View {
.foregroundColor(theme.colors.secondary)
}
}
if contact.ready && contact.active {
Section(header: Text("Servers").foregroundColor(theme.colors.secondary)) {
networkStatusRow()
@@ -226,12 +253,12 @@ struct ChatInfoView: View {
}
}
}
Section {
clearChatButton()
deleteContactButton()
}
if developerTools {
Section(header: Text("For console").foregroundColor(theme.colors.secondary)) {
infoRow("Local name", chat.chatInfo.localDisplayName)
@@ -260,6 +287,24 @@ struct ChatInfoView: View {
sendReceiptsUserDefault = currentUser.sendRcptsContacts
}
sendReceipts = SendReceipts.fromBool(contact.chatSettings.sendRcpts, userDefault: sendReceiptsUserDefault)
Task {
do {
let (stats, profile) = try await apiContactInfo(chat.chatInfo.apiId)
let (ct, code) = try await apiGetContactCode(chat.chatInfo.apiId)
await MainActor.run {
connectionStats = stats
customUserProfile = profile
connectionCode = code
if contact.activeConn?.connectionCode != ct.activeConn?.connectionCode {
chat.chatInfo = .direct(contact: ct)
}
}
} catch let error {
logger.error("apiContactInfo or apiGetContactCode error: \(responseError(error))")
}
}
}
.alert(item: $alert) { alertItem in
switch(alertItem) {
@@ -269,37 +314,26 @@ struct ChatInfoView: View {
case .abortSwitchAddressAlert: return abortSwitchAddressAlert(abortSwitchContactAddress)
case .syncConnectionForceAlert: return syncConnectionForceAlert({ syncContactConnection(force: true) })
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)
}
}
.actionSheet(isPresented: $showDeleteContactActionSheet) {
if contact.sndReady && contact.active {
return ActionSheet(
title: Text("Delete contact?\nThis cannot be undone!"),
buttons: [
.destructive(Text("Delete and notify contact")) { deleteContact(notify: true) },
.destructive(Text("Delete")) { deleteContact(notify: false) },
.cancel()
]
)
.actionSheet(item: $actionSheet) { $0.actionSheet }
.sheet(item: $sheet) {
if #available(iOS 16.0, *) {
$0.content
.presentationDetents([.fraction(0.4)])
} else {
return ActionSheet(
title: Text("Delete contact?\nThis cannot be undone!"),
buttons: [
.destructive(Text("Delete")) { deleteContact() },
.cancel()
]
)
$0.content
}
}
}
private func contactInfoHeader() -> some View {
VStack {
VStack(spacing: 8) {
let cInfo = chat.chatInfo
ChatInfoImage(chat: chat, size: 192, color: Color(uiColor: .tertiarySystemFill))
.padding(.top, 12)
.padding()
.padding(.vertical, 12)
if contact.verified {
(
Text(Image(systemName: "checkmark.shield"))
@@ -328,7 +362,7 @@ struct ChatInfoView: View {
}
.frame(maxWidth: .infinity, alignment: .center)
}
private func localAliasTextEdit() -> some View {
TextField("Set contact name…", text: $localAlias)
.disableAutocorrection(true)
@@ -345,7 +379,7 @@ struct ChatInfoView: View {
.multilineTextAlignment(.center)
.foregroundColor(theme.colors.secondary)
}
private func setContactAlias() {
Task {
do {
@@ -360,6 +394,25 @@ struct ChatInfoView: View {
}
}
private func searchButton(width: CGFloat) -> some View {
InfoViewButton(image: "magnifyingglass", title: "search", width: width) {
dismiss()
onSearch()
}
.disabled(!contact.ready || chat.chatItems.isEmpty)
}
private func muteButton(width: CGFloat) -> some View {
InfoViewButton(
image: chat.chatInfo.ntfsEnabled ? "speaker.slash.fill" : "speaker.wave.2.fill",
title: chat.chatInfo.ntfsEnabled ? "mute" : "unmute",
width: width
) {
toggleNotifications(chat, enableNtfs: !chat.chatInfo.ntfsEnabled)
}
.disabled(!contact.ready || !contact.active)
}
private func verifyCodeButton(_ code: String) -> some View {
NavigationLink {
VerifyCodeView(
@@ -389,7 +442,7 @@ struct ChatInfoView: View {
)
}
}
private func contactPreferencesButton() -> some View {
NavigationLink {
ContactPreferencesView(
@@ -404,7 +457,7 @@ struct ChatInfoView: View {
Label("Contact preferences", systemImage: "switch.2")
}
}
private func sendReceiptsOption() -> some View {
Picker(selection: $sendReceipts) {
ForEach([.yes, .no, .userDefault(sendReceiptsUserDefault)]) { (opt: SendReceipts) in
@@ -418,13 +471,13 @@ struct ChatInfoView: View {
setSendReceipts()
}
}
private func setSendReceipts() {
var chatSettings = chat.chatInfo.chatSettings ?? ChatSettings.defaults
chatSettings.sendRcpts = sendReceipts.bool()
updateChatSettings(chat, chatSettings: chatSettings)
}
private func synchronizeConnectionButton() -> some View {
Button {
syncContactConnection(force: false)
@@ -433,7 +486,7 @@ struct ChatInfoView: View {
.foregroundColor(.orange)
}
}
private func synchronizeConnectionButtonForce() -> some View {
Button {
alert = .syncConnectionForceAlert
@@ -442,7 +495,7 @@ struct ChatInfoView: View {
.foregroundColor(.red)
}
}
private func networkStatusRow() -> some View {
HStack {
Text("Network status")
@@ -450,28 +503,35 @@ struct ChatInfoView: View {
.foregroundColor(theme.colors.primary)
.font(.system(size: 14))
Spacer()
Text(chatModel.contactNetworkStatus(contact).statusString)
Text(networkModel.contactNetworkStatus(contact).statusString)
.foregroundColor(theme.colors.secondary)
serverImage()
}
}
private func serverImage() -> some View {
let status = chatModel.contactNetworkStatus(contact)
let status = networkModel.contactNetworkStatus(contact)
return Image(systemName: status.imageName)
.foregroundColor(status == .connected ? .green : theme.colors.secondary)
.font(.system(size: 12))
}
private func deleteContactButton() -> some View {
Button(role: .destructive) {
showDeleteContactActionSheet = true
deleteContactDialog(
chat,
contact,
dismissToChatList: true,
showAlert: { alert = .someAlert(alert: $0) },
showActionSheet: { actionSheet = $0 },
showSheetContent: { sheet = $0 }
)
} label: {
Label("Delete contact", systemImage: "trash")
Label("Delete contact", systemImage: "person.badge.minus")
.foregroundColor(Color.red)
}
}
private func clearChatButton() -> some View {
Button() {
alert = .clearChatAlert
@@ -480,26 +540,7 @@ struct ChatInfoView: View {
.foregroundColor(Color.orange)
}
}
private func deleteContact(notify: Bool? = nil) {
Task {
do {
try await apiDeleteChat(type: chat.chatInfo.chatType, id: chat.chatInfo.apiId, notify: notify)
await MainActor.run {
dismiss()
chatModel.chatId = nil
chatModel.removeChat(chat.chatInfo.id)
}
} catch let error {
logger.error("deleteContactAlert apiDeleteChat error: \(responseError(error))")
let a = getErrorAlert(error, "Error deleting contact")
await MainActor.run {
alert = .error(title: a.title, error: a.message)
}
}
}
}
private func clearChatAlert() -> Alert {
Alert(
title: Text("Clear conversation?"),
@@ -513,14 +554,14 @@ struct ChatInfoView: View {
secondaryButton: .cancel()
)
}
private func networkStatusAlert() -> Alert {
Alert(
title: Text("Network status"),
message: Text(chatModel.contactNetworkStatus(contact).statusExplanation)
message: Text(networkModel.contactNetworkStatus(contact).statusExplanation)
)
}
private func switchContactAddress() {
Task {
do {
@@ -539,7 +580,7 @@ struct ChatInfoView: View {
}
}
}
private func abortSwitchContactAddress() {
Task {
do {
@@ -557,7 +598,7 @@ struct ChatInfoView: View {
}
}
}
private func syncContactConnection(force: Bool) {
Task {
do {
@@ -578,6 +619,160 @@ struct ChatInfoView: View {
}
}
struct AudioCallButton: View {
var chat: Chat
var contact: Contact
var width: CGFloat
var showAlert: (SomeAlert) -> Void
var body: some View {
CallButton(
chat: chat,
contact: contact,
image: "phone.fill",
title: "call",
mediaType: .audio,
width: width,
showAlert: showAlert
)
}
}
struct VideoButton: View {
var chat: Chat
var contact: Contact
var width: CGFloat
var showAlert: (SomeAlert) -> Void
var body: some View {
CallButton(
chat: chat,
contact: contact,
image: "video.fill",
title: "video",
mediaType: .video,
width: width,
showAlert: showAlert
)
}
}
private struct CallButton: View {
var chat: Chat
var contact: Contact
var image: String
var title: LocalizedStringKey
var mediaType: CallMediaType
var width: CGFloat
var showAlert: (SomeAlert) -> Void
var body: some View {
let canCall = contact.ready && contact.active && chat.chatInfo.featureEnabled(.calls) && ChatModel.shared.activeCall == nil
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)
}
}
} else if contact.nextSendGrpInv {
showAlert(SomeAlert(
alert: mkAlert(
title: "Can't call contact",
message: "Send message to enable calls."
),
id: "can't call contact, send message"
))
} else if !contact.active {
showAlert(SomeAlert(
alert: mkAlert(
title: "Can't call contact",
message: "Contact is deleted."
),
id: "can't call contact, contact deleted"
))
} else if !contact.ready {
showAlert(SomeAlert(
alert: mkAlert(
title: "Can't call contact",
message: "Connecting to contact, please wait or check later!"
),
id: "can't call contact, contact not ready"
))
} else if !chat.chatInfo.featureEnabled(.calls) {
switch chat.chatInfo.showEnableCallsAlert {
case .userEnable:
showAlert(SomeAlert(
alert: Alert(
title: Text("Allow calls?"),
message: Text("You need to allow your contact to call to be able to call them."),
primaryButton: .default(Text("Allow")) {
allowFeatureToContact(contact, .calls)
},
secondaryButton: .cancel()
),
id: "allow calls"
))
case .askContact:
showAlert(SomeAlert(
alert: mkAlert(
title: "Calls prohibited!",
message: "Please ask your contact to enable calls."
),
id: "calls prohibited, ask contact"
))
case .other:
showAlert(SomeAlert(
alert: mkAlert(
title: "Calls prohibited!",
message: "Please check yours and your contact preferences."
)
, id: "calls prohibited, other"
))
}
} else {
showAlert(SomeAlert(
alert: mkAlert(title: "Can't call contact"),
id: "can't call contact"
))
}
}
.disabled(ChatModel.shared.activeCall != nil)
}
}
let infoViewActionButtonHeight: CGFloat = 60
struct InfoViewButton: View {
var image: String
var title: LocalizedStringKey
var disabledLook: Bool = false
var width: CGFloat
var action: () -> Void
var body: some View {
VStack(spacing: 4) {
Image(systemName: image)
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
Text(title)
.font(.caption)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.foregroundColor(.accentColor)
.background(Color(.secondarySystemGroupedBackground))
.cornerRadius(10.0)
.frame(width: width, height: infoViewActionButtonHeight)
.disabled(disabledLook)
.onTapGesture(perform: action)
}
}
struct ChatWallpaperEditorSheet: View {
@Environment(\.dismiss) var dismiss
@EnvironmentObject var theme: AppTheme
@@ -747,7 +942,7 @@ func syncConnectionForceAlert(_ syncConnectionForce: @escaping () -> Void) -> Al
)
}
func queueInfoText(_ info: (RcvMsgInfo?, QueueInfo)) -> String {
func queueInfoText(_ info: (RcvMsgInfo?, ServerQueueInfo)) -> String {
let (rcvMsgInfo, qInfo) = info
var msgInfo: String
if let rcvMsgInfo { msgInfo = encodeJSON(rcvMsgInfo) } else { msgInfo = "none" }
@@ -763,15 +958,222 @@ func queueInfoAlert(_ info: String) -> Alert {
)
}
func deleteContactDialog(
_ chat: Chat,
_ contact: Contact,
dismissToChatList: Bool,
showAlert: @escaping (SomeAlert) -> Void,
showActionSheet: @escaping (SomeActionSheet) -> Void,
showSheetContent: @escaping (SomeSheet<AnyView>) -> Void
) {
if contact.sndReady && contact.active && !contact.chatDeleted {
deleteContactOrConversationDialog(chat, contact, dismissToChatList, showAlert, showActionSheet, showSheetContent)
} else if contact.sndReady && contact.active && contact.chatDeleted {
deleteContactWithoutConversation(chat, contact, dismissToChatList, showAlert, showActionSheet)
} else { // !(contact.sndReady && contact.active)
deleteNotReadyContact(chat, contact, dismissToChatList, showAlert, showActionSheet)
}
}
private func deleteContactOrConversationDialog(
_ chat: Chat,
_ contact: Contact,
_ dismissToChatList: Bool,
_ showAlert: @escaping (SomeAlert) -> Void,
_ showActionSheet: @escaping (SomeActionSheet) -> Void,
_ showSheetContent: @escaping (SomeSheet<AnyView>) -> Void
) {
showActionSheet(SomeActionSheet(
actionSheet: ActionSheet(
title: Text("Delete contact?"),
buttons: [
.destructive(Text("Only delete conversation")) {
deleteContactMaybeErrorAlert(chat, contact, chatDeleteMode: .messages, dismissToChatList, showAlert)
},
.destructive(Text("Delete contact")) {
showSheetContent(SomeSheet(
content: { AnyView(
DeleteActiveContactDialog(
chat: chat,
contact: contact,
dismissToChatList: dismissToChatList,
showAlert: showAlert
)
) },
id: "DeleteActiveContactDialog"
))
},
.cancel()
]
),
id: "deleteContactOrConversationDialog"
))
}
private func deleteContactMaybeErrorAlert(
_ chat: Chat,
_ contact: Contact,
chatDeleteMode: ChatDeleteMode,
_ dismissToChatList: Bool,
_ showAlert: @escaping (SomeAlert) -> Void
) {
Task {
let alert_ = await deleteContactChat(chat, chatDeleteMode: chatDeleteMode)
if let alert = alert_ {
showAlert(SomeAlert(alert: alert, id: "deleteContactMaybeErrorAlert, error"))
} else {
if dismissToChatList {
await MainActor.run {
ChatModel.shared.chatId = nil
}
DispatchQueue.main.async {
dismissAllSheets(animated: true) {
if case .messages = chatDeleteMode, showDeleteConversationNoticeDefault.get() {
AlertManager.shared.showAlert(deleteConversationNotice(contact))
} else if chatDeleteMode.isEntity, showDeleteContactNoticeDefault.get() {
AlertManager.shared.showAlert(deleteContactNotice(contact))
}
}
}
} else {
if case .messages = chatDeleteMode, showDeleteConversationNoticeDefault.get() {
showAlert(SomeAlert(alert: deleteConversationNotice(contact), id: "deleteContactMaybeErrorAlert, deleteConversationNotice"))
} else if chatDeleteMode.isEntity, showDeleteContactNoticeDefault.get() {
showAlert(SomeAlert(alert: deleteContactNotice(contact), id: "deleteContactMaybeErrorAlert, deleteContactNotice"))
}
}
}
}
}
private func deleteConversationNotice(_ contact: Contact) -> Alert {
return Alert(
title: Text("Conversation deleted!"),
message: Text("You can send messages to \(contact.displayName) from Archived contacts."),
primaryButton: .default(Text("Don't show again")) {
showDeleteConversationNoticeDefault.set(false)
},
secondaryButton: .default(Text("Ok"))
)
}
private func deleteContactNotice(_ contact: Contact) -> Alert {
return Alert(
title: Text("Contact deleted!"),
message: Text("You can still view conversation with \(contact.displayName) in the list of chats."),
primaryButton: .default(Text("Don't show again")) {
showDeleteContactNoticeDefault.set(false)
},
secondaryButton: .default(Text("Ok"))
)
}
enum ContactDeleteMode {
case full
case entity
public func toChatDeleteMode(notify: Bool) -> ChatDeleteMode {
switch self {
case .full: .full(notify: notify)
case .entity: .entity(notify: notify)
}
}
}
struct DeleteActiveContactDialog: View {
@Environment(\.dismiss) var dismiss
@EnvironmentObject var theme: AppTheme
var chat: Chat
var contact: Contact
var dismissToChatList: Bool
var showAlert: (SomeAlert) -> Void
@State private var keepConversation = false
var body: some View {
NavigationView {
List {
Section {
Toggle("Keep conversation", isOn: $keepConversation)
Button(role: .destructive) {
dismiss()
deleteContactMaybeErrorAlert(chat, contact, chatDeleteMode: contactDeleteMode.toChatDeleteMode(notify: false), dismissToChatList, showAlert)
} label: {
Text("Delete without notification")
}
Button(role: .destructive) {
dismiss()
deleteContactMaybeErrorAlert(chat, contact, chatDeleteMode: contactDeleteMode.toChatDeleteMode(notify: true), dismissToChatList, showAlert)
} label: {
Text("Delete and notify contact")
}
} footer: {
Text("Contact will be deleted - this cannot be undone!")
.foregroundColor(theme.colors.secondary)
}
}
.modifier(ThemedBackground(grouped: true))
}
}
var contactDeleteMode: ContactDeleteMode {
keepConversation ? .entity : .full
}
}
private func deleteContactWithoutConversation(
_ chat: Chat,
_ contact: Contact,
_ dismissToChatList: Bool,
_ showAlert: @escaping (SomeAlert) -> Void,
_ showActionSheet: @escaping (SomeActionSheet) -> Void
) {
showActionSheet(SomeActionSheet(
actionSheet: ActionSheet(
title: Text("Confirm contact deletion?"),
buttons: [
.destructive(Text("Delete and notify contact")) {
deleteContactMaybeErrorAlert(chat, contact, chatDeleteMode: .full(notify: true), dismissToChatList, showAlert)
},
.destructive(Text("Delete without notification")) {
deleteContactMaybeErrorAlert(chat, contact, chatDeleteMode: .full(notify: false), dismissToChatList, showAlert)
},
.cancel()
]
),
id: "deleteContactWithoutConversation"
))
}
private func deleteNotReadyContact(
_ chat: Chat,
_ contact: Contact,
_ dismissToChatList: Bool,
_ showAlert: @escaping (SomeAlert) -> Void,
_ showActionSheet: @escaping (SomeActionSheet) -> Void
) {
showActionSheet(SomeActionSheet(
actionSheet: ActionSheet(
title: Text("Confirm contact deletion?"),
buttons: [
.destructive(Text("Confirm")) {
deleteContactMaybeErrorAlert(chat, contact, chatDeleteMode: .full(notify: false), dismissToChatList, showAlert)
},
.cancel()
]
),
id: "deleteNotReadyContact"
))
}
struct ChatInfoView_Previews: PreviewProvider {
static var previews: some View {
ChatInfoView(
chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []),
contact: Contact.sampleData,
connectionStats: Binding.constant(nil),
customUserProfile: Binding.constant(nil),
localAlias: "",
connectionCode: Binding.constant(nil)
onSearch: {}
)
}
}
@@ -294,7 +294,6 @@ struct FramedItemView: View {
.padding(.horizontal, 12)
.overlay(DetermineWidth())
.frame(minWidth: 0, alignment: .leading)
.textSelection(.enabled)
if let mediaWidth = maxMediaWidth(), mediaWidth < maxWidth {
v.frame(maxWidth: mediaWidth, alignment: .leading)
@@ -97,7 +97,7 @@ struct ChatItemForwardingView: View {
)
} else {
composeState = ComposeState.init(forwardingItem: ci, fromChatInfo: fromChatInfo)
chatModel.chatId = chat.id
ItemsModel.shared.loadOpenChat(chat.id)
}
}
} label: {
@@ -351,7 +351,7 @@ struct ChatItemInfoView: View {
Button {
Task {
await MainActor.run {
chatModel.chatId = forwardedFromItem.chatInfo.id
ItemsModel.shared.loadOpenChat(forwardedFromItem.chatInfo.id)
dismiss()
}
}
@@ -440,7 +440,7 @@ struct ChatItemInfoView: View {
private func memberDeliveryStatusView(_ member: GroupMember, _ status: GroupSndStatus, _ sentViaProxy: Bool?) -> some View {
HStack{
ProfileImage(imageStr: member.image, size: 30)
MemberProfileImage(member, size: 30)
.padding(.trailing, 2)
Text(member.chatViewName)
.lineLimit(1)
+152 -147
View File
@@ -47,65 +47,69 @@ struct ChatView: View {
@State private var showDeleteSelectedMessages: Bool = false
@State private var allowToDeleteSelectedMessagesForAll: Bool = false
@AppStorage(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial
var body: some View {
if #available(iOS 16.0, *) {
let v = viewBody
.scrollDismissesKeyboard(.immediately)
.keyboardPadding()
if (searchMode) {
v.toolbarBackground(.thinMaterial, for: .navigationBar)
} else {
v.toolbarBackground(.visible, for: .navigationBar)
}
viewBody
.scrollDismissesKeyboard(.immediately)
.toolbarBackground(.hidden, for: .navigationBar)
} else {
viewBody
}
}
@ViewBuilder
private var viewBody: some View {
let cInfo = chat.chatInfo
return VStack(spacing: 0) {
if searchMode {
searchToolbar()
ZStack {
let wallpaperImage = theme.wallpaper.type.image
let wallpaperType = theme.wallpaper.type
let backgroundColor = theme.wallpaper.background ?? wallpaperType.defaultBackgroundColor(theme.base, theme.colors.background)
let tintColor = theme.wallpaper.tint ?? wallpaperType.defaultTintColor(theme.base)
Color.clear.ignoresSafeArea(.all)
.if(wallpaperImage != nil) { view in
view.modifier(
ChatViewBackground(image: wallpaperImage!, imageType: wallpaperType, background: backgroundColor, tint: tintColor)
)
}
VStack(spacing: 0) {
ZStack(alignment: .bottomTrailing) {
chatItemsList()
floatingButtons(counts: floatingButtonModel.unreadChatItemCounts)
}
connectingText()
if selectedChatItems == nil {
ComposeView(
chat: chat,
composeState: $composeState,
keyboardVisible: $keyboardVisible
)
.disabled(!cInfo.sendMsgEnabled)
} else {
SelectedItemsBottomToolbar(
chatItems: ItemsModel.shared.reversedChatItems,
selectedChatItems: $selectedChatItems,
chatInfo: chat.chatInfo,
deleteItems: { forAll in
allowToDeleteSelectedMessagesForAll = forAll
showDeleteSelectedMessages = true
},
moderateItems: {
if case let .group(groupInfo) = chat.chatInfo {
showModerateSelectedMessagesAlert(groupInfo)
}
}
)
}
}
}
.safeAreaInset(edge: .top) {
VStack(spacing: .zero) {
if searchMode { searchToolbar() }
Divider()
}
ZStack(alignment: .bottomTrailing) {
let wallpaperImage = theme.wallpaper.type.image
let wallpaperType = theme.wallpaper.type
let backgroundColor = theme.wallpaper.background ?? wallpaperType.defaultBackgroundColor(theme.base, theme.colors.background)
let tintColor = theme.wallpaper.tint ?? wallpaperType.defaultTintColor(theme.base)
chatItemsList()
.if(wallpaperImage != nil) { view in
view.modifier(
ChatViewBackground(image: wallpaperImage!, imageType: wallpaperType, background: backgroundColor, tint: tintColor)
)
}
floatingButtons(counts: floatingButtonModel.unreadChatItemCounts)
}
connectingText()
if selectedChatItems == nil {
ComposeView(
chat: chat,
composeState: $composeState,
keyboardVisible: $keyboardVisible
)
.disabled(!cInfo.sendMsgEnabled)
} else {
SelectedItemsBottomToolbar(
chatItems: ItemsModel.shared.reversedChatItems,
selectedChatItems: $selectedChatItems,
chatInfo: chat.chatInfo,
deleteItems: { forAll in
allowToDeleteSelectedMessagesForAll = forAll
showDeleteSelectedMessages = true
},
moderateItems: {
if case let .group(groupInfo) = chat.chatInfo {
showModerateSelectedMessagesAlert(groupInfo)
}
}
)
}
.background(ToolbarMaterial.material(toolbarMaterial))
}
.navigationTitle(cInfo.chatViewName)
.background(theme.colors.background)
@@ -125,16 +129,23 @@ struct ChatView: View {
}
}
}
.appSheet(item: $selectedMember) { member in
Group {
if case let .group(groupInfo) = chat.chatInfo {
GroupMemberInfoView(groupInfo: groupInfo, groupMember: member, navigation: true)
}
}
}
.onAppear {
loadChat(chat: chat)
initChatView()
selectedChatItems = nil
initChatView()
}
.onChange(of: chatModel.chatId) { cId in
showChatInfoSheet = false
selectedChatItems = nil
scrollModel.scrollToBottom()
stopAudioPlayer()
if let cId {
selectedChatItems = nil
if let c = chatModel.getChat(cId) {
chat = c
}
@@ -147,8 +158,10 @@ struct ChatView: View {
.onChange(of: revealedChatItem) { _ in
NotificationCenter.postReverseListNeedsLayout()
}
.onChange(of: im.reversedChatItems) { reversedChatItems in
if reversedChatItems.count <= loadItemsPerPage && filtered(reversedChatItems).count < 10 {
.onChange(of: im.isLoading) { isLoading in
if !isLoading,
im.reversedChatItems.count <= loadItemsPerPage,
filtered(im.reversedChatItems).count < 10 {
loadChatItems(chat.chatInfo)
}
}
@@ -157,7 +170,6 @@ struct ChatView: View {
VideoPlayerView.players.removeAll()
stopAudioPlayer()
if chatModel.chatId == cInfo.id && !presentationMode.wrappedValue.isPresented {
chatModel.chatId = nil
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
if chatModel.chatId == nil {
chatModel.chatItemStatuses = [:]
@@ -179,32 +191,18 @@ struct ChatView: View {
} else if case let .direct(contact) = cInfo {
Button {
Task {
do {
let (stats, profile) = try await apiContactInfo(chat.chatInfo.apiId)
let (ct, code) = try await apiGetContactCode(chat.chatInfo.apiId)
await MainActor.run {
connectionStats = stats
customUserProfile = profile
connectionCode = code
if contact.activeConn?.connectionCode != ct.activeConn?.connectionCode {
chat.chatInfo = .direct(contact: ct)
}
}
} catch let error {
logger.error("apiContactInfo or apiGetContactCode error: \(responseError(error))")
}
await MainActor.run { showChatInfoSheet = true }
showChatInfoSheet = true
}
} label: {
ChatInfoToolbar(chat: chat)
}
.appSheet(isPresented: $showChatInfoSheet, onDismiss: {
connectionStats = nil
customUserProfile = nil
connectionCode = nil
theme = buildTheme()
}) {
ChatInfoView(chat: chat, contact: contact, connectionStats: $connectionStats, customUserProfile: $customUserProfile, localAlias: chat.chatInfo.localAlias, connectionCode: $connectionCode)
.appSheet(isPresented: $showChatInfoSheet, onDismiss: { theme = buildTheme() }) {
ChatInfoView(
chat: chat,
contact: contact,
localAlias: chat.chatInfo.localAlias,
onSearch: { focusSearch() }
)
}
} else if case let .group(groupInfo) = cInfo {
Button {
@@ -222,7 +220,8 @@ struct ChatView: View {
chat.chatInfo = .group(groupInfo: gInfo)
chat.created = Date.now
}
)
),
onSearch: { focusSearch() }
)
}
} else if case .local = cInfo {
@@ -230,6 +229,7 @@ struct ChatView: View {
}
}
ToolbarItem(placement: .navigationBarTrailing) {
let isLoading = im.isLoading && im.showLoadingProgress
if selectedChatItems != nil {
Button {
withAnimation {
@@ -252,19 +252,23 @@ struct ChatView: View {
}
}
Menu {
if callsPrefEnabled && chatModel.activeCall == nil {
Button {
CallController.shared.startCall(contact, .video)
} label: {
Label("Video call", systemImage: "video")
if !isLoading {
if callsPrefEnabled && chatModel.activeCall == nil {
Button {
CallController.shared.startCall(contact, .video)
} label: {
Label("Video call", systemImage: "video")
}
.disabled(!contact.ready || !contact.active)
}
.disabled(!contact.ready || !contact.active)
searchButton()
ToggleNtfsButton(chat: chat)
.disabled(!contact.ready || !contact.active)
}
searchButton()
ToggleNtfsButton(chat: chat)
.disabled(!contact.ready || !contact.active)
} label: {
Image(systemName: "ellipsis")
.tint(isLoading ? Color.clear : nil)
.overlay { if isLoading { ProgressView() } }
}
}
case let .group(groupInfo):
@@ -289,10 +293,14 @@ struct ChatView: View {
}
}
Menu {
searchButton()
ToggleNtfsButton(chat: chat)
if !isLoading {
searchButton()
ToggleNtfsButton(chat: chat)
}
} label: {
Image(systemName: "ellipsis")
.tint(isLoading ? Color.clear : nil)
.overlay { if isLoading { ProgressView() } }
}
}
case .local:
@@ -358,14 +366,11 @@ struct ChatView: View {
searchText = ""
searchMode = false
searchFocussed = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
loadChat(chat: chat)
}
Task { await loadChat(chat: chat) }
}
}
.padding(.horizontal)
.padding(.vertical, 8)
.background(.thinMaterial)
}
private func voiceWithoutFrame(_ ci: ChatItem) -> Bool {
@@ -376,7 +381,7 @@ struct ChatView: View {
reversedChatItems
.enumerated()
.filter { (index, chatItem) in
if let mergeCategory = chatItem.mergeCategory, index > .zero {
if let mergeCategory = chatItem.mergeCategory, index > 0 {
mergeCategory != reversedChatItems[index - 1].mergeCategory
} else {
true
@@ -399,31 +404,42 @@ struct ChatView: View {
: voiceNoFrame
? (g.size.width - 32)
: (g.size.width - 32) * 0.84
return chatItemView(ci, maxWidth)
.onAppear {
floatingButtonModel.appeared(viewId: ci.viewId)
}
.onDisappear {
floatingButtonModel.disappeared(viewId: ci.viewId)
}
.id(ci.id) // Required to trigger `onAppear` on iOS15
return ChatItemWithMenu(
chat: $chat,
chatItem: ci,
maxWidth: maxWidth,
composeState: $composeState,
selectedMember: $selectedMember,
revealedChatItem: $revealedChatItem,
selectedChatItems: $selectedChatItems
)
.onAppear {
floatingButtonModel.appeared(viewId: ci.viewId)
}
.onDisappear {
floatingButtonModel.disappeared(viewId: ci.viewId)
}
.id(ci.id) // Required to trigger `onAppear` on iOS15
} loadPage: {
loadChatItems(cInfo)
}
.onTapGesture { hideKeyboard() }
.onChange(of: searchText) { _ in
loadChat(chat: chat, search: searchText)
}
.onChange(of: chatModel.chatId) { chatId in
if let chatId, let c = chatModel.getChat(chatId) {
chat = c
showChatInfoSheet = false
loadChat(chat: c)
.opacity(ItemsModel.shared.isLoading ? 0 : 1)
.padding(.vertical, -InvertedTableView.inset)
.onTapGesture { hideKeyboard() }
.onChange(of: searchText) { _ in
Task { await loadChat(chat: chat, search: searchText) }
}
.onChange(of: im.reversedChatItems) { _ in
floatingButtonModel.chatItemsChanged()
}
.onChange(of: im.itemAdded) { added in
if added {
im.itemAdded = false
if floatingButtonModel.unreadChatItemCounts.isReallyNearBottom {
scrollModel.scrollToBottom()
}
}
.onChange(of: im.reversedChatItems) { _ in
floatingButtonModel.chatItemsChanged()
}
}
}
}
@@ -456,19 +472,19 @@ struct ChatView: View {
init() {
unreadChatItemCounts = UnreadChatItemCounts(
isNearBottom: true,
unreadBelow: .zero
isReallyNearBottom: true,
unreadBelow: 0
)
events
.receive(on: DispatchQueue.global(qos: .background))
.scan(Set<String>()) { itemsInView, event in
return switch event {
case let .appeared(viewId):
itemsInView.union([viewId])
case let .disappeared(viewId):
itemsInView.subtracting([viewId])
case .chatItemsChanged:
itemsInView
var updated = itemsInView
switch event {
case let .appeared(viewId): updated.insert(viewId)
case let .disappeared(viewId): updated.remove(viewId)
case .chatItemsChanged: ()
}
return updated
}
.map { ChatModel.shared.unreadChatItemCounts(itemsInView: $0) }
.removeDuplicates()
@@ -552,8 +568,8 @@ struct ChatView: View {
private func endCallButton(_ call: Call) -> some View {
Button {
if let uuid = call.callkitUUID {
CallController.shared.endCall(callUUID: uuid)
if CallController.useCallKit(), let callUUID = call.callUUID {
CallController.shared.endCall(callUUID: callUUID)
} else {
CallController.shared.endCall(call: call) {}
}
@@ -564,14 +580,18 @@ struct ChatView: View {
private func searchButton() -> some View {
Button {
searchMode = true
searchFocussed = true
searchText = ""
focusSearch()
} label: {
Label("Search", systemImage: "magnifyingglass")
}
}
private func focusSearch() {
searchMode = true
searchFocussed = true
searchText = ""
}
private func addMembersButton() -> some View {
Button {
if case let .group(gInfo) = chat.chatInfo {
@@ -672,22 +692,10 @@ struct ChatView: View {
VoiceItemState.chatView = [:]
}
@ViewBuilder private func chatItemView(_ ci: ChatItem, _ maxWidth: CGFloat) -> some View {
ChatItemWithMenu(
chat: chat,
chatItem: ci,
maxWidth: maxWidth,
composeState: $composeState,
selectedMember: $selectedMember,
revealedChatItem: $revealedChatItem,
selectedChatItems: $selectedChatItems
)
}
private struct ChatItemWithMenu: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@ObservedObject var chat: Chat
@Binding @ObservedObject var chat: Chat
let chatItem: ChatItem
let maxWidth: CGFloat
@Binding var composeState: ComposeState
@@ -717,7 +725,7 @@ struct ChatView: View {
Group {
if revealed, let range = range {
let items = Array(zip(Array(range), im.reversedChatItems[range]))
ForEach(items, id: \.1.viewId) { (i, ci) in
ForEach(items.reversed(), id: \.1.viewId) { (i, ci) in
let prev = i == prevHidden ? prevItem : im.reversedChatItems[i + 1]
chatItemView(ci, nil, prev)
.overlay {
@@ -813,10 +821,10 @@ struct ChatView: View {
.padding(.trailing, 12)
}
HStack(alignment: .top, spacing: 8) {
ProfileImage(imageStr: member.memberProfile.image, size: memberImageSize, backgroundColor: theme.colors.background)
MemberProfileImage(member, size: memberImageSize, backgroundColor: theme.colors.background)
.onTapGesture {
if m.membersLoaded {
selectedMember = m.getGroupMember(member.groupMemberId)
if let member = m.getGroupMember(member.groupMemberId) {
selectedMember = member
} else {
Task {
await m.loadGroupMembers(groupInfo) {
@@ -825,9 +833,6 @@ struct ChatView: View {
}
}
}
.appSheet(item: $selectedMember) { member in
GroupMemberInfoView(groupInfo: groupInfo, groupMember: member, navigation: true)
}
chatItemWithMenu(ci, range, maxWidth)
}
}
@@ -1098,7 +1103,7 @@ struct ChatView: View {
}
func reactions(from: Int? = nil, till: Int? = nil) -> some View {
ForEach(availableReactions[(from ?? .zero)..<(till ?? availableReactions.count)]) { reaction in
ForEach(availableReactions[(from ?? 0)..<(till ?? availableReactions.count)]) { reaction in
Button(reaction.text) {
setReaction(chatItem, add: true, reaction: reaction)
}
@@ -55,7 +55,7 @@ struct ComposeLinkView: View {
.foregroundColor(theme.colors.secondary)
}
.padding(.vertical, 5)
.frame(maxWidth: .infinity, minHeight: 60, maxHeight: 60)
.frame(maxWidth: .infinity, minHeight: 60)
}
}
}
@@ -281,6 +281,7 @@ struct ComposeView: View {
@State private var stopPlayback: Bool = false
@AppStorage(DEFAULT_PRIVACY_SAVE_LAST_DRAFT) private var saveLastDraft = true
@AppStorage(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial
var body: some View {
VStack(spacing: 0) {
@@ -381,7 +382,11 @@ struct ComposeView: View {
}
}
}
.background(.thinMaterial)
.background {
Color.clear
.overlay(ToolbarMaterial.material(toolbarMaterial))
.ignoresSafeArea(.all, edges: .bottom)
}
.onChange(of: composeState.message) { msg in
if composeState.linkPreviewAllowed {
if msg.count > 0 {
@@ -47,14 +47,13 @@ struct AddGroupMembersViewCommon: View {
var body: some View {
if creatingGroup {
NavigationView {
addGroupMembersView()
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button ("Skip") { addedMembersCb(selectedContacts) }
}
addGroupMembersView()
.navigationBarBackButtonHidden()
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button ("Skip") { addedMembersCb(selectedContacts) }
}
}
}
} else {
addGroupMembersView()
}
@@ -17,10 +17,12 @@ struct GroupChatInfoView: View {
@Environment(\.dismiss) var dismiss: DismissAction
@ObservedObject var chat: Chat
@Binding var groupInfo: GroupInfo
var onSearch: () -> Void
@State private var alert: GroupChatInfoViewAlert? = nil
@State private var groupLink: String?
@State private var groupLinkMemberRole: GroupMemberRole = .member
@State private var showAddMembersSheet: Bool = false
@State private var groupLinkNavLinkActive: Bool = false
@State private var addMembersNavLinkActive: Bool = false
@State private var connectionStats: ConnectionStats?
@State private var connectionCode: String?
@State private var sendReceipts = SendReceipts.userDefault(true)
@@ -68,6 +70,15 @@ struct GroupChatInfoView: View {
List {
groupInfoHeader()
.listRowBackground(Color.clear)
.padding(.bottom, 18)
infoActionButtons()
.padding(.horizontal)
.frame(maxWidth: .infinity)
.frame(height: infoViewActionButtonHeight)
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
Section {
if groupInfo.canEdit {
@@ -174,7 +185,6 @@ struct GroupChatInfoView: View {
logger.error("GroupChatInfoView apiGetGroupLink: \(responseError(error))")
}
}
.keyboardPadding()
}
private func groupInfoHeader() -> some View {
@@ -198,24 +208,95 @@ struct GroupChatInfoView: View {
.frame(maxWidth: .infinity, alignment: .center)
}
func infoActionButtons() -> some View {
GeometryReader { g in
let buttonWidth = g.size.width / 4
HStack(alignment: .center, spacing: 8) {
searchButton(width: buttonWidth)
if groupInfo.canAddMembers {
addMembersActionButton(width: buttonWidth)
}
muteButton(width: buttonWidth)
}
.frame(maxWidth: .infinity, alignment: .center)
}
}
private func searchButton(width: CGFloat) -> some View {
InfoViewButton(image: "magnifyingglass", title: "search", width: width) {
dismiss()
onSearch()
}
.disabled(!groupInfo.ready || chat.chatItems.isEmpty)
}
@ViewBuilder private func addMembersActionButton(width: CGFloat) -> some View {
if chat.chatInfo.incognito {
ZStack {
InfoViewButton(image: "link.badge.plus", title: "invite", width: width) {
groupLinkNavLinkActive = true
}
NavigationLink(isActive: $groupLinkNavLinkActive) {
groupLinkDestinationView()
} label: {
EmptyView()
}
.frame(width: 1, height: 1)
.hidden()
}
.disabled(!groupInfo.ready)
} else {
ZStack {
InfoViewButton(image: "person.fill.badge.plus", title: "invite", width: width) {
addMembersNavLinkActive = true
}
NavigationLink(isActive: $addMembersNavLinkActive) {
addMembersDestinationView()
} label: {
EmptyView()
}
.frame(width: 1, height: 1)
.hidden()
}
.disabled(!groupInfo.ready)
}
}
private func muteButton(width: CGFloat) -> some View {
InfoViewButton(
image: chat.chatInfo.ntfsEnabled ? "speaker.slash.fill" : "speaker.wave.2.fill",
title: chat.chatInfo.ntfsEnabled ? "mute" : "unmute",
width: width
) {
toggleNotifications(chat, enableNtfs: !chat.chatInfo.ntfsEnabled)
}
.disabled(!groupInfo.ready)
}
private func addMembersButton() -> some View {
NavigationLink {
AddGroupMembersView(chat: chat, groupInfo: groupInfo)
.onAppear {
searchFocussed = false
Task {
let groupMembers = await apiListMembers(groupInfo.groupId)
await MainActor.run {
chatModel.groupMembers = groupMembers.map { GMember.init($0) }
chatModel.populateGroupMembersIndexes()
}
}
}
addMembersDestinationView()
} label: {
Label("Invite members", systemImage: "plus")
}
}
private func addMembersDestinationView() -> some View {
AddGroupMembersView(chat: chat, groupInfo: groupInfo)
.onAppear {
searchFocussed = false
Task {
let groupMembers = await apiListMembers(groupInfo.groupId)
await MainActor.run {
chatModel.groupMembers = groupMembers.map { GMember.init($0) }
chatModel.populateGroupMembersIndexes()
}
}
}
}
private struct MemberRowView: View {
var groupInfo: GroupInfo
@ObservedObject var groupMember: GMember
@@ -226,7 +307,7 @@ struct GroupChatInfoView: View {
var body: some View {
let member = groupMember.wrapped
let v = HStack{
ProfileImage(imageStr: member.image, size: 38)
MemberProfileImage(member, size: 38)
.padding(.trailing, 2)
// TODO server connection status
VStack(alignment: .leading) {
@@ -352,16 +433,7 @@ struct GroupChatInfoView: View {
private func groupLinkButton() -> some View {
NavigationLink {
GroupLinkView(
groupId: groupInfo.groupId,
groupLink: $groupLink,
groupLinkMemberRole: $groupLinkMemberRole,
showTitle: false,
creatingGroup: false
)
.navigationBarTitle("Group link")
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(.large)
groupLinkDestinationView()
} label: {
if groupLink == nil {
Label("Create group link", systemImage: "link.badge.plus")
@@ -371,6 +443,19 @@ struct GroupChatInfoView: View {
}
}
private func groupLinkDestinationView() -> some View {
GroupLinkView(
groupId: groupInfo.groupId,
groupLink: $groupLink,
groupLinkMemberRole: $groupLinkMemberRole,
showTitle: false,
creatingGroup: false
)
.navigationBarTitle("Group link")
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(.large)
}
private func editGroupButton() -> some View {
NavigationLink {
GroupProfileView(
@@ -577,7 +662,8 @@ struct GroupChatInfoView_Previews: PreviewProvider {
static var previews: some View {
GroupChatInfoView(
chat: Chat(chatInfo: ChatInfo.sampleData.group, chatItems: []),
groupInfo: Binding.constant(GroupInfo.sampleData)
groupInfo: Binding.constant(GroupInfo.sampleData),
onSearch: {}
)
}
}
@@ -34,14 +34,13 @@ struct GroupLinkView: View {
var body: some View {
if creatingGroup {
NavigationView {
groupLinkView()
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button ("Continue") { linkCreatedCb?() }
}
groupLinkView()
.navigationBarBackButtonHidden()
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button ("Continue") { linkCreatedCb?() }
}
}
}
} else {
groupLinkView()
}
@@ -37,6 +37,7 @@ struct GroupMemberInfoView: View {
case syncConnectionForceAlert
case planAndConnectAlert(alert: PlanAndConnectAlert)
case queueInfo(info: String)
case someAlert(alert: SomeAlert)
case error(title: LocalizedStringKey, error: LocalizedStringKey?)
var id: String {
@@ -52,6 +53,7 @@ struct GroupMemberInfoView: View {
case .syncConnectionForceAlert: return "syncConnectionForceAlert"
case let .planAndConnectAlert(alert): return "planAndConnectAlert \(alert.id)"
case let .queueInfo(info): return "queueInfo \(info)"
case let .someAlert(alert): return "someAlert \(alert.id)"
case let .error(title, _): return "error \(title)"
}
}
@@ -65,10 +67,11 @@ struct GroupMemberInfoView: View {
}
}
private func knownDirectChat(_ contactId: Int64) -> Chat? {
private func knownDirectChat(_ contactId: Int64) -> (Chat, Contact)? {
if let chat = chatModel.getContactChat(contactId),
chat.chatInfo.contact?.directOrUsed == true {
return chat
let contact = chat.chatInfo.contact,
contact.directOrUsed == true {
return (chat, contact)
} else {
return nil
}
@@ -80,21 +83,22 @@ struct GroupMemberInfoView: View {
List {
groupMemberInfoHeader(member)
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
.padding(.bottom, 18)
infoActionButtons(member)
.padding(.horizontal)
.frame(maxWidth: .infinity)
.frame(height: infoViewActionButtonHeight)
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
if member.memberActive {
Section {
if let contactId = member.memberContactId, let chat = knownDirectChat(contactId) {
knownDirectChatButton(chat)
} else if groupInfo.fullGroupPreferences.directMessages.on(for: groupInfo.membership) {
if let contactId = member.memberContactId {
newDirectChatButton(contactId)
} else if member.activeConn?.peerChatVRange.isCompatibleRange(CREATE_MEMBER_CONTACT_VRANGE) ?? false {
createMemberContactButton()
}
}
if let code = connectionCode { verifyCodeButton(code) }
if let connStats = connectionStats,
connStats.ratchetSyncAllowed {
connStats.ratchetSyncAllowed {
synchronizeConnectionButton()
}
// } else if developerTools {
@@ -237,6 +241,7 @@ struct GroupMemberInfoView: View {
case .syncConnectionForceAlert: return syncConnectionForceAlert({ syncMemberConnection(force: true) })
case let .planAndConnectAlert(alert): return planAndConnectAlert(alert, dismiss: true)
case let .queueInfo(info): return queueInfoAlert(info)
case let .someAlert(a): return a.alert
case let .error(title, error): return mkAlert(title: title, message: error)
}
}
@@ -249,6 +254,57 @@ struct GroupMemberInfoView: View {
.modifier(ThemedBackground(grouped: true))
}
func infoActionButtons(_ member: GroupMember) -> some 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) {
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) }
} else if groupInfo.fullGroupPreferences.directMessages.on(for: groupInfo.membership) {
if let contactId = member.memberContactId {
newDirectChatButton(contactId, width: buttonWidth)
} else if member.activeConn?.peerChatVRange.isCompatibleRange(CREATE_MEMBER_CONTACT_VRANGE) ?? false {
createMemberContactButton(width: buttonWidth)
}
InfoViewButton(image: "phone.fill", title: "call", disabledLook: true, width: buttonWidth) { showSendMessageToEnableCallsAlert()
}
InfoViewButton(image: "video.fill", title: "video", disabledLook: true, width: buttonWidth) { showSendMessageToEnableCallsAlert()
}
} else { // no known contact chat && directMessages are off
InfoViewButton(image: "message.fill", title: "message", disabledLook: true, width: buttonWidth) { showDirectMessagesProhibitedAlert("Can't message member")
}
InfoViewButton(image: "phone.fill", title: "call", disabledLook: true, width: buttonWidth) { showDirectMessagesProhibitedAlert("Can't call member")
}
InfoViewButton(image: "video.fill", title: "video", disabledLook: true, width: buttonWidth) { showDirectMessagesProhibitedAlert("Can't call member")
}
}
}
.frame(maxWidth: .infinity, alignment: .center)
}
}
func showSendMessageToEnableCallsAlert() {
alert = .someAlert(alert: SomeAlert(
alert: mkAlert(
title: "Can't call member",
message: "Send message to enable calls."
),
id: "can't call member, send message"
))
}
func showDirectMessagesProhibitedAlert(_ title: LocalizedStringKey) {
alert = .someAlert(alert: SomeAlert(
alert: mkAlert(
title: title,
message: "Direct messages between members are prohibited in this group."
),
id: "can't message member, direct messages prohibited"
))
}
func connectViaAddressButton(_ contactLink: String) -> some View {
Button {
planAndConnect(
@@ -263,36 +319,32 @@ struct GroupMemberInfoView: View {
}
}
func knownDirectChatButton(_ chat: Chat) -> some View {
Button {
dismissAllSheets(animated: true)
DispatchQueue.main.async {
chatModel.chatId = chat.id
}
} label: {
Label("Send direct message", systemImage: "message")
}
}
func newDirectChatButton(_ contactId: Int64) -> some View {
Button {
do {
let chat = try apiGetChat(type: .direct, id: contactId)
chatModel.addChat(chat)
func knownDirectChatButton(_ chat: Chat, width: CGFloat) -> some View {
InfoViewButton(image: "message.fill", title: "message", width: width) {
ItemsModel.shared.loadOpenChat(chat.id) {
dismissAllSheets(animated: true)
DispatchQueue.main.async {
chatModel.chatId = chat.id
}
} catch let error {
logger.error("openDirectChatButton apiGetChat error: \(responseError(error))")
}
} label: {
Label("Send direct message", systemImage: "message")
}
}
func createMemberContactButton() -> some View {
Button {
func newDirectChatButton(_ contactId: Int64, width: CGFloat) -> some View {
InfoViewButton(image: "message.fill", title: "message", width: width) {
Task {
do {
let chat = try await apiGetChat(type: .direct, id: contactId)
chatModel.addChat(chat)
ItemsModel.shared.loadOpenChat(chat.id) {
dismissAllSheets(animated: true)
}
} catch let error {
logger.error("openDirectChatButton apiGetChat error: \(responseError(error))")
}
}
}
}
func createMemberContactButton(width: CGFloat) -> some View {
InfoViewButton(image: "message.fill", title: "message", width: width) {
progressIndicator = true
Task {
do {
@@ -300,9 +352,10 @@ struct GroupMemberInfoView: View {
await MainActor.run {
progressIndicator = false
chatModel.addChat(Chat(chatInfo: .direct(contact: memberContact)))
dismissAllSheets(animated: true)
chatModel.chatId = memberContact.id
chatModel.setContactNetworkStatus(memberContact, .connected)
ItemsModel.shared.loadOpenChat(memberContact.id) {
dismissAllSheets(animated: true)
}
NetworkModel.shared.setContactNetworkStatus(memberContact, .connected)
}
} catch let error {
logger.error("createMemberContactButton apiCreateMemberContact error: \(responseError(error))")
@@ -313,14 +366,12 @@ struct GroupMemberInfoView: View {
}
}
}
} label: {
Label("Send direct message", systemImage: "message")
}
}
private func groupMemberInfoHeader(_ mem: GroupMember) -> some View {
VStack {
ProfileImage(imageStr: mem.image, size: 192, color: Color(uiColor: .tertiarySystemFill))
MemberProfileImage(mem, size: 192, color: Color(uiColor: .tertiarySystemFill))
.padding(.top, 12)
.padding()
if mem.verified {
@@ -582,6 +633,21 @@ struct GroupMemberInfoView: View {
}
}
func MemberProfileImage(
_ mem: GroupMember,
size: CGFloat,
color: Color = Color(uiColor: .tertiarySystemGroupedBackground),
backgroundColor: Color? = nil
) -> some View {
ProfileImage(
imageStr: mem.image,
size: size,
color: color,
backgroundColor: backgroundColor,
blurred: mem.blocked
)
}
func blockMemberAlert(_ gInfo: GroupInfo, _ mem: GroupMember) -> Alert {
Alert(
title: Text("Block member?"),
+54 -12
View File
@@ -11,7 +11,6 @@ import Combine
/// A List, which displays it's items in reverse order - from bottom to top
struct ReverseList<Item: Identifiable & Hashable & Sendable, Content: View>: UIViewControllerRepresentable {
let items: Array<Item>
@Binding var scrollState: ReverseListScrollModel<Item>.State
@@ -33,7 +32,7 @@ struct ReverseList<Item: Identifiable & Hashable & Sendable, Content: View>: UIV
case let .item(id):
controller.scroll(to: items.firstIndex(where: { $0.id == id }), position: .bottom)
case .bottom:
controller.scroll(to: .zero, position: .top)
controller.scroll(to: 0, position: .top)
}
} else {
controller.update(items: items)
@@ -45,7 +44,7 @@ struct ReverseList<Item: Identifiable & Hashable & Sendable, Content: View>: UIV
private enum Section { case main }
private let representer: ReverseList
private var dataSource: UITableViewDiffableDataSource<Section, Item>!
private var itemCount: Int = .zero
private var itemCount: Int = 0
private var bag = Set<AnyCancellable>()
init(representer: ReverseList) {
@@ -53,6 +52,7 @@ struct ReverseList<Item: Identifiable & Hashable & Sendable, Content: View>: UIV
super.init(style: .plain)
// 1. Style
tableView = InvertedTableView()
tableView.separatorStyle = .none
tableView.transform = .verticalFlip
tableView.backgroundColor = .clear
@@ -80,7 +80,7 @@ struct ReverseList<Item: Identifiable & Hashable & Sendable, Content: View>: UIV
let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseId, for: indexPath)
if #available(iOS 16.0, *) {
cell.contentConfiguration = UIHostingConfiguration { self.representer.content(item) }
.margins(.all, .zero)
.margins(.all, 0)
.minSize(height: 1) // Passing zero will result in system default of 44 points being used
} else {
if let cell = cell as? HostingCell<Content> {
@@ -132,6 +132,11 @@ struct ReverseList<Item: Identifiable & Hashable & Sendable, Content: View>: UIV
NotificationCenter.default.post(name: .chatViewWillBeginScrolling, object: nil)
}
override func viewDidAppear(_ animated: Bool) {
tableView.clipsToBounds = false
parent?.viewIfLoaded?.clipsToBounds = false
}
/// Scrolls up
func scrollToNextPage() {
tableView.setContentOffset(
@@ -147,18 +152,23 @@ struct ReverseList<Item: Identifiable & Hashable & Sendable, Content: View>: UIV
/// Scrolls to Item at index path
/// - Parameter indexPath: Item to scroll to - will scroll to beginning of the list, if `nil`
func scroll(to index: Int?, position: UITableView.ScrollPosition) {
if let index {
var animated = false
if #available(iOS 16.0, *) {
animated = true
}
var animated = false
if #available(iOS 16.0, *) {
animated = true
}
if let index, tableView.numberOfRows(inSection: 0) != 0 {
tableView.scrollToRow(
at: IndexPath(row: index, section: .zero),
at: IndexPath(row: index, section: 0),
at: position,
animated: animated
)
Task { representer.scrollState = .atDestination }
} else {
tableView.setContentOffset(
CGPoint(x: .zero, y: -InvertedTableView.inset),
animated: animated
)
}
Task { representer.scrollState = .atDestination }
}
func update(items: Array<Item>) {
@@ -168,8 +178,15 @@ struct ReverseList<Item: Identifiable & Hashable & Sendable, Content: View>: UIV
dataSource.defaultRowAnimation = .none
dataSource.apply(
snapshot,
animatingDifferences: itemCount != .zero && abs(items.count - itemCount) == 1
animatingDifferences: itemCount != 0 && abs(items.count - itemCount) == 1
)
// Sets content offset on initial load
if itemCount == 0 {
tableView.setContentOffset(
CGPoint(x: 0, y: -InvertedTableView.inset),
animated: false
)
}
itemCount = items.count
}
}
@@ -272,3 +289,28 @@ func withConditionalAnimation<Result>(
try body()
}
}
class InvertedTableView: UITableView {
static let inset = CGFloat(100)
static let insets = UIEdgeInsets(
top: inset,
left: .zero,
bottom: inset,
right: .zero
)
override var contentInsetAdjustmentBehavior: UIScrollView.ContentInsetAdjustmentBehavior {
get { .never }
set { }
}
override var contentInset: UIEdgeInsets {
get { Self.insets }
set { }
}
override var adjustedContentInset: UIEdgeInsets {
Self.insets
}
}
@@ -104,19 +104,22 @@ struct SelectedItemsBottomToolbar: View {
allButtonsDisabled = count == 0 || count > 20
canModerate = possibleToModerate(chatInfo)
if let selected = selectedItems {
(deleteEnabled, deleteForEveryoneEnabled, moderateEnabled, _, selectedChatItems) = chatItems.reduce((true, true, true, true, [])) { (r, ci) in
let me: Bool
let onlyOwnGroupItems: Bool
(deleteEnabled, deleteForEveryoneEnabled, me, onlyOwnGroupItems, selectedChatItems) = chatItems.reduce((true, true, true, true, [])) { (r, ci) in
if selected.contains(ci.id) {
var (de, dee, me, onlyOwnGroupItems, sel) = r
de = de && ci.canBeDeletedForSelf
dee = dee && ci.meta.deletable && !ci.localNote
onlyOwnGroupItems = onlyOwnGroupItems && ci.chatDir == .groupSnd
me = me && !onlyOwnGroupItems && ci.content.msgContent != nil && ci.memberToModerate(chatInfo) != nil
me = me && ci.content.msgContent != nil && ci.memberToModerate(chatInfo) != nil
sel.insert(ci.id) // we are collecting new selected items here to account for any changes in chat items list
return (de, dee, me, onlyOwnGroupItems, sel)
} else {
return r
}
}
moderateEnabled = me && !onlyOwnGroupItems
}
}
@@ -11,7 +11,6 @@ import SwiftUI
struct ChatHelp: View {
@EnvironmentObject var chatModel: ChatModel
@Binding var showSettings: Bool
@State private var newChatMenuOption: NewChatMenuOption? = nil
var body: some View {
ScrollView { chatHelp() }
@@ -39,7 +38,7 @@ struct ChatHelp: View {
HStack(spacing: 8) {
Text("Tap button ")
NewChatMenuButton(newChatMenuOption: $newChatMenuOption)
NewChatMenuButton()
Text("above, then choose:")
}
@@ -44,17 +44,20 @@ struct ChatListNavLink: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize
@AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = false
@ObservedObject var chat: Chat
@State private var showContactRequestDialog = false
@State private var showJoinGroupDialog = false
@State private var showContactConnectionInfo = false
@State private var showInvalidJSON = false
@State private var showDeleteContactActionSheet = false
@State private var alert: SomeAlert? = nil
@State private var actionSheet: SomeActionSheet? = nil
@State private var sheet: SomeSheet<AnyView>? = nil
@State private var showConnectContactViaAddressDialog = false
@State private var inProgress = false
@State private var progressByTimeout = false
var dynamicRowHeight: CGFloat { dynamicSizes[userFont]?.rowHeight ?? 80 }
var dynamicRowHeight: CGFloat { dynamicSize(userFont).rowHeight }
var body: some View {
Group {
@@ -83,17 +86,24 @@ struct ChatListNavLink: View {
}
}
}
@ViewBuilder private func contactNavLink(_ contact: Contact) -> some View {
Group {
if contact.activeConn == nil && contact.profile.contactLink != nil {
if contact.activeConn == nil && contact.profile.contactLink != nil && contact.active {
ChatPreviewView(chat: chat, progressByTimeout: Binding.constant(false))
.frame(height: dynamicRowHeight)
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
Button {
showDeleteContactActionSheet = true
deleteContactDialog(
chat,
contact,
dismissToChatList: false,
showAlert: { alert = $0 },
showActionSheet: { actionSheet = $0 },
showSheetContent: { sheet = $0 }
)
} label: {
Label("Delete", systemImage: "trash")
deleteLabel
}
.tint(.red)
}
@@ -104,51 +114,44 @@ struct ChatListNavLink: View {
}
} else {
NavLinkPlain(
tag: chat.chatInfo.id,
chatId: chat.chatInfo.id,
selection: $chatModel.chatId,
label: { ChatPreviewView(chat: chat, progressByTimeout: Binding.constant(false)) }
)
.swipeActions(edge: .leading, allowsFullSwipe: true) {
markReadButton()
toggleFavoriteButton()
ToggleNtfsButton(chat: chat)
toggleNtfsButton(chat: chat)
}
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
if !chat.chatItems.isEmpty {
clearChatButton()
}
Button {
if contact.sndReady || !contact.active {
showDeleteContactActionSheet = true
} else {
AlertManager.shared.showAlert(deletePendingContactAlert(chat, contact))
}
deleteContactDialog(
chat,
contact,
dismissToChatList: false,
showAlert: { alert = $0 },
showActionSheet: { actionSheet = $0 },
showSheetContent: { sheet = $0 }
)
} label: {
Label("Delete", systemImage: "trash")
deleteLabel
}
.tint(.red)
}
.frame(height: dynamicRowHeight)
}
}
.actionSheet(isPresented: $showDeleteContactActionSheet) {
if contact.sndReady && contact.active {
return ActionSheet(
title: Text("Delete contact?\nThis cannot be undone!"),
buttons: [
.destructive(Text("Delete and notify contact")) { Task { await deleteChat(chat, notify: true) } },
.destructive(Text("Delete")) { Task { await deleteChat(chat, notify: false) } },
.cancel()
]
)
.alert(item: $alert) { $0.alert }
.actionSheet(item: $actionSheet) { $0.actionSheet }
.sheet(item: $sheet) {
if #available(iOS 16.0, *) {
$0.content
.presentationDetents([.fraction(0.4)])
} else {
return ActionSheet(
title: Text("Delete contact?\nThis cannot be undone!"),
buttons: [
.destructive(Text("Delete")) { Task { await deleteChat(chat) } },
.cancel()
]
)
$0.content
}
}
}
@@ -191,7 +194,7 @@ struct ChatListNavLink: View {
}
default:
NavLinkPlain(
tag: chat.chatInfo.id,
chatId: chat.chatInfo.id,
selection: $chatModel.chatId,
label: { ChatPreviewView(chat: chat, progressByTimeout: Binding.constant(false)) },
disabled: !groupInfo.ready
@@ -200,7 +203,7 @@ struct ChatListNavLink: View {
.swipeActions(edge: .leading, allowsFullSwipe: true) {
markReadButton()
toggleFavoriteButton()
ToggleNtfsButton(chat: chat)
toggleNtfsButton(chat: chat)
}
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
if !chat.chatItems.isEmpty {
@@ -218,7 +221,7 @@ struct ChatListNavLink: View {
@ViewBuilder private func noteFolderNavLink(_ noteFolder: NoteFolder) -> some View {
NavLinkPlain(
tag: chat.chatInfo.id,
chatId: chat.chatInfo.id,
selection: $chatModel.chatId,
label: { ChatPreviewView(chat: chat, progressByTimeout: Binding.constant(false)) },
disabled: !noteFolder.ready
@@ -241,7 +244,7 @@ struct ChatListNavLink: View {
await MainActor.run { inProgress = false }
}
} label: {
Label("Join", systemImage: chat.chatInfo.incognito ? "theatermasks" : "ipad.and.arrow.forward")
SwipeLabel(NSLocalizedString("Join", comment: "swipe action"), systemImage: chat.chatInfo.incognito ? "theatermasks" : "ipad.and.arrow.forward", inverted: oneHandUI)
}
.tint(chat.chatInfo.incognito ? .indigo : theme.colors.primary)
}
@@ -251,14 +254,14 @@ struct ChatListNavLink: View {
Button {
Task { await markChatRead(chat) }
} label: {
Label("Read", systemImage: "checkmark")
SwipeLabel(NSLocalizedString("Read", comment: "swipe action"), systemImage: "checkmark", inverted: oneHandUI)
}
.tint(theme.colors.primary)
} else {
Button {
Task { await markChatUnread(chat) }
} label: {
Label("Unread", systemImage: "circlebadge.fill")
SwipeLabel(NSLocalizedString("Unread", comment: "swipe action"), systemImage: "circlebadge.fill", inverted: oneHandUI)
}
.tint(theme.colors.primary)
}
@@ -270,24 +273,36 @@ struct ChatListNavLink: View {
Button {
toggleChatFavorite(chat, favorite: false)
} label: {
Label("Unfav.", systemImage: "star.slash")
SwipeLabel(NSLocalizedString("Unfav.", comment: "swipe action"), systemImage: "star.slash.fill", inverted: oneHandUI)
}
.tint(.green)
} else {
Button {
toggleChatFavorite(chat, favorite: true)
} label: {
Label("Favorite", systemImage: "star.fill")
SwipeLabel(NSLocalizedString("Favorite", comment: "swipe action"), systemImage: "star.fill", inverted: oneHandUI)
}
.tint(.green)
}
}
@ViewBuilder private func toggleNtfsButton(chat: Chat) -> some View {
Button {
toggleNotifications(chat, enableNtfs: !chat.chatInfo.ntfsEnabled)
} label: {
if chat.chatInfo.ntfsEnabled {
SwipeLabel(NSLocalizedString("Mute", comment: "swipe action"), systemImage: "speaker.slash.fill", inverted: oneHandUI)
} else {
SwipeLabel(NSLocalizedString("Unmute", comment: "swipe action"), systemImage: "speaker.wave.2.fill", inverted: oneHandUI)
}
}
}
private func clearChatButton() -> some View {
Button {
AlertManager.shared.showAlert(clearChatAlert())
} label: {
Label("Clear", systemImage: "gobackward")
SwipeLabel(NSLocalizedString("Clear", comment: "swipe action"), systemImage: "gobackward", inverted: oneHandUI)
}
.tint(Color.orange)
}
@@ -296,7 +311,7 @@ struct ChatListNavLink: View {
Button {
AlertManager.shared.showAlert(clearNoteFolderAlert())
} label: {
Label("Clear", systemImage: "gobackward")
SwipeLabel(NSLocalizedString("Clear", comment: "swipe action"), systemImage: "gobackward", inverted: oneHandUI)
}
.tint(Color.orange)
}
@@ -305,7 +320,7 @@ struct ChatListNavLink: View {
Button {
AlertManager.shared.showAlert(leaveGroupAlert(groupInfo))
} label: {
Label("Leave", systemImage: "rectangle.portrait.and.arrow.right")
SwipeLabel(NSLocalizedString("Leave", comment: "swipe action"), systemImage: "rectangle.portrait.and.arrow.right.fill", inverted: oneHandUI)
}
.tint(Color.yellow)
}
@@ -314,7 +329,7 @@ struct ChatListNavLink: View {
Button {
AlertManager.shared.showAlert(deleteGroupAlert(groupInfo))
} label: {
Label("Delete", systemImage: "trash")
deleteLabel
}
.tint(.red)
}
@@ -324,22 +339,23 @@ struct ChatListNavLink: View {
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
Button {
Task { await acceptContactRequest(incognito: false, contactRequest: contactRequest) }
} label: { Label("Accept", systemImage: "checkmark") }
} label: { SwipeLabel(NSLocalizedString("Accept", comment: "swipe action"), systemImage: "checkmark", inverted: oneHandUI) }
.tint(theme.colors.primary)
Button {
Task { await acceptContactRequest(incognito: true, contactRequest: contactRequest) }
} label: {
Label("Accept incognito", systemImage: "theatermasks")
SwipeLabel(NSLocalizedString("Accept incognito", comment: "swipe action"), systemImage: "theatermasks.fill", inverted: oneHandUI)
}
.tint(.indigo)
Button {
AlertManager.shared.showAlert(rejectContactRequestAlert(contactRequest))
} label: {
Label("Reject", systemImage: "multiply")
SwipeLabel(NSLocalizedString("Reject", comment: "swipe action"), systemImage: "multiply.fill", inverted: oneHandUI)
}
.tint(.red)
}
.frame(height: dynamicRowHeight)
.contentShape(Rectangle())
.onTapGesture { showContactRequestDialog = true }
.confirmationDialog("Accept connection request?", isPresented: $showContactRequestDialog, titleVisibility: .visible) {
Button("Accept") { Task { await acceptContactRequest(incognito: false, contactRequest: contactRequest) } }
@@ -356,14 +372,14 @@ struct ChatListNavLink: View {
AlertManager.shared.showAlertMsg(title: a.title, message: a.message)
})
} label: {
Label("Delete", systemImage: "trash")
deleteLabel
}
.tint(.red)
Button {
showContactConnectionInfo = true
} label: {
Label("Name", systemImage: "pencil")
SwipeLabel(NSLocalizedString("Name", comment: "swipe action"), systemImage: "pencil", inverted: oneHandUI)
}
.tint(theme.colors.primary)
}
@@ -377,11 +393,16 @@ struct ChatListNavLink: View {
}
}
}
.contentShape(Rectangle())
.onTapGesture {
showContactConnectionInfo = true
}
}
private var deleteLabel: some View {
SwipeLabel(NSLocalizedString("Delete", comment: "swipe action"), systemImage: "trash.fill", inverted: oneHandUI)
}
private func deleteGroupAlert(_ groupInfo: GroupInfo) -> Alert {
Alert(
title: Text("Delete group?"),
@@ -430,28 +451,6 @@ struct ChatListNavLink: View {
)
}
private func rejectContactRequestAlert(_ contactRequest: UserContactRequest) -> Alert {
Alert(
title: Text("Reject contact request"),
message: Text("The sender will NOT be notified"),
primaryButton: .destructive(Text("Reject")) {
Task { await rejectContactRequest(contactRequest) }
},
secondaryButton: .cancel()
)
}
private func pendingContactAlert(_ chat: Chat, _ contact: Contact) -> Alert {
Alert(
title: Text("Contact is not connected yet!"),
message: Text("Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)."),
primaryButton: .cancel(),
secondaryButton: .destructive(Text("Delete Contact")) {
removePendingContact(chat, contact)
}
)
}
private func groupInvitationAcceptedAlert() -> Alert {
Alert(
title: Text("Joining group"),
@@ -459,30 +458,6 @@ struct ChatListNavLink: View {
)
}
private func deletePendingContactAlert(_ chat: Chat, _ contact: Contact) -> Alert {
Alert(
title: Text("Delete pending connection"),
message: Text("Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)."),
primaryButton: .destructive(Text("Delete")) {
removePendingContact(chat, contact)
},
secondaryButton: .cancel()
)
}
private func removePendingContact(_ chat: Chat, _ contact: Contact) {
Task {
do {
try await apiDeleteChat(type: chat.chatInfo.chatType, id: chat.chatInfo.apiId)
DispatchQueue.main.async {
chatModel.removeChat(contact.id)
}
} catch let error {
logger.error("ChatListNavLink.removePendingContact apiDeleteChat error: \(responseError(error))")
}
}
}
private func invalidJSONPreview(_ json: String) -> some View {
Text("invalid chat data")
.foregroundColor(.red)
@@ -497,16 +472,26 @@ struct ChatListNavLink: View {
private func connectContactViaAddress_(_ contact: Contact, _ incognito: Bool) {
Task {
let ok = await connectContactViaAddress(contact.contactId, incognito)
let ok = await connectContactViaAddress(contact.contactId, incognito, showAlert: { AlertManager.shared.showAlert($0) })
if ok {
await MainActor.run {
chatModel.chatId = contact.id
}
ItemsModel.shared.loadOpenChat(contact.id)
AlertManager.shared.showAlert(connReqSentAlert(.contact))
}
}
}
}
func rejectContactRequestAlert(_ contactRequest: UserContactRequest) -> Alert {
Alert(
title: Text("Reject contact request"),
message: Text("The sender will NOT be notified"),
primaryButton: .destructive(Text("Reject")) {
Task { await rejectContactRequest(contactRequest) }
},
secondaryButton: .cancel()
)
}
func deleteContactConnectionAlert(_ contactConnection: PendingContactConnection, showError: @escaping (ErrorAlert) -> Void, success: @escaping () -> Void = {}) -> Alert {
Alert(
title: Text("Delete pending connection?"),
@@ -533,15 +518,14 @@ func deleteContactConnectionAlert(_ contactConnection: PendingContactConnection,
)
}
func connectContactViaAddress(_ contactId: Int64, _ incognito: Bool) async -> Bool {
func connectContactViaAddress(_ contactId: Int64, _ incognito: Bool, showAlert: (Alert) -> Void) async -> Bool {
let (contact, alert) = await apiConnectContactViaAddress(incognito: incognito, contactId: contactId)
if let alert = alert {
AlertManager.shared.showAlert(alert)
showAlert(alert)
return false
} else if let contact = contact {
await MainActor.run {
ChatModel.shared.updateContact(contact)
AlertManager.shared.showAlert(connReqSentAlert(.contact))
}
return true
}
+202 -134
View File
@@ -18,11 +18,14 @@ struct ChatListView: View {
@State private var searchText = ""
@State private var searchShowingSimplexLink = false
@State private var searchChatFilteredBySimplexLink: String? = nil
@State private var newChatMenuOption: NewChatMenuOption? = nil
@State private var userPickerVisible = false
@State private var showConnectDesktop = false
@State private var scrollToSearchBar = false
@AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false
@AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = true
@AppStorage(DEFAULT_ONE_HAND_UI_CARD_SHOWN) private var oneHandUICardShown = false
@AppStorage(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial
var body: some View {
if #available(iOS 16.0, *) {
@@ -33,21 +36,16 @@ struct ChatListView: View {
}
private var viewBody: some View {
ZStack(alignment: .topLeading) {
ZStack(alignment: oneHandUI ? .bottomLeading : .topLeading) {
NavStackCompat(
isActive: Binding(
get: { chatModel.chatId != nil },
set: { _ in }
set: { active in
if !active { chatModel.chatId = nil }
}
),
destination: chatView
) {
VStack {
if chatModel.chats.isEmpty {
onboardingButtons()
}
chatListView
}
}
) { chatListView }
if userPickerVisible {
Rectangle().fill(.white.opacity(0.001)).onTapGesture {
withAnimation {
@@ -67,9 +65,14 @@ struct ChatListView: View {
}
private var chatListView: some View {
VStack {
let tm = ToolbarMaterial.material(toolbarMaterial)
return withToolbar(tm) {
chatList
.background(theme.colors.background)
.navigationBarTitleDisplayMode(.inline)
.navigationBarHidden(searchMode || oneHandUI)
}
.scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center)
.onDisappear() { withAnimation { userPickerVisible = false } }
.refreshable {
AlertManager.shared.showAlert(Alert(
@@ -87,55 +90,111 @@ struct ChatListView: View {
secondaryButton: .cancel()
))
}
.listStyle(.plain)
.background(theme.colors.background)
.navigationBarTitleDisplayMode(.inline)
.navigationBarHidden(searchMode)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
let user = chatModel.currentUser ?? User.sampleData
ZStack(alignment: .topTrailing) {
ProfileImage(imageStr: user.image, size: 32, color: Color(uiColor: .quaternaryLabel))
.padding(.trailing, 4)
let allRead = chatModel.users
.filter { u in !u.user.activeUser && !u.user.hidden }
.allSatisfy { u in u.unreadCount == 0 }
if !allRead {
unreadBadge(size: 12)
}
}
.onTapGesture {
if chatModel.users.filter({ u in u.user.activeUser || !u.user.hidden }).count > 1 {
withAnimation {
userPickerVisible.toggle()
}
} else {
showSettings = true
}
}
.safeAreaInset(edge: .top) {
if oneHandUI { Divider().background(tm) }
}
.safeAreaInset(edge: .bottom) {
if oneHandUI {
Divider().padding(.bottom, Self.hasHomeIndicator ? 0 : 8).background(tm)
}
ToolbarItem(placement: .principal) {
HStack(spacing: 4) {
Text("Chats")
.font(.headline)
SubsStatusIndicator()
}
.frame(maxWidth: .infinity, alignment: .center)
}
}
static var hasHomeIndicator: Bool = {
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window = windowScene.windows.first {
window.safeAreaInsets.bottom > 0
} else { false }
}()
@ViewBuilder func withToolbar(_ material: Material, content: () -> some View) -> some View {
if #available(iOS 16.0, *) {
if oneHandUI {
content()
.toolbarBackground(.hidden, for: .bottomBar)
.toolbar { bottomToolbar }
} else {
content()
.toolbarBackground(.automatic, for: .navigationBar)
.toolbarBackground(material)
.toolbar { topToolbar }
}
ToolbarItem(placement: .navigationBarTrailing) {
switch chatModel.chatRunning {
case .some(true): NewChatMenuButton(newChatMenuOption: $newChatMenuOption)
case .some(false): chatStoppedIcon()
case .none: EmptyView()
}
} else {
if oneHandUI {
content().toolbar { bottomToolbarGroup }
} else {
content().toolbar { topToolbar }
}
}
}
@ToolbarContentBuilder var topToolbar: some ToolbarContent {
ToolbarItem(placement: .topBarLeading) { leadingToolbarItem }
ToolbarItem(placement: .principal) { SubsStatusIndicator() }
ToolbarItem(placement: .topBarTrailing) { trailingToolbarItem }
}
@ToolbarContentBuilder var bottomToolbar: some ToolbarContent {
let padding: Double = Self.hasHomeIndicator ? 0 : 14
ToolbarItem(placement: .bottomBar) {
HStack {
leadingToolbarItem.padding(.bottom, padding)
Spacer()
SubsStatusIndicator().padding(.bottom, padding)
Spacer()
trailingToolbarItem.padding(.bottom, padding)
}
.contentShape(Rectangle())
.onTapGesture { scrollToSearchBar = true }
}
}
@ToolbarContentBuilder var bottomToolbarGroup: some ToolbarContent {
let padding: Double = Self.hasHomeIndicator ? 0 : 14
ToolbarItemGroup(placement: .bottomBar) {
leadingToolbarItem.padding(.bottom, padding)
Spacer()
SubsStatusIndicator().padding(.bottom, padding)
Spacer()
trailingToolbarItem.padding(.bottom, padding)
}
}
@ViewBuilder var leadingToolbarItem: some View {
let user = chatModel.currentUser ?? User.sampleData
ZStack(alignment: .topTrailing) {
ProfileImage(imageStr: user.image, size: 32, color: Color(uiColor: .quaternaryLabel))
.padding(.trailing, 4)
let allRead = chatModel.users
.filter { u in !u.user.activeUser && !u.user.hidden }
.allSatisfy { u in u.unreadCount == 0 }
if !allRead {
unreadBadge(size: 12)
}
}
.onTapGesture {
if chatModel.users.filter({ u in u.user.activeUser || !u.user.hidden }).count > 1 {
withAnimation {
userPickerVisible.toggle()
}
} else {
showSettings = true
}
}
}
@ViewBuilder var trailingToolbarItem: some View {
switch chatModel.chatRunning {
case .some(true): NewChatMenuButton()
case .some(false): chatStoppedIcon()
case .none: EmptyView()
}
}
@ViewBuilder private var chatList: some View {
let cs = filteredChats()
ZStack {
VStack {
ScrollViewReader { scrollProxy in
List {
if !chatModel.chats.isEmpty {
ChatListSearchBar(
@@ -145,31 +204,67 @@ struct ChatListView: View {
searchShowingSimplexLink: $searchShowingSimplexLink,
searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink
)
.scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center)
.listRowSeparator(.hidden)
.listRowBackground(Color.clear)
.frame(maxWidth: .infinity)
.padding(.top, oneHandUI ? 8 : 0)
.id("searchBar")
}
ForEach(cs, id: \.viewId) { chat in
ChatListNavLink(chat: chat)
.padding(.trailing, -16)
.disabled(chatModel.chatRunning != true || chatModel.deletedChats.contains(chat.chatInfo.id))
if !oneHandUICardShown {
OneHandUICard()
.scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center)
.listRowSeparator(.hidden)
.listRowBackground(Color.clear)
}
.offset(x: -8)
if #available(iOS 16.0, *) {
ForEach(cs, id: \.viewId) { chat in
ChatListNavLink(chat: chat)
.scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center)
.padding(.trailing, -16)
.disabled(chatModel.chatRunning != true || chatModel.deletedChats.contains(chat.chatInfo.id))
.listRowBackground(Color.clear)
}
.offset(x: -8)
} else {
ForEach(cs, id: \.viewId) { chat in
VStack(spacing: .zero) {
Divider()
.padding(.leading, 16)
ChatListNavLink(chat: chat)
.padding(.horizontal, 8)
.padding(.vertical, 6)
}
.scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center)
.listRowSeparator(.hidden)
.listRowInsets(EdgeInsets())
.background { theme.colors.background } // Hides default list selection colour
.disabled(chatModel.chatRunning != true || chatModel.deletedChats.contains(chat.chatInfo.id))
}
}
}
}
.onChange(of: chatModel.chatId) { _ in
if chatModel.chatId == nil, let chatId = chatModel.chatToTop {
chatModel.chatToTop = nil
chatModel.popChat(chatId)
.listStyle(.plain)
.onChange(of: chatModel.chatId) { currentChatId in
if let chatId = chatModel.chatToTop, currentChatId != chatId {
chatModel.chatToTop = nil
chatModel.popChat(chatId)
}
stopAudioPlayer()
}
.onChange(of: chatModel.currentUser?.userId) { _ in
stopAudioPlayer()
}
.onChange(of: scrollToSearchBar) { scrollToSearchBar in
if scrollToSearchBar {
Task { self.scrollToSearchBar = false }
withAnimation { scrollProxy.scrollTo("searchBar") }
}
}
stopAudioPlayer()
}
.onChange(of: chatModel.currentUser?.userId) { _ in
stopAudioPlayer()
}
if cs.isEmpty && !chatModel.chats.isEmpty {
Text("No filtered chats").foregroundColor(theme.colors.secondary)
Text("No filtered chats")
.scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center)
.foregroundColor(.secondary)
}
}
}
@@ -180,42 +275,6 @@ struct ChatListView: View {
.foregroundColor(theme.colors.primary)
}
private func onboardingButtons() -> some View {
VStack(alignment: .trailing, spacing: 0) {
Path { p in
p.move(to: CGPoint(x: 8, y: 0))
p.addLine(to: CGPoint(x: 16, y: 10))
p.addLine(to: CGPoint(x: 0, y: 10))
p.addLine(to: CGPoint(x: 8, y: 0))
}
.fill(theme.colors.primary)
.frame(width: 20, height: 10)
.padding(.trailing, 12)
connectButton("Tap to start a new chat") {
newChatMenuOption = .newContact
}
Spacer()
Text("You have no chats")
.foregroundColor(theme.colors.secondary)
.frame(maxWidth: .infinity)
}
.padding(.trailing, 6)
.frame(maxHeight: .infinity)
}
private func connectButton(_ label: LocalizedStringKey, action: @escaping () -> Void) -> some View {
Button(action: action) {
Text(label)
.padding(.vertical, 10)
.padding(.horizontal, 20)
}
.background(theme.colors.primary)
.foregroundColor(.white)
.clipShape(RoundedRectangle(cornerRadius: 16))
}
@ViewBuilder private func chatView() -> some View {
if let chatId = chatModel.chatId, let chat = chatModel.getChat(chatId) {
ChatView(chat: chat)
@@ -233,16 +292,20 @@ struct ChatListView: View {
} else {
let s = searchString()
return s == "" && !showUnreadAndFavorites
? chatModel.chats
? chatModel.chats.filter { chat in
!chat.chatInfo.chatDeleted && chatContactType(chat: chat) != ContactType.card
}
: chatModel.chats.filter { chat in
let cInfo = chat.chatInfo
switch cInfo {
case let .direct(contact):
return s == ""
? filtered(chat)
: (viewNameContains(cInfo, s) ||
contact.profile.displayName.localizedLowercase.contains(s) ||
contact.fullName.localizedLowercase.contains(s))
return !contact.chatDeleted && chatContactType(chat: chat) != ContactType.card && (
s == ""
? filtered(chat)
: (viewNameContains(cInfo, s) ||
contact.profile.displayName.localizedLowercase.contains(s) ||
contact.fullName.localizedLowercase.contains(s))
)
case let .group(gInfo):
return s == ""
? (filtered(chat) || gInfo.membership.memberStatus == .memInvited)
@@ -278,7 +341,7 @@ struct ChatListView: View {
struct SubsStatusIndicator: View {
@State private var subs: SMPServerSubs = SMPServerSubs.newSMPServerSubs
@State private var hasSess: Bool = false
@State private var timer: Timer? = nil
@State private var task: Task<Void, Never>?
@State private var showServersSummary = false
@AppStorage(DEFAULT_SHOW_SUBSCRIPTION_PERCENTAGE) private var showSubscriptionPercentage = false
@@ -288,42 +351,48 @@ struct SubsStatusIndicator: View {
showServersSummary = true
} label: {
HStack(spacing: 4) {
Text("Chats").foregroundStyle(Color.primary).fixedSize().font(.headline)
SubscriptionStatusIndicatorView(subs: subs, hasSess: hasSess)
if showSubscriptionPercentage {
SubscriptionStatusPercentageView(subs: subs, hasSess: hasSess)
}
}
}
.disabled(ChatModel.shared.chatRunning != true)
.onAppear {
startTimer()
startTask()
}
.onDisappear {
stopTimer()
stopTask()
}
.sheet(isPresented: $showServersSummary) {
.appSheet(isPresented: $showServersSummary) {
ServersSummaryView()
.environment(\EnvironmentValues.refresh as! WritableKeyPath<EnvironmentValues, RefreshAction?>, nil)
}
}
private func startTimer() {
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
if AppChatState.shared.value == .active {
getSubsTotal()
private func startTask() {
task = Task {
while !Task.isCancelled {
if AppChatState.shared.value == .active {
do {
let (subs, hasSess) = try await getAgentSubsTotal()
await MainActor.run {
self.subs = subs
self.hasSess = hasSess
}
} catch let error {
logger.error("getSubsTotal error: \(responseError(error))")
}
}
try? await Task.sleep(nanoseconds: 1_000_000_000) // Sleep for 1 second
}
}
}
func stopTimer() {
timer?.invalidate()
timer = nil
}
private func getSubsTotal() {
do {
(subs, hasSess) = try getAgentSubsTotal()
} catch let error {
logger.error("getSubsTotal error: \(responseError(error))")
}
func stopTask() {
task?.cancel()
task = nil
}
}
@@ -373,7 +442,6 @@ struct ChatListSearchBar: View {
toggleFilterButton()
}
}
Divider()
}
.onChange(of: searchFocussed) { sf in
withAnimation { searchMode = sf }
@@ -450,21 +518,21 @@ func chatStoppedIcon() -> some View {
struct ChatListView_Previews: PreviewProvider {
static var previews: some View {
let chatModel = ChatModel()
chatModel.chats = [
Chat(
chatModel.updateChats([
ChatData(
chatInfo: ChatInfo.sampleData.direct,
chatItems: [ChatItem.getSample(1, .directSnd, .now, "hello")]
),
Chat(
ChatData(
chatInfo: ChatInfo.sampleData.group,
chatItems: [ChatItem.getSample(1, .directSnd, .now, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")]
),
Chat(
ChatData(
chatInfo: ChatInfo.sampleData.contactRequest,
chatItems: []
)
]
])
return Group {
ChatListView(showSettings: Binding.constant(false))
.environmentObject(chatModel)
@@ -275,7 +275,7 @@ struct ChatPreviewView: View {
} else {
switch (chat.chatInfo) {
case let .direct(contact):
if contact.activeConn == nil && contact.profile.contactLink != nil {
if contact.activeConn == nil && contact.profile.contactLink != nil && contact.active {
chatPreviewInfoText("Tap to Connect")
.foregroundColor(theme.colors.primary)
} else if !contact.sndReady && contact.activeConn != nil {
@@ -376,17 +376,7 @@ struct ChatPreviewView: View {
switch chat.chatInfo {
case let .direct(contact):
if contact.active && contact.activeConn != nil {
switch (chatModel.contactNetworkStatus(contact)) {
case .connected: incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary, size: size)
case .error:
Image(systemName: "exclamationmark.circle")
.resizable()
.scaledToFit()
.frame(width: dynamicChatInfoSize, height: dynamicChatInfoSize)
.foregroundColor(theme.colors.secondary)
default:
ProgressView()
}
NetworkStatusView(contact: contact, size: size)
} else {
incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary, size: size)
}
@@ -400,6 +390,30 @@ struct ChatPreviewView: View {
incognitoIcon(chat.chatInfo.incognito, theme.colors.secondary, size: size)
}
}
struct NetworkStatusView: View {
@Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize
@EnvironmentObject var theme: AppTheme
@ObservedObject var networkModel = NetworkModel.shared
let contact: Contact
let size: CGFloat
var body: some View {
let dynamicChatInfoSize = dynamicSize(userFont).chatInfoSize
switch (networkModel.contactNetworkStatus(contact)) {
case .connected: incognitoIcon(contact.contactConnIncognito, theme.colors.secondary, size: size)
case .error:
Image(systemName: "exclamationmark.circle")
.resizable()
.scaledToFit()
.frame(width: dynamicChatInfoSize, height: dynamicChatInfoSize)
.foregroundColor(theme.colors.secondary)
default:
ProgressView()
}
}
}
}
@ViewBuilder func incognitoIcon(_ incognito: Bool, _ secondaryColor: Color, size: CGFloat) -> some View {
@@ -16,7 +16,6 @@ struct ContactConnectionView: View {
@Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize
@State private var localAlias = ""
@FocusState private var aliasTextFieldFocused: Bool
@State private var showContactConnectionInfo = false
var body: some View {
if case let .contactConnection(conn) = chat.chatInfo {
@@ -32,7 +31,6 @@ struct ContactConnectionView: View {
.scaledToFill()
.frame(width: 48, height: 48)
.foregroundColor(Color(uiColor: .tertiarySystemGroupedBackground).asAnotherColorFromSecondaryVariant(theme))
.onTapGesture { showContactConnectionInfo = true }
}
.frame(width: 63, height: 63)
.padding(.leading, 4)
@@ -72,9 +70,6 @@ struct ContactConnectionView: View {
Spacer()
}
.frame(maxHeight: .infinity)
.appSheet(isPresented: $showContactConnectionInfo) {
ContactConnectionInfo(contactConnection: contactConnection)
}
}
}
}
@@ -0,0 +1,52 @@
//
// OneHandUICard.swift
// SimpleX (iOS)
//
// Created by EP on 06/08/2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import SwiftUI
import SimpleXChat
struct OneHandUICard: View {
@EnvironmentObject var theme: AppTheme
@Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize
@AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = true
@AppStorage(DEFAULT_ONE_HAND_UI_CARD_SHOWN) private var oneHandUICardShown = false
@State private var showOneHandUIAlert = false
var body: some View {
ZStack(alignment: .topTrailing) {
VStack(alignment: .leading, spacing: 8) {
Text("Toggle chat list:").font(.title3)
Toggle("Reachable chat toolbar", isOn: $oneHandUI)
}
Image(systemName: "multiply")
.foregroundColor(theme.colors.secondary)
.onTapGesture {
showOneHandUIAlert = true
}
}
.padding()
.background(theme.appColors.sentMessage)
.cornerRadius(12)
.frame(height: dynamicSize(userFont).rowHeight)
.padding(.vertical, 12)
.alert(isPresented: $showOneHandUIAlert) {
Alert(
title: Text("Reachable chat toolbar"),
message: Text("You can change it in Appearance settings."),
dismissButton: .default(Text("Ok")) {
withAnimation {
oneHandUICardShown = true
}
}
)
}
}
}
#Preview {
OneHandUICard()
}
@@ -407,12 +407,18 @@ struct ServersSummaryView: View {
struct SubscriptionStatusIndicatorView: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
var subs: SMPServerSubs
var hasSess: Bool
var body: some View {
let onionHosts = networkUseOnionHostsGroupDefault.get()
let (color, variableValue, opacity, _) = subscriptionStatusColorAndPercentage(m.networkInfo.online, onionHosts, subs, hasSess)
let (color, variableValue, opacity, _) = subscriptionStatusColorAndPercentage(
online: m.networkInfo.online,
usesProxy: networkUseOnionHostsGroupDefault.get() != .no || groupDefaults.string(forKey: GROUP_DEFAULT_NETWORK_SOCKS_PROXY) != nil,
subs: subs,
hasSess: hasSess,
primaryColor: theme.colors.primary
)
if #available(iOS 16.0, *) {
Image(systemName: "dot.radiowaves.up.forward", variableValue: variableValue)
.foregroundColor(color)
@@ -425,42 +431,52 @@ struct SubscriptionStatusIndicatorView: View {
struct SubscriptionStatusPercentageView: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
var subs: SMPServerSubs
var hasSess: Bool
var body: some View {
let onionHosts = networkUseOnionHostsGroupDefault.get()
let (_, _, _, statusPercent) = subscriptionStatusColorAndPercentage(m.networkInfo.online, onionHosts, subs, hasSess)
let (_, _, _, statusPercent) = subscriptionStatusColorAndPercentage(
online: m.networkInfo.online,
usesProxy: networkUseOnionHostsGroupDefault.get() != .no || groupDefaults.string(forKey: GROUP_DEFAULT_NETWORK_SOCKS_PROXY) != nil,
subs: subs,
hasSess: hasSess,
primaryColor: theme.colors.primary
)
Text(verbatim: "\(Int(floor(statusPercent * 100)))%")
.foregroundColor(.secondary)
.font(.caption)
}
}
func subscriptionStatusColorAndPercentage(_ online: Bool, _ onionHosts: OnionHosts, _ subs: SMPServerSubs, _ hasSess: Bool) -> (Color, Double, Double, Double) {
func subscriptionStatusColorAndPercentage(online: Bool, usesProxy: Bool, subs: SMPServerSubs, hasSess: Bool, primaryColor: Color) -> (Color, Double, Double, Double) {
func roundedToQuarter(_ n: Double) -> Double {
n >= 1 ? 1
: n <= 0 ? 0
: (n * 4).rounded() / 4
}
let activeColor: Color = onionHosts == .require ? .indigo : .accentColor
let activeColor: Color = usesProxy ? .indigo : primaryColor
let noConnColorAndPercent: (Color, Double, Double, Double) = (Color(uiColor: .tertiaryLabel), 1, 1, 0)
let activeSubsRounded = roundedToQuarter(subs.shareOfActive)
return online && subs.total > 0
? (
subs.ssActive == 0
? (
hasSess ? (activeColor, activeSubsRounded, subs.shareOfActive, subs.shareOfActive) : noConnColorAndPercent
return !online
? noConnColorAndPercent
: (
subs.total == 0 && !hasSess
? (activeColor, 0, 0.33, 0) // On freshly installed app (without chats) and on app start
: (
subs.ssActive == 0
? (
hasSess ? (activeColor, activeSubsRounded, subs.shareOfActive, subs.shareOfActive) : noConnColorAndPercent
)
: ( // ssActive > 0
hasSess
? (activeColor, activeSubsRounded, subs.shareOfActive, subs.shareOfActive)
: (.orange, activeSubsRounded, subs.shareOfActive, subs.shareOfActive) // This would mean implementation error
)
)
: ( // ssActive > 0
hasSess
? (activeColor, activeSubsRounded, subs.shareOfActive, subs.shareOfActive)
: (.orange, activeSubsRounded, subs.shareOfActive, subs.shareOfActive) // This would mean implementation error
)
)
: noConnColorAndPercent
}
struct SMPServerSummaryView: View {
@@ -85,15 +85,18 @@ struct UserPicker: View {
.padding(8)
.opacity(userPickerVisible ? 1.0 : 0.0)
.onAppear {
do {
// This check prevents the call of listUsers after the app is suspended, and the database is closed.
if case .active = scenePhase {
m.users = try listUsers()
}
} catch let error {
logger.error("Error loading users \(responseError(error))")
}
}
// This check prevents the call of listUsers after the app is suspended, and the database is closed.
if case .active = scenePhase {
Task {
do {
let users = try await listUsersAsync()
await MainActor.run { m.users = users }
} catch {
logger.error("Error loading users \(responseError(error))")
}
}
}
}
}
private func userView(_ u: UserInfo) -> some View {
@@ -0,0 +1,267 @@
//
// ContactListNavLink.swift
// SimpleX (iOS)
//
// Created by Diogo Cunha on 01/08/2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import SwiftUI
import SimpleXChat
struct ContactListNavLink: View {
@EnvironmentObject var theme: AppTheme
@ObservedObject var chat: Chat
var showDeletedChatIcon: Bool
@State private var alert: SomeAlert? = nil
@State private var actionSheet: SomeActionSheet? = nil
@State private var sheet: SomeSheet<AnyView>? = nil
@State private var showConnectContactViaAddressDialog = false
@State private var showContactRequestDialog = false
var body: some View {
let contactType = chatContactType(chat: chat)
Group {
switch (chat.chatInfo) {
case let .direct(contact):
switch contactType {
case .recent:
recentContactNavLink(contact)
case .chatDeleted:
deletedChatNavLink(contact)
case .card:
contactCardNavLink(contact)
default:
EmptyView()
}
case let .contactRequest(contactRequest):
contactRequestNavLink(contactRequest)
default:
EmptyView()
}
}
.alert(item: $alert) { $0.alert }
.actionSheet(item: $actionSheet) { $0.actionSheet }
.sheet(item: $sheet) {
if #available(iOS 16.0, *) {
$0.content
.presentationDetents([.fraction(0.4)])
} else {
$0.content
}
}
}
func recentContactNavLink(_ contact: Contact) -> some View {
Button {
dismissAllSheets(animated: true) {
ItemsModel.shared.loadOpenChat(contact.id)
}
} label: {
contactPreview(contact, titleColor: theme.colors.onBackground)
}
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
Button {
deleteContactDialog(
chat,
contact,
dismissToChatList: false,
showAlert: { alert = $0 },
showActionSheet: { actionSheet = $0 },
showSheetContent: { sheet = $0 }
)
} label: {
Label("Delete", systemImage: "trash")
}
.tint(.red)
}
}
func deletedChatNavLink(_ contact: Contact) -> some View {
Button {
Task {
await MainActor.run {
dismissAllSheets(animated: true) {
ItemsModel.shared.loadOpenChat(contact.id)
}
}
}
} label: {
contactPreview(contact, titleColor: theme.colors.onBackground)
}
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
Button {
deleteContactDialog(
chat,
contact,
dismissToChatList: false,
showAlert: { alert = $0 },
showActionSheet: { actionSheet = $0 },
showSheetContent: { sheet = $0 }
)
} label: {
Label("Delete", systemImage: "trash")
}
.tint(.red)
}
}
func contactPreview(_ contact: Contact, titleColor: Color) -> some View {
HStack{
ProfileImage(imageStr: contact.image, size: 30)
previewTitle(contact, titleColor: titleColor)
Spacer()
HStack {
if showDeletedChatIcon && contact.chatDeleted {
Image(systemName: "archivebox")
.resizable()
.scaledToFit()
.frame(width: 18, height: 18)
.foregroundColor(.secondary.opacity(0.65))
} else if chat.chatInfo.chatSettings?.favorite ?? false {
Image(systemName: "star.fill")
.resizable()
.scaledToFill()
.frame(width: 18, height: 18)
.foregroundColor(.secondary.opacity(0.65))
}
if contact.contactConnIncognito {
Image(systemName: "theatermasks")
.resizable()
.scaledToFit()
.frame(width: 22, height: 22)
.foregroundColor(.secondary)
}
}
}
}
@ViewBuilder private func previewTitle(_ contact: Contact, titleColor: Color) -> some View {
let t = Text(chat.chatInfo.chatViewName).foregroundColor(titleColor)
(
contact.verified == true
? verifiedIcon + t
: t
)
.lineLimit(1)
}
private var verifiedIcon: Text {
(Text(Image(systemName: "checkmark.shield")) + Text(" "))
.foregroundColor(.secondary)
.baselineOffset(1)
.kerning(-2)
}
func contactCardNavLink(_ contact: Contact) -> some View {
Button {
showConnectContactViaAddressDialog = true
} label: {
contactCardPreview(contact)
}
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
Button {
deleteContactDialog(
chat,
contact,
dismissToChatList: false,
showAlert: { alert = $0 },
showActionSheet: { actionSheet = $0 },
showSheetContent: { sheet = $0 }
)
} label: {
Label("Delete", systemImage: "trash")
}
.tint(.red)
}
.confirmationDialog("Connect with \(contact.chatViewName)", isPresented: $showConnectContactViaAddressDialog, titleVisibility: .visible) {
Button("Use current profile") { connectContactViaAddress_(contact, false) }
Button("Use new incognito profile") { connectContactViaAddress_(contact, true) }
}
}
private func connectContactViaAddress_(_ contact: Contact, _ incognito: Bool) {
Task {
let ok = await connectContactViaAddress(contact.contactId, incognito, showAlert: { alert = SomeAlert(alert: $0, id: "ContactListNavLink connectContactViaAddress") })
if ok {
ItemsModel.shared.loadOpenChat(contact.id)
DispatchQueue.main.async {
dismissAllSheets(animated: true) {
AlertManager.shared.showAlert(connReqSentAlert(.contact))
}
}
}
}
}
func contactCardPreview(_ contact: Contact) -> some View {
HStack{
ProfileImage(imageStr: contact.image, size: 30)
Text(chat.chatInfo.chatViewName)
.foregroundColor(.accentColor)
.lineLimit(1)
Spacer()
Image(systemName: "envelope")
.resizable()
.scaledToFill()
.frame(width: 14, height: 14)
.foregroundColor(.accentColor)
}
}
func contactRequestNavLink(_ contactRequest: UserContactRequest) -> some View {
Button {
showContactRequestDialog = true
} label: {
contactRequestPreview(contactRequest)
}
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
Button {
Task { await acceptContactRequest(incognito: false, contactRequest: contactRequest) }
} label: { Label("Accept", systemImage: "checkmark") }
.tint(theme.colors.primary)
Button {
Task { await acceptContactRequest(incognito: true, contactRequest: contactRequest) }
} label: {
Label("Accept incognito", systemImage: "theatermasks")
}
.tint(.indigo)
Button {
alert = SomeAlert(alert: rejectContactRequestAlert(contactRequest), id: "rejectContactRequestAlert")
} label: {
Label("Reject", systemImage: "multiply")
}
.tint(.red)
}
.confirmationDialog("Accept connection request?", isPresented: $showContactRequestDialog, titleVisibility: .visible) {
Button("Accept") { Task { await acceptContactRequest(incognito: false, contactRequest: contactRequest) } }
Button("Accept incognito") { Task { await acceptContactRequest(incognito: true, contactRequest: contactRequest) } }
Button("Reject (sender NOT notified)", role: .destructive) { Task { await rejectContactRequest(contactRequest) } }
}
}
func contactRequestPreview(_ contactRequest: UserContactRequest) -> some View {
HStack{
ProfileImage(imageStr: contactRequest.image, size: 30)
Text(chat.chatInfo.chatViewName)
.foregroundColor(.accentColor)
.lineLimit(1)
Spacer()
Image(systemName: "checkmark")
.resizable()
.scaledToFill()
.frame(width: 14, height: 14)
.foregroundColor(.accentColor)
}
}
}
@@ -15,6 +15,7 @@ enum DatabaseAlert: Identifiable {
case importArchive
case archiveImported
case archiveImportedWithErrors(archiveErrors: [ArchiveError])
case archiveExportedWithErrors(archivePath: URL, archiveErrors: [ArchiveError])
case deleteChat
case chatDeleted
case deleteLegacyDatabase
@@ -29,6 +30,7 @@ enum DatabaseAlert: Identifiable {
case .importArchive: return "importArchive"
case .archiveImported: return "archiveImported"
case .archiveImportedWithErrors: return "archiveImportedWithErrors"
case .archiveExportedWithErrors: return "archiveExportedWithErrors"
case .deleteChat: return "deleteChat"
case .chatDeleted: return "chatDeleted"
case .deleteLegacyDatabase: return "deleteLegacyDatabase"
@@ -265,10 +267,18 @@ struct DatabaseView: View {
title: Text("Chat database imported"),
message: Text("Restart the app to use imported chat database")
)
case .archiveImportedWithErrors:
case let .archiveImportedWithErrors(errs):
return Alert(
title: Text("Chat database imported"),
message: Text("Restart the app to use imported chat database") + Text("\n") + Text("Some non-fatal errors occurred during import - you may see Chat console for more details.")
message: Text("Restart the app to use imported chat database") + Text(verbatim: "\n\n") + Text("Some non-fatal errors occurred during import:") + archiveErrorsText(errs)
)
case let .archiveExportedWithErrors(archivePath, errs):
return Alert(
title: Text("Chat database exported"),
message: Text("You may save the exported archive.") + Text(verbatim: "\n\n") + Text("Some file(s) were not exported:") + archiveErrorsText(errs),
dismissButton: .default(Text("Continue")) {
showShareSheet(items: [archivePath])
}
)
case .deleteChat:
return Alert(
@@ -349,9 +359,16 @@ struct DatabaseView: View {
progressIndicator = true
Task {
do {
let archivePath = try await exportChatArchive()
showShareSheet(items: [archivePath])
await MainActor.run { progressIndicator = false }
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
}
}
} catch let error {
await MainActor.run {
alert = .error(title: "Error exporting chat database", error: responseError(error))
@@ -474,7 +491,7 @@ struct DatabaseView: View {
appFilesCountAndSize = directoryFileCountAndSize(getAppFilesDirectory())
do {
let chats = try apiGetChats()
m.updateChats(with: chats)
m.updateChats(chats)
} catch let error {
logger.error("apiGetChats: cannot update chats \(responseError(error))")
}
@@ -486,6 +503,17 @@ struct DatabaseView: View {
}
}
func archiveErrorsText(_ errs: [ArchiveError]) -> Text {
return Text("\n" + errs.map(showArchiveError).joined(separator: "\n"))
func showArchiveError(_ err: ArchiveError) -> String {
switch err {
case let .import(importError): importError
case let .fileError(file, fileError): "\(file): \(fileError)"
}
}
}
func stopChatAsync() async throws {
try await apiStopChat()
ChatReceiver.shared.stop()
@@ -190,7 +190,7 @@ struct MigrateToAppGroupView: View {
do {
try apiSaveAppSettings(settings: AppSettings.current.prepareForExport())
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
try await apiExportArchive(config: config)
_ = try await apiExportArchive(config: config)
await MainActor.run { setV3DBMigration(.exported) }
} catch let error {
await MainActor.run {
@@ -222,7 +222,7 @@ struct MigrateToAppGroupView: View {
}
}
func exportChatArchive(_ storagePath: URL? = nil) async throws -> URL {
func exportChatArchive(_ storagePath: URL? = nil) async throws -> (URL, [ArchiveError]) {
let archiveTime = Date.now
let ts = archiveTime.ISO8601Format(Date.ISO8601FormatStyle(timeSeparator: .omitted))
let archiveName = "simplex-chat.\(ts).zip"
@@ -233,13 +233,13 @@ func exportChatArchive(_ storagePath: URL? = nil) async throws -> URL {
try apiSaveAppSettings(settings: AppSettings.current.prepareForExport())
}
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
try await apiExportArchive(config: config)
let errs = try await apiExportArchive(config: config)
if storagePath == nil {
deleteOldArchive()
UserDefaults.standard.set(archiveName, forKey: DEFAULT_CHAT_ARCHIVE_NAME)
chatArchiveTimeDefault.set(archiveTime)
}
return archivePath
return (archivePath, errs)
}
func deleteOldArchive() {
+13 -29
View File
@@ -8,41 +8,21 @@
import SwiftUI
private struct SheetIsPresented<C>: ViewModifier where C: View {
var isPresented: Binding<Bool>
var onDismiss: (() -> Void)?
var sheetContent: () -> C
@Environment(\.scenePhase) var scenePhase
func body(content: Content) -> some View {
content.sheet(isPresented: isPresented, onDismiss: onDismiss) {
sheetContent().modifier(PrivacySensitive())
}
}
}
private struct SheetForItem<T, C>: ViewModifier where T: Identifiable, C: View {
var item: Binding<T?>
var onDismiss: (() -> Void)?
var sheetContent: (T) -> C
@Environment(\.scenePhase) var scenePhase
func body(content: Content) -> some View {
content.sheet(item: item, onDismiss: onDismiss) { it in
sheetContent(it).modifier(PrivacySensitive())
}
}
class AppSheetState: ObservableObject {
static let shared = AppSheetState()
@Published var scenePhaseActive: Bool = false
}
private struct PrivacySensitive: ViewModifier {
@AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = false
@Environment(\.scenePhase) var scenePhase
// Screen protection doesn't work for appSheet on iOS 16 if @Environment(\.scenePhase) is used instead of global state
@ObservedObject var appSheetState: AppSheetState = AppSheetState.shared
func body(content: Content) -> some View {
if case .active = scenePhase {
if !protectScreen {
content
} else {
content.privacySensitive(protectScreen).redacted(reason: .privacy)
content.privacySensitive(!appSheetState.scenePhaseActive).redacted(reason: .privacy)
}
}
}
@@ -53,7 +33,9 @@ extension View {
onDismiss: (() -> Void)? = nil,
content: @escaping () -> Content
) -> some View where Content: View {
modifier(SheetIsPresented(isPresented: isPresented, onDismiss: onDismiss, sheetContent: content))
sheet(isPresented: isPresented, onDismiss: onDismiss) {
content().modifier(PrivacySensitive())
}
}
func appSheet<T, Content>(
@@ -61,6 +43,8 @@ extension View {
onDismiss: (() -> Void)? = nil,
content: @escaping (T) -> Content
) -> some View where T: Identifiable, Content: View {
modifier(SheetForItem(item: item, onDismiss: onDismiss, sheetContent: content))
sheet(item: item, onDismiss: onDismiss) { it in
content(it).modifier(PrivacySensitive())
}
}
}
@@ -11,13 +11,22 @@ import SwiftUI
import SimpleXChat
struct ChatViewBackground: ViewModifier {
@EnvironmentObject var theme: AppTheme
@Environment(\.colorScheme) var colorScheme
var image: Image
var imageType: WallpaperType
var background: Color
var tint: Color
func body(content: Content) -> some View {
// Workaround a problem (SwiftUI bug?) when wallpaper is not updated when user changes global theme in iOS settings from dark to light and vice versa
if colorScheme == .light {
back(content)
} else {
back(content)
}
}
func back(_ content: Content) -> some View {
content.background(
Canvas { context, size in
var image = context.resolve(image)
@@ -81,7 +90,7 @@ struct ChatViewBackground: ViewModifier {
case WallpaperType.empty: ()
}
}
)
).ignoresSafeArea(.all)
}
}
@@ -91,7 +100,7 @@ extension PresetWallpaper {
scale
} else if let type = ChatModel.shared.currentUser?.uiThemes?.preferredMode(base.mode == DefaultThemeMode.dark)?.wallpaper?.toAppWallpaper().type, type.sameType(WallpaperType.preset(filename, nil)) {
type.scale
} else if let scale = themeOverridesDefault.get().first(where: { $0.wallpaper != nil && $0.wallpaper!.preset == filename })?.wallpaper?.scale {
} else if let scale = themeOverridesDefault.get().first(where: { $0.wallpaper != nil && $0.wallpaper!.preset == filename && $0.base == base })?.wallpaper?.scale {
scale
} else {
Float(1.0)
@@ -1,21 +0,0 @@
//
// KeyboardPadding.swift
// SimpleX (iOS)
//
// Created by Evgeny on 10/07/2023.
// Copyright © 2023 SimpleX Chat. All rights reserved.
//
import SwiftUI
extension View {
@ViewBuilder func keyboardPadding() -> some View {
if #available(iOS 17.0, *) {
GeometryReader { g in
self.padding(.bottom, max(0, ChatModel.shared.keyboardHeight - g.safeAreaInsets.bottom))
}
} else {
self
}
}
}
@@ -7,16 +7,17 @@
//
import SwiftUI
import SimpleXChat
struct NavLinkPlain<V: Hashable, Label: View>: View {
@State var tag: V
@Binding var selection: V?
struct NavLinkPlain<Label: View>: View {
let chatId: ChatId
@Binding var selection: ChatId?
@ViewBuilder var label: () -> Label
var disabled = false
var body: some View {
ZStack {
Button("") { DispatchQueue.main.async { selection = tag } }
Button("") { ItemsModel.shared.loadOpenChat(chatId) }
.disabled(disabled)
label()
}
@@ -17,7 +17,9 @@ struct NavStackCompat <C: View, D: View>: View {
if #available(iOS 16, *) {
NavigationStack(path: Binding(
get: { isActive.wrappedValue ? [true] : [] },
set: { _ in }
set: { path in
if path.isEmpty { isActive.wrappedValue = false }
}
)) {
ZStack {
NavigationLink(value: true) { EmptyView() }
@@ -16,11 +16,12 @@ struct ProfileImage: View {
var size: CGFloat
var color = Color(uiColor: .tertiarySystemGroupedBackground)
var backgroundColor: Color? = nil
var blurred = false
@AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var radius = defaultProfileImageCorner
var body: some View {
if let uiImage = UIImage(base64Encoded: imageStr) {
clipProfileImage(Image(uiImage: uiImage), size: size, radius: radius)
clipProfileImage(Image(uiImage: uiImage), size: size, radius: radius, blurred: blurred)
} else {
let c = color.asAnotherColorFromSecondaryVariant(theme)
Image(systemName: iconName)
@@ -0,0 +1,80 @@
//
// SwipeLabel.swift
// SimpleX (iOS)
//
// Created by Levitating Pineapple on 06/08/2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import SwiftUI
struct SwipeLabel: View {
private let text: String
private let systemImage: String
private let inverted: Bool
init(_ text: String, systemImage: String, inverted: Bool) {
self.text = text
self.systemImage = systemImage
self.inverted = inverted
}
var body: some View {
if inverted {
Image(
uiImage: SwipeActionView(
systemName: systemImage,
text: text
).snapshot(inverted: inverted)
)
} else {
Label(text, systemImage: systemImage)
}
}
private class SwipeActionView: UIView {
private let imageView = UIImageView()
private let label = UILabel()
private let fontSize: CGFloat
init(systemName: String, text: String) {
fontSize = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .subheadline).pointSize
super.init(frame: CGRect(x: 0, y: 0, width: 64, height: 32 + fontSize))
imageView.image = UIImage(systemName: systemName)
imageView.contentMode = .scaleAspectFit
label.text = text
label.textAlignment = .center
label.font = UIFont.systemFont(ofSize: fontSize, weight: .medium)
addSubview(imageView)
addSubview(label)
}
override func layoutSubviews() {
imageView.frame = CGRect(
x: 20,
y: 0,
width: 24,
height: 24
)
label.frame = CGRect(
x: 0,
y: 32,
width: 64,
height: fontSize
)
}
@available(*, unavailable)
required init?(coder: NSCoder) { fatalError("not implemented") }
func snapshot(inverted: Bool) -> UIImage {
UIGraphicsImageRenderer(bounds: bounds).image { context in
if inverted {
context.cgContext.scaleBy(x: 1, y: -1)
context.cgContext.translateBy(x: 0, y: -bounds.height)
}
layer.render(in: context.cgContext)
}.withRenderingMode(.alwaysTemplate)
}
}
}
@@ -9,8 +9,8 @@
import SwiftUI
extension View {
@ViewBuilder func `if`<Content: View>(_ condition: @autoclosure () -> Bool, transform: (Self) -> Content) -> some View {
if condition() {
@ViewBuilder func `if`<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View {
if condition {
transform(self)
} else {
self
@@ -65,7 +65,7 @@ struct LocalAuthView: View {
// Clear sensitive data on screen just in case app fails to hide its views while new database is created
m.chatId = nil
ItemsModel.shared.reversedChatItems = []
m.chats = []
m.updateChats([])
m.users = []
_ = kcAppPassword.set(password)
_ = kcSelfDestructPassword.remove()
@@ -24,6 +24,7 @@ private enum MigrationFromState: Equatable {
}
private enum MigrateFromDeviceViewAlert: Identifiable {
case finishMigration(_ fileId: Int64, _ ctrl: chat_ctrl)
case deleteChat(_ title: LocalizedStringKey = "Delete chat profile?", _ text: LocalizedStringKey = "This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost.")
case startChat(_ title: LocalizedStringKey = "Start chat?", _ text: LocalizedStringKey = "Warning: starting chat on multiple devices is not supported and will cause message delivery failures")
@@ -32,11 +33,13 @@ private enum MigrateFromDeviceViewAlert: Identifiable {
case keychainError(_ title: LocalizedStringKey = "Keychain error")
case databaseError(_ title: LocalizedStringKey = "Database error", message: String)
case unknownError(_ title: LocalizedStringKey = "Unknown error", message: String)
case archiveExportedWithErrors(archivePath: URL, archiveErrors: [ArchiveError])
case error(title: LocalizedStringKey, error: String = "")
var id: String {
switch self {
case .finishMigration: return "finishMigration"
case let .deleteChat(title, text): return "\(title) \(text)"
case let .startChat(title, text): return "\(title) \(text)"
@@ -45,6 +48,7 @@ private enum MigrateFromDeviceViewAlert: Identifiable {
case .keychainError: return "keychainError"
case let .databaseError(title, message): return "\(title) \(message)"
case let .unknownError(title, message): return "\(title) \(message)"
case let .archiveExportedWithErrors(path, _): return "archiveExportedWithErrors \(path)"
case let .error(title, _): return "error \(title)"
}
@@ -136,6 +140,15 @@ struct MigrateFromDevice: View {
}
.alert(item: $alert) { alert in
switch alert {
case let .finishMigration(fileId, ctrl):
return Alert(
title: Text("Remove archive?"),
message: Text("The uploaded database archive will be permanently removed from the servers."),
primaryButton: .destructive(Text("Continue")) {
finishMigration(fileId, ctrl)
},
secondaryButton: .cancel()
)
case let .startChat(title, text):
return Alert(
title: Text(title),
@@ -166,6 +179,14 @@ struct MigrateFromDevice: View {
return Alert(title: Text(title), message: Text(message))
case let .unknownError(title, message):
return Alert(title: Text(title), message: Text(message))
case let .archiveExportedWithErrors(archivePath, errs):
return Alert(
title: Text("Chat database exported"),
message: Text("You may migrate the exported database.") + Text(verbatim: "\n\n") + Text("Some file(s) were not exported:") + archiveErrorsText(errs),
dismissButton: .default(Text("Continue")) {
Task { await uploadArchive(path: archivePath) }
}
)
case let .error(title, error):
return Alert(title: Text(title), message: Text(error))
}
@@ -308,7 +329,7 @@ struct MigrateFromDevice: View {
Text("Cancel migration").foregroundColor(.red)
}
}
Button(action: { finishMigration(fileId, ctrl) }) {
Button(action: { alert = .finishMigration(fileId, ctrl) }) {
settingsRow("checkmark", color: theme.colors.secondary) {
Text("Finalize migration").foregroundColor(theme.colors.primary)
}
@@ -449,15 +470,12 @@ struct MigrateFromDevice: View {
Task {
do {
try? FileManager.default.createDirectory(at: getMigrationTempFilesDirectory(), withIntermediateDirectories: true)
let archivePath = try await exportChatArchive(getMigrationTempFilesDirectory())
if let attrs = try? FileManager.default.attributesOfItem(atPath: archivePath.path),
let totalBytes = attrs[.size] as? Int64 {
await MainActor.run {
migrationState = .uploadProgress(uploadedBytes: 0, totalBytes: totalBytes, fileId: 0, archivePath: archivePath, ctrl: nil)
}
let (archivePath, errs) = try await exportChatArchive(getMigrationTempFilesDirectory())
if errs.isEmpty {
await uploadArchive(path: archivePath)
} else {
await MainActor.run {
alert = .error(title: "Exported file doesn't exist")
alert = .archiveExportedWithErrors(archivePath: archivePath, archiveErrors: errs)
migrationState = .uploadConfirmation
}
}
@@ -469,6 +487,20 @@ struct MigrateFromDevice: View {
}
}
}
private func uploadArchive(path archivePath: URL) async {
if let attrs = try? FileManager.default.attributesOfItem(atPath: archivePath.path),
let totalBytes = attrs[.size] as? Int64 {
await MainActor.run {
migrationState = .uploadProgress(uploadedBytes: 0, totalBytes: totalBytes, fileId: 0, archivePath: archivePath, ctrl: nil)
}
} else {
await MainActor.run {
alert = .error(title: "Exported file doesn't exist")
migrationState = .uploadConfirmation
}
}
}
private func initTemporaryDatabase() -> (chat_ctrl, User)? {
let (status, ctrl) = chatInitTemporaryDatabase(url: tempDatabaseUrl)
@@ -502,9 +534,15 @@ struct MigrateFromDevice: View {
}
case let .sndStandaloneFileComplete(_, fileTransferMeta, rcvURIs):
let cfg = getNetCfg()
let proxy: NetworkProxy? = if cfg.socksProxy == nil {
nil
} else {
networkProxyDefault.get()
}
let data = MigrationFileLinkData.init(
networkConfig: MigrationFileLinkData.NetworkConfig(
socksProxy: cfg.socksProxy,
networkProxy: proxy,
hostMode: cfg.hostMode,
requiredHostMode: cfg.requiredHostMode
)
@@ -93,7 +93,6 @@ struct MigrateToDevice: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@Environment(\.dismiss) var dismiss: DismissAction
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@Binding var migrationState: MigrationToState?
@State private var useKeychain = storeDBPassphraseGroupDefault.get()
@State private var alert: MigrateToDeviceViewAlert?
@@ -102,6 +101,7 @@ struct MigrateToDevice: View {
// Prevent from hiding the view until migration is finished or app deleted
@State private var backDisabled: Bool = false
@State private var showQRCodeScanner: Bool = true
@State private var pasteboardHasStrings = UIPasteboard.general.hasStrings
var body: some View {
VStack {
@@ -197,10 +197,8 @@ struct MigrateToDevice: View {
}
}
}
if developerTools {
Section(header: Text("Or paste archive link").foregroundColor(theme.colors.secondary)) {
pasteLinkView()
}
Section(header: Text("Or paste archive link").foregroundColor(theme.colors.secondary)) {
pasteLinkView()
}
}
}
@@ -218,7 +216,7 @@ struct MigrateToDevice: View {
} label: {
Text("Tap to paste link")
}
.disabled(!ChatModel.shared.pasteboardHasStrings)
.disabled(!pasteboardHasStrings)
.frame(maxWidth: .infinity, alignment: .center)
}
@@ -487,6 +485,9 @@ struct MigrateToDevice: View {
do {
if !hasChatCtrl() {
chatInitControllerRemovingDatabases()
} else if ChatModel.shared.chatRunning == true {
// cannot delete storage if chat is running
try await apiStopChat()
}
try await apiDeleteStorage()
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
@@ -556,11 +557,22 @@ struct MigrateToDevice: View {
do {
try? FileManager.default.removeItem(at: getMigrationTempFilesDirectory())
MigrationToDeviceState.save(nil)
appSettings.importIntoApp()
try SimpleX.startChat(refreshInvitations: true)
AlertManager.shared.showAlertMsg(title: "Chat migrated!", message: "Finalize migration on another device.")
try ObjC.catchException {
appSettings.importIntoApp()
}
do {
try SimpleX.startChat(refreshInvitations: true)
AlertManager.shared.showAlertMsg(title: "Chat migrated!", message: "Finalize migration on another device.")
} catch let error {
AlertManager.shared.showAlert(Alert(title: Text("Error starting chat"), message: Text(responseError(error))))
}
} catch let error {
AlertManager.shared.showAlert(Alert(title: Text("Error starting chat"), message: Text(responseError(error))))
logger.error("Error importing settings: \(error.localizedDescription)")
AlertManager.shared.showAlert(
Alert(
title: Text("Error migrating settings"),
message: Text ("Not all settings were migrated. Repeat migration if you need them.") + Text("\n\n") + Text(responseError(error)))
)
}
hideView()
}
@@ -30,7 +30,7 @@ struct AddContactLearnMore: View {
}
.listRowBackground(Color.clear)
}
.modifier(ThemedBackground())
.modifier(ThemedBackground(grouped: true))
}
}
@@ -35,40 +35,37 @@ struct AddGroupView: View {
creatingGroup: true,
showFooterCounter: false
) { _ in
dismiss()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
m.chatId = groupInfo.id
dismissAllSheets(animated: true) {
ItemsModel.shared.loadOpenChat(groupInfo.id)
}
}
}
.navigationBarTitleDisplayMode(.inline)
} else {
GroupLinkView(
groupId: groupInfo.groupId,
groupLink: $groupLink,
groupLinkMemberRole: $groupLinkMemberRole,
showTitle: true,
showTitle: false,
creatingGroup: true
) {
dismiss()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
m.chatId = groupInfo.id
dismissAllSheets(animated: true) {
ItemsModel.shared.loadOpenChat(groupInfo.id)
}
}
}
.navigationBarTitle("Group link")
}
} else {
createGroupView().keyboardPadding()
createGroupView()
}
}
func createGroupView() -> some View {
List {
Group {
Text("Create secret group")
.font(.largeTitle)
.bold()
.fixedSize(horizontal: false, vertical: true)
.padding(.bottom, 24)
.onTapGesture(perform: hideKeyboard)
ZStack(alignment: .center) {
ZStack(alignment: .topTrailing) {
ProfileImage(imageStr: profile.image, size: 128)
@@ -204,13 +201,14 @@ struct AddGroupView: View {
chat = c
}
} catch {
dismiss()
AlertManager.shared.showAlert(
Alert(
title: Text("Error creating group"),
message: Text(responseError(error))
dismissAllSheets(animated: true) {
AlertManager.shared.showAlert(
Alert(
title: Text("Error creating group"),
message: Text(responseError(error))
)
)
)
}
}
}
@@ -7,53 +7,499 @@
//
import SwiftUI
import SimpleXChat
enum NewChatMenuOption: Identifiable {
case newContact
case scanPaste
case newGroup
var id: Self { self }
enum ContactType: Int {
case card, request, recent, chatDeleted, unlisted
}
struct NewChatMenuButton: View {
@Binding var newChatMenuOption: NewChatMenuOption?
@State private var showNewChatSheet = false
@State private var alert: SomeAlert? = nil
@State private var globalAlert: SomeAlert? = nil
var body: some View {
Menu {
Button {
newChatMenuOption = .newContact
} label: {
Text("Add contact")
}
Button {
newChatMenuOption = .scanPaste
} label: {
Text("Scan / Paste link")
}
Button {
newChatMenuOption = .newGroup
} label: {
Text("Create group")
}
showNewChatSheet = true
} label: {
Image(systemName: "square.and.pencil")
.resizable()
.scaledToFit()
.frame(width: 24, height: 24)
}
.sheet(item: $newChatMenuOption) { opt in
switch opt {
case .newContact: NewChatView(selection: .invite)
case .scanPaste: NewChatView(selection: .connect, showQRCodeScanner: true)
case .newGroup: AddGroupView()
.appSheet(isPresented: $showNewChatSheet) {
NewChatSheet(alert: $alert)
.environment(\EnvironmentValues.refresh as! WritableKeyPath<EnvironmentValues, RefreshAction?>, nil)
.alert(item: $alert) { a in
return a.alert
}
}
// This is a workaround to show "Keep unused invitation" alert in both following cases:
// - on going back from NewChatView to NewChatSheet,
// - on dismissing NewChatMenuButton sheet while on NewChatView (skipping NewChatSheet)
.onChange(of: alert?.id) { a in
if !showNewChatSheet && alert != nil {
globalAlert = alert
alert = nil
}
}
.alert(item: $globalAlert) { a in
return a.alert
}
}
}
#Preview {
NewChatMenuButton(
newChatMenuOption: Binding.constant(nil)
)
private var indent: CGFloat = 36
struct NewChatSheet: View {
@EnvironmentObject var theme: AppTheme
@State private var baseContactTypes: [ContactType] = [.card, .request, .recent]
@EnvironmentObject var chatModel: ChatModel
@State private var searchMode = false
@FocusState var searchFocussed: Bool
@State private var searchText = ""
@State private var searchShowingSimplexLink = false
@State private var searchChatFilteredBySimplexLink: String? = nil
@Binding var alert: SomeAlert?
// Sheet height management
@State private var isAddContactActive = false
@State private var isScanPasteLinkActive = false
@State private var isLargeSheet = false
@State private var allowSmallSheet = true
@AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = true
var body: some View {
let showArchive = !filterContactTypes(chats: chatModel.chats, contactTypes: [.chatDeleted]).isEmpty
let v = NavigationView {
viewBody(showArchive)
.navigationTitle("New message")
.navigationBarTitleDisplayMode(.large)
.navigationBarHidden(searchMode)
.modifier(ThemedBackground(grouped: true))
}
if #available(iOS 16.0, *), oneHandUI {
let sheetHeight: CGFloat = showArchive ? 575 : 500
v.presentationDetents(
allowSmallSheet ? [.height(sheetHeight), .large] : [.large],
selection: Binding(
get: { isLargeSheet || !allowSmallSheet ? .large : .height(sheetHeight) },
set: { isLargeSheet = $0 == .large }
)
)
} else {
v
}
}
@ViewBuilder private func viewBody(_ showArchive: Bool) -> some View {
List {
HStack {
ContactsListSearchBar(
searchMode: $searchMode,
searchFocussed: $searchFocussed,
searchText: $searchText,
searchShowingSimplexLink: $searchShowingSimplexLink,
searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink
)
.frame(maxWidth: .infinity)
}
.listRowSeparator(.hidden)
.listRowBackground(Color.clear)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
if (searchText.isEmpty) {
Section {
NavigationLink(isActive: $isAddContactActive) {
NewChatView(selection: .invite, parentAlert: $alert)
.navigationTitle("New chat")
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(.large)
} label: {
navigateOnTap(Label("Add contact", systemImage: "link.badge.plus")) {
isAddContactActive = true
}
}
NavigationLink(isActive: $isScanPasteLinkActive) {
NewChatView(selection: .connect, showQRCodeScanner: true, parentAlert: $alert)
.navigationTitle("New chat")
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(.large)
} label: {
navigateOnTap(Label("Scan / Paste link", systemImage: "qrcode")) {
isScanPasteLinkActive = true
}
}
NavigationLink {
AddGroupView()
.navigationTitle("Create secret group")
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(.large)
} label: {
Label("Create group", systemImage: "person.2.circle.fill")
}
}
if (showArchive) {
Section {
NavigationLink {
DeletedChats()
} label: {
newChatActionButton("archivebox", color: theme.colors.secondary) { Text("Archived contacts") }
}
}
}
}
ContactsList(
baseContactTypes: $baseContactTypes,
searchMode: $searchMode,
searchText: $searchText,
header: "Your Contacts",
searchFocussed: $searchFocussed,
searchShowingSimplexLink: $searchShowingSimplexLink,
searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink,
showDeletedChatIcon: true
)
}
}
/// Extends label's tap area to match `.insetGrouped` list row insets
private func navigateOnTap<L: View>(_ label: L, setActive: @escaping () -> Void) -> some View {
label
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.leading, 16).padding(.vertical, 8).padding(.trailing, 32)
.contentShape(Rectangle())
.onTapGesture {
isLargeSheet = true
DispatchQueue.main.async {
allowSmallSheet = false
setActive()
}
}
.padding(.leading, -16).padding(.vertical, -8).padding(.trailing, -32)
}
func newChatActionButton<Content : View>(_ icon: String, color: Color/* = .secondary*/, content: @escaping () -> Content) -> some View {
ZStack(alignment: .leading) {
Image(systemName: icon)
.resizable()
.scaledToFit()
.frame(maxWidth: 24, maxHeight: 24, alignment: .center)
.symbolRenderingMode(.monochrome)
.foregroundColor(color)
content().foregroundColor(theme.colors.onBackground).padding(.leading, indent)
}
}
}
func chatContactType(chat: Chat) -> ContactType {
switch chat.chatInfo {
case .contactRequest:
return .request
case let .direct(contact):
if contact.activeConn == nil && contact.profile.contactLink != nil && contact.active {
return .card
} else if contact.chatDeleted {
return .chatDeleted
} else if contact.contactStatus == .active {
return .recent
} else {
return .unlisted
}
default:
return .unlisted
}
}
private func filterContactTypes(chats: [Chat], contactTypes: [ContactType]) -> [Chat] {
return chats.filter { chat in
contactTypes.contains(chatContactType(chat: chat))
}
}
struct ContactsList: View {
@EnvironmentObject var theme: AppTheme
@EnvironmentObject var chatModel: ChatModel
@Binding var baseContactTypes: [ContactType]
@Binding var searchMode: Bool
@Binding var searchText: String
var header: String? = nil
@FocusState.Binding var searchFocussed: Bool
@Binding var searchShowingSimplexLink: Bool
@Binding var searchChatFilteredBySimplexLink: String?
var showDeletedChatIcon: Bool
@AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false
var body: some View {
let contactTypes = contactTypesSearchTargets(baseContactTypes: baseContactTypes, searchEmpty: searchText.isEmpty)
let contactChats = filterContactTypes(chats: chatModel.chats, contactTypes: contactTypes)
let filteredContactChats = filteredContactChats(
showUnreadAndFavorites: showUnreadAndFavorites,
searchShowingSimplexLink: searchShowingSimplexLink,
searchChatFilteredBySimplexLink: searchChatFilteredBySimplexLink,
searchText: searchText,
contactChats: contactChats
)
if !filteredContactChats.isEmpty {
Section(header: Group {
if let header = header {
Text(header)
.textCase(.uppercase)
.foregroundColor(theme.colors.secondary)
}
}
) {
ForEach(filteredContactChats, id: \.viewId) { chat in
ContactListNavLink(chat: chat, showDeletedChatIcon: showDeletedChatIcon)
.disabled(chatModel.chatRunning != true)
}
}
}
if filteredContactChats.isEmpty && !contactChats.isEmpty {
noResultSection(text: "No filtered contacts")
} else if contactChats.isEmpty {
noResultSection(text: "No contacts")
}
}
@ViewBuilder private func noResultSection(text: String) -> some View {
Section {
Text(text)
.foregroundColor(theme.colors.secondary)
.frame(maxWidth: .infinity, alignment: .center)
}
.listRowSeparator(.hidden)
.listRowBackground(Color.clear)
.listRowInsets(EdgeInsets(top: 7, leading: 0, bottom: 7, trailing: 0))
}
private func contactTypesSearchTargets(baseContactTypes: [ContactType], searchEmpty: Bool) -> [ContactType] {
if baseContactTypes.contains(.chatDeleted) || searchEmpty {
return baseContactTypes
} else {
return baseContactTypes + [.chatDeleted]
}
}
private func chatsByTypeComparator(chat1: Chat, chat2: Chat) -> Bool {
let chat1Type = chatContactType(chat: chat1)
let chat2Type = chatContactType(chat: chat2)
if chat1Type.rawValue < chat2Type.rawValue {
return true
} else if chat1Type.rawValue > chat2Type.rawValue {
return false
} else {
return chat2.chatInfo.chatTs < chat1.chatInfo.chatTs
}
}
private func filterChat(chat: Chat, searchText: String, showUnreadAndFavorites: Bool) -> Bool {
var meetsPredicate = true
let s = searchText.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
let cInfo = chat.chatInfo
if !searchText.isEmpty {
if (!cInfo.chatViewName.lowercased().contains(searchText.lowercased())) {
if case let .direct(contact) = cInfo {
meetsPredicate = contact.profile.displayName.lowercased().contains(s) || contact.fullName.lowercased().contains(s)
} else {
meetsPredicate = false
}
}
}
if showUnreadAndFavorites {
meetsPredicate = meetsPredicate && (cInfo.chatSettings?.favorite ?? false)
}
return meetsPredicate
}
func filteredContactChats(
showUnreadAndFavorites: Bool,
searchShowingSimplexLink: Bool,
searchChatFilteredBySimplexLink: String?,
searchText: String,
contactChats: [Chat]
) -> [Chat] {
let linkChatId = searchChatFilteredBySimplexLink
let s = searchShowingSimplexLink ? "" : searchText.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
let filteredChats: [Chat]
if let linkChatId = linkChatId {
filteredChats = contactChats.filter { $0.id == linkChatId }
} else {
filteredChats = contactChats.filter { chat in
filterChat(chat: chat, searchText: s, showUnreadAndFavorites: showUnreadAndFavorites)
}
}
return filteredChats.sorted(by: chatsByTypeComparator)
}
}
struct ContactsListSearchBar: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@Binding var searchMode: Bool
@FocusState.Binding var searchFocussed: Bool
@Binding var searchText: String
@Binding var searchShowingSimplexLink: Bool
@Binding var searchChatFilteredBySimplexLink: String?
@State private var ignoreSearchTextChange = false
@State private var alert: PlanAndConnectAlert?
@State private var sheet: PlanAndConnectActionSheet?
@AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false
var body: some View {
HStack(spacing: 12) {
HStack(spacing: 4) {
Spacer()
.frame(width: 8)
Image(systemName: "magnifyingglass")
.resizable()
.scaledToFit()
.frame(width: 16, height: 16)
TextField("Search or paste SimpleX link", text: $searchText)
.foregroundColor(searchShowingSimplexLink ? theme.colors.secondary : theme.colors.onBackground)
.disabled(searchShowingSimplexLink)
.focused($searchFocussed)
.frame(maxWidth: .infinity)
if !searchText.isEmpty {
Image(systemName: "xmark.circle.fill")
.resizable()
.scaledToFit()
.frame(width: 16, height: 16)
.onTapGesture {
searchText = ""
}
}
}
.padding(EdgeInsets(top: 7, leading: 7, bottom: 7, trailing: 7))
.foregroundColor(theme.colors.secondary)
.background(Color(uiColor: .secondarySystemGroupedBackground))
.cornerRadius(10.0)
if searchFocussed {
Text("Cancel")
.foregroundColor(theme.colors.primary)
.onTapGesture {
searchText = ""
searchFocussed = false
}
} else if m.chats.count > 0 {
toggleFilterButton()
}
}
.padding(.top, 24)
.onChange(of: searchFocussed) { sf in
withAnimation { searchMode = sf }
}
.onChange(of: searchText) { t in
if ignoreSearchTextChange {
ignoreSearchTextChange = false
} else {
if let link = strHasSingleSimplexLink(t.trimmingCharacters(in: .whitespaces)) { // if SimpleX link is pasted, show connection dialogue
searchFocussed = false
if case let .simplexLink(linkType, _, smpHosts) = link.format {
ignoreSearchTextChange = true
searchText = simplexLinkText(linkType, smpHosts)
}
searchShowingSimplexLink = true
searchChatFilteredBySimplexLink = nil
connect(link.text)
} else {
if t != "" { // if some other text is pasted, enter search mode
searchFocussed = true
}
searchShowingSimplexLink = false
searchChatFilteredBySimplexLink = nil
}
}
}
.alert(item: $alert) { a in
planAndConnectAlert(a, dismiss: true, cleanup: { searchText = "" })
}
.actionSheet(item: $sheet) { s in
planAndConnectActionSheet(s, dismiss: true, cleanup: { searchText = "" })
}
}
private func toggleFilterButton() -> some View {
ZStack {
Color.clear
.frame(width: 22, height: 22)
Image(systemName: showUnreadAndFavorites ? "line.3.horizontal.decrease.circle.fill" : "line.3.horizontal.decrease")
.resizable()
.scaledToFit()
.foregroundColor(showUnreadAndFavorites ? theme.colors.primary : theme.colors.secondary)
.frame(width: showUnreadAndFavorites ? 22 : 16, height: showUnreadAndFavorites ? 22 : 16)
.onTapGesture {
showUnreadAndFavorites = !showUnreadAndFavorites
}
}
}
private func connect(_ link: String) {
planAndConnect(
link,
showAlert: { alert = $0 },
showActionSheet: { sheet = $0 },
dismiss: true,
incognito: nil,
filterKnownContact: { searchChatFilteredBySimplexLink = $0.id }
)
}
}
struct DeletedChats: View {
@State private var baseContactTypes: [ContactType] = [.chatDeleted]
@State private var searchMode = false
@FocusState var searchFocussed: Bool
@State private var searchText = ""
@State private var searchShowingSimplexLink = false
@State private var searchChatFilteredBySimplexLink: String? = nil
var body: some View {
List {
ContactsListSearchBar(
searchMode: $searchMode,
searchFocussed: $searchFocussed,
searchText: $searchText,
searchShowingSimplexLink: $searchShowingSimplexLink,
searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink
)
.listRowSeparator(.hidden)
.listRowBackground(Color.clear)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.frame(maxWidth: .infinity)
ContactsList(
baseContactTypes: $baseContactTypes,
searchMode: $searchMode,
searchText: $searchText,
searchFocussed: $searchFocussed,
searchShowingSimplexLink: $searchShowingSimplexLink,
searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink,
showDeletedChatIcon: false
)
}
.navigationTitle("Archived contacts")
.navigationBarTitleDisplayMode(.large)
.navigationBarHidden(searchMode)
.modifier(ThemedBackground(grouped: true))
}
}
#Preview {
NewChatMenuButton()
}
+53 -36
View File
@@ -17,10 +17,19 @@ struct SomeAlert: Identifiable {
var id: String
}
struct SomeActionSheet: Identifiable {
var actionSheet: ActionSheet
var id: String
}
struct SomeSheet<Content: View>: Identifiable {
@ViewBuilder var content: Content
var id: String
}
private enum NewChatViewAlert: Identifiable {
case planAndConnectAlert(alert: PlanAndConnectAlert)
case newChatSomeAlert(alert: SomeAlert)
var id: String {
switch self {
case let .planAndConnectAlert(alert): return "planAndConnectAlert \(alert.id)"
@@ -47,22 +56,10 @@ struct NewChatView: View {
@State private var creatingConnReq = false
@State private var pastedLink: String = ""
@State private var alert: NewChatViewAlert?
@Binding var parentAlert: SomeAlert?
var body: some View {
VStack(alignment: .leading) {
HStack {
Text("New chat")
.font(.largeTitle)
.bold()
.fixedSize(horizontal: false, vertical: true)
Spacer()
InfoSheetButton {
AddContactLearnMore(showTitle: true)
}
}
.padding()
.padding(.top)
Picker("New chat", selection: $selection) {
Label("Add contact", systemImage: "link")
.tag(NewChatOption.invite)
@@ -88,6 +85,7 @@ struct NewChatView: View {
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.modifier(ThemedBackground(grouped: true))
.background(
// Rectangle is needed for swipe gesture to work on mostly empty views (creatingLinkProgressView and retryButton)
Rectangle()
@@ -110,6 +108,13 @@ struct NewChatView: View {
}
)
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
InfoSheetButton {
AddContactLearnMore(showTitle: true)
}
}
}
.modifier(ThemedBackground(grouped: true))
.onChange(of: invitationUsed) { used in
if used && !(m.showingInvitation?.connChatUsed ?? true) {
@@ -119,19 +124,22 @@ struct NewChatView: View {
.onDisappear {
if !(m.showingInvitation?.connChatUsed ?? true),
let conn = contactConnection {
AlertManager.shared.showAlert(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: []
))
parentAlert = 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: []
))
}
}
}
))
),
id: "keepUnusedInvitation"
)
}
m.showingInvitation = nil
}
@@ -288,6 +296,7 @@ private struct ConnectView: View {
@Binding var pastedLink: String
@Binding var alert: NewChatViewAlert?
@State private var sheet: PlanAndConnectActionSheet?
@State private var pasteboardHasStrings = UIPasteboard.general.hasStrings
var body: some View {
List {
@@ -324,7 +333,7 @@ private struct ConnectView: View {
} label: {
Text("Tap to paste link")
}
.disabled(!ChatModel.shared.pasteboardHasStrings)
.disabled(!pasteboardHasStrings)
.frame(maxWidth: .infinity, alignment: .center)
} else {
linkTextView(pastedLink)
@@ -837,7 +846,10 @@ private func connectContactViaAddress_(_ contact: Contact, dismiss: Bool, incogn
dismissAllSheets(animated: true)
}
}
_ = await connectContactViaAddress(contact.contactId, incognito)
let ok = await connectContactViaAddress(contact.contactId, incognito, showAlert: { AlertManager.shared.showAlert($0) })
if ok {
AlertManager.shared.showAlert(connReqSentAlert(.contact))
}
cleanup?()
}
}
@@ -887,11 +899,11 @@ func openKnownContact(_ contact: Contact, dismiss: Bool, showAlreadyExistsAlert:
DispatchQueue.main.async {
if dismiss {
dismissAllSheets(animated: true) {
m.chatId = c.id
ItemsModel.shared.loadOpenChat(c.id)
showAlreadyExistsAlert?()
}
} else {
m.chatId = c.id
ItemsModel.shared.loadOpenChat(c.id)
showAlreadyExistsAlert?()
}
}
@@ -906,11 +918,11 @@ func openKnownGroup(_ groupInfo: GroupInfo, dismiss: Bool, showAlreadyExistsAler
DispatchQueue.main.async {
if dismiss {
dismissAllSheets(animated: true) {
m.chatId = g.id
ItemsModel.shared.loadOpenChat(g.id)
showAlreadyExistsAlert?()
}
} else {
m.chatId = g.id
ItemsModel.shared.loadOpenChat(g.id)
showAlreadyExistsAlert?()
}
}
@@ -961,8 +973,13 @@ func connReqSentAlert(_ type: ConnReqType) -> Alert {
)
}
#Preview {
NewChatView(
selection: .invite
)
struct NewChatView_Previews: PreviewProvider {
static var previews: some View {
@State var parentAlert: SomeAlert?
NewChatView(
selection: .invite,
parentAlert: $parentAlert
)
}
}
@@ -77,7 +77,6 @@ struct CreateProfile: View {
focusDisplayName = true
}
}
.keyboardPadding()
}
}
@@ -128,7 +127,6 @@ struct CreateFirstProfile: View {
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.keyboardPadding()
}
func onboardingButtons() -> some View {
@@ -15,9 +15,10 @@ private struct VersionDescription {
}
private struct FeatureDescription {
var icon: String
var icon: String?
var title: LocalizedStringKey
var description: LocalizedStringKey
var description: LocalizedStringKey?
var subfeatures: [(icon: String, description: LocalizedStringKey)] = []
}
private let versionDescriptions: [VersionDescription] = [
@@ -428,6 +429,44 @@ private let versionDescriptions: [VersionDescription] = [
)
]
),
VersionDescription(
version: "v6.0",
post: URL(string: "https://simplex.chat/blog/20240814-simplex-chat-vision-funding-v6-private-routing-new-user-experience.html"),
features: [
FeatureDescription(
icon: nil,
title: "New chat experience 🎉",
description: nil,
subfeatures: [
("link.badge.plus", "Connect to your friends faster."),
("archivebox", "Archive contacts to chat later."),
("trash", "Delete up to 20 messages at once."),
("platter.filled.bottom.and.arrow.down.iphone", "Use the app with one hand."),
("paintpalette", "Color chats with the new themes."),
]
),
FeatureDescription(
icon: nil,
title: "New media options",
description: nil,
subfeatures: [
("square.and.arrow.up", "Share from other apps."),
("play.circle", "Play from the chat list."),
("circle.filled.pattern.diagonalline.rectangle", "Blur for better privacy.")
]
),
FeatureDescription(
icon: "arrow.forward",
title: "Private message routing 🚀",
description: "It protects your IP address and connections."
),
FeatureDescription(
icon: "network",
title: "Better networking",
description: "Connection and servers status."
)
]
),
]
private let lastVersion = versionDescriptions.last!.version
@@ -453,35 +492,37 @@ struct WhatsNewView: View {
VStack {
TabView(selection: $currentVersion) {
ForEach(Array(versionDescriptions.enumerated()), id: \.0) { (i, v) in
VStack(alignment: .leading, spacing: 16) {
Text("New in \(v.version)")
.font(.title)
.foregroundColor(theme.colors.secondary)
.frame(maxWidth: .infinity)
.padding(.vertical)
ForEach(v.features, id: \.icon) { f in
featureDescription(f.icon, f.title, f.description)
.padding(.bottom, 8)
}
if let post = v.post {
Link(destination: post) {
HStack {
Text("Read more")
Image(systemName: "arrow.up.right.circle")
ScrollView {
VStack(alignment: .leading, spacing: 16) {
Text("New in \(v.version)")
.font(.title)
.foregroundColor(theme.colors.secondary)
.frame(maxWidth: .infinity)
.padding(.vertical)
ForEach(v.features, id: \.title) { f in
featureDescription(f)
.padding(.bottom, 8)
}
if let post = v.post {
Link(destination: post) {
HStack {
Text("Read more")
Image(systemName: "arrow.up.right.circle")
}
}
}
}
if !viaSettings {
Spacer()
Button("Ok") {
dismiss()
if !viaSettings {
Spacer()
Button("Ok") {
dismiss()
}
.font(.title3)
.frame(maxWidth: .infinity, alignment: .center)
Spacer()
}
.font(.title3)
.frame(maxWidth: .infinity, alignment: .center)
Spacer()
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
.tag(i)
}
}
@@ -495,18 +536,37 @@ struct WhatsNewView: View {
}
}
private func featureDescription(_ icon: String, _ title: LocalizedStringKey, _ description: LocalizedStringKey) -> some View {
private func featureDescription(_ f: FeatureDescription) -> some View {
VStack(alignment: .leading, spacing: 4) {
HStack(alignment: .center, spacing: 4) {
Image(systemName: icon)
.symbolRenderingMode(.monochrome)
.foregroundColor(theme.colors.secondary)
.frame(minWidth: 30, alignment: .center)
Text(title).font(.title3).bold()
if let icon = f.icon {
HStack(alignment: .center, spacing: 4) {
Image(systemName: icon)
.symbolRenderingMode(.monochrome)
.foregroundColor(theme.colors.secondary)
.frame(minWidth: 30, alignment: .center)
Text(f.title).font(.title3).bold()
}
} else {
Text(f.title).font(.title3).bold()
}
if let d = f.description {
Text(d)
.multilineTextAlignment(.leading)
.lineLimit(10)
}
if f.subfeatures.count > 0 {
ForEach(f.subfeatures, id: \.icon) { s in
HStack(alignment: .center, spacing: 4) {
Image(systemName: s.icon)
.symbolRenderingMode(.monochrome)
.foregroundColor(theme.colors.secondary)
.frame(minWidth: 30, alignment: .center)
Text(s.description)
.multilineTextAlignment(.leading)
.lineLimit(3)
}
}
}
Text(description)
.multilineTextAlignment(.leading)
.lineLimit(10)
}
}
@@ -24,34 +24,188 @@ enum NetworkSettingsAlert: Identifiable {
}
struct AdvancedNetworkSettings: View {
@Environment(\.dismiss) var dismiss: DismissAction
@EnvironmentObject var theme: AppTheme
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = false
@State private var netCfg = NetCfg.defaults
@State private var currentNetCfg = NetCfg.defaults
@State private var cfgLoaded = false
@State private var enableKeepAlive = true
@State private var keepAliveOpts = KeepAliveOpts.defaults
@State private var showSettingsAlert: NetworkSettingsAlert?
@State private var onionHosts: OnionHosts = .no
@State private var showSaveDialog = false
@State private var netProxy = networkProxyDefault.get()
@State private var currentNetProxy = networkProxyDefault.get()
@State private var useNetProxy = false
@State private var netProxyAuth = false
var body: some View {
VStack {
List {
Section {
Button {
updateNetCfgView(NetCfg.defaults)
showSettingsAlert = .update
NavigationLink {
List {
Section {
SelectionListView(list: SMPProxyMode.values, selection: $netCfg.smpProxyMode) { mode in
netCfg.smpProxyMode = mode
}
} footer: {
Text(proxyModeInfo(netCfg.smpProxyMode))
.font(.callout)
.foregroundColor(theme.colors.secondary)
}
}
.navigationTitle("Private routing")
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(.inline)
} label: {
Text("Reset to defaults")
HStack {
Text("Private routing")
Spacer()
Text(netCfg.smpProxyMode.label)
}
}
.disabled(currentNetCfg == NetCfg.defaults)
Button {
updateNetCfgView(NetCfg.proxyDefaults)
showSettingsAlert = .update
NavigationLink {
List {
Section {
SelectionListView(list: SMPProxyFallback.values, selection: $netCfg.smpProxyFallback) { mode in
netCfg.smpProxyFallback = mode
}
.disabled(netCfg.smpProxyMode == .never)
} footer: {
Text(proxyFallbackInfo(netCfg.smpProxyFallback))
.font(.callout)
.foregroundColor(theme.colors.secondary)
}
}
.navigationTitle("Allow downgrade")
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(.inline)
} label: {
Text("Set timeouts for proxy/VPN")
HStack {
Text("Allow downgrade")
Spacer()
Text(netCfg.smpProxyFallback.label)
}
}
.disabled(currentNetCfg == NetCfg.proxyDefaults)
Toggle("Show message status", isOn: $showSentViaProxy)
} header: {
Text("Private message routing")
.foregroundColor(theme.colors.secondary)
} footer: {
VStack(alignment: .leading) {
Text("To protect your IP address, private routing uses your SMP servers to deliver messages.")
if showSentViaProxy {
Text("Show → on messages sent via private routing.")
}
}
.foregroundColor(theme.colors.secondary)
}
Section {
Toggle("Use SOCKS proxy", isOn: $useNetProxy)
Group {
TextField("IP address", text: $netProxy.host)
TextField(
"Port",
text: Binding(
get: { netProxy.port > 0 ? "\(netProxy.port)" : "" },
set: { s in
netProxy.port = if let port = Int(s), port > 0 {
port
} else {
0
}
}
)
)
Toggle("Proxy requires password", isOn: $netProxyAuth)
if netProxyAuth {
TextField("Username", text: $netProxy.username)
PassphraseField(
key: $netProxy.password,
placeholder: "Password",
valid: NetworkProxy.validCredential(netProxy.password)
)
}
}
.if(!useNetProxy) { $0.foregroundColor(theme.colors.secondary) }
.disabled(!useNetProxy)
} header: {
HStack {
Text("SOCKS proxy").foregroundColor(theme.colors.secondary)
if useNetProxy && !netProxy.valid {
Spacer()
Image(systemName: "exclamationmark.circle.fill").foregroundColor(.red)
}
}
} footer: {
if netProxyAuth {
Text("Your credentials may be sent unencrypted.")
.foregroundColor(theme.colors.secondary)
} else {
Text("Do not use credentials with proxy.")
.foregroundColor(theme.colors.secondary)
}
}
.onChange(of: useNetProxy) { useNetProxy in
netCfg.socksProxy = useNetProxy && currentNetProxy.valid
? currentNetProxy.toProxyString()
: nil
netProxy = currentNetProxy
netProxyAuth = netProxy.username != "" || netProxy.password != ""
}
.onChange(of: netProxyAuth) { netProxyAuth in
if netProxyAuth {
netProxy.auth = currentNetProxy.auth
netProxy.username = currentNetProxy.username
netProxy.password = currentNetProxy.password
} else {
netProxy.auth = .username
netProxy.username = ""
netProxy.password = ""
}
}
.onChange(of: netProxy) { netProxy in
netCfg.socksProxy = useNetProxy && netProxy.valid
? netProxy.toProxyString()
: nil
}
Section {
Picker("Use .onion hosts", selection: $onionHosts) {
ForEach(OnionHosts.values, id: \.self) { Text($0.text) }
}
.frame(height: 36)
} footer: {
Text(onionHostsInfo(onionHosts))
.foregroundColor(theme.colors.secondary)
}
.onChange(of: onionHosts) { hosts in
if hosts != OnionHosts(netCfg: currentNetCfg) {
let (hostMode, requiredHostMode) = hosts.hostMode
netCfg.hostMode = hostMode
netCfg.requiredHostMode = requiredHostMode
}
}
if developerTools {
Section {
Picker("Transport isolation", selection: $netCfg.sessionMode) {
ForEach(TransportSessionMode.values, id: \.self) { Text($0.text) }
}
.frame(height: 36)
} footer: {
Text(sessionModeInfo(netCfg.sessionMode))
.foregroundColor(theme.colors.secondary)
}
}
Section("TCP connection") {
timeoutSettingPicker("TCP connection timeout", selection: $netCfg.tcpConnectTimeout, values: [10_000000, 15_000000, 20_000000, 30_000000, 45_000000, 60_000000, 90_000000], label: secondsLabel)
timeoutSettingPicker("Protocol timeout", selection: $netCfg.tcpTimeout, values: [5_000000, 7_000000, 10_000000, 15_000000, 20_000000, 30_000000], label: secondsLabel)
timeoutSettingPicker("Protocol timeout per KB", selection: $netCfg.tcpTimeoutPerKb, values: [2_500, 5_000, 10_000, 15_000, 20_000, 30_000], label: secondsLabel)
@@ -72,26 +226,23 @@ struct AdvancedNetworkSettings: View {
}
.foregroundColor(theme.colors.secondary)
}
} header: {
Text("")
.foregroundColor(theme.colors.secondary)
} footer: {
HStack {
Button {
updateNetCfgView(currentNetCfg)
} label: {
Label("Revert", systemImage: "arrow.counterclockwise").font(.callout)
}
Spacer()
Button {
showSettingsAlert = .update
} label: {
Label("Save", systemImage: "checkmark").font(.callout)
}
}
Section {
Button("Reset to defaults") {
updateNetCfgView(NetCfg.defaults, NetworkProxy.def)
}
.disabled(netCfg == currentNetCfg)
.disabled(netCfg == NetCfg.defaults)
Button("Set timeouts for proxy/VPN") {
updateNetCfgView(netCfg.withProxyTimeouts, netProxy)
}
.disabled(netCfg.hasProxyTimeouts)
Button("Save and reconnect") {
showSettingsAlert = .update
}
.disabled(netCfg == currentNetCfg || (useNetProxy && !netProxy.valid))
}
}
}
@@ -105,16 +256,17 @@ struct AdvancedNetworkSettings: View {
if cfgLoaded { return }
cfgLoaded = true
currentNetCfg = getNetCfg()
updateNetCfgView(currentNetCfg)
currentNetProxy = networkProxyDefault.get()
updateNetCfgView(currentNetCfg, currentNetProxy)
}
.alert(item: $showSettingsAlert) { a in
switch a {
case .update:
return Alert(
title: Text("Update network settings?"),
title: Text("Update settings?"),
message: Text("Updating settings will re-connect the client to all servers."),
primaryButton: .default(Text("Ok")) {
saveNetCfg()
_ = saveNetCfg()
},
secondaryButton: .cancel()
)
@@ -125,23 +277,51 @@ struct AdvancedNetworkSettings: View {
)
}
}
.modifier(BackButton(disabled: Binding.constant(false)) {
if netCfg == currentNetCfg {
dismiss()
cfgLoaded = false
} else if !useNetProxy || netProxy.valid {
showSaveDialog = true
}
})
.confirmationDialog("Update network settings?", isPresented: $showSaveDialog, titleVisibility: .visible) {
Button("Save and reconnect") {
if saveNetCfg() {
dismiss()
cfgLoaded = false
}
}
Button("Exit without saving") { dismiss() }
}
}
private func updateNetCfgView(_ cfg: NetCfg) {
private func updateNetCfgView(_ cfg: NetCfg, _ proxy: NetworkProxy) {
netCfg = cfg
netProxy = proxy
onionHosts = OnionHosts(netCfg: netCfg)
enableKeepAlive = netCfg.enableKeepAlive
keepAliveOpts = netCfg.tcpKeepAlive ?? KeepAliveOpts.defaults
useNetProxy = netCfg.socksProxy != nil
netProxyAuth = switch netProxy.auth {
case .username: netProxy.username != "" || netProxy.password != ""
case .isolate: false
}
}
private func saveNetCfg() {
private func saveNetCfg() -> Bool {
do {
try setNetworkConfig(netCfg)
currentNetCfg = netCfg
setNetCfg(netCfg)
setNetCfg(netCfg, networkProxy: useNetProxy ? netProxy : nil)
currentNetProxy = netProxy
networkProxyDefault.set(netProxy)
return true
} catch let error {
let err = responseError(error)
showSettingsAlert = .error(err: err)
logger.error("\(err)")
return false
}
}
@@ -164,6 +344,38 @@ struct AdvancedNetworkSettings: View {
}
.frame(height: 36)
}
private func onionHostsInfo(_ hosts: OnionHosts) -> LocalizedStringKey {
switch hosts {
case .no: return "Onion hosts will not be used."
case .prefer: return "Onion hosts will be used when available.\nRequires compatible VPN."
case .require: return "Onion hosts will be **required** for connection.\nRequires compatible VPN."
}
}
private func sessionModeInfo(_ mode: TransportSessionMode) -> LocalizedStringKey {
switch mode {
case .user: return "A separate TCP connection will be used **for each chat profile you have in the app**."
case .entity: return "A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail."
}
}
private func proxyModeInfo(_ mode: SMPProxyMode) -> LocalizedStringKey {
switch mode {
case .always: return "Always use private routing."
case .unknown: return "Use private routing with unknown servers."
case .unprotected: return "Use private routing with unknown servers when IP address is not protected."
case .never: return "Do NOT use private routing."
}
}
private func proxyFallbackInfo(_ proxyFallback: SMPProxyFallback) -> LocalizedStringKey {
switch proxyFallback {
case .allow: return "Send messages directly when your or destination server does not support private routing."
case .allowProtected: return "Send messages directly when IP address is protected and your or destination server does not support private routing."
case .prohibit: return "Do NOT send messages directly, even if your or destination server does not support private routing."
}
}
}
struct AdvancedNetworkSettings_Previews: PreviewProvider {
@@ -19,16 +19,25 @@ extension AppSettings {
val.hostMode = .publicHost
val.requiredHostMode = true
}
val.socksProxy = nil
setNetCfg(val)
if val.socksProxy != nil {
val.socksProxy = networkProxy?.toProxyString()
setNetCfg(val, networkProxy: networkProxy)
} else {
val.socksProxy = nil
setNetCfg(val, networkProxy: nil)
}
}
if let val = networkProxy { networkProxyDefault.set(val) }
if let val = privacyEncryptLocalFiles { privacyEncryptLocalFilesGroupDefault.set(val) }
if let val = privacyAskToApproveRelays { privacyAskToApproveRelaysGroupDefault.set(val) }
if let val = privacyAcceptImages {
privacyAcceptImagesGroupDefault.set(val)
def.setValue(val, forKey: DEFAULT_PRIVACY_ACCEPT_IMAGES)
}
if let val = privacyLinkPreviews { def.setValue(val, forKey: DEFAULT_PRIVACY_LINK_PREVIEWS) }
if let val = privacyLinkPreviews {
privacyLinkPreviewsGroupDefault.set(val)
def.setValue(val, forKey: DEFAULT_PRIVACY_LINK_PREVIEWS)
}
if let val = privacyShowChatPreviews { def.setValue(val, forKey: DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS) }
if let val = privacySaveLastDraft { def.setValue(val, forKey: DEFAULT_PRIVACY_SAVE_LAST_DRAFT) }
if let val = privacyProtectScreen { def.setValue(val, forKey: DEFAULT_PRIVACY_PROTECT_SCREEN) }
@@ -45,17 +54,22 @@ extension AppSettings {
if let val = androidCallOnLockScreen { def.setValue(val.rawValue, forKey: ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN) }
if let val = iosCallKitEnabled { callKitEnabledGroupDefault.set(val) }
if let val = iosCallKitCallsInRecents { def.setValue(val, forKey: DEFAULT_CALL_KIT_CALLS_IN_RECENTS) }
if let val = uiProfileImageCornerRadius { def.setValue(val, forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) }
if let val = uiColorScheme { def.setValue(val, forKey: DEFAULT_CURRENT_THEME) }
if let val = uiDarkColorScheme { def.setValue(val, forKey: DEFAULT_SYSTEM_DARK_THEME) }
if let val = uiCurrentThemeIds { def.setValue(val, forKey: DEFAULT_CURRENT_THEME_IDS) }
if let val = uiThemes { def.setValue(val.skipDuplicates(), forKey: DEFAULT_THEME_OVERRIDES) }
if let val = uiProfileImageCornerRadius {
profileImageCornerRadiusGroupDefault.set(val)
def.setValue(val, forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS)
}
if let val = uiColorScheme { currentThemeDefault.set(val) }
if let val = uiDarkColorScheme { systemDarkThemeDefault.set(val) }
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) }
}
public static var current: AppSettings {
let def = UserDefaults.standard
var c = AppSettings.defaults
c.networkConfig = getNetCfg()
c.networkProxy = networkProxyDefault.get()
c.privacyEncryptLocalFiles = privacyEncryptLocalFilesGroupDefault.get()
c.privacyAskToApproveRelays = privacyAskToApproveRelaysGroupDefault.get()
c.privacyAcceptImages = privacyAcceptImagesGroupDefault.get()
@@ -81,6 +95,7 @@ extension AppSettings {
c.uiDarkColorScheme = systemDarkThemeDefault.get()
c.uiCurrentThemeIds = currentThemeIdsDefault.get()
c.uiThemes = themeOverridesDefault.get()
c.oneHandUI = groupDefaults.bool(forKey: GROUP_DEFAULT_ONE_HAND_UI)
return c
}
}
@@ -33,6 +33,8 @@ struct AppearanceSettings: View {
}()
@State private var darkModeTheme: String = UserDefaults.standard.string(forKey: DEFAULT_SYSTEM_DARK_THEME) ?? DefaultTheme.DARK.themeName
@AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var profileImageCornerRadius = defaultProfileImageCorner
@AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = true
@AppStorage(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial
@State var themeUserDestination: (Int64, ThemeModeOverrides?)? = {
if let currentUser = ChatModel.shared.currentUser, let uiThemes = currentUser.uiThemes, uiThemes.preferredMode(!CurrentColors.colors.isLight) != nil {
@@ -62,6 +64,16 @@ struct AppearanceSettings: View {
}
}
Section("Chat list") {
Toggle("Reachable chat toolbar", isOn: $oneHandUI)
Picker("Toolbar opacity", selection: $toolbarMaterial) {
ForEach(ToolbarMaterial.allCases, id: \.rawValue) { tm in
Text(tm.text).tag(tm.rawValue)
}
}
.frame(height: 36)
}
Section {
ThemeDestinationPicker(themeUserDestination: $themeUserDestination, themeUserDest: themeUserDestination?.0, customizeThemeIsOpen: $customizeThemeIsOpen)
@@ -291,6 +303,43 @@ struct AppearanceSettings: View {
}
}
enum ToolbarMaterial: String, CaseIterable {
case bar
case ultraThin
case thin
case regular
case thick
case ultraThick
static func material(_ s: String) -> Material {
ToolbarMaterial(rawValue: s)?.material ?? Material.bar
}
static let defaultMaterial: String = ToolbarMaterial.regular.rawValue
var material: Material {
switch self {
case .bar: .bar
case .ultraThin: .ultraThin
case .thin: .thin
case .regular: .regular
case .thick: .thick
case .ultraThick: .ultraThick
}
}
var text: String {
switch self {
case .bar: "System"
case .ultraThin: "Ultra thin"
case .thin: "Thin"
case .regular: "Regular"
case .thick: "Thick"
case .ultraThick: "Ultra thick"
}
}
}
struct ChatThemePreview: View {
@EnvironmentObject var theme: AppTheme
var base: DefaultTheme
@@ -13,6 +13,8 @@ struct DeveloperView: View {
@EnvironmentObject var theme: AppTheme
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@AppStorage(GROUP_DEFAULT_CONFIRM_DB_UPGRADES, store: groupDefaults) private var confirmDatabaseUpgrades = false
@State private var hintsUnchanged = hintDefaultsUnchanged()
@Environment(\.colorScheme) var colorScheme
var body: some View {
@@ -33,8 +35,9 @@ struct DeveloperView: View {
} label: {
settingsRow("terminal", color: theme.colors.secondary) { Text("Chat console") }
}
settingsRow("internaldrive", color: theme.colors.secondary) {
Toggle("Confirm database upgrades", isOn: $confirmDatabaseUpgrades)
settingsRow("lightbulb.max", color: theme.colors.secondary) {
Button("Reset all hints", action: resetHintDefaults)
.disabled(hintsUnchanged)
}
settingsRow("chevron.left.forwardslash.chevron.right", color: theme.colors.secondary) {
Toggle("Show developer options", isOn: $developerTools)
@@ -45,9 +48,34 @@ struct DeveloperView: View {
((developerTools ? Text("Show:") : Text("Hide:")) + Text(" ") + Text("Database IDs and Transport isolation option."))
.foregroundColor(theme.colors.secondary)
}
if developerTools {
Section {
settingsRow("internaldrive", color: theme.colors.secondary) {
Toggle("Confirm database upgrades", isOn: $confirmDatabaseUpgrades)
}
} header: {
Text("Developer options")
}
}
}
}
}
private func resetHintDefaults() {
for def in hintDefaults {
if let val = appDefaults[def] as? Bool {
UserDefaults.standard.set(val, forKey: def)
}
}
hintsUnchanged = true
}
}
private func hintDefaultsUnchanged() -> Bool {
hintDefaults.allSatisfy { def in
appDefaults[def] as? Bool == UserDefaults.standard.bool(forKey: def)
}
}
struct DeveloperView_Previews: PreviewProvider {
@@ -10,18 +10,10 @@ import SwiftUI
import SimpleXChat
private enum NetworkAlert: Identifiable {
case updateOnionHosts(hosts: OnionHosts)
case updateSessionMode(mode: TransportSessionMode)
case updateSMPProxyMode(proxyMode: SMPProxyMode)
case updateSMPProxyFallback(proxyFallback: SMPProxyFallback)
case error(err: String)
var id: String {
switch self {
case let .updateOnionHosts(hosts): return "updateOnionHosts \(hosts)"
case let .updateSessionMode(mode): return "updateSessionMode \(mode)"
case let .updateSMPProxyMode(proxyMode): return "updateSMPProxyMode \(proxyMode)"
case let .updateSMPProxyFallback(proxyFallback): return "updateSMPProxyFallback \(proxyFallback)"
case let .error(err): return "error \(err)"
}
}
@@ -30,16 +22,6 @@ private enum NetworkAlert: Identifiable {
struct NetworkAndServers: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = false
@State private var cfgLoaded = false
@State private var currentNetCfg = NetCfg.defaults
@State private var netCfg = NetCfg.defaults
@State private var onionHosts: OnionHosts = .no
@State private var sessionMode: TransportSessionMode = .user
@State private var proxyMode: SMPProxyMode = .never
@State private var proxyFallback: SMPProxyFallback = .allow
@State private var alert: NetworkAlert?
var body: some View {
VStack {
@@ -50,7 +32,7 @@ struct NetworkAndServers: View {
.navigationTitle("Your SMP servers")
.modifier(ThemedBackground(grouped: true))
} label: {
Text("SMP servers")
Text("Message servers")
}
NavigationLink {
@@ -58,24 +40,12 @@ struct NetworkAndServers: View {
.navigationTitle("Your XFTP servers")
.modifier(ThemedBackground(grouped: true))
} label: {
Text("XFTP servers")
}
Picker("Use .onion hosts", selection: $onionHosts) {
ForEach(OnionHosts.values, id: \.self) { Text($0.text) }
}
.frame(height: 36)
if developerTools {
Picker("Transport isolation", selection: $sessionMode) {
ForEach(TransportSessionMode.values, id: \.self) { Text($0.text) }
}
.frame(height: 36)
Text("Media & file servers")
}
NavigationLink {
AdvancedNetworkSettings()
.navigationTitle("Network settings")
.navigationTitle("Advanced settings")
.modifier(ThemedBackground(grouped: true))
} label: {
Text("Advanced network settings")
@@ -83,35 +53,6 @@ struct NetworkAndServers: View {
} header: {
Text("Messages & files")
.foregroundColor(theme.colors.secondary)
} footer: {
Text("Using .onion hosts requires compatible VPN provider.")
.foregroundColor(theme.colors.secondary)
}
Section {
Picker("Private routing", selection: $proxyMode) {
ForEach(SMPProxyMode.values, id: \.self) { Text($0.text) }
}
.frame(height: 36)
Picker("Allow downgrade", selection: $proxyFallback) {
ForEach(SMPProxyFallback.values, id: \.self) { Text($0.text) }
}
.disabled(proxyMode == .never)
.frame(height: 36)
Toggle("Show message status", isOn: $showSentViaProxy)
} header: {
Text("Private message routing")
.foregroundColor(theme.colors.secondary)
} footer: {
VStack(alignment: .leading) {
Text("To protect your IP address, private routing uses your SMP servers to deliver messages.")
if showSentViaProxy {
Text("Show → on messages sent via private routing.")
}
}
.foregroundColor(theme.colors.secondary)
}
Section(header: Text("Calls").foregroundColor(theme.colors.secondary)) {
@@ -133,147 +74,6 @@ struct NetworkAndServers: View {
}
}
}
.onAppear {
if cfgLoaded { return }
cfgLoaded = true
currentNetCfg = getNetCfg()
resetNetCfgView()
}
.onChange(of: onionHosts) { hosts in
if hosts != OnionHosts(netCfg: currentNetCfg) {
alert = .updateOnionHosts(hosts: hosts)
}
}
.onChange(of: sessionMode) { mode in
if mode != netCfg.sessionMode {
alert = .updateSessionMode(mode: mode)
}
}
.onChange(of: proxyMode) { mode in
if mode != netCfg.smpProxyMode {
alert = .updateSMPProxyMode(proxyMode: mode)
}
}
.onChange(of: proxyFallback) { fallbackMode in
if fallbackMode != netCfg.smpProxyFallback {
alert = .updateSMPProxyFallback(proxyFallback: fallbackMode)
}
}
.alert(item: $alert) { a in
switch a {
case let .updateOnionHosts(hosts):
return Alert(
title: Text("Update .onion hosts setting?"),
message: Text(onionHostsInfo(hosts)) + Text("\n") + Text("Updating this setting will re-connect the client to all servers."),
primaryButton: .default(Text("Ok")) {
let (hostMode, requiredHostMode) = hosts.hostMode
netCfg.hostMode = hostMode
netCfg.requiredHostMode = requiredHostMode
saveNetCfg()
},
secondaryButton: .cancel() {
resetNetCfgView()
}
)
case let .updateSessionMode(mode):
return Alert(
title: Text("Update transport isolation mode?"),
message: Text(sessionModeInfo(mode)) + Text("\n") + Text("Updating this setting will re-connect the client to all servers."),
primaryButton: .default(Text("Ok")) {
netCfg.sessionMode = mode
saveNetCfg()
},
secondaryButton: .cancel() {
resetNetCfgView()
}
)
case let .updateSMPProxyMode(proxyMode):
return Alert(
title: Text("Message routing mode"),
message: Text(proxyModeInfo(proxyMode)) + Text("\n") + Text("Updating this setting will re-connect the client to all servers."),
primaryButton: .default(Text("Ok")) {
netCfg.smpProxyMode = proxyMode
saveNetCfg()
},
secondaryButton: .cancel() {
resetNetCfgView()
}
)
case let .updateSMPProxyFallback(proxyFallback):
return Alert(
title: Text("Message routing fallback"),
message: Text(proxyFallbackInfo(proxyFallback)) + Text("\n") + Text("Updating this setting will re-connect the client to all servers."),
primaryButton: .default(Text("Ok")) {
netCfg.smpProxyFallback = proxyFallback
saveNetCfg()
},
secondaryButton: .cancel() {
resetNetCfgView()
}
)
case let .error(err):
return Alert(
title: Text("Error updating settings"),
message: Text(err)
)
}
}
}
private func saveNetCfg() {
do {
let def = netCfg.hostMode == .onionHost ? NetCfg.proxyDefaults : NetCfg.defaults
netCfg.tcpConnectTimeout = def.tcpConnectTimeout
netCfg.tcpTimeout = def.tcpTimeout
try setNetworkConfig(netCfg)
currentNetCfg = netCfg
setNetCfg(netCfg)
} catch let error {
let err = responseError(error)
resetNetCfgView()
alert = .error(err: err)
logger.error("\(err)")
}
}
private func resetNetCfgView() {
netCfg = currentNetCfg
onionHosts = OnionHosts(netCfg: netCfg)
sessionMode = netCfg.sessionMode
proxyMode = netCfg.smpProxyMode
proxyFallback = netCfg.smpProxyFallback
}
private func onionHostsInfo(_ hosts: OnionHosts) -> LocalizedStringKey {
switch hosts {
case .no: return "Onion hosts will not be used."
case .prefer: return "Onion hosts will be used when available. Requires enabling VPN."
case .require: return "Onion hosts will be required for connection. Requires enabling VPN."
}
}
private func sessionModeInfo(_ mode: TransportSessionMode) -> LocalizedStringKey {
switch mode {
case .user: return "A separate TCP connection will be used **for each chat profile you have in the app**."
case .entity: return "A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail."
}
}
private func proxyModeInfo(_ mode: SMPProxyMode) -> LocalizedStringKey {
switch mode {
case .always: return "Always use private routing."
case .unknown: return "Use private routing with unknown servers."
case .unprotected: return "Use private routing with unknown servers when IP address is not protected."
case .never: return "Do NOT use private routing."
}
}
private func proxyFallbackInfo(_ proxyFallback: SMPProxyFallback) -> LocalizedStringKey {
switch proxyFallback {
case .allow: return "Send messages directly when your or destination server does not support private routing."
case .allowProtected: return "Send messages directly when IP address is protected and your or destination server does not support private routing."
case .prohibit: return "Do NOT send messages directly, even if your or destination server does not support private routing."
}
}
}
@@ -117,7 +117,7 @@ struct PrivacySettings: View {
privacyAcceptImagesGroupDefault.set($0)
}
}
settingsRow("circle.rectangle.filled.pattern.diagonalline", color: theme.colors.secondary) {
settingsRow("circle.filled.pattern.diagonalline.rectangle", color: theme.colors.secondary) {
Picker("Blur media", selection: $privacyMediaBlurRadius) {
let values = [0, 12, 24, 48] + ([0, 12, 24, 48].contains(privacyMediaBlurRadius) ? [] : [privacyMediaBlurRadius])
ForEach(values, id: \.self) { radius in
@@ -470,7 +470,7 @@ struct SimplexLockView: View {
}
}
.onChange(of: performLA) { performLAToggle in
performLAGroupDefault.set(performLAToggle)
appLocalAuthEnabledGroupDefault.set(performLAToggle)
prefLANoticeShown = true
if performLAToggleReset {
performLAToggleReset = false
@@ -130,7 +130,7 @@ struct ProtocolServersView: View {
showSaveDialog = true
}
})
.confirmationDialog("Save servers?", isPresented: $showSaveDialog) {
.confirmationDialog("Save servers?", isPresented: $showSaveDialog, titleVisibility: .visible) {
Button("Save") {
saveServers()
dismiss()
@@ -47,6 +47,8 @@ let DEFAULT_ACCENT_COLOR_GREEN = "accentColorGreen" // deprecated, only used for
let DEFAULT_ACCENT_COLOR_BLUE = "accentColorBlue" // deprecated, only used for migration
let DEFAULT_USER_INTERFACE_STYLE = "userInterfaceStyle" // deprecated, only used for migration
let DEFAULT_PROFILE_IMAGE_CORNER_RADIUS = "profileImageCornerRadius"
let DEFAULT_ONE_HAND_UI_CARD_SHOWN = "oneHandUICardShown"
let DEFAULT_TOOLBAR_MATERIAL = "toolbarMaterial"
let DEFAULT_CONNECT_VIA_LINK_TAB = "connectViaLinkTab"
let DEFAULT_LIVE_MESSAGE_ALERT_SHOWN = "liveMessageAlertShown"
let DEFAULT_SHOW_HIDDEN_PROFILES_NOTICE = "showHiddenProfilesNotice"
@@ -61,6 +63,8 @@ let DEFAULT_DEVICE_NAME_FOR_REMOTE_ACCESS = "deviceNameForRemoteAccess"
let DEFAULT_CONFIRM_REMOTE_SESSIONS = "confirmRemoteSessions"
let DEFAULT_CONNECT_REMOTE_VIA_MULTICAST = "connectRemoteViaMulticast"
let DEFAULT_CONNECT_REMOTE_VIA_MULTICAST_AUTO = "connectRemoteViaMulticastAuto"
let DEFAULT_SHOW_DELETE_CONVERSATION_NOTICE = "showDeleteConversationNotice"
let DEFAULT_SHOW_DELETE_CONTACT_NOTICE = "showDeleteContactNotice"
let DEFAULT_SHOW_SENT_VIA_RPOXY = "showSentViaProxy"
let DEFAULT_SHOW_SUBSCRIPTION_PERCENTAGE = "showSubscriptionPercentage"
@@ -69,6 +73,8 @@ let DEFAULT_SYSTEM_DARK_THEME = "systemDarkTheme"
let DEFAULT_CURRENT_THEME_IDS = "currentThemeIds"
let DEFAULT_THEME_OVERRIDES = "themeOverrides"
let DEFAULT_NETWORK_PROXY = "networkProxy"
let ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN = "androidCallOnLockScreen"
let appDefaults: [String: Any] = [
@@ -94,6 +100,8 @@ let appDefaults: [String: Any] = [
DEFAULT_DEVELOPER_TOOLS: false,
DEFAULT_ENCRYPTION_STARTED: false,
DEFAULT_PROFILE_IMAGE_CORNER_RADIUS: defaultProfileImageCorner,
DEFAULT_ONE_HAND_UI_CARD_SHOWN: false,
DEFAULT_TOOLBAR_MATERIAL: ToolbarMaterial.defaultMaterial,
DEFAULT_CONNECT_VIA_LINK_TAB: ConnectViaLinkTab.scan.rawValue,
DEFAULT_LIVE_MESSAGE_ALERT_SHOWN: false,
DEFAULT_SHOW_HIDDEN_PROFILES_NOTICE: true,
@@ -104,6 +112,8 @@ let appDefaults: [String: Any] = [
DEFAULT_CONFIRM_REMOTE_SESSIONS: false,
DEFAULT_CONNECT_REMOTE_VIA_MULTICAST: true,
DEFAULT_CONNECT_REMOTE_VIA_MULTICAST_AUTO: true,
DEFAULT_SHOW_DELETE_CONVERSATION_NOTICE: true,
DEFAULT_SHOW_DELETE_CONTACT_NOTICE: true,
DEFAULT_SHOW_SENT_VIA_RPOXY: false,
DEFAULT_SHOW_SUBSCRIPTION_PERCENTAGE: false,
ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN: AppSettingsLockScreenCalls.show.rawValue,
@@ -114,6 +124,18 @@ let appDefaults: [String: Any] = [
DEFAULT_CURRENT_THEME_IDS: "{}"
]
// only Bool defaults can be used here,
// or hintDefaultsUnchanged and resetHintDefaults need to be changed
let hintDefaults = [
DEFAULT_LA_NOTICE_SHOWN,
DEFAULT_ONE_HAND_UI_CARD_SHOWN,
DEFAULT_LIVE_MESSAGE_ALERT_SHOWN,
DEFAULT_SHOW_HIDDEN_PROFILES_NOTICE,
DEFAULT_SHOW_MUTE_PROFILE_ALERT,
DEFAULT_SHOW_DELETE_CONVERSATION_NOTICE,
DEFAULT_SHOW_DELETE_CONTACT_NOTICE
]
// not used anymore
enum ConnectViaLinkTab: String {
case scan
@@ -158,6 +180,9 @@ let onboardingStageDefault = EnumDefault<OnboardingStage>(defaults: UserDefaults
let customDisappearingMessageTimeDefault = IntDefault(defaults: UserDefaults.standard, forKey: DEFAULT_CUSTOM_DISAPPEARING_MESSAGE_TIME)
let showDeleteConversationNoticeDefault = BoolDefault(defaults: UserDefaults.standard, forKey: DEFAULT_SHOW_DELETE_CONVERSATION_NOTICE)
let showDeleteContactNoticeDefault = BoolDefault(defaults: UserDefaults.standard, forKey: DEFAULT_SHOW_DELETE_CONTACT_NOTICE)
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: [:] )
@@ -165,7 +190,7 @@ let themeOverridesDefault: CodableDefault<[ThemeOverrides]> = CodableDefault(def
func setGroupDefaults() {
privacyAcceptImagesGroupDefault.set(UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_ACCEPT_IMAGES))
performLAGroupDefault.set(UserDefaults.standard.bool(forKey: DEFAULT_PERFORM_LA))
appLocalAuthEnabledGroupDefault.set(UserDefaults.standard.bool(forKey: DEFAULT_PERFORM_LA))
privacyLinkPreviewsGroupDefault.set(UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_LINK_PREVIEWS))
profileImageCornerRadiusGroupDefault.set(UserDefaults.standard.double(forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS))
}
@@ -222,6 +247,7 @@ public class CodableDefault<T: Codable> {
}
}
let networkProxyDefault: CodableDefault<NetworkProxy> = CodableDefault(defaults: UserDefaults.standard, forKey: DEFAULT_NETWORK_PROXY, withDefault: NetworkProxy.def)
struct SettingsView: View {
@Environment(\.colorScheme) var colorScheme
@@ -39,7 +39,7 @@
</trans-unit>
<trans-unit id="!1 colored!" xml:space="preserve" approved="no">
<source>!1 colored!</source>
<target state="translated">! 1 ملون!</target>
<target state="translated">! 1 مُلوَّن!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="#secret#" xml:space="preserve" approved="no">
@@ -69,7 +69,7 @@
</trans-unit>
<trans-unit id="%@ is not verified" xml:space="preserve" approved="no">
<source>%@ is not verified</source>
<target state="translated">%@ لم يتم التحقق منها</target>
<target state="translated">%@ لم يتم التحقق منه</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ is verified" xml:space="preserve" approved="no">
@@ -107,8 +107,9 @@
<target state="translated">%d ثانية</target>
<note>message ttl</note>
</trans-unit>
<trans-unit id="%d skipped message(s)" xml:space="preserve">
<trans-unit id="%d skipped message(s)" xml:space="preserve" approved="no">
<source>%d skipped message(s)</source>
<target state="translated">%d الرسائل المتخطية</target>
<note>integrity error chat item</note>
</trans-unit>
<trans-unit id="%lld" xml:space="preserve" approved="no">
@@ -121,12 +122,14 @@
<target state="needs-translation">%lld %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld contact(s) selected" xml:space="preserve">
<trans-unit id="%lld contact(s) selected" xml:space="preserve" approved="no">
<source>%lld contact(s) selected</source>
<target state="translated">%lld تم اختيار جهات الاتصال</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld file(s) with total size of %@" xml:space="preserve">
<trans-unit id="%lld file(s) with total size of %@" xml:space="preserve" approved="no">
<source>%lld file(s) with total size of %@</source>
<target state="translated">%lld الملفات ذات الحجم الإجمالي %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld members" xml:space="preserve" approved="no">
@@ -134,8 +137,9 @@
<target state="translated">%lld أعضاء</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld second(s)" xml:space="preserve">
<trans-unit id="%lld second(s)" xml:space="preserve" approved="no">
<source>%lld second(s)</source>
<target state="translated">%lld ثوانى</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lldd" xml:space="preserve" approved="no">
@@ -185,7 +189,7 @@
</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>
<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">
@@ -195,12 +199,12 @@
</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>
<target state="translated">** المزيد من الخصوصية **: تحقق من الرسائل الجديدة كل 20 دقيقة. تتم مشاركة رمز الجهاز مع خادم SimpleX Chat ، ولكن ليس عدد جهات الاتصال أو الرسائل لديك.</target>
<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>
<target state="translated">** الأكثر خصوصية **: لا تستخدم خادم إشعارات SimpleX Chat ، وتحقق من الرسائل بشكل دوري في الخلفية (يعتمد على عدد مرات استخدامك للتطبيق).</target>
<target state="translated">** الأكثر خصوصية **: لا تستخدم خادم إشعارات SimpleX Chat ، وتحقق من الرسائل بشكل دوري في الخلفية (يعتمد على عدد مرات استخدامكم للتطبيق).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="**Paste received link** or open it in the browser and tap **Open in mobile app**." xml:space="preserve" approved="no">
@@ -210,7 +214,7 @@
</trans-unit>
<trans-unit id="**Please note**: you will NOT be able to recover or change passphrase if you lose it." xml:space="preserve" approved="no">
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
<target state="translated">** يرجى ملاحظة **: لن تتمكن من استعادة أو تغيير عبارة المرور إذا فقدتها.</target>
<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">
@@ -305,7 +309,7 @@
</trans-unit>
<trans-unit id="A separate TCP connection will be used **for each chat profile you have in the app**." xml:space="preserve" approved="no">
<source>A separate TCP connection will be used **for each chat profile you have in the app**.</source>
<target state="translated">سيتم استخدام اتصال TCP منفصل ** لكل ملف تعريف دردشة لديك في التطبيق **.</target>
<target state="translated">سيتم استخدام اتصال TCP منفصل ** لكل ملف تعريف دردشة لديكم في التطبيق **.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="A separate TCP connection will be used **for each contact and group member**.&#10;**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail." xml:space="preserve" approved="no">
@@ -355,24 +359,29 @@
<source>Accept requests</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add preset servers" xml:space="preserve">
<trans-unit id="Add preset servers" xml:space="preserve" approved="no">
<source>Add preset servers</source>
<target state="translated">إضافة خوادم محددة مسبقا</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add profile" xml:space="preserve">
<trans-unit id="Add profile" xml:space="preserve" approved="no">
<source>Add profile</source>
<target state="translated">إضافة الملف الشخصي</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add servers by scanning QR codes." xml:space="preserve">
<trans-unit id="Add servers by scanning QR codes." xml:space="preserve" approved="no">
<source>Add servers by scanning QR codes.</source>
<target state="translated">إضافة خوادم عن طريق مسح رموز QR.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<trans-unit id="Add server" xml:space="preserve" approved="no">
<source>Add server</source>
<target state="translated">أضف الخادم</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
<trans-unit id="Add to another device" xml:space="preserve" approved="no">
<source>Add to another device</source>
<target state="translated">أضف إلى جهاز آخر</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Admins can create the links to join groups." xml:space="preserve">
@@ -3667,7 +3676,7 @@ SimpleX servers cannot see your profile.</source>
</trans-unit>
<trans-unit id="## In reply to" xml:space="preserve" approved="no">
<source>## In reply to</source>
<target state="translated">## ردًا على</target>
<target state="translated">## ردًّا على</target>
<note>copied message info</note>
</trans-unit>
<trans-unit id="%@ and %@ connected" xml:space="preserve" approved="no">
@@ -3675,6 +3684,208 @@ SimpleX servers cannot see your profile.</source>
<target state="translated">%@ و %@ متصل</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ downloaded" xml:space="preserve" approved="no">
<source>%@ downloaded</source>
<target state="translated">%@ تم التنزيل</target>
</trans-unit>
<trans-unit id="%@ and %@" xml:space="preserve" approved="no">
<source>%@ and %@</source>
<target state="translated">%@ و %@</target>
</trans-unit>
<trans-unit id="%@ connected" xml:space="preserve" approved="no">
<source>%@ connected</source>
<target state="translated">%@ متصل</target>
</trans-unit>
<trans-unit id="%lld minutes" xml:space="preserve" approved="no">
<source>%lld minutes</source>
<target state="translated">%lld دقائق</target>
</trans-unit>
<trans-unit id="%@, %@ and %lld members" xml:space="preserve" approved="no">
<source>%@, %@ and %lld members</source>
<target state="translated">%@, %@ و %lld أعضاء</target>
</trans-unit>
<trans-unit id="%d weeks" xml:space="preserve" approved="no">
<source>%d weeks</source>
<target state="translated">%d أسابيع</target>
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve" approved="no">
<source>%@ uploaded</source>
<target state="translated">%@ تم الرفع</target>
</trans-unit>
<trans-unit id="%@, %@ and %lld other members connected" xml:space="preserve" approved="no">
<source>%@, %@ and %lld other members connected</source>
<target state="translated">%@, %@ و %lld أعضاء آخرين متصلين</target>
</trans-unit>
<trans-unit id="%lld seconds" xml:space="preserve" approved="no">
<source>%lld seconds</source>
<target state="translated">%lld ثواني</target>
</trans-unit>
<trans-unit id="%u messages failed to decrypt." xml:space="preserve" approved="no">
<source>%u messages failed to decrypt.</source>
<target state="translated">%u فشلت عملية فك تشفير الرسائل.</target>
</trans-unit>
<trans-unit id="%lld messages marked deleted" xml:space="preserve" approved="no">
<source>%lld messages marked deleted</source>
<target state="translated">%lld الرسائل معلمه بالحذف</target>
</trans-unit>
<trans-unit id="%lld messages moderated by %@" xml:space="preserve" approved="no">
<source>%lld messages moderated by %@</source>
<target state="translated">%lld رسائل تمت إدارتها بواسطة %@</target>
</trans-unit>
<trans-unit id="%lld new interface languages" xml:space="preserve" approved="no">
<source>%lld new interface languages</source>
<target state="translated">%lld لغات واجهة جديدة</target>
</trans-unit>
<trans-unit id="%lld group events" xml:space="preserve" approved="no">
<source>%lld group events</source>
<target state="translated">%lld أحداث المجموعة</target>
</trans-unit>
<trans-unit id="%lld messages blocked by admin" xml:space="preserve" approved="no">
<source>%lld messages blocked by admin</source>
<target state="translated">%lld رسائل محظورة بواسطه المسؤول</target>
</trans-unit>
<trans-unit id="%lld messages blocked" xml:space="preserve" approved="no">
<source>%lld messages blocked</source>
<target state="translated">%lld رسائل تم حظرها</target>
</trans-unit>
<trans-unit id="%u messages skipped." xml:space="preserve" approved="no">
<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>
<target state="translated">**إضافة جهة اتصال**: لإنشاء رابط دعوة جديد، أو الاتصال عبر الرابط الذي تلقيتوهم.</target>
</trans-unit>
<trans-unit id="**Create group**: to create a new group." xml:space="preserve" approved="no">
<source>**Create group**: to create a new group.</source>
<target state="translated">**إنشاء مجموعة**: لإنشاء مجموعة جديدة.</target>
</trans-unit>
<trans-unit id="(this device v%@)" xml:space="preserve" approved="no">
<source>(this device v%@)</source>
<target state="translated">(هذا الجهاز v%@)</target>
</trans-unit>
<trans-unit id="(new)" xml:space="preserve" approved="no">
<source>(new)</source>
<target state="translated">(جديد)</target>
</trans-unit>
<trans-unit id="**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." xml:space="preserve" approved="no">
<source>**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection.</source>
<target state="translated">**يرجى الملاحظة**: سيؤدي استخدام نفس قاعدة البيانات على جهازين إلى كسر فك تشفير الرسائل من اتصالاتكم كحماية أمنية.</target>
</trans-unit>
<trans-unit id="A new random profile will be shared." xml:space="preserve" approved="no">
<source>A new random profile will be shared.</source>
<target state="translated">سيتم مشاركة ملف تعريفي عشوائي جديد.</target>
</trans-unit>
<trans-unit id="30 seconds" xml:space="preserve" approved="no">
<source>30 seconds</source>
<target state="translated">30 ثانيه</target>
</trans-unit>
<trans-unit id="- more stable message delivery.&#10;- a bit better groups.&#10;- and more!" xml:space="preserve" approved="no">
<source>- more stable message delivery.
- a bit better groups.
- and more!</source>
<target state="translated">- تسليم رسائل أكثر استقرارًا.
- مجموعات أفضل قليلاً.
- والمزيد!</target>
</trans-unit>
<trans-unit id="0 sec" xml:space="preserve" approved="no">
<source>0 sec</source>
<target state="translated">0 ثانيه</target>
</trans-unit>
<trans-unit id="1 minute" xml:space="preserve" approved="no">
<source>1 minute</source>
<target state="translated">1 دقيقة</target>
</trans-unit>
<trans-unit id="5 minutes" xml:space="preserve" approved="no">
<source>5 minutes</source>
<target state="translated">5 دقائق</target>
</trans-unit>
<trans-unit id="&lt;p&gt;Hi!&lt;/p&gt;&#10;&lt;p&gt;&lt;a href=&quot;%@&quot;&gt;Connect to me via SimpleX Chat&lt;/a&gt;&lt;/p&gt;" xml:space="preserve" approved="no">
<source>&lt;p&gt;Hi!&lt;/p&gt;
&lt;p&gt;&lt;a href="%@"&gt;Connect to me via SimpleX Chat&lt;/a&gt;&lt;/p&gt;</source>
<target state="translated">&lt;p&gt;مرحبا!&lt;/p&gt;
&lt;p&gt;&lt;a href="%@"&gt;أتصل بى من خلال SimpleX Chat&lt;/a&gt;&lt;/p&gt;</target>
</trans-unit>
<trans-unit id="0s" xml:space="preserve" approved="no">
<source>0s</source>
<target state="translated">0 ث</target>
</trans-unit>
<trans-unit id="A few more things" xml:space="preserve" approved="no">
<source>A few more things</source>
<target state="translated">بعض الأشياء الأخرى</target>
</trans-unit>
<trans-unit id="- connect to [directory service](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)!&#10;- delivery receipts (up to 20 members).&#10;- faster and more stable." xml:space="preserve" approved="no">
<source>- connect to [directory service](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)!
- delivery receipts (up to 20 members).
- faster and more stable.</source>
<target state="translated">- أتصل بـ [directory service](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)!
- delivery receipts (up to 20 members).
- أسرع و أكثر اسْتِقْرارًا.</target>
</trans-unit>
<trans-unit id="**Warning**: the archive will be removed." xml:space="preserve" approved="no">
<source>**Warning**: the archive will be removed.</source>
<target state="translated">**تحذير**: سيتم إزالة الأرشيف.</target>
</trans-unit>
<trans-unit id="- optionally notify deleted contacts.&#10;- profile names with spaces.&#10;- and more!" xml:space="preserve" approved="no">
<source>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</source>
<target state="translated">- إخطار جهات الاتصال المحذوفة بشكل اختياري.
- أسماء الملفات الشخصية مع المسافات.
- والمزيد!</target>
</trans-unit>
<trans-unit id="- voice messages up to 5 minutes.&#10;- custom time to disappear.&#10;- editing history." xml:space="preserve" approved="no">
<source>- voice messages up to 5 minutes.
- custom time to disappear.
- editing history.</source>
<target state="translated">- رسائل صوتية تصل مدتها إلى 5 دقائق.
- وقت مخصص للاختفاء.
- تعديل السجل.</target>
</trans-unit>
<trans-unit id="Add welcome message" xml:space="preserve" approved="no">
<source>Add welcome message</source>
<target state="translated">إضافة رسالة ترحيب</target>
</trans-unit>
<trans-unit id="Abort changing address?" xml:space="preserve" approved="no">
<source>Abort changing address?</source>
<target state="translated">هل تريد إلغاء تغيير العنوان؟</target>
</trans-unit>
<trans-unit id="Add contact" xml:space="preserve" approved="no">
<source>Add contact</source>
<target state="translated">إضافة جهة اتصال</target>
</trans-unit>
<trans-unit id="Abort" xml:space="preserve" approved="no">
<source>Abort</source>
<target state="translated">إحباط</target>
</trans-unit>
<trans-unit id="About SimpleX address" xml:space="preserve" approved="no">
<source>About SimpleX address</source>
<target state="translated">حول عنوان SimpleX</target>
</trans-unit>
<trans-unit id="Accept connection request?" xml:space="preserve" approved="no">
<source>Accept connection request?</source>
<target state="translated">قبول طلب الاتصال؟</target>
</trans-unit>
<trans-unit id="Acknowledged" xml:space="preserve" approved="no">
<source>Acknowledged</source>
<target state="translated">معترف به</target>
</trans-unit>
<trans-unit id="Acknowledgement errors" xml:space="preserve" approved="no">
<source>Acknowledgement errors</source>
<target state="translated">أخطاء الإقرار</target>
</trans-unit>
<trans-unit id="Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." xml:space="preserve" approved="no">
<source>Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts.</source>
<target state="translated">أضف عنوانًا إلى ملفكم الشخصي، حتى تتمكن جهات الاتصال الخاصة بكم من مشاركته مع أشخاص اخرين. سيتم إرسال تحديث الملف الشخصي إلى جهات الاتصال الخاصة بكم.</target>
</trans-unit>
<trans-unit id="Abort changing address" xml:space="preserve" approved="no">
<source>Abort changing address</source>
<target state="translated">إحباط تغيير العنوان</target>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve" approved="no">
<source>Active connections</source>
<target state="translated">اتصالات نشطة</target>
</trans-unit>
</body>
</file>
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="ar" datatype="plaintext">
@@ -560,7 +560,8 @@
<source>Accept</source>
<target>Приеми</target>
<note>accept contact request via notification
accept incoming call via notification</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept connection request?" xml:space="preserve">
<source>Accept connection request?</source>
@@ -575,7 +576,8 @@
<trans-unit id="Accept incognito" xml:space="preserve">
<source>Accept incognito</source>
<target>Приеми инкогнито</target>
<note>accept contact request via notification</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Acknowledged" xml:space="preserve">
<source>Acknowledged</source>
@@ -738,6 +740,10 @@
<target>Позволи обаждания само ако вашият контакт ги разрешава.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow calls?" xml:space="preserve">
<source>Allow calls?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow disappearing messages only if your contact allows it to you." xml:space="preserve">
<source>Allow disappearing messages only if your contact allows it to you.</source>
<target>Позволи изчезващи съобщения само ако вашият контакт ги разрешава.</target>
@@ -924,6 +930,14 @@
<target>Архивиране и качване</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archive contacts to chat later." xml:space="preserve">
<source>Archive contacts to chat later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archived contacts" xml:space="preserve">
<source>Archived contacts</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archiving database" xml:space="preserve">
<source>Archiving database</source>
<target>Архивиране на база данни</target>
@@ -1023,6 +1037,10 @@
<target>По-добри съобщения</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better networking" xml:space="preserve">
<source>Better networking</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Black" xml:space="preserve">
<source>Black</source>
<note>No comment provided by engineer.</note>
@@ -1062,6 +1080,10 @@
<target>Блокиран от админ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Blur for better privacy." xml:space="preserve">
<source>Blur for better privacy.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Blur media" xml:space="preserve">
<source>Blur media</source>
<note>No comment provided by engineer.</note>
@@ -1111,11 +1133,23 @@
<target>Обаждания</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Calls prohibited!" xml:space="preserve">
<source>Calls prohibited!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Camera not available" xml:space="preserve">
<source>Camera not available</source>
<target>Камерата е неодстъпна</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't call contact" xml:space="preserve">
<source>Can't call contact</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't call member" xml:space="preserve">
<source>Can't call member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't invite contact!" xml:space="preserve">
<source>Can't invite contact!</source>
<target>Не може да покани контакта!</target>
@@ -1126,6 +1160,10 @@
<target>Не може да поканят контактите!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't message member" xml:space="preserve">
<source>Can't message member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cancel" xml:space="preserve">
<source>Cancel</source>
<target>Отказ</target>
@@ -1234,6 +1272,10 @@
<target>Базата данни на чата е изтрита</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database exported" xml:space="preserve">
<source>Chat database exported</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database imported" xml:space="preserve">
<source>Chat database imported</source>
<target>Базата данни на чат е импортирана</target>
@@ -1254,6 +1296,10 @@
<target>Чатът е спрян. Ако вече сте използвали тази база данни на друго устройство, трябва да я прехвърлите обратно, преди да стартирате чата отново.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat list" xml:space="preserve">
<source>Chat list</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat migrated!" xml:space="preserve">
<source>Chat migrated!</source>
<target>Чатът е мигриран!</target>
@@ -1313,7 +1359,7 @@
<trans-unit id="Clear" xml:space="preserve">
<source>Clear</source>
<target>Изчисти</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Clear conversation" xml:space="preserve">
<source>Clear conversation</source>
@@ -1335,6 +1381,10 @@
<target>Изчисти проверката</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Color chats with the new themes." xml:space="preserve">
<source>Color chats with the new themes.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Color mode" xml:space="preserve">
<source>Color mode</source>
<note>No comment provided by engineer.</note>
@@ -1372,6 +1422,10 @@
<target>Потвърди kодa за достъп</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm contact deletion?" xml:space="preserve">
<source>Confirm contact deletion?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm database upgrades" xml:space="preserve">
<source>Confirm database upgrades</source>
<target>Потвърди актуализаациите на базата данни</target>
@@ -1426,6 +1480,10 @@
<target>Свързване с настолно устройство</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect to your friends faster." xml:space="preserve">
<source>Connect to your friends faster.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect to yourself?" xml:space="preserve">
<source>Connect to yourself?</source>
<target>Свърване със себе си?</target>
@@ -1497,6 +1555,10 @@ This is your own one-time link!</source>
<target>Свързване със сървър…(грешка: %@)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting to contact, please wait or check later!" xml:space="preserve">
<source>Connecting to contact, please wait or check later!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting to desktop" xml:space="preserve">
<source>Connecting to desktop</source>
<target>Свързване с настолно устройство</target>
@@ -1507,6 +1569,10 @@ This is your own one-time link!</source>
<target>Връзка</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connection and servers status." xml:space="preserve">
<source>Connection and servers status.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connection error" xml:space="preserve">
<source>Connection error</source>
<target>Грешка при свързване</target>
@@ -1554,6 +1620,10 @@ This is your own one-time link!</source>
<target>Контактът вече съществува</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact deleted!" xml:space="preserve">
<source>Contact deleted!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact hidden:" xml:space="preserve">
<source>Contact hidden:</source>
<target>Контактът е скрит:</target>
@@ -1564,9 +1634,8 @@ This is your own one-time link!</source>
<target>Контактът е свързан</target>
<note>notification</note>
</trans-unit>
<trans-unit id="Contact is not connected yet!" xml:space="preserve">
<source>Contact is not connected yet!</source>
<target>Контактът все още не е свързан!</target>
<trans-unit id="Contact is deleted." xml:space="preserve">
<source>Contact is deleted.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact name" xml:space="preserve">
@@ -1579,6 +1648,10 @@ This is your own one-time link!</source>
<target>Настройки за контакт</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact will be deleted - this cannot be undone!" xml:space="preserve">
<source>Contact will be deleted - this cannot be undone!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contacts" xml:space="preserve">
<source>Contacts</source>
<target>Контакти</target>
@@ -1594,6 +1667,10 @@ This is your own one-time link!</source>
<target>Продължи</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Conversation deleted!" xml:space="preserve">
<source>Conversation deleted!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy" xml:space="preserve">
<source>Copy</source>
<target>Копирай</target>
@@ -1859,7 +1936,8 @@ This is your own one-time link!</source>
<trans-unit id="Delete" xml:space="preserve">
<source>Delete</source>
<target>Изтрий</target>
<note>chat item action</note>
<note>chat item action
swipe action</note>
</trans-unit>
<trans-unit id="Delete %lld messages of members?" xml:space="preserve">
<source>Delete %lld messages of members?</source>
@@ -1870,11 +1948,6 @@ This is your own one-time link!</source>
<target>Изтриване на %lld съобщения?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete Contact" xml:space="preserve">
<source>Delete Contact</source>
<target>Изтрий контакт</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete address" xml:space="preserve">
<source>Delete address</source>
<target>Изтрий адрес</target>
@@ -1930,11 +2003,8 @@ This is your own one-time link!</source>
<target>Изтрий контакт</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete contact?&#10;This cannot be undone!" xml:space="preserve">
<source>Delete contact?
This cannot be undone!</source>
<target>Изтрий контакт?
Това не може да бъде отменено!</target>
<trans-unit id="Delete contact?" xml:space="preserve">
<source>Delete contact?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete database" xml:space="preserve">
@@ -2027,11 +2097,6 @@ This cannot be undone!</source>
<target>Изтрий старата база данни?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete pending connection" xml:space="preserve">
<source>Delete pending connection</source>
<target>Изтрий предстоящата връзка</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete pending connection?" xml:space="preserve">
<source>Delete pending connection?</source>
<target>Изтрий предстоящата връзка?</target>
@@ -2047,11 +2112,19 @@ This cannot be undone!</source>
<target>Изтрий опашка</target>
<note>server test step</note>
</trans-unit>
<trans-unit id="Delete up to 20 messages at once." xml:space="preserve">
<source>Delete up to 20 messages at once.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete user profile?" xml:space="preserve">
<source>Delete user profile?</source>
<target>Изтрий потребителския профил?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete without notification" xml:space="preserve">
<source>Delete without notification</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Deleted" xml:space="preserve">
<source>Deleted</source>
<note>No comment provided by engineer.</note>
@@ -2130,6 +2203,10 @@ This cannot be undone!</source>
<target>Разработване</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Developer options" xml:space="preserve">
<source>Developer options</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Developer tools" xml:space="preserve">
<source>Developer tools</source>
<target>Инструменти за разработчици</target>
@@ -2632,11 +2709,6 @@ This cannot be undone!</source>
<target>Грешка при изтриване на връзката</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting contact" xml:space="preserve">
<source>Error deleting contact</source>
<target>Грешка при изтриване на контакт</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting database" xml:space="preserve">
<source>Error deleting database</source>
<target>Грешка при изтриване на базата данни</target>
@@ -2925,7 +2997,7 @@ This cannot be undone!</source>
<trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source>
<target>Любим</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="File error" xml:space="preserve">
<source>File error</source>
@@ -3662,6 +3734,10 @@ Error: %2$@</source>
3. Връзката е била компрометирана.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It protects your IP address and connections." xml:space="preserve">
<source>It protects your IP address and connections.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It seems like you are already connected via this link. If it is not the case, there was an error (%@)." xml:space="preserve">
<source>It seems like you are already connected via this link. If it is not the case, there was an error (%@).</source>
<target>Изглежда, че вече сте свързани чрез този линк. Ако не е така, има грешка (%@).</target>
@@ -3680,7 +3756,7 @@ Error: %2$@</source>
<trans-unit id="Join" xml:space="preserve">
<source>Join</source>
<target>Присъединяване</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Join group" xml:space="preserve">
<source>Join group</source>
@@ -3724,6 +3800,10 @@ This is your link for group %@!</source>
<target>Запази</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep conversation" xml:space="preserve">
<source>Keep conversation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
<source>Keep the app open to use it from desktop</source>
<target>Дръжте приложението отворено, за да го използвате от настолното устройство</target>
@@ -3767,7 +3847,7 @@ This is your link for group %@!</source>
<trans-unit id="Leave" xml:space="preserve">
<source>Leave</source>
<target>Напусни</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Leave group" xml:space="preserve">
<source>Leave group</source>
@@ -3899,6 +3979,10 @@ This is your link for group %@!</source>
<target>Макс. 30 секунди, получено незабавно.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Media &amp; file servers" xml:space="preserve">
<source>Media &amp; file servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Medium" xml:space="preserve">
<source>Medium</source>
<note>blur media</note>
@@ -3981,12 +4065,8 @@ This is your link for group %@!</source>
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing mode" xml:space="preserve">
<source>Message routing mode</source>
<trans-unit id="Message servers" xml:space="preserve">
<source>Message servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message source remains private." xml:space="preserve">
@@ -4143,7 +4223,7 @@ This is your link for group %@!</source>
<trans-unit id="Mute" xml:space="preserve">
<source>Mute</source>
<target>Без звук</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Muted when inactive!" xml:space="preserve">
<source>Muted when inactive!</source>
@@ -4153,7 +4233,7 @@ This is your link for group %@!</source>
<trans-unit id="Name" xml:space="preserve">
<source>Name</source>
<target>Име</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Network &amp; servers" xml:space="preserve">
<source>Network &amp; servers</source>
@@ -4194,6 +4274,10 @@ This is your link for group %@!</source>
<target>Нов чат</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat experience 🎉" xml:space="preserve">
<source>New chat experience 🎉</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New contact request" xml:space="preserve">
<source>New contact request</source>
<target>Нова заявка за контакт</target>
@@ -4224,6 +4308,10 @@ This is your link for group %@!</source>
<target>Ново в %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New media options" xml:space="preserve">
<source>New media options</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member role" xml:space="preserve">
<source>New member role</source>
<target>Нова членска роля</target>
@@ -4365,14 +4453,18 @@ This is your link for group %@!</source>
<target>Линк за еднократна покана</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be required for connection. Requires enabling VPN." xml:space="preserve">
<source>Onion hosts will be required for connection. Requires enabling VPN.</source>
<target>За свързване ще са необходими Onion хостове. Изисква се активиране на VPN.</target>
<trans-unit id="Onion hosts will be **required** for connection.&#10;Requires compatible VPN." xml:space="preserve">
<source>Onion hosts will be **required** for connection.
Requires compatible VPN.</source>
<target>За свързване ще са **необходими** Onion хостове.
Изисква се активиране на VPN.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be used when available. Requires enabling VPN." xml:space="preserve">
<source>Onion hosts will be used when available. Requires enabling VPN.</source>
<target>Ще се използват Onion хостове, когато са налични. Изисква се активиране на VPN.</target>
<trans-unit id="Onion hosts will be used when available.&#10;Requires compatible VPN." xml:space="preserve">
<source>Onion hosts will be used when available.
Requires compatible VPN.</source>
<target>Ще се използват Onion хостове, когато са налични.
Изисква се активиране на VPN.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will not be used." xml:space="preserve">
@@ -4385,6 +4477,10 @@ This is your link for group %@!</source>
<target>Само потребителските устройства съхраняват потребителски профили, контакти, групи и съобщения, изпратени с **двуслойно криптиране от край до край**.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only delete conversation" xml:space="preserve">
<source>Only delete conversation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
<source>Only group owners can change group preferences.</source>
<target>Само собствениците на групата могат да променят груповите настройки.</target>
@@ -4617,6 +4713,14 @@ This is your link for group %@!</source>
<target>Обаждания "картина в картина"</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Play from the chat list." xml:space="preserve">
<source>Play from the chat list.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please ask your contact to enable calls." xml:space="preserve">
<source>Please ask your contact to enable calls.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please ask your contact to enable sending voice messages." xml:space="preserve">
<source>Please ask your contact to enable sending voice messages.</source>
<target>Моля, попитайте вашия контакт, за да активирате изпращане на гласови съобщения.</target>
@@ -4905,6 +5009,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Оценете приложението</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reachable chat toolbar" xml:space="preserve">
<source>Reachable chat toolbar</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="React…" xml:space="preserve">
<source>React…</source>
<target>Реагирай…</target>
@@ -4913,7 +5021,7 @@ Enable in *Network &amp; servers* settings.</source>
<trans-unit id="Read" xml:space="preserve">
<source>Read</source>
<target>Прочетено</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Read more" xml:space="preserve">
<source>Read more</source>
@@ -5064,7 +5172,8 @@ Enable in *Network &amp; servers* settings.</source>
<trans-unit id="Reject" xml:space="preserve">
<source>Reject</source>
<target>Отхвърляне</target>
<note>reject incoming call via notification</note>
<note>reject incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Reject (sender NOT notified)" xml:space="preserve">
<source>Reject (sender NOT notified)</source>
@@ -5165,6 +5274,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Нулиране</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all hints" xml:space="preserve">
<source>Reset all hints</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all statistics" xml:space="preserve">
<source>Reset all statistics</source>
<note>No comment provided by engineer.</note>
@@ -5231,11 +5344,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>Покажи</target>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Revert" xml:space="preserve">
<source>Revert</source>
<target>Отмени промените</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>Отзови</target>
@@ -5265,11 +5373,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>SMP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SMP servers" xml:space="preserve">
<source>SMP servers</source>
<target>SMP сървъри</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safely receive files" xml:space="preserve">
<source>Safely receive files</source>
<note>No comment provided by engineer.</note>
@@ -5299,6 +5402,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Запази и уведоми членовете на групата</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save and reconnect" xml:space="preserve">
<source>Save and reconnect</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save and update group profile" xml:space="preserve">
<source>Save and update group profile</source>
<target>Запази и актуализирай профила на групата</target>
@@ -5498,11 +5605,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>Изпращайте потвърждениe за доставка на</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send direct message" xml:space="preserve">
<source>Send direct message</source>
<target>Изпрати лично съобщение</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send direct message to connect" xml:space="preserve">
<source>Send direct message to connect</source>
<target>Изпрати лично съобщение за свързване</target>
@@ -5527,6 +5629,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Изпрати съобщение на живо</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send message to enable calls." xml:space="preserve">
<source>Send message to enable calls.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when IP address is protected and your or destination server does not support private routing." xml:space="preserve">
<source>Send messages directly when IP address is protected and your or destination server does not support private routing.</source>
<note>No comment provided by engineer.</note>
@@ -5796,6 +5902,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Сподели адреса с контактите?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share from other apps." xml:space="preserve">
<source>Share from other apps.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share link" xml:space="preserve">
<source>Share link</source>
<target>Сподели линк</target>
@@ -5959,11 +6069,19 @@ Enable in *Network &amp; servers* settings.</source>
<source>Soft</source>
<note>blur media</note>
</trans-unit>
<trans-unit id="Some file(s) were not exported:" xml:space="preserve">
<source>Some file(s) were not exported:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some non-fatal errors occurred during import - you may see Chat console for more details." xml:space="preserve">
<source>Some non-fatal errors occurred during import - you may see Chat console for more details.</source>
<target>Някои не-фатални грешки са възникнали по време на импортиране - може да видите конзолата за повече подробности.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some non-fatal errors occurred during import:" xml:space="preserve">
<source>Some non-fatal errors occurred during import:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Somebody" xml:space="preserve">
<source>Somebody</source>
<target>Някой</target>
@@ -6093,6 +6211,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Системна идентификация</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="TCP connection" xml:space="preserve">
<source>TCP connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="TCP connection timeout" xml:space="preserve">
<source>TCP connection timeout</source>
<target>Времето на изчакване за установяване на TCP връзка</target>
@@ -6153,11 +6275,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>Докосни за сканиране</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Tap to start a new chat" xml:space="preserve">
<source>Tap to start a new chat</source>
<target>Докосни за започване на нов чат</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Temporary file error" xml:space="preserve">
<source>Temporary file error</source>
<note>No comment provided by engineer.</note>
@@ -6449,11 +6566,19 @@ You will be prompted to complete authentication before this feature is enabled.<
<target>За да проверите криптирането от край до край с вашия контакт, сравнете (или сканирайте) кода на вашите устройства.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toggle chat list:" xml:space="preserve">
<source>Toggle chat list:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toggle incognito when connecting." xml:space="preserve">
<source>Toggle incognito when connecting.</source>
<target>Избор на инкогнито при свързване.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toolbar opacity" xml:space="preserve">
<source>Toolbar opacity</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Total" xml:space="preserve">
<source>Total</source>
<note>No comment provided by engineer.</note>
@@ -6530,7 +6655,7 @@ You will be prompted to complete authentication before this feature is enabled.<
<trans-unit id="Unfav." xml:space="preserve">
<source>Unfav.</source>
<target>Премахни от любимите</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Unhide" xml:space="preserve">
<source>Unhide</source>
@@ -6606,12 +6731,12 @@ To connect, please ask your contact to create another connection link and check
<trans-unit id="Unmute" xml:space="preserve">
<source>Unmute</source>
<target>Уведомявай</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Unread" xml:space="preserve">
<source>Unread</source>
<target>Непрочетено</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Up to 100 last messages are sent to new members." xml:space="preserve">
<source>Up to 100 last messages are sent to new members.</source>
@@ -6623,11 +6748,6 @@ To connect, please ask your contact to create another connection link and check
<target>Актуализация</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update .onion hosts setting?" xml:space="preserve">
<source>Update .onion hosts setting?</source>
<target>Актуализиране на настройката за .onion хостове?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update database passphrase" xml:space="preserve">
<source>Update database passphrase</source>
<target>Актуализирай паролата на базата данни</target>
@@ -6638,9 +6758,8 @@ To connect, please ask your contact to create another connection link and check
<target>Актуализиране на мрежовите настройки?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update transport isolation mode?" xml:space="preserve">
<source>Update transport isolation mode?</source>
<target>Актуализиране на режима на изолация на транспорта?</target>
<trans-unit id="Update settings?" xml:space="preserve">
<source>Update settings?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Updating settings will re-connect the client to all servers." xml:space="preserve">
@@ -6648,11 +6767,6 @@ To connect, please ask your contact to create another connection link and check
<target>Актуализирането на настройките ще свърже отново клиента към всички сървъри.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Updating this setting will re-connect the client to all servers." xml:space="preserve">
<source>Updating this setting will re-connect the client to all servers.</source>
<target>Актуализирането на тази настройка ще свърже повторно клиента към всички сървъри.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Upgrade and open chat" xml:space="preserve">
<source>Upgrade and open chat</source>
<target>Актуализирай и отвори чата</target>
@@ -6748,6 +6862,10 @@ To connect, please ask your contact to create another connection link and check
<target>Използвайте приложението по време на разговора.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use the app with one hand." xml:space="preserve">
<source>Use the app with one hand.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>Потребителски профил</target>
@@ -6757,11 +6875,6 @@ To connect, please ask your contact to create another connection link and check
<source>User selection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using .onion hosts requires compatible VPN provider." xml:space="preserve">
<source>Using .onion hosts requires compatible VPN provider.</source>
<target>Използването на .onion хостове изисква съвместим VPN доставчик.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using SimpleX Chat servers." xml:space="preserve">
<source>Using SimpleX Chat servers.</source>
<target>Използват се сървърите на SimpleX Chat.</target>
@@ -7015,11 +7128,6 @@ To connect, please ask your contact to create another connection link and check
<source>XFTP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="XFTP servers" xml:space="preserve">
<source>XFTP servers</source>
<target>XFTP сървъри</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>Вие</target>
@@ -7106,6 +7214,10 @@ Repeat join request?</source>
<target>Можете да приемате обаждания от заключен екран, без идентификация на устройство и приложението.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can change it in Appearance settings." xml:space="preserve">
<source>You can change it in Appearance settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can create it later" xml:space="preserve">
<source>You can create it later</source>
<target>Можете да го създадете по-късно</target>
@@ -7141,6 +7253,10 @@ Repeat join request?</source>
<target>Вече можете да изпращате съобщения до %@</target>
<note>notification body</note>
</trans-unit>
<trans-unit id="You can send messages to %@ from Archived contacts." xml:space="preserve">
<source>You can send messages to %@ from Archived contacts.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can set lock screen notification preview via settings." xml:space="preserve">
<source>You can set lock screen notification preview via settings.</source>
<target>Можете да зададете визуализация на известията на заключен екран през настройките.</target>
@@ -7166,6 +7282,10 @@ Repeat join request?</source>
<target>Можете да започнете чат през Настройки на приложението / База данни или като рестартирате приложението</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can still view conversation with %@ in the list of chats." xml:space="preserve">
<source>You can still view conversation with %@ in the list of chats.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can turn on SimpleX Lock via Settings." xml:space="preserve">
<source>You can turn on SimpleX Lock via Settings.</source>
<target>Можете да включите SimpleX заключване през Настройки.</target>
@@ -7208,11 +7328,6 @@ Repeat connection request?</source>
Изпрати отново заявката за свързване?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You have no chats" xml:space="preserve">
<source>You have no chats</source>
<target>Нямате чатове</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You have to enter passphrase every time the app starts - it is not stored on the device." xml:space="preserve">
<source>You have to enter passphrase every time the app starts - it is not stored on the device.</source>
<target>Трябва да въвеждате парола при всяко стартиране на приложението - тя не се съхранява на устройството.</target>
@@ -7233,11 +7348,23 @@ Repeat connection request?</source>
<target>Вие се присъединихте към тази група. Свързване с поканващия член на групата.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You may migrate the exported database." xml:space="preserve">
<source>You may migrate the exported database.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You may save the exported archive." xml:space="preserve">
<source>You may save the exported archive.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="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." xml:space="preserve">
<source>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.</source>
<target>Трябва да използвате най-новата версия на вашата чат база данни САМО на едно устройство, в противен случай може да спрете да получавате съобщения от някои контакти.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You need to allow your contact to call to be able to call them." xml:space="preserve">
<source>You need to allow your contact to call to be able to call them.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You need to allow your contact to send voice messages to be able to send them." xml:space="preserve">
<source>You need to allow your contact to send voice messages to be able to send them.</source>
<target>Трябва да разрешите на вашия контакт да изпраща гласови съобщения, за да можете да ги изпращате.</target>
@@ -7353,13 +7480,6 @@ Repeat connection request?</source>
<target>Вашите чат профили</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact needs to be online for the connection to complete.&#10;You can cancel this connection and remove the contact (and try later with a new link)." xml:space="preserve">
<source>Your contact needs to be online for the connection to complete.
You can cancel this connection and remove the contact (and try later with a new link).</source>
<target>Вашият контакт трябва да бъде онлайн, за да осъществите връзката.
Можете да откажете тази връзка и да премахнете контакта (и да опитате по -късно с нов линк).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
<target>Вашият контакт изпрати файл, който е по-голям от поддържания в момента максимален размер (%@).</target>
@@ -7551,6 +7671,10 @@ SimpleX сървърите не могат да видят вашия профи
<target>удебелен</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="call" xml:space="preserve">
<source>call</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="call error" xml:space="preserve">
<source>call error</source>
<target>грешка при повикване</target>
@@ -7917,6 +8041,10 @@ SimpleX сървърите не могат да видят вашия профи
<target>покана за група %@</target>
<note>group name</note>
</trans-unit>
<trans-unit id="invite" xml:space="preserve">
<source>invite</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="invited" xml:space="preserve">
<source>invited</source>
<target>поканен</target>
@@ -7972,6 +8100,10 @@ SimpleX сървърите не могат да видят вашия профи
<target>свързан</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message received" xml:space="preserve">
<source>message received</source>
<target>получено съобщение</target>
@@ -8002,6 +8134,10 @@ SimpleX сървърите не могат да видят вашия профи
<target>месеци</target>
<note>time unit</note>
</trans-unit>
<trans-unit id="mute" xml:space="preserve">
<source>mute</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="never" xml:space="preserve">
<source>never</source>
<target>никога</target>
@@ -8132,6 +8268,10 @@ SimpleX сървърите не могат да видят вашия профи
<target>запазено от %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="search" xml:space="preserve">
<source>search</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve">
<source>sec</source>
<target>сек.</target>
@@ -8203,8 +8343,8 @@ last received msg: %2$@</source>
<target>неизвестен</target>
<note>connection info</note>
</trans-unit>
<trans-unit id="unknown relays" xml:space="preserve">
<source>unknown relays</source>
<trans-unit id="unknown servers" xml:space="preserve">
<source>unknown servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unknown status" xml:space="preserve">
@@ -8212,6 +8352,10 @@ last received msg: %2$@</source>
<target>неизвестен статус</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unmute" xml:space="preserve">
<source>unmute</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unprotected" xml:space="preserve">
<source>unprotected</source>
<note>No comment provided by engineer.</note>
@@ -8256,6 +8400,10 @@ last received msg: %2$@</source>
<target>чрез реле</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="video" xml:space="preserve">
<source>video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="video call (not e2e encrypted)" xml:space="preserve">
<source>video call (not e2e encrypted)</source>
<target>видео разговор (не е e2e криптиран)</target>
@@ -8502,10 +8650,6 @@ last received msg: %2$@</source>
<source>Database upgrade required</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Dismiss Sheet" xml:space="preserve">
<source>Dismiss Sheet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error preparing file" xml:space="preserve">
<source>Error preparing file</source>
<note>No comment provided by engineer.</note>
@@ -8530,10 +8674,6 @@ last received msg: %2$@</source>
<source>Invalid migration confirmation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep Trying" xml:space="preserve">
<source>Keep Trying</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keychain error" xml:space="preserve">
<source>Keychain error</source>
<note>No comment provided by engineer.</note>
@@ -8546,10 +8686,6 @@ last received msg: %2$@</source>
<source>No active profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<note>No comment provided by engineer.</note>
@@ -8574,14 +8710,22 @@ last received msg: %2$@</source>
<source>Selected chat preferences prohibit this message.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sending File" xml:space="preserve">
<source>Sending File</source>
<trans-unit id="Sending a message takes longer than expected." xml:space="preserve">
<source>Sending a message takes longer than expected.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sending message…" xml:space="preserve">
<source>Sending message…</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share" xml:space="preserve">
<source>Share</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Slow network?" xml:space="preserve">
<source>Slow network?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unknown database error: %@" xml:space="preserve">
<source>Unknown database error: %@</source>
<note>No comment provided by engineer.</note>
@@ -8590,6 +8734,10 @@ last received msg: %2$@</source>
<source>Unsupported format</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wait" xml:space="preserve">
<source>Wait</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wrong database passphrase" xml:space="preserve">
<source>Wrong database passphrase</source>
<note>No comment provided by engineer.</note>
@@ -542,7 +542,8 @@
<source>Accept</source>
<target>Přijmout</target>
<note>accept contact request via notification
accept incoming call via notification</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept connection request?" xml:space="preserve">
<source>Accept connection request?</source>
@@ -557,7 +558,8 @@
<trans-unit id="Accept incognito" xml:space="preserve">
<source>Accept incognito</source>
<target>Přijmout inkognito</target>
<note>accept contact request via notification</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Acknowledged" xml:space="preserve">
<source>Acknowledged</source>
@@ -715,6 +717,10 @@
<target>Povolte hovory, pouze pokud je váš kontakt povolí.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow calls?" xml:space="preserve">
<source>Allow calls?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow disappearing messages only if your contact allows it to you." xml:space="preserve">
<source>Allow disappearing messages only if your contact allows it to you.</source>
<target>Povolte mizící zprávy, pouze pokud vám to váš kontakt dovolí.</target>
@@ -895,6 +901,14 @@
<source>Archive and upload</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archive contacts to chat later." xml:space="preserve">
<source>Archive contacts to chat later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archived contacts" xml:space="preserve">
<source>Archived contacts</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archiving database" xml:space="preserve">
<source>Archiving database</source>
<note>No comment provided by engineer.</note>
@@ -991,6 +1005,10 @@
<target>Lepší zprávy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better networking" xml:space="preserve">
<source>Better networking</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Black" xml:space="preserve">
<source>Black</source>
<note>No comment provided by engineer.</note>
@@ -1023,6 +1041,10 @@
<source>Blocked by admin</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Blur for better privacy." xml:space="preserve">
<source>Blur for better privacy.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Blur media" xml:space="preserve">
<source>Blur media</source>
<note>No comment provided by engineer.</note>
@@ -1072,10 +1094,22 @@
<target>Hovory</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Calls prohibited!" xml:space="preserve">
<source>Calls prohibited!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Camera not available" xml:space="preserve">
<source>Camera not available</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't call contact" xml:space="preserve">
<source>Can't call contact</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't call member" xml:space="preserve">
<source>Can't call member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't invite contact!" xml:space="preserve">
<source>Can't invite contact!</source>
<target>Nelze pozvat kontakt!</target>
@@ -1086,6 +1120,10 @@
<target>Nelze pozvat kontakty!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't message member" xml:space="preserve">
<source>Can't message member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cancel" xml:space="preserve">
<source>Cancel</source>
<target>Zrušit</target>
@@ -1192,6 +1230,10 @@
<target>Databáze chatu odstraněna</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database exported" xml:space="preserve">
<source>Chat database exported</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database imported" xml:space="preserve">
<source>Chat database imported</source>
<target>Importovaná databáze chatu</target>
@@ -1211,6 +1253,10 @@
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat list" xml:space="preserve">
<source>Chat list</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat migrated!" xml:space="preserve">
<source>Chat migrated!</source>
<note>No comment provided by engineer.</note>
@@ -1268,7 +1314,7 @@
<trans-unit id="Clear" xml:space="preserve">
<source>Clear</source>
<target>Vyčistit</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Clear conversation" xml:space="preserve">
<source>Clear conversation</source>
@@ -1289,6 +1335,10 @@
<target>Zrušte ověření</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Color chats with the new themes." xml:space="preserve">
<source>Color chats with the new themes.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Color mode" xml:space="preserve">
<source>Color mode</source>
<note>No comment provided by engineer.</note>
@@ -1326,6 +1376,10 @@
<target>Potvrdit heslo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm contact deletion?" xml:space="preserve">
<source>Confirm contact deletion?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm database upgrades" xml:space="preserve">
<source>Confirm database upgrades</source>
<target>Potvrdit aktualizaci databáze</target>
@@ -1375,6 +1429,10 @@
<source>Connect to desktop</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect to your friends faster." xml:space="preserve">
<source>Connect to your friends faster.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect to yourself?" xml:space="preserve">
<source>Connect to yourself?</source>
<note>No comment provided by engineer.</note>
@@ -1437,6 +1495,10 @@ This is your own one-time link!</source>
<target>Připojování k serveru... (chyba: %@)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting to contact, please wait or check later!" xml:space="preserve">
<source>Connecting to contact, please wait or check later!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting to desktop" xml:space="preserve">
<source>Connecting to desktop</source>
<note>No comment provided by engineer.</note>
@@ -1446,6 +1508,10 @@ This is your own one-time link!</source>
<target>Připojení</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connection and servers status." xml:space="preserve">
<source>Connection and servers status.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connection error" xml:space="preserve">
<source>Connection error</source>
<target>Chyba připojení</target>
@@ -1492,6 +1558,10 @@ This is your own one-time link!</source>
<target>Kontakt již existuje</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact deleted!" xml:space="preserve">
<source>Contact deleted!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact hidden:" xml:space="preserve">
<source>Contact hidden:</source>
<target>Skrytý kontakt:</target>
@@ -1502,9 +1572,8 @@ This is your own one-time link!</source>
<target>Kontakt je připojen</target>
<note>notification</note>
</trans-unit>
<trans-unit id="Contact is not connected yet!" xml:space="preserve">
<source>Contact is not connected yet!</source>
<target>Kontakt ještě není připojen!</target>
<trans-unit id="Contact is deleted." xml:space="preserve">
<source>Contact is deleted.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact name" xml:space="preserve">
@@ -1517,6 +1586,10 @@ This is your own one-time link!</source>
<target>Předvolby kontaktů</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact will be deleted - this cannot be undone!" xml:space="preserve">
<source>Contact will be deleted - this cannot be undone!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contacts" xml:space="preserve">
<source>Contacts</source>
<target>Kontakty</target>
@@ -1532,6 +1605,10 @@ This is your own one-time link!</source>
<target>Pokračovat</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Conversation deleted!" xml:space="preserve">
<source>Conversation deleted!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy" xml:space="preserve">
<source>Copy</source>
<target>Kopírovat</target>
@@ -1789,7 +1866,8 @@ This is your own one-time link!</source>
<trans-unit id="Delete" xml:space="preserve">
<source>Delete</source>
<target>Smazat</target>
<note>chat item action</note>
<note>chat item action
swipe action</note>
</trans-unit>
<trans-unit id="Delete %lld messages of members?" xml:space="preserve">
<source>Delete %lld messages of members?</source>
@@ -1799,11 +1877,6 @@ This is your own one-time link!</source>
<source>Delete %lld messages?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete Contact" xml:space="preserve">
<source>Delete Contact</source>
<target>Smazat kontakt</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete address" xml:space="preserve">
<source>Delete address</source>
<target>Odstranit adresu</target>
@@ -1858,9 +1931,8 @@ This is your own one-time link!</source>
<target>Smazat kontakt</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete contact?&#10;This cannot be undone!" xml:space="preserve">
<source>Delete contact?
This cannot be undone!</source>
<trans-unit id="Delete contact?" xml:space="preserve">
<source>Delete contact?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete database" xml:space="preserve">
@@ -1952,11 +2024,6 @@ This cannot be undone!</source>
<target>Smazat starou databázi?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete pending connection" xml:space="preserve">
<source>Delete pending connection</source>
<target>Smazat čekající připojení</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete pending connection?" xml:space="preserve">
<source>Delete pending connection?</source>
<target>Smazat čekající připojení?</target>
@@ -1972,11 +2039,19 @@ This cannot be undone!</source>
<target>Odstranit frontu</target>
<note>server test step</note>
</trans-unit>
<trans-unit id="Delete up to 20 messages at once." xml:space="preserve">
<source>Delete up to 20 messages at once.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete user profile?" xml:space="preserve">
<source>Delete user profile?</source>
<target>Smazat uživatelský profil?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete without notification" xml:space="preserve">
<source>Delete without notification</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Deleted" xml:space="preserve">
<source>Deleted</source>
<note>No comment provided by engineer.</note>
@@ -2052,6 +2127,10 @@ This cannot be undone!</source>
<target>Vyvinout</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Developer options" xml:space="preserve">
<source>Developer options</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Developer tools" xml:space="preserve">
<source>Developer tools</source>
<target>Nástroje pro vývojáře</target>
@@ -2536,11 +2615,6 @@ This cannot be undone!</source>
<target>Chyba při mazání připojení</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting contact" xml:space="preserve">
<source>Error deleting contact</source>
<target>Chyba mazání kontaktu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting database" xml:space="preserve">
<source>Error deleting database</source>
<target>Chyba při mazání databáze</target>
@@ -2820,7 +2894,7 @@ This cannot be undone!</source>
<trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source>
<target>Oblíbené</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="File error" xml:space="preserve">
<source>File error</source>
@@ -3530,6 +3604,10 @@ Error: %2$@</source>
3. Spojení je kompromitováno.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It protects your IP address and connections." xml:space="preserve">
<source>It protects your IP address and connections.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It seems like you are already connected via this link. If it is not the case, there was an error (%@)." xml:space="preserve">
<source>It seems like you are already connected via this link. If it is not the case, there was an error (%@).</source>
<target>Zdá se, že jste již připojeni prostřednictvím tohoto odkazu. Pokud tomu tak není, došlo k chybě (%@).</target>
@@ -3548,7 +3626,7 @@ Error: %2$@</source>
<trans-unit id="Join" xml:space="preserve">
<source>Join</source>
<target>Připojte se na</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Join group" xml:space="preserve">
<source>Join group</source>
@@ -3586,6 +3664,10 @@ This is your link for group %@!</source>
<source>Keep</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep conversation" xml:space="preserve">
<source>Keep conversation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
<source>Keep the app open to use it from desktop</source>
<note>No comment provided by engineer.</note>
@@ -3627,7 +3709,7 @@ This is your link for group %@!</source>
<trans-unit id="Leave" xml:space="preserve">
<source>Leave</source>
<target>Opustit</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Leave group" xml:space="preserve">
<source>Leave group</source>
@@ -3756,6 +3838,10 @@ This is your link for group %@!</source>
<target>Max 30 vteřin, přijato okamžitě.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Media &amp; file servers" xml:space="preserve">
<source>Media &amp; file servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Medium" xml:space="preserve">
<source>Medium</source>
<note>blur media</note>
@@ -3838,12 +3924,8 @@ This is your link for group %@!</source>
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing mode" xml:space="preserve">
<source>Message routing mode</source>
<trans-unit id="Message servers" xml:space="preserve">
<source>Message servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message source remains private." xml:space="preserve">
@@ -3987,7 +4069,7 @@ This is your link for group %@!</source>
<trans-unit id="Mute" xml:space="preserve">
<source>Mute</source>
<target>Ztlumit</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Muted when inactive!" xml:space="preserve">
<source>Muted when inactive!</source>
@@ -3997,7 +4079,7 @@ This is your link for group %@!</source>
<trans-unit id="Name" xml:space="preserve">
<source>Name</source>
<target>Jméno</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Network &amp; servers" xml:space="preserve">
<source>Network &amp; servers</source>
@@ -4035,6 +4117,10 @@ This is your link for group %@!</source>
<source>New chat</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat experience 🎉" xml:space="preserve">
<source>New chat experience 🎉</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New contact request" xml:space="preserve">
<source>New contact request</source>
<target>Žádost o nový kontakt</target>
@@ -4065,6 +4151,10 @@ This is your link for group %@!</source>
<target>Nový V %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New media options" xml:space="preserve">
<source>New media options</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member role" xml:space="preserve">
<source>New member role</source>
<target>Nová role člena</target>
@@ -4203,14 +4293,18 @@ This is your link for group %@!</source>
<target>Jednorázový zvací odkaz</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be required for connection. Requires enabling VPN." xml:space="preserve">
<source>Onion hosts will be required for connection. Requires enabling VPN.</source>
<target>Pro připojení budou vyžadováni Onion hostitelé. Vyžaduje povolení sítě VPN.</target>
<trans-unit id="Onion hosts will be **required** for connection.&#10;Requires compatible VPN." xml:space="preserve">
<source>Onion hosts will be **required** for connection.
Requires compatible VPN.</source>
<target>Pro připojení budou vyžadováni Onion hostitelé.
Vyžaduje povolení sítě VPN.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be used when available. Requires enabling VPN." xml:space="preserve">
<source>Onion hosts will be used when available. Requires enabling VPN.</source>
<target>Onion hostitelé budou použiti, pokud jsou k dispozici. Vyžaduje povolení sítě VPN.</target>
<trans-unit id="Onion hosts will be used when available.&#10;Requires compatible VPN." xml:space="preserve">
<source>Onion hosts will be used when available.
Requires compatible VPN.</source>
<target>Onion hostitelé budou použiti, pokud jsou k dispozici.
Vyžaduje povolení sítě VPN.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will not be used." xml:space="preserve">
@@ -4223,6 +4317,10 @@ This is your link for group %@!</source>
<target>Pouze klientská zařízení ukládají uživatelské profily, kontakty, skupiny a zprávy odeslané s **2vrstvým šifrováním typu end-to-end**.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only delete conversation" xml:space="preserve">
<source>Only delete conversation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
<source>Only group owners can change group preferences.</source>
<target>Předvolby skupiny mohou měnit pouze vlastníci skupiny.</target>
@@ -4442,6 +4540,14 @@ This is your link for group %@!</source>
<source>Picture-in-picture calls</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Play from the chat list." xml:space="preserve">
<source>Play from the chat list.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please ask your contact to enable calls." xml:space="preserve">
<source>Please ask your contact to enable calls.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please ask your contact to enable sending voice messages." xml:space="preserve">
<source>Please ask your contact to enable sending voice messages.</source>
<target>Prosím, požádejte kontaktní osobu, aby umožnila odesílání hlasových zpráv.</target>
@@ -4720,6 +4826,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Ohodnoťte aplikaci</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reachable chat toolbar" xml:space="preserve">
<source>Reachable chat toolbar</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="React…" xml:space="preserve">
<source>React…</source>
<target>Reagovat…</target>
@@ -4728,7 +4838,7 @@ Enable in *Network &amp; servers* settings.</source>
<trans-unit id="Read" xml:space="preserve">
<source>Read</source>
<target>Číst</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Read more" xml:space="preserve">
<source>Read more</source>
@@ -4876,7 +4986,8 @@ Enable in *Network &amp; servers* settings.</source>
<trans-unit id="Reject" xml:space="preserve">
<source>Reject</source>
<target>Odmítnout</target>
<note>reject incoming call via notification</note>
<note>reject incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Reject (sender NOT notified)" xml:space="preserve">
<source>Reject (sender NOT notified)</source>
@@ -4972,6 +5083,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Obnovit</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all hints" xml:space="preserve">
<source>Reset all hints</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all statistics" xml:space="preserve">
<source>Reset all statistics</source>
<note>No comment provided by engineer.</note>
@@ -5037,11 +5152,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>Odhalit</target>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Revert" xml:space="preserve">
<source>Revert</source>
<target>Vrátit</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>Odvolat</target>
@@ -5071,11 +5181,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>SMP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SMP servers" xml:space="preserve">
<source>SMP servers</source>
<target>SMP servery</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safely receive files" xml:space="preserve">
<source>Safely receive files</source>
<note>No comment provided by engineer.</note>
@@ -5104,6 +5209,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Uložit a upozornit členy skupiny</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save and reconnect" xml:space="preserve">
<source>Save and reconnect</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save and update group profile" xml:space="preserve">
<source>Save and update group profile</source>
<target>Uložit a aktualizovat profil skupiny</target>
@@ -5297,11 +5406,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>Potvrzení o doručení zasílat na</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send direct message" xml:space="preserve">
<source>Send direct message</source>
<target>Odeslat přímou zprávu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send direct message to connect" xml:space="preserve">
<source>Send direct message to connect</source>
<target>Odeslat přímou zprávu pro připojení</target>
@@ -5326,6 +5430,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Odeslat živou zprávu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send message to enable calls." xml:space="preserve">
<source>Send message to enable calls.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when IP address is protected and your or destination server does not support private routing." xml:space="preserve">
<source>Send messages directly when IP address is protected and your or destination server does not support private routing.</source>
<note>No comment provided by engineer.</note>
@@ -5591,6 +5699,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Sdílet adresu s kontakty?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share from other apps." xml:space="preserve">
<source>Share from other apps.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share link" xml:space="preserve">
<source>Share link</source>
<target>Sdílet odkaz</target>
@@ -5750,11 +5862,19 @@ Enable in *Network &amp; servers* settings.</source>
<source>Soft</source>
<note>blur media</note>
</trans-unit>
<trans-unit id="Some file(s) were not exported:" xml:space="preserve">
<source>Some file(s) were not exported:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some non-fatal errors occurred during import - you may see Chat console for more details." xml:space="preserve">
<source>Some non-fatal errors occurred during import - you may see Chat console for more details.</source>
<target>Během importu došlo k nezávažným chybám - podrobnosti naleznete v chat konzoli.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some non-fatal errors occurred during import:" xml:space="preserve">
<source>Some non-fatal errors occurred during import:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Somebody" xml:space="preserve">
<source>Somebody</source>
<target>Někdo</target>
@@ -5880,6 +6000,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Ověření systému</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="TCP connection" xml:space="preserve">
<source>TCP connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="TCP connection timeout" xml:space="preserve">
<source>TCP connection timeout</source>
<target>Časový limit připojení TCP</target>
@@ -5937,11 +6061,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Tap to scan</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Tap to start a new chat" xml:space="preserve">
<source>Tap to start a new chat</source>
<target>Klepnutím na zahájíte nový chat</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Temporary file error" xml:space="preserve">
<source>Temporary file error</source>
<note>No comment provided by engineer.</note>
@@ -6224,11 +6343,19 @@ Před zapnutím této funkce budete vyzváni k dokončení ověření.</target>
<target>Chcete-li ověřit koncové šifrování u svého kontaktu, porovnejte (nebo naskenujte) kód na svých zařízeních.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toggle chat list:" xml:space="preserve">
<source>Toggle chat list:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toggle incognito when connecting." xml:space="preserve">
<source>Toggle incognito when connecting.</source>
<target>Změnit inkognito režim při připojení.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toolbar opacity" xml:space="preserve">
<source>Toolbar opacity</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Total" xml:space="preserve">
<source>Total</source>
<note>No comment provided by engineer.</note>
@@ -6299,7 +6426,7 @@ Před zapnutím této funkce budete vyzváni k dokončení ověření.</target>
<trans-unit id="Unfav." xml:space="preserve">
<source>Unfav.</source>
<target>Odobl.</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Unhide" xml:space="preserve">
<source>Unhide</source>
@@ -6373,12 +6500,12 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
<trans-unit id="Unmute" xml:space="preserve">
<source>Unmute</source>
<target>Zrušit ztlumení</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Unread" xml:space="preserve">
<source>Unread</source>
<target>Nepřečtený</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Up to 100 last messages are sent to new members." xml:space="preserve">
<source>Up to 100 last messages are sent to new members.</source>
@@ -6389,11 +6516,6 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
<target>Aktualizovat</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update .onion hosts setting?" xml:space="preserve">
<source>Update .onion hosts setting?</source>
<target>Aktualizovat nastavení hostitelů .onion?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update database passphrase" xml:space="preserve">
<source>Update database passphrase</source>
<target>Aktualizovat přístupovou frázi databáze</target>
@@ -6404,9 +6526,8 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
<target>Aktualizovat nastavení sítě?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update transport isolation mode?" xml:space="preserve">
<source>Update transport isolation mode?</source>
<target>Aktualizovat režim dopravní izolace?</target>
<trans-unit id="Update settings?" xml:space="preserve">
<source>Update settings?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Updating settings will re-connect the client to all servers." xml:space="preserve">
@@ -6414,11 +6535,6 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
<target>Aktualizací nastavení se klient znovu připojí ke všem serverům.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Updating this setting will re-connect the client to all servers." xml:space="preserve">
<source>Updating this setting will re-connect the client to all servers.</source>
<target>Aktualizace tohoto nastavení znovu připojí klienta ke všem serverům.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Upgrade and open chat" xml:space="preserve">
<source>Upgrade and open chat</source>
<target>Zvýšit a otevřít chat</target>
@@ -6509,6 +6625,10 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
<source>Use the app while in the call.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use the app with one hand." xml:space="preserve">
<source>Use the app with one hand.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>Profil uživatele</target>
@@ -6518,11 +6638,6 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
<source>User selection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using .onion hosts requires compatible VPN provider." xml:space="preserve">
<source>Using .onion hosts requires compatible VPN provider.</source>
<target>Použití hostitelů .onion vyžaduje kompatibilního poskytovatele VPN.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using SimpleX Chat servers." xml:space="preserve">
<source>Using SimpleX Chat servers.</source>
<target>Používat servery SimpleX Chat.</target>
@@ -6759,11 +6874,6 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
<source>XFTP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="XFTP servers" xml:space="preserve">
<source>XFTP servers</source>
<target>XFTP servery</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>Vy</target>
@@ -6841,6 +6951,10 @@ Repeat join request?</source>
<target>Můžete přijímat hovory z obrazovky zámku, bez ověření zařízení a aplikace.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can change it in Appearance settings." xml:space="preserve">
<source>You can change it in Appearance settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can create it later" xml:space="preserve">
<source>You can create it later</source>
<target>Můžete vytvořit později</target>
@@ -6874,6 +6988,10 @@ Repeat join request?</source>
<target>Nyní můžete posílat zprávy %@</target>
<note>notification body</note>
</trans-unit>
<trans-unit id="You can send messages to %@ from Archived contacts." xml:space="preserve">
<source>You can send messages to %@ from Archived contacts.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can set lock screen notification preview via settings." xml:space="preserve">
<source>You can set lock screen notification preview via settings.</source>
<target>Náhled oznámení na zamykací obrazovce můžete změnit v nastavení.</target>
@@ -6899,6 +7017,10 @@ Repeat join request?</source>
<target>Chat můžete zahájit prostřednictvím aplikace Nastavení / Databáze nebo restartováním aplikace</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can still view conversation with %@ in the list of chats." xml:space="preserve">
<source>You can still view conversation with %@ in the list of chats.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can turn on SimpleX Lock via Settings." xml:space="preserve">
<source>You can turn on SimpleX Lock via Settings.</source>
<target>Zámek SimpleX můžete zapnout v Nastavení.</target>
@@ -6937,11 +7059,6 @@ Repeat join request?</source>
Repeat connection request?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You have no chats" xml:space="preserve">
<source>You have no chats</source>
<target>Nemáte žádné konverzace</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You have to enter passphrase every time the app starts - it is not stored on the device." xml:space="preserve">
<source>You have to enter passphrase every time the app starts - it is not stored on the device.</source>
<target>Musíte zadat přístupovou frázi při každém spuštění aplikace - není uložena v zařízení.</target>
@@ -6962,11 +7079,23 @@ Repeat connection request?</source>
<target>Připojili jste se k této skupině. Připojení k pozvání člena skupiny.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You may migrate the exported database." xml:space="preserve">
<source>You may migrate the exported database.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You may save the exported archive." xml:space="preserve">
<source>You may save the exported archive.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="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." xml:space="preserve">
<source>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.</source>
<target>Nejnovější verzi databáze chatu musíte používat POUZE v jednom zařízení, jinak se může stát, že přestanete přijímat zprávy od některých kontaktů.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You need to allow your contact to call to be able to call them." xml:space="preserve">
<source>You need to allow your contact to call to be able to call them.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You need to allow your contact to send voice messages to be able to send them." xml:space="preserve">
<source>You need to allow your contact to send voice messages to be able to send them.</source>
<target>Abyste mohli odesílat hlasové zprávy, musíte je povolit svému kontaktu.</target>
@@ -7080,13 +7209,6 @@ Repeat connection request?</source>
<target>Vaše chat profily</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact needs to be online for the connection to complete.&#10;You can cancel this connection and remove the contact (and try later with a new link)." xml:space="preserve">
<source>Your contact needs to be online for the connection to complete.
You can cancel this connection and remove the contact (and try later with a new link).</source>
<target>K dokončení připojení, musí být váš kontakt online.
Toto připojení můžete zrušit a kontakt odebrat (a zkusit to později s novým odkazem).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
<target>Kontakt odeslal soubor, který je větší než aktuálně podporovaná maximální velikost (%@).</target>
@@ -7270,6 +7392,10 @@ Servery SimpleX nevidí váš profil.</target>
<target>tučně</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="call" xml:space="preserve">
<source>call</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="call error" xml:space="preserve">
<source>call error</source>
<target>chyba volání</target>
@@ -7632,6 +7758,10 @@ Servery SimpleX nevidí váš profil.</target>
<target>pozvánka do skupiny %@</target>
<note>group name</note>
</trans-unit>
<trans-unit id="invite" xml:space="preserve">
<source>invite</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="invited" xml:space="preserve">
<source>invited</source>
<target>pozvánka</target>
@@ -7686,6 +7816,10 @@ Servery SimpleX nevidí váš profil.</target>
<target>připojeno</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message received" xml:space="preserve">
<source>message received</source>
<target>zpráva přijata</target>
@@ -7716,6 +7850,10 @@ Servery SimpleX nevidí váš profil.</target>
<target>měsíců</target>
<note>time unit</note>
</trans-unit>
<trans-unit id="mute" xml:space="preserve">
<source>mute</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="never" xml:space="preserve">
<source>never</source>
<target>nikdy</target>
@@ -7840,6 +7978,10 @@ Servery SimpleX nevidí váš profil.</target>
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="search" xml:space="preserve">
<source>search</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve">
<source>sec</source>
<target>sek</target>
@@ -7907,14 +8049,18 @@ last received msg: %2$@</source>
<target>neznámý</target>
<note>connection info</note>
</trans-unit>
<trans-unit id="unknown relays" xml:space="preserve">
<source>unknown relays</source>
<trans-unit id="unknown servers" xml:space="preserve">
<source>unknown servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unknown status" xml:space="preserve">
<source>unknown status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unmute" xml:space="preserve">
<source>unmute</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unprotected" xml:space="preserve">
<source>unprotected</source>
<note>No comment provided by engineer.</note>
@@ -7957,6 +8103,10 @@ last received msg: %2$@</source>
<target>přes relé</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="video" xml:space="preserve">
<source>video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="video call (not e2e encrypted)" xml:space="preserve">
<source>video call (not e2e encrypted)</source>
<target>videohovoru (nešifrovaného e2e)</target>
@@ -8199,10 +8349,6 @@ last received msg: %2$@</source>
<source>Database upgrade required</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Dismiss Sheet" xml:space="preserve">
<source>Dismiss Sheet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error preparing file" xml:space="preserve">
<source>Error preparing file</source>
<note>No comment provided by engineer.</note>
@@ -8227,10 +8373,6 @@ last received msg: %2$@</source>
<source>Invalid migration confirmation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep Trying" xml:space="preserve">
<source>Keep Trying</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keychain error" xml:space="preserve">
<source>Keychain error</source>
<note>No comment provided by engineer.</note>
@@ -8243,10 +8385,6 @@ last received msg: %2$@</source>
<source>No active profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<note>No comment provided by engineer.</note>
@@ -8271,14 +8409,22 @@ last received msg: %2$@</source>
<source>Selected chat preferences prohibit this message.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sending File" xml:space="preserve">
<source>Sending File</source>
<trans-unit id="Sending a message takes longer than expected." xml:space="preserve">
<source>Sending a message takes longer than expected.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sending message…" xml:space="preserve">
<source>Sending message…</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share" xml:space="preserve">
<source>Share</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Slow network?" xml:space="preserve">
<source>Slow network?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unknown database error: %@" xml:space="preserve">
<source>Unknown database error: %@</source>
<note>No comment provided by engineer.</note>
@@ -8287,6 +8433,10 @@ last received msg: %2$@</source>
<source>Unsupported format</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wait" xml:space="preserve">
<source>Wait</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wrong database passphrase" xml:space="preserve">
<source>Wrong database passphrase</source>
<note>No comment provided by engineer.</note>
File diff suppressed because it is too large Load Diff
@@ -561,7 +561,8 @@
<source>Accept</source>
<target>Accept</target>
<note>accept contact request via notification
accept incoming call via notification</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept connection request?" xml:space="preserve">
<source>Accept connection request?</source>
@@ -576,7 +577,8 @@
<trans-unit id="Accept incognito" xml:space="preserve">
<source>Accept incognito</source>
<target>Accept incognito</target>
<note>accept contact request via notification</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Acknowledged" xml:space="preserve">
<source>Acknowledged</source>
@@ -748,6 +750,11 @@
<target>Allow calls only if your contact allows them.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow calls?" xml:space="preserve">
<source>Allow calls?</source>
<target>Allow calls?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow disappearing messages only if your contact allows it to you." xml:space="preserve">
<source>Allow disappearing messages only if your contact allows it to you.</source>
<target>Allow disappearing messages only if your contact allows it to you.</target>
@@ -938,6 +945,16 @@
<target>Archive and upload</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archive contacts to chat later." xml:space="preserve">
<source>Archive contacts to chat later.</source>
<target>Archive contacts to chat later.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archived contacts" xml:space="preserve">
<source>Archived contacts</source>
<target>Archived contacts</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archiving database" xml:space="preserve">
<source>Archiving database</source>
<target>Archiving database</target>
@@ -1038,6 +1055,11 @@
<target>Better messages</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better networking" xml:space="preserve">
<source>Better networking</source>
<target>Better networking</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Black" xml:space="preserve">
<source>Black</source>
<target>Black</target>
@@ -1078,6 +1100,11 @@
<target>Blocked by admin</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Blur for better privacy." xml:space="preserve">
<source>Blur for better privacy.</source>
<target>Blur for better privacy.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Blur media" xml:space="preserve">
<source>Blur media</source>
<target>Blur media</target>
@@ -1128,11 +1155,26 @@
<target>Calls</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Calls prohibited!" xml:space="preserve">
<source>Calls prohibited!</source>
<target>Calls prohibited!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Camera not available" xml:space="preserve">
<source>Camera not available</source>
<target>Camera not available</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't call contact" xml:space="preserve">
<source>Can't call contact</source>
<target>Can't call contact</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't call member" xml:space="preserve">
<source>Can't call member</source>
<target>Can't call member</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't invite contact!" xml:space="preserve">
<source>Can't invite contact!</source>
<target>Can't invite contact!</target>
@@ -1143,6 +1185,11 @@
<target>Can't invite contacts!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't message member" xml:space="preserve">
<source>Can't message member</source>
<target>Can't message member</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cancel" xml:space="preserve">
<source>Cancel</source>
<target>Cancel</target>
@@ -1254,6 +1301,11 @@
<target>Chat database deleted</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database exported" xml:space="preserve">
<source>Chat database exported</source>
<target>Chat database exported</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database imported" xml:space="preserve">
<source>Chat database imported</source>
<target>Chat database imported</target>
@@ -1274,6 +1326,11 @@
<target>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat list" xml:space="preserve">
<source>Chat list</source>
<target>Chat list</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat migrated!" xml:space="preserve">
<source>Chat migrated!</source>
<target>Chat migrated!</target>
@@ -1337,7 +1394,7 @@
<trans-unit id="Clear" xml:space="preserve">
<source>Clear</source>
<target>Clear</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Clear conversation" xml:space="preserve">
<source>Clear conversation</source>
@@ -1359,6 +1416,11 @@
<target>Clear verification</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Color chats with the new themes." xml:space="preserve">
<source>Color chats with the new themes.</source>
<target>Color chats with the new themes.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Color mode" xml:space="preserve">
<source>Color mode</source>
<target>Color mode</target>
@@ -1399,6 +1461,11 @@
<target>Confirm Passcode</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm contact deletion?" xml:space="preserve">
<source>Confirm contact deletion?</source>
<target>Confirm contact deletion?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm database upgrades" xml:space="preserve">
<source>Confirm database upgrades</source>
<target>Confirm database upgrades</target>
@@ -1454,6 +1521,11 @@
<target>Connect to desktop</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect to your friends faster." xml:space="preserve">
<source>Connect to your friends faster.</source>
<target>Connect to your friends faster.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect to yourself?" xml:space="preserve">
<source>Connect to yourself?</source>
<target>Connect to yourself?</target>
@@ -1528,6 +1600,11 @@ This is your own one-time link!</target>
<target>Connecting to server… (error: %@)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting to contact, please wait or check later!" xml:space="preserve">
<source>Connecting to contact, please wait or check later!</source>
<target>Connecting to contact, please wait or check later!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting to desktop" xml:space="preserve">
<source>Connecting to desktop</source>
<target>Connecting to desktop</target>
@@ -1538,6 +1615,11 @@ This is your own one-time link!</target>
<target>Connection</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connection and servers status." xml:space="preserve">
<source>Connection and servers status.</source>
<target>Connection and servers status.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connection error" xml:space="preserve">
<source>Connection error</source>
<target>Connection error</target>
@@ -1588,6 +1670,11 @@ This is your own one-time link!</target>
<target>Contact already exists</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact deleted!" xml:space="preserve">
<source>Contact deleted!</source>
<target>Contact deleted!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact hidden:" xml:space="preserve">
<source>Contact hidden:</source>
<target>Contact hidden:</target>
@@ -1598,9 +1685,9 @@ This is your own one-time link!</target>
<target>Contact is connected</target>
<note>notification</note>
</trans-unit>
<trans-unit id="Contact is not connected yet!" xml:space="preserve">
<source>Contact is not connected yet!</source>
<target>Contact is not connected yet!</target>
<trans-unit id="Contact is deleted." xml:space="preserve">
<source>Contact is deleted.</source>
<target>Contact is deleted.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact name" xml:space="preserve">
@@ -1613,6 +1700,11 @@ This is your own one-time link!</target>
<target>Contact preferences</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact will be deleted - this cannot be undone!" xml:space="preserve">
<source>Contact will be deleted - this cannot be undone!</source>
<target>Contact will be deleted - this cannot be undone!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contacts" xml:space="preserve">
<source>Contacts</source>
<target>Contacts</target>
@@ -1628,6 +1720,11 @@ This is your own one-time link!</target>
<target>Continue</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Conversation deleted!" xml:space="preserve">
<source>Conversation deleted!</source>
<target>Conversation deleted!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy" xml:space="preserve">
<source>Copy</source>
<target>Copy</target>
@@ -1899,7 +1996,8 @@ This is your own one-time link!</target>
<trans-unit id="Delete" xml:space="preserve">
<source>Delete</source>
<target>Delete</target>
<note>chat item action</note>
<note>chat item action
swipe action</note>
</trans-unit>
<trans-unit id="Delete %lld messages of members?" xml:space="preserve">
<source>Delete %lld messages of members?</source>
@@ -1911,11 +2009,6 @@ This is your own one-time link!</target>
<target>Delete %lld messages?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete Contact" xml:space="preserve">
<source>Delete Contact</source>
<target>Delete Contact</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete address" xml:space="preserve">
<source>Delete address</source>
<target>Delete address</target>
@@ -1971,11 +2064,9 @@ This is your own one-time link!</target>
<target>Delete contact</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete contact?&#10;This cannot be undone!" xml:space="preserve">
<source>Delete contact?
This cannot be undone!</source>
<target>Delete contact?
This cannot be undone!</target>
<trans-unit id="Delete contact?" xml:space="preserve">
<source>Delete contact?</source>
<target>Delete contact?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete database" xml:space="preserve">
@@ -2068,11 +2159,6 @@ This cannot be undone!</target>
<target>Delete old database?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete pending connection" xml:space="preserve">
<source>Delete pending connection</source>
<target>Delete pending connection</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete pending connection?" xml:space="preserve">
<source>Delete pending connection?</source>
<target>Delete pending connection?</target>
@@ -2088,11 +2174,21 @@ This cannot be undone!</target>
<target>Delete queue</target>
<note>server test step</note>
</trans-unit>
<trans-unit id="Delete up to 20 messages at once." xml:space="preserve">
<source>Delete up to 20 messages at once.</source>
<target>Delete up to 20 messages at once.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete user profile?" xml:space="preserve">
<source>Delete user profile?</source>
<target>Delete user profile?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete without notification" xml:space="preserve">
<source>Delete without notification</source>
<target>Delete without notification</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Deleted" xml:space="preserve">
<source>Deleted</source>
<target>Deleted</target>
@@ -2178,6 +2274,11 @@ This cannot be undone!</target>
<target>Develop</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Developer options" xml:space="preserve">
<source>Developer options</source>
<target>Developer options</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Developer tools" xml:space="preserve">
<source>Developer tools</source>
<target>Developer tools</target>
@@ -2688,11 +2789,6 @@ This cannot be undone!</target>
<target>Error deleting connection</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting contact" xml:space="preserve">
<source>Error deleting contact</source>
<target>Error deleting contact</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting database" xml:space="preserve">
<source>Error deleting database</source>
<target>Error deleting database</target>
@@ -2987,7 +3083,7 @@ This cannot be undone!</target>
<trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source>
<target>Favorite</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="File error" xml:space="preserve">
<source>File error</source>
@@ -3741,6 +3837,11 @@ Error: %2$@</target>
3. The connection was compromised.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It protects your IP address and connections." xml:space="preserve">
<source>It protects your IP address and connections.</source>
<target>It protects your IP address and connections.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It seems like you are already connected via this link. If it is not the case, there was an error (%@)." xml:space="preserve">
<source>It seems like you are already connected via this link. If it is not the case, there was an error (%@).</source>
<target>It seems like you are already connected via this link. If it is not the case, there was an error (%@).</target>
@@ -3759,7 +3860,7 @@ Error: %2$@</target>
<trans-unit id="Join" xml:space="preserve">
<source>Join</source>
<target>Join</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Join group" xml:space="preserve">
<source>Join group</source>
@@ -3803,6 +3904,11 @@ This is your link for group %@!</target>
<target>Keep</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep conversation" xml:space="preserve">
<source>Keep conversation</source>
<target>Keep conversation</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
<source>Keep the app open to use it from desktop</source>
<target>Keep the app open to use it from desktop</target>
@@ -3846,7 +3952,7 @@ This is your link for group %@!</target>
<trans-unit id="Leave" xml:space="preserve">
<source>Leave</source>
<target>Leave</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Leave group" xml:space="preserve">
<source>Leave group</source>
@@ -3978,6 +4084,11 @@ This is your link for group %@!</target>
<target>Max 30 seconds, received instantly.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Media &amp; file servers" xml:space="preserve">
<source>Media &amp; file servers</source>
<target>Media &amp; file servers</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Medium" xml:space="preserve">
<source>Medium</source>
<target>Medium</target>
@@ -4068,14 +4179,9 @@ This is your link for group %@!</target>
<target>Message reception</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<target>Message routing fallback</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing mode" xml:space="preserve">
<source>Message routing mode</source>
<target>Message routing mode</target>
<trans-unit id="Message servers" xml:space="preserve">
<source>Message servers</source>
<target>Message servers</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message source remains private." xml:space="preserve">
@@ -4236,7 +4342,7 @@ This is your link for group %@!</target>
<trans-unit id="Mute" xml:space="preserve">
<source>Mute</source>
<target>Mute</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Muted when inactive!" xml:space="preserve">
<source>Muted when inactive!</source>
@@ -4246,7 +4352,7 @@ This is your link for group %@!</target>
<trans-unit id="Name" xml:space="preserve">
<source>Name</source>
<target>Name</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Network &amp; servers" xml:space="preserve">
<source>Network &amp; servers</source>
@@ -4288,6 +4394,11 @@ This is your link for group %@!</target>
<target>New chat</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat experience 🎉" xml:space="preserve">
<source>New chat experience 🎉</source>
<target>New chat experience 🎉</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New contact request" xml:space="preserve">
<source>New contact request</source>
<target>New contact request</target>
@@ -4318,6 +4429,11 @@ This is your link for group %@!</target>
<target>New in %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New media options" xml:space="preserve">
<source>New media options</source>
<target>New media options</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member role" xml:space="preserve">
<source>New member role</source>
<target>New member role</target>
@@ -4462,14 +4578,18 @@ This is your link for group %@!</target>
<target>One-time invitation link</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be required for connection. Requires enabling VPN." xml:space="preserve">
<source>Onion hosts will be required for connection. Requires enabling VPN.</source>
<target>Onion hosts will be required for connection. Requires enabling VPN.</target>
<trans-unit id="Onion hosts will be **required** for connection.&#10;Requires compatible VPN." xml:space="preserve">
<source>Onion hosts will be **required** for connection.
Requires compatible VPN.</source>
<target>Onion hosts will be **required** for connection.
Requires compatible VPN.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be used when available. Requires enabling VPN." xml:space="preserve">
<source>Onion hosts will be used when available. Requires enabling VPN.</source>
<target>Onion hosts will be used when available. Requires enabling VPN.</target>
<trans-unit id="Onion hosts will be used when available.&#10;Requires compatible VPN." xml:space="preserve">
<source>Onion hosts will be used when available.
Requires compatible VPN.</source>
<target>Onion hosts will be used when available.
Requires compatible VPN.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will not be used." xml:space="preserve">
@@ -4482,6 +4602,11 @@ This is your link for group %@!</target>
<target>Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only delete conversation" xml:space="preserve">
<source>Only delete conversation</source>
<target>Only delete conversation</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
<source>Only group owners can change group preferences.</source>
<target>Only group owners can change group preferences.</target>
@@ -4717,6 +4842,16 @@ This is your link for group %@!</target>
<target>Picture-in-picture calls</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Play from the chat list." xml:space="preserve">
<source>Play from the chat list.</source>
<target>Play from the chat list.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please ask your contact to enable calls." xml:space="preserve">
<source>Please ask your contact to enable calls.</source>
<target>Please ask your contact to enable calls.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please ask your contact to enable sending voice messages." xml:space="preserve">
<source>Please ask your contact to enable sending voice messages.</source>
<target>Please ask your contact to enable sending voice messages.</target>
@@ -5018,6 +5153,11 @@ Enable in *Network &amp; servers* settings.</target>
<target>Rate the app</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reachable chat toolbar" xml:space="preserve">
<source>Reachable chat toolbar</source>
<target>Reachable chat toolbar</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="React…" xml:space="preserve">
<source>React…</source>
<target>React…</target>
@@ -5026,7 +5166,7 @@ Enable in *Network &amp; servers* settings.</target>
<trans-unit id="Read" xml:space="preserve">
<source>Read</source>
<target>Read</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Read more" xml:space="preserve">
<source>Read more</source>
@@ -5186,7 +5326,8 @@ Enable in *Network &amp; servers* settings.</target>
<trans-unit id="Reject" xml:space="preserve">
<source>Reject</source>
<target>Reject</target>
<note>reject incoming call via notification</note>
<note>reject incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Reject (sender NOT notified)" xml:space="preserve">
<source>Reject (sender NOT notified)</source>
@@ -5288,6 +5429,11 @@ Enable in *Network &amp; servers* settings.</target>
<target>Reset</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all hints" xml:space="preserve">
<source>Reset all hints</source>
<target>Reset all hints</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all statistics" xml:space="preserve">
<source>Reset all statistics</source>
<target>Reset all statistics</target>
@@ -5358,11 +5504,6 @@ Enable in *Network &amp; servers* settings.</target>
<target>Reveal</target>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Revert" xml:space="preserve">
<source>Revert</source>
<target>Revert</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>Revoke</target>
@@ -5393,11 +5534,6 @@ Enable in *Network &amp; servers* settings.</target>
<target>SMP server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SMP servers" xml:space="preserve">
<source>SMP servers</source>
<target>SMP servers</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safely receive files" xml:space="preserve">
<source>Safely receive files</source>
<target>Safely receive files</target>
@@ -5428,6 +5564,11 @@ Enable in *Network &amp; servers* settings.</target>
<target>Save and notify group members</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save and reconnect" xml:space="preserve">
<source>Save and reconnect</source>
<target>Save and reconnect</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save and update group profile" xml:space="preserve">
<source>Save and update group profile</source>
<target>Save and update group profile</target>
@@ -5633,11 +5774,6 @@ Enable in *Network &amp; servers* settings.</target>
<target>Send delivery receipts to</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send direct message" xml:space="preserve">
<source>Send direct message</source>
<target>Send direct message</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send direct message to connect" xml:space="preserve">
<source>Send direct message to connect</source>
<target>Send direct message to connect</target>
@@ -5663,6 +5799,11 @@ Enable in *Network &amp; servers* settings.</target>
<target>Send live message</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send message to enable calls." xml:space="preserve">
<source>Send message to enable calls.</source>
<target>Send message to enable calls.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when IP address is protected and your or destination server does not support private routing." xml:space="preserve">
<source>Send messages directly when IP address is protected and your or destination server does not support private routing.</source>
<target>Send messages directly when IP address is protected and your or destination server does not support private routing.</target>
@@ -5948,6 +6089,11 @@ Enable in *Network &amp; servers* settings.</target>
<target>Share address with contacts?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share from other apps." xml:space="preserve">
<source>Share from other apps.</source>
<target>Share from other apps.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share link" xml:space="preserve">
<source>Share link</source>
<target>Share link</target>
@@ -6118,11 +6264,21 @@ Enable in *Network &amp; servers* settings.</target>
<target>Soft</target>
<note>blur media</note>
</trans-unit>
<trans-unit id="Some file(s) were not exported:" xml:space="preserve">
<source>Some file(s) were not exported:</source>
<target>Some file(s) were not exported:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some non-fatal errors occurred during import - you may see Chat console for more details." xml:space="preserve">
<source>Some non-fatal errors occurred during import - you may see Chat console for more details.</source>
<target>Some non-fatal errors occurred during import - you may see Chat console for more details.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some non-fatal errors occurred during import:" xml:space="preserve">
<source>Some non-fatal errors occurred during import:</source>
<target>Some non-fatal errors occurred during import:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Somebody" xml:space="preserve">
<source>Somebody</source>
<target>Somebody</target>
@@ -6258,6 +6414,11 @@ Enable in *Network &amp; servers* settings.</target>
<target>System authentication</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="TCP connection" xml:space="preserve">
<source>TCP connection</source>
<target>TCP connection</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="TCP connection timeout" xml:space="preserve">
<source>TCP connection timeout</source>
<target>TCP connection timeout</target>
@@ -6318,11 +6479,6 @@ Enable in *Network &amp; servers* settings.</target>
<target>Tap to scan</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Tap to start a new chat" xml:space="preserve">
<source>Tap to start a new chat</source>
<target>Tap to start a new chat</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Temporary file error" xml:space="preserve">
<source>Temporary file error</source>
<target>Temporary file error</target>
@@ -6622,11 +6778,21 @@ You will be prompted to complete authentication before this feature is enabled.<
<target>To verify end-to-end encryption with your contact compare (or scan) the code on your devices.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toggle chat list:" xml:space="preserve">
<source>Toggle chat list:</source>
<target>Toggle chat list:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toggle incognito when connecting." xml:space="preserve">
<source>Toggle incognito when connecting.</source>
<target>Toggle incognito when connecting.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toolbar opacity" xml:space="preserve">
<source>Toolbar opacity</source>
<target>Toolbar opacity</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Total" xml:space="preserve">
<source>Total</source>
<target>Total</target>
@@ -6705,7 +6871,7 @@ You will be prompted to complete authentication before this feature is enabled.<
<trans-unit id="Unfav." xml:space="preserve">
<source>Unfav.</source>
<target>Unfav.</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Unhide" xml:space="preserve">
<source>Unhide</source>
@@ -6782,12 +6948,12 @@ To connect, please ask your contact to create another connection link and check
<trans-unit id="Unmute" xml:space="preserve">
<source>Unmute</source>
<target>Unmute</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Unread" xml:space="preserve">
<source>Unread</source>
<target>Unread</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Up to 100 last messages are sent to new members." xml:space="preserve">
<source>Up to 100 last messages are sent to new members.</source>
@@ -6799,11 +6965,6 @@ To connect, please ask your contact to create another connection link and check
<target>Update</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update .onion hosts setting?" xml:space="preserve">
<source>Update .onion hosts setting?</source>
<target>Update .onion hosts setting?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update database passphrase" xml:space="preserve">
<source>Update database passphrase</source>
<target>Update database passphrase</target>
@@ -6814,9 +6975,9 @@ To connect, please ask your contact to create another connection link and check
<target>Update network settings?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update transport isolation mode?" xml:space="preserve">
<source>Update transport isolation mode?</source>
<target>Update transport isolation mode?</target>
<trans-unit id="Update settings?" xml:space="preserve">
<source>Update settings?</source>
<target>Update settings?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Updating settings will re-connect the client to all servers." xml:space="preserve">
@@ -6824,11 +6985,6 @@ To connect, please ask your contact to create another connection link and check
<target>Updating settings will re-connect the client to all servers.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Updating this setting will re-connect the client to all servers." xml:space="preserve">
<source>Updating this setting will re-connect the client to all servers.</source>
<target>Updating this setting will re-connect the client to all servers.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Upgrade and open chat" xml:space="preserve">
<source>Upgrade and open chat</source>
<target>Upgrade and open chat</target>
@@ -6929,6 +7085,11 @@ To connect, please ask your contact to create another connection link and check
<target>Use the app while in the call.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use the app with one hand." xml:space="preserve">
<source>Use the app with one hand.</source>
<target>Use the app with one hand.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>User profile</target>
@@ -6939,11 +7100,6 @@ To connect, please ask your contact to create another connection link and check
<target>User selection</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using .onion hosts requires compatible VPN provider." xml:space="preserve">
<source>Using .onion hosts requires compatible VPN provider.</source>
<target>Using .onion hosts requires compatible VPN provider.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using SimpleX Chat servers." xml:space="preserve">
<source>Using SimpleX Chat servers.</source>
<target>Using SimpleX Chat servers.</target>
@@ -7204,11 +7360,6 @@ To connect, please ask your contact to create another connection link and check
<target>XFTP server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="XFTP servers" xml:space="preserve">
<source>XFTP servers</source>
<target>XFTP servers</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>You</target>
@@ -7296,6 +7447,11 @@ Repeat join request?</target>
<target>You can accept calls from lock screen, without device and app authentication.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can change it in Appearance settings." xml:space="preserve">
<source>You can change it in Appearance settings.</source>
<target>You can change it in Appearance settings.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can create it later" xml:space="preserve">
<source>You can create it later</source>
<target>You can create it later</target>
@@ -7331,6 +7487,11 @@ Repeat join request?</target>
<target>You can now chat with %@</target>
<note>notification body</note>
</trans-unit>
<trans-unit id="You can send messages to %@ from Archived contacts." xml:space="preserve">
<source>You can send messages to %@ from Archived contacts.</source>
<target>You can send messages to %@ from Archived contacts.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can set lock screen notification preview via settings." xml:space="preserve">
<source>You can set lock screen notification preview via settings.</source>
<target>You can set lock screen notification preview via settings.</target>
@@ -7356,6 +7517,11 @@ Repeat join request?</target>
<target>You can start chat via app Settings / Database or by restarting the app</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can still view conversation with %@ in the list of chats." xml:space="preserve">
<source>You can still view conversation with %@ in the list of chats.</source>
<target>You can still view conversation with %@ in the list of chats.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can turn on SimpleX Lock via Settings." xml:space="preserve">
<source>You can turn on SimpleX Lock via Settings.</source>
<target>You can turn on SimpleX Lock via Settings.</target>
@@ -7398,11 +7564,6 @@ Repeat connection request?</source>
Repeat connection request?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You have no chats" xml:space="preserve">
<source>You have no chats</source>
<target>You have no chats</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You have to enter passphrase every time the app starts - it is not stored on the device." xml:space="preserve">
<source>You have to enter passphrase every time the app starts - it is not stored on the device.</source>
<target>You have to enter passphrase every time the app starts - it is not stored on the device.</target>
@@ -7423,11 +7584,26 @@ Repeat connection request?</target>
<target>You joined this group. Connecting to inviting group member.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You may migrate the exported database." xml:space="preserve">
<source>You may migrate the exported database.</source>
<target>You may migrate the exported database.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You may save the exported archive." xml:space="preserve">
<source>You may save the exported archive.</source>
<target>You may save the exported archive.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="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." xml:space="preserve">
<source>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.</source>
<target>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.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You need to allow your contact to call to be able to call them." xml:space="preserve">
<source>You need to allow your contact to call to be able to call them.</source>
<target>You need to allow your contact to call to be able to call them.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You need to allow your contact to send voice messages to be able to send them." xml:space="preserve">
<source>You need to allow your contact to send voice messages to be able to send them.</source>
<target>You need to allow your contact to send voice messages to be able to send them.</target>
@@ -7543,13 +7719,6 @@ Repeat connection request?</target>
<target>Your chat profiles</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact needs to be online for the connection to complete.&#10;You can cancel this connection and remove the contact (and try later with a new link)." xml:space="preserve">
<source>Your contact needs to be online for the connection to complete.
You can cancel this connection and remove the contact (and try later with a new link).</source>
<target>Your contact needs to be online for the connection to complete.
You can cancel this connection and remove the contact (and try later with a new link).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
<target>Your contact sent a file that is larger than currently supported maximum size (%@).</target>
@@ -7742,6 +7911,11 @@ SimpleX servers cannot see your profile.</target>
<target>bold</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="call" xml:space="preserve">
<source>call</source>
<target>call</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="call error" xml:space="preserve">
<source>call error</source>
<target>call error</target>
@@ -8112,6 +8286,11 @@ SimpleX servers cannot see your profile.</target>
<target>invitation to group %@</target>
<note>group name</note>
</trans-unit>
<trans-unit id="invite" xml:space="preserve">
<source>invite</source>
<target>invite</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="invited" xml:space="preserve">
<source>invited</source>
<target>invited</target>
@@ -8167,6 +8346,11 @@ SimpleX servers cannot see your profile.</target>
<target>connected</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<target>message</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message received" xml:space="preserve">
<source>message received</source>
<target>message received</target>
@@ -8197,6 +8381,11 @@ SimpleX servers cannot see your profile.</target>
<target>months</target>
<note>time unit</note>
</trans-unit>
<trans-unit id="mute" xml:space="preserve">
<source>mute</source>
<target>mute</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="never" xml:space="preserve">
<source>never</source>
<target>never</target>
@@ -8329,6 +8518,11 @@ SimpleX servers cannot see your profile.</target>
<target>saved from %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="search" xml:space="preserve">
<source>search</source>
<target>search</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve">
<source>sec</source>
<target>sec</target>
@@ -8403,9 +8597,9 @@ last received msg: %2$@</target>
<target>unknown</target>
<note>connection info</note>
</trans-unit>
<trans-unit id="unknown relays" xml:space="preserve">
<source>unknown relays</source>
<target>unknown relays</target>
<trans-unit id="unknown servers" xml:space="preserve">
<source>unknown servers</source>
<target>unknown servers</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unknown status" xml:space="preserve">
@@ -8413,6 +8607,11 @@ last received msg: %2$@</target>
<target>unknown status</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unmute" xml:space="preserve">
<source>unmute</source>
<target>unmute</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unprotected" xml:space="preserve">
<source>unprotected</source>
<target>unprotected</target>
@@ -8458,6 +8657,11 @@ last received msg: %2$@</target>
<target>via relay</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="video" xml:space="preserve">
<source>video</source>
<target>video</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="video call (not e2e encrypted)" xml:space="preserve">
<source>video call (not e2e encrypted)</source>
<target>video call (not e2e encrypted)</target>
@@ -8721,11 +8925,6 @@ last received msg: %2$@</target>
<target>Database upgrade required</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Dismiss Sheet" xml:space="preserve">
<source>Dismiss Sheet</source>
<target>Dismiss Sheet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error preparing file" xml:space="preserve">
<source>Error preparing file</source>
<target>Error preparing file</target>
@@ -8756,11 +8955,6 @@ last received msg: %2$@</target>
<target>Invalid migration confirmation</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep Trying" xml:space="preserve">
<source>Keep Trying</source>
<target>Keep Trying</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keychain error" xml:space="preserve">
<source>Keychain error</source>
<target>Keychain error</target>
@@ -8776,11 +8970,6 @@ last received msg: %2$@</target>
<target>No active profile</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<target>No network connection</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<target>Ok</target>
@@ -8811,9 +9000,14 @@ last received msg: %2$@</target>
<target>Selected chat preferences prohibit this message.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sending File" xml:space="preserve">
<source>Sending File</source>
<target>Sending File</target>
<trans-unit id="Sending a message takes longer than expected." xml:space="preserve">
<source>Sending a message takes longer than expected.</source>
<target>Sending a message takes longer than expected.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sending message…" xml:space="preserve">
<source>Sending message…</source>
<target>Sending message…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share" xml:space="preserve">
@@ -8821,6 +9015,11 @@ last received msg: %2$@</target>
<target>Share</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Slow network?" xml:space="preserve">
<source>Slow network?</source>
<target>Slow network?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unknown database error: %@" xml:space="preserve">
<source>Unknown database error: %@</source>
<target>Unknown database error: %@</target>
@@ -8831,6 +9030,11 @@ last received msg: %2$@</target>
<target>Unsupported format</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wait" xml:space="preserve">
<source>Wait</source>
<target>Wait</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wrong database passphrase" xml:space="preserve">
<source>Wrong database passphrase</source>
<target>Wrong database passphrase</target>
File diff suppressed because it is too large Load Diff
@@ -537,7 +537,8 @@
<source>Accept</source>
<target>Hyväksy</target>
<note>accept contact request via notification
accept incoming call via notification</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept connection request?" xml:space="preserve">
<source>Accept connection request?</source>
@@ -552,7 +553,8 @@
<trans-unit id="Accept incognito" xml:space="preserve">
<source>Accept incognito</source>
<target>Hyväksy tuntematon</target>
<note>accept contact request via notification</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Acknowledged" xml:space="preserve">
<source>Acknowledged</source>
@@ -710,6 +712,10 @@
<target>Salli puhelut vain, jos kontaktisi sallii ne.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow calls?" xml:space="preserve">
<source>Allow calls?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow disappearing messages only if your contact allows it to you." xml:space="preserve">
<source>Allow disappearing messages only if your contact allows it to you.</source>
<target>Salli katoavat viestit vain, jos kontaktisi sallii sen sinulle.</target>
@@ -889,6 +895,14 @@
<source>Archive and upload</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archive contacts to chat later." xml:space="preserve">
<source>Archive contacts to chat later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archived contacts" xml:space="preserve">
<source>Archived contacts</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archiving database" xml:space="preserve">
<source>Archiving database</source>
<note>No comment provided by engineer.</note>
@@ -985,6 +999,10 @@
<target>Parempia viestejä</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better networking" xml:space="preserve">
<source>Better networking</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Black" xml:space="preserve">
<source>Black</source>
<note>No comment provided by engineer.</note>
@@ -1017,6 +1035,10 @@
<source>Blocked by admin</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Blur for better privacy." xml:space="preserve">
<source>Blur for better privacy.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Blur media" xml:space="preserve">
<source>Blur media</source>
<note>No comment provided by engineer.</note>
@@ -1065,10 +1087,22 @@
<target>Puhelut</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Calls prohibited!" xml:space="preserve">
<source>Calls prohibited!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Camera not available" xml:space="preserve">
<source>Camera not available</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't call contact" xml:space="preserve">
<source>Can't call contact</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't call member" xml:space="preserve">
<source>Can't call member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't invite contact!" xml:space="preserve">
<source>Can't invite contact!</source>
<target>Kontaktia ei voi kutsua!</target>
@@ -1079,6 +1113,10 @@
<target>Kontakteja ei voi kutsua!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't message member" xml:space="preserve">
<source>Can't message member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cancel" xml:space="preserve">
<source>Cancel</source>
<target>Peruuta</target>
@@ -1185,6 +1223,10 @@
<target>Chat-tietokanta poistettu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database exported" xml:space="preserve">
<source>Chat database exported</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database imported" xml:space="preserve">
<source>Chat database imported</source>
<target>Chat-tietokanta tuotu</target>
@@ -1204,6 +1246,10 @@
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat list" xml:space="preserve">
<source>Chat list</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat migrated!" xml:space="preserve">
<source>Chat migrated!</source>
<note>No comment provided by engineer.</note>
@@ -1261,7 +1307,7 @@
<trans-unit id="Clear" xml:space="preserve">
<source>Clear</source>
<target>Tyhjennä</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Clear conversation" xml:space="preserve">
<source>Clear conversation</source>
@@ -1282,6 +1328,10 @@
<target>Tyhjennä vahvistus</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Color chats with the new themes." xml:space="preserve">
<source>Color chats with the new themes.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Color mode" xml:space="preserve">
<source>Color mode</source>
<note>No comment provided by engineer.</note>
@@ -1319,6 +1369,10 @@
<target>Vahvista pääsykoodi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm contact deletion?" xml:space="preserve">
<source>Confirm contact deletion?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm database upgrades" xml:space="preserve">
<source>Confirm database upgrades</source>
<target>Vahvista tietokannan päivitykset</target>
@@ -1368,6 +1422,10 @@
<source>Connect to desktop</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect to your friends faster." xml:space="preserve">
<source>Connect to your friends faster.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect to yourself?" xml:space="preserve">
<source>Connect to yourself?</source>
<note>No comment provided by engineer.</note>
@@ -1430,6 +1488,10 @@ This is your own one-time link!</source>
<target>Yhteyden muodostaminen palvelimeen... (virhe: %@)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting to contact, please wait or check later!" xml:space="preserve">
<source>Connecting to contact, please wait or check later!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting to desktop" xml:space="preserve">
<source>Connecting to desktop</source>
<note>No comment provided by engineer.</note>
@@ -1439,6 +1501,10 @@ This is your own one-time link!</source>
<target>Yhteys</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connection and servers status." xml:space="preserve">
<source>Connection and servers status.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connection error" xml:space="preserve">
<source>Connection error</source>
<target>Yhteysvirhe</target>
@@ -1485,6 +1551,10 @@ This is your own one-time link!</source>
<target>Kontakti on jo olemassa</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact deleted!" xml:space="preserve">
<source>Contact deleted!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact hidden:" xml:space="preserve">
<source>Contact hidden:</source>
<target>Kontakti piilotettu:</target>
@@ -1495,9 +1565,8 @@ This is your own one-time link!</source>
<target>Kontakti on yhdistetty</target>
<note>notification</note>
</trans-unit>
<trans-unit id="Contact is not connected yet!" xml:space="preserve">
<source>Contact is not connected yet!</source>
<target>Kontaktia ei ole vielä yhdistetty!</target>
<trans-unit id="Contact is deleted." xml:space="preserve">
<source>Contact is deleted.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact name" xml:space="preserve">
@@ -1510,6 +1579,10 @@ This is your own one-time link!</source>
<target>Kontaktin asetukset</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact will be deleted - this cannot be undone!" xml:space="preserve">
<source>Contact will be deleted - this cannot be undone!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contacts" xml:space="preserve">
<source>Contacts</source>
<target>Kontaktit</target>
@@ -1525,6 +1598,10 @@ This is your own one-time link!</source>
<target>Jatka</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Conversation deleted!" xml:space="preserve">
<source>Conversation deleted!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy" xml:space="preserve">
<source>Copy</source>
<target>Kopioi</target>
@@ -1782,7 +1859,8 @@ This is your own one-time link!</source>
<trans-unit id="Delete" xml:space="preserve">
<source>Delete</source>
<target>Poista</target>
<note>chat item action</note>
<note>chat item action
swipe action</note>
</trans-unit>
<trans-unit id="Delete %lld messages of members?" xml:space="preserve">
<source>Delete %lld messages of members?</source>
@@ -1792,11 +1870,6 @@ This is your own one-time link!</source>
<source>Delete %lld messages?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete Contact" xml:space="preserve">
<source>Delete Contact</source>
<target>Poista kontakti</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete address" xml:space="preserve">
<source>Delete address</source>
<target>Poista osoite</target>
@@ -1851,9 +1924,8 @@ This is your own one-time link!</source>
<target>Poista kontakti</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete contact?&#10;This cannot be undone!" xml:space="preserve">
<source>Delete contact?
This cannot be undone!</source>
<trans-unit id="Delete contact?" xml:space="preserve">
<source>Delete contact?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete database" xml:space="preserve">
@@ -1945,11 +2017,6 @@ This cannot be undone!</source>
<target>Poista vanha tietokanta?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete pending connection" xml:space="preserve">
<source>Delete pending connection</source>
<target>Poista vireillä oleva yhteys</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete pending connection?" xml:space="preserve">
<source>Delete pending connection?</source>
<target>Poistetaanko odottava yhteys?</target>
@@ -1965,11 +2032,19 @@ This cannot be undone!</source>
<target>Poista jono</target>
<note>server test step</note>
</trans-unit>
<trans-unit id="Delete up to 20 messages at once." xml:space="preserve">
<source>Delete up to 20 messages at once.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete user profile?" xml:space="preserve">
<source>Delete user profile?</source>
<target>Poista käyttäjäprofiili?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete without notification" xml:space="preserve">
<source>Delete without notification</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Deleted" xml:space="preserve">
<source>Deleted</source>
<note>No comment provided by engineer.</note>
@@ -2045,6 +2120,10 @@ This cannot be undone!</source>
<target>Kehitä</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Developer options" xml:space="preserve">
<source>Developer options</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Developer tools" xml:space="preserve">
<source>Developer tools</source>
<target>Kehittäjätyökalut</target>
@@ -2527,11 +2606,6 @@ This cannot be undone!</source>
<target>Virhe yhteyden poistamisessa</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting contact" xml:space="preserve">
<source>Error deleting contact</source>
<target>Virhe kontaktin poistamisessa</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting database" xml:space="preserve">
<source>Error deleting database</source>
<target>Virhe tietokannan poistamisessa</target>
@@ -2810,7 +2884,7 @@ This cannot be undone!</source>
<trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source>
<target>Suosikki</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="File error" xml:space="preserve">
<source>File error</source>
@@ -3520,6 +3594,10 @@ Error: %2$@</source>
3. Yhteys vaarantui.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It protects your IP address and connections." xml:space="preserve">
<source>It protects your IP address and connections.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It seems like you are already connected via this link. If it is not the case, there was an error (%@)." xml:space="preserve">
<source>It seems like you are already connected via this link. If it is not the case, there was an error (%@).</source>
<target>Näyttäisi, että olet jo yhteydessä tämän linkin kautta. Jos näin ei ole, tapahtui virhe (%@).</target>
@@ -3538,7 +3616,7 @@ Error: %2$@</source>
<trans-unit id="Join" xml:space="preserve">
<source>Join</source>
<target>Liity</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Join group" xml:space="preserve">
<source>Join group</source>
@@ -3576,6 +3654,10 @@ This is your link for group %@!</source>
<source>Keep</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep conversation" xml:space="preserve">
<source>Keep conversation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
<source>Keep the app open to use it from desktop</source>
<note>No comment provided by engineer.</note>
@@ -3617,7 +3699,7 @@ This is your link for group %@!</source>
<trans-unit id="Leave" xml:space="preserve">
<source>Leave</source>
<target>Poistu</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Leave group" xml:space="preserve">
<source>Leave group</source>
@@ -3746,6 +3828,10 @@ This is your link for group %@!</source>
<target>Enintään 30 sekuntia, vastaanotetaan välittömästi.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Media &amp; file servers" xml:space="preserve">
<source>Media &amp; file servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Medium" xml:space="preserve">
<source>Medium</source>
<note>blur media</note>
@@ -3828,12 +3914,8 @@ This is your link for group %@!</source>
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing mode" xml:space="preserve">
<source>Message routing mode</source>
<trans-unit id="Message servers" xml:space="preserve">
<source>Message servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message source remains private." xml:space="preserve">
@@ -3977,7 +4059,7 @@ This is your link for group %@!</source>
<trans-unit id="Mute" xml:space="preserve">
<source>Mute</source>
<target>Mykistä</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Muted when inactive!" xml:space="preserve">
<source>Muted when inactive!</source>
@@ -3987,7 +4069,7 @@ This is your link for group %@!</source>
<trans-unit id="Name" xml:space="preserve">
<source>Name</source>
<target>Nimi</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Network &amp; servers" xml:space="preserve">
<source>Network &amp; servers</source>
@@ -4025,6 +4107,10 @@ This is your link for group %@!</source>
<source>New chat</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat experience 🎉" xml:space="preserve">
<source>New chat experience 🎉</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New contact request" xml:space="preserve">
<source>New contact request</source>
<target>Uusi kontaktipyyntö</target>
@@ -4054,6 +4140,10 @@ This is your link for group %@!</source>
<target>Uutta %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New media options" xml:space="preserve">
<source>New media options</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member role" xml:space="preserve">
<source>New member role</source>
<target>Uusi jäsenrooli</target>
@@ -4192,14 +4282,18 @@ This is your link for group %@!</source>
<target>Kertakutsulinkki</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be required for connection. Requires enabling VPN." xml:space="preserve">
<source>Onion hosts will be required for connection. Requires enabling VPN.</source>
<target>Yhteyden muodostamiseen tarvitaan Onion-isäntiä. Edellyttää VPN:n sallimista.</target>
<trans-unit id="Onion hosts will be **required** for connection.&#10;Requires compatible VPN." xml:space="preserve">
<source>Onion hosts will be **required** for connection.
Requires compatible VPN.</source>
<target>Yhteyden muodostamiseen tarvitaan Onion-isäntiä.
Edellyttää VPN:n sallimista.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be used when available. Requires enabling VPN." xml:space="preserve">
<source>Onion hosts will be used when available. Requires enabling VPN.</source>
<target>Onion-isäntiä käytetään, kun niitä on saatavilla. Edellyttää VPN:n sallimista.</target>
<trans-unit id="Onion hosts will be used when available.&#10;Requires compatible VPN." xml:space="preserve">
<source>Onion hosts will be used when available.
Requires compatible VPN.</source>
<target>Onion-isäntiä käytetään, kun niitä on saatavilla.
Edellyttää VPN:n sallimista.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will not be used." xml:space="preserve">
@@ -4212,6 +4306,10 @@ This is your link for group %@!</source>
<target>Vain asiakaslaitteet tallentavat käyttäjäprofiileja, yhteystietoja, ryhmiä ja viestejä, jotka on lähetetty **kaksinkertaisella päästä päähän -salauksella**.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only delete conversation" xml:space="preserve">
<source>Only delete conversation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
<source>Only group owners can change group preferences.</source>
<target>Vain ryhmän omistajat voivat muuttaa ryhmän asetuksia.</target>
@@ -4430,6 +4528,14 @@ This is your link for group %@!</source>
<source>Picture-in-picture calls</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Play from the chat list." xml:space="preserve">
<source>Play from the chat list.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please ask your contact to enable calls." xml:space="preserve">
<source>Please ask your contact to enable calls.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please ask your contact to enable sending voice messages." xml:space="preserve">
<source>Please ask your contact to enable sending voice messages.</source>
<target>Pyydä kontaktiasi sallimaan ääniviestien lähettäminen.</target>
@@ -4708,6 +4814,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Arvioi sovellus</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reachable chat toolbar" xml:space="preserve">
<source>Reachable chat toolbar</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="React…" xml:space="preserve">
<source>React…</source>
<target>Reagoi…</target>
@@ -4716,7 +4826,7 @@ Enable in *Network &amp; servers* settings.</source>
<trans-unit id="Read" xml:space="preserve">
<source>Read</source>
<target>Lue</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Read more" xml:space="preserve">
<source>Read more</source>
@@ -4864,7 +4974,8 @@ Enable in *Network &amp; servers* settings.</source>
<trans-unit id="Reject" xml:space="preserve">
<source>Reject</source>
<target>Hylkää</target>
<note>reject incoming call via notification</note>
<note>reject incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Reject (sender NOT notified)" xml:space="preserve">
<source>Reject (sender NOT notified)</source>
@@ -4960,6 +5071,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Oletustilaan</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all hints" xml:space="preserve">
<source>Reset all hints</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all statistics" xml:space="preserve">
<source>Reset all statistics</source>
<note>No comment provided by engineer.</note>
@@ -5025,11 +5140,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>Paljasta</target>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Revert" xml:space="preserve">
<source>Revert</source>
<target>Palauta</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>Peruuta</target>
@@ -5059,11 +5169,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>SMP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SMP servers" xml:space="preserve">
<source>SMP servers</source>
<target>SMP-palvelimet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safely receive files" xml:space="preserve">
<source>Safely receive files</source>
<note>No comment provided by engineer.</note>
@@ -5092,6 +5197,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Tallenna ja ilmoita ryhmän jäsenille</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save and reconnect" xml:space="preserve">
<source>Save and reconnect</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save and update group profile" xml:space="preserve">
<source>Save and update group profile</source>
<target>Tallenna ja päivitä ryhmäprofiili</target>
@@ -5285,11 +5394,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>Lähetä toimituskuittaukset vastaanottajalle</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send direct message" xml:space="preserve">
<source>Send direct message</source>
<target>Lähetä yksityisviesti</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send direct message to connect" xml:space="preserve">
<source>Send direct message to connect</source>
<note>No comment provided by engineer.</note>
@@ -5313,6 +5417,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Lähetä live-viesti</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send message to enable calls." xml:space="preserve">
<source>Send message to enable calls.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when IP address is protected and your or destination server does not support private routing." xml:space="preserve">
<source>Send messages directly when IP address is protected and your or destination server does not support private routing.</source>
<note>No comment provided by engineer.</note>
@@ -5578,6 +5686,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Jaa osoite kontakteille?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share from other apps." xml:space="preserve">
<source>Share from other apps.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share link" xml:space="preserve">
<source>Share link</source>
<target>Jaa linkki</target>
@@ -5736,11 +5848,19 @@ Enable in *Network &amp; servers* settings.</source>
<source>Soft</source>
<note>blur media</note>
</trans-unit>
<trans-unit id="Some file(s) were not exported:" xml:space="preserve">
<source>Some file(s) were not exported:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some non-fatal errors occurred during import - you may see Chat console for more details." xml:space="preserve">
<source>Some non-fatal errors occurred during import - you may see Chat console for more details.</source>
<target>Tuonnin aikana tapahtui joitakin ei-vakavia virheitä – saatat nähdä Chat-konsolissa lisätietoja.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some non-fatal errors occurred during import:" xml:space="preserve">
<source>Some non-fatal errors occurred during import:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Somebody" xml:space="preserve">
<source>Somebody</source>
<target>Joku</target>
@@ -5866,6 +5986,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Järjestelmän todennus</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="TCP connection" xml:space="preserve">
<source>TCP connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="TCP connection timeout" xml:space="preserve">
<source>TCP connection timeout</source>
<target>TCP-yhteyden aikakatkaisu</target>
@@ -5923,11 +6047,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Tap to scan</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Tap to start a new chat" xml:space="preserve">
<source>Tap to start a new chat</source>
<target>Aloita uusi keskustelu napauttamalla</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Temporary file error" xml:space="preserve">
<source>Temporary file error</source>
<note>No comment provided by engineer.</note>
@@ -6210,10 +6329,18 @@ Sinua kehotetaan suorittamaan todennus loppuun, ennen kuin tämä ominaisuus ote
<target>Voit tarkistaa päästä päähän -salauksen kontaktisi kanssa vertaamalla (tai skannaamalla) laitteidenne koodia.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toggle chat list:" xml:space="preserve">
<source>Toggle chat list:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toggle incognito when connecting." xml:space="preserve">
<source>Toggle incognito when connecting.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toolbar opacity" xml:space="preserve">
<source>Toolbar opacity</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Total" xml:space="preserve">
<source>Total</source>
<note>No comment provided by engineer.</note>
@@ -6284,7 +6411,7 @@ Sinua kehotetaan suorittamaan todennus loppuun, ennen kuin tämä ominaisuus ote
<trans-unit id="Unfav." xml:space="preserve">
<source>Unfav.</source>
<target>Epäsuotuisa.</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Unhide" xml:space="preserve">
<source>Unhide</source>
@@ -6358,12 +6485,12 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
<trans-unit id="Unmute" xml:space="preserve">
<source>Unmute</source>
<target>Poista mykistys</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Unread" xml:space="preserve">
<source>Unread</source>
<target>Lukematon</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Up to 100 last messages are sent to new members." xml:space="preserve">
<source>Up to 100 last messages are sent to new members.</source>
@@ -6374,11 +6501,6 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
<target>Päivitä</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update .onion hosts setting?" xml:space="preserve">
<source>Update .onion hosts setting?</source>
<target>Päivitä .onion-isäntien asetus?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update database passphrase" xml:space="preserve">
<source>Update database passphrase</source>
<target>Päivitä tietokannan tunnuslause</target>
@@ -6389,9 +6511,8 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
<target>Päivitä verkkoasetukset?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update transport isolation mode?" xml:space="preserve">
<source>Update transport isolation mode?</source>
<target>Päivitä kuljetuksen eristystila?</target>
<trans-unit id="Update settings?" xml:space="preserve">
<source>Update settings?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Updating settings will re-connect the client to all servers." xml:space="preserve">
@@ -6399,11 +6520,6 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
<target>Asetusten päivittäminen yhdistää asiakkaan uudelleen kaikkiin palvelimiin.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Updating this setting will re-connect the client to all servers." xml:space="preserve">
<source>Updating this setting will re-connect the client to all servers.</source>
<target>Tämän asetuksen päivittäminen yhdistää asiakkaan uudelleen kaikkiin palvelimiin.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Upgrade and open chat" xml:space="preserve">
<source>Upgrade and open chat</source>
<target>Päivitä ja avaa keskustelu</target>
@@ -6494,6 +6610,10 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
<source>Use the app while in the call.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use the app with one hand." xml:space="preserve">
<source>Use the app with one hand.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>Käyttäjäprofiili</target>
@@ -6503,11 +6623,6 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
<source>User selection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using .onion hosts requires compatible VPN provider." xml:space="preserve">
<source>Using .onion hosts requires compatible VPN provider.</source>
<target>.onion-isäntien käyttäminen vaatii yhteensopivan VPN-palveluntarjoajan.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using SimpleX Chat servers." xml:space="preserve">
<source>Using SimpleX Chat servers.</source>
<target>Käyttää SimpleX Chat -palvelimia.</target>
@@ -6744,11 +6859,6 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
<source>XFTP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="XFTP servers" xml:space="preserve">
<source>XFTP servers</source>
<target>XFTP-palvelimet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>Sinä</target>
@@ -6826,6 +6936,10 @@ Repeat join request?</source>
<target>Voit vastaanottaa puheluita lukitusnäytöltä ilman laitteen ja sovelluksen todennusta.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can change it in Appearance settings." xml:space="preserve">
<source>You can change it in Appearance settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can create it later" xml:space="preserve">
<source>You can create it later</source>
<target>Voit luoda sen myöhemmin</target>
@@ -6859,6 +6973,10 @@ Repeat join request?</source>
<target>Voit nyt lähettää viestejä %@:lle</target>
<note>notification body</note>
</trans-unit>
<trans-unit id="You can send messages to %@ from Archived contacts." xml:space="preserve">
<source>You can send messages to %@ from Archived contacts.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can set lock screen notification preview via settings." xml:space="preserve">
<source>You can set lock screen notification preview via settings.</source>
<target>Voit määrittää lukitusnäytön ilmoituksen esikatselun asetuksista.</target>
@@ -6884,6 +7002,10 @@ Repeat join request?</source>
<target>Voit aloittaa keskustelun sovelluksen Asetukset / Tietokanta kautta tai käynnistämällä sovelluksen uudelleen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can still view conversation with %@ in the list of chats." xml:space="preserve">
<source>You can still view conversation with %@ in the list of chats.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can turn on SimpleX Lock via Settings." xml:space="preserve">
<source>You can turn on SimpleX Lock via Settings.</source>
<target>Voit ottaa SimpleX Lockin käyttöön Asetusten kautta.</target>
@@ -6922,11 +7044,6 @@ Repeat join request?</source>
Repeat connection request?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You have no chats" xml:space="preserve">
<source>You have no chats</source>
<target>Sinulla ei ole keskusteluja</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You have to enter passphrase every time the app starts - it is not stored on the device." xml:space="preserve">
<source>You have to enter passphrase every time the app starts - it is not stored on the device.</source>
<target>Sinun on annettava tunnuslause aina, kun sovellus käynnistyy - sitä ei tallenneta laitteeseen.</target>
@@ -6947,11 +7064,23 @@ Repeat connection request?</source>
<target>Liityit tähän ryhmään. Muodostetaan yhteyttä ryhmän jäsenten kutsumiseksi.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You may migrate the exported database." xml:space="preserve">
<source>You may migrate the exported database.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You may save the exported archive." xml:space="preserve">
<source>You may save the exported archive.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="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." xml:space="preserve">
<source>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.</source>
<target>Sinun tulee käyttää keskustelujen-tietokannan uusinta versiota AINOSTAAN yhdessä laitteessa, muuten saatat lakata vastaanottamasta viestejä joiltakin kontakteilta.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You need to allow your contact to call to be able to call them." xml:space="preserve">
<source>You need to allow your contact to call to be able to call them.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You need to allow your contact to send voice messages to be able to send them." xml:space="preserve">
<source>You need to allow your contact to send voice messages to be able to send them.</source>
<target>Sinun on sallittava kontaktiesi lähettää ääniviestejä, jotta voit lähettää niitä.</target>
@@ -7065,13 +7194,6 @@ Repeat connection request?</source>
<target>Keskusteluprofiilisi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact needs to be online for the connection to complete.&#10;You can cancel this connection and remove the contact (and try later with a new link)." xml:space="preserve">
<source>Your contact needs to be online for the connection to complete.
You can cancel this connection and remove the contact (and try later with a new link).</source>
<target>Kontaktin tulee olla online-tilassa, jotta yhteys voidaan muodostaa.
Voit peruuttaa tämän yhteyden ja poistaa kontaktin (ja yrittää myöhemmin uudella linkillä).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
<target>Yhteyshenkilösi lähetti tiedoston, joka on suurempi kuin tällä hetkellä tuettu enimmäiskoko (%@).</target>
@@ -7255,6 +7377,10 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>lihavoitu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="call" xml:space="preserve">
<source>call</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="call error" xml:space="preserve">
<source>call error</source>
<target>soittovirhe</target>
@@ -7617,6 +7743,10 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>kutsu ryhmään %@</target>
<note>group name</note>
</trans-unit>
<trans-unit id="invite" xml:space="preserve">
<source>invite</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="invited" xml:space="preserve">
<source>invited</source>
<target>kutsuttu</target>
@@ -7671,6 +7801,10 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>yhdistetty</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message received" xml:space="preserve">
<source>message received</source>
<target>viesti vastaanotettu</target>
@@ -7701,6 +7835,10 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>kuukautta</target>
<note>time unit</note>
</trans-unit>
<trans-unit id="mute" xml:space="preserve">
<source>mute</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="never" xml:space="preserve">
<source>never</source>
<target>ei koskaan</target>
@@ -7825,6 +7963,10 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="search" xml:space="preserve">
<source>search</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve">
<source>sec</source>
<target>sek</target>
@@ -7891,14 +8033,18 @@ last received msg: %2$@</source>
<target>tuntematon</target>
<note>connection info</note>
</trans-unit>
<trans-unit id="unknown relays" xml:space="preserve">
<source>unknown relays</source>
<trans-unit id="unknown servers" xml:space="preserve">
<source>unknown servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unknown status" xml:space="preserve">
<source>unknown status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unmute" xml:space="preserve">
<source>unmute</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unprotected" xml:space="preserve">
<source>unprotected</source>
<note>No comment provided by engineer.</note>
@@ -7941,6 +8087,10 @@ last received msg: %2$@</source>
<target>releellä</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="video" xml:space="preserve">
<source>video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="video call (not e2e encrypted)" xml:space="preserve">
<source>video call (not e2e encrypted)</source>
<target>videopuhelu (ei e2e-salattu)</target>
@@ -8183,10 +8333,6 @@ last received msg: %2$@</source>
<source>Database upgrade required</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Dismiss Sheet" xml:space="preserve">
<source>Dismiss Sheet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error preparing file" xml:space="preserve">
<source>Error preparing file</source>
<note>No comment provided by engineer.</note>
@@ -8211,10 +8357,6 @@ last received msg: %2$@</source>
<source>Invalid migration confirmation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep Trying" xml:space="preserve">
<source>Keep Trying</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keychain error" xml:space="preserve">
<source>Keychain error</source>
<note>No comment provided by engineer.</note>
@@ -8227,10 +8369,6 @@ last received msg: %2$@</source>
<source>No active profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<note>No comment provided by engineer.</note>
@@ -8255,14 +8393,22 @@ last received msg: %2$@</source>
<source>Selected chat preferences prohibit this message.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sending File" xml:space="preserve">
<source>Sending File</source>
<trans-unit id="Sending a message takes longer than expected." xml:space="preserve">
<source>Sending a message takes longer than expected.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sending message…" xml:space="preserve">
<source>Sending message…</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share" xml:space="preserve">
<source>Share</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Slow network?" xml:space="preserve">
<source>Slow network?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unknown database error: %@" xml:space="preserve">
<source>Unknown database error: %@</source>
<note>No comment provided by engineer.</note>
@@ -8271,6 +8417,10 @@ last received msg: %2$@</source>
<source>Unsupported format</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wait" xml:space="preserve">
<source>Wait</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wrong database passphrase" xml:space="preserve">
<source>Wrong database passphrase</source>
<note>No comment provided by engineer.</note>
File diff suppressed because it is too large Load Diff
@@ -5316,6 +5316,278 @@ SimpleX servers cannot see your profile.</source>
<target state="translated">%@ ו-%@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect automatically" xml:space="preserve" approved="no">
<source>Connect automatically</source>
<target state="translated">התבר אוטומטי</target>
</trans-unit>
<trans-unit id="Create profile" xml:space="preserve" approved="no">
<source>Create profile</source>
<target state="translated">צור פרופיל</target>
</trans-unit>
<trans-unit id="Created at: %@" xml:space="preserve" approved="no">
<source>Created at: %@</source>
<target state="translated">נוצר ב:%@</target>
</trans-unit>
<trans-unit id="Desktop devices" xml:space="preserve" approved="no">
<source>Desktop devices</source>
<target state="translated">מכשירי מחשב</target>
</trans-unit>
<trans-unit id="Discover via local network" xml:space="preserve" approved="no">
<source>Discover via local network</source>
<target state="translated">גלה באמצעות הרשת המקומית</target>
</trans-unit>
<trans-unit id="Forward" xml:space="preserve" approved="no">
<source>Forward</source>
<target state="translated">העבר</target>
</trans-unit>
<trans-unit id="Group already exists" xml:space="preserve" approved="no">
<source>Group already exists</source>
<target state="translated">קבוצה כבר קיימת</target>
</trans-unit>
<trans-unit id="Connected to desktop" xml:space="preserve" approved="no">
<source>Connected to desktop</source>
<target state="translated">מחובר למחשב</target>
</trans-unit>
<trans-unit id="Group already exists!" xml:space="preserve" approved="no">
<source>Group already exists!</source>
<target state="translated">קבוצה כבר קיימת!</target>
</trans-unit>
<trans-unit id="Confirm upload" xml:space="preserve" approved="no">
<source>Confirm upload</source>
<target state="translated">אשר ההעלאה</target>
</trans-unit>
<trans-unit id="Block for all" xml:space="preserve" approved="no">
<source>Block for all</source>
<target state="translated">חסום לכולם</target>
</trans-unit>
<trans-unit id="Blocked by admin" xml:space="preserve" approved="no">
<source>Blocked by admin</source>
<target state="translated">נחסם ע"י מנהל</target>
</trans-unit>
<trans-unit id="Block member for all?" xml:space="preserve" approved="no">
<source>Block member for all?</source>
<target state="translated">לחסום את החבר לכולם?</target>
</trans-unit>
<trans-unit id="Camera not available" xml:space="preserve" approved="no">
<source>Camera not available</source>
<target state="translated">מצלמה לא זמינה</target>
</trans-unit>
<trans-unit id="Connect to desktop" xml:space="preserve" approved="no">
<source>Connect to desktop</source>
<target state="translated">חבר למחשב</target>
</trans-unit>
<trans-unit id="Created at" xml:space="preserve" approved="no">
<source>Created at</source>
<target state="translated">נוצר ב</target>
</trans-unit>
<trans-unit id="(new)" xml:space="preserve" approved="no">
<source>(new)</source>
<target state="translated">(חדש)</target>
</trans-unit>
<trans-unit id="Block member" xml:space="preserve" approved="no">
<source>Block member</source>
<target state="translated">חבר חסום</target>
</trans-unit>
<trans-unit id="Block member?" xml:space="preserve" approved="no">
<source>Block member?</source>
<target state="translated">לחסום חבר?</target>
</trans-unit>
<trans-unit id="Creating link…" xml:space="preserve" approved="no">
<source>Creating link…</source>
<target state="translated">יוצר קישור…</target>
</trans-unit>
<trans-unit id="Files" xml:space="preserve" approved="no">
<source>Files</source>
<target state="translated">קבצים</target>
</trans-unit>
<trans-unit id="Disabled" xml:space="preserve" approved="no">
<source>Disabled</source>
<target state="translated">מושבת</target>
</trans-unit>
<trans-unit id="Enter passphrase" xml:space="preserve" approved="no">
<source>Enter passphrase</source>
<target state="translated">הכנס סיסמא</target>
</trans-unit>
<trans-unit id="Apply" xml:space="preserve" approved="no">
<source>Apply</source>
<target state="translated">החל</target>
</trans-unit>
<trans-unit id="Apply to" xml:space="preserve" approved="no">
<source>Apply to</source>
<target state="translated">החל ל</target>
</trans-unit>
<trans-unit id="Background" xml:space="preserve" approved="no">
<source>Background</source>
<target state="translated">ברקע</target>
</trans-unit>
<trans-unit id="Black" xml:space="preserve" approved="no">
<source>Black</source>
<target state="translated">שחור</target>
</trans-unit>
<trans-unit id="Blur media" xml:space="preserve" approved="no">
<source>Blur media</source>
<target state="translated">טשטש מדיה</target>
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve" approved="no">
<source>Chat theme</source>
<target state="translated">צבע ערכת נושא</target>
</trans-unit>
<trans-unit id="Completed" xml:space="preserve" approved="no">
<source>Completed</source>
<target state="translated">הושלם</target>
</trans-unit>
<trans-unit id="Connected" xml:space="preserve" approved="no">
<source>Connected</source>
<target state="translated">מחובר</target>
</trans-unit>
<trans-unit id="Connection notifications" xml:space="preserve" approved="no">
<source>Connection notifications</source>
<target state="translated">התראות חיבור</target>
</trans-unit>
<trans-unit id="Connections" xml:space="preserve" approved="no">
<source>Connections</source>
<target state="translated">חיבורים</target>
</trans-unit>
<trans-unit id="Current profile" xml:space="preserve" approved="no">
<source>Current profile</source>
<target state="translated">פרופיל נוכחי</target>
</trans-unit>
<trans-unit id="Disconnect desktop?" xml:space="preserve" approved="no">
<source>Disconnect desktop?</source>
<target state="translated">להתנתק מהמחשב?</target>
</trans-unit>
<trans-unit id="Discover and join groups" xml:space="preserve" approved="no">
<source>Discover and join groups</source>
<target state="translated">גלה והצטרף לקבוצות</target>
</trans-unit>
<trans-unit id="Enabled" xml:space="preserve" approved="no">
<source>Enabled</source>
<target state="translated">מופעל</target>
</trans-unit>
<trans-unit id="Error opening chat" xml:space="preserve" approved="no">
<source>Error opening chat</source>
<target state="translated">שגיאה בפתיחת הצ'אט</target>
</trans-unit>
<trans-unit id="Good morning!" xml:space="preserve" approved="no">
<source>Good morning!</source>
<target state="translated">בוקר טוב!</target>
</trans-unit>
<trans-unit id="Connect to yourself?&#10;This is your own SimpleX address!" xml:space="preserve" approved="no">
<source>Connect to yourself?
This is your own SimpleX address!</source>
<target state="translated">להתחבר אליך?
זו כתובת הSimpleX שלך!</target>
</trans-unit>
<trans-unit id="Connect to yourself?" xml:space="preserve" approved="no">
<source>Connect to yourself?</source>
<target state="translated">להתחבר אליך?</target>
</trans-unit>
<trans-unit id="Connect to yourself?&#10;This is your own one-time link!" xml:space="preserve" approved="no">
<source>Connect to yourself?
This is your own one-time link!</source>
<target state="translated">להתחבר אליך?
זו כתובת ההזמנה החד-פעמי שלך!</target>
</trans-unit>
<trans-unit id="Connected desktop" xml:space="preserve" approved="no">
<source>Connected desktop</source>
<target state="translated">מחשב מחובר</target>
</trans-unit>
<trans-unit id="Connected servers" xml:space="preserve" approved="no">
<source>Connected servers</source>
<target state="translated">שרתים מחוברים</target>
</trans-unit>
<trans-unit id="Enter group name…" xml:space="preserve" approved="no">
<source>Enter group name…</source>
<target state="translated">הכנס שם לקבוצה…</target>
</trans-unit>
<trans-unit id="Enter this device name…" xml:space="preserve" approved="no">
<source>Enter this device name…</source>
<target state="translated">הכנס שם למכשיר הזה…</target>
</trans-unit>
<trans-unit id="Enter your name…" xml:space="preserve" approved="no">
<source>Enter your name…</source>
<target state="translated">הכנס את השם שלך…</target>
</trans-unit>
<trans-unit id="Error decrypting file" xml:space="preserve" approved="no">
<source>Error decrypting file</source>
<target state="translated">שגיאה בפענוח הקובץ</target>
</trans-unit>
<trans-unit id="Errors" xml:space="preserve" approved="no">
<source>Errors</source>
<target state="translated">שגיאות</target>
</trans-unit>
<trans-unit id="File status" xml:space="preserve" approved="no">
<source>File status</source>
<target state="translated">מצב הקובץ</target>
</trans-unit>
<trans-unit id="Connecting" xml:space="preserve" approved="no">
<source>Connecting</source>
<target state="translated">מתחבר</target>
</trans-unit>
<trans-unit id="Connecting to desktop" xml:space="preserve" approved="no">
<source>Connecting to desktop</source>
<target state="translated">מתחבר למחשב</target>
</trans-unit>
<trans-unit id="Deleted" xml:space="preserve" approved="no">
<source>Deleted</source>
<target state="translated">נמחק</target>
</trans-unit>
<trans-unit id="Deletion errors" xml:space="preserve" approved="no">
<source>Deletion errors</source>
<target state="translated">שגיאות במחיקה</target>
</trans-unit>
<trans-unit id="Details" xml:space="preserve" approved="no">
<source>Details</source>
<target state="translated">פרטים</target>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve" approved="no">
<source>Forwarded</source>
<target state="translated">הועבר</target>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve" approved="no">
<source>Found desktop</source>
<target state="translated">נמצא מחשב</target>
</trans-unit>
<trans-unit id="Good afternoon!" xml:space="preserve" approved="no">
<source>Good afternoon!</source>
<target state="translated">אחר צהריים טובים!</target>
</trans-unit>
<trans-unit id="Desktop address" xml:space="preserve" approved="no">
<source>Desktop address</source>
<target state="translated">כתובת מחשב</target>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve" approved="no">
<source>Forwarded from</source>
<target state="translated">הועבר מ</target>
</trans-unit>
<trans-unit id="History is not sent to new members." xml:space="preserve" approved="no">
<source>History is not sent to new members.</source>
<target state="translated">היסטוריה לא נשלחת לחברים חדשים.</target>
</trans-unit>
<trans-unit id="Created" xml:space="preserve" approved="no">
<source>Created</source>
<target state="translated">נוצר</target>
</trans-unit>
<trans-unit id="Copy error" xml:space="preserve" approved="no">
<source>Copy error</source>
<target state="translated">שגיאת העתקה</target>
</trans-unit>
<trans-unit id="Create group" xml:space="preserve" approved="no">
<source>Create group</source>
<target state="translated">צור קבוצה</target>
</trans-unit>
<trans-unit id="Enabled for" xml:space="preserve" approved="no">
<source>Enabled for</source>
<target state="translated">מופעל עבור</target>
</trans-unit>
<trans-unit id="Error creating message" xml:space="preserve" approved="no">
<source>Error creating message</source>
<target state="translated">שגיאה ביצירת הודעה</target>
</trans-unit>
<trans-unit id="File error" xml:space="preserve" approved="no">
<source>File error</source>
<target state="translated">שגיאה בקובץ</target>
</trans-unit>
</body>
</file>
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="he" datatype="plaintext">
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -554,7 +554,8 @@
<source>Accept</source>
<target>承諾</target>
<note>accept contact request via notification
accept incoming call via notification</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept connection request?" xml:space="preserve">
<source>Accept connection request?</source>
@@ -569,7 +570,8 @@
<trans-unit id="Accept incognito" xml:space="preserve">
<source>Accept incognito</source>
<target>シークレットモードで承諾</target>
<note>accept contact request via notification</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Acknowledged" xml:space="preserve">
<source>Acknowledged</source>
@@ -728,6 +730,10 @@
<target>連絡先が通話を許可している場合のみ通話を許可する。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow calls?" xml:space="preserve">
<source>Allow calls?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow disappearing messages only if your contact allows it to you." xml:space="preserve">
<source>Allow disappearing messages only if your contact allows it to you.</source>
<target>連絡先が許可している場合のみ消えるメッセージを許可する。</target>
@@ -912,6 +918,14 @@
<source>Archive and upload</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archive contacts to chat later." xml:space="preserve">
<source>Archive contacts to chat later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archived contacts" xml:space="preserve">
<source>Archived contacts</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archiving database" xml:space="preserve">
<source>Archiving database</source>
<note>No comment provided by engineer.</note>
@@ -1008,6 +1022,10 @@
<target>より良いメッセージ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better networking" xml:space="preserve">
<source>Better networking</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Black" xml:space="preserve">
<source>Black</source>
<note>No comment provided by engineer.</note>
@@ -1040,6 +1058,10 @@
<source>Blocked by admin</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Blur for better privacy." xml:space="preserve">
<source>Blur for better privacy.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Blur media" xml:space="preserve">
<source>Blur media</source>
<note>No comment provided by engineer.</note>
@@ -1089,10 +1111,22 @@
<target>通話</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Calls prohibited!" xml:space="preserve">
<source>Calls prohibited!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Camera not available" xml:space="preserve">
<source>Camera not available</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't call contact" xml:space="preserve">
<source>Can't call contact</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't call member" xml:space="preserve">
<source>Can't call member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't invite contact!" xml:space="preserve">
<source>Can't invite contact!</source>
<target>連絡先を招待できません!</target>
@@ -1103,6 +1137,10 @@
<target>連絡先を招待できません!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't message member" xml:space="preserve">
<source>Can't message member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cancel" xml:space="preserve">
<source>Cancel</source>
<target>中止</target>
@@ -1209,6 +1247,10 @@
<target>チャットのデータベースが削除されました</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database exported" xml:space="preserve">
<source>Chat database exported</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database imported" xml:space="preserve">
<source>Chat database imported</source>
<target>チャットのデータベースが読み込まれました</target>
@@ -1228,6 +1270,10 @@
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat list" xml:space="preserve">
<source>Chat list</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat migrated!" xml:space="preserve">
<source>Chat migrated!</source>
<note>No comment provided by engineer.</note>
@@ -1285,7 +1331,7 @@
<trans-unit id="Clear" xml:space="preserve">
<source>Clear</source>
<target>消す</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Clear conversation" xml:space="preserve">
<source>Clear conversation</source>
@@ -1306,6 +1352,10 @@
<target>検証を消す</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Color chats with the new themes." xml:space="preserve">
<source>Color chats with the new themes.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Color mode" xml:space="preserve">
<source>Color mode</source>
<note>No comment provided by engineer.</note>
@@ -1343,6 +1393,10 @@
<target>パスコードを確認</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm contact deletion?" xml:space="preserve">
<source>Confirm contact deletion?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm database upgrades" xml:space="preserve">
<source>Confirm database upgrades</source>
<target>データベースのアップグレードを確認</target>
@@ -1392,6 +1446,10 @@
<source>Connect to desktop</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect to your friends faster." xml:space="preserve">
<source>Connect to your friends faster.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect to yourself?" xml:space="preserve">
<source>Connect to yourself?</source>
<note>No comment provided by engineer.</note>
@@ -1454,6 +1512,10 @@ This is your own one-time link!</source>
<target>サーバーに接続中… (エラー: %@)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting to contact, please wait or check later!" xml:space="preserve">
<source>Connecting to contact, please wait or check later!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting to desktop" xml:space="preserve">
<source>Connecting to desktop</source>
<note>No comment provided by engineer.</note>
@@ -1463,6 +1525,10 @@ This is your own one-time link!</source>
<target>接続</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connection and servers status." xml:space="preserve">
<source>Connection and servers status.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connection error" xml:space="preserve">
<source>Connection error</source>
<target>接続エラー</target>
@@ -1509,6 +1575,10 @@ This is your own one-time link!</source>
<target>連絡先に既に存在します</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact deleted!" xml:space="preserve">
<source>Contact deleted!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact hidden:" xml:space="preserve">
<source>Contact hidden:</source>
<target>連絡先が非表示:</target>
@@ -1519,9 +1589,8 @@ This is your own one-time link!</source>
<target>連絡先は接続中</target>
<note>notification</note>
</trans-unit>
<trans-unit id="Contact is not connected yet!" xml:space="preserve">
<source>Contact is not connected yet!</source>
<target>連絡先がまだ繋がってません!</target>
<trans-unit id="Contact is deleted." xml:space="preserve">
<source>Contact is deleted.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact name" xml:space="preserve">
@@ -1534,6 +1603,10 @@ This is your own one-time link!</source>
<target>連絡先の設定</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact will be deleted - this cannot be undone!" xml:space="preserve">
<source>Contact will be deleted - this cannot be undone!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contacts" xml:space="preserve">
<source>Contacts</source>
<target>連絡先</target>
@@ -1549,6 +1622,10 @@ This is your own one-time link!</source>
<target>続ける</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Conversation deleted!" xml:space="preserve">
<source>Conversation deleted!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy" xml:space="preserve">
<source>Copy</source>
<target>コピー</target>
@@ -1806,7 +1883,8 @@ This is your own one-time link!</source>
<trans-unit id="Delete" xml:space="preserve">
<source>Delete</source>
<target>削除</target>
<note>chat item action</note>
<note>chat item action
swipe action</note>
</trans-unit>
<trans-unit id="Delete %lld messages of members?" xml:space="preserve">
<source>Delete %lld messages of members?</source>
@@ -1816,11 +1894,6 @@ This is your own one-time link!</source>
<source>Delete %lld messages?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete Contact" xml:space="preserve">
<source>Delete Contact</source>
<target>連絡先を削除</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete address" xml:space="preserve">
<source>Delete address</source>
<target>アドレスを削除</target>
@@ -1875,9 +1948,8 @@ This is your own one-time link!</source>
<target>連絡先を削除</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete contact?&#10;This cannot be undone!" xml:space="preserve">
<source>Delete contact?
This cannot be undone!</source>
<trans-unit id="Delete contact?" xml:space="preserve">
<source>Delete contact?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete database" xml:space="preserve">
@@ -1969,11 +2041,6 @@ This cannot be undone!</source>
<target>古いデータベースを削除しますか?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete pending connection" xml:space="preserve">
<source>Delete pending connection</source>
<target>確認待ちの接続を削除</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete pending connection?" xml:space="preserve">
<source>Delete pending connection?</source>
<target>接続待ちの接続を削除しますか?</target>
@@ -1989,11 +2056,19 @@ This cannot be undone!</source>
<target>待ち行列を削除</target>
<note>server test step</note>
</trans-unit>
<trans-unit id="Delete up to 20 messages at once." xml:space="preserve">
<source>Delete up to 20 messages at once.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete user profile?" xml:space="preserve">
<source>Delete user profile?</source>
<target>ユーザープロフィールを削除しますか?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete without notification" xml:space="preserve">
<source>Delete without notification</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Deleted" xml:space="preserve">
<source>Deleted</source>
<note>No comment provided by engineer.</note>
@@ -2069,6 +2144,10 @@ This cannot be undone!</source>
<target>開発</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Developer options" xml:space="preserve">
<source>Developer options</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Developer tools" xml:space="preserve">
<source>Developer tools</source>
<target>開発ツール</target>
@@ -2553,11 +2632,6 @@ This cannot be undone!</source>
<target>接続の削除エラー</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting contact" xml:space="preserve">
<source>Error deleting contact</source>
<target>連絡先の削除にエラー発生</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting database" xml:space="preserve">
<source>Error deleting database</source>
<target>データベースの削除にエラー発生</target>
@@ -2835,7 +2909,7 @@ This cannot be undone!</source>
<trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source>
<target>お気に入り</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="File error" xml:space="preserve">
<source>File error</source>
@@ -3545,6 +3619,10 @@ Error: %2$@</source>
3. 接続に問題があった。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It protects your IP address and connections." xml:space="preserve">
<source>It protects your IP address and connections.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It seems like you are already connected via this link. If it is not the case, there was an error (%@)." xml:space="preserve">
<source>It seems like you are already connected via this link. If it is not the case, there was an error (%@).</source>
<target>このリンクからすでに接続されているようです。そうでない場合は、エラー(%@)が発生しました。</target>
@@ -3563,7 +3641,7 @@ Error: %2$@</source>
<trans-unit id="Join" xml:space="preserve">
<source>Join</source>
<target>参加</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Join group" xml:space="preserve">
<source>Join group</source>
@@ -3601,6 +3679,10 @@ This is your link for group %@!</source>
<source>Keep</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep conversation" xml:space="preserve">
<source>Keep conversation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
<source>Keep the app open to use it from desktop</source>
<note>No comment provided by engineer.</note>
@@ -3642,7 +3724,7 @@ This is your link for group %@!</source>
<trans-unit id="Leave" xml:space="preserve">
<source>Leave</source>
<target>脱退</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Leave group" xml:space="preserve">
<source>Leave group</source>
@@ -3771,6 +3853,10 @@ This is your link for group %@!</source>
<target>最大 30 秒で即時受信します。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Media &amp; file servers" xml:space="preserve">
<source>Media &amp; file servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Medium" xml:space="preserve">
<source>Medium</source>
<note>blur media</note>
@@ -3852,12 +3938,8 @@ This is your link for group %@!</source>
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing mode" xml:space="preserve">
<source>Message routing mode</source>
<trans-unit id="Message servers" xml:space="preserve">
<source>Message servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message source remains private." xml:space="preserve">
@@ -4001,7 +4083,7 @@ This is your link for group %@!</source>
<trans-unit id="Mute" xml:space="preserve">
<source>Mute</source>
<target>ミュート</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Muted when inactive!" xml:space="preserve">
<source>Muted when inactive!</source>
@@ -4011,7 +4093,7 @@ This is your link for group %@!</source>
<trans-unit id="Name" xml:space="preserve">
<source>Name</source>
<target>名前</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Network &amp; servers" xml:space="preserve">
<source>Network &amp; servers</source>
@@ -4049,6 +4131,10 @@ This is your link for group %@!</source>
<source>New chat</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat experience 🎉" xml:space="preserve">
<source>New chat experience 🎉</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New contact request" xml:space="preserve">
<source>New contact request</source>
<target>新しい繋がりのリクエスト</target>
@@ -4079,6 +4165,10 @@ This is your link for group %@!</source>
<target>%@ の新機能</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New media options" xml:space="preserve">
<source>New media options</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member role" xml:space="preserve">
<source>New member role</source>
<target>新しいメンバーの役割</target>
@@ -4217,14 +4307,18 @@ This is your link for group %@!</source>
<target>使い捨ての招待リンク</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be required for connection. Requires enabling VPN." xml:space="preserve">
<source>Onion hosts will be required for connection. Requires enabling VPN.</source>
<target>接続にオニオンのホストが必要となります。VPN を有効にする必要があります。</target>
<trans-unit id="Onion hosts will be **required** for connection.&#10;Requires compatible VPN." xml:space="preserve">
<source>Onion hosts will be **required** for connection.
Requires compatible VPN.</source>
<target>接続にオニオンのホストが必要となります。
VPN を有効にする必要があります。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be used when available. Requires enabling VPN." xml:space="preserve">
<source>Onion hosts will be used when available. Requires enabling VPN.</source>
<target>オニオンのホストが利用可能時に使われます。VPN を有効にする必要があります。</target>
<trans-unit id="Onion hosts will be used when available.&#10;Requires compatible VPN." xml:space="preserve">
<source>Onion hosts will be used when available.
Requires compatible VPN.</source>
<target>オニオンのホストが利用可能時に使われます。
VPN を有効にする必要があります。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will not be used." xml:space="preserve">
@@ -4237,6 +4331,10 @@ This is your link for group %@!</source>
<target>**2 レイヤーのエンドツーエンド暗号化**を使用して送信されたユーザー プロファイル、連絡先、グループ、メッセージを保存できるのはクライアント デバイスのみです。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only delete conversation" xml:space="preserve">
<source>Only delete conversation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
<source>Only group owners can change group preferences.</source>
<target>グループ設定を変えられるのはグループのオーナーだけです。</target>
@@ -4456,6 +4554,14 @@ This is your link for group %@!</source>
<source>Picture-in-picture calls</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Play from the chat list." xml:space="preserve">
<source>Play from the chat list.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please ask your contact to enable calls." xml:space="preserve">
<source>Please ask your contact to enable calls.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please ask your contact to enable sending voice messages." xml:space="preserve">
<source>Please ask your contact to enable sending voice messages.</source>
<target>音声メッセージを有効にするように連絡相手に要求してください。</target>
@@ -4734,6 +4840,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>アプリを評価</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reachable chat toolbar" xml:space="preserve">
<source>Reachable chat toolbar</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="React…" xml:space="preserve">
<source>React…</source>
<target>反応する…</target>
@@ -4742,7 +4852,7 @@ Enable in *Network &amp; servers* settings.</source>
<trans-unit id="Read" xml:space="preserve">
<source>Read</source>
<target>読む</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Read more" xml:space="preserve">
<source>Read more</source>
@@ -4889,7 +4999,8 @@ Enable in *Network &amp; servers* settings.</source>
<trans-unit id="Reject" xml:space="preserve">
<source>Reject</source>
<target>拒否</target>
<note>reject incoming call via notification</note>
<note>reject incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Reject (sender NOT notified)" xml:space="preserve">
<source>Reject (sender NOT notified)</source>
@@ -4985,6 +5096,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>戻す</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all hints" xml:space="preserve">
<source>Reset all hints</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all statistics" xml:space="preserve">
<source>Reset all statistics</source>
<note>No comment provided by engineer.</note>
@@ -5050,11 +5165,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>開示する</target>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Revert" xml:space="preserve">
<source>Revert</source>
<target>元に戻す</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>取り消す</target>
@@ -5084,11 +5194,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>SMP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SMP servers" xml:space="preserve">
<source>SMP servers</source>
<target>SMPサーバ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safely receive files" xml:space="preserve">
<source>Safely receive files</source>
<note>No comment provided by engineer.</note>
@@ -5117,6 +5222,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>保存して、グループのメンバーにに知らせる</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save and reconnect" xml:space="preserve">
<source>Save and reconnect</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save and update group profile" xml:space="preserve">
<source>Save and update group profile</source>
<target>グループプロファイルの保存と更新</target>
@@ -5309,11 +5418,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Send delivery receipts to</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send direct message" xml:space="preserve">
<source>Send direct message</source>
<target>ダイレクトメッセージを送信</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send direct message to connect" xml:space="preserve">
<source>Send direct message to connect</source>
<target>ダイレクトメッセージを送信して接続する</target>
@@ -5338,6 +5442,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>ライブメッセージを送信</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send message to enable calls." xml:space="preserve">
<source>Send message to enable calls.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when IP address is protected and your or destination server does not support private routing." xml:space="preserve">
<source>Send messages directly when IP address is protected and your or destination server does not support private routing.</source>
<note>No comment provided by engineer.</note>
@@ -5596,6 +5704,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>アドレスを連絡先と共有しますか?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share from other apps." xml:space="preserve">
<source>Share from other apps.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share link" xml:space="preserve">
<source>Share link</source>
<target>リンクを送る</target>
@@ -5755,11 +5867,19 @@ Enable in *Network &amp; servers* settings.</source>
<source>Soft</source>
<note>blur media</note>
</trans-unit>
<trans-unit id="Some file(s) were not exported:" xml:space="preserve">
<source>Some file(s) were not exported:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some non-fatal errors occurred during import - you may see Chat console for more details." xml:space="preserve">
<source>Some non-fatal errors occurred during import - you may see Chat console for more details.</source>
<target>インポート中に致命的でないエラーが発生しました - 詳細はチャットコンソールを参照してください。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some non-fatal errors occurred during import:" xml:space="preserve">
<source>Some non-fatal errors occurred during import:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Somebody" xml:space="preserve">
<source>Somebody</source>
<target>誰か</target>
@@ -5885,6 +6005,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>システム認証</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="TCP connection" xml:space="preserve">
<source>TCP connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="TCP connection timeout" xml:space="preserve">
<source>TCP connection timeout</source>
<target>TCP接続タイムアウト</target>
@@ -5942,11 +6066,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Tap to scan</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Tap to start a new chat" xml:space="preserve">
<source>Tap to start a new chat</source>
<target>タップして新しいチャットを始める</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Temporary file error" xml:space="preserve">
<source>Temporary file error</source>
<note>No comment provided by engineer.</note>
@@ -6228,10 +6347,18 @@ You will be prompted to complete authentication before this feature is enabled.<
<target>エンドツーエンド暗号化を確認するには、ご自分の端末と連絡先の端末のコードを比べます (スキャンします)。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toggle chat list:" xml:space="preserve">
<source>Toggle chat list:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toggle incognito when connecting." xml:space="preserve">
<source>Toggle incognito when connecting.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toolbar opacity" xml:space="preserve">
<source>Toolbar opacity</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Total" xml:space="preserve">
<source>Total</source>
<note>No comment provided by engineer.</note>
@@ -6302,7 +6429,7 @@ You will be prompted to complete authentication before this feature is enabled.<
<trans-unit id="Unfav." xml:space="preserve">
<source>Unfav.</source>
<target>お気に入りを取り消す。</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Unhide" xml:space="preserve">
<source>Unhide</source>
@@ -6376,12 +6503,12 @@ To connect, please ask your contact to create another connection link and check
<trans-unit id="Unmute" xml:space="preserve">
<source>Unmute</source>
<target>ミュート解除</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Unread" xml:space="preserve">
<source>Unread</source>
<target>未読</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Up to 100 last messages are sent to new members." xml:space="preserve">
<source>Up to 100 last messages are sent to new members.</source>
@@ -6392,11 +6519,6 @@ To connect, please ask your contact to create another connection link and check
<target>更新</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update .onion hosts setting?" xml:space="preserve">
<source>Update .onion hosts setting?</source>
<target>.onionのホスト設定を更新しますか?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update database passphrase" xml:space="preserve">
<source>Update database passphrase</source>
<target>データベースのパスフレーズを更新</target>
@@ -6407,9 +6529,8 @@ To connect, please ask your contact to create another connection link and check
<target>ネットワーク設定を更新しますか?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update transport isolation mode?" xml:space="preserve">
<source>Update transport isolation mode?</source>
<target>トランスポート隔離モードを更新しますか?</target>
<trans-unit id="Update settings?" xml:space="preserve">
<source>Update settings?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Updating settings will re-connect the client to all servers." xml:space="preserve">
@@ -6417,11 +6538,6 @@ To connect, please ask your contact to create another connection link and check
<target>設定を更新すると、全サーバにクライントの再接続が行われます。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Updating this setting will re-connect the client to all servers." xml:space="preserve">
<source>Updating this setting will re-connect the client to all servers.</source>
<target>設定を更新すると、全サーバにクライントの再接続が行われます。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Upgrade and open chat" xml:space="preserve">
<source>Upgrade and open chat</source>
<target>アップグレードしてチャットを開く</target>
@@ -6512,6 +6628,10 @@ To connect, please ask your contact to create another connection link and check
<source>Use the app while in the call.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use the app with one hand." xml:space="preserve">
<source>Use the app with one hand.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>ユーザープロフィール</target>
@@ -6521,11 +6641,6 @@ To connect, please ask your contact to create another connection link and check
<source>User selection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using .onion hosts requires compatible VPN provider." xml:space="preserve">
<source>Using .onion hosts requires compatible VPN provider.</source>
<target>.onionホストを使用するには、互換性のあるVPNプロバイダーが必要です。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using SimpleX Chat servers." xml:space="preserve">
<source>Using SimpleX Chat servers.</source>
<target>SimpleX チャット サーバーを使用する。</target>
@@ -6762,11 +6877,6 @@ To connect, please ask your contact to create another connection link and check
<source>XFTP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="XFTP servers" xml:space="preserve">
<source>XFTP servers</source>
<target>XFTPサーバ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>あなた</target>
@@ -6844,6 +6954,10 @@ Repeat join request?</source>
<target>デバイスやアプリの認証を行わずに、ロック画面から通話を受けることができます。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can change it in Appearance settings." xml:space="preserve">
<source>You can change it in Appearance settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can create it later" xml:space="preserve">
<source>You can create it later</source>
<target>後からでも作成できます</target>
@@ -6877,6 +6991,10 @@ Repeat join request?</source>
<target>%@ にメッセージを送信できるようになりました</target>
<note>notification body</note>
</trans-unit>
<trans-unit id="You can send messages to %@ from Archived contacts." xml:space="preserve">
<source>You can send messages to %@ from Archived contacts.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can set lock screen notification preview via settings." xml:space="preserve">
<source>You can set lock screen notification preview via settings.</source>
<target>設定からロック画面の通知プレビューを設定できます。</target>
@@ -6902,6 +7020,10 @@ Repeat join request?</source>
<target>アプリの設定/データベースから、またはアプリを再起動することでチャットを開始できます</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can still view conversation with %@ in the list of chats." xml:space="preserve">
<source>You can still view conversation with %@ in the list of chats.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can turn on SimpleX Lock via Settings." xml:space="preserve">
<source>You can turn on SimpleX Lock via Settings.</source>
<target>設定からSimpleXのロックをオンにすることができます。</target>
@@ -6940,11 +7062,6 @@ Repeat join request?</source>
Repeat connection request?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You have no chats" xml:space="preserve">
<source>You have no chats</source>
<target>あなたはチャットがありません</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You have to enter passphrase every time the app starts - it is not stored on the device." xml:space="preserve">
<source>You have to enter passphrase every time the app starts - it is not stored on the device.</source>
<target>アプリ起動時にパスフレーズを入力しなければなりません。端末に保存されてません。</target>
@@ -6965,11 +7082,23 @@ Repeat connection request?</source>
<target>グループに参加しました。招待をくれたメンバーに接続してます。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You may migrate the exported database." xml:space="preserve">
<source>You may migrate the exported database.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You may save the exported archive." xml:space="preserve">
<source>You may save the exported archive.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="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." xml:space="preserve">
<source>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.</source>
<target>あなたの最新データベースを1つの端末にしか使わなければ、一部の連絡先からメッセージが届きかねます。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You need to allow your contact to call to be able to call them." xml:space="preserve">
<source>You need to allow your contact to call to be able to call them.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You need to allow your contact to send voice messages to be able to send them." xml:space="preserve">
<source>You need to allow your contact to send voice messages to be able to send them.</source>
<target>音声メッセージを送るには、連絡相手からの音声メッセージを許可しなければなりません。</target>
@@ -7083,13 +7212,6 @@ Repeat connection request?</source>
<target>あなたのチャットプロフィール</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact needs to be online for the connection to complete.&#10;You can cancel this connection and remove the contact (and try later with a new link)." xml:space="preserve">
<source>Your contact needs to be online for the connection to complete.
You can cancel this connection and remove the contact (and try later with a new link).</source>
<target>接続を完了するには、連絡相手がオンラインになる必要があります。
この接続をキャンセルして、連絡先を削除をすることもできます (後でやり直すこともできます)。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
<target>連絡先が現在サポートされている最大サイズ (%@) より大きいファイルを送信しました。</target>
@@ -7273,6 +7395,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>太文字</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="call" xml:space="preserve">
<source>call</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="call error" xml:space="preserve">
<source>call error</source>
<target>通話エラー</target>
@@ -7635,6 +7761,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>グループ %@ への招待</target>
<note>group name</note>
</trans-unit>
<trans-unit id="invite" xml:space="preserve">
<source>invite</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="invited" xml:space="preserve">
<source>invited</source>
<target>招待済み</target>
@@ -7689,6 +7819,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>接続中</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message received" xml:space="preserve">
<source>message received</source>
<target>メッセージを受信</target>
@@ -7719,6 +7853,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>月</target>
<note>time unit</note>
</trans-unit>
<trans-unit id="mute" xml:space="preserve">
<source>mute</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="never" xml:space="preserve">
<source>never</source>
<target>一度も</target>
@@ -7843,6 +7981,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="search" xml:space="preserve">
<source>search</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve">
<source>sec</source>
<target>秒</target>
@@ -7909,14 +8051,18 @@ last received msg: %2$@</source>
<target>不明</target>
<note>connection info</note>
</trans-unit>
<trans-unit id="unknown relays" xml:space="preserve">
<source>unknown relays</source>
<trans-unit id="unknown servers" xml:space="preserve">
<source>unknown servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unknown status" xml:space="preserve">
<source>unknown status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unmute" xml:space="preserve">
<source>unmute</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unprotected" xml:space="preserve">
<source>unprotected</source>
<note>No comment provided by engineer.</note>
@@ -7959,6 +8105,10 @@ last received msg: %2$@</source>
<target>リレー経由</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="video" xml:space="preserve">
<source>video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="video call (not e2e encrypted)" xml:space="preserve">
<source>video call (not e2e encrypted)</source>
<target>ビデオ通話 (非エンドツーエンド暗号化)</target>
@@ -8201,10 +8351,6 @@ last received msg: %2$@</source>
<source>Database upgrade required</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Dismiss Sheet" xml:space="preserve">
<source>Dismiss Sheet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error preparing file" xml:space="preserve">
<source>Error preparing file</source>
<note>No comment provided by engineer.</note>
@@ -8229,10 +8375,6 @@ last received msg: %2$@</source>
<source>Invalid migration confirmation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep Trying" xml:space="preserve">
<source>Keep Trying</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keychain error" xml:space="preserve">
<source>Keychain error</source>
<note>No comment provided by engineer.</note>
@@ -8245,10 +8387,6 @@ last received msg: %2$@</source>
<source>No active profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<note>No comment provided by engineer.</note>
@@ -8273,14 +8411,22 @@ last received msg: %2$@</source>
<source>Selected chat preferences prohibit this message.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sending File" xml:space="preserve">
<source>Sending File</source>
<trans-unit id="Sending a message takes longer than expected." xml:space="preserve">
<source>Sending a message takes longer than expected.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sending message…" xml:space="preserve">
<source>Sending message…</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share" xml:space="preserve">
<source>Share</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Slow network?" xml:space="preserve">
<source>Slow network?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unknown database error: %@" xml:space="preserve">
<source>Unknown database error: %@</source>
<note>No comment provided by engineer.</note>
@@ -8289,6 +8435,10 @@ last received msg: %2$@</source>
<source>Unsupported format</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wait" xml:space="preserve">
<source>Wait</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wrong database passphrase" xml:space="preserve">
<source>Wrong database passphrase</source>
<note>No comment provided by engineer.</note>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -5234,6 +5234,274 @@ Isso pode acontecer por causa de algum bug ou quando a conexão está comprometi
<target state="translated">%1$@ em %2$@:</target>
<note>copied message info, &lt;sender&gt; at &lt;time&gt;</note>
</trans-unit>
<trans-unit id="Allow your contacts to irreversibly delete sent messages. (24 hours)" xml:space="preserve" approved="no">
<source>Allow your contacts to irreversibly delete sent messages. (24 hours)</source>
<target state="translated">Permitir que seus contatos deletem mensagens enviadas de maneira irreversível. (24 horas)</target>
</trans-unit>
<trans-unit id="%@ downloaded" xml:space="preserve" approved="no">
<source>%@ downloaded</source>
<target state="translated">baixado</target>
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve" approved="no">
<source>%@ uploaded</source>
<target state="translated">transferido</target>
</trans-unit>
<trans-unit id="A new random profile will be shared." xml:space="preserve" approved="no">
<source>A new random profile will be shared.</source>
<target state="translated">Um novo perfil aleatório será compartilhado.</target>
</trans-unit>
<trans-unit id="Camera not available" xml:space="preserve" approved="no">
<source>Camera not available</source>
<target state="translated">Câmera indisponível</target>
</trans-unit>
<trans-unit id="Admins can block a member for all." xml:space="preserve" approved="no">
<source>Admins can block a member for all.</source>
<target state="translated">Administradores podem bloquear um membro para todos.</target>
</trans-unit>
<trans-unit id="Allow to irreversibly delete sent messages. (24 hours)" xml:space="preserve" approved="no">
<source>Allow to irreversibly delete sent messages. (24 hours)</source>
<target state="translated">Permitir que mensagens enviadas sejam deletadas de maneira irreversível. (24 horas)</target>
</trans-unit>
<trans-unit id="Apply" xml:space="preserve" approved="no">
<source>Apply</source>
<target state="translated">Aplicar</target>
</trans-unit>
<trans-unit id="Accent" xml:space="preserve" approved="no">
<source>Accent</source>
<target state="translated">Esquema</target>
</trans-unit>
<trans-unit id="Accept connection request?" xml:space="preserve" approved="no">
<source>Accept connection request?</source>
<target state="translated">Aceitar solicitação de conexão?</target>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve" approved="no">
<source>Active connections</source>
<target state="translated">Conexões ativas</target>
</trans-unit>
<trans-unit id="Add contact" xml:space="preserve" approved="no">
<source>Add contact</source>
<target state="translated">Adicionar contato</target>
</trans-unit>
<trans-unit id="Additional accent" xml:space="preserve" approved="no">
<source>Additional accent</source>
<target state="translated">Esquema adicional</target>
</trans-unit>
<trans-unit id="All new messages from %@ will be hidden!" xml:space="preserve" approved="no">
<source>All new messages from %@ will be hidden!</source>
<target state="translated">Todas as novas mensagens de %@ serão ocultas!</target>
</trans-unit>
<trans-unit id="All profiles" xml:space="preserve" approved="no">
<source>All profiles</source>
<target state="translated">Todos perfis</target>
</trans-unit>
<trans-unit id="Allow calls?" xml:space="preserve" approved="no">
<source>Allow calls?</source>
<target state="translated">Permitir chamadas?</target>
</trans-unit>
<trans-unit id="Archive contacts to chat later." xml:space="preserve" approved="no">
<source>Archive contacts to chat later.</source>
<target state="translated">Arquivar contatos para conversar depois.</target>
</trans-unit>
<trans-unit id="Blur media" xml:space="preserve" approved="no">
<source>Blur media</source>
<target state="translated">Censurar mídia</target>
</trans-unit>
<trans-unit id="Calls prohibited!" xml:space="preserve" approved="no">
<source>Calls prohibited!</source>
<target state="translated">Chamadas proibidas!</target>
</trans-unit>
<trans-unit id="Can't call contact" xml:space="preserve" approved="no">
<source>Can't call contact</source>
<target state="translated">Não foi possível ligar para o contato</target>
</trans-unit>
<trans-unit id="%lld messages marked deleted" xml:space="preserve" approved="no">
<source>%lld messages marked deleted</source>
<target state="translated">mensagens deletadas</target>
</trans-unit>
<trans-unit id="0 sec" xml:space="preserve" approved="no">
<source>0 sec</source>
<target state="translated">0 seg</target>
</trans-unit>
<trans-unit id="%lld messages blocked" xml:space="preserve" approved="no">
<source>%lld messages blocked</source>
<target state="translated">mensagens bloqueadas</target>
</trans-unit>
<trans-unit id="%lld messages blocked by admin" xml:space="preserve" approved="no">
<source>%lld messages blocked by admin</source>
<target state="translated">mensagens bloqueadas pelo administrador</target>
</trans-unit>
<trans-unit id="**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." xml:space="preserve" approved="no">
<source>**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection.</source>
<target state="translated">**Nota**: usar o mesmo banco de dados em dois dispositivos irá quebrar a desencriptação das mensagens de suas conexões como uma medida de segurança.</target>
</trans-unit>
<trans-unit id="- more stable message delivery.&#10;- a bit better groups.&#10;- and more!" xml:space="preserve" approved="no">
<source>- more stable message delivery.
- a bit better groups.
- and more!</source>
<target state="translated">- entrega de mensagens mais estável.
- grupos melhorados.
- e muito mais!</target>
</trans-unit>
<trans-unit id="All messages will be deleted - this cannot be undone!" xml:space="preserve" approved="no">
<source>All messages will be deleted - this cannot be undone!</source>
<target state="translated">Todas as mensagens serão deletadas - isto não pode ser desfeito!</target>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve" approved="no">
<source>Allow to send files and media.</source>
<target state="translated">Permitir o envio de arquivos e mídia.</target>
</trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve" approved="no">
<source>Allow to send SimpleX links.</source>
<target state="translated">Permitir envio de links SimpleX.</target>
</trans-unit>
<trans-unit id="Block for all" xml:space="preserve" approved="no">
<source>Block for all</source>
<target state="translated">Bloquear para todos</target>
</trans-unit>
<trans-unit id="Block member" xml:space="preserve" approved="no">
<source>Block member</source>
<target state="translated">Bloquear membro</target>
</trans-unit>
<trans-unit id="Blocked by admin" xml:space="preserve" approved="no">
<source>Blocked by admin</source>
<target state="translated">Bloqueado por um administrador</target>
</trans-unit>
<trans-unit id="Block group members" xml:space="preserve" approved="no">
<source>Block group members</source>
<target state="translated">Bloquear membros de grupo</target>
</trans-unit>
<trans-unit id="Block member for all?" xml:space="preserve" approved="no">
<source>Block member for all?</source>
<target state="translated">Bloquear membro para todos?</target>
</trans-unit>
<trans-unit id="Block member?" xml:space="preserve" approved="no">
<source>Block member?</source>
<target state="translated">Bloquear membro?</target>
</trans-unit>
<trans-unit id="Both you and your contact can irreversibly delete sent messages. (24 hours)" xml:space="preserve" approved="no">
<source>Both you and your contact can irreversibly delete sent messages. (24 hours)</source>
<target state="translated">Você e seu contato podem apagar mensagens enviadas de maneira irreversível. (24 horas)</target>
</trans-unit>
<trans-unit id="Can't call member" xml:space="preserve" approved="no">
<source>Can't call member</source>
<target state="translated">Não foi possível ligar para este membro</target>
</trans-unit>
<trans-unit id="Can't message member" xml:space="preserve" approved="no">
<source>Can't message member</source>
<target state="translated">Não foi possível enviar mensagem para este membro</target>
</trans-unit>
<trans-unit id="Cancel migration" xml:space="preserve" approved="no">
<source>Cancel migration</source>
<target state="translated">Cancelar migração</target>
</trans-unit>
<trans-unit id="Abort" xml:space="preserve" approved="no">
<source>Abort</source>
<target state="translated">Abortar</target>
</trans-unit>
<trans-unit id="Abort changing address" xml:space="preserve" approved="no">
<source>Abort changing address</source>
<target state="translated">Abortar troca de endereço</target>
</trans-unit>
<trans-unit id="Abort changing address?" xml:space="preserve" approved="no">
<source>Abort changing address?</source>
<target state="translated">Abortar troca de endereço?</target>
</trans-unit>
<trans-unit id="- optionally notify deleted contacts.&#10;- profile names with spaces.&#10;- and more!" xml:space="preserve" approved="no">
<source>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</source>
<target state="translated">- notificar contatos apagados de maneira opcional.
- nome de perfil com espaços.
- e muito mais!</target>
</trans-unit>
<trans-unit id="Allow sharing" xml:space="preserve" approved="no">
<source>Allow sharing</source>
<target state="translated">Permitir compartilhamento</target>
</trans-unit>
<trans-unit id="Block" xml:space="preserve" approved="no">
<source>Block</source>
<target state="translated">Bloquear</target>
</trans-unit>
<trans-unit id="Additional accent 2" xml:space="preserve" approved="no">
<source>Additional accent 2</source>
<target state="translated">Esquema adicional 2</target>
</trans-unit>
<trans-unit id="Address change will be aborted. Old receiving address will be used." xml:space="preserve" approved="no">
<source>Address change will be aborted. Old receiving address will be used.</source>
<target state="translated">Alteração de endereço será abortada. O endereço antigo será utilizado.</target>
</trans-unit>
<trans-unit id="Advanced settings" xml:space="preserve" approved="no">
<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>
<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">
<source>All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays.</source>
<target state="translated">Todos os seus contatos, conversas e arquivos serão encriptados e enviados em pedaços para nós XFTP.</target>
</trans-unit>
<trans-unit id="Allow irreversible message deletion only if your contact allows it to you. (24 hours)" xml:space="preserve" approved="no">
<source>Allow irreversible message deletion only if your contact allows it to you. (24 hours)</source>
<target state="translated">Permitir deletar mensagens de maneira irreversível apenas se seu contato permitir para você. (24 horas)</target>
</trans-unit>
<trans-unit id="Already connecting!" xml:space="preserve" approved="no">
<source>Already connecting!</source>
<target state="translated">Já está conectando!</target>
</trans-unit>
<trans-unit id="Already joining the group!" xml:space="preserve" approved="no">
<source>Already joining the group!</source>
<target state="translated">Já está entrando no grupo!</target>
</trans-unit>
<trans-unit id="Always use private routing." xml:space="preserve" approved="no">
<source>Always use private routing.</source>
<target state="translated">Sempre use rotas privadas.</target>
</trans-unit>
<trans-unit id="Apply to" xml:space="preserve" approved="no">
<source>Apply to</source>
<target state="translated">Aplicar em</target>
</trans-unit>
<trans-unit id="Archiving database" xml:space="preserve" approved="no">
<source>Archiving database</source>
<target state="translated">Arquivando banco de dados</target>
</trans-unit>
<trans-unit id="Black" xml:space="preserve" approved="no">
<source>Black</source>
<target state="translated">Preto</target>
</trans-unit>
<trans-unit id="Cannot forward message" xml:space="preserve" approved="no">
<source>Cannot forward message</source>
<target state="translated">Não é possível encaminhar mensagem</target>
</trans-unit>
<trans-unit id="(new)" xml:space="preserve" approved="no">
<source>(new)</source>
<target state="translated">(novo)</target>
</trans-unit>
<trans-unit id="(this device v%@)" xml:space="preserve" approved="no">
<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>
<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">
<source>**Create group**: to create a new group.</source>
<target state="translated">**Criar grupo**: criar um novo grupo.</target>
</trans-unit>
<trans-unit id="**Warning**: the archive will be removed." xml:space="preserve" approved="no">
<source>**Warning**: the archive will be removed.</source>
<target state="translated">**Aviso**: o arquivo será removido.</target>
</trans-unit>
<trans-unit id="A few more things" xml:space="preserve" approved="no">
<source>A few more things</source>
<target state="translated">E mais algumas coisas</target>
</trans-unit>
<trans-unit id="Archived contacts" xml:space="preserve" approved="no">
<source>Archived contacts</source>
<target state="translated">Contatos arquivados</target>
</trans-unit>
</body>
</file>
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="pt-BR" datatype="plaintext">
File diff suppressed because it is too large Load Diff
@@ -530,7 +530,8 @@
<source>Accept</source>
<target>รับ</target>
<note>accept contact request via notification
accept incoming call via notification</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept connection request?" xml:space="preserve">
<source>Accept connection request?</source>
@@ -544,7 +545,8 @@
<trans-unit id="Accept incognito" xml:space="preserve">
<source>Accept incognito</source>
<target>ยอมรับโหมดไม่ระบุตัวตน</target>
<note>accept contact request via notification</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Acknowledged" xml:space="preserve">
<source>Acknowledged</source>
@@ -702,6 +704,10 @@
<target>อนุญาตการโทรเฉพาะเมื่อผู้ติดต่อของคุณอนุญาตเท่านั้น.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow calls?" xml:space="preserve">
<source>Allow calls?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow disappearing messages only if your contact allows it to you." xml:space="preserve">
<source>Allow disappearing messages only if your contact allows it to you.</source>
<target>อนุญาตให้ข้อความที่หายไปเฉพาะในกรณีที่ผู้ติดต่อของคุณอนุญาตเท่านั้น.</target>
@@ -881,6 +887,14 @@
<source>Archive and upload</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archive contacts to chat later." xml:space="preserve">
<source>Archive contacts to chat later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archived contacts" xml:space="preserve">
<source>Archived contacts</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archiving database" xml:space="preserve">
<source>Archiving database</source>
<note>No comment provided by engineer.</note>
@@ -977,6 +991,10 @@
<target>ข้อความที่ดีขึ้น</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better networking" xml:space="preserve">
<source>Better networking</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Black" xml:space="preserve">
<source>Black</source>
<note>No comment provided by engineer.</note>
@@ -1009,6 +1027,10 @@
<source>Blocked by admin</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Blur for better privacy." xml:space="preserve">
<source>Blur for better privacy.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Blur media" xml:space="preserve">
<source>Blur media</source>
<note>No comment provided by engineer.</note>
@@ -1057,10 +1079,22 @@
<target>โทร</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Calls prohibited!" xml:space="preserve">
<source>Calls prohibited!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Camera not available" xml:space="preserve">
<source>Camera not available</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't call contact" xml:space="preserve">
<source>Can't call contact</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't call member" xml:space="preserve">
<source>Can't call member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't invite contact!" xml:space="preserve">
<source>Can't invite contact!</source>
<target>ไม่สามารถเชิญผู้ติดต่อได้!</target>
@@ -1071,6 +1105,10 @@
<target>ไม่สามารถเชิญผู้ติดต่อได้!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't message member" xml:space="preserve">
<source>Can't message member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cancel" xml:space="preserve">
<source>Cancel</source>
<target>ยกเลิก</target>
@@ -1177,6 +1215,10 @@
<target>ลบฐานข้อมูลแชทแล้ว</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database exported" xml:space="preserve">
<source>Chat database exported</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database imported" xml:space="preserve">
<source>Chat database imported</source>
<target>นำฐานข้อมูลแชทเข้าแล้ว</target>
@@ -1196,6 +1238,10 @@
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat list" xml:space="preserve">
<source>Chat list</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat migrated!" xml:space="preserve">
<source>Chat migrated!</source>
<note>No comment provided by engineer.</note>
@@ -1253,7 +1299,7 @@
<trans-unit id="Clear" xml:space="preserve">
<source>Clear</source>
<target>ลบ</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Clear conversation" xml:space="preserve">
<source>Clear conversation</source>
@@ -1274,6 +1320,10 @@
<target>ล้างการยืนยัน</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Color chats with the new themes." xml:space="preserve">
<source>Color chats with the new themes.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Color mode" xml:space="preserve">
<source>Color mode</source>
<note>No comment provided by engineer.</note>
@@ -1311,6 +1361,10 @@
<target>ยืนยันรหัสผ่าน</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm contact deletion?" xml:space="preserve">
<source>Confirm contact deletion?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm database upgrades" xml:space="preserve">
<source>Confirm database upgrades</source>
<target>ยืนยันการอัพเกรดฐานข้อมูล</target>
@@ -1359,6 +1413,10 @@
<source>Connect to desktop</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect to your friends faster." xml:space="preserve">
<source>Connect to your friends faster.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect to yourself?" xml:space="preserve">
<source>Connect to yourself?</source>
<note>No comment provided by engineer.</note>
@@ -1420,6 +1478,10 @@ This is your own one-time link!</source>
<target>กำลังเชื่อมต่อกับเซิร์ฟเวอร์... (ข้อผิดพลาด: %@)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting to contact, please wait or check later!" xml:space="preserve">
<source>Connecting to contact, please wait or check later!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting to desktop" xml:space="preserve">
<source>Connecting to desktop</source>
<note>No comment provided by engineer.</note>
@@ -1429,6 +1491,10 @@ This is your own one-time link!</source>
<target>การเชื่อมต่อ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connection and servers status." xml:space="preserve">
<source>Connection and servers status.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connection error" xml:space="preserve">
<source>Connection error</source>
<target>การเชื่อมต่อผิดพลาด</target>
@@ -1475,6 +1541,10 @@ This is your own one-time link!</source>
<target>ผู้ติดต่อรายนี้มีอยู่แล้ว</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact deleted!" xml:space="preserve">
<source>Contact deleted!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact hidden:" xml:space="preserve">
<source>Contact hidden:</source>
<target>ผู้ติดต่อถูกซ่อน:</target>
@@ -1485,9 +1555,8 @@ This is your own one-time link!</source>
<target>เชื่อมต่อกับผู้ติดต่อแล้ว</target>
<note>notification</note>
</trans-unit>
<trans-unit id="Contact is not connected yet!" xml:space="preserve">
<source>Contact is not connected yet!</source>
<target>ผู้ติดต่อยังไม่ได้เชื่อมต่อ!</target>
<trans-unit id="Contact is deleted." xml:space="preserve">
<source>Contact is deleted.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact name" xml:space="preserve">
@@ -1500,6 +1569,10 @@ This is your own one-time link!</source>
<target>การกําหนดลักษณะการติดต่อ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact will be deleted - this cannot be undone!" xml:space="preserve">
<source>Contact will be deleted - this cannot be undone!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contacts" xml:space="preserve">
<source>Contacts</source>
<target>ติดต่อ</target>
@@ -1515,6 +1588,10 @@ This is your own one-time link!</source>
<target>ดำเนินการต่อ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Conversation deleted!" xml:space="preserve">
<source>Conversation deleted!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy" xml:space="preserve">
<source>Copy</source>
<target>คัดลอก</target>
@@ -1771,7 +1848,8 @@ This is your own one-time link!</source>
<trans-unit id="Delete" xml:space="preserve">
<source>Delete</source>
<target>ลบ</target>
<note>chat item action</note>
<note>chat item action
swipe action</note>
</trans-unit>
<trans-unit id="Delete %lld messages of members?" xml:space="preserve">
<source>Delete %lld messages of members?</source>
@@ -1781,11 +1859,6 @@ This is your own one-time link!</source>
<source>Delete %lld messages?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete Contact" xml:space="preserve">
<source>Delete Contact</source>
<target>ลบผู้ติดต่อ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete address" xml:space="preserve">
<source>Delete address</source>
<target>ลบที่อยู่</target>
@@ -1840,9 +1913,8 @@ This is your own one-time link!</source>
<target>ลบผู้ติดต่อ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete contact?&#10;This cannot be undone!" xml:space="preserve">
<source>Delete contact?
This cannot be undone!</source>
<trans-unit id="Delete contact?" xml:space="preserve">
<source>Delete contact?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete database" xml:space="preserve">
@@ -1934,11 +2006,6 @@ This cannot be undone!</source>
<target>ลบฐานข้อมูลเก่า?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete pending connection" xml:space="preserve">
<source>Delete pending connection</source>
<target>ลบการเชื่อมต่อที่รอดำเนินการ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete pending connection?" xml:space="preserve">
<source>Delete pending connection?</source>
<target>ลบการเชื่อมต่อที่รอดำเนินการหรือไม่?</target>
@@ -1954,11 +2021,19 @@ This cannot be undone!</source>
<target>ลบคิว</target>
<note>server test step</note>
</trans-unit>
<trans-unit id="Delete up to 20 messages at once." xml:space="preserve">
<source>Delete up to 20 messages at once.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete user profile?" xml:space="preserve">
<source>Delete user profile?</source>
<target>ลบโปรไฟล์ผู้ใช้?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete without notification" xml:space="preserve">
<source>Delete without notification</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Deleted" xml:space="preserve">
<source>Deleted</source>
<note>No comment provided by engineer.</note>
@@ -2033,6 +2108,10 @@ This cannot be undone!</source>
<target>พัฒนา</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Developer options" xml:space="preserve">
<source>Developer options</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Developer tools" xml:space="preserve">
<source>Developer tools</source>
<target>เครื่องมือสำหรับนักพัฒนา</target>
@@ -2512,11 +2591,6 @@ This cannot be undone!</source>
<target>เกิดข้อผิดพลาดในการลบการเชื่อมต่อ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting contact" xml:space="preserve">
<source>Error deleting contact</source>
<target>เกิดข้อผิดพลาดในการลบผู้ติดต่อ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting database" xml:space="preserve">
<source>Error deleting database</source>
<target>เกิดข้อผิดพลาดในการลบฐานข้อมูล</target>
@@ -2795,7 +2869,7 @@ This cannot be undone!</source>
<trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source>
<target>ที่ชอบ</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="File error" xml:space="preserve">
<source>File error</source>
@@ -3503,6 +3577,10 @@ Error: %2$@</source>
3. การเชื่อมต่อถูกบุกรุก</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It protects your IP address and connections." xml:space="preserve">
<source>It protects your IP address and connections.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It seems like you are already connected via this link. If it is not the case, there was an error (%@)." xml:space="preserve">
<source>It seems like you are already connected via this link. If it is not the case, there was an error (%@).</source>
<target>ดูเหมือนว่าคุณได้เชื่อมต่อผ่านลิงก์นี้แล้ว หากไม่เป็นเช่นนั้น แสดงว่ามีข้อผิดพลาด (%@).</target>
@@ -3521,7 +3599,7 @@ Error: %2$@</source>
<trans-unit id="Join" xml:space="preserve">
<source>Join</source>
<target>เข้าร่วม</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Join group" xml:space="preserve">
<source>Join group</source>
@@ -3559,6 +3637,10 @@ This is your link for group %@!</source>
<source>Keep</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep conversation" xml:space="preserve">
<source>Keep conversation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
<source>Keep the app open to use it from desktop</source>
<note>No comment provided by engineer.</note>
@@ -3600,7 +3682,7 @@ This is your link for group %@!</source>
<trans-unit id="Leave" xml:space="preserve">
<source>Leave</source>
<target>ออกจาก</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Leave group" xml:space="preserve">
<source>Leave group</source>
@@ -3729,6 +3811,10 @@ This is your link for group %@!</source>
<target>สูงสุด 30 วินาที รับทันที</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Media &amp; file servers" xml:space="preserve">
<source>Media &amp; file servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Medium" xml:space="preserve">
<source>Medium</source>
<note>blur media</note>
@@ -3811,12 +3897,8 @@ This is your link for group %@!</source>
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing mode" xml:space="preserve">
<source>Message routing mode</source>
<trans-unit id="Message servers" xml:space="preserve">
<source>Message servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message source remains private." xml:space="preserve">
@@ -3959,7 +4041,7 @@ This is your link for group %@!</source>
<trans-unit id="Mute" xml:space="preserve">
<source>Mute</source>
<target>ปิดเสียง</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Muted when inactive!" xml:space="preserve">
<source>Muted when inactive!</source>
@@ -3969,7 +4051,7 @@ This is your link for group %@!</source>
<trans-unit id="Name" xml:space="preserve">
<source>Name</source>
<target>ชื่อ</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Network &amp; servers" xml:space="preserve">
<source>Network &amp; servers</source>
@@ -4007,6 +4089,10 @@ This is your link for group %@!</source>
<source>New chat</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat experience 🎉" xml:space="preserve">
<source>New chat experience 🎉</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New contact request" xml:space="preserve">
<source>New contact request</source>
<target>คำขอติดต่อใหม่</target>
@@ -4036,6 +4122,10 @@ This is your link for group %@!</source>
<target>ใหม่ใน %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New media options" xml:space="preserve">
<source>New media options</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member role" xml:space="preserve">
<source>New member role</source>
<target>บทบาทของสมาชิกใหม่</target>
@@ -4173,13 +4263,15 @@ This is your link for group %@!</source>
<target>ลิงก์คำเชิญแบบใช้ครั้งเดียว</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be required for connection. Requires enabling VPN." xml:space="preserve">
<source>Onion hosts will be required for connection. Requires enabling VPN.</source>
<trans-unit id="Onion hosts will be **required** for connection.&#10;Requires compatible VPN." xml:space="preserve">
<source>Onion hosts will be **required** for connection.
Requires compatible VPN.</source>
<target>จำเป็นต้องมีโฮสต์หัวหอมสำหรับการเชื่อมต่อ ต้องเปิดใช้งาน VPN สำหรับการเชื่อมต่อ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be used when available. Requires enabling VPN." xml:space="preserve">
<source>Onion hosts will be used when available. Requires enabling VPN.</source>
<trans-unit id="Onion hosts will be used when available.&#10;Requires compatible VPN." xml:space="preserve">
<source>Onion hosts will be used when available.
Requires compatible VPN.</source>
<target>จำเป็นต้องมีโฮสต์หัวหอมสำหรับการเชื่อมต่อ ต้องเปิดใช้งาน VPN สำหรับการเชื่อมต่อ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -4193,6 +4285,10 @@ This is your link for group %@!</source>
<target>เฉพาะอุปกรณ์ไคลเอนต์เท่านั้นที่จัดเก็บโปรไฟล์ผู้ใช้ ผู้ติดต่อ กลุ่ม และข้อความที่ส่งด้วย **การเข้ารหัส encrypt แบบ 2 ชั้น**</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only delete conversation" xml:space="preserve">
<source>Only delete conversation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
<source>Only group owners can change group preferences.</source>
<target>เฉพาะเจ้าของกลุ่มเท่านั้นที่สามารถเปลี่ยนค่ากําหนดลักษณะกลุ่มได้</target>
@@ -4411,6 +4507,14 @@ This is your link for group %@!</source>
<source>Picture-in-picture calls</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Play from the chat list." xml:space="preserve">
<source>Play from the chat list.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please ask your contact to enable calls." xml:space="preserve">
<source>Please ask your contact to enable calls.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please ask your contact to enable sending voice messages." xml:space="preserve">
<source>Please ask your contact to enable sending voice messages.</source>
<target>โปรดขอให้ผู้ติดต่อของคุณเปิดใช้งานการส่งข้อความเสียง</target>
@@ -4689,6 +4793,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>ให้คะแนนแอป</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reachable chat toolbar" xml:space="preserve">
<source>Reachable chat toolbar</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="React…" xml:space="preserve">
<source>React…</source>
<target>ตอบสนอง…</target>
@@ -4697,7 +4805,7 @@ Enable in *Network &amp; servers* settings.</source>
<trans-unit id="Read" xml:space="preserve">
<source>Read</source>
<target>อ่าน</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Read more" xml:space="preserve">
<source>Read more</source>
@@ -4844,7 +4952,8 @@ Enable in *Network &amp; servers* settings.</source>
<trans-unit id="Reject" xml:space="preserve">
<source>Reject</source>
<target>ปฏิเสธ</target>
<note>reject incoming call via notification</note>
<note>reject incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Reject (sender NOT notified)" xml:space="preserve">
<source>Reject (sender NOT notified)</source>
@@ -4939,6 +5048,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>รีเซ็ต</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all hints" xml:space="preserve">
<source>Reset all hints</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all statistics" xml:space="preserve">
<source>Reset all statistics</source>
<note>No comment provided by engineer.</note>
@@ -5004,11 +5117,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>เปิดเผย</target>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Revert" xml:space="preserve">
<source>Revert</source>
<target>เปลี่ยนกลับ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>ถอน</target>
@@ -5038,11 +5146,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>SMP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SMP servers" xml:space="preserve">
<source>SMP servers</source>
<target>เซิร์ฟเวอร์ SMP</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safely receive files" xml:space="preserve">
<source>Safely receive files</source>
<note>No comment provided by engineer.</note>
@@ -5071,6 +5174,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>บันทึกและแจ้งให้สมาชิกในกลุ่มทราบ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save and reconnect" xml:space="preserve">
<source>Save and reconnect</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save and update group profile" xml:space="preserve">
<source>Save and update group profile</source>
<target>บันทึกและอัปเดตโปรไฟล์กลุ่ม</target>
@@ -5264,11 +5371,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>ส่งใบเสร็จรับการจัดส่งข้อความไปที่</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send direct message" xml:space="preserve">
<source>Send direct message</source>
<target>ส่งข้อความโดยตรง</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send direct message to connect" xml:space="preserve">
<source>Send direct message to connect</source>
<note>No comment provided by engineer.</note>
@@ -5292,6 +5394,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>ส่งข้อความสด</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send message to enable calls." xml:space="preserve">
<source>Send message to enable calls.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when IP address is protected and your or destination server does not support private routing." xml:space="preserve">
<source>Send messages directly when IP address is protected and your or destination server does not support private routing.</source>
<note>No comment provided by engineer.</note>
@@ -5555,6 +5661,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>แชร์ที่อยู่กับผู้ติดต่อ?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share from other apps." xml:space="preserve">
<source>Share from other apps.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share link" xml:space="preserve">
<source>Share link</source>
<target>แชร์ลิงก์</target>
@@ -5711,11 +5821,19 @@ Enable in *Network &amp; servers* settings.</source>
<source>Soft</source>
<note>blur media</note>
</trans-unit>
<trans-unit id="Some file(s) were not exported:" xml:space="preserve">
<source>Some file(s) were not exported:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some non-fatal errors occurred during import - you may see Chat console for more details." xml:space="preserve">
<source>Some non-fatal errors occurred during import - you may see Chat console for more details.</source>
<target>ข้อผิดพลาดที่ไม่ร้ายแรงบางอย่างเกิดขึ้นระหว่างการนำเข้า - คุณอาจดูรายละเอียดเพิ่มเติมได้ที่คอนโซล Chat</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some non-fatal errors occurred during import:" xml:space="preserve">
<source>Some non-fatal errors occurred during import:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Somebody" xml:space="preserve">
<source>Somebody</source>
<target>ใครบางคน</target>
@@ -5841,6 +5959,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>การรับรองความถูกต้องของระบบ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="TCP connection" xml:space="preserve">
<source>TCP connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="TCP connection timeout" xml:space="preserve">
<source>TCP connection timeout</source>
<target>หมดเวลาการเชื่อมต่อ TCP</target>
@@ -5898,11 +6020,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Tap to scan</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Tap to start a new chat" xml:space="preserve">
<source>Tap to start a new chat</source>
<target>แตะเพื่อเริ่มแชทใหม่</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Temporary file error" xml:space="preserve">
<source>Temporary file error</source>
<note>No comment provided by engineer.</note>
@@ -6184,10 +6301,18 @@ You will be prompted to complete authentication before this feature is enabled.<
<target>ในการตรวจสอบการเข้ารหัสแบบ encrypt จากต้นจนจบ กับผู้ติดต่อของคุณ ให้เปรียบเทียบ (หรือสแกน) รหัสบนอุปกรณ์ของคุณ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toggle chat list:" xml:space="preserve">
<source>Toggle chat list:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toggle incognito when connecting." xml:space="preserve">
<source>Toggle incognito when connecting.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toolbar opacity" xml:space="preserve">
<source>Toolbar opacity</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Total" xml:space="preserve">
<source>Total</source>
<note>No comment provided by engineer.</note>
@@ -6258,7 +6383,7 @@ You will be prompted to complete authentication before this feature is enabled.<
<trans-unit id="Unfav." xml:space="preserve">
<source>Unfav.</source>
<target>เลิกชอบ</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Unhide" xml:space="preserve">
<source>Unhide</source>
@@ -6332,12 +6457,12 @@ To connect, please ask your contact to create another connection link and check
<trans-unit id="Unmute" xml:space="preserve">
<source>Unmute</source>
<target>เปิดเสียง</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Unread" xml:space="preserve">
<source>Unread</source>
<target>เปลี่ยนเป็นยังไม่ได้อ่าน</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Up to 100 last messages are sent to new members." xml:space="preserve">
<source>Up to 100 last messages are sent to new members.</source>
@@ -6348,11 +6473,6 @@ To connect, please ask your contact to create another connection link and check
<target>อัปเดต</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update .onion hosts setting?" xml:space="preserve">
<source>Update .onion hosts setting?</source>
<target>อัปเดตการตั้งค่าโฮสต์ .onion ไหม?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update database passphrase" xml:space="preserve">
<source>Update database passphrase</source>
<target>อัปเดตรหัสผ่านของฐานข้อมูล</target>
@@ -6363,9 +6483,8 @@ To connect, please ask your contact to create another connection link and check
<target>อัปเดตการตั้งค่าเครือข่ายไหม?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update transport isolation mode?" xml:space="preserve">
<source>Update transport isolation mode?</source>
<target>อัปเดตโหมดการแยกการขนส่งไหม?</target>
<trans-unit id="Update settings?" xml:space="preserve">
<source>Update settings?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Updating settings will re-connect the client to all servers." xml:space="preserve">
@@ -6373,11 +6492,6 @@ To connect, please ask your contact to create another connection link and check
<target>การอัปเดตการตั้งค่าจะเชื่อมต่อไคลเอนต์กับเซิร์ฟเวอร์ทั้งหมดอีกครั้ง</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Updating this setting will re-connect the client to all servers." xml:space="preserve">
<source>Updating this setting will re-connect the client to all servers.</source>
<target>การอัปเดตการตั้งค่านี้จะเชื่อมต่อไคลเอนต์กับเซิร์ฟเวอร์ทั้งหมดอีกครั้ง</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Upgrade and open chat" xml:space="preserve">
<source>Upgrade and open chat</source>
<target>อัปเกรดและเปิดการแชท</target>
@@ -6466,6 +6580,10 @@ To connect, please ask your contact to create another connection link and check
<source>Use the app while in the call.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use the app with one hand." xml:space="preserve">
<source>Use the app with one hand.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>โปรไฟล์ผู้ใช้</target>
@@ -6475,11 +6593,6 @@ To connect, please ask your contact to create another connection link and check
<source>User selection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using .onion hosts requires compatible VPN provider." xml:space="preserve">
<source>Using .onion hosts requires compatible VPN provider.</source>
<target>การใช้โฮสต์ .onion ต้องการผู้ให้บริการ VPN ที่เข้ากันได้</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using SimpleX Chat servers." xml:space="preserve">
<source>Using SimpleX Chat servers.</source>
<target>กำลังใช้เซิร์ฟเวอร์ SimpleX Chat อยู่</target>
@@ -6716,11 +6829,6 @@ To connect, please ask your contact to create another connection link and check
<source>XFTP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="XFTP servers" xml:space="preserve">
<source>XFTP servers</source>
<target>เซิร์ฟเวอร์ XFTP</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>คุณ</target>
@@ -6798,6 +6906,10 @@ Repeat join request?</source>
<target>คุณสามารถรับสายจากหน้าจอล็อกโดยไม่ต้องมีการตรวจสอบสิทธิ์อุปกรณ์และแอป</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can change it in Appearance settings." xml:space="preserve">
<source>You can change it in Appearance settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can create it later" xml:space="preserve">
<source>You can create it later</source>
<target>คุณสามารถสร้างได้ในภายหลัง</target>
@@ -6831,6 +6943,10 @@ Repeat join request?</source>
<target>ตอนนี้คุณสามารถส่งข้อความถึง %@</target>
<note>notification body</note>
</trans-unit>
<trans-unit id="You can send messages to %@ from Archived contacts." xml:space="preserve">
<source>You can send messages to %@ from Archived contacts.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can set lock screen notification preview via settings." xml:space="preserve">
<source>You can set lock screen notification preview via settings.</source>
<target>คุณสามารถตั้งค่าแสดงตัวอย่างการแจ้งเตือนบนหน้าจอล็อคผ่านการตั้งค่า</target>
@@ -6856,6 +6972,10 @@ Repeat join request?</source>
<target>คุณสามารถเริ่มแชทผ่านการตั้งค่าแอป / ฐานข้อมูล หรือโดยการรีสตาร์ทแอป</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can still view conversation with %@ in the list of chats." xml:space="preserve">
<source>You can still view conversation with %@ in the list of chats.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can turn on SimpleX Lock via Settings." xml:space="preserve">
<source>You can turn on SimpleX Lock via Settings.</source>
<target>คุณสามารถเปิด SimpleX Lock ผ่านการตั้งค่า</target>
@@ -6894,11 +7014,6 @@ Repeat join request?</source>
Repeat connection request?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You have no chats" xml:space="preserve">
<source>You have no chats</source>
<target>คุณไม่มีการแชท</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You have to enter passphrase every time the app starts - it is not stored on the device." xml:space="preserve">
<source>You have to enter passphrase every time the app starts - it is not stored on the device.</source>
<target>คุณต้องใส่รหัสผ่านทุกครั้งที่เริ่มแอป - รหัสผ่านไม่ได้จัดเก็บไว้ในอุปกรณ์</target>
@@ -6918,11 +7033,23 @@ Repeat connection request?</source>
<target>คุณเข้าร่วมกลุ่มนี้แล้ว กำลังเชื่อมต่อเพื่อเชิญสมาชิกกลุ่ม</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You may migrate the exported database." xml:space="preserve">
<source>You may migrate the exported database.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You may save the exported archive." xml:space="preserve">
<source>You may save the exported archive.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="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." xml:space="preserve">
<source>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.</source>
<target>คุณต้องใช้ฐานข้อมูลแชทเวอร์ชันล่าสุดบนอุปกรณ์เครื่องเดียวเท่านั้น มิฉะนั้น คุณอาจหยุดได้รับข้อความจากผู้ติดต่อบางคน</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You need to allow your contact to call to be able to call them." xml:space="preserve">
<source>You need to allow your contact to call to be able to call them.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You need to allow your contact to send voice messages to be able to send them." xml:space="preserve">
<source>You need to allow your contact to send voice messages to be able to send them.</source>
<target>คุณต้องอนุญาตให้ผู้ติดต่อของคุณส่งข้อความเสียงจึงจะสามารถส่งได้</target>
@@ -7036,13 +7163,6 @@ Repeat connection request?</source>
<target>โปรไฟล์แชทของคุณ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact needs to be online for the connection to complete.&#10;You can cancel this connection and remove the contact (and try later with a new link)." xml:space="preserve">
<source>Your contact needs to be online for the connection to complete.
You can cancel this connection and remove the contact (and try later with a new link).</source>
<target>ผู้ติดต่อของคุณจะต้องออนไลน์เพื่อให้การเชื่อมต่อเสร็จสมบูรณ์
คุณสามารถยกเลิกการเชื่อมต่อนี้และลบผู้ติดต่อออก (และลองใหม่ในภายหลังด้วยลิงก์ใหม่)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
<target>ผู้ติดต่อของคุณส่งไฟล์ที่ใหญ่กว่าขนาดสูงสุดที่รองรับในปัจจุบัน (%@)</target>
@@ -7225,6 +7345,10 @@ SimpleX servers cannot see your profile.</source>
<target>ตัวหนา</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="call" xml:space="preserve">
<source>call</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="call error" xml:space="preserve">
<source>call error</source>
<target>การโทรผิดพลาด</target>
@@ -7585,6 +7709,10 @@ SimpleX servers cannot see your profile.</source>
<target>คำเชิญเข้าร่วมกลุ่ม %@</target>
<note>group name</note>
</trans-unit>
<trans-unit id="invite" xml:space="preserve">
<source>invite</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="invited" xml:space="preserve">
<source>invited</source>
<target>เชิญ</target>
@@ -7639,6 +7767,10 @@ SimpleX servers cannot see your profile.</source>
<target>เชื่อมต่อสำเร็จ</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message received" xml:space="preserve">
<source>message received</source>
<target>ข้อความที่ได้รับ</target>
@@ -7669,6 +7801,10 @@ SimpleX servers cannot see your profile.</source>
<target>เดือน</target>
<note>time unit</note>
</trans-unit>
<trans-unit id="mute" xml:space="preserve">
<source>mute</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="never" xml:space="preserve">
<source>never</source>
<target>ไม่เคย</target>
@@ -7793,6 +7929,10 @@ SimpleX servers cannot see your profile.</source>
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="search" xml:space="preserve">
<source>search</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve">
<source>sec</source>
<target>วินาที</target>
@@ -7859,14 +7999,18 @@ last received msg: %2$@</source>
<target>ไม่ทราบ</target>
<note>connection info</note>
</trans-unit>
<trans-unit id="unknown relays" xml:space="preserve">
<source>unknown relays</source>
<trans-unit id="unknown servers" xml:space="preserve">
<source>unknown servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unknown status" xml:space="preserve">
<source>unknown status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unmute" xml:space="preserve">
<source>unmute</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unprotected" xml:space="preserve">
<source>unprotected</source>
<note>No comment provided by engineer.</note>
@@ -7909,6 +8053,10 @@ last received msg: %2$@</source>
<target>ผ่านรีเลย์</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="video" xml:space="preserve">
<source>video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="video call (not e2e encrypted)" xml:space="preserve">
<source>video call (not e2e encrypted)</source>
<target>การสนทนาทางวิดีโอ (ไม่ได้ encrypt จากต้นจนจบ)</target>
@@ -8151,10 +8299,6 @@ last received msg: %2$@</source>
<source>Database upgrade required</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Dismiss Sheet" xml:space="preserve">
<source>Dismiss Sheet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error preparing file" xml:space="preserve">
<source>Error preparing file</source>
<note>No comment provided by engineer.</note>
@@ -8179,10 +8323,6 @@ last received msg: %2$@</source>
<source>Invalid migration confirmation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep Trying" xml:space="preserve">
<source>Keep Trying</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keychain error" xml:space="preserve">
<source>Keychain error</source>
<note>No comment provided by engineer.</note>
@@ -8195,10 +8335,6 @@ last received msg: %2$@</source>
<source>No active profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<note>No comment provided by engineer.</note>
@@ -8223,14 +8359,22 @@ last received msg: %2$@</source>
<source>Selected chat preferences prohibit this message.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sending File" xml:space="preserve">
<source>Sending File</source>
<trans-unit id="Sending a message takes longer than expected." xml:space="preserve">
<source>Sending a message takes longer than expected.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sending message…" xml:space="preserve">
<source>Sending message…</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share" xml:space="preserve">
<source>Share</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Slow network?" xml:space="preserve">
<source>Slow network?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unknown database error: %@" xml:space="preserve">
<source>Unknown database error: %@</source>
<note>No comment provided by engineer.</note>
@@ -8239,6 +8383,10 @@ last received msg: %2$@</source>
<source>Unsupported format</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wait" xml:space="preserve">
<source>Wait</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wrong database passphrase" xml:space="preserve">
<source>Wrong database passphrase</source>
<note>No comment provided by engineer.</note>
@@ -560,7 +560,8 @@
<source>Accept</source>
<target>Kabul et</target>
<note>accept contact request via notification
accept incoming call via notification</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept connection request?" xml:space="preserve">
<source>Accept connection request?</source>
@@ -575,7 +576,8 @@
<trans-unit id="Accept incognito" xml:space="preserve">
<source>Accept incognito</source>
<target>Takma adla kabul et</target>
<note>accept contact request via notification</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Acknowledged" xml:space="preserve">
<source>Acknowledged</source>
@@ -738,6 +740,10 @@
<target>Yalnızca irtibat kişiniz izin veriyorsa aramalara izin verin.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow calls?" xml:space="preserve">
<source>Allow calls?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow disappearing messages only if your contact allows it to you." xml:space="preserve">
<source>Allow disappearing messages only if your contact allows it to you.</source>
<target>Eğer kişide izin verirse kaybolan mesajlara izin ver.</target>
@@ -926,6 +932,14 @@
<target>Arşivle ve yükle</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archive contacts to chat later." xml:space="preserve">
<source>Archive contacts to chat later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archived contacts" xml:space="preserve">
<source>Archived contacts</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archiving database" xml:space="preserve">
<source>Archiving database</source>
<target>Veritabanı arşivleniyor</target>
@@ -1025,6 +1039,10 @@
<target>Daha iyi mesajlar</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better networking" xml:space="preserve">
<source>Better networking</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Black" xml:space="preserve">
<source>Black</source>
<note>No comment provided by engineer.</note>
@@ -1064,6 +1082,10 @@
<target>Yönetici tarafından engellendi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Blur for better privacy." xml:space="preserve">
<source>Blur for better privacy.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Blur media" xml:space="preserve">
<source>Blur media</source>
<note>No comment provided by engineer.</note>
@@ -1113,11 +1135,23 @@
<target>Aramalar</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Calls prohibited!" xml:space="preserve">
<source>Calls prohibited!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Camera not available" xml:space="preserve">
<source>Camera not available</source>
<target>Kamera mevcut değil</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't call contact" xml:space="preserve">
<source>Can't call contact</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't call member" xml:space="preserve">
<source>Can't call member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't invite contact!" xml:space="preserve">
<source>Can't invite contact!</source>
<target>Kişi davet edilemiyor!</target>
@@ -1128,6 +1162,10 @@
<target>Kişiler davet edilemiyor!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't message member" xml:space="preserve">
<source>Can't message member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cancel" xml:space="preserve">
<source>Cancel</source>
<target>İptal et</target>
@@ -1237,6 +1275,10 @@
<target>Sohbet veritabanı silindi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database exported" xml:space="preserve">
<source>Chat database exported</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database imported" xml:space="preserve">
<source>Chat database imported</source>
<target>Sohbet veritabanı içe aktarıldı</target>
@@ -1257,6 +1299,10 @@
<target>Sohbet durduruldu. Bu veritabanını zaten başka bir cihazda kullandıysanız, sohbete başlamadan önce onu geri aktarmalısınız.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat list" xml:space="preserve">
<source>Chat list</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat migrated!" xml:space="preserve">
<source>Chat migrated!</source>
<target>Sohbet taşındı!</target>
@@ -1316,7 +1362,7 @@
<trans-unit id="Clear" xml:space="preserve">
<source>Clear</source>
<target>Temizle</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Clear conversation" xml:space="preserve">
<source>Clear conversation</source>
@@ -1338,6 +1384,10 @@
<target>Doğrulamayı temizle</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Color chats with the new themes." xml:space="preserve">
<source>Color chats with the new themes.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Color mode" xml:space="preserve">
<source>Color mode</source>
<note>No comment provided by engineer.</note>
@@ -1375,6 +1425,10 @@
<target>Parolayı onayla</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm contact deletion?" xml:space="preserve">
<source>Confirm contact deletion?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm database upgrades" xml:space="preserve">
<source>Confirm database upgrades</source>
<target>Veritabanı geliştirmelerini onayla</target>
@@ -1430,6 +1484,10 @@
<target>Bilgisayara bağlan</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect to your friends faster." xml:space="preserve">
<source>Connect to your friends faster.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect to yourself?" xml:space="preserve">
<source>Connect to yourself?</source>
<target>Kendine mi bağlanacaksın?</target>
@@ -1501,6 +1559,10 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
<target>Sunucuya bağlanıyor…(hata:%@)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting to contact, please wait or check later!" xml:space="preserve">
<source>Connecting to contact, please wait or check later!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting to desktop" xml:space="preserve">
<source>Connecting to desktop</source>
<target>Bilgisayara bağlanıyor</target>
@@ -1511,6 +1573,10 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
<target>Bağlantı</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connection and servers status." xml:space="preserve">
<source>Connection and servers status.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connection error" xml:space="preserve">
<source>Connection error</source>
<target>Bağlantı hatası</target>
@@ -1558,6 +1624,10 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
<target>Kişi zaten mevcut</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact deleted!" xml:space="preserve">
<source>Contact deleted!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact hidden:" xml:space="preserve">
<source>Contact hidden:</source>
<target>Kişi gizli:</target>
@@ -1568,9 +1638,8 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
<target>Kişi bağlandı</target>
<note>notification</note>
</trans-unit>
<trans-unit id="Contact is not connected yet!" xml:space="preserve">
<source>Contact is not connected yet!</source>
<target>Kişi şuan bağlanmadı!</target>
<trans-unit id="Contact is deleted." xml:space="preserve">
<source>Contact is deleted.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact name" xml:space="preserve">
@@ -1583,6 +1652,10 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
<target>Kişi tercihleri</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact will be deleted - this cannot be undone!" xml:space="preserve">
<source>Contact will be deleted - this cannot be undone!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contacts" xml:space="preserve">
<source>Contacts</source>
<target>Kişiler</target>
@@ -1598,6 +1671,10 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
<target>Devam et</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Conversation deleted!" xml:space="preserve">
<source>Conversation deleted!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy" xml:space="preserve">
<source>Copy</source>
<target>Kopyala</target>
@@ -1864,7 +1941,8 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
<trans-unit id="Delete" xml:space="preserve">
<source>Delete</source>
<target>Sil</target>
<note>chat item action</note>
<note>chat item action
swipe action</note>
</trans-unit>
<trans-unit id="Delete %lld messages of members?" xml:space="preserve">
<source>Delete %lld messages of members?</source>
@@ -1875,11 +1953,6 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
<target>%lld mesaj silinsin mi?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete Contact" xml:space="preserve">
<source>Delete Contact</source>
<target>Kişiyi sil</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete address" xml:space="preserve">
<source>Delete address</source>
<target>Adresi sil</target>
@@ -1935,11 +2008,8 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
<target>Kişiyi sil</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete contact?&#10;This cannot be undone!" xml:space="preserve">
<source>Delete contact?
This cannot be undone!</source>
<target>Kişi silinsin mi?
Bu geri alınamaz!</target>
<trans-unit id="Delete contact?" xml:space="preserve">
<source>Delete contact?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete database" xml:space="preserve">
@@ -2032,11 +2102,6 @@ Bu geri alınamaz!</target>
<target>Eski veritabanı silinsin mi?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete pending connection" xml:space="preserve">
<source>Delete pending connection</source>
<target>Bekleyen bağlantıyı sil</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete pending connection?" xml:space="preserve">
<source>Delete pending connection?</source>
<target>Bekleyen bağlantı silinsin mi?</target>
@@ -2052,11 +2117,19 @@ Bu geri alınamaz!</target>
<target>Sırayı sil</target>
<note>server test step</note>
</trans-unit>
<trans-unit id="Delete up to 20 messages at once." xml:space="preserve">
<source>Delete up to 20 messages at once.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete user profile?" xml:space="preserve">
<source>Delete user profile?</source>
<target>Kullanıcı profili silinsin mi?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete without notification" xml:space="preserve">
<source>Delete without notification</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Deleted" xml:space="preserve">
<source>Deleted</source>
<note>No comment provided by engineer.</note>
@@ -2136,6 +2209,10 @@ Bu geri alınamaz!</target>
<target>Geliştir</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Developer options" xml:space="preserve">
<source>Developer options</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Developer tools" xml:space="preserve">
<source>Developer tools</source>
<target>Geliştirici araçları</target>
@@ -2640,11 +2717,6 @@ Bu geri alınamaz!</target>
<target>Bağlantı silinirken hata oluştu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting contact" xml:space="preserve">
<source>Error deleting contact</source>
<target>Kişi silinirken hata oluştu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting database" xml:space="preserve">
<source>Error deleting database</source>
<target>Veritabanı silinirken hata oluştu</target>
@@ -2933,7 +3005,7 @@ Bu geri alınamaz!</target>
<trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source>
<target>Favori</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="File error" xml:space="preserve">
<source>File error</source>
@@ -3675,6 +3747,10 @@ Hata: %2$@</target>
3. Bağlantı tehlikeye girmiştir.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It protects your IP address and connections." xml:space="preserve">
<source>It protects your IP address and connections.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It seems like you are already connected via this link. If it is not the case, there was an error (%@)." xml:space="preserve">
<source>It seems like you are already connected via this link. If it is not the case, there was an error (%@).</source>
<target>Bu bağlantı üzerinden zaten bağlanmışsınız gibi görünüyor. Eğer durum böyle değilse, bir hata oluştu (%@).</target>
@@ -3693,7 +3769,7 @@ Hata: %2$@</target>
<trans-unit id="Join" xml:space="preserve">
<source>Join</source>
<target>Katıl</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Join group" xml:space="preserve">
<source>Join group</source>
@@ -3737,6 +3813,10 @@ Bu senin grup için bağlantın %@!</target>
<target>Tut</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep conversation" xml:space="preserve">
<source>Keep conversation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
<source>Keep the app open to use it from desktop</source>
<target>Bilgisayardan kullanmak için uygulamayı açık tut</target>
@@ -3780,7 +3860,7 @@ Bu senin grup için bağlantın %@!</target>
<trans-unit id="Leave" xml:space="preserve">
<source>Leave</source>
<target>Ayrıl</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Leave group" xml:space="preserve">
<source>Leave group</source>
@@ -3912,6 +3992,10 @@ Bu senin grup için bağlantın %@!</target>
<target>Maksimum 30 saniye, anında alındı.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Media &amp; file servers" xml:space="preserve">
<source>Media &amp; file servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Medium" xml:space="preserve">
<source>Medium</source>
<note>blur media</note>
@@ -3996,14 +4080,8 @@ Bu senin grup için bağlantın %@!</target>
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<target>Mesaj yönlendirme yedeklemesi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing mode" xml:space="preserve">
<source>Message routing mode</source>
<target>Mesaj yönlendirme modu</target>
<trans-unit id="Message servers" xml:space="preserve">
<source>Message servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message source remains private." xml:space="preserve">
@@ -4160,7 +4238,7 @@ Bu senin grup için bağlantın %@!</target>
<trans-unit id="Mute" xml:space="preserve">
<source>Mute</source>
<target>Sustur</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Muted when inactive!" xml:space="preserve">
<source>Muted when inactive!</source>
@@ -4170,7 +4248,7 @@ Bu senin grup için bağlantın %@!</target>
<trans-unit id="Name" xml:space="preserve">
<source>Name</source>
<target>İsim</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Network &amp; servers" xml:space="preserve">
<source>Network &amp; servers</source>
@@ -4212,6 +4290,10 @@ Bu senin grup için bağlantın %@!</target>
<target>Yeni sohbet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat experience 🎉" xml:space="preserve">
<source>New chat experience 🎉</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New contact request" xml:space="preserve">
<source>New contact request</source>
<target>Yeni bağlantı isteği</target>
@@ -4242,6 +4324,10 @@ Bu senin grup için bağlantın %@!</target>
<target>%@ da yeni</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New media options" xml:space="preserve">
<source>New media options</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member role" xml:space="preserve">
<source>New member role</source>
<target>Yeni üye rolü</target>
@@ -4383,14 +4469,18 @@ Bu senin grup için bağlantın %@!</target>
<target>Tek zamanlı bağlantı daveti</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be required for connection. Requires enabling VPN." xml:space="preserve">
<source>Onion hosts will be required for connection. Requires enabling VPN.</source>
<target>Bağlantı için Onion ana bilgisayarları gerekecektir. VPN'nin etkinleştirilmesi gerekir.</target>
<trans-unit id="Onion hosts will be **required** for connection.&#10;Requires compatible VPN." xml:space="preserve">
<source>Onion hosts will be **required** for connection.
Requires compatible VPN.</source>
<target>Bağlantı için Onion ana bilgisayarları gerekecektir.
VPN'nin etkinleştirilmesi gerekir.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be used when available. Requires enabling VPN." xml:space="preserve">
<source>Onion hosts will be used when available. Requires enabling VPN.</source>
<target>Onion ana bilgisayarları mevcutsa kullanılacaktır. VPN'nin etkinleştirilmesi gerekir.</target>
<trans-unit id="Onion hosts will be used when available.&#10;Requires compatible VPN." xml:space="preserve">
<source>Onion hosts will be used when available.
Requires compatible VPN.</source>
<target>Onion ana bilgisayarları mevcutsa kullanılacaktır.
VPN'nin etkinleştirilmesi gerekir.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will not be used." xml:space="preserve">
@@ -4403,6 +4493,10 @@ Bu senin grup için bağlantın %@!</target>
<target>Yalnızca istemci cihazlar kullanıcı profillerini, kişileri, grupları ve **2 katmanlı uçtan uca şifreleme** ile gönderilen mesajları depolar.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only delete conversation" xml:space="preserve">
<source>Only delete conversation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
<source>Only group owners can change group preferences.</source>
<target>Grup tercihlerini yalnızca grup sahipleri değiştirebilir.</target>
@@ -4635,6 +4729,14 @@ Bu senin grup için bağlantın %@!</target>
<target>Resim içinde resim aramaları</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Play from the chat list." xml:space="preserve">
<source>Play from the chat list.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please ask your contact to enable calls." xml:space="preserve">
<source>Please ask your contact to enable calls.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please ask your contact to enable sending voice messages." xml:space="preserve">
<source>Please ask your contact to enable sending voice messages.</source>
<target>Lütfen konuştuğunuz kişiden sesli mesaj göndermeyi etkinleştirmesini isteyin.</target>
@@ -4929,6 +5031,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Uygulamayı değerlendir</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reachable chat toolbar" xml:space="preserve">
<source>Reachable chat toolbar</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="React…" xml:space="preserve">
<source>React…</source>
<target>Tepki ver…</target>
@@ -4937,7 +5043,7 @@ Enable in *Network &amp; servers* settings.</source>
<trans-unit id="Read" xml:space="preserve">
<source>Read</source>
<target>Oku</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Read more" xml:space="preserve">
<source>Read more</source>
@@ -5088,7 +5194,8 @@ Enable in *Network &amp; servers* settings.</source>
<trans-unit id="Reject" xml:space="preserve">
<source>Reject</source>
<target>Reddet</target>
<note>reject incoming call via notification</note>
<note>reject incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Reject (sender NOT notified)" xml:space="preserve">
<source>Reject (sender NOT notified)</source>
@@ -5189,6 +5296,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Sıfırla</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all hints" xml:space="preserve">
<source>Reset all hints</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all statistics" xml:space="preserve">
<source>Reset all statistics</source>
<note>No comment provided by engineer.</note>
@@ -5255,11 +5366,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>Göster</target>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Revert" xml:space="preserve">
<source>Revert</source>
<target>Geri al</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>İptal et</target>
@@ -5289,11 +5395,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>SMP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SMP servers" xml:space="preserve">
<source>SMP servers</source>
<target>SMP sunucuları</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safely receive files" xml:space="preserve">
<source>Safely receive files</source>
<target>Dosyaları güvenle alın</target>
@@ -5324,6 +5425,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Kaydet ve grup üyelerine bildir</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save and reconnect" xml:space="preserve">
<source>Save and reconnect</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save and update group profile" xml:space="preserve">
<source>Save and update group profile</source>
<target>Kaydet ve grup profilini güncelle</target>
@@ -5523,11 +5628,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>Görüldü bilgilerini şuraya gönder</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send direct message" xml:space="preserve">
<source>Send direct message</source>
<target>Doğrudan mesaj gönder</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send direct message to connect" xml:space="preserve">
<source>Send direct message to connect</source>
<target>Bağlanmak için doğrudan mesaj gönder</target>
@@ -5552,6 +5652,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Canlı mesaj gönder</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send message to enable calls." xml:space="preserve">
<source>Send message to enable calls.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when IP address is protected and your or destination server does not support private routing." xml:space="preserve">
<source>Send messages directly when IP address is protected and your or destination server does not support private routing.</source>
<target>IP adresi korumalı olduğunda ve sizin veya hedef sunucunun özel yönlendirmeyi desteklemediği durumlarda mesajları doğrudan gönderin.</target>
@@ -5825,6 +5929,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Kişilerle adres paylaşılsın mı?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share from other apps." xml:space="preserve">
<source>Share from other apps.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share link" xml:space="preserve">
<source>Share link</source>
<target>Bağlantıyı paylaş</target>
@@ -5990,11 +6098,19 @@ Enable in *Network &amp; servers* settings.</source>
<source>Soft</source>
<note>blur media</note>
</trans-unit>
<trans-unit id="Some file(s) were not exported:" xml:space="preserve">
<source>Some file(s) were not exported:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some non-fatal errors occurred during import - you may see Chat console for more details." xml:space="preserve">
<source>Some non-fatal errors occurred during import - you may see Chat console for more details.</source>
<target>İçe aktarma sırasında bazı ölümcül olmayan hatalar oluştu - daha fazla ayrıntı için Sohbet konsoluna bakabilirsiniz.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some non-fatal errors occurred during import:" xml:space="preserve">
<source>Some non-fatal errors occurred during import:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Somebody" xml:space="preserve">
<source>Somebody</source>
<target>Biri</target>
@@ -6124,6 +6240,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Sistem yetkilendirilmesi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="TCP connection" xml:space="preserve">
<source>TCP connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="TCP connection timeout" xml:space="preserve">
<source>TCP connection timeout</source>
<target>TCP bağlantı zaman aşımı</target>
@@ -6184,11 +6304,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>Taramak için tıkla</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Tap to start a new chat" xml:space="preserve">
<source>Tap to start a new chat</source>
<target>Yeni bir sohbet başlatmak için tıkla</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Temporary file error" xml:space="preserve">
<source>Temporary file error</source>
<note>No comment provided by engineer.</note>
@@ -6482,11 +6597,19 @@ Bu özellik etkinleştirilmeden önce kimlik doğrulamayı tamamlamanız istenec
<target>Kişinizle uçtan uca şifrelemeyi doğrulamak için cihazlarınızdaki kodu karşılaştırın (veya tarayın).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toggle chat list:" xml:space="preserve">
<source>Toggle chat list:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toggle incognito when connecting." xml:space="preserve">
<source>Toggle incognito when connecting.</source>
<target>Bağlanırken gizli moda geçiş yap.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toolbar opacity" xml:space="preserve">
<source>Toolbar opacity</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Total" xml:space="preserve">
<source>Total</source>
<note>No comment provided by engineer.</note>
@@ -6563,7 +6686,7 @@ Bu özellik etkinleştirilmeden önce kimlik doğrulamayı tamamlamanız istenec
<trans-unit id="Unfav." xml:space="preserve">
<source>Unfav.</source>
<target>Favorilerden çık.</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Unhide" xml:space="preserve">
<source>Unhide</source>
@@ -6640,12 +6763,12 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste
<trans-unit id="Unmute" xml:space="preserve">
<source>Unmute</source>
<target>Susturmayı kaldır</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Unread" xml:space="preserve">
<source>Unread</source>
<target>Okunmamış</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Up to 100 last messages are sent to new members." xml:space="preserve">
<source>Up to 100 last messages are sent to new members.</source>
@@ -6657,11 +6780,6 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste
<target>Güncelle</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update .onion hosts setting?" xml:space="preserve">
<source>Update .onion hosts setting?</source>
<target>.onion ana bilgisayarların ayarı güncellensin mi?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update database passphrase" xml:space="preserve">
<source>Update database passphrase</source>
<target>Veritabanı parolasını güncelle</target>
@@ -6672,9 +6790,8 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste
<target>Bağlantı ayarları güncellensin mi?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update transport isolation mode?" xml:space="preserve">
<source>Update transport isolation mode?</source>
<target>Taşıma izolasyon modu güncellensin mi?</target>
<trans-unit id="Update settings?" xml:space="preserve">
<source>Update settings?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Updating settings will re-connect the client to all servers." xml:space="preserve">
@@ -6682,11 +6799,6 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste
<target>Ayarların güncellenmesi, istemciyi tüm sunuculara yeniden bağlayacaktır.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Updating this setting will re-connect the client to all servers." xml:space="preserve">
<source>Updating this setting will re-connect the client to all servers.</source>
<target>Bu ayarın güncellenmesi, istemciyi tüm sunuculara yeniden bağlayacaktır.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Upgrade and open chat" xml:space="preserve">
<source>Upgrade and open chat</source>
<target>Yükselt ve sohbeti aç</target>
@@ -6784,6 +6896,10 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste
<target>Görüşme sırasında uygulamayı kullanın.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use the app with one hand." xml:space="preserve">
<source>Use the app with one hand.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>Kullanıcı profili</target>
@@ -6793,11 +6909,6 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste
<source>User selection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using .onion hosts requires compatible VPN provider." xml:space="preserve">
<source>Using .onion hosts requires compatible VPN provider.</source>
<target>.onion ana bilgisayarlarını kullanmak için uyumlu VPN sağlayıcısı gerekir.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using SimpleX Chat servers." xml:space="preserve">
<source>Using SimpleX Chat servers.</source>
<target>SimpleX Chat sunucuları kullanılıyor.</target>
@@ -7054,11 +7165,6 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste
<source>XFTP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="XFTP servers" xml:space="preserve">
<source>XFTP servers</source>
<target>XFTP sunucuları</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>Sen</target>
@@ -7145,6 +7251,10 @@ Katılma isteği tekrarlansın mı?</target>
<target>Cihaz ve uygulama kimlik doğrulaması olmadan kilit ekranından çağrı kabul edebilirsiniz.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can change it in Appearance settings." xml:space="preserve">
<source>You can change it in Appearance settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can create it later" xml:space="preserve">
<source>You can create it later</source>
<target>Daha sonra oluşturabilirsiniz</target>
@@ -7180,6 +7290,10 @@ Katılma isteği tekrarlansın mı?</target>
<target>Artık %@ adresine mesaj gönderebilirsin</target>
<note>notification body</note>
</trans-unit>
<trans-unit id="You can send messages to %@ from Archived contacts." xml:space="preserve">
<source>You can send messages to %@ from Archived contacts.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can set lock screen notification preview via settings." xml:space="preserve">
<source>You can set lock screen notification preview via settings.</source>
<target>Kilit ekranı bildirim önizlemesini ayarlar üzerinden ayarlayabilirsiniz.</target>
@@ -7205,6 +7319,10 @@ Katılma isteği tekrarlansın mı?</target>
<target>Sohbeti uygulamada Ayarlar / Veritabanı üzerinden veya uygulamayı yeniden başlatarak başlatabilirsiniz</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can still view conversation with %@ in the list of chats." xml:space="preserve">
<source>You can still view conversation with %@ in the list of chats.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can turn on SimpleX Lock via Settings." xml:space="preserve">
<source>You can turn on SimpleX Lock via Settings.</source>
<target>SimpleX Kilidini Ayarlar üzerinden açabilirsiniz.</target>
@@ -7247,11 +7365,6 @@ Repeat connection request?</source>
Bağlantı isteği tekrarlansın mı?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You have no chats" xml:space="preserve">
<source>You have no chats</source>
<target>Hiç sohbetiniz yok</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You have to enter passphrase every time the app starts - it is not stored on the device." xml:space="preserve">
<source>You have to enter passphrase every time the app starts - it is not stored on the device.</source>
<target>Uygulama her başladığında parola girmeniz gerekir - parola cihazınızda saklanmaz.</target>
@@ -7272,11 +7385,23 @@ Bağlantı isteği tekrarlansın mı?</target>
<target>Bu gruba katıldınız. Davet eden grup üyesine bağlanılıyor.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You may migrate the exported database." xml:space="preserve">
<source>You may migrate the exported database.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You may save the exported archive." xml:space="preserve">
<source>You may save the exported archive.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="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." xml:space="preserve">
<source>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.</source>
<target>Sohbet veritabanınızın en son sürümünü SADECE bir cihazda kullanmalısınız, aksi takdirde bazı kişilerden daha fazla mesaj alamayabilirsiniz.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You need to allow your contact to call to be able to call them." xml:space="preserve">
<source>You need to allow your contact to call to be able to call them.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You need to allow your contact to send voice messages to be able to send them." xml:space="preserve">
<source>You need to allow your contact to send voice messages to be able to send them.</source>
<target>Sesli mesaj gönderebilmeniz için kişinizin de sesli mesaj göndermesine izin vermeniz gerekir.</target>
@@ -7392,13 +7517,6 @@ Bağlantı isteği tekrarlansın mı?</target>
<target>Sohbet profillerin</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact needs to be online for the connection to complete.&#10;You can cancel this connection and remove the contact (and try later with a new link)." xml:space="preserve">
<source>Your contact needs to be online for the connection to complete.
You can cancel this connection and remove the contact (and try later with a new link).</source>
<target>Bağlantının tamamlanması için kişinizin çevrimiçi olması gerekir.
Bu bağlantıyı iptal edebilir ve kişiyi kaldırabilirsiniz (ve daha sonra yeni bir bağlantıyla deneyebilirsiniz).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
<target>Kişiniz şu anda desteklenen maksimum boyuttan (%@) daha büyük bir dosya gönderdi.</target>
@@ -7590,6 +7708,10 @@ SimpleX sunucuları profilinizi göremez.</target>
<target>kalın</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="call" xml:space="preserve">
<source>call</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="call error" xml:space="preserve">
<source>call error</source>
<target>arama hatası</target>
@@ -7956,6 +8078,10 @@ SimpleX sunucuları profilinizi göremez.</target>
<target>%@ grubuna davet</target>
<note>group name</note>
</trans-unit>
<trans-unit id="invite" xml:space="preserve">
<source>invite</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="invited" xml:space="preserve">
<source>invited</source>
<target>davet edildi</target>
@@ -8011,6 +8137,10 @@ SimpleX sunucuları profilinizi göremez.</target>
<target>bağlanıldı</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message received" xml:space="preserve">
<source>message received</source>
<target>mesaj alındı</target>
@@ -8041,6 +8171,10 @@ SimpleX sunucuları profilinizi göremez.</target>
<target>aylar</target>
<note>time unit</note>
</trans-unit>
<trans-unit id="mute" xml:space="preserve">
<source>mute</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="never" xml:space="preserve">
<source>never</source>
<target>asla</target>
@@ -8171,6 +8305,10 @@ SimpleX sunucuları profilinizi göremez.</target>
<target>%@ tarafından kaydedildi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="search" xml:space="preserve">
<source>search</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve">
<source>sec</source>
<target>sn</target>
@@ -8245,8 +8383,8 @@ son alınan msj: %2$@</target>
<target>bilinmeyen</target>
<note>connection info</note>
</trans-unit>
<trans-unit id="unknown relays" xml:space="preserve">
<source>unknown relays</source>
<trans-unit id="unknown servers" xml:space="preserve">
<source>unknown servers</source>
<target>bilinmeyen yönlendiriciler</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -8255,6 +8393,10 @@ son alınan msj: %2$@</target>
<target>bilinmeyen durum</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unmute" xml:space="preserve">
<source>unmute</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unprotected" xml:space="preserve">
<source>unprotected</source>
<target>korumasız</target>
@@ -8300,6 +8442,10 @@ son alınan msj: %2$@</target>
<target>yönlendirici aracılığıyla</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="video" xml:space="preserve">
<source>video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="video call (not e2e encrypted)" xml:space="preserve">
<source>video call (not e2e encrypted)</source>
<target>Görüntülü arama (şifrelenmiş değil)</target>
@@ -8547,10 +8693,6 @@ son alınan msj: %2$@</target>
<source>Database upgrade required</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Dismiss Sheet" xml:space="preserve">
<source>Dismiss Sheet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error preparing file" xml:space="preserve">
<source>Error preparing file</source>
<note>No comment provided by engineer.</note>
@@ -8575,10 +8717,6 @@ son alınan msj: %2$@</target>
<source>Invalid migration confirmation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep Trying" xml:space="preserve">
<source>Keep Trying</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keychain error" xml:space="preserve">
<source>Keychain error</source>
<note>No comment provided by engineer.</note>
@@ -8591,10 +8729,6 @@ son alınan msj: %2$@</target>
<source>No active profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<note>No comment provided by engineer.</note>
@@ -8619,14 +8753,22 @@ son alınan msj: %2$@</target>
<source>Selected chat preferences prohibit this message.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sending File" xml:space="preserve">
<source>Sending File</source>
<trans-unit id="Sending a message takes longer than expected." xml:space="preserve">
<source>Sending a message takes longer than expected.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sending message…" xml:space="preserve">
<source>Sending message…</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share" xml:space="preserve">
<source>Share</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Slow network?" xml:space="preserve">
<source>Slow network?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unknown database error: %@" xml:space="preserve">
<source>Unknown database error: %@</source>
<note>No comment provided by engineer.</note>
@@ -8635,6 +8777,10 @@ son alınan msj: %2$@</target>
<source>Unsupported format</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wait" xml:space="preserve">
<source>Wait</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wrong database passphrase" xml:space="preserve">
<source>Wrong database passphrase</source>
<note>No comment provided by engineer.</note>
File diff suppressed because it is too large Load Diff
@@ -548,7 +548,8 @@
<source>Accept</source>
<target>接受</target>
<note>accept contact request via notification
accept incoming call via notification</note>
accept incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Accept connection request?" xml:space="preserve">
<source>Accept connection request?</source>
@@ -563,7 +564,8 @@
<trans-unit id="Accept incognito" xml:space="preserve">
<source>Accept incognito</source>
<target>接受隐身聊天</target>
<note>accept contact request via notification</note>
<note>accept contact request via notification
swipe action</note>
</trans-unit>
<trans-unit id="Acknowledged" xml:space="preserve">
<source>Acknowledged</source>
@@ -725,6 +727,10 @@
<target>仅当您的联系人允许时才允许呼叫。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow calls?" xml:space="preserve">
<source>Allow calls?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow disappearing messages only if your contact allows it to you." xml:space="preserve">
<source>Allow disappearing messages only if your contact allows it to you.</source>
<target>仅当您的联系人允许时才允许限时消息。</target>
@@ -911,6 +917,14 @@
<target>存档和上传</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archive contacts to chat later." xml:space="preserve">
<source>Archive contacts to chat later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archived contacts" xml:space="preserve">
<source>Archived contacts</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archiving database" xml:space="preserve">
<source>Archiving database</source>
<target>正在存档数据库</target>
@@ -1010,6 +1024,10 @@
<target>更好的消息</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Better networking" xml:space="preserve">
<source>Better networking</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Black" xml:space="preserve">
<source>Black</source>
<note>No comment provided by engineer.</note>
@@ -1049,6 +1067,10 @@
<target>由管理员封禁</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Blur for better privacy." xml:space="preserve">
<source>Blur for better privacy.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Blur media" xml:space="preserve">
<source>Blur media</source>
<note>No comment provided by engineer.</note>
@@ -1098,11 +1120,23 @@
<target>通话</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Calls prohibited!" xml:space="preserve">
<source>Calls prohibited!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Camera not available" xml:space="preserve">
<source>Camera not available</source>
<target>相机不可用</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't call contact" xml:space="preserve">
<source>Can't call contact</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't call member" xml:space="preserve">
<source>Can't call member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't invite contact!" xml:space="preserve">
<source>Can't invite contact!</source>
<target>无法邀请联系人!</target>
@@ -1113,6 +1147,10 @@
<target>无法邀请联系人!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't message member" xml:space="preserve">
<source>Can't message member</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cancel" xml:space="preserve">
<source>Cancel</source>
<target>取消</target>
@@ -1221,6 +1259,10 @@
<target>聊天数据库已删除</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database exported" xml:space="preserve">
<source>Chat database exported</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database imported" xml:space="preserve">
<source>Chat database imported</source>
<target>聊天数据库已导入</target>
@@ -1241,6 +1283,10 @@
<target>聊天已停止。如果你已经在另一台设备商使用过此数据库,你应该在启动聊天前将数据库传输回来。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat list" xml:space="preserve">
<source>Chat list</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat migrated!" xml:space="preserve">
<source>Chat migrated!</source>
<target>已迁移聊天!</target>
@@ -1299,7 +1345,7 @@
<trans-unit id="Clear" xml:space="preserve">
<source>Clear</source>
<target>清除</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Clear conversation" xml:space="preserve">
<source>Clear conversation</source>
@@ -1321,6 +1367,10 @@
<target>清除验证</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Color chats with the new themes." xml:space="preserve">
<source>Color chats with the new themes.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Color mode" xml:space="preserve">
<source>Color mode</source>
<note>No comment provided by engineer.</note>
@@ -1358,6 +1408,10 @@
<target>确认密码</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm contact deletion?" xml:space="preserve">
<source>Confirm contact deletion?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm database upgrades" xml:space="preserve">
<source>Confirm database upgrades</source>
<target>确认数据库升级</target>
@@ -1412,6 +1466,10 @@
<target>连接到桌面</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect to your friends faster." xml:space="preserve">
<source>Connect to your friends faster.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connect to yourself?" xml:space="preserve">
<source>Connect to yourself?</source>
<target>连接到你自己?</target>
@@ -1477,6 +1535,10 @@ This is your own one-time link!</source>
<target>连接服务器中……(错误:%@)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting to contact, please wait or check later!" xml:space="preserve">
<source>Connecting to contact, please wait or check later!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting to desktop" xml:space="preserve">
<source>Connecting to desktop</source>
<target>正连接到桌面</target>
@@ -1487,6 +1549,10 @@ This is your own one-time link!</source>
<target>连接</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connection and servers status." xml:space="preserve">
<source>Connection and servers status.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connection error" xml:space="preserve">
<source>Connection error</source>
<target>连接错误</target>
@@ -1534,6 +1600,10 @@ This is your own one-time link!</source>
<target>联系人已存在</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact deleted!" xml:space="preserve">
<source>Contact deleted!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact hidden:" xml:space="preserve">
<source>Contact hidden:</source>
<target>联系人已隐藏:</target>
@@ -1544,9 +1614,8 @@ This is your own one-time link!</source>
<target>联系已连接</target>
<note>notification</note>
</trans-unit>
<trans-unit id="Contact is not connected yet!" xml:space="preserve">
<source>Contact is not connected yet!</source>
<target>联系人尚未连接!</target>
<trans-unit id="Contact is deleted." xml:space="preserve">
<source>Contact is deleted.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact name" xml:space="preserve">
@@ -1559,6 +1628,10 @@ This is your own one-time link!</source>
<target>联系人偏好设置</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact will be deleted - this cannot be undone!" xml:space="preserve">
<source>Contact will be deleted - this cannot be undone!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contacts" xml:space="preserve">
<source>Contacts</source>
<target>联系人</target>
@@ -1574,6 +1647,10 @@ This is your own one-time link!</source>
<target>继续</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Conversation deleted!" xml:space="preserve">
<source>Conversation deleted!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Copy" xml:space="preserve">
<source>Copy</source>
<target>复制</target>
@@ -1837,7 +1914,8 @@ This is your own one-time link!</source>
<trans-unit id="Delete" xml:space="preserve">
<source>Delete</source>
<target>删除</target>
<note>chat item action</note>
<note>chat item action
swipe action</note>
</trans-unit>
<trans-unit id="Delete %lld messages of members?" xml:space="preserve">
<source>Delete %lld messages of members?</source>
@@ -1847,11 +1925,6 @@ This is your own one-time link!</source>
<source>Delete %lld messages?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete Contact" xml:space="preserve">
<source>Delete Contact</source>
<target>删除联系人</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete address" xml:space="preserve">
<source>Delete address</source>
<target>删除地址</target>
@@ -1907,9 +1980,8 @@ This is your own one-time link!</source>
<target>删除联系人</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete contact?&#10;This cannot be undone!" xml:space="preserve">
<source>Delete contact?
This cannot be undone!</source>
<trans-unit id="Delete contact?" xml:space="preserve">
<source>Delete contact?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete database" xml:space="preserve">
@@ -2002,11 +2074,6 @@ This cannot be undone!</source>
<target>删除旧数据库吗?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete pending connection" xml:space="preserve">
<source>Delete pending connection</source>
<target>删除挂起连接</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete pending connection?" xml:space="preserve">
<source>Delete pending connection?</source>
<target>删除待定连接?</target>
@@ -2022,11 +2089,19 @@ This cannot be undone!</source>
<target>删除队列</target>
<note>server test step</note>
</trans-unit>
<trans-unit id="Delete up to 20 messages at once." xml:space="preserve">
<source>Delete up to 20 messages at once.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete user profile?" xml:space="preserve">
<source>Delete user profile?</source>
<target>删除用户资料?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete without notification" xml:space="preserve">
<source>Delete without notification</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Deleted" xml:space="preserve">
<source>Deleted</source>
<note>No comment provided by engineer.</note>
@@ -2104,6 +2179,10 @@ This cannot be undone!</source>
<target>开发</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Developer options" xml:space="preserve">
<source>Developer options</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Developer tools" xml:space="preserve">
<source>Developer tools</source>
<target>开发者工具</target>
@@ -2603,11 +2682,6 @@ This cannot be undone!</source>
<target>删除连接错误</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting contact" xml:space="preserve">
<source>Error deleting contact</source>
<target>删除联系人错误</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error deleting database" xml:space="preserve">
<source>Error deleting database</source>
<target>删除数据库错误</target>
@@ -2894,7 +2968,7 @@ This cannot be undone!</source>
<trans-unit id="Favorite" xml:space="preserve">
<source>Favorite</source>
<target>最喜欢</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="File error" xml:space="preserve">
<source>File error</source>
@@ -3628,6 +3702,10 @@ Error: %2$@</source>
3.连接被破坏。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It protects your IP address and connections." xml:space="preserve">
<source>It protects your IP address and connections.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It seems like you are already connected via this link. If it is not the case, there was an error (%@)." xml:space="preserve">
<source>It seems like you are already connected via this link. If it is not the case, there was an error (%@).</source>
<target>您似乎已经通过此链接连接。如果不是这样,则有一个错误 (%@)。</target>
@@ -3646,7 +3724,7 @@ Error: %2$@</source>
<trans-unit id="Join" xml:space="preserve">
<source>Join</source>
<target>加入</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Join group" xml:space="preserve">
<source>Join group</source>
@@ -3687,6 +3765,10 @@ This is your link for group %@!</source>
<target>保留</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep conversation" xml:space="preserve">
<source>Keep conversation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
<source>Keep the app open to use it from desktop</source>
<note>No comment provided by engineer.</note>
@@ -3729,7 +3811,7 @@ This is your link for group %@!</source>
<trans-unit id="Leave" xml:space="preserve">
<source>Leave</source>
<target>离开</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Leave group" xml:space="preserve">
<source>Leave group</source>
@@ -3861,6 +3943,10 @@ This is your link for group %@!</source>
<target>最长30秒,立即接收。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Media &amp; file servers" xml:space="preserve">
<source>Media &amp; file servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Medium" xml:space="preserve">
<source>Medium</source>
<note>blur media</note>
@@ -3943,12 +4029,8 @@ This is your link for group %@!</source>
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing mode" xml:space="preserve">
<source>Message routing mode</source>
<trans-unit id="Message servers" xml:space="preserve">
<source>Message servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message source remains private." xml:space="preserve">
@@ -4102,7 +4184,7 @@ This is your link for group %@!</source>
<trans-unit id="Mute" xml:space="preserve">
<source>Mute</source>
<target>静音</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Muted when inactive!" xml:space="preserve">
<source>Muted when inactive!</source>
@@ -4112,7 +4194,7 @@ This is your link for group %@!</source>
<trans-unit id="Name" xml:space="preserve">
<source>Name</source>
<target>名称</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Network &amp; servers" xml:space="preserve">
<source>Network &amp; servers</source>
@@ -4153,6 +4235,10 @@ This is your link for group %@!</source>
<target>新聊天</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New chat experience 🎉" xml:space="preserve">
<source>New chat experience 🎉</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New contact request" xml:space="preserve">
<source>New contact request</source>
<target>新联系人请求</target>
@@ -4183,6 +4269,10 @@ This is your link for group %@!</source>
<target>%@ 的新内容</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New media options" xml:space="preserve">
<source>New media options</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member role" xml:space="preserve">
<source>New member role</source>
<target>新成员角色</target>
@@ -4324,13 +4414,15 @@ This is your link for group %@!</source>
<target>一次性邀请链接</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be required for connection. Requires enabling VPN." xml:space="preserve">
<source>Onion hosts will be required for connection. Requires enabling VPN.</source>
<trans-unit id="Onion hosts will be **required** for connection.&#10;Requires compatible VPN." xml:space="preserve">
<source>Onion hosts will be **required** for connection.
Requires compatible VPN.</source>
<target>Onion 主机将用于连接。需要启用 VPN。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be used when available. Requires enabling VPN." xml:space="preserve">
<source>Onion hosts will be used when available. Requires enabling VPN.</source>
<trans-unit id="Onion hosts will be used when available.&#10;Requires compatible VPN." xml:space="preserve">
<source>Onion hosts will be used when available.
Requires compatible VPN.</source>
<target>当可用时,将使用 Onion 主机。需要启用 VPN。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -4344,6 +4436,10 @@ This is your link for group %@!</source>
<target>只有客户端设备存储用户资料、联系人、群组和**双层端到端加密**发送的消息。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only delete conversation" xml:space="preserve">
<source>Only delete conversation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
<source>Only group owners can change group preferences.</source>
<target>只有群主可以改变群组偏好设置。</target>
@@ -4573,6 +4669,14 @@ This is your link for group %@!</source>
<target>画中画通话</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Play from the chat list." xml:space="preserve">
<source>Play from the chat list.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please ask your contact to enable calls." xml:space="preserve">
<source>Please ask your contact to enable calls.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please ask your contact to enable sending voice messages." xml:space="preserve">
<source>Please ask your contact to enable sending voice messages.</source>
<target>请让您的联系人启用发送语音消息。</target>
@@ -4856,6 +4960,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>评价此应用程序</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reachable chat toolbar" xml:space="preserve">
<source>Reachable chat toolbar</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="React…" xml:space="preserve">
<source>React…</source>
<target>回应…</target>
@@ -4864,7 +4972,7 @@ Enable in *Network &amp; servers* settings.</source>
<trans-unit id="Read" xml:space="preserve">
<source>Read</source>
<target>已读</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Read more" xml:space="preserve">
<source>Read more</source>
@@ -5014,7 +5122,8 @@ Enable in *Network &amp; servers* settings.</source>
<trans-unit id="Reject" xml:space="preserve">
<source>Reject</source>
<target>拒绝</target>
<note>reject incoming call via notification</note>
<note>reject incoming call via notification
swipe action</note>
</trans-unit>
<trans-unit id="Reject (sender NOT notified)" xml:space="preserve">
<source>Reject (sender NOT notified)</source>
@@ -5115,6 +5224,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>重置</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all hints" xml:space="preserve">
<source>Reset all hints</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all statistics" xml:space="preserve">
<source>Reset all statistics</source>
<note>No comment provided by engineer.</note>
@@ -5181,11 +5294,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>揭示</target>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Revert" xml:space="preserve">
<source>Revert</source>
<target>恢复</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
<source>Revoke</source>
<target>撤销</target>
@@ -5215,11 +5323,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>SMP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SMP servers" xml:space="preserve">
<source>SMP servers</source>
<target>SMP 服务器</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Safely receive files" xml:space="preserve">
<source>Safely receive files</source>
<note>No comment provided by engineer.</note>
@@ -5249,6 +5352,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>保存并通知群组成员</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save and reconnect" xml:space="preserve">
<source>Save and reconnect</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save and update group profile" xml:space="preserve">
<source>Save and update group profile</source>
<target>保存和更新组配置文件</target>
@@ -5448,11 +5555,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>将送达回执发送给</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send direct message" xml:space="preserve">
<source>Send direct message</source>
<target>发送私信</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send direct message to connect" xml:space="preserve">
<source>Send direct message to connect</source>
<target>发送私信来连接</target>
@@ -5477,6 +5579,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>发送实时消息</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send message to enable calls." xml:space="preserve">
<source>Send message to enable calls.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when IP address is protected and your or destination server does not support private routing." xml:space="preserve">
<source>Send messages directly when IP address is protected and your or destination server does not support private routing.</source>
<note>No comment provided by engineer.</note>
@@ -5746,6 +5852,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>与联系人分享地址?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share from other apps." xml:space="preserve">
<source>Share from other apps.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share link" xml:space="preserve">
<source>Share link</source>
<target>分享链接</target>
@@ -5909,11 +6019,19 @@ Enable in *Network &amp; servers* settings.</source>
<source>Soft</source>
<note>blur media</note>
</trans-unit>
<trans-unit id="Some file(s) were not exported:" xml:space="preserve">
<source>Some file(s) were not exported:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some non-fatal errors occurred during import - you may see Chat console for more details." xml:space="preserve">
<source>Some non-fatal errors occurred during import - you may see Chat console for more details.</source>
<target>导入过程中发生了一些非致命错误——您可以查看聊天控制台了解更多详细信息。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some non-fatal errors occurred during import:" xml:space="preserve">
<source>Some non-fatal errors occurred during import:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Somebody" xml:space="preserve">
<source>Somebody</source>
<target>某人</target>
@@ -6043,6 +6161,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>系统验证</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="TCP connection" xml:space="preserve">
<source>TCP connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="TCP connection timeout" xml:space="preserve">
<source>TCP connection timeout</source>
<target>TCP 连接超时</target>
@@ -6103,11 +6225,6 @@ Enable in *Network &amp; servers* settings.</source>
<target>轻按扫描</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Tap to start a new chat" xml:space="preserve">
<source>Tap to start a new chat</source>
<target>点击开始一个新聊天</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Temporary file error" xml:space="preserve">
<source>Temporary file error</source>
<note>No comment provided by engineer.</note>
@@ -6399,11 +6516,19 @@ You will be prompted to complete authentication before this feature is enabled.<
<target>要与您的联系人验证端到端加密,请比较(或扫描)您设备上的代码。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toggle chat list:" xml:space="preserve">
<source>Toggle chat list:</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toggle incognito when connecting." xml:space="preserve">
<source>Toggle incognito when connecting.</source>
<target>在连接时切换隐身模式。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Toolbar opacity" xml:space="preserve">
<source>Toolbar opacity</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Total" xml:space="preserve">
<source>Total</source>
<note>No comment provided by engineer.</note>
@@ -6479,7 +6604,7 @@ You will be prompted to complete authentication before this feature is enabled.<
<trans-unit id="Unfav." xml:space="preserve">
<source>Unfav.</source>
<target>取消最喜欢</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Unhide" xml:space="preserve">
<source>Unhide</source>
@@ -6555,12 +6680,12 @@ To connect, please ask your contact to create another connection link and check
<trans-unit id="Unmute" xml:space="preserve">
<source>Unmute</source>
<target>取消静音</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Unread" xml:space="preserve">
<source>Unread</source>
<target>未读</target>
<note>No comment provided by engineer.</note>
<note>swipe action</note>
</trans-unit>
<trans-unit id="Up to 100 last messages are sent to new members." xml:space="preserve">
<source>Up to 100 last messages are sent to new members.</source>
@@ -6572,11 +6697,6 @@ To connect, please ask your contact to create another connection link and check
<target>更新</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update .onion hosts setting?" xml:space="preserve">
<source>Update .onion hosts setting?</source>
<target>更新 .onion 主机设置?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update database passphrase" xml:space="preserve">
<source>Update database passphrase</source>
<target>更新数据库密码</target>
@@ -6587,9 +6707,8 @@ To connect, please ask your contact to create another connection link and check
<target>更新网络设置?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update transport isolation mode?" xml:space="preserve">
<source>Update transport isolation mode?</source>
<target>更新传输隔离模式?</target>
<trans-unit id="Update settings?" xml:space="preserve">
<source>Update settings?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Updating settings will re-connect the client to all servers." xml:space="preserve">
@@ -6597,11 +6716,6 @@ To connect, please ask your contact to create another connection link and check
<target>更新设置会将客户端重新连接到所有服务器。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Updating this setting will re-connect the client to all servers." xml:space="preserve">
<source>Updating this setting will re-connect the client to all servers.</source>
<target>更新此设置将重新连接客户端到所有服务器。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Upgrade and open chat" xml:space="preserve">
<source>Upgrade and open chat</source>
<target>升级并打开聊天</target>
@@ -6696,6 +6810,10 @@ To connect, please ask your contact to create another connection link and check
<target>通话时使用本应用</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use the app with one hand." xml:space="preserve">
<source>Use the app with one hand.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="User profile" xml:space="preserve">
<source>User profile</source>
<target>用户资料</target>
@@ -6705,11 +6823,6 @@ To connect, please ask your contact to create another connection link and check
<source>User selection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using .onion hosts requires compatible VPN provider." xml:space="preserve">
<source>Using .onion hosts requires compatible VPN provider.</source>
<target>使用 .onion 主机需要兼容的 VPN 提供商。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using SimpleX Chat servers." xml:space="preserve">
<source>Using SimpleX Chat servers.</source>
<target>使用 SimpleX Chat 服务器。</target>
@@ -6962,11 +7075,6 @@ To connect, please ask your contact to create another connection link and check
<source>XFTP server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="XFTP servers" xml:space="preserve">
<source>XFTP servers</source>
<target>XFTP 服务器</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>您</target>
@@ -7046,6 +7154,10 @@ Repeat join request?</source>
<target>您可以从锁屏上接听电话,无需设备和应用程序的认证。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can change it in Appearance settings." xml:space="preserve">
<source>You can change it in Appearance settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can create it later" xml:space="preserve">
<source>You can create it later</source>
<target>您可以以后创建它</target>
@@ -7081,6 +7193,10 @@ Repeat join request?</source>
<target>您现在可以给 %@ 发送消息</target>
<note>notification body</note>
</trans-unit>
<trans-unit id="You can send messages to %@ from Archived contacts." xml:space="preserve">
<source>You can send messages to %@ from Archived contacts.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can set lock screen notification preview via settings." xml:space="preserve">
<source>You can set lock screen notification preview via settings.</source>
<target>您可以通过设置来设置锁屏通知预览。</target>
@@ -7106,6 +7222,10 @@ Repeat join request?</source>
<target>您可以通过应用程序设置/数据库或重新启动应用程序开始聊天</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can still view conversation with %@ in the list of chats." xml:space="preserve">
<source>You can still view conversation with %@ in the list of chats.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can turn on SimpleX Lock via Settings." xml:space="preserve">
<source>You can turn on SimpleX Lock via Settings.</source>
<target>您可以通过设置开启 SimpleX 锁定。</target>
@@ -7146,11 +7266,6 @@ Repeat join request?</source>
Repeat connection request?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You have no chats" xml:space="preserve">
<source>You have no chats</source>
<target>您没有聊天记录</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You have to enter passphrase every time the app starts - it is not stored on the device." xml:space="preserve">
<source>You have to enter passphrase every time the app starts - it is not stored on the device.</source>
<target>您必须在每次应用程序启动时输入密码——它不存储在设备上。</target>
@@ -7171,11 +7286,23 @@ Repeat connection request?</source>
<target>你加入了这个群组。连接到邀请组成员。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You may migrate the exported database." xml:space="preserve">
<source>You may migrate the exported database.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You may save the exported archive." xml:space="preserve">
<source>You may save the exported archive.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="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." xml:space="preserve">
<source>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.</source>
<target>您只能在一台设备上使用最新版本的聊天数据库,否则您可能会停止接收来自某些联系人的消息。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You need to allow your contact to call to be able to call them." xml:space="preserve">
<source>You need to allow your contact to call to be able to call them.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You need to allow your contact to send voice messages to be able to send them." xml:space="preserve">
<source>You need to allow your contact to send voice messages to be able to send them.</source>
<target>您需要允许您的联系人发送语音消息,以便您能够发送语音消息。</target>
@@ -7290,13 +7417,6 @@ Repeat connection request?</source>
<target>您的聊天资料</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact needs to be online for the connection to complete.&#10;You can cancel this connection and remove the contact (and try later with a new link)." xml:space="preserve">
<source>Your contact needs to be online for the connection to complete.
You can cancel this connection and remove the contact (and try later with a new link).</source>
<target>您的联系人需要在线才能完成连接。
您可以取消此连接并删除联系人(然后尝试使用新链接)。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
<target>您的联系人发送的文件大于当前支持的最大大小 (%@)。</target>
@@ -7486,6 +7606,10 @@ SimpleX 服务器无法看到您的资料。</target>
<target>加粗</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="call" xml:space="preserve">
<source>call</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="call error" xml:space="preserve">
<source>call error</source>
<target>通话错误</target>
@@ -7851,6 +7975,10 @@ SimpleX 服务器无法看到您的资料。</target>
<target>邀请您加入群组 %@</target>
<note>group name</note>
</trans-unit>
<trans-unit id="invite" xml:space="preserve">
<source>invite</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="invited" xml:space="preserve">
<source>invited</source>
<target>已邀请</target>
@@ -7905,6 +8033,10 @@ SimpleX 服务器无法看到您的资料。</target>
<target>已连接</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
<source>message</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message received" xml:space="preserve">
<source>message received</source>
<target>消息已收到</target>
@@ -7935,6 +8067,10 @@ SimpleX 服务器无法看到您的资料。</target>
<target>月</target>
<note>time unit</note>
</trans-unit>
<trans-unit id="mute" xml:space="preserve">
<source>mute</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="never" xml:space="preserve">
<source>never</source>
<target>从不</target>
@@ -8064,6 +8200,10 @@ SimpleX 服务器无法看到您的资料。</target>
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="search" xml:space="preserve">
<source>search</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve">
<source>sec</source>
<target>秒</target>
@@ -8134,8 +8274,8 @@ last received msg: %2$@</source>
<target>未知</target>
<note>connection info</note>
</trans-unit>
<trans-unit id="unknown relays" xml:space="preserve">
<source>unknown relays</source>
<trans-unit id="unknown servers" xml:space="preserve">
<source>unknown servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unknown status" xml:space="preserve">
@@ -8143,6 +8283,10 @@ last received msg: %2$@</source>
<target>未知状态</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unmute" xml:space="preserve">
<source>unmute</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unprotected" xml:space="preserve">
<source>unprotected</source>
<note>No comment provided by engineer.</note>
@@ -8186,6 +8330,10 @@ last received msg: %2$@</source>
<target>通过中继</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="video" xml:space="preserve">
<source>video</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="video call (not e2e encrypted)" xml:space="preserve">
<source>video call (not e2e encrypted)</source>
<target>视频通话(非端到端加密)</target>
@@ -8429,10 +8577,6 @@ last received msg: %2$@</source>
<source>Database upgrade required</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Dismiss Sheet" xml:space="preserve">
<source>Dismiss Sheet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error preparing file" xml:space="preserve">
<source>Error preparing file</source>
<note>No comment provided by engineer.</note>
@@ -8457,10 +8601,6 @@ last received msg: %2$@</source>
<source>Invalid migration confirmation</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keep Trying" xml:space="preserve">
<source>Keep Trying</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Keychain error" xml:space="preserve">
<source>Keychain error</source>
<note>No comment provided by engineer.</note>
@@ -8473,10 +8613,6 @@ last received msg: %2$@</source>
<source>No active profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Ok" xml:space="preserve">
<source>Ok</source>
<note>No comment provided by engineer.</note>
@@ -8501,14 +8637,22 @@ last received msg: %2$@</source>
<source>Selected chat preferences prohibit this message.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sending File" xml:space="preserve">
<source>Sending File</source>
<trans-unit id="Sending a message takes longer than expected." xml:space="preserve">
<source>Sending a message takes longer than expected.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sending message…" xml:space="preserve">
<source>Sending message…</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share" xml:space="preserve">
<source>Share</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Slow network?" xml:space="preserve">
<source>Slow network?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unknown database error: %@" xml:space="preserve">
<source>Unknown database error: %@</source>
<note>No comment provided by engineer.</note>
@@ -8517,6 +8661,10 @@ last received msg: %2$@</source>
<source>Unsupported format</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wait" xml:space="preserve">
<source>Wait</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wrong database passphrase" xml:space="preserve">
<source>Wrong database passphrase</source>
<note>No comment provided by engineer.</note>
@@ -314,7 +314,7 @@
</trans-unit>
<trans-unit id="About SimpleX Chat" xml:space="preserve" approved="no">
<source>About SimpleX Chat</source>
<target state="translated">關於 SimpleX 對話</target>
<target state="translated">關於 SimpleX Chat</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Accent color" xml:space="preserve" approved="no">
@@ -445,7 +445,7 @@
</trans-unit>
<trans-unit id="Allow your contacts to send disappearing messages." xml:space="preserve" approved="no">
<source>Allow your contacts to send disappearing messages.</source>
<target state="translated">允許你的聯絡人傳送自動銷毀的訊息。</target>
<target state="translated">允許您的聯絡人傳送限時訊息。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow your contacts to send voice messages." xml:space="preserve" approved="no">
@@ -5898,6 +5898,230 @@ It can happen because of some bug or when the connection is compromised.</source
<target state="translated">%@ 和 %@ 已連接</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ downloaded" xml:space="preserve" approved="no">
<source>%@ downloaded</source>
<target state="translated">%@ 下載</target>
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve" approved="no">
<source>%@ uploaded</source>
<target state="translated">%@ 上傳</target>
</trans-unit>
<trans-unit id="Abort" xml:space="preserve" approved="no">
<source>Abort</source>
<target state="translated">中止</target>
</trans-unit>
<trans-unit id="**Create group**: to create a new group." xml:space="preserve" approved="no">
<source>**Create group**: to create a new group.</source>
<target state="translated">**創建群組**: 創建一個新的群組。</target>
</trans-unit>
<trans-unit id="Abort changing address" xml:space="preserve" approved="no">
<source>Abort changing address</source>
<target state="translated">中止更改地址</target>
</trans-unit>
<trans-unit id="Accept connection request?" xml:space="preserve" approved="no">
<source>Accept connection request?</source>
<target state="translated">接受連線請求?</target>
</trans-unit>
<trans-unit id="Camera not available" xml:space="preserve" approved="no">
<source>Camera not available</source>
<target state="translated">相機不可用</target>
</trans-unit>
<trans-unit id="All messages will be deleted - this cannot be undone!" xml:space="preserve" approved="no">
<source>All messages will be deleted - this cannot be undone!</source>
<target state="translated">所有訊息都將被刪除 - 這不能還原!</target>
</trans-unit>
<trans-unit id="Allow irreversible message deletion only if your contact allows it to you. (24 hours)" xml:space="preserve" approved="no">
<source>Allow irreversible message deletion only if your contact allows it to you. (24 hours)</source>
<target state="translated">只有你的聯絡人允許的情況下,才允許不可逆地將訊息刪除。(24小時)</target>
</trans-unit>
<trans-unit id="Allow to irreversibly delete sent messages. (24 hours)" xml:space="preserve" approved="no">
<source>Allow to irreversibly delete sent messages. (24 hours)</source>
<target state="translated">允許將不可撤銷的訊息刪除。(24小時)</target>
</trans-unit>
<trans-unit id="Allow your contacts to irreversibly delete sent messages. (24 hours)" xml:space="preserve" approved="no">
<source>Allow your contacts to irreversibly delete sent messages. (24 hours)</source>
<target state="translated">允許您的聯絡人不可復原地刪除已傳送的訊息。(24小時)</target>
</trans-unit>
<trans-unit id="Bad desktop address" xml:space="preserve" approved="no">
<source>Bad desktop address</source>
<target state="translated">無效的桌面地址</target>
</trans-unit>
<trans-unit id="Error decrypting file" xml:space="preserve" approved="no">
<source>Error decrypting file</source>
<target state="translated">解密檔案時出錯</target>
</trans-unit>
<trans-unit id="Add contact" xml:space="preserve" approved="no">
<source>Add contact</source>
<target state="translated">新增聯絡人</target>
</trans-unit>
<trans-unit id="Advanced settings" xml:space="preserve" approved="no">
<source>Advanced settings</source>
<target state="translated">進階設定</target>
</trans-unit>
<trans-unit id="Allow calls?" xml:space="preserve" approved="no">
<source>Allow calls?</source>
<target state="translated">允許通話?</target>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve" approved="no">
<source>Allow to send files and media.</source>
<target state="translated">允許傳送檔案和媒體。</target>
</trans-unit>
<trans-unit id="Already joining the group!" xml:space="preserve" approved="no">
<source>Already joining the group!</source>
<target state="translated">已加入群組!</target>
</trans-unit>
<trans-unit id="App data migration" xml:space="preserve" approved="no">
<source>App data migration</source>
<target state="translated">應用資料轉移</target>
</trans-unit>
<trans-unit id="Apply" xml:space="preserve" approved="no">
<source>Apply</source>
<target state="translated">應用</target>
</trans-unit>
<trans-unit id="Apply to" xml:space="preserve" approved="no">
<source>Apply to</source>
<target state="translated">應用到</target>
</trans-unit>
<trans-unit id="Archive and upload" xml:space="preserve" approved="no">
<source>Archive and upload</source>
<target state="translated">儲存並上傳</target>
</trans-unit>
<trans-unit id="Block" xml:space="preserve" approved="no">
<source>Block</source>
<target state="translated">封鎖</target>
</trans-unit>
<trans-unit id="Block group members" xml:space="preserve" approved="no">
<source>Block group members</source>
<target state="translated">封鎖群組成員</target>
</trans-unit>
<trans-unit id="Block member" xml:space="preserve" approved="no">
<source>Block member</source>
<target state="translated">封鎖成員</target>
</trans-unit>
<trans-unit id="Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" xml:space="preserve" approved="no">
<source>Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!</source>
<target state="translated">保加利亞語、芬蘭語、泰語和烏克蘭語——感謝使用者們和[Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!</target>
</trans-unit>
<trans-unit id="Can't call member" xml:space="preserve" approved="no">
<source>Can't call member</source>
<target state="translated">無法與成員通話</target>
</trans-unit>
<trans-unit id="Can't message member" xml:space="preserve" approved="no">
<source>Can't message member</source>
<target state="translated">無法傳送訊息給成員</target>
</trans-unit>
<trans-unit id="Cancel migration" xml:space="preserve" approved="no">
<source>Cancel migration</source>
<target state="translated">取消遷移</target>
</trans-unit>
<trans-unit id="Chat database exported" xml:space="preserve" approved="no">
<source>Chat database exported</source>
<target state="translated">導出聊天數據庫</target>
</trans-unit>
<trans-unit id="0 sec" xml:space="preserve" approved="no">
<source>0 sec</source>
<target state="translated">0 秒</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">
<source>All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays.</source>
<target state="translated">你的所有聯絡人、對話和文件將被安全加密並切塊上傳到設置的 XFTP 中繼。</target>
</trans-unit>
<trans-unit id="Address change will be aborted. Old receiving address will be used." xml:space="preserve" approved="no">
<source>Address change will be aborted. Old receiving address will be used.</source>
<target state="translated">將取消地址更改。將使用舊聯絡地址。</target>
</trans-unit>
<trans-unit id="Archiving database" xml:space="preserve" approved="no">
<source>Archiving database</source>
<target state="translated">正在儲存資料庫</target>
</trans-unit>
<trans-unit id="Cellular" xml:space="preserve" approved="no">
<source>Cellular</source>
<target state="translated">行動網路</target>
</trans-unit>
<trans-unit id="%@, %@ and %lld members" xml:space="preserve" approved="no">
<source>%@, %@ and %lld members</source>
<target state="translated">%@, %@ 和 %lld 成員</target>
</trans-unit>
<trans-unit id="%lld messages marked deleted" xml:space="preserve" approved="no">
<source>%lld messages marked deleted</source>
<target state="translated">%lld 條訊息已刪除</target>
</trans-unit>
<trans-unit id="Already connecting!" xml:space="preserve" approved="no">
<source>Already connecting!</source>
<target state="translated">已連接!</target>
</trans-unit>
<trans-unit id="Block member?" xml:space="preserve" approved="no">
<source>Block member?</source>
<target state="translated">封鎖成員?</target>
</trans-unit>
<trans-unit id="(new)" xml:space="preserve" approved="no">
<source>(new)</source>
<target state="translated">(新)</target>
</trans-unit>
<trans-unit id="%@, %@ and %lld other members connected" xml:space="preserve" approved="no">
<source>%@, %@ and %lld other members connected</source>
<target state="translated">%@, %@ 和 %lld 個成員已連接</target>
</trans-unit>
<trans-unit id="A few more things" xml:space="preserve" approved="no">
<source>A few more things</source>
<target state="translated">其他</target>
</trans-unit>
<trans-unit id="Show last messages" xml:space="preserve" approved="no">
<source>Show last messages</source>
<target state="translated">顯示最新的訊息</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">應用程式將為新的本機文件(影片除外)加密。</target>
</trans-unit>
<trans-unit id="Better groups" xml:space="preserve" approved="no">
<source>Better groups</source>
<target state="translated">更加的群組</target>
</trans-unit>
<trans-unit id="%lld new interface languages" xml:space="preserve" approved="no">
<source>%lld new interface languages</source>
<target state="translated">%lld 種新的介面語言</target>
</trans-unit>
<trans-unit id="Blocked by admin" xml:space="preserve" approved="no">
<source>Blocked by admin</source>
<target state="translated">由管理員封鎖</target>
</trans-unit>
<trans-unit id="Both you and your contact can irreversibly delete sent messages. (24 hours)" xml:space="preserve" approved="no">
<source>Both you and your contact can irreversibly delete sent messages. (24 hours)</source>
<target state="translated">您與您的聯絡人都可以不可復原地删除已傳送的訊息。(24小時)</target>
</trans-unit>
<trans-unit id="Encrypt local files" xml:space="preserve" approved="no">
<source>Encrypt local files</source>
<target state="translated">加密本機檔案</target>
</trans-unit>
<trans-unit id="- more stable message delivery.&#10;- a bit better groups.&#10;- and more!" xml:space="preserve" approved="no">
<source>- more stable message delivery.
- a bit better groups.
- and more!</source>
<target state="translated">- 更穩定的傳送!
- 更好的社群!
- 以及更多!</target>
</trans-unit>
<trans-unit id="- optionally notify deleted contacts.&#10;- profile names with spaces.&#10;- and more!" xml:space="preserve" approved="no">
<source>- optionally notify deleted contacts.
- profile names with spaces.
- and more!</source>
<target state="translated">- 可選擇通知已刪除的聯絡人
- 帶空格的共人資料名稱。
-以及更多!</target>
</trans-unit>
<trans-unit id="Abort changing address?" xml:space="preserve" approved="no">
<source>Abort changing address?</source>
<target state="translated">中止更改地址?</target>
</trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve" approved="no">
<source>Allow to send SimpleX links.</source>
<target state="translated">允許傳送 SimpleX 連結。</target>
</trans-unit>
<trans-unit id="Background" xml:space="preserve" approved="no">
<source>Background</source>
<target state="translated">後台</target>
</trans-unit>
</body>
</file>
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="zh-Hant" datatype="plaintext">
@@ -339,7 +339,9 @@ class NotificationService: UNNotificationServiceExtension {
CXProvider.reportNewIncomingVoIPPushPayload([
"displayName": invitation.contact.displayName,
"contactId": invitation.contact.id,
"media": invitation.callType.media.rawValue
"callUUID": invitation.callUUID ?? "",
"media": invitation.callType.media.rawValue,
"callTs": invitation.callTs.timeIntervalSince1970
]) { error in
logger.debug("reportNewIncomingVoIPPushPayload result: \(error)")
deliver(error == nil ? nil : createCallInvitationNtf(invitation))
+7 -7
View File
@@ -29,6 +29,7 @@ class ShareModel: ObservableObject {
@Published var errorAlert: ErrorAlert?
@Published var hasSimplexLink = false
@Published var alertRequiresPassword = false
var networkTimeout = CFAbsoluteTimeGetCurrent()
enum BottomBar {
case sendButton
@@ -73,7 +74,7 @@ class ShareModel: ObservableObject {
}
func setup(context: NSExtensionContext) {
if performLAGroupDefault.get() && !allowShareExtensionGroupDefault.get() {
if appLocalAuthEnabledGroupDefault.get() && !allowShareExtensionGroupDefault.get() {
errorAlert = ErrorAlert(title: "App is locked!", message: "You can allow sharing in Privacy & Security / SimpleX Lock settings.")
return
}
@@ -148,7 +149,7 @@ class ShareModel: ObservableObject {
if selected.chatInfo.chatType == .local {
completion()
} else {
await MainActor.run { self.bottomBar = .loadingBar(progress: .zero) }
await MainActor.run { self.bottomBar = .loadingBar(progress: 0) }
if let e = await handleEvents(
isGroupChat: ci.chatInfo.chatType == .group,
isWithoutFile: sharedContent.cryptoFile == nil,
@@ -290,14 +291,13 @@ class ShareModel: ObservableObject {
CompletionHandler.isEventLoopEnabled = true
let ch = CompletionHandler()
if isWithoutFile { await ch.completeFile() }
var networkTimeout = CFAbsoluteTimeGetCurrent()
networkTimeout = CFAbsoluteTimeGetCurrent()
while await ch.isRunning {
if CFAbsoluteTimeGetCurrent() - networkTimeout > 30 {
networkTimeout = CFAbsoluteTimeGetCurrent()
await MainActor.run {
self.errorAlert = ErrorAlert(title: "No network connection") {
Button("Keep Trying", role: .cancel) { }
Button("Dismiss Sheet", role: .destructive) { self.completion() }
self.errorAlert = ErrorAlert(title: "Slow network?", message: "Sending a message takes longer than expected.") {
Button("Wait", role: .cancel) { self.networkTimeout = CFAbsoluteTimeGetCurrent() }
Button("Cancel", role: .destructive) { self.completion() }
}
}
}
+26 -3
View File
@@ -85,7 +85,7 @@ struct ShareView: View {
}
private func compose(isLoading: Bool) -> some View {
VStack(spacing: .zero) {
VStack(spacing: 0) {
Divider()
if let content = model.sharedContent {
itemPreview(content)
@@ -132,7 +132,7 @@ struct ShareView: View {
switch content {
case let .image(preview, _): imagePreview(preview)
case let .movie(preview, _, _): imagePreview(preview)
case let .url(linkPreview): imagePreview(linkPreview.image)
case let .url(preview): linkPreview(preview)
case let .data(cryptoFile):
previewArea {
Image(systemName: "doc.fill")
@@ -160,6 +160,29 @@ struct ShareView: View {
}
}
@ViewBuilder private func linkPreview(_ linkPreview: LinkPreview) -> some View {
previewArea {
HStack(alignment: .center, spacing: 8) {
if let uiImage = UIImage(base64Encoded: linkPreview.image) {
Image(uiImage: uiImage)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 80, maxHeight: 60)
}
VStack(alignment: .center, spacing: 4) {
Text(linkPreview.title)
.lineLimit(1)
Text(linkPreview.uri.absoluteString)
.font(.caption)
.lineLimit(1)
.foregroundColor(.secondary)
}
.padding(.vertical, 5)
.frame(maxWidth: .infinity, minHeight: 60)
}
}
}
@ViewBuilder private func previewArea<V: View>(@ViewBuilder content: @escaping () -> V) -> some View {
HStack(alignment: .center, spacing: 8) {
content()
@@ -179,7 +202,7 @@ struct ShareView: View {
private func loadingBar(progress: Double) -> some View {
VStack {
Text("Sending File")
Text("Sending message…")
ProgressView(value: progress)
}
.padding()
@@ -1,7 +1,9 @@
/*
InfoPlist.strings
SimpleX
/* Bundle display name */
"CFBundleDisplayName" = "SimpleX SE";
/* Bundle name */
"CFBundleName" = "SimpleX SE";
/* Copyright (human-readable) */
"NSHumanReadableCopyright" = "Copyright © 2024 SimpleX Chat. Alle Rechte vorbehalten.";
Created by EP on 30/07/2024.
Copyright © 2024 SimpleX Chat. All rights reserved.
*/
@@ -1,7 +1,111 @@
/*
Localizable.strings
SimpleX
/* No comment provided by engineer. */
"%@" = "%@";
/* No comment provided by engineer. */
"App is locked!" = "Die App ist gesperrt!";
/* No comment provided by engineer. */
"Cancel" = "Abbrechen";
/* No comment provided by engineer. */
"Cannot access keychain to save database password" = "Es ist nicht möglich, auf den Schlüsselbund zuzugreifen, um das Datenbankpasswort zu speichern";
/* No comment provided by engineer. */
"Cannot forward message" = "Nachricht kann nicht weitergeleitet werden";
/* No comment provided by engineer. */
"Comment" = "Kommentieren";
/* No comment provided by engineer. */
"Currently maximum supported file size is %@." = "Die maximale erlaubte Dateigröße beträgt aktuell %@.";
/* No comment provided by engineer. */
"Database downgrade required" = "Datenbank-Herabstufung erforderlich";
/* No comment provided by engineer. */
"Database encrypted!" = "Datenbank verschlüsselt!";
/* No comment provided by engineer. */
"Database error" = "Datenbankfehler";
/* No comment provided by engineer. */
"Database passphrase is different from saved in the keychain." = "Das Datenbank-Passwort unterscheidet sich vom im Schlüsselbund gespeicherten.";
/* No comment provided by engineer. */
"Database passphrase is required to open chat." = "Ein Datenbank-Passwort ist erforderlich, um den Chat zu öffnen.";
/* No comment provided by engineer. */
"Database upgrade required" = "Datenbank-Aktualisierung erforderlich";
/* No comment provided by engineer. */
"Error preparing file" = "Fehler beim Vorbereiten der Datei";
/* No comment provided by engineer. */
"Error preparing message" = "Fehler beim Vorbereiten der Nachricht";
/* No comment provided by engineer. */
"Error: %@" = "Fehler: %@";
/* No comment provided by engineer. */
"File error" = "Dateifehler";
/* No comment provided by engineer. */
"Incompatible database version" = "Datenbank-Version nicht kompatibel";
/* No comment provided by engineer. */
"Invalid migration confirmation" = "Migrations-Bestätigung ungültig";
/* No comment provided by engineer. */
"Keychain error" = "Schlüsselbund-Fehler";
/* No comment provided by engineer. */
"Large file!" = "Große Datei!";
/* No comment provided by engineer. */
"No active profile" = "Kein aktives Profil";
/* No comment provided by engineer. */
"Ok" = "OK";
/* No comment provided by engineer. */
"Open the app to downgrade the database." = "Öffne die App, um die Datenbank herabzustufen.";
/* No comment provided by engineer. */
"Open the app to upgrade the database." = "Öffne die App, um die Datenbank zu aktualisieren.";
/* No comment provided by engineer. */
"Passphrase" = "Passwort";
/* No comment provided by engineer. */
"Please create a profile in the SimpleX app" = "Bitte erstelle ein Profil in der SimpleX-App";
/* No comment provided by engineer. */
"Selected chat preferences prohibit this message." = "Diese Nachricht ist wegen der gewählten Chat-Einstellungen nicht erlaubt.";
/* No comment provided by engineer. */
"Sending a message takes longer than expected." = "Das Senden einer Nachricht dauert länger als erwartet.";
/* No comment provided by engineer. */
"Sending message…" = "Nachricht wird gesendet…";
/* No comment provided by engineer. */
"Share" = "Teilen";
/* No comment provided by engineer. */
"Slow network?" = "Langsames Netzwerk?";
/* No comment provided by engineer. */
"Unknown database error: %@" = "Unbekannter Datenbankfehler: %@";
/* No comment provided by engineer. */
"Unsupported format" = "Nicht unterstütztes Format";
/* No comment provided by engineer. */
"Wait" = "Warten";
/* No comment provided by engineer. */
"Wrong database passphrase" = "Falsches Datenbank-Passwort";
/* No comment provided by engineer. */
"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "Du kannst das Teilen in den Einstellungen zu Datenschutz & Sicherheit - SimpleX-Sperre erlauben.";
Created by EP on 30/07/2024.
Copyright © 2024 SimpleX Chat. All rights reserved.
*/
@@ -1,7 +1,9 @@
/*
InfoPlist.strings
SimpleX
/* Bundle display name */
"CFBundleDisplayName" = "SimpleX SE";
/* Bundle name */
"CFBundleName" = "SimpleX SE";
/* Copyright (human-readable) */
"NSHumanReadableCopyright" = "Copyright © 2024 SimpleX Chat. Todos los derechos reservados.";
Created by EP on 30/07/2024.
Copyright © 2024 SimpleX Chat. All rights reserved.
*/
@@ -1,7 +1,111 @@
/*
Localizable.strings
SimpleX
/* No comment provided by engineer. */
"%@" = "%@";
/* No comment provided by engineer. */
"App is locked!" = "¡Aplicación bloqueada!";
/* No comment provided by engineer. */
"Cancel" = "Cancelar";
/* No comment provided by engineer. */
"Cannot access keychain to save database password" = "Keychain inaccesible para guardar la contraseña de la base de datos";
/* No comment provided by engineer. */
"Cannot forward message" = "No se puede reenviar el mensaje";
/* No comment provided by engineer. */
"Comment" = "Comentario";
/* No comment provided by engineer. */
"Currently maximum supported file size is %@." = "El tamaño máximo de archivo admitido es %@.";
/* No comment provided by engineer. */
"Database downgrade required" = "Se requiere volver a versión anterior de la base de datos";
/* No comment provided by engineer. */
"Database encrypted!" = "¡Base de datos cifrada!";
/* No comment provided by engineer. */
"Database error" = "Error en base de datos";
/* No comment provided by engineer. */
"Database passphrase is different from saved in the keychain." = "La contraseña de la base de datos es distinta a la almacenada en keychain.";
/* No comment provided by engineer. */
"Database passphrase is required to open chat." = "Se requiere la contraseña de la base de datos para abrir la aplicación.";
/* No comment provided by engineer. */
"Database upgrade required" = "Se requiere actualizar la base de datos";
/* No comment provided by engineer. */
"Error preparing file" = "Error al preparar el archivo";
/* No comment provided by engineer. */
"Error preparing message" = "Error al preparar el mensaje";
/* No comment provided by engineer. */
"Error: %@" = "Error: %@";
/* No comment provided by engineer. */
"File error" = "Error de archivo";
/* No comment provided by engineer. */
"Incompatible database version" = "Versión de base de datos incompatible";
/* No comment provided by engineer. */
"Invalid migration confirmation" = "Confirmación de migración no válida";
/* No comment provided by engineer. */
"Keychain error" = "Error en keychain";
/* No comment provided by engineer. */
"Large file!" = "¡Archivo grande!";
/* No comment provided by engineer. */
"No active profile" = "Ningún perfil activo";
/* No comment provided by engineer. */
"Ok" = "Ok";
/* No comment provided by engineer. */
"Open the app to downgrade the database." = "Abre la aplicación para volver a versión anterior de la base de datos.";
/* No comment provided by engineer. */
"Open the app to upgrade the database." = "Abre la aplicación para actualizar la base de datos.";
/* No comment provided by engineer. */
"Passphrase" = "Frase de contraseña";
/* No comment provided by engineer. */
"Please create a profile in the SimpleX app" = "Por favor, crea un perfil en SimpleX";
/* No comment provided by engineer. */
"Selected chat preferences prohibit this message." = "Las preferencias seleccionadas no permiten este mensaje.";
/* No comment provided by engineer. */
"Sending a message takes longer than expected." = "Enviar el mensaje lleva más tiempo del esperado.";
/* No comment provided by engineer. */
"Sending message…" = "Enviando mensaje…";
/* No comment provided by engineer. */
"Share" = "Compartir";
/* No comment provided by engineer. */
"Slow network?" = "¿Red lenta?";
/* No comment provided by engineer. */
"Unknown database error: %@" = "Error desconocido en la base de datos: %@";
/* No comment provided by engineer. */
"Unsupported format" = "Formato sin soporte";
/* No comment provided by engineer. */
"Wait" = "Espera";
/* No comment provided by engineer. */
"Wrong database passphrase" = "Contraseña incorrecta de la base de datos";
/* No comment provided by engineer. */
"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "Puedes dar permiso para compartir en Privacidad y Seguridad / Bloque SimpleX.";
Created by EP on 30/07/2024.
Copyright © 2024 SimpleX Chat. All rights reserved.
*/
@@ -1,7 +1,9 @@
/*
InfoPlist.strings
SimpleX
/* Bundle display name */
"CFBundleDisplayName" = "SimpleX SE";
/* Bundle name */
"CFBundleName" = "SimpleX SE";
/* Copyright (human-readable) */
"NSHumanReadableCopyright" = "Copyright © 2024 SimpleX Chat. Tous droits réservés.";
Created by EP on 30/07/2024.
Copyright © 2024 SimpleX Chat. All rights reserved.
*/
@@ -1,7 +1,111 @@
/*
Localizable.strings
SimpleX
/* No comment provided by engineer. */
"%@" = "%@";
/* No comment provided by engineer. */
"App is locked!" = "L'app est verrouillée !";
/* No comment provided by engineer. */
"Cancel" = "Annuler";
/* No comment provided by engineer. */
"Cannot access keychain to save database password" = "Impossible d'accéder à la keychain pour enregistrer le mot de passe de la base de données";
/* No comment provided by engineer. */
"Cannot forward message" = "Impossible de transférer le message";
/* No comment provided by engineer. */
"Comment" = "Commenter";
/* No comment provided by engineer. */
"Currently maximum supported file size is %@." = "Actuellement, la taille maximale des fichiers supportés est de %@.";
/* No comment provided by engineer. */
"Database downgrade required" = "Mise à jour de la base de données nécessaire";
/* No comment provided by engineer. */
"Database encrypted!" = "Base de données chiffrée !";
/* No comment provided by engineer. */
"Database error" = "Erreur de base de données";
/* No comment provided by engineer. */
"Database passphrase is different from saved in the keychain." = "La phrase secrète de la base de données est différente de celle enregistrée dans la keychain.";
/* No comment provided by engineer. */
"Database passphrase is required to open chat." = "La phrase secrète de la base de données est nécessaire pour ouvrir le chat.";
/* No comment provided by engineer. */
"Database upgrade required" = "Mise à niveau de la base de données nécessaire";
/* No comment provided by engineer. */
"Error preparing file" = "Erreur lors de la préparation du fichier";
/* No comment provided by engineer. */
"Error preparing message" = "Erreur lors de la préparation du message";
/* No comment provided by engineer. */
"Error: %@" = "Erreur : %@";
/* No comment provided by engineer. */
"File error" = "Erreur de fichier";
/* No comment provided by engineer. */
"Incompatible database version" = "Version de la base de données incompatible";
/* No comment provided by engineer. */
"Invalid migration confirmation" = "Confirmation de migration invalide";
/* No comment provided by engineer. */
"Keychain error" = "Erreur de la keychain";
/* No comment provided by engineer. */
"Large file!" = "Fichier trop lourd !";
/* No comment provided by engineer. */
"No active profile" = "Pas de profil actif";
/* No comment provided by engineer. */
"Ok" = "Ok";
/* No comment provided by engineer. */
"Open the app to downgrade the database." = "Ouvrez l'app pour rétrograder la base de données.";
/* No comment provided by engineer. */
"Open the app to upgrade the database." = "Ouvrez l'app pour mettre à jour la base de données.";
/* No comment provided by engineer. */
"Passphrase" = "Phrase secrète";
/* No comment provided by engineer. */
"Please create a profile in the SimpleX app" = "Veuillez créer un profil dans l'app SimpleX";
/* No comment provided by engineer. */
"Selected chat preferences prohibit this message." = "Les paramètres de chat sélectionnés ne permettent pas l'envoi de ce message.";
/* No comment provided by engineer. */
"Sending a message takes longer than expected." = "L'envoi d'un message prend plus de temps que prévu.";
/* No comment provided by engineer. */
"Sending message…" = "Envoi du message…";
/* No comment provided by engineer. */
"Share" = "Partager";
/* No comment provided by engineer. */
"Slow network?" = "Réseau lent ?";
/* No comment provided by engineer. */
"Unknown database error: %@" = "Erreur inconnue de la base de données : %@";
/* No comment provided by engineer. */
"Unsupported format" = "Format non pris en charge";
/* No comment provided by engineer. */
"Wait" = "Attendez";
/* No comment provided by engineer. */
"Wrong database passphrase" = "Mauvaise phrase secrète pour la base de données";
/* No comment provided by engineer. */
"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "Vous pouvez autoriser le partage dans les paramètres Confidentialité et sécurité / SimpleX Lock.";
Created by EP on 30/07/2024.
Copyright © 2024 SimpleX Chat. All rights reserved.
*/
@@ -1,7 +1,9 @@
/*
InfoPlist.strings
SimpleX
/* Bundle display name */
"CFBundleDisplayName" = "SimpleX SE";
/* Bundle name */
"CFBundleName" = "SimpleX SE";
/* Copyright (human-readable) */
"NSHumanReadableCopyright" = "Copyright © 2024 SimpleX Chat. Minden jog fenntartva.";
Created by EP on 30/07/2024.
Copyright © 2024 SimpleX Chat. All rights reserved.
*/
@@ -1,7 +1,111 @@
/*
Localizable.strings
SimpleX
/* No comment provided by engineer. */
"%@" = "%@";
/* No comment provided by engineer. */
"App is locked!" = "Az alkalmazás zárolva!";
/* No comment provided by engineer. */
"Cancel" = "Mégse";
/* No comment provided by engineer. */
"Cannot access keychain to save database password" = "Nem lehet hozzáférni a kulcstartóhoz az adatbázis jelszavának mentéséhez";
/* No comment provided by engineer. */
"Cannot forward message" = "Nem lehet továbbítani az üzenetet";
/* No comment provided by engineer. */
"Comment" = "Hozzászólás";
/* No comment provided by engineer. */
"Currently maximum supported file size is %@." = "Jelenleg a maximális támogatott fájlméret %@.";
/* No comment provided by engineer. */
"Database downgrade required" = "Adatbázis visszafejlesztése szükséges";
/* No comment provided by engineer. */
"Database encrypted!" = "Adatbázis titkosítva!";
/* No comment provided by engineer. */
"Database error" = "Adatbázis hiba";
/* No comment provided by engineer. */
"Database passphrase is different from saved in the keychain." = "Az adatbázis jelmondata eltér a kulcstartóban lévőtől.";
/* No comment provided by engineer. */
"Database passphrase is required to open chat." = "Adatbázis jelmondat szükséges a csevegés megnyitásához.";
/* No comment provided by engineer. */
"Database upgrade required" = "Adatbázis fejlesztése szükséges";
/* No comment provided by engineer. */
"Error preparing file" = "Hiba a fájl előkészítésekor";
/* No comment provided by engineer. */
"Error preparing message" = "Hiba az üzenet előkészítésekor";
/* No comment provided by engineer. */
"Error: %@" = "Hiba: %@";
/* No comment provided by engineer. */
"File error" = "Fájlhiba";
/* No comment provided by engineer. */
"Incompatible database version" = "Nem kompatibilis adatbázis verzió";
/* No comment provided by engineer. */
"Invalid migration confirmation" = "Érvénytelen átköltöztetési visszaigazolás";
/* No comment provided by engineer. */
"Keychain error" = "Kulcstartó hiba";
/* No comment provided by engineer. */
"Large file!" = "Nagy fájl!";
/* No comment provided by engineer. */
"No active profile" = "Nincs aktív profil";
/* No comment provided by engineer. */
"Ok" = "Rendben";
/* No comment provided by engineer. */
"Open the app to downgrade the database." = "Nyissa meg az alkalmazást az adatbázis visszafejlesztéséhez.";
/* No comment provided by engineer. */
"Open the app to upgrade the database." = "Nyissa meg az alkalmazást az adatbázis fejlesztéséhez.";
/* No comment provided by engineer. */
"Passphrase" = "Jelmondat";
/* No comment provided by engineer. */
"Please create a profile in the SimpleX app" = "Hozzon létre egy profilt a SimpleX alkalmazásban";
/* No comment provided by engineer. */
"Selected chat preferences prohibit this message." = "A kiválasztott csevegési beállítások tiltják ezt az üzenetet.";
/* No comment provided by engineer. */
"Sending a message takes longer than expected." = "Az üzenet elküldése a vártnál tovább tart.";
/* No comment provided by engineer. */
"Sending message…" = "Üzenet küldése…";
/* No comment provided by engineer. */
"Share" = "Megosztás";
/* No comment provided by engineer. */
"Slow network?" = "Lassú internetkapcsolat?";
/* No comment provided by engineer. */
"Unknown database error: %@" = "Ismeretlen adatbázis hiba: %@";
/* No comment provided by engineer. */
"Unsupported format" = "Nem támogatott formátum";
/* No comment provided by engineer. */
"Wait" = "Várjon";
/* No comment provided by engineer. */
"Wrong database passphrase" = "Hibás adatbázis jelmondat";
/* No comment provided by engineer. */
"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "A megosztást az Adatvédelem és biztonság / SimpleX zár menüben engedélyezheti.";
Created by EP on 30/07/2024.
Copyright © 2024 SimpleX Chat. All rights reserved.
*/
@@ -1,7 +1,9 @@
/*
InfoPlist.strings
SimpleX
/* Bundle display name */
"CFBundleDisplayName" = "SimpleX SE";
/* Bundle name */
"CFBundleName" = "SimpleX SE";
/* Copyright (human-readable) */
"NSHumanReadableCopyright" = "Copyright © 2024 SimpleX Chat. Tutti i diritti riservati.";
Created by EP on 30/07/2024.
Copyright © 2024 SimpleX Chat. All rights reserved.
*/
@@ -1,7 +1,111 @@
/*
Localizable.strings
SimpleX
/* No comment provided by engineer. */
"%@" = "%@";
/* No comment provided by engineer. */
"App is locked!" = "L'app è bloccata!";
/* No comment provided by engineer. */
"Cancel" = "Annulla";
/* No comment provided by engineer. */
"Cannot access keychain to save database password" = "Impossibile accedere al portachiavi per salvare la password del database";
/* No comment provided by engineer. */
"Cannot forward message" = "Impossibile inoltrare il messaggio";
/* No comment provided by engineer. */
"Comment" = "Commento";
/* No comment provided by engineer. */
"Currently maximum supported file size is %@." = "Attualmente la dimensione massima supportata è di %@.";
/* No comment provided by engineer. */
"Database downgrade required" = "Downgrade del database necessario";
/* No comment provided by engineer. */
"Database encrypted!" = "Database crittografato!";
/* No comment provided by engineer. */
"Database error" = "Errore del database";
/* No comment provided by engineer. */
"Database passphrase is different from saved in the keychain." = "La password del database è diversa da quella salvata nel portachiavi.";
/* No comment provided by engineer. */
"Database passphrase is required to open chat." = "La password del database è necessaria per aprire la chat.";
/* No comment provided by engineer. */
"Database upgrade required" = "Aggiornamento del database necessario";
/* No comment provided by engineer. */
"Error preparing file" = "Errore nella preparazione del file";
/* No comment provided by engineer. */
"Error preparing message" = "Errore nella preparazione del messaggio";
/* No comment provided by engineer. */
"Error: %@" = "Errore: %@";
/* No comment provided by engineer. */
"File error" = "Errore del file";
/* No comment provided by engineer. */
"Incompatible database version" = "Versione del database incompatibile";
/* No comment provided by engineer. */
"Invalid migration confirmation" = "Conferma di migrazione non valida";
/* No comment provided by engineer. */
"Keychain error" = "Errore del portachiavi";
/* No comment provided by engineer. */
"Large file!" = "File grande!";
/* No comment provided by engineer. */
"No active profile" = "Nessun profilo attivo";
/* No comment provided by engineer. */
"Ok" = "Ok";
/* No comment provided by engineer. */
"Open the app to downgrade the database." = "Apri l'app per eseguire il downgrade del database.";
/* No comment provided by engineer. */
"Open the app to upgrade the database." = "Apri l'app per aggiornare il database.";
/* No comment provided by engineer. */
"Passphrase" = "Password";
/* No comment provided by engineer. */
"Please create a profile in the SimpleX app" = "Crea un profilo nell'app SimpleX";
/* No comment provided by engineer. */
"Selected chat preferences prohibit this message." = "Le preferenze della chat selezionata vietano questo messaggio.";
/* No comment provided by engineer. */
"Sending a message takes longer than expected." = "L'invio di un messaggio richiede più tempo del previsto.";
/* No comment provided by engineer. */
"Sending message…" = "Invio messaggio…";
/* No comment provided by engineer. */
"Share" = "Condividi";
/* No comment provided by engineer. */
"Slow network?" = "Rete lenta?";
/* No comment provided by engineer. */
"Unknown database error: %@" = "Errore del database sconosciuto: %@";
/* No comment provided by engineer. */
"Unsupported format" = "Formato non supportato";
/* No comment provided by engineer. */
"Wait" = "Attendi";
/* No comment provided by engineer. */
"Wrong database passphrase" = "Password del database sbagliata";
/* No comment provided by engineer. */
"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "Puoi consentire la condivisione in Privacy e sicurezza / impostazioni di SimpleX Lock.";
Created by EP on 30/07/2024.
Copyright © 2024 SimpleX Chat. All rights reserved.
*/

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