Compare commits

..

182 Commits

Author SHA1 Message Date
Evgeny Poberezkin ea60e87f1d Merge branch 'ep/revert-base64' into ep/revert-base64-android 2024-04-02 23:38:12 +01:00
Evgeny Poberezkin a722751b77 Revert "deps: switch to base64 via simplexmq (#3957)"
This reverts commit d65137882b.
2024-04-02 23:36:38 +01:00
Evgeny Poberezkin 73ec44a9be Merge branch 'master' into master-android 2024-04-01 14:37:08 +01:00
Evgeny Poberezkin 59fe6ab8ce Merge branch 'master' into master-android 2024-03-21 19:00:42 +00:00
Evgeny Poberezkin ec64be7c89 Merge branch 'master' into master-android 2024-03-19 16:08:24 +00:00
Evgeny Poberezkin 41984e56c0 Merge branch 'master' into master-android 2024-03-12 18:29:34 +00:00
Evgeny Poberezkin c435510b2e Merge branch 'master' into master-android 2024-03-12 15:21:02 +00:00
Evgeny Poberezkin de65d0e0a2 Merge branch 'master' into master-android 2024-03-11 09:27:17 +00:00
Evgeny Poberezkin 9cde37c6f5 Merge branch 'master' into master-android 2024-02-11 11:41:36 +00:00
Evgeny Poberezkin 9514acf6aa Merge branch 'master' into master-android 2024-02-03 18:39:08 +00:00
Evgeny Poberezkin 40d54f3f16 Merge branch 'master' into master-android 2024-02-02 08:32:46 +00:00
Evgeny Poberezkin 3249c5e463 Merge branch 'master' into master-android 2024-01-30 23:18:11 +00:00
Evgeny Poberezkin e440fba582 Merge branch 'master' into master-android 2024-01-27 20:06:31 +00:00
Evgeny Poberezkin 43cedbce26 Merge branch 'master' into master-android 2024-01-23 21:13:20 +00:00
spaced4ndy ebad009553 Merge branch 'master-ghc8107' into master-android 2024-01-22 18:44:55 +04:00
spaced4ndy 35cf113d98 Merge branch 'master' into master-ghc8107 2024-01-22 18:44:13 +04:00
Evgeny Poberezkin e82d2cbea2 Merge branch 'master-ghc8107' into master-android 2024-01-20 21:36:45 +00:00
Evgeny Poberezkin a0e056994e Merge branch 'master' into master-ghc8107 2024-01-20 21:36:23 +00:00
Evgeny Poberezkin e4d8ea17fc Merge branch 'master-ghc8107' into master-android 2024-01-20 15:07:37 +00:00
Evgeny Poberezkin e69db17804 Merge branch 'master' into master-ghc8107 2024-01-20 15:07:20 +00:00
Evgeny Poberezkin 9282cca396 Merge branch 'master-ghc8107' into master-android 2024-01-17 19:30:14 +00:00
Evgeny Poberezkin ad2eaeb004 Merge branch 'master' into master-ghc8107 2024-01-17 19:29:49 +00:00
Evgeny Poberezkin 3f44c9af24 Merge branch 'master-ghc8107' into master-android 2024-01-10 12:09:28 +00:00
Evgeny Poberezkin 4296515473 Merge branch 'master' into master-ghc8107 2024-01-10 12:06:48 +00:00
Evgeny Poberezkin 88830a2e09 Merge branch 'master-ghc8107' into master-android 2024-01-09 11:07:50 +00:00
Evgeny Poberezkin c8a269e391 Merge branch 'master' into master-ghc8107 2024-01-09 11:07:32 +00:00
Evgeny Poberezkin a45897007c Merge branch 'master-ghc8107' into master-android 2023-12-30 19:32:05 +00:00
Evgeny Poberezkin 42ea1df342 Merge branch 'master' into master-ghc8107 2023-12-30 19:19:39 +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 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 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 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 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 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 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 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
214 changed files with 1544 additions and 9418 deletions
+3 -9
View File
@@ -5,20 +5,14 @@ on:
pull_request_target: pull_request_target:
types: [opened, closed, synchronize] types: [opened, closed, synchronize]
permissions:
actions: write
contents: write
pull-requests: write
statuses: write
jobs: jobs:
CLAssistant: CLAssistant:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: "CLA Assistant" - name: "CLA Assistant"
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request'
# Beta Release # Beta Release
uses: cla-assistant/github-action@v2.3.0 uses: cla-assistant/github-action@v2.1.3-beta
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# the below token should have repo scope and must be manually added by you in the repository's secret # the below token should have repo scope and must be manually added by you in the repository's secret
@@ -39,4 +33,4 @@ jobs:
#custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA' #custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA'
#custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.' #custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.'
#lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true) #lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true)
#use-dco-flag: true - If you are using DCO instead of CLA #use-dco-flag: true - If you are using DCO instead of CLA
+1 -1
View File
@@ -4,7 +4,7 @@
[![Join on Reddit](https://img.shields.io/reddit/subreddit-subscribers/SimpleXChat?style=social)](https://www.reddit.com/r/SimpleXChat) [![Join on Reddit](https://img.shields.io/reddit/subreddit-subscribers/SimpleXChat?style=social)](https://www.reddit.com/r/SimpleXChat)
<a rel="me" href="https://mastodon.social/@simplex">![Follow on Mastodon](https://img.shields.io/mastodon/follow/108619463746856738?domain=https%3A%2F%2Fmastodon.social&style=social)</a> <a rel="me" href="https://mastodon.social/@simplex">![Follow on Mastodon](https://img.shields.io/mastodon/follow/108619463746856738?domain=https%3A%2F%2Fmastodon.social&style=social)</a>
| 30/03/2023 | EN, [FR](/docs/lang/fr/README.md), [CZ](/docs/lang/cs/README.md), [PL](/docs/lang/pl/README.md) | | 30/03/2023 | EN, [FR](/docs/lang/fr/README.md), [CZ](/docs/lang/cs/README.md) |
<img src="images/simplex-chat-logo.svg" alt="SimpleX logo" width="100%"> <img src="images/simplex-chat-logo.svg" alt="SimpleX logo" width="100%">
-19
View File
@@ -103,7 +103,6 @@ final class ChatModel: ObservableObject {
// tracks keyboard height via subscription in AppDelegate // tracks keyboard height via subscription in AppDelegate
@Published var keyboardHeight: CGFloat = 0 @Published var keyboardHeight: CGFloat = 0
@Published var pasteboardHasStrings: Bool = UIPasteboard.general.hasStrings @Published var pasteboardHasStrings: Bool = UIPasteboard.general.hasStrings
@Published var networkInfo = UserNetworkInfo(networkType: .other, online: true)
var messageDelivery: Dictionary<Int64, () -> Void> = [:] var messageDelivery: Dictionary<Int64, () -> Void> = [:]
@@ -779,24 +778,6 @@ final class Chat: ObservableObject, Identifiable {
var viewId: String { get { "\(chatInfo.id) \(created.timeIntervalSince1970)" } } var viewId: String { get { "\(chatInfo.id) \(created.timeIntervalSince1970)" } }
func groupFeatureEnabled(_ feature: GroupFeature) -> Bool {
if case let .group(groupInfo) = self.chatInfo {
let p = groupInfo.fullGroupPreferences
return switch feature {
case .timedMessages: p.timedMessages.on
case .directMessages: p.directMessages.on(for: groupInfo.membership)
case .fullDelete: p.fullDelete.on
case .reactions: p.reactions.on
case .voice: p.voice.on(for: groupInfo.membership)
case .files: p.files.on(for: groupInfo.membership)
case .simplexLinks: p.simplexLinks.on(for: groupInfo.membership)
case .history: p.history.on
}
} else {
return true
}
}
public static var sampleData: Chat = Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []) public static var sampleData: Chat = Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: [])
} }
@@ -1,73 +0,0 @@
//
// NetworkObserver.swift
// SimpleX (iOS)
//
// Created by Avently on 05.04.2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import Foundation
import Network
import SimpleXChat
class NetworkObserver {
static let shared = NetworkObserver()
private let queue: DispatchQueue = DispatchQueue(label: "chat.simplex.app.NetworkObserver")
private var prevInfo: UserNetworkInfo? = nil
private var monitor: NWPathMonitor?
private let monitorLock: DispatchQueue = DispatchQueue(label: "chat.simplex.app.monitorLock")
func restartMonitor() {
monitorLock.sync {
monitor?.cancel()
let mon = NWPathMonitor()
mon.pathUpdateHandler = { [weak self] path in
self?.networkPathChanged(path: path)
}
mon.start(queue: queue)
monitor = mon
}
}
private func networkPathChanged(path: NWPath) {
let info = UserNetworkInfo(
networkType: networkTypeFromPath(path),
online: path.status == .satisfied
)
if (prevInfo != info) {
prevInfo = info
setNetworkInfo(info)
}
}
private func networkTypeFromPath(_ path: NWPath) -> UserNetworkType {
if path.usesInterfaceType(.wiredEthernet) {
.ethernet
} else if path.usesInterfaceType(.wifi) {
.wifi
} else if path.usesInterfaceType(.cellular) {
.cellular
} else if path.usesInterfaceType(.other) {
.other
} else {
.none
}
}
private static var networkObserver: NetworkObserver? = nil
private func setNetworkInfo(_ info: UserNetworkInfo) {
logger.debug("setNetworkInfo Network changed: \(String(describing: info))")
DispatchQueue.main.sync {
ChatModel.shared.networkInfo = info
}
if !hasChatCtrl() { return }
self.monitorLock.sync {
do {
try apiSetNetworkInfo(info)
} catch let err {
logger.error("setNetworkInfo error: \(responseError(err))")
}
}
}
}
+4 -24
View File
@@ -353,20 +353,11 @@ func apiGetChatItemInfo(type: ChatType, id: Int64, itemId: Int64) async throws -
throw r throw r
} }
func apiForwardChatItem(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemId: Int64) async -> ChatItem? {
let cmd: ChatCommand = .apiForwardChatItem(toChatType: toChatType, toChatId: toChatId, fromChatType: fromChatType, fromChatId: fromChatId, itemId: itemId)
return await processSendMessageCmd(toChatType: toChatType, cmd: cmd)
}
func apiSendMessage(type: ChatType, id: Int64, file: CryptoFile?, quotedItemId: Int64?, msg: MsgContent, live: Bool = false, ttl: Int? = nil) async -> ChatItem? { func apiSendMessage(type: ChatType, id: Int64, file: CryptoFile?, quotedItemId: Int64?, msg: MsgContent, live: Bool = false, ttl: Int? = nil) async -> ChatItem? {
let cmd: ChatCommand = .apiSendMessage(type: type, id: id, file: file, quotedItemId: quotedItemId, msg: msg, live: live, ttl: ttl)
return await processSendMessageCmd(toChatType: type, cmd: cmd)
}
private func processSendMessageCmd(toChatType: ChatType, cmd: ChatCommand) async -> ChatItem? {
let chatModel = ChatModel.shared let chatModel = ChatModel.shared
let cmd: ChatCommand = .apiSendMessage(type: type, id: id, file: file, quotedItemId: quotedItemId, msg: msg, live: live, ttl: ttl)
let r: ChatResponse let r: ChatResponse
if toChatType == .direct { if type == .direct {
var cItem: ChatItem? = nil var cItem: ChatItem? = nil
let endTask = beginBGTask({ let endTask = beginBGTask({
if let cItem = cItem { if let cItem = cItem {
@@ -406,7 +397,7 @@ func apiCreateChatItem(noteFolderId: Int64, file: CryptoFile?, msg: MsgContent)
} }
private func sendMessageErrorAlert(_ r: ChatResponse) { private func sendMessageErrorAlert(_ r: ChatResponse) {
logger.error("send message error: \(String(describing: r))") logger.error("apiSendMessage error: \(String(describing: r))")
AlertManager.shared.showAlertMsg( AlertManager.shared.showAlertMsg(
title: "Error sending message", title: "Error sending message",
message: "Error: \(String(describing: r))" message: "Error: \(String(describing: r))"
@@ -543,12 +534,6 @@ func setNetworkConfig(_ cfg: NetCfg, ctrl: chat_ctrl? = nil) throws {
throw r throw r
} }
func apiSetNetworkInfo(_ networkInfo: UserNetworkInfo) throws {
let r = chatSendCmdSync(.apiSetNetworkInfo(networkInfo: networkInfo))
if case .cmdOk = r { return }
throw r
}
func reconnectAllServers() async throws { func reconnectAllServers() async throws {
try await sendCommandOkResp(.reconnectAllServers) try await sendCommandOkResp(.reconnectAllServers)
} }
@@ -1312,7 +1297,6 @@ func initializeChat(start: Bool, confirmStart: Bool = false, dbKey: String? = ni
defer { m.ctrlInitInProgress = false } defer { m.ctrlInitInProgress = false }
(m.chatDbEncrypted, m.chatDbStatus) = chatMigrateInit(dbKey, confirmMigrations: confirmMigrations) (m.chatDbEncrypted, m.chatDbStatus) = chatMigrateInit(dbKey, confirmMigrations: confirmMigrations)
if m.chatDbStatus != .ok { return } if m.chatDbStatus != .ok { return }
NetworkObserver.shared.restartMonitor()
// If we migrated successfully means previous re-encryption process on database level finished successfully too // If we migrated successfully means previous re-encryption process on database level finished successfully too
if encryptionStartedDefault.get() { if encryptionStartedDefault.get() {
encryptionStartedDefault.set(false) encryptionStartedDefault.set(false)
@@ -1478,8 +1462,6 @@ class ChatReceiver {
private var receiveMessages = true private var receiveMessages = true
private var _lastMsgTime = Date.now private var _lastMsgTime = Date.now
var messagesChannel: ((ChatResponse) -> Void)? = nil
static let shared = ChatReceiver() static let shared = ChatReceiver()
var lastMsgTime: Date { get { _lastMsgTime } } var lastMsgTime: Date { get { _lastMsgTime } }
@@ -1497,9 +1479,6 @@ class ChatReceiver {
if let msg = await chatRecvMsg() { if let msg = await chatRecvMsg() {
self._lastMsgTime = .now self._lastMsgTime = .now
await processReceivedMsg(msg) await processReceivedMsg(msg)
if let messagesChannel {
messagesChannel(msg)
}
} }
_ = try? await Task.sleep(nanoseconds: 7_500_000) _ = try? await Task.sleep(nanoseconds: 7_500_000)
} }
@@ -1812,6 +1791,7 @@ func processReceivedMsg(_ res: ChatResponse) async {
} }
case let .sndFileCompleteXFTP(user, aChatItem, _): case let .sndFileCompleteXFTP(user, aChatItem, _):
await chatItemSimpleUpdate(user, aChatItem) await chatItemSimpleUpdate(user, aChatItem)
Task { cleanupFile(aChatItem) }
case let .sndFileError(user, aChatItem, _): case let .sndFileError(user, aChatItem, _):
if let aChatItem = aChatItem { if let aChatItem = aChatItem {
await chatItemSimpleUpdate(user, aChatItem) await chatItemSimpleUpdate(user, aChatItem)
@@ -9,7 +9,6 @@
import SwiftUI import SwiftUI
import WebKit import WebKit
import SimpleXChat import SimpleXChat
import AVFoundation
struct ActiveCallView: View { struct ActiveCallView: View {
@EnvironmentObject var m: ChatModel @EnvironmentObject var m: ChatModel
@@ -22,7 +21,6 @@ struct ActiveCallView: View {
@Binding var canConnectCall: Bool @Binding var canConnectCall: Bool
@State var prevColorScheme: ColorScheme = .dark @State var prevColorScheme: ColorScheme = .dark
@State var pipShown = false @State var pipShown = false
@State var wasConnected = false
var body: some View { var body: some View {
ZStack(alignment: .topLeading) { ZStack(alignment: .topLeading) {
@@ -71,11 +69,6 @@ struct ActiveCallView: View {
Task { await m.callCommand.setClient(nil) } Task { await m.callCommand.setClient(nil) }
AppDelegate.keepScreenOn(false) AppDelegate.keepScreenOn(false)
client?.endCall() client?.endCall()
CallSoundsPlayer.shared.stop()
try? AVAudioSession.sharedInstance().setCategory(.soloAmbient)
if (wasConnected) {
CallSoundsPlayer.shared.vibrate(long: true)
}
} }
.background(m.activeCallViewIsCollapsed ? .clear : .black) .background(m.activeCallViewIsCollapsed ? .clear : .black)
// Quite a big delay when opening/closing the view when a scheme changes (globally) this way. It's not needed when CallKit is used since status bar is green with white text on it // Quite a big delay when opening/closing the view when a scheme changes (globally) this way. It's not needed when CallKit is used since status bar is green with white text on it
@@ -110,11 +103,6 @@ struct ActiveCallView: View {
call.callState = .invitationSent call.callState = .invitationSent
call.localCapabilities = capabilities call.localCapabilities = capabilities
} }
if call.supportsVideo {
try? AVAudioSession.sharedInstance().setCategory(.playback, options: .defaultToSpeaker)
}
CallSoundsPlayer.shared.startConnectingCallSound()
activeCallWaitDeliveryReceipt()
} }
case let .offer(offer, iceCandidates, capabilities): case let .offer(offer, iceCandidates, capabilities):
Task { Task {
@@ -138,8 +126,6 @@ struct ActiveCallView: View {
} }
await MainActor.run { await MainActor.run {
call.callState = .negotiated call.callState = .negotiated
CallSoundsPlayer.shared.stop()
try? AVAudioSession.sharedInstance().setCategory(.soloAmbient)
} }
} }
case let .ice(iceCandidates): case let .ice(iceCandidates):
@@ -158,10 +144,6 @@ struct ActiveCallView: View {
: CallController.shared.reportIncomingCall(call: call, connectedAt: nil) : CallController.shared.reportIncomingCall(call: call, connectedAt: nil)
call.callState = .connected call.callState = .connected
call.connectedAt = .now call.connectedAt = .now
if !wasConnected {
CallSoundsPlayer.shared.vibrate(long: false)
wasConnected = true
}
} }
if state.connectionState == "closed" { if state.connectionState == "closed" {
closeCallView(client) closeCallView(client)
@@ -179,10 +161,6 @@ struct ActiveCallView: View {
call.callState = .connected call.callState = .connected
call.connectionInfo = connectionInfo call.connectionInfo = connectionInfo
call.connectedAt = .now call.connectedAt = .now
if !wasConnected {
CallSoundsPlayer.shared.vibrate(long: false)
wasConnected = true
}
case .ended: case .ended:
closeCallView(client) closeCallView(client)
call.callState = .ended call.callState = .ended
@@ -209,22 +187,6 @@ struct ActiveCallView: View {
} }
} }
private func activeCallWaitDeliveryReceipt() {
ChatReceiver.shared.messagesChannel = { msg in
guard let call = ChatModel.shared.activeCall, call.callState == .invitationSent else {
ChatReceiver.shared.messagesChannel = nil
return
}
if case let .chatItemStatusUpdated(_, msg) = msg,
msg.chatInfo.id == call.contact.id,
case .sndCall = msg.chatItem.content,
case .sndRcvd = msg.chatItem.meta.itemStatus {
CallSoundsPlayer.shared.startInCallSound()
ChatReceiver.shared.messagesChannel = nil
}
}
}
private func closeCallView(_ client: WebRTCClient) { private func closeCallView(_ client: WebRTCClient) {
if m.activeCall != nil { if m.activeCall != nil {
m.showCallView = false m.showCallView = false
@@ -8,7 +8,6 @@
import Foundation import Foundation
import AVFoundation import AVFoundation
import UIKit
class SoundPlayer { class SoundPlayer {
static let shared = SoundPlayer() static let shared = SoundPlayer()
@@ -44,63 +43,3 @@ class SoundPlayer {
audioPlayer = nil audioPlayer = nil
} }
} }
class CallSoundsPlayer {
static let shared = CallSoundsPlayer()
private var audioPlayer: AVAudioPlayer?
private var playerTask: Task = Task {}
private func start(_ soundName: String, delayMs: Double) {
audioPlayer?.stop()
playerTask.cancel()
logger.debug("start \(soundName)")
guard let path = Bundle.main.path(forResource: soundName, ofType: "mp3", inDirectory: "sounds") else {
logger.debug("start: file not found")
return
}
do {
let player = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
if player.prepareToPlay() {
audioPlayer = player
}
} catch {
logger.debug("start: AVAudioPlayer error \(error.localizedDescription)")
}
playerTask = Task {
while let player = audioPlayer {
player.play()
do {
try await Task.sleep(nanoseconds: UInt64((player.duration * 1_000_000_000) + delayMs * 1_000_000))
} catch {
break
}
}
}
}
func startConnectingCallSound() {
start("connecting_call", delayMs: 0)
}
func startInCallSound() {
// Taken from https://github.com/TelegramOrg/Telegram-Android
// https://github.com/TelegramOrg/Telegram-Android/blob/master/LICENSE
start("in_call", delayMs: 1000)
}
func stop() {
playerTask.cancel()
audioPlayer?.stop()
audioPlayer = nil
}
func vibrate(long: Bool) {
// iOS just don't want to vibrate more than once after a short period of time, and all 'styles' feel the same
if long {
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate)
} else {
UIImpactFeedbackGenerator(style: .heavy).impactOccurred()
}
}
}
+3 -4
View File
@@ -431,18 +431,17 @@ struct RTCIceServer: Codable, Equatable {
} }
// the servers are expected in this format: // the servers are expected in this format:
// stuns:stun.simplex.im:443?transport=tcp // stun:stun.simplex.im:443?transport=tcp
// turns:private2:Hxuq2QxUjnhj96Zq2r4HjqHRj@turn.simplex.im:443?transport=tcp // turn:private:yleob6AVkiNI87hpR94Z@turn.simplex.im:443?transport=tcp
func parseRTCIceServer(_ str: String) -> RTCIceServer? { func parseRTCIceServer(_ str: String) -> RTCIceServer? {
var s = replaceScheme(str, "stun:") var s = replaceScheme(str, "stun:")
s = replaceScheme(s, "stuns:")
s = replaceScheme(s, "turn:") s = replaceScheme(s, "turn:")
s = replaceScheme(s, "turns:") s = replaceScheme(s, "turns:")
if let u: URL = URL(string: s), if let u: URL = URL(string: s),
let scheme = u.scheme, let scheme = u.scheme,
let host = u.host, let host = u.host,
let port = u.port, let port = u.port,
u.path == "" && (scheme == "stun" || scheme == "stuns" || scheme == "turn" || scheme == "turns") { u.path == "" && (scheme == "stun" || scheme == "turn" || scheme == "turns") {
let query = u.query == nil || u.query == "" ? "" : "?" + (u.query ?? "") let query = u.query == nil || u.query == "" ? "" : "?" + (u.query ?? "")
return RTCIceServer( return RTCIceServer(
urls: ["\(scheme):\(host):\(port)\(query)"], urls: ["\(scheme):\(host):\(port)\(query)"],
@@ -49,7 +49,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
} }
private let rtcAudioSession = RTCAudioSession.sharedInstance() private let rtcAudioSession = RTCAudioSession.sharedInstance()
private let audioQueue = DispatchQueue(label: "chat.simplex.app.audio") private let audioQueue = DispatchQueue(label: "audio")
private var sendCallResponse: (WVAPIMessage) async -> Void private var sendCallResponse: (WVAPIMessage) async -> Void
var activeCall: Binding<Call?> var activeCall: Binding<Call?>
private var localRendererAspectRatio: Binding<CGFloat?> private var localRendererAspectRatio: Binding<CGFloat?>
@@ -65,14 +65,14 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
self.localRendererAspectRatio = localRendererAspectRatio self.localRendererAspectRatio = localRendererAspectRatio
rtcAudioSession.useManualAudio = CallController.useCallKit() rtcAudioSession.useManualAudio = CallController.useCallKit()
rtcAudioSession.isAudioEnabled = !CallController.useCallKit() rtcAudioSession.isAudioEnabled = !CallController.useCallKit()
logger.debug("WebRTCClient: rtcAudioSession has manual audio \(self.rtcAudioSession.useManualAudio) and audio enabled \(self.rtcAudioSession.isAudioEnabled)") logger.debug("WebRTCClient: rtcAudioSession has manual audio \(self.rtcAudioSession.useManualAudio) and audio enabled \(self.rtcAudioSession.isAudioEnabled)}")
super.init() super.init()
} }
let defaultIceServers: [WebRTC.RTCIceServer] = [ let defaultIceServers: [WebRTC.RTCIceServer] = [
WebRTC.RTCIceServer(urlStrings: ["stuns:stun.simplex.im:443"]), WebRTC.RTCIceServer(urlStrings: ["stun:stun.simplex.im:443"]),
//WebRTC.RTCIceServer(urlStrings: ["turns:turn.simplex.im:443?transport=udp"], username: "private2", credential: "Hxuq2QxUjnhj96Zq2r4HjqHRj"), WebRTC.RTCIceServer(urlStrings: ["turn:turn.simplex.im:443?transport=udp"], username: "private", credential: "yleob6AVkiNI87hpR94Z"),
WebRTC.RTCIceServer(urlStrings: ["turns:turn.simplex.im:443?transport=tcp"], username: "private2", credential: "Hxuq2QxUjnhj96Zq2r4HjqHRj"), WebRTC.RTCIceServer(urlStrings: ["turn:turn.simplex.im:443?transport=tcp"], username: "private", credential: "yleob6AVkiNI87hpR94Z"),
] ]
func initializeCall(_ iceServers: [WebRTC.RTCIceServer]?, _ mediaType: CallMediaType, _ aesKey: String?, _ relay: Bool?) -> Call { func initializeCall(_ iceServers: [WebRTC.RTCIceServer]?, _ mediaType: CallMediaType, _ aesKey: String?, _ relay: Bool?) -> Call {
@@ -11,7 +11,6 @@ import SimpleXChat
struct CIChatFeatureView: View { struct CIChatFeatureView: View {
@EnvironmentObject var m: ChatModel @EnvironmentObject var m: ChatModel
@ObservedObject var chat: Chat
var chatItem: ChatItem var chatItem: ChatItem
@Binding var revealed: Bool @Binding var revealed: Bool
var feature: Feature var feature: Feature
@@ -19,7 +18,7 @@ struct CIChatFeatureView: View {
var iconColor: Color var iconColor: Color
var body: some View { var body: some View {
if !revealed, let fs = mergedFeatures() { if !revealed, let fs = mergedFeautures() {
HStack { HStack {
ForEach(fs, content: featureIconView) ForEach(fs, content: featureIconView)
} }
@@ -48,7 +47,7 @@ struct CIChatFeatureView: View {
} }
} }
private func mergedFeatures() -> [FeatureInfo]? { private func mergedFeautures() -> [FeatureInfo]? {
var fs: [FeatureInfo] = [] var fs: [FeatureInfo] = []
var icons: Set<String> = [] var icons: Set<String> = []
if var i = m.getChatItemIndex(chatItem) { if var i = m.getChatItemIndex(chatItem) {
@@ -68,8 +67,8 @@ struct CIChatFeatureView: View {
switch ci.content { switch ci.content {
case let .rcvChatFeature(feature, enabled, param): FeatureInfo(feature, enabled.iconColor, param) case let .rcvChatFeature(feature, enabled, param): FeatureInfo(feature, enabled.iconColor, param)
case let .sndChatFeature(feature, enabled, param): FeatureInfo(feature, enabled.iconColor, param) case let .sndChatFeature(feature, enabled, param): FeatureInfo(feature, enabled.iconColor, param)
case let .rcvGroupFeature(feature, preference, param, role): FeatureInfo(feature, preference.enabled(role, for: chat.chatInfo.groupInfo?.membership).iconColor, param) case let .rcvGroupFeature(feature, preference, param): FeatureInfo(feature, preference.enable.iconColor, param)
case let .sndGroupFeature(feature, preference, param, role): FeatureInfo(feature, preference.enabled(role, for: chat.chatInfo.groupInfo?.membership).iconColor, param) case let .sndGroupFeature(feature, preference, param): FeatureInfo(feature, preference.enable.iconColor, param)
default: nil default: nil
} }
} }
@@ -104,6 +103,6 @@ struct CIChatFeatureView: View {
struct CIChatFeatureView_Previews: PreviewProvider { struct CIChatFeatureView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
let enabled = FeatureEnabled(forUser: false, forContact: false) let enabled = FeatureEnabled(forUser: false, forContact: false)
CIChatFeatureView(chat: Chat.sampleData, chatItem: ChatItem.getChatFeatureSample(.fullDelete, enabled), revealed: Binding.constant(true), feature: ChatFeature.fullDelete, iconColor: enabled.iconColor) CIChatFeatureView(chatItem: ChatItem.getChatFeatureSample(.fullDelete, enabled), revealed: Binding.constant(true), feature: ChatFeature.fullDelete, iconColor: enabled.iconColor)
} }
} }
@@ -17,7 +17,6 @@ struct CIEventView: View {
.padding(.horizontal, 6) .padding(.horizontal, 6)
.padding(.vertical, 4) .padding(.vertical, 4)
.textSelection(.disabled) .textSelection(.disabled)
.lineLimit(3)
} }
} }
@@ -69,12 +69,19 @@ struct CIFileView: View {
return false return false
} }
private func fileSizeValid() -> Bool {
if let file = file {
return file.fileSize <= getMaxFileSize(file.fileProtocol)
}
return false
}
private func fileAction() { private func fileAction() {
logger.debug("CIFileView fileAction") logger.debug("CIFileView fileAction")
if let file = file { if let file = file {
switch (file.fileStatus) { switch (file.fileStatus) {
case .rcvInvitation: case .rcvInvitation:
if fileSizeValid(file) { if fileSizeValid() {
Task { Task {
logger.debug("CIFileView fileAction - in .rcvInvitation, in Task") logger.debug("CIFileView fileAction - in .rcvInvitation, in Task")
if let user = m.currentUser { if let user = m.currentUser {
@@ -136,7 +143,7 @@ struct CIFileView: View {
case .sndCancelled: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10) case .sndCancelled: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10)
case .sndError: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10) case .sndError: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10)
case .rcvInvitation: case .rcvInvitation:
if fileSizeValid(file) { if fileSizeValid() {
fileIcon("arrow.down.doc.fill", color: .accentColor) fileIcon("arrow.down.doc.fill", color: .accentColor)
} else { } else {
fileIcon("doc.fill", color: .orange, innerIcon: "exclamationmark", innerIconSize: 12) fileIcon("doc.fill", color: .orange, innerIcon: "exclamationmark", innerIconSize: 12)
@@ -194,13 +201,6 @@ struct CIFileView: View {
} }
} }
func fileSizeValid(_ file: CIFile?) -> Bool {
if let file = file {
return file.fileSize <= getMaxFileSize(file.fileProtocol)
}
return false
}
func saveCryptoFile(_ fileSource: CryptoFile) { func saveCryptoFile(_ fileSource: CryptoFile) {
if let cfArgs = fileSource.cryptoArgs { if let cfArgs = fileSource.cryptoArgs {
let url = getAppFilePath(fileSource.filePath) let url = getAppFilePath(fileSource.filePath)
@@ -126,7 +126,7 @@ struct CIVideoView: View {
if !decryptionInProgress { if !decryptionInProgress {
Button { Button {
decrypt(file: file) { decrypt(file: file) {
if urlDecrypted != nil { if let decrypted = urlDecrypted {
videoPlaying = true videoPlaying = true
player?.play() player?.play()
} }
@@ -65,8 +65,6 @@ struct FramedItemView: View {
} }
} }
} }
} else if let itemForwarded = chatItem.meta.itemForwarded {
framedItemHeader(icon: "arrowshape.turn.up.forward", caption: Text(itemForwarded.text(chat.chatInfo.chatType)).italic(), pad: true)
} }
ChatItemContentView(chat: chat, chatItem: chatItem, revealed: $revealed, msgContentView: framedMsgContentView) ChatItemContentView(chat: chat, chatItem: chatItem, revealed: $revealed, msgContentView: framedMsgContentView)
@@ -165,7 +163,7 @@ struct FramedItemView: View {
) )
} }
@ViewBuilder func framedItemHeader(icon: String? = nil, caption: Text, pad: Bool = false) -> some View { @ViewBuilder func framedItemHeader(icon: String? = nil, caption: Text) -> some View {
let v = HStack(spacing: 6) { let v = HStack(spacing: 6) {
if let icon = icon { if let icon = icon {
Image(systemName: icon) Image(systemName: icon)
@@ -180,7 +178,7 @@ struct FramedItemView: View {
.foregroundColor(.secondary) .foregroundColor(.secondary)
.padding(.horizontal, 12) .padding(.horizontal, 12)
.padding(.top, 6) .padding(.top, 6)
.padding(.bottom, pad || (chatItem.quotedItem == nil && chatItem.meta.itemForwarded == nil) ? 6 : 0) .padding(.bottom, chatItem.quotedItem == nil ? 6 : 0) // TODO think how to regroup
.overlay(DetermineWidth()) .overlay(DetermineWidth())
.frame(minWidth: msgWidth, alignment: .leading) .frame(minWidth: msgWidth, alignment: .leading)
.background(chatItemFrameContextColor(chatItem, colorScheme)) .background(chatItemFrameContextColor(chatItem, colorScheme))
@@ -355,9 +353,9 @@ private struct MetaColorPreferenceKey: PreferenceKey {
func onlyImageOrVideo(_ ci: ChatItem) -> Bool { func onlyImageOrVideo(_ ci: ChatItem) -> Bool {
if case let .image(text, _) = ci.content.msgContent { if case let .image(text, _) = ci.content.msgContent {
return ci.meta.itemDeleted == nil && !ci.meta.isLive && ci.quotedItem == nil && ci.meta.itemForwarded == nil && text == "" return ci.meta.itemDeleted == nil && !ci.meta.isLive && ci.quotedItem == nil && text == ""
} else if case let .video(text, _, _) = ci.content.msgContent { } else if case let .video(text, _, _) = ci.content.msgContent {
return ci.meta.itemDeleted == nil && !ci.meta.isLive && ci.quotedItem == nil && ci.meta.itemForwarded == nil && text == "" return ci.meta.itemDeleted == nil && !ci.meta.isLive && ci.quotedItem == nil && text == ""
} }
return false return false
} }
@@ -1,152 +0,0 @@
//
// ChatItemForwardingView.swift
// SimpleX (iOS)
//
// Created by spaced4ndy on 12.04.2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import SwiftUI
import SimpleXChat
struct ChatItemForwardingView: View {
@EnvironmentObject var chatModel: ChatModel
@Environment(\.dismiss) var dismiss
var ci: ChatItem
var fromChatInfo: ChatInfo
@Binding var composeState: ComposeState
@State private var searchText: String = ""
@FocusState private var searchFocused
var body: some View {
NavigationView {
forwardListView()
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Cancel") {
dismiss()
}
}
ToolbarItem(placement: .principal) {
Text("Forward")
.bold()
}
}
}
}
@ViewBuilder private func forwardListView() -> some View {
VStack(alignment: .leading) {
let chatsToForwardTo = filterChatsToForwardTo()
if !chatsToForwardTo.isEmpty {
ScrollView {
LazyVStack(alignment: .leading, spacing: 8) {
searchFieldView(text: $searchText, focussed: $searchFocused)
.padding(.leading, 2)
let s = searchText.trimmingCharacters(in: .whitespaces).localizedLowercase
let chats = s == "" ? chatsToForwardTo : chatsToForwardTo.filter { filterChatSearched($0, s) }
ForEach(chats) { chat in
Divider()
forwardListNavLinkView(chat)
.disabled(chatModel.deletedChats.contains(chat.chatInfo.id))
}
}
.padding(.horizontal)
.padding(.vertical, 8)
.background(Color(uiColor: .systemBackground))
.cornerRadius(12)
.padding(.horizontal)
}
.background(Color(.systemGroupedBackground))
} else {
emptyList()
}
}
}
private func filterChatsToForwardTo() -> [Chat] {
var filteredChats = chatModel.chats.filter({ canForwardToChat($0) })
if let index = filteredChats.firstIndex(where: { $0.chatInfo.chatType == .local }) {
let privateNotes = filteredChats.remove(at: index)
filteredChats.insert(privateNotes, at: 0)
}
return filteredChats
}
private func filterChatSearched(_ chat: Chat, _ searchStr: String) -> Bool {
let cInfo = chat.chatInfo
return switch cInfo {
case let .direct(contact):
viewNameContains(cInfo, searchStr) ||
contact.profile.displayName.localizedLowercase.contains(searchStr) ||
contact.fullName.localizedLowercase.contains(searchStr)
default:
viewNameContains(cInfo, searchStr)
}
func viewNameContains(_ cInfo: ChatInfo, _ s: String) -> Bool {
cInfo.chatViewName.localizedLowercase.contains(s)
}
}
private func canForwardToChat(_ chat: Chat) -> Bool {
switch chat.chatInfo {
case let .direct(contact): contact.sendMsgEnabled && !contact.nextSendGrpInv
case let .group(groupInfo): groupInfo.sendMsgEnabled
case let .local(noteFolder): noteFolder.sendMsgEnabled
case .contactRequest: false
case .contactConnection: false
case .invalidJSON: false
}
}
private func emptyList() -> some View {
Text("No filtered chats")
.foregroundColor(.secondary)
.frame(maxWidth: .infinity)
}
@ViewBuilder private func forwardListNavLinkView(_ chat: Chat) -> some View {
Button {
dismiss()
if chat.id == fromChatInfo.id {
composeState = ComposeState(
message: composeState.message,
preview: composeState.linkPreview != nil ? composeState.preview : .noPreview,
contextItem: .forwardingItem(chatItem: ci, fromChatInfo: fromChatInfo)
)
} else {
composeState = ComposeState.init(forwardingItem: ci, fromChatInfo: fromChatInfo)
chatModel.chatId = chat.id
}
} label: {
HStack {
ChatInfoImage(chat: chat)
.frame(width: 30, height: 30)
.padding(.trailing, 2)
Text(chat.chatInfo.chatViewName)
.foregroundColor(.primary)
.lineLimit(1)
if chat.chatInfo.incognito {
Spacer()
Image(systemName: "theatermasks")
.resizable()
.scaledToFit()
.frame(width: 22, height: 22)
.foregroundColor(.secondary)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}
}
#Preview {
ChatItemForwardingView(
ci: ChatItem.getSample(1, .directSnd, .now, "hello"),
fromChatInfo: .direct(contact: Contact.sampleData),
composeState: Binding.constant(ComposeState(message: "hello"))
)
}
@@ -11,7 +11,6 @@ import SimpleXChat
struct ChatItemInfoView: View { struct ChatItemInfoView: View {
@EnvironmentObject var chatModel: ChatModel @EnvironmentObject var chatModel: ChatModel
@Environment(\.dismiss) var dismiss
@Environment(\.colorScheme) var colorScheme @Environment(\.colorScheme) var colorScheme
var ci: ChatItem var ci: ChatItem
@Binding var chatItemInfo: ChatItemInfo? @Binding var chatItemInfo: ChatItemInfo?
@@ -22,7 +21,6 @@ struct ChatItemInfoView: View {
enum CIInfoTab { enum CIInfoTab {
case history case history
case quote case quote
case forwarded
case delivery case delivery
} }
@@ -70,20 +68,9 @@ struct ChatItemInfoView: View {
if ci.quotedItem != nil { if ci.quotedItem != nil {
numTabs += 1 numTabs += 1
} }
if chatItemInfo?.forwardedFromChatItem != nil {
numTabs += 1
}
return numTabs return numTabs
} }
private var local: Bool {
switch ci.chatDir {
case .localSnd: true
case .localRcv: true
default: false
}
}
@ViewBuilder private func itemInfoView() -> some View { @ViewBuilder private func itemInfoView() -> some View {
if numTabs > 1 { if numTabs > 1 {
TabView(selection: $selection) { TabView(selection: $selection) {
@@ -106,13 +93,6 @@ struct ChatItemInfoView: View {
} }
.tag(CIInfoTab.quote) .tag(CIInfoTab.quote)
} }
if let forwardedFromItem = chatItemInfo?.forwardedFromChatItem {
forwardedFromTab(forwardedFromItem)
.tabItem {
Label(local ? "Saved" : "Forwarded", systemImage: "arrowshape.turn.up.forward")
}
.tag(CIInfoTab.forwarded)
}
} }
.onAppear { .onAppear {
if chatItemInfo?.memberDeliveryStatuses != nil { if chatItemInfo?.memberDeliveryStatuses != nil {
@@ -295,75 +275,6 @@ struct ChatItemInfoView: View {
: Color(uiColor: .tertiarySystemGroupedBackground) : Color(uiColor: .tertiarySystemGroupedBackground)
} }
@ViewBuilder private func forwardedFromTab(_ forwardedFromItem: AChatItem) -> some View {
ScrollView {
VStack(alignment: .leading, spacing: 16) {
details()
Divider().padding(.vertical)
Text(local ? "Saved from" : "Forwarded from")
.font(.title2)
.padding(.bottom, 4)
forwardedFromView(forwardedFromItem)
}
.padding()
}
.frame(maxHeight: .infinity, alignment: .top)
}
private func forwardedFromView(_ forwardedFromItem: AChatItem) -> some View {
VStack(alignment: .leading, spacing: 8) {
Button {
Task {
await MainActor.run {
chatModel.chatId = forwardedFromItem.chatInfo.id
dismiss()
}
}
} label: {
forwardedFromSender(forwardedFromItem)
}
if !local {
Divider().padding(.top, 32)
Text("Recipient(s) can't see who this message is from.")
.font(.caption)
.foregroundColor(.secondary)
}
}
}
@ViewBuilder private func forwardedFromSender(_ forwardedFromItem: AChatItem) -> some View {
HStack {
ChatInfoImage(chat: Chat(chatInfo: forwardedFromItem.chatInfo))
.frame(width: 48, height: 48)
.padding(.trailing, 6)
if forwardedFromItem.chatItem.chatDir.sent {
VStack(alignment: .leading) {
Text("you")
.italic()
.foregroundColor(.primary)
Text(forwardedFromItem.chatInfo.chatViewName)
.foregroundColor(.secondary)
.lineLimit(1)
}
} else if case let .groupRcv(groupMember) = forwardedFromItem.chatItem.chatDir {
VStack(alignment: .leading) {
Text(groupMember.chatViewName)
.foregroundColor(.primary)
.lineLimit(1)
Text(forwardedFromItem.chatInfo.chatViewName)
.foregroundColor(.secondary)
.lineLimit(1)
}
} else {
Text(forwardedFromItem.chatInfo.chatViewName)
.foregroundColor(.primary)
.lineLimit(1)
}
}
}
@ViewBuilder private func deliveryTab(_ memberDeliveryStatuses: [MemberDeliveryStatus]) -> some View { @ViewBuilder private func deliveryTab(_ memberDeliveryStatuses: [MemberDeliveryStatus]) -> some View {
ScrollView { ScrollView {
VStack(alignment: .leading, spacing: 16) { VStack(alignment: .leading, spacing: 16) {
@@ -46,7 +46,7 @@ struct ChatItemView: View {
let ci = chatItem let ci = chatItem
if chatItem.meta.itemDeleted != nil && (!revealed || chatItem.isDeletedContent) { if chatItem.meta.itemDeleted != nil && (!revealed || chatItem.isDeletedContent) {
MarkedDeletedItemView(chat: chat, chatItem: chatItem, revealed: $revealed) MarkedDeletedItemView(chat: chat, chatItem: chatItem, revealed: $revealed)
} else if ci.quotedItem == nil && ci.meta.itemForwarded == nil && ci.meta.itemDeleted == nil && !ci.meta.isLive { } else if ci.quotedItem == nil && ci.meta.itemDeleted == nil && !ci.meta.isLive {
if let mc = ci.content.msgContent, mc.isText && isShortEmoji(ci.content.text) { if let mc = ci.content.msgContent, mc.isText && isShortEmoji(ci.content.text) {
EmojiItemView(chat: chat, chatItem: ci) EmojiItemView(chat: chat, chatItem: ci)
} else if ci.content.text.isEmpty, case let .voice(_, duration) = ci.content.msgContent { } else if ci.content.text.isEmpty, case let .voice(_, duration) = ci.content.msgContent {
@@ -102,9 +102,9 @@ struct ChatItemContentView<Content: View>: View {
case let .rcvChatPreference(feature, allowed, param): case let .rcvChatPreference(feature, allowed, param):
CIFeaturePreferenceView(chat: chat, chatItem: chatItem, feature: feature, allowed: allowed, param: param) CIFeaturePreferenceView(chat: chat, chatItem: chatItem, feature: feature, allowed: allowed, param: param)
case let .sndChatPreference(feature, _, _): case let .sndChatPreference(feature, _, _):
CIChatFeatureView(chat: chat, chatItem: chatItem, revealed: $revealed, feature: feature, icon: feature.icon, iconColor: .secondary) CIChatFeatureView(chatItem: chatItem, revealed: $revealed, feature: feature, icon: feature.icon, iconColor: .secondary)
case let .rcvGroupFeature(feature, preference, _, role): chatFeatureView(feature, preference.enabled(role, for: chat.chatInfo.groupInfo?.membership).iconColor) case let .rcvGroupFeature(feature, preference, _): chatFeatureView(feature, preference.enable.iconColor)
case let .sndGroupFeature(feature, preference, _, role): chatFeatureView(feature, preference.enabled(role, for: chat.chatInfo.groupInfo?.membership).iconColor) case let .sndGroupFeature(feature, preference, _): chatFeatureView(feature, preference.enable.iconColor)
case let .rcvChatFeatureRejected(feature): chatFeatureView(feature, .red) case let .rcvChatFeatureRejected(feature): chatFeatureView(feature, .red)
case let .rcvGroupFeatureRejected(feature): chatFeatureView(feature, .red) case let .rcvGroupFeatureRejected(feature): chatFeatureView(feature, .red)
case .sndModerated: deletedItemView() case .sndModerated: deletedItemView()
@@ -149,7 +149,7 @@ struct ChatItemContentView<Content: View>: View {
} }
private func chatFeatureView(_ feature: Feature, _ iconColor: Color) -> some View { private func chatFeatureView(_ feature: Feature, _ iconColor: Color) -> some View {
CIChatFeatureView(chat: chat, chatItem: chatItem, revealed: $revealed, feature: feature, iconColor: iconColor) CIChatFeatureView(chatItem: chatItem, revealed: $revealed, feature: feature, iconColor: iconColor)
} }
private var mergedGroupEventText: Text? { private var mergedGroupEventText: Text? {
+17 -65
View File
@@ -83,11 +83,7 @@ struct ChatView: View {
initChatView() initChatView()
} }
.onChange(of: chatModel.chatId) { cId in .onChange(of: chatModel.chatId) { cId in
showChatInfoSheet = false if cId != nil {
if let cId {
if let c = chatModel.getChat(cId) {
chat = c
}
initChatView() initChatView()
} else { } else {
dismiss() dismiss()
@@ -183,7 +179,7 @@ struct ChatView: View {
.disabled(!contact.ready || !contact.active) .disabled(!contact.ready || !contact.active)
} }
searchButton() searchButton()
ToggleNtfsButton(chat: chat) toggleNtfsButton(chat)
.disabled(!contact.ready || !contact.active) .disabled(!contact.ready || !contact.active)
} label: { } label: {
Image(systemName: "ellipsis") Image(systemName: "ellipsis")
@@ -212,7 +208,7 @@ struct ChatView: View {
} }
Menu { Menu {
searchButton() searchButton()
ToggleNtfsButton(chat: chat) toggleNtfsButton(chat)
} label: { } label: {
Image(systemName: "ellipsis") Image(systemName: "ellipsis")
} }
@@ -255,8 +251,7 @@ struct ChatView: View {
} }
} }
} }
if chatModel.draftChatId == cInfo.id && !composeState.forwarding, if chatModel.draftChatId == cInfo.id, let draft = chatModel.draft {
let draft = chatModel.draft {
composeState = draft composeState = draft
} }
if chat.chatStats.unreadChat { if chat.chatStats.unreadChat {
@@ -301,7 +296,7 @@ struct ChatView: View {
} }
private func voiceWithoutFrame(_ ci: ChatItem) -> Bool { private func voiceWithoutFrame(_ ci: ChatItem) -> Bool {
ci.content.msgContent?.isVoice == true && ci.content.text.count == 0 && ci.quotedItem == nil && ci.meta.itemForwarded == nil ci.content.msgContent?.isVoice == true && ci.content.text.count == 0 && ci.quotedItem == nil
} }
private func chatItemsList() -> some View { private func chatItemsList() -> some View {
@@ -347,8 +342,8 @@ struct ChatView: View {
.onChange(of: searchText) { _ in .onChange(of: searchText) { _ in
loadChat(chat: chat, search: searchText) loadChat(chat: chat, search: searchText)
} }
.onChange(of: chatModel.chatId) { chatId in .onChange(of: chatModel.chatId) { _ in
if let chatId, let c = chatModel.getChat(chatId) { if let chatId = chatModel.chatId, let c = chatModel.getChat(chatId) {
chat = c chat = c
showChatInfoSheet = false showChatInfoSheet = false
loadChat(chat: c) loadChat(chat: c)
@@ -544,7 +539,6 @@ struct ChatView: View {
@State private var revealed = false @State private var revealed = false
@State private var showChatItemInfoSheet: Bool = false @State private var showChatItemInfoSheet: Bool = false
@State private var chatItemInfo: ChatItemInfo? @State private var chatItemInfo: ChatItemInfo?
@State private var showForwardingSheet: Bool = false
@State private var allowMenu: Bool = true @State private var allowMenu: Bool = true
@@ -673,7 +667,7 @@ struct ChatView: View {
Button("Delete for me", role: .destructive) { Button("Delete for me", role: .destructive) {
deleteMessage(.cidmInternal) deleteMessage(.cidmInternal)
} }
if let di = deletingItem, di.meta.deletable && !di.localNote { if let di = deletingItem, di.meta.editable && !di.localNote {
Button(broadcastDeleteButtonText, role: .destructive) { Button(broadcastDeleteButtonText, role: .destructive) {
deleteMessage(.cidmBroadcast) deleteMessage(.cidmBroadcast)
} }
@@ -699,14 +693,6 @@ struct ChatView: View {
}) { }) {
ChatItemInfoView(ci: ci, chatItemInfo: $chatItemInfo) ChatItemInfoView(ci: ci, chatItemInfo: $chatItemInfo)
} }
.sheet(isPresented: $showForwardingSheet) {
if #available(iOS 16.0, *) {
ChatItemForwardingView(ci: ci, fromChatInfo: chat.chatInfo, composeState: $composeState)
.presentationDetents([.fraction(0.8)])
} else {
ChatItemForwardingView(ci: ci, fromChatInfo: chat.chatInfo, composeState: $composeState)
}
}
} }
private func showMemberImage(_ member: GroupMember, _ prevItem: ChatItem?) -> Bool { private func showMemberImage(_ member: GroupMember, _ prevItem: ChatItem?) -> Bool {
@@ -785,17 +771,10 @@ struct ChatView: View {
} else { } else {
menu.append(saveFileAction(fileSource)) menu.append(saveFileAction(fileSource))
} }
} else if let file = ci.file, case .rcvInvitation = file.fileStatus, fileSizeValid(file) {
menu.append(downloadFileAction(file))
} }
if ci.meta.editable && !mc.isVoice && !live { if ci.meta.editable && !mc.isVoice && !live {
menu.append(editAction(ci)) menu.append(editAction(ci))
} }
if ci.meta.itemDeleted == nil
&& (ci.file == nil || (fileSource != nil && fileExists))
&& !ci.isLiveDummy && !live {
menu.append(forwardUIAction(ci))
}
if !ci.isLiveDummy { if !ci.isLiveDummy {
menu.append(viewInfoUIAction(ci)) menu.append(viewInfoUIAction(ci))
} }
@@ -847,15 +826,6 @@ struct ChatView: View {
} }
} }
private func forwardUIAction(_ ci: ChatItem) -> UIAction {
UIAction(
title: NSLocalizedString("Forward", comment: "chat item action"),
image: UIImage(systemName: "arrowshape.turn.up.forward")
) { _ in
showForwardingSheet = true
}
}
private func reactionUIMenuPreiOS16(_ rs: [UIAction]) -> UIMenu { private func reactionUIMenuPreiOS16(_ rs: [UIAction]) -> UIMenu {
UIMenu( UIMenu(
title: NSLocalizedString("React…", comment: "chat item menu"), title: NSLocalizedString("React…", comment: "chat item menu"),
@@ -953,21 +923,7 @@ struct ChatView: View {
saveCryptoFile(fileSource) saveCryptoFile(fileSource)
} }
} }
private func downloadFileAction(_ file: CIFile) -> UIAction {
UIAction(
title: NSLocalizedString("Download", comment: "chat item action"),
image: UIImage(systemName: "arrow.down.doc")
) { _ in
Task {
logger.debug("ChatView downloadFileAction, in Task")
if let user = m.currentUser {
await receiveFile(user: user, fileId: file.fileId)
}
}
}
}
private func editAction(_ ci: ChatItem) -> UIAction { private func editAction(_ ci: ChatItem) -> UIAction {
UIAction( UIAction(
title: NSLocalizedString("Edit", comment: "chat item action"), title: NSLocalizedString("Edit", comment: "chat item action"),
@@ -1216,18 +1172,14 @@ struct ChatView: View {
} }
} }
struct ToggleNtfsButton: View { @ViewBuilder func toggleNtfsButton(_ chat: Chat) -> some View {
@ObservedObject var chat: Chat Button {
toggleNotifications(chat, enableNtfs: !chat.chatInfo.ntfsEnabled)
var body: some View { } label: {
Button { if chat.chatInfo.ntfsEnabled {
toggleNotifications(chat, enableNtfs: !chat.chatInfo.ntfsEnabled) Label("Mute", systemImage: "speaker.slash")
} label: { } else {
if chat.chatInfo.ntfsEnabled { Label("Unmute", systemImage: "speaker.wave.2")
Label("Mute", systemImage: "speaker.slash")
} else {
Label("Unmute", systemImage: "speaker.wave.2")
}
} }
} }
} }
@@ -23,7 +23,6 @@ enum ComposeContextItem {
case noContextItem case noContextItem
case quotedItem(chatItem: ChatItem) case quotedItem(chatItem: ChatItem)
case editingItem(chatItem: ChatItem) case editingItem(chatItem: ChatItem)
case forwardingItem(chatItem: ChatItem, fromChatInfo: ChatInfo)
} }
enum VoiceMessageRecordingState { enum VoiceMessageRecordingState {
@@ -73,13 +72,6 @@ struct ComposeState {
} }
} }
init(forwardingItem: ChatItem, fromChatInfo: ChatInfo) {
self.message = ""
self.preview = .noPreview
self.contextItem = .forwardingItem(chatItem: forwardingItem, fromChatInfo: fromChatInfo)
self.voiceMessageRecordingState = .noRecording
}
func copy( func copy(
message: String? = nil, message: String? = nil,
liveMessage: LiveMessage? = nil, liveMessage: LiveMessage? = nil,
@@ -110,19 +102,12 @@ struct ComposeState {
} }
} }
var forwarding: Bool {
switch contextItem {
case .forwardingItem: return true
default: return false
}
}
var sendEnabled: Bool { var sendEnabled: Bool {
switch preview { switch preview {
case let .mediaPreviews(media): return !media.isEmpty case let .mediaPreviews(media): return !media.isEmpty
case .voicePreview: return voiceMessageRecordingState == .finished case .voicePreview: return voiceMessageRecordingState == .finished
case .filePreview: return true case .filePreview: return true
default: return !message.isEmpty || forwarding || liveMessage != nil default: return !message.isEmpty || liveMessage != nil
} }
} }
@@ -168,7 +153,7 @@ struct ComposeState {
} }
var attachmentDisabled: Bool { var attachmentDisabled: Bool {
if editing || forwarding || liveMessage != nil || inProgress { return true } if editing || liveMessage != nil || inProgress { return true }
switch preview { switch preview {
case .noPreview: return false case .noPreview: return false
case .linkPreview: return false case .linkPreview: return false
@@ -176,16 +161,6 @@ struct ComposeState {
} }
} }
var attachmentPreview: Bool {
switch preview {
case .noPreview: false
case .linkPreview: false
case let .mediaPreviews(mediaPreviews): !mediaPreviews.isEmpty
case .voicePreview: false
case .filePreview: true
}
}
var empty: Bool { var empty: Bool {
message == "" && noPreview message == "" && noPreview
} }
@@ -259,7 +234,6 @@ struct ComposeView: View {
@Binding var keyboardVisible: Bool @Binding var keyboardVisible: Bool
@State var linkUrl: URL? = nil @State var linkUrl: URL? = nil
@State var hasSimplexLink: Bool = false
@State var prevLinkUrl: URL? = nil @State var prevLinkUrl: URL? = nil
@State var pendingLinkUrl: URL? = nil @State var pendingLinkUrl: URL? = nil
@State var cancelledLinks: Set<String> = [] @State var cancelledLinks: Set<String> = []
@@ -286,16 +260,6 @@ struct ComposeView: View {
if chat.chatInfo.contact?.nextSendGrpInv ?? false { if chat.chatInfo.contact?.nextSendGrpInv ?? false {
ContextInvitingContactMemberView() ContextInvitingContactMemberView()
} }
let simplexLinkProhibited = hasSimplexLink && !chat.groupFeatureEnabled(.simplexLinks)
let fileProhibited = composeState.attachmentPreview && !chat.groupFeatureEnabled(.files)
let voiceProhibited = composeState.voicePreview && !chat.chatInfo.featureEnabled(.voice)
if simplexLinkProhibited {
msgNotAllowedView("SimpleX links not allowed", icon: "link")
} else if fileProhibited {
msgNotAllowedView("Files and media not allowed", icon: "doc")
} else if voiceProhibited {
msgNotAllowedView("Voice messages not allowed", icon: "mic")
}
contextItemView() contextItemView()
switch (composeState.editing, composeState.preview) { switch (composeState.editing, composeState.preview) {
case (true, .filePreview): EmptyView() case (true, .filePreview): EmptyView()
@@ -314,7 +278,7 @@ struct ComposeView: View {
.padding(.bottom, 12) .padding(.bottom, 12)
.padding(.leading, 12) .padding(.leading, 12)
if case let .group(g) = chat.chatInfo, if case let .group(g) = chat.chatInfo,
!g.fullGroupPreferences.files.on(for: g.membership) { !g.fullGroupPreferences.files.on {
b.disabled(true).onTapGesture { b.disabled(true).onTapGesture {
AlertManager.shared.showAlertMsg( AlertManager.shared.showAlertMsg(
title: "Files and media prohibited!", title: "Files and media prohibited!",
@@ -339,7 +303,6 @@ struct ComposeView: View {
}, },
nextSendGrpInv: chat.chatInfo.contact?.nextSendGrpInv ?? false, nextSendGrpInv: chat.chatInfo.contact?.nextSendGrpInv ?? false,
voiceMessageAllowed: chat.chatInfo.featureEnabled(.voice), voiceMessageAllowed: chat.chatInfo.featureEnabled(.voice),
disableSendButton: simplexLinkProhibited || fileProhibited || voiceProhibited,
showEnableVoiceMessagesAlert: chat.chatInfo.showEnableVoiceMessagesAlert, showEnableVoiceMessagesAlert: chat.chatInfo.showEnableVoiceMessagesAlert,
startVoiceMessageRecording: { startVoiceMessageRecording: {
Task { Task {
@@ -374,18 +337,13 @@ struct ComposeView: View {
} }
} }
} }
.onChange(of: composeState.message) { msg in .onChange(of: composeState.message) { _ in
if composeState.linkPreviewAllowed { if composeState.linkPreviewAllowed {
if msg.count > 0 { if composeState.message.count > 0 {
showLinkPreview(msg) showLinkPreview(composeState.message)
} else { } else {
resetLinkPreview() resetLinkPreview()
hasSimplexLink = false
} }
} else if msg.count > 0 && !chat.groupFeatureEnabled(.simplexLinks) {
(_, hasSimplexLink) = parseMessage(msg)
} else {
hasSimplexLink = false
} }
} }
.onChange(of: chat.userCanSend) { canSend in .onChange(of: chat.userCanSend) { canSend in
@@ -652,18 +610,6 @@ struct ComposeView: View {
} }
} }
private func msgNotAllowedView(_ reason: LocalizedStringKey, icon: String) -> some View {
HStack {
Image(systemName: icon).foregroundColor(.secondary)
Text(reason).italic()
}
.padding(12)
.frame(minHeight: 50)
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(uiColor: .tertiarySystemGroupedBackground))
.padding(.top, 8)
}
@ViewBuilder private func contextItemView() -> some View { @ViewBuilder private func contextItemView() -> some View {
switch composeState.contextItem { switch composeState.contextItem {
case .noContextItem: case .noContextItem:
@@ -682,14 +628,6 @@ struct ComposeView: View {
contextIcon: "pencil", contextIcon: "pencil",
cancelContextItem: { clearState() } cancelContextItem: { clearState() }
) )
case let .forwardingItem(chatItem: forwardedItem, _):
ContextItemView(
chat: chat,
contextItem: forwardedItem,
contextIcon: "arrowshape.turn.up.forward",
cancelContextItem: { composeState = composeState.copy(contextItem: .noContextItem) },
showSender: false
)
} }
} }
@@ -711,11 +649,6 @@ struct ComposeView: View {
} }
if chat.chatInfo.contact?.nextSendGrpInv ?? false { if chat.chatInfo.contact?.nextSendGrpInv ?? false {
await sendMemberContactInvitation() await sendMemberContactInvitation()
} else if case let .forwardingItem(ci, fromChatInfo) = composeState.contextItem {
sent = await forwardItem(ci, fromChatInfo)
if !composeState.message.isEmpty {
sent = await send(checkLinkPreview(), quoted: sent?.id, live: false, ttl: nil)
}
} else if case let .editingItem(ci) = composeState.contextItem { } else if case let .editingItem(ci) = composeState.contextItem {
sent = await updateMessage(ci, live: live) sent = await updateMessage(ci, live: live)
} else if let liveMessage = liveMessage, liveMessage.sentMsg != nil { } else if let liveMessage = liveMessage, liveMessage.sentMsg != nil {
@@ -761,15 +694,7 @@ struct ComposeView: View {
} }
} }
} }
await MainActor.run { await MainActor.run { clearState(live: live) }
let wasForwarding = composeState.forwarding
clearState(live: live)
if wasForwarding,
chatModel.draftChatId == chat.chatInfo.id,
let draft = chatModel.draft {
composeState = draft
}
}
return sent return sent
func sending() async { func sending() async {
@@ -890,26 +815,10 @@ struct ComposeView: View {
return nil return nil
} }
func forwardItem(_ forwardedItem: ChatItem, _ fromChatInfo: ChatInfo) async -> ChatItem? {
if let chatItem = await apiForwardChatItem(
toChatType: chat.chatInfo.chatType,
toChatId: chat.chatInfo.apiId,
fromChatType: fromChatInfo.chatType,
fromChatId: fromChatInfo.apiId,
itemId: forwardedItem.id
) {
await MainActor.run {
chatModel.addChatItem(chat.chatInfo, chatItem)
}
return chatItem
}
return nil
}
func checkLinkPreview() -> MsgContent { func checkLinkPreview() -> MsgContent {
switch (composeState.preview) { switch (composeState.preview) {
case let .linkPreview(linkPreview: linkPreview): case let .linkPreview(linkPreview: linkPreview):
if let url = parseMessage(msgText).url, if let url = parseMessage(msgText),
let linkPreview = linkPreview, let linkPreview = linkPreview,
url == linkPreview.uri { url == linkPreview.uri {
return .link(text: msgText, preview: linkPreview) return .link(text: msgText, preview: linkPreview)
@@ -1038,7 +947,7 @@ struct ComposeView: View {
private func showLinkPreview(_ s: String) { private func showLinkPreview(_ s: String) {
prevLinkUrl = linkUrl prevLinkUrl = linkUrl
(linkUrl, hasSimplexLink) = parseMessage(s) linkUrl = parseMessage(s)
if let url = linkUrl { if let url = linkUrl {
if url != composeState.linkPreview?.uri && url != pendingLinkUrl { if url != composeState.linkPreview?.uri && url != pendingLinkUrl {
pendingLinkUrl = url pendingLinkUrl = url
@@ -1055,17 +964,13 @@ struct ComposeView: View {
} }
} }
private func parseMessage(_ msg: String) -> (url: URL?, hasSimplexLink: Bool) { private func parseMessage(_ msg: String) -> URL? {
guard let parsedMsg = parseSimpleXMarkdown(msg) else { return (nil, false) } let parsedMsg = parseSimpleXMarkdown(msg)
let url: URL? = if let uri = parsedMsg.first(where: { ft in let uri = parsedMsg?.first(where: { ft in
ft.format == .uri && !cancelledLinks.contains(ft.text) && !isSimplexLink(ft.text) ft.format == .uri && !cancelledLinks.contains(ft.text) && !isSimplexLink(ft.text)
}) { })
URL(string: uri.text) if let uri = uri { return URL(string: uri.text) }
} else { else { return nil }
nil
}
let simplexLink = parsedMsg.contains(where: { ft in ft.format?.isSimplexLink ?? false })
return (url, simplexLink)
} }
private func isSimplexLink(_ link: String) -> Bool { private func isSimplexLink(_ link: String) -> Bool {
@@ -15,7 +15,6 @@ struct ContextItemView: View {
let contextItem: ChatItem let contextItem: ChatItem
let contextIcon: String let contextIcon: String
let cancelContextItem: () -> Void let cancelContextItem: () -> Void
var showSender: Bool = true
var body: some View { var body: some View {
HStack { HStack {
@@ -24,7 +23,7 @@ struct ContextItemView: View {
.aspectRatio(contentMode: .fit) .aspectRatio(contentMode: .fit)
.frame(width: 16, height: 16) .frame(width: 16, height: 16)
.foregroundColor(.secondary) .foregroundColor(.secondary)
if showSender, let sender = contextItem.memberDisplayName { if let sender = contextItem.memberDisplayName {
VStack(alignment: .leading, spacing: 4) { VStack(alignment: .leading, spacing: 4) {
Text(sender).font(.caption).foregroundColor(.secondary) Text(sender).font(.caption).foregroundColor(.secondary)
msgContentView(lines: 2) msgContentView(lines: 2)
@@ -49,26 +48,14 @@ struct ContextItemView: View {
} }
private func msgContentView(lines: Int) -> some View { private func msgContentView(lines: Int) -> some View {
contextMsgPreview() MsgContentView(
.multilineTextAlignment(isRightToLeft(contextItem.text) ? .trailing : .leading) chat: chat,
.lineLimit(lines) text: contextItem.text,
} formattedText: contextItem.formattedText,
showSecrets: false
private func contextMsgPreview() -> Text { )
return attachment() + messageText(contextItem.text, contextItem.formattedText, nil, preview: true, showSecrets: false) .multilineTextAlignment(isRightToLeft(contextItem.text) ? .trailing : .leading)
.lineLimit(lines)
func attachment() -> Text {
switch contextItem.content.msgContent {
case .file: return image("doc.fill")
case .image: return image("photo")
case .voice: return image("play.fill")
default: return Text("")
}
}
func image(_ s: String) -> Text {
Text(Image(systemName: s)).foregroundColor(Color(uiColor: .tertiaryLabel)) + Text(" ")
}
} }
} }
@@ -16,6 +16,7 @@ struct NativeTextEditor: UIViewRepresentable {
@Binding var disableEditing: Bool @Binding var disableEditing: Bool
@Binding var height: CGFloat @Binding var height: CGFloat
@Binding var focused: Bool @Binding var focused: Bool
let alignment: TextAlignment
let onImagesAdded: ([UploadContent]) -> Void let onImagesAdded: ([UploadContent]) -> Void
private let minHeight: CGFloat = 37 private let minHeight: CGFloat = 37
@@ -29,16 +30,13 @@ struct NativeTextEditor: UIViewRepresentable {
func makeUIView(context: Context) -> UITextView { func makeUIView(context: Context) -> UITextView {
let field = CustomUITextField(height: _height) let field = CustomUITextField(height: _height)
field.text = text field.text = text
field.textAlignment = alignment(text) field.textAlignment = alignment == .leading ? .left : .right
field.autocapitalizationType = .sentences field.autocapitalizationType = .sentences
field.setOnTextChangedListener { newText, images in field.setOnTextChangedListener { newText, images in
if !disableEditing { if !disableEditing {
text = newText
field.textAlignment = alignment(text)
updateFont(field)
// Speed up the process of updating layout, reduce jumping content on screen // Speed up the process of updating layout, reduce jumping content on screen
updateHeight(field) if !isShortEmoji(newText) { updateHeight(field) }
self.height = field.frame.size.height text = newText
} else { } else {
field.text = text field.text = text
} }
@@ -55,12 +53,10 @@ struct NativeTextEditor: UIViewRepresentable {
} }
func updateUIView(_ field: UITextView, context: Context) { func updateUIView(_ field: UITextView, context: Context) {
if field.markedTextRange == nil && field.text != text { field.text = text
field.text = text field.textAlignment = alignment == .leading ? .left : .right
field.textAlignment = alignment(text) updateFont(field)
updateFont(field) updateHeight(field)
updateHeight(field)
}
} }
private func updateHeight(_ field: UITextView) { private func updateHeight(_ field: UITextView) {
@@ -77,19 +73,12 @@ struct NativeTextEditor: UIViewRepresentable {
} }
private func updateFont(_ field: UITextView) { private func updateFont(_ field: UITextView) {
let newFont = isShortEmoji(field.text) field.font = isShortEmoji(field.text)
? (field.text.count < 4 ? largeEmojiUIFont : mediumEmojiUIFont) ? (field.text.count < 4 ? largeEmojiUIFont : mediumEmojiUIFont)
: UIFont.preferredFont(forTextStyle: .body) : UIFont.preferredFont(forTextStyle: .body)
if field.font != newFont {
field.font = newFont
}
} }
} }
private func alignment(_ text: String) -> NSTextAlignment {
isRightToLeft(text) ? .right : .left
}
private class CustomUITextField: UITextView, UITextViewDelegate { private class CustomUITextField: UITextView, UITextViewDelegate {
var height: Binding<CGFloat> var height: Binding<CGFloat>
var newHeight: CGFloat = 0 var newHeight: CGFloat = 0
@@ -216,6 +205,7 @@ struct NativeTextEditor_Previews: PreviewProvider{
disableEditing: Binding.constant(false), disableEditing: Binding.constant(false),
height: Binding.constant(100), height: Binding.constant(100),
focused: Binding.constant(false), focused: Binding.constant(false),
alignment: TextAlignment.leading,
onImagesAdded: { _ in } onImagesAdded: { _ in }
) )
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
@@ -20,7 +20,6 @@ struct SendMessageView: View {
var nextSendGrpInv: Bool = false var nextSendGrpInv: Bool = false
var showVoiceMessageButton: Bool = true var showVoiceMessageButton: Bool = true
var voiceMessageAllowed: Bool = true var voiceMessageAllowed: Bool = true
var disableSendButton = false
var showEnableVoiceMessagesAlert: ChatInfo.ShowEnableVoiceMessagesAlert = .other var showEnableVoiceMessagesAlert: ChatInfo.ShowEnableVoiceMessagesAlert = .other
var startVoiceMessageRecording: (() -> Void)? = nil var startVoiceMessageRecording: (() -> Void)? = nil
var finishVoiceMessageRecording: (() -> Void)? = nil var finishVoiceMessageRecording: (() -> Void)? = nil
@@ -54,11 +53,13 @@ struct SendMessageView: View {
.padding(.vertical, 8) .padding(.vertical, 8)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
} else { } else {
let alignment: TextAlignment = isRightToLeft(composeState.message) ? .trailing : .leading
NativeTextEditor( NativeTextEditor(
text: $composeState.message, text: $composeState.message,
disableEditing: $composeState.inProgress, disableEditing: $composeState.inProgress,
height: $teHeight, height: $teHeight,
focused: $keyboardVisible, focused: $keyboardVisible,
alignment: alignment,
onImagesAdded: onMediaAdded onImagesAdded: onMediaAdded
) )
.allowsTightening(false) .allowsTightening(false)
@@ -108,7 +109,6 @@ struct SendMessageView: View {
} else if showVoiceMessageButton } else if showVoiceMessageButton
&& composeState.message.isEmpty && composeState.message.isEmpty
&& !composeState.editing && !composeState.editing
&& !composeState.forwarding
&& composeState.liveMessage == nil && composeState.liveMessage == nil
&& ((composeState.noPreview && vmrs == .noRecording) && ((composeState.noPreview && vmrs == .noRecording)
|| (vmrs == .recording && holdingVMR)) { || (vmrs == .recording && holdingVMR)) {
@@ -184,8 +184,7 @@ struct SendMessageView: View {
!composeState.sendEnabled || !composeState.sendEnabled ||
composeState.inProgress || composeState.inProgress ||
(!voiceMessageAllowed && composeState.voicePreview) || (!voiceMessageAllowed && composeState.voicePreview) ||
composeState.endLiveDisabled || composeState.endLiveDisabled
disableSendButton
) )
.frame(width: 29, height: 29) .frame(width: 29, height: 29)
.contextMenu{ .contextMenu{
@@ -224,8 +223,7 @@ struct SendMessageView: View {
@ViewBuilder private func sendButtonContextMenuItems() -> some View { @ViewBuilder private func sendButtonContextMenuItems() -> some View {
if composeState.liveMessage == nil, if composeState.liveMessage == nil,
!composeState.editing, !composeState.editing {
!composeState.forwarding {
if case .noContextItem = composeState.contextItem, if case .noContextItem = composeState.contextItem,
!composeState.voicePreview, !composeState.voicePreview,
let send = sendLiveMessage, let send = sendLiveMessage,
@@ -62,7 +62,7 @@ struct GroupChatInfoView: View {
NavigationView { NavigationView {
let members = chatModel.groupMembers let members = chatModel.groupMembers
.filter { m in let status = m.wrapped.memberStatus; return status != .memLeft && status != .memRemoved } .filter { m in let status = m.wrapped.memberStatus; return status != .memLeft && status != .memRemoved }
.sorted { $0.wrapped.memberRole > $1.wrapped.memberRole } .sorted { $0.displayName.lowercased() < $1.displayName.lowercased() }
List { List {
groupInfoHeader() groupInfoHeader()
@@ -83,7 +83,7 @@ struct GroupMemberInfoView: View {
Section { Section {
if let contactId = member.memberContactId, let chat = knownDirectChat(contactId) { if let contactId = member.memberContactId, let chat = knownDirectChat(contactId) {
knownDirectChatButton(chat) knownDirectChatButton(chat)
} else if groupInfo.fullGroupPreferences.directMessages.on(for: groupInfo.membership) { } else if groupInfo.fullGroupPreferences.directMessages.on {
if let contactId = member.memberContactId { if let contactId = member.memberContactId {
newDirectChatButton(contactId) newDirectChatButton(contactId)
} else if member.activeConn?.peerChatVRange.isCompatibleRange(CREATE_MEMBER_CONTACT_VRANGE) ?? false { } else if member.activeConn?.peerChatVRange.isCompatibleRange(CREATE_MEMBER_CONTACT_VRANGE) ?? false {
@@ -110,7 +110,7 @@ struct GroupMemberInfoView: View {
Label("Share address", systemImage: "square.and.arrow.up") Label("Share address", systemImage: "square.and.arrow.up")
} }
if let contactId = member.memberContactId { if let contactId = member.memberContactId {
if knownDirectChat(contactId) == nil && !groupInfo.fullGroupPreferences.directMessages.on(for: groupInfo.membership) { if knownDirectChat(contactId) == nil && !groupInfo.fullGroupPreferences.directMessages.on {
connectViaAddressButton(contactLink) connectViaAddressButton(contactLink)
} }
} else { } else {
@@ -9,12 +9,6 @@
import SwiftUI import SwiftUI
import SimpleXChat import SimpleXChat
private let featureRoles: [(role: GroupMemberRole?, text: LocalizedStringKey)] = [
(nil, "all members"),
(.admin, "admins"),
(.owner, "owners")
]
struct GroupPreferencesView: View { struct GroupPreferencesView: View {
@Environment(\.dismiss) var dismiss: DismissAction @Environment(\.dismiss) var dismiss: DismissAction
@EnvironmentObject var chatModel: ChatModel @EnvironmentObject var chatModel: ChatModel
@@ -30,12 +24,10 @@ struct GroupPreferencesView: View {
List { List {
featureSection(.timedMessages, $preferences.timedMessages.enable) featureSection(.timedMessages, $preferences.timedMessages.enable)
featureSection(.fullDelete, $preferences.fullDelete.enable) featureSection(.fullDelete, $preferences.fullDelete.enable)
featureSection(.directMessages, $preferences.directMessages.enable, $preferences.directMessages.role) featureSection(.directMessages, $preferences.directMessages.enable)
featureSection(.reactions, $preferences.reactions.enable) featureSection(.reactions, $preferences.reactions.enable)
featureSection(.voice, $preferences.voice.enable, $preferences.voice.role) featureSection(.voice, $preferences.voice.enable)
featureSection(.files, $preferences.files.enable, $preferences.files.role) featureSection(.files, $preferences.files.enable)
// TODO enable simplexLinks preference in 5.8
// featureSection(.simplexLinks, $preferences.simplexLinks.enable, $preferences.simplexLinks.role)
featureSection(.history, $preferences.history.enable) featureSection(.history, $preferences.history.enable)
if groupInfo.canEdit { if groupInfo.canEdit {
@@ -72,7 +64,7 @@ struct GroupPreferencesView: View {
} }
} }
private func featureSection(_ feature: GroupFeature, _ enableFeature: Binding<GroupFeatureEnabled>, _ enableForRole: Binding<GroupMemberRole?>? = nil) -> some View { private func featureSection(_ feature: GroupFeature, _ enableFeature: Binding<GroupFeatureEnabled>) -> some View {
Section { Section {
let color: Color = enableFeature.wrappedValue == .on ? .green : .secondary let color: Color = enableFeature.wrappedValue == .on ? .green : .secondary
let icon = enableFeature.wrappedValue == .on ? feature.iconFilled : feature.icon let icon = enableFeature.wrappedValue == .on ? feature.iconFilled : feature.icon
@@ -95,16 +87,6 @@ struct GroupPreferencesView: View {
) )
.frame(height: 36) .frame(height: 36)
} }
if enableFeature.wrappedValue == .on, let enableForRole {
Picker("Enabled for", selection: enableForRole) {
ForEach(featureRoles, id: \.role) { fr in
Text(fr.text)
}
}
.frame(height: 36)
// remove in v5.8
.disabled(true)
}
} else { } else {
settingsRow(icon, color: color) { settingsRow(icon, color: color) {
infoRow(Text(feature.text), enableFeature.wrappedValue.text) infoRow(Text(feature.text), enableFeature.wrappedValue.text)
@@ -112,26 +94,10 @@ struct GroupPreferencesView: View {
if timedOn { if timedOn {
infoRow("Delete after", timeText(preferences.timedMessages.ttl)) infoRow("Delete after", timeText(preferences.timedMessages.ttl))
} }
if enableFeature.wrappedValue == .on, let enableForRole {
HStack {
Text("Enabled for").foregroundColor(.secondary)
Spacer()
Text(
featureRoles.first(where: { fr in fr.role == enableForRole.wrappedValue })?.text
?? "all members"
)
.foregroundColor(.secondary)
}
}
} }
} footer: { } footer: {
Text(feature.enableDescription(enableFeature.wrappedValue, groupInfo.canEdit)) Text(feature.enableDescription(enableFeature.wrappedValue, groupInfo.canEdit))
} }
.onChange(of: enableFeature.wrappedValue) { enabled in
if case .off = enabled {
enableForRole?.wrappedValue = nil
}
}
} }
private func savePreferences() { private func savePreferences() {
@@ -92,7 +92,7 @@ struct ChatListNavLink: View {
.swipeActions(edge: .leading, allowsFullSwipe: true) { .swipeActions(edge: .leading, allowsFullSwipe: true) {
markReadButton() markReadButton()
toggleFavoriteButton() toggleFavoriteButton()
ToggleNtfsButton(chat: chat) toggleNtfsButton(chat)
} }
.swipeActions(edge: .trailing, allowsFullSwipe: true) { .swipeActions(edge: .trailing, allowsFullSwipe: true) {
if !chat.chatItems.isEmpty { if !chat.chatItems.isEmpty {
@@ -181,7 +181,7 @@ struct ChatListNavLink: View {
.swipeActions(edge: .leading, allowsFullSwipe: true) { .swipeActions(edge: .leading, allowsFullSwipe: true) {
markReadButton() markReadButton()
toggleFavoriteButton() toggleFavoriteButton()
ToggleNtfsButton(chat: chat) toggleNtfsButton(chat)
} }
.swipeActions(edge: .trailing, allowsFullSwipe: true) { .swipeActions(edge: .trailing, allowsFullSwipe: true) {
if !chat.chatItems.isEmpty { if !chat.chatItems.isEmpty {
@@ -448,9 +448,6 @@ struct MigrateToDevice: View {
case .rcvFileError: case .rcvFileError:
alert = .error(title: "Download failed", error: "File was deleted or link is invalid") alert = .error(title: "Download failed", error: "File was deleted or link is invalid")
migrationState = .downloadFailed(totalBytes: totalBytes, link: link, archivePath: archivePath) migrationState = .downloadFailed(totalBytes: totalBytes, link: link, archivePath: archivePath)
case .chatError(_, .error(.noRcvFileUser)):
alert = .error(title: "Download failed", error: "File was deleted or link is invalid")
migrationState = .downloadFailed(totalBytes: totalBytes, link: link, archivePath: archivePath)
default: default:
logger.debug("unsupported event: \(msg.responseType)") logger.debug("unsupported event: \(msg.responseType)")
} }
@@ -51,10 +51,9 @@ struct AdvancedNetworkSettings: View {
} }
.disabled(currentNetCfg == NetCfg.proxyDefaults) .disabled(currentNetCfg == NetCfg.proxyDefaults)
timeoutSettingPicker("TCP connection timeout", selection: $netCfg.tcpConnectTimeout, values: [5_000000, 7_500000, 10_000000, 15_000000, 20_000000, 30_000000, 40_000000], label: secondsLabel) timeoutSettingPicker("TCP connection timeout", selection: $netCfg.tcpConnectTimeout, values: [7_500000, 10_000000, 15_000000, 20_000000, 30_000000, 45_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", 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) timeoutSettingPicker("Protocol timeout per KB", selection: $netCfg.tcpTimeoutPerKb, values: [15_000, 30_000, 45_000, 60_000, 90_000, 120_000], label: secondsLabel)
intSettingPicker("Receiving concurrency", selection: $netCfg.rcvConcurrency, values: [1, 2, 4, 8, 12, 16, 24], label: "")
timeoutSettingPicker("PING interval", selection: $netCfg.smpPingInterval, values: [120_000000, 300_000000, 600_000000, 1200_000000, 2400_000000, 3600_000000], label: secondsLabel) timeoutSettingPicker("PING interval", selection: $netCfg.smpPingInterval, values: [120_000000, 300_000000, 600_000000, 1200_000000, 2400_000000, 3600_000000], label: secondsLabel)
intSettingPicker("PING count", selection: $netCfg.smpPingCount, values: [1, 2, 3, 5, 8], label: "") intSettingPicker("PING count", selection: $netCfg.smpPingCount, values: [1, 2, 3, 5, 8], label: "")
Toggle("Enable TCP keep-alive", isOn: $enableKeepAlive) Toggle("Enable TCP keep-alive", isOn: $enableKeepAlive)
@@ -24,7 +24,6 @@ private enum NetworkAlert: Identifiable {
} }
struct NetworkAndServers: View { struct NetworkAndServers: View {
@EnvironmentObject var m: ChatModel
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@State private var cfgLoaded = false @State private var cfgLoaded = false
@State private var currentNetCfg = NetCfg.defaults @State private var currentNetCfg = NetCfg.defaults
@@ -83,14 +82,6 @@ struct NetworkAndServers: View {
Text("WebRTC ICE servers") Text("WebRTC ICE servers")
} }
} }
Section("Network connection") {
HStack {
Text(m.networkInfo.networkType.text)
Spacer()
Image(systemName: "circle.fill").foregroundColor(m.networkInfo.online ? .green : .red)
}
}
} }
} }
.onAppear { .onAppear {
@@ -739,10 +739,6 @@
<target>Позволи необратимо изтриване на изпратените съобщения. (24 часа)</target> <target>Позволи необратимо изтриване на изпратените съобщения. (24 часа)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve"> <trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source> <source>Allow to send files and media.</source>
<target>Позволи изпращане на файлове и медия.</target> <target>Позволи изпращане на файлове и медия.</target>
@@ -1083,10 +1079,6 @@
<target>Файлът не може да бъде получен</target> <target>Файлът не може да бъде получен</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve"> <trans-unit id="Change" xml:space="preserve">
<source>Change</source> <source>Change</source>
<target>Промени</target> <target>Промени</target>
@@ -2081,10 +2073,6 @@ This cannot be undone!</source>
<target>Понижи версията и отвори чата</target> <target>Понижи версията и отвори чата</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve"> <trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source> <source>Download failed</source>
<target>Неуспешно изтегляне</target> <target>Неуспешно изтегляне</target>
@@ -2195,10 +2183,6 @@ This cannot be undone!</source>
<target>Активирай kод за достъп за самоунищожение</target> <target>Активирай kод за достъп за самоунищожение</target>
<note>set passcode view</note> <note>set passcode view</note>
</trans-unit> </trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve"> <trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source> <source>Encrypt</source>
<target>Криптирай</target> <target>Криптирай</target>
@@ -2724,10 +2708,6 @@ This cannot be undone!</source>
<target>Файловете и медията са забранени в тази група.</target> <target>Файловете и медията са забранени в тази група.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve"> <trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source> <source>Files and media prohibited!</source>
<target>Файловете и медията са забранени!</target> <target>Файловете и медията са забранени!</target>
@@ -2793,18 +2773,6 @@ This cannot be undone!</source>
<target>За конзолата</target> <target>За конзолата</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve"> <trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source> <source>Found desktop</source>
<target>Намерено настолно устройство</target> <target>Намерено настолно устройство</target>
@@ -2915,10 +2883,6 @@ This cannot be undone!</source>
<target>Членовете на групата могат необратимо да изтриват изпратените съобщения. (24 часа)</target> <target>Членовете на групата могат необратимо да изтриват изпратените съобщения. (24 часа)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve"> <trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source> <source>Group members can send direct messages.</source>
<target>Членовете на групата могат да изпращат лични съобщения.</target> <target>Членовете на групата могат да изпращат лични съобщения.</target>
@@ -3782,10 +3746,6 @@ This is your link for group %@!</source>
<target>Мрежа и сървъри</target> <target>Мрежа и сървъри</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve"> <trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source> <source>Network settings</source>
<target>Мрежови настройки</target> <target>Мрежови настройки</target>
@@ -3896,10 +3856,6 @@ This is your link for group %@!</source>
<target>Няма история</target> <target>Няма история</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve"> <trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source> <source>No permission to record voice message</source>
<target>Няма разрешение за запис на гласово съобщение</target> <target>Няма разрешение за запис на гласово съобщение</target>
@@ -4114,10 +4070,6 @@ This is your link for group %@!</source>
<target>Или покажи този код</target> <target>Или покажи този код</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve"> <trans-unit id="PING count" xml:space="preserve">
<source>PING count</source> <source>PING count</source>
<target>PING бройка</target> <target>PING бройка</target>
@@ -4380,10 +4332,6 @@ Error: %@</source>
<target>Забрани реакциите на съобщенията.</target> <target>Забрани реакциите на съобщенията.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve"> <trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source> <source>Prohibit sending direct messages to members.</source>
<target>Забрани изпращането на лични съобщения до членовете.</target> <target>Забрани изпращането на лични съобщения до членовете.</target>
@@ -4514,10 +4462,6 @@ Error: %@</source>
<target>Получаващият адрес ще бъде променен към друг сървър. Промяната на адреса ще завърши, след като подателят е онлайн.</target> <target>Получаващият адрес ще бъде променен към друг сървър. Промяната на адреса ще завърши, след като подателят е онлайн.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Receiving concurrency" xml:space="preserve">
<source>Receiving concurrency</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving file will be stopped." xml:space="preserve"> <trans-unit id="Receiving file will be stopped." xml:space="preserve">
<source>Receiving file will be stopped.</source> <source>Receiving file will be stopped.</source>
<target>Получаващият се файл ще бъде спрян.</target> <target>Получаващият се файл ще бъде спрян.</target>
@@ -4533,10 +4477,6 @@ Error: %@</source>
<target>Скорошна история и подобрен [bot за директория за групи](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPd jdLW3%23%2F%3Fv%3D1-2% 26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target> <target>Скорошна история и подобрен [bot за директория за групи](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPd jdLW3%23%2F%3Fv%3D1-2% 26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve"> <trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source> <source>Recipients see updates as you type them.</source>
<target>Получателите виждат актуализации, докато ги въвеждате.</target> <target>Получателите виждат актуализации, докато ги въвеждате.</target>
@@ -4837,19 +4777,11 @@ Error: %@</source>
<target>Запази съобщението при посрещане?</target> <target>Запази съобщението при посрещане?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve"> <trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source> <source>Saved WebRTC ICE servers will be removed</source>
<target>Запазените WebRTC ICE сървъри ще бъдат премахнати</target> <target>Запазените WebRTC ICE сървъри ще бъдат премахнати</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve"> <trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source> <source>Saved message</source>
<target>Запазено съобщение</target> <target>Запазено съобщение</target>
@@ -5273,14 +5205,6 @@ Error: %@</source>
<trans-unit id="SimpleX links" xml:space="preserve"> <trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source> <source>SimpleX links</source>
<target>SimpleX линкове</target> <target>SimpleX линкове</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve"> <trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -6119,10 +6043,6 @@ To connect, please ask your contact to create another connection link and check
<target>Гласовите съобщения са забранени в тази група.</target> <target>Гласовите съобщения са забранени в тази група.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve"> <trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source> <source>Voice messages prohibited!</source>
<target>Гласовите съобщения са забранени!</target> <target>Гласовите съобщения са забранени!</target>
@@ -6203,14 +6123,6 @@ To connect, please ask your contact to create another connection link and check
<target>Когато споделяте инкогнито профил с някого, този профил ще се използва за групите, в които той ви кани.</target> <target>Когато споделяте инкогнито профил с някого, този профил ще се използва за групите, в които той ви кани.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve"> <trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source> <source>With encrypted files and media.</source>
<target>С криптирани файлове и медия.</target> <target>С криптирани файлове и медия.</target>
@@ -6693,10 +6605,6 @@ SimpleX сървърите не могат да видят вашия профи
<target>админ</target> <target>админ</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve"> <trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source> <source>agreeing encryption for %@…</source>
<target>съгласуване на криптиране за %@…</target> <target>съгласуване на криптиране за %@…</target>
@@ -6707,10 +6615,6 @@ SimpleX сървърите не могат да видят вашия профи
<target>съгласуване на криптиране…</target> <target>съгласуване на криптиране…</target>
<note>chat item text</note> <note>chat item text</note>
</trans-unit> </trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve"> <trans-unit id="always" xml:space="preserve">
<source>always</source> <source>always</source>
<target>винаги</target> <target>винаги</target>
@@ -7041,10 +6945,6 @@ SimpleX сървърите не могат да видят вашия профи
<target>събитие се случи</target> <target>събитие се случи</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve"> <trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source> <source>group deleted</source>
<target>групата е изтрита</target> <target>групата е изтрита</target>
@@ -7252,10 +7152,6 @@ SimpleX сървърите не могат да видят вашия профи
<target>собственик</target> <target>собственик</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve"> <trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source> <source>peer-to-peer</source>
<target>peer-to-peer</target> <target>peer-to-peer</target>
@@ -7306,14 +7202,6 @@ SimpleX сървърите не могат да видят вашия профи
<target>ви острани</target> <target>ви острани</target>
<note>rcv group event chat item</note> <note>rcv group event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve"> <trans-unit id="sec" xml:space="preserve">
<source>sec</source> <source>sec</source>
<target>сек.</target> <target>сек.</target>
@@ -7454,10 +7342,6 @@ SimpleX сървърите не могат да видят вашия профи
<target>да</target> <target>да</target>
<note>pref value</note> <note>pref value</note>
</trans-unit> </trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve"> <trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source> <source>you are invited to group</source>
<target>вие сте поканени в групата</target> <target>вие сте поканени в групата</target>
@@ -720,10 +720,6 @@
<target>Povolit nevratné smazání odeslaných zpráv. (24 hodin)</target> <target>Povolit nevratné smazání odeslaných zpráv. (24 hodin)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve"> <trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source> <source>Allow to send files and media.</source>
<target>Povolit odesílání souborů a médii.</target> <target>Povolit odesílání souborů a médii.</target>
@@ -1047,10 +1043,6 @@
<target>Nelze přijmout soubor</target> <target>Nelze přijmout soubor</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve"> <trans-unit id="Change" xml:space="preserve">
<source>Change</source> <source>Change</source>
<target>Změnit</target> <target>Změnit</target>
@@ -2007,10 +1999,6 @@ This cannot be undone!</source>
<target>Snížit a otevřít chat</target> <target>Snížit a otevřít chat</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve"> <trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source> <source>Download failed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -2116,10 +2104,6 @@ This cannot be undone!</source>
<target>Povolit sebedestrukční heslo</target> <target>Povolit sebedestrukční heslo</target>
<note>set passcode view</note> <note>set passcode view</note>
</trans-unit> </trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve"> <trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source> <source>Encrypt</source>
<target>Šifrovat</target> <target>Šifrovat</target>
@@ -2627,10 +2611,6 @@ This cannot be undone!</source>
<target>Soubory a média jsou zakázány v této skupině.</target> <target>Soubory a média jsou zakázány v této skupině.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve"> <trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source> <source>Files and media prohibited!</source>
<target>Soubory a média jsou zakázány!</target> <target>Soubory a média jsou zakázány!</target>
@@ -2694,18 +2674,6 @@ This cannot be undone!</source>
<target>Pro konzoli</target> <target>Pro konzoli</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve"> <trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source> <source>Found desktop</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -2812,10 +2780,6 @@ This cannot be undone!</source>
<target>Členové skupiny mohou nevratně mazat odeslané zprávy. (24 hodin)</target> <target>Členové skupiny mohou nevratně mazat odeslané zprávy. (24 hodin)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve"> <trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source> <source>Group members can send direct messages.</source>
<target>Členové skupiny mohou posílat přímé zprávy.</target> <target>Členové skupiny mohou posílat přímé zprávy.</target>
@@ -3645,10 +3609,6 @@ This is your link for group %@!</source>
<target>Síť a servery</target> <target>Síť a servery</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve"> <trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source> <source>Network settings</source>
<target>Nastavení sítě</target> <target>Nastavení sítě</target>
@@ -3758,10 +3718,6 @@ This is your link for group %@!</source>
<target>Žádná historie</target> <target>Žádná historie</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve"> <trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source> <source>No permission to record voice message</source>
<target>Nemáte oprávnění nahrávat hlasové zprávy</target> <target>Nemáte oprávnění nahrávat hlasové zprávy</target>
@@ -3967,10 +3923,6 @@ This is your link for group %@!</source>
<source>Or show this code</source> <source>Or show this code</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve"> <trans-unit id="PING count" xml:space="preserve">
<source>PING count</source> <source>PING count</source>
<target>Počet PING</target> <target>Počet PING</target>
@@ -4221,10 +4173,6 @@ Error: %@</source>
<target>Zakázat reakce na zprávy.</target> <target>Zakázat reakce na zprávy.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve"> <trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source> <source>Prohibit sending direct messages to members.</source>
<target>Zakázat odesílání přímých zpráv členům.</target> <target>Zakázat odesílání přímých zpráv členům.</target>
@@ -4352,10 +4300,6 @@ Error: %@</source>
<target>Přijímací adresa bude změněna na jiný server. Změna adresy bude dokončena po připojení odesílatele.</target> <target>Přijímací adresa bude změněna na jiný server. Změna adresy bude dokončena po připojení odesílatele.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Receiving concurrency" xml:space="preserve">
<source>Receiving concurrency</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving file will be stopped." xml:space="preserve"> <trans-unit id="Receiving file will be stopped." xml:space="preserve">
<source>Receiving file will be stopped.</source> <source>Receiving file will be stopped.</source>
<target>Příjem souboru bude zastaven.</target> <target>Příjem souboru bude zastaven.</target>
@@ -4370,10 +4314,6 @@ Error: %@</source>
<source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source> <source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve"> <trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source> <source>Recipients see updates as you type them.</source>
<target>Příjemci uvidí aktualizace během jejich psaní.</target> <target>Příjemci uvidí aktualizace během jejich psaní.</target>
@@ -4667,19 +4607,11 @@ Error: %@</source>
<target>Uložit uvítací zprávu?</target> <target>Uložit uvítací zprávu?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve"> <trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source> <source>Saved WebRTC ICE servers will be removed</source>
<target>Uložené servery WebRTC ICE budou odstraněny</target> <target>Uložené servery WebRTC ICE budou odstraněny</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve"> <trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source> <source>Saved message</source>
<note>message info title</note> <note>message info title</note>
@@ -5094,14 +5026,6 @@ Error: %@</source>
<trans-unit id="SimpleX links" xml:space="preserve"> <trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source> <source>SimpleX links</source>
<target>Odkazy na SimpleX</target> <target>Odkazy na SimpleX</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve"> <trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -5904,10 +5828,6 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
<target>Hlasové zprávy jsou v této skupině zakázány.</target> <target>Hlasové zprávy jsou v této skupině zakázány.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve"> <trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source> <source>Voice messages prohibited!</source>
<target>Hlasové zprávy jsou zakázány!</target> <target>Hlasové zprávy jsou zakázány!</target>
@@ -5985,14 +5905,6 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
<target>Pokud s někým sdílíte inkognito profil, bude tento profil použit pro skupiny, do kterých vás pozve.</target> <target>Pokud s někým sdílíte inkognito profil, bude tento profil použit pro skupiny, do kterých vás pozve.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve"> <trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source> <source>With encrypted files and media.</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -6456,10 +6368,6 @@ Servery SimpleX nevidí váš profil.</target>
<target>správce</target> <target>správce</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve"> <trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source> <source>agreeing encryption for %@…</source>
<target>povoluji šifrování pro %@…</target> <target>povoluji šifrování pro %@…</target>
@@ -6470,10 +6378,6 @@ Servery SimpleX nevidí váš profil.</target>
<target>povoluji šifrování…</target> <target>povoluji šifrování…</target>
<note>chat item text</note> <note>chat item text</note>
</trans-unit> </trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve"> <trans-unit id="always" xml:space="preserve">
<source>always</source> <source>always</source>
<target>vždy</target> <target>vždy</target>
@@ -6796,10 +6700,6 @@ Servery SimpleX nevidí váš profil.</target>
<source>event happened</source> <source>event happened</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve"> <trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source> <source>group deleted</source>
<target>skupina smazána</target> <target>skupina smazána</target>
@@ -7006,10 +6906,6 @@ Servery SimpleX nevidí váš profil.</target>
<target>vlastník</target> <target>vlastník</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve"> <trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source> <source>peer-to-peer</source>
<target>peer-to-peer</target> <target>peer-to-peer</target>
@@ -7057,14 +6953,6 @@ Servery SimpleX nevidí váš profil.</target>
<target>odstranil vás</target> <target>odstranil vás</target>
<note>rcv group event chat item</note> <note>rcv group event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve"> <trans-unit id="sec" xml:space="preserve">
<source>sec</source> <source>sec</source>
<target>sek</target> <target>sek</target>
@@ -7198,10 +7086,6 @@ Servery SimpleX nevidí váš profil.</target>
<target>ano</target> <target>ano</target>
<note>pref value</note> <note>pref value</note>
</trans-unit> </trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve"> <trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source> <source>you are invited to group</source>
<target>jste pozváni do skupiny</target> <target>jste pozváni do skupiny</target>
@@ -743,10 +743,6 @@
<target>Unwiederbringliches löschen von gesendeten Nachrichten erlauben. (24 Stunden)</target> <target>Unwiederbringliches löschen von gesendeten Nachrichten erlauben. (24 Stunden)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve"> <trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source> <source>Allow to send files and media.</source>
<target>Das Senden von Dateien und Medien erlauben.</target> <target>Das Senden von Dateien und Medien erlauben.</target>
@@ -1087,10 +1083,6 @@
<target>Datei kann nicht empfangen werden</target> <target>Datei kann nicht empfangen werden</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve"> <trans-unit id="Change" xml:space="preserve">
<source>Change</source> <source>Change</source>
<target>Ändern</target> <target>Ändern</target>
@@ -2086,10 +2078,6 @@ Das kann nicht rückgängig gemacht werden!</target>
<target>Datenbank herabstufen und den Chat öffnen</target> <target>Datenbank herabstufen und den Chat öffnen</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve"> <trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source> <source>Download failed</source>
<target>Herunterladen fehlgeschlagen</target> <target>Herunterladen fehlgeschlagen</target>
@@ -2200,10 +2188,6 @@ Das kann nicht rückgängig gemacht werden!</target>
<target>Selbstzerstörungs-Zugangscode aktivieren</target> <target>Selbstzerstörungs-Zugangscode aktivieren</target>
<note>set passcode view</note> <note>set passcode view</note>
</trans-unit> </trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve"> <trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source> <source>Encrypt</source>
<target>Verschlüsseln</target> <target>Verschlüsseln</target>
@@ -2729,10 +2713,6 @@ Das kann nicht rückgängig gemacht werden!</target>
<target>In dieser Gruppe sind Dateien und Medien nicht erlaubt.</target> <target>In dieser Gruppe sind Dateien und Medien nicht erlaubt.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve"> <trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source> <source>Files and media prohibited!</source>
<target>Dateien und Medien sind nicht erlaubt!</target> <target>Dateien und Medien sind nicht erlaubt!</target>
@@ -2798,18 +2778,6 @@ Das kann nicht rückgängig gemacht werden!</target>
<target>Für Konsole</target> <target>Für Konsole</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve"> <trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source> <source>Found desktop</source>
<target>Gefundener Desktop</target> <target>Gefundener Desktop</target>
@@ -2920,10 +2888,6 @@ Das kann nicht rückgängig gemacht werden!</target>
<target>Gruppenmitglieder können gesendete Nachrichten unwiederbringlich löschen. (24 Stunden)</target> <target>Gruppenmitglieder können gesendete Nachrichten unwiederbringlich löschen. (24 Stunden)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve"> <trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source> <source>Group members can send direct messages.</source>
<target>Gruppenmitglieder können Direktnachrichten versenden.</target> <target>Gruppenmitglieder können Direktnachrichten versenden.</target>
@@ -3789,10 +3753,6 @@ Das ist Ihr Link für die Gruppe %@!</target>
<target>Netzwerk &amp; Server</target> <target>Netzwerk &amp; Server</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve"> <trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source> <source>Network settings</source>
<target>Netzwerkeinstellungen</target> <target>Netzwerkeinstellungen</target>
@@ -3903,10 +3863,6 @@ Das ist Ihr Link für die Gruppe %@!</target>
<target>Kein Nachrichtenverlauf</target> <target>Kein Nachrichtenverlauf</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve"> <trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source> <source>No permission to record voice message</source>
<target>Keine Berechtigung für das Aufnehmen von Sprachnachrichten</target> <target>Keine Berechtigung für das Aufnehmen von Sprachnachrichten</target>
@@ -4121,10 +4077,6 @@ Das ist Ihr Link für die Gruppe %@!</target>
<target>Oder diesen QR-Code anzeigen</target> <target>Oder diesen QR-Code anzeigen</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve"> <trans-unit id="PING count" xml:space="preserve">
<source>PING count</source> <source>PING count</source>
<target>PING-Zähler</target> <target>PING-Zähler</target>
@@ -4387,10 +4339,6 @@ Fehler: %@</target>
<target>Reaktionen auf Nachrichten nicht erlauben.</target> <target>Reaktionen auf Nachrichten nicht erlauben.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve"> <trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source> <source>Prohibit sending direct messages to members.</source>
<target>Das Senden von Direktnachrichten an Gruppenmitglieder nicht erlauben.</target> <target>Das Senden von Direktnachrichten an Gruppenmitglieder nicht erlauben.</target>
@@ -4521,10 +4469,6 @@ Fehler: %@</target>
<target>Die Empfängeradresse wird auf einen anderen Server geändert. Der Adresswechsel wird abgeschlossen, wenn der Absender wieder online ist.</target> <target>Die Empfängeradresse wird auf einen anderen Server geändert. Der Adresswechsel wird abgeschlossen, wenn der Absender wieder online ist.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Receiving concurrency" xml:space="preserve">
<source>Receiving concurrency</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving file will be stopped." xml:space="preserve"> <trans-unit id="Receiving file will be stopped." xml:space="preserve">
<source>Receiving file will be stopped.</source> <source>Receiving file will be stopped.</source>
<target>Der Empfang der Datei wird beendet.</target> <target>Der Empfang der Datei wird beendet.</target>
@@ -4540,10 +4484,6 @@ Fehler: %@</target>
<target>Aktueller Nachrichtenverlauf und verbesserter [Gruppenverzeichnis-Bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target> <target>Aktueller Nachrichtenverlauf und verbesserter [Gruppenverzeichnis-Bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve"> <trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source> <source>Recipients see updates as you type them.</source>
<target>Die Empfänger sehen Nachrichtenaktualisierungen, während Sie sie eingeben.</target> <target>Die Empfänger sehen Nachrichtenaktualisierungen, während Sie sie eingeben.</target>
@@ -4844,19 +4784,11 @@ Fehler: %@</target>
<target>Begrüßungsmeldung speichern?</target> <target>Begrüßungsmeldung speichern?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve"> <trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source> <source>Saved WebRTC ICE servers will be removed</source>
<target>Gespeicherte WebRTC ICE-Server werden entfernt</target> <target>Gespeicherte WebRTC ICE-Server werden entfernt</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve"> <trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source> <source>Saved message</source>
<target>Gespeicherte Nachricht</target> <target>Gespeicherte Nachricht</target>
@@ -5280,14 +5212,6 @@ Fehler: %@</target>
<trans-unit id="SimpleX links" xml:space="preserve"> <trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source> <source>SimpleX links</source>
<target>SimpleX-Links</target> <target>SimpleX-Links</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve"> <trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -6126,10 +6050,6 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
<target>In dieser Gruppe sind Sprachnachrichten nicht erlaubt.</target> <target>In dieser Gruppe sind Sprachnachrichten nicht erlaubt.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve"> <trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source> <source>Voice messages prohibited!</source>
<target>Sprachnachrichten sind nicht erlaubt!</target> <target>Sprachnachrichten sind nicht erlaubt!</target>
@@ -6210,14 +6130,6 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
<target>Wenn Sie ein Inkognito-Profil mit Jemandem teilen, wird dieses Profil auch für die Gruppen verwendet, für die Sie von diesem Kontakt eingeladen werden.</target> <target>Wenn Sie ein Inkognito-Profil mit Jemandem teilen, wird dieses Profil auch für die Gruppen verwendet, für die Sie von diesem Kontakt eingeladen werden.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve"> <trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source> <source>With encrypted files and media.</source>
<target>Mit verschlüsselten Dateien und Medien.</target> <target>Mit verschlüsselten Dateien und Medien.</target>
@@ -6701,10 +6613,6 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
<target>Admin</target> <target>Admin</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve"> <trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source> <source>agreeing encryption for %@…</source>
<target>Verschlüsselung von %@ zustimmen…</target> <target>Verschlüsselung von %@ zustimmen…</target>
@@ -6715,10 +6623,6 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
<target>Verschlüsselung zustimmen…</target> <target>Verschlüsselung zustimmen…</target>
<note>chat item text</note> <note>chat item text</note>
</trans-unit> </trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve"> <trans-unit id="always" xml:space="preserve">
<source>always</source> <source>always</source>
<target>Immer</target> <target>Immer</target>
@@ -7049,10 +6953,6 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
<target>event happened</target> <target>event happened</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve"> <trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source> <source>group deleted</source>
<target>Gruppe gelöscht</target> <target>Gruppe gelöscht</target>
@@ -7260,10 +7160,6 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
<target>Eigentümer</target> <target>Eigentümer</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve"> <trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source> <source>peer-to-peer</source>
<target>Peer-to-Peer</target> <target>Peer-to-Peer</target>
@@ -7314,14 +7210,6 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
<target>hat Sie aus der Gruppe entfernt</target> <target>hat Sie aus der Gruppe entfernt</target>
<note>rcv group event chat item</note> <note>rcv group event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve"> <trans-unit id="sec" xml:space="preserve">
<source>sec</source> <source>sec</source>
<target>sek</target> <target>sek</target>
@@ -7462,10 +7350,6 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
<target>Ja</target> <target>Ja</target>
<note>pref value</note> <note>pref value</note>
</trans-unit> </trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve"> <trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source> <source>you are invited to group</source>
<target>Sie sind zu der Gruppe eingeladen</target> <target>Sie sind zu der Gruppe eingeladen</target>
@@ -743,11 +743,6 @@
<target>Allow to irreversibly delete sent messages. (24 hours)</target> <target>Allow to irreversibly delete sent messages. (24 hours)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<target>Allow to send SimpleX links.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve"> <trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source> <source>Allow to send files and media.</source>
<target>Allow to send files and media.</target> <target>Allow to send files and media.</target>
@@ -1088,11 +1083,6 @@
<target>Cannot receive file</target> <target>Cannot receive file</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<target>Cellular</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve"> <trans-unit id="Change" xml:space="preserve">
<source>Change</source> <source>Change</source>
<target>Change</target> <target>Change</target>
@@ -2088,11 +2078,6 @@ This cannot be undone!</target>
<target>Downgrade and open chat</target> <target>Downgrade and open chat</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<target>Download</target>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve"> <trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source> <source>Download failed</source>
<target>Download failed</target> <target>Download failed</target>
@@ -2203,11 +2188,6 @@ This cannot be undone!</target>
<target>Enable self-destruct passcode</target> <target>Enable self-destruct passcode</target>
<note>set passcode view</note> <note>set passcode view</note>
</trans-unit> </trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<target>Enabled for</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve"> <trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source> <source>Encrypt</source>
<target>Encrypt</target> <target>Encrypt</target>
@@ -2733,11 +2713,6 @@ This cannot be undone!</target>
<target>Files and media are prohibited in this group.</target> <target>Files and media are prohibited in this group.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<target>Files and media not allowed</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve"> <trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source> <source>Files and media prohibited!</source>
<target>Files and media prohibited!</target> <target>Files and media prohibited!</target>
@@ -2803,21 +2778,6 @@ This cannot be undone!</target>
<target>For console</target> <target>For console</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<target>Forward</target>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<target>Forwarded</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<target>Forwarded from</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve"> <trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source> <source>Found desktop</source>
<target>Found desktop</target> <target>Found desktop</target>
@@ -2928,11 +2888,6 @@ This cannot be undone!</target>
<target>Group members can irreversibly delete sent messages. (24 hours)</target> <target>Group members can irreversibly delete sent messages. (24 hours)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<target>Group members can send SimpleX links.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve"> <trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source> <source>Group members can send direct messages.</source>
<target>Group members can send direct messages.</target> <target>Group members can send direct messages.</target>
@@ -3798,11 +3753,6 @@ This is your link for group %@!</target>
<target>Network &amp; servers</target> <target>Network &amp; servers</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<target>Network connection</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve"> <trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source> <source>Network settings</source>
<target>Network settings</target> <target>Network settings</target>
@@ -3913,11 +3863,6 @@ This is your link for group %@!</target>
<target>No history</target> <target>No history</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<target>No network connection</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve"> <trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source> <source>No permission to record voice message</source>
<target>No permission to record voice message</target> <target>No permission to record voice message</target>
@@ -4132,11 +4077,6 @@ This is your link for group %@!</target>
<target>Or show this code</target> <target>Or show this code</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<target>Other</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve"> <trans-unit id="PING count" xml:space="preserve">
<source>PING count</source> <source>PING count</source>
<target>PING count</target> <target>PING count</target>
@@ -4399,11 +4339,6 @@ Error: %@</target>
<target>Prohibit messages reactions.</target> <target>Prohibit messages reactions.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<target>Prohibit sending SimpleX links.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve"> <trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source> <source>Prohibit sending direct messages to members.</source>
<target>Prohibit sending direct messages to members.</target> <target>Prohibit sending direct messages to members.</target>
@@ -4534,11 +4469,6 @@ Error: %@</target>
<target>Receiving address will be changed to a different server. Address change will complete after sender comes online.</target> <target>Receiving address will be changed to a different server. Address change will complete after sender comes online.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Receiving concurrency" xml:space="preserve">
<source>Receiving concurrency</source>
<target>Receiving concurrency</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving file will be stopped." xml:space="preserve"> <trans-unit id="Receiving file will be stopped." xml:space="preserve">
<source>Receiving file will be stopped.</source> <source>Receiving file will be stopped.</source>
<target>Receiving file will be stopped.</target> <target>Receiving file will be stopped.</target>
@@ -4554,11 +4484,6 @@ Error: %@</target>
<target>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target> <target>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<target>Recipient(s) can't see who this message is from.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve"> <trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source> <source>Recipients see updates as you type them.</source>
<target>Recipients see updates as you type them.</target> <target>Recipients see updates as you type them.</target>
@@ -4859,21 +4784,11 @@ Error: %@</target>
<target>Save welcome message?</target> <target>Save welcome message?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<target>Saved</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve"> <trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source> <source>Saved WebRTC ICE servers will be removed</source>
<target>Saved WebRTC ICE servers will be removed</target> <target>Saved WebRTC ICE servers will be removed</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<target>Saved from</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve"> <trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source> <source>Saved message</source>
<target>Saved message</target> <target>Saved message</target>
@@ -5297,16 +5212,6 @@ Error: %@</target>
<trans-unit id="SimpleX links" xml:space="preserve"> <trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source> <source>SimpleX links</source>
<target>SimpleX links</target> <target>SimpleX links</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<target>SimpleX links are prohibited in this group.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<target>SimpleX links not allowed</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve"> <trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -6145,11 +6050,6 @@ To connect, please ask your contact to create another connection link and check
<target>Voice messages are prohibited in this group.</target> <target>Voice messages are prohibited in this group.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<target>Voice messages not allowed</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve"> <trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source> <source>Voice messages prohibited!</source>
<target>Voice messages prohibited!</target> <target>Voice messages prohibited!</target>
@@ -6230,16 +6130,6 @@ To connect, please ask your contact to create another connection link and check
<target>When you share an incognito profile with somebody, this profile will be used for the groups they invite you to.</target> <target>When you share an incognito profile with somebody, this profile will be used for the groups they invite you to.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<target>WiFi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<target>Wired ethernet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve"> <trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source> <source>With encrypted files and media.</source>
<target>With encrypted files and media.</target> <target>With encrypted files and media.</target>
@@ -6723,11 +6613,6 @@ SimpleX servers cannot see your profile.</target>
<target>admin</target> <target>admin</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<target>admins</target>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve"> <trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source> <source>agreeing encryption for %@…</source>
<target>agreeing encryption for %@…</target> <target>agreeing encryption for %@…</target>
@@ -6738,11 +6623,6 @@ SimpleX servers cannot see your profile.</target>
<target>agreeing encryption…</target> <target>agreeing encryption…</target>
<note>chat item text</note> <note>chat item text</note>
</trans-unit> </trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<target>all members</target>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve"> <trans-unit id="always" xml:space="preserve">
<source>always</source> <source>always</source>
<target>always</target> <target>always</target>
@@ -7073,11 +6953,6 @@ SimpleX servers cannot see your profile.</target>
<target>event happened</target> <target>event happened</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<target>forwarded</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve"> <trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source> <source>group deleted</source>
<target>group deleted</target> <target>group deleted</target>
@@ -7285,11 +7160,6 @@ SimpleX servers cannot see your profile.</target>
<target>owner</target> <target>owner</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<target>owners</target>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve"> <trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source> <source>peer-to-peer</source>
<target>peer-to-peer</target> <target>peer-to-peer</target>
@@ -7340,16 +7210,6 @@ SimpleX servers cannot see your profile.</target>
<target>removed you</target> <target>removed you</target>
<note>rcv group event chat item</note> <note>rcv group event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<target>saved</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<target>saved from %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve"> <trans-unit id="sec" xml:space="preserve">
<source>sec</source> <source>sec</source>
<target>sec</target> <target>sec</target>
@@ -7490,11 +7350,6 @@ SimpleX servers cannot see your profile.</target>
<target>yes</target> <target>yes</target>
<note>pref value</note> <note>pref value</note>
</trans-unit> </trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<target>you</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve"> <trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source> <source>you are invited to group</source>
<target>you are invited to group</target> <target>you are invited to group</target>
@@ -743,10 +743,6 @@
<target>Se permite la eliminación irreversible de mensajes. (24 horas)</target> <target>Se permite la eliminación irreversible de mensajes. (24 horas)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve"> <trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source> <source>Allow to send files and media.</source>
<target>Se permite enviar archivos y multimedia.</target> <target>Se permite enviar archivos y multimedia.</target>
@@ -1087,10 +1083,6 @@
<target>No se puede recibir el archivo</target> <target>No se puede recibir el archivo</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve"> <trans-unit id="Change" xml:space="preserve">
<source>Change</source> <source>Change</source>
<target>Cambiar</target> <target>Cambiar</target>
@@ -2086,10 +2078,6 @@ This cannot be undone!</source>
<target>Degradar y abrir Chat</target> <target>Degradar y abrir Chat</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve"> <trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source> <source>Download failed</source>
<target>Error en la descarga</target> <target>Error en la descarga</target>
@@ -2200,10 +2188,6 @@ This cannot be undone!</source>
<target>Activar código de autodestrucción</target> <target>Activar código de autodestrucción</target>
<note>set passcode view</note> <note>set passcode view</note>
</trans-unit> </trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve"> <trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source> <source>Encrypt</source>
<target>Cifrar</target> <target>Cifrar</target>
@@ -2729,10 +2713,6 @@ This cannot be undone!</source>
<target>No se permiten archivos y multimedia en este grupo.</target> <target>No se permiten archivos y multimedia en este grupo.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve"> <trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source> <source>Files and media prohibited!</source>
<target>¡Archivos y multimedia no permitidos!</target> <target>¡Archivos y multimedia no permitidos!</target>
@@ -2798,18 +2778,6 @@ This cannot be undone!</source>
<target>Para consola</target> <target>Para consola</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve"> <trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source> <source>Found desktop</source>
<target>Ordenador encontrado</target> <target>Ordenador encontrado</target>
@@ -2920,10 +2888,6 @@ This cannot be undone!</source>
<target>Los miembros del grupo pueden eliminar mensajes de forma irreversible. (24 horas)</target> <target>Los miembros del grupo pueden eliminar mensajes de forma irreversible. (24 horas)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve"> <trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source> <source>Group members can send direct messages.</source>
<target>Los miembros del grupo pueden enviar mensajes directos.</target> <target>Los miembros del grupo pueden enviar mensajes directos.</target>
@@ -3789,10 +3753,6 @@ This is your link for group %@!</source>
<target>Servidores y Redes</target> <target>Servidores y Redes</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve"> <trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source> <source>Network settings</source>
<target>Configuración de red</target> <target>Configuración de red</target>
@@ -3903,10 +3863,6 @@ This is your link for group %@!</source>
<target>Sin historial</target> <target>Sin historial</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve"> <trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source> <source>No permission to record voice message</source>
<target>Sin permiso para grabar mensajes de voz</target> <target>Sin permiso para grabar mensajes de voz</target>
@@ -4121,10 +4077,6 @@ This is your link for group %@!</source>
<target>O mostrar este código</target> <target>O mostrar este código</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve"> <trans-unit id="PING count" xml:space="preserve">
<source>PING count</source> <source>PING count</source>
<target>Contador PING</target> <target>Contador PING</target>
@@ -4387,10 +4339,6 @@ Error: %@</target>
<target>No se permiten reacciones a los mensajes.</target> <target>No se permiten reacciones a los mensajes.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve"> <trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source> <source>Prohibit sending direct messages to members.</source>
<target>No se permiten mensajes directos entre miembros.</target> <target>No se permiten mensajes directos entre miembros.</target>
@@ -4521,10 +4469,6 @@ Error: %@</target>
<target>La dirección de recepción pasará a otro servidor. El cambio se completará cuando el remitente esté en línea.</target> <target>La dirección de recepción pasará a otro servidor. El cambio se completará cuando el remitente esté en línea.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Receiving concurrency" xml:space="preserve">
<source>Receiving concurrency</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving file will be stopped." xml:space="preserve"> <trans-unit id="Receiving file will be stopped." xml:space="preserve">
<source>Receiving file will be stopped.</source> <source>Receiving file will be stopped.</source>
<target>Se detendrá la recepción del archivo.</target> <target>Se detendrá la recepción del archivo.</target>
@@ -4540,10 +4484,6 @@ Error: %@</target>
<target>Historial reciente y [bot del directorio](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) mejorados.</target> <target>Historial reciente y [bot del directorio](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) mejorados.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve"> <trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source> <source>Recipients see updates as you type them.</source>
<target>Los destinatarios ven actualizarse mientras escribes.</target> <target>Los destinatarios ven actualizarse mientras escribes.</target>
@@ -4844,19 +4784,11 @@ Error: %@</target>
<target>¿Guardar mensaje de bienvenida?</target> <target>¿Guardar mensaje de bienvenida?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve"> <trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source> <source>Saved WebRTC ICE servers will be removed</source>
<target>Los servidores WebRTC ICE guardados serán eliminados</target> <target>Los servidores WebRTC ICE guardados serán eliminados</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve"> <trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source> <source>Saved message</source>
<target>Mensaje guardado</target> <target>Mensaje guardado</target>
@@ -5280,14 +5212,6 @@ Error: %@</target>
<trans-unit id="SimpleX links" xml:space="preserve"> <trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source> <source>SimpleX links</source>
<target>Enlaces SimpleX</target> <target>Enlaces SimpleX</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve"> <trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -6127,10 +6051,6 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb
<target>Los mensajes de voz no están permitidos en este grupo.</target> <target>Los mensajes de voz no están permitidos en este grupo.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve"> <trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source> <source>Voice messages prohibited!</source>
<target>¡Mensajes de voz no permitidos!</target> <target>¡Mensajes de voz no permitidos!</target>
@@ -6211,14 +6131,6 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb
<target>Cuando compartes un perfil incógnito con alguien, este perfil también se usará para los grupos a los que te inviten.</target> <target>Cuando compartes un perfil incógnito con alguien, este perfil también se usará para los grupos a los que te inviten.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve"> <trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source> <source>With encrypted files and media.</source>
<target>Con cifrado de archivos y multimedia.</target> <target>Con cifrado de archivos y multimedia.</target>
@@ -6702,10 +6614,6 @@ Los servidores de SimpleX no pueden ver tu perfil.</target>
<target>administrador</target> <target>administrador</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve"> <trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source> <source>agreeing encryption for %@…</source>
<target>acordando cifrado para %@…</target> <target>acordando cifrado para %@…</target>
@@ -6716,10 +6624,6 @@ Los servidores de SimpleX no pueden ver tu perfil.</target>
<target>acordando cifrado…</target> <target>acordando cifrado…</target>
<note>chat item text</note> <note>chat item text</note>
</trans-unit> </trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve"> <trans-unit id="always" xml:space="preserve">
<source>always</source> <source>always</source>
<target>siempre</target> <target>siempre</target>
@@ -7050,10 +6954,6 @@ Los servidores de SimpleX no pueden ver tu perfil.</target>
<target>evento ocurrido</target> <target>evento ocurrido</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve"> <trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source> <source>group deleted</source>
<target>grupo eliminado</target> <target>grupo eliminado</target>
@@ -7261,10 +7161,6 @@ Los servidores de SimpleX no pueden ver tu perfil.</target>
<target>propietario</target> <target>propietario</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve"> <trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source> <source>peer-to-peer</source>
<target>p2p</target> <target>p2p</target>
@@ -7315,14 +7211,6 @@ Los servidores de SimpleX no pueden ver tu perfil.</target>
<target>te ha expulsado</target> <target>te ha expulsado</target>
<note>rcv group event chat item</note> <note>rcv group event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve"> <trans-unit id="sec" xml:space="preserve">
<source>sec</source> <source>sec</source>
<target>seg</target> <target>seg</target>
@@ -7463,10 +7351,6 @@ Los servidores de SimpleX no pueden ver tu perfil.</target>
<target>sí</target> <target>sí</target>
<note>pref value</note> <note>pref value</note>
</trans-unit> </trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve"> <trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source> <source>you are invited to group</source>
<target>has sido invitado a un grupo</target> <target>has sido invitado a un grupo</target>
@@ -715,10 +715,6 @@
<target>Salli lähetettyjen viestien peruuttamaton poistaminen. (24 tuntia)</target> <target>Salli lähetettyjen viestien peruuttamaton poistaminen. (24 tuntia)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve"> <trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source> <source>Allow to send files and media.</source>
<target>Salli tiedostojen ja median lähettäminen.</target> <target>Salli tiedostojen ja median lähettäminen.</target>
@@ -1040,10 +1036,6 @@
<target>Tiedostoa ei voi vastaanottaa</target> <target>Tiedostoa ei voi vastaanottaa</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve"> <trans-unit id="Change" xml:space="preserve">
<source>Change</source> <source>Change</source>
<target>Muuta</target> <target>Muuta</target>
@@ -2000,10 +1992,6 @@ This cannot be undone!</source>
<target>Alenna ja avaa keskustelu</target> <target>Alenna ja avaa keskustelu</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve"> <trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source> <source>Download failed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -2109,10 +2097,6 @@ This cannot be undone!</source>
<target>Ota itsetuhoava pääsykoodi käyttöön</target> <target>Ota itsetuhoava pääsykoodi käyttöön</target>
<note>set passcode view</note> <note>set passcode view</note>
</trans-unit> </trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve"> <trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source> <source>Encrypt</source>
<target>Salaa</target> <target>Salaa</target>
@@ -2617,10 +2601,6 @@ This cannot be undone!</source>
<target>Tiedostot ja media ovat tässä ryhmässä kiellettyjä.</target> <target>Tiedostot ja media ovat tässä ryhmässä kiellettyjä.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve"> <trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source> <source>Files and media prohibited!</source>
<target>Tiedostot ja media kielletty!</target> <target>Tiedostot ja media kielletty!</target>
@@ -2684,18 +2664,6 @@ This cannot be undone!</source>
<target>Konsoliin</target> <target>Konsoliin</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve"> <trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source> <source>Found desktop</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -2802,10 +2770,6 @@ This cannot be undone!</source>
<target>Ryhmän jäsenet voivat poistaa lähetetyt viestit peruuttamattomasti. (24 tuntia)</target> <target>Ryhmän jäsenet voivat poistaa lähetetyt viestit peruuttamattomasti. (24 tuntia)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve"> <trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source> <source>Group members can send direct messages.</source>
<target>Ryhmän jäsenet voivat lähettää suoraviestejä.</target> <target>Ryhmän jäsenet voivat lähettää suoraviestejä.</target>
@@ -3635,10 +3599,6 @@ This is your link for group %@!</source>
<target>Verkko ja palvelimet</target> <target>Verkko ja palvelimet</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve"> <trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source> <source>Network settings</source>
<target>Verkkoasetukset</target> <target>Verkkoasetukset</target>
@@ -3747,10 +3707,6 @@ This is your link for group %@!</source>
<target>Ei historiaa</target> <target>Ei historiaa</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve"> <trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source> <source>No permission to record voice message</source>
<target>Ei lupaa ääniviestin tallentamiseen</target> <target>Ei lupaa ääniviestin tallentamiseen</target>
@@ -3955,10 +3911,6 @@ This is your link for group %@!</source>
<source>Or show this code</source> <source>Or show this code</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve"> <trans-unit id="PING count" xml:space="preserve">
<source>PING count</source> <source>PING count</source>
<target>PING-määrä</target> <target>PING-määrä</target>
@@ -4209,10 +4161,6 @@ Error: %@</source>
<target>Estä viestireaktiot.</target> <target>Estä viestireaktiot.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve"> <trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source> <source>Prohibit sending direct messages to members.</source>
<target>Estä suorien viestien lähettäminen jäsenille.</target> <target>Estä suorien viestien lähettäminen jäsenille.</target>
@@ -4340,10 +4288,6 @@ Error: %@</source>
<target>Vastaanotto-osoite vaihdetaan toiseen palvelimeen. Osoitteenmuutos tehdään sen jälkeen, kun lähettäjä tulee verkkoon.</target> <target>Vastaanotto-osoite vaihdetaan toiseen palvelimeen. Osoitteenmuutos tehdään sen jälkeen, kun lähettäjä tulee verkkoon.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Receiving concurrency" xml:space="preserve">
<source>Receiving concurrency</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving file will be stopped." xml:space="preserve"> <trans-unit id="Receiving file will be stopped." xml:space="preserve">
<source>Receiving file will be stopped.</source> <source>Receiving file will be stopped.</source>
<target>Tiedoston vastaanotto pysäytetään.</target> <target>Tiedoston vastaanotto pysäytetään.</target>
@@ -4358,10 +4302,6 @@ Error: %@</source>
<source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source> <source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve"> <trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source> <source>Recipients see updates as you type them.</source>
<target>Vastaanottajat näkevät päivitykset, kun kirjoitat niitä.</target> <target>Vastaanottajat näkevät päivitykset, kun kirjoitat niitä.</target>
@@ -4655,19 +4595,11 @@ Error: %@</source>
<target>Tallenna tervetuloviesti?</target> <target>Tallenna tervetuloviesti?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve"> <trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source> <source>Saved WebRTC ICE servers will be removed</source>
<target>Tallennetut WebRTC ICE -palvelimet poistetaan</target> <target>Tallennetut WebRTC ICE -palvelimet poistetaan</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve"> <trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source> <source>Saved message</source>
<note>message info title</note> <note>message info title</note>
@@ -5081,14 +5013,6 @@ Error: %@</source>
<trans-unit id="SimpleX links" xml:space="preserve"> <trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source> <source>SimpleX links</source>
<target>SimpleX-linkit</target> <target>SimpleX-linkit</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve"> <trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -5889,10 +5813,6 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
<target>Ääniviestit ovat kiellettyjä tässä ryhmässä.</target> <target>Ääniviestit ovat kiellettyjä tässä ryhmässä.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve"> <trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source> <source>Voice messages prohibited!</source>
<target>Ääniviestit kielletty!</target> <target>Ääniviestit kielletty!</target>
@@ -5970,14 +5890,6 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
<target>Kun jaat inkognitoprofiilin jonkun kanssa, tätä profiilia käytetään ryhmissä, joihin tämä sinut kutsuu.</target> <target>Kun jaat inkognitoprofiilin jonkun kanssa, tätä profiilia käytetään ryhmissä, joihin tämä sinut kutsuu.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve"> <trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source> <source>With encrypted files and media.</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -6441,10 +6353,6 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>ylläpitäjä</target> <target>ylläpitäjä</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve"> <trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source> <source>agreeing encryption for %@…</source>
<target>salauksesta sovitaan %@:lle…</target> <target>salauksesta sovitaan %@:lle…</target>
@@ -6455,10 +6363,6 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>hyväksyy salausta…</target> <target>hyväksyy salausta…</target>
<note>chat item text</note> <note>chat item text</note>
</trans-unit> </trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve"> <trans-unit id="always" xml:space="preserve">
<source>always</source> <source>always</source>
<target>aina</target> <target>aina</target>
@@ -6781,10 +6685,6 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>tapahtuma tapahtui</target> <target>tapahtuma tapahtui</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve"> <trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source> <source>group deleted</source>
<target>ryhmä poistettu</target> <target>ryhmä poistettu</target>
@@ -6991,10 +6891,6 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>omistaja</target> <target>omistaja</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve"> <trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source> <source>peer-to-peer</source>
<target>vertais</target> <target>vertais</target>
@@ -7042,14 +6938,6 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>poisti sinut</target> <target>poisti sinut</target>
<note>rcv group event chat item</note> <note>rcv group event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve"> <trans-unit id="sec" xml:space="preserve">
<source>sec</source> <source>sec</source>
<target>sek</target> <target>sek</target>
@@ -7182,10 +7070,6 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>kyllä</target> <target>kyllä</target>
<note>pref value</note> <note>pref value</note>
</trans-unit> </trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve"> <trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source> <source>you are invited to group</source>
<target>sinut on kutsuttu ryhmään</target> <target>sinut on kutsuttu ryhmään</target>
@@ -743,10 +743,6 @@
<target>Autoriser la suppression irréversible de messages envoyés. (24 heures)</target> <target>Autoriser la suppression irréversible de messages envoyés. (24 heures)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve"> <trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source> <source>Allow to send files and media.</source>
<target>Permet l'envoi de fichiers et de médias.</target> <target>Permet l'envoi de fichiers et de médias.</target>
@@ -1087,10 +1083,6 @@
<target>Impossible de recevoir le fichier</target> <target>Impossible de recevoir le fichier</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve"> <trans-unit id="Change" xml:space="preserve">
<source>Change</source> <source>Change</source>
<target>Changer</target> <target>Changer</target>
@@ -2086,10 +2078,6 @@ Cette opération ne peut être annulée !</target>
<target>Rétrograder et ouvrir le chat</target> <target>Rétrograder et ouvrir le chat</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve"> <trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source> <source>Download failed</source>
<target>Échec du téléchargement</target> <target>Échec du téléchargement</target>
@@ -2200,10 +2188,6 @@ Cette opération ne peut être annulée !</target>
<target>Activer le code d'autodestruction</target> <target>Activer le code d'autodestruction</target>
<note>set passcode view</note> <note>set passcode view</note>
</trans-unit> </trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve"> <trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source> <source>Encrypt</source>
<target>Chiffrer</target> <target>Chiffrer</target>
@@ -2729,10 +2713,6 @@ Cette opération ne peut être annulée !</target>
<target>Les fichiers et les médias sont interdits dans ce groupe.</target> <target>Les fichiers et les médias sont interdits dans ce groupe.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve"> <trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source> <source>Files and media prohibited!</source>
<target>Fichiers et médias interdits !</target> <target>Fichiers et médias interdits !</target>
@@ -2798,18 +2778,6 @@ Cette opération ne peut être annulée !</target>
<target>Pour la console</target> <target>Pour la console</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve"> <trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source> <source>Found desktop</source>
<target>Bureau trouvé</target> <target>Bureau trouvé</target>
@@ -2920,10 +2888,6 @@ Cette opération ne peut être annulée !</target>
<target>Les membres du groupe peuvent supprimer de manière irréversible les messages envoyés. (24 heures)</target> <target>Les membres du groupe peuvent supprimer de manière irréversible les messages envoyés. (24 heures)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve"> <trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source> <source>Group members can send direct messages.</source>
<target>Les membres du groupe peuvent envoyer des messages directs.</target> <target>Les membres du groupe peuvent envoyer des messages directs.</target>
@@ -3789,10 +3753,6 @@ Voici votre lien pour le groupe %@ !</target>
<target>Réseau et serveurs</target> <target>Réseau et serveurs</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve"> <trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source> <source>Network settings</source>
<target>Paramètres réseau</target> <target>Paramètres réseau</target>
@@ -3903,10 +3863,6 @@ Voici votre lien pour le groupe %@ !</target>
<target>Aucun historique</target> <target>Aucun historique</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve"> <trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source> <source>No permission to record voice message</source>
<target>Pas l'autorisation d'enregistrer un message vocal</target> <target>Pas l'autorisation d'enregistrer un message vocal</target>
@@ -4121,10 +4077,6 @@ Voici votre lien pour le groupe %@ !</target>
<target>Ou présenter ce code</target> <target>Ou présenter ce code</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve"> <trans-unit id="PING count" xml:space="preserve">
<source>PING count</source> <source>PING count</source>
<target>Nombre de PING</target> <target>Nombre de PING</target>
@@ -4387,10 +4339,6 @@ Erreur: %@</target>
<target>Interdire les réactions aux messages.</target> <target>Interdire les réactions aux messages.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve"> <trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source> <source>Prohibit sending direct messages to members.</source>
<target>Interdire l'envoi de messages directs aux membres.</target> <target>Interdire l'envoi de messages directs aux membres.</target>
@@ -4521,10 +4469,6 @@ Erreur: %@</target>
<target>L'adresse de réception sera changée pour un autre serveur. Le changement d'adresse sera terminé lorsque l'expéditeur sera en ligne.</target> <target>L'adresse de réception sera changée pour un autre serveur. Le changement d'adresse sera terminé lorsque l'expéditeur sera en ligne.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Receiving concurrency" xml:space="preserve">
<source>Receiving concurrency</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving file will be stopped." xml:space="preserve"> <trans-unit id="Receiving file will be stopped." xml:space="preserve">
<source>Receiving file will be stopped.</source> <source>Receiving file will be stopped.</source>
<target>La réception du fichier sera interrompue.</target> <target>La réception du fichier sera interrompue.</target>
@@ -4540,10 +4484,6 @@ Erreur: %@</target>
<target>Historique récent et amélioration du [bot annuaire](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target> <target>Historique récent et amélioration du [bot annuaire](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve"> <trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source> <source>Recipients see updates as you type them.</source>
<target>Les destinataires voient les mises à jour au fur et à mesure que vous leur écrivez.</target> <target>Les destinataires voient les mises à jour au fur et à mesure que vous leur écrivez.</target>
@@ -4844,19 +4784,11 @@ Erreur: %@</target>
<target>Enregistrer le message d'accueil?</target> <target>Enregistrer le message d'accueil?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve"> <trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source> <source>Saved WebRTC ICE servers will be removed</source>
<target>Les serveurs WebRTC ICE sauvegardés seront supprimés</target> <target>Les serveurs WebRTC ICE sauvegardés seront supprimés</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve"> <trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source> <source>Saved message</source>
<target>Message enregistré</target> <target>Message enregistré</target>
@@ -5280,14 +5212,6 @@ Erreur: %@</target>
<trans-unit id="SimpleX links" xml:space="preserve"> <trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source> <source>SimpleX links</source>
<target>Liens SimpleX</target> <target>Liens SimpleX</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve"> <trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -6126,10 +6050,6 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien
<target>Les messages vocaux sont interdits dans ce groupe.</target> <target>Les messages vocaux sont interdits dans ce groupe.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve"> <trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source> <source>Voice messages prohibited!</source>
<target>Messages vocaux interdits !</target> <target>Messages vocaux interdits !</target>
@@ -6210,14 +6130,6 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien
<target>Lorsque vous partagez un profil incognito avec quelqu'un, ce profil sera utilisé pour les groupes auxquels il vous invite.</target> <target>Lorsque vous partagez un profil incognito avec quelqu'un, ce profil sera utilisé pour les groupes auxquels il vous invite.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve"> <trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source> <source>With encrypted files and media.</source>
<target>Avec les fichiers et les médias chiffrés.</target> <target>Avec les fichiers et les médias chiffrés.</target>
@@ -6701,10 +6613,6 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
<target>admin</target> <target>admin</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve"> <trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source> <source>agreeing encryption for %@…</source>
<target>négociation du chiffrement avec %@…</target> <target>négociation du chiffrement avec %@…</target>
@@ -6715,10 +6623,6 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
<target>négociation du chiffrement…</target> <target>négociation du chiffrement…</target>
<note>chat item text</note> <note>chat item text</note>
</trans-unit> </trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve"> <trans-unit id="always" xml:space="preserve">
<source>always</source> <source>always</source>
<target>toujours</target> <target>toujours</target>
@@ -7049,10 +6953,6 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
<target>event happened</target> <target>event happened</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve"> <trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source> <source>group deleted</source>
<target>groupe supprimé</target> <target>groupe supprimé</target>
@@ -7260,10 +7160,6 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
<target>propriétaire</target> <target>propriétaire</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve"> <trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source> <source>peer-to-peer</source>
<target>pair-à-pair</target> <target>pair-à-pair</target>
@@ -7314,14 +7210,6 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
<target>vous a retiré</target> <target>vous a retiré</target>
<note>rcv group event chat item</note> <note>rcv group event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve"> <trans-unit id="sec" xml:space="preserve">
<source>sec</source> <source>sec</source>
<target>sec</target> <target>sec</target>
@@ -7462,10 +7350,6 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
<target>oui</target> <target>oui</target>
<note>pref value</note> <note>pref value</note>
</trans-unit> </trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve"> <trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source> <source>you are invited to group</source>
<target>vous êtes invité·e au groupe</target> <target>vous êtes invité·e au groupe</target>
@@ -737,10 +737,6 @@
<target>Elküldött üzenetek visszafordíthatatlan törlésének engedélyezése. (24 óra)</target> <target>Elküldött üzenetek visszafordíthatatlan törlésének engedélyezése. (24 óra)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve"> <trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source> <source>Allow to send files and media.</source>
<target>Fájlok és médiatartalom küldésének engedélyezése.</target> <target>Fájlok és médiatartalom küldésének engedélyezése.</target>
@@ -1076,10 +1072,6 @@
<target>Nem lehet fogadni a fájlt</target> <target>Nem lehet fogadni a fájlt</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve"> <trans-unit id="Change" xml:space="preserve">
<source>Change</source> <source>Change</source>
<target>Változtatás</target> <target>Változtatás</target>
@@ -2068,10 +2060,6 @@ Ezt nem vonható vissza!</target>
<target>Visszatérés a korábbi verzióra és a csevegés megnyitása</target> <target>Visszatérés a korábbi verzióra és a csevegés megnyitása</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve"> <trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source> <source>Download failed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -2178,10 +2166,6 @@ Ezt nem vonható vissza!</target>
<target>Önmegsemmisítő jelkód engedélyezése</target> <target>Önmegsemmisítő jelkód engedélyezése</target>
<note>set passcode view</note> <note>set passcode view</note>
</trans-unit> </trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve"> <trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source> <source>Encrypt</source>
<target>Titkosít</target> <target>Titkosít</target>
@@ -2700,10 +2684,6 @@ Ezt nem vonható vissza!</target>
<target>A fájlok- és a médiatartalom küldése le van tiltva ebben a csoportban.</target> <target>A fájlok- és a médiatartalom küldése le van tiltva ebben a csoportban.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve"> <trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source> <source>Files and media prohibited!</source>
<target>A fájlok- és a médiatartalom küldése le van tiltva!</target> <target>A fájlok- és a médiatartalom küldése le van tiltva!</target>
@@ -2767,18 +2747,6 @@ Ezt nem vonható vissza!</target>
<target>Konzolhoz</target> <target>Konzolhoz</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve"> <trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source> <source>Found desktop</source>
<target>Megtalált számítógép</target> <target>Megtalált számítógép</target>
@@ -2889,10 +2857,6 @@ Ezt nem vonható vissza!</target>
<target>Csoporttagok visszafordíthatatlanul törölhetik az elküldött üzeneteket. (24 óra)</target> <target>Csoporttagok visszafordíthatatlanul törölhetik az elküldött üzeneteket. (24 óra)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve"> <trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source> <source>Group members can send direct messages.</source>
<target>Csoporttagok küldhetnek közvetlen üzeneteket.</target> <target>Csoporttagok küldhetnek közvetlen üzeneteket.</target>
@@ -3743,10 +3707,6 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
<target>Hálózat és kiszolgálók</target> <target>Hálózat és kiszolgálók</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve"> <trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source> <source>Network settings</source>
<target>Hálózati beállítások</target> <target>Hálózati beállítások</target>
@@ -3857,10 +3817,6 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
<target>Nincsenek előzmények</target> <target>Nincsenek előzmények</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve"> <trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source> <source>No permission to record voice message</source>
<target>Nincs engedély a hangüzenet rögzítésére</target> <target>Nincs engedély a hangüzenet rögzítésére</target>
@@ -4072,10 +4028,6 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
<target>Vagy mutassa meg ezt a kódot</target> <target>Vagy mutassa meg ezt a kódot</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve"> <trans-unit id="PING count" xml:space="preserve">
<source>PING count</source> <source>PING count</source>
<target>PING számláló</target> <target>PING számláló</target>
@@ -4335,10 +4287,6 @@ Hiba: %@</target>
<target>Az üzenetreakciók tiltása.</target> <target>Az üzenetreakciók tiltása.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve"> <trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source> <source>Prohibit sending direct messages to members.</source>
<target>Közvetlen üzenetek küldésének letiltása tagok részére.</target> <target>Közvetlen üzenetek küldésének letiltása tagok részére.</target>
@@ -4467,10 +4415,6 @@ Hiba: %@</target>
<target>A fogadó cím egy másik kiszolgálóra változik. A címváltoztatás a feladó online állapotba kerülése után fejeződik be.</target> <target>A fogadó cím egy másik kiszolgálóra változik. A címváltoztatás a feladó online állapotba kerülése után fejeződik be.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Receiving concurrency" xml:space="preserve">
<source>Receiving concurrency</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving file will be stopped." xml:space="preserve"> <trans-unit id="Receiving file will be stopped." xml:space="preserve">
<source>Receiving file will be stopped.</source> <source>Receiving file will be stopped.</source>
<target>A fájl fogadása leállt.</target> <target>A fájl fogadása leállt.</target>
@@ -4486,10 +4430,6 @@ Hiba: %@</target>
<target>Legutóbbi előzmények és továbbfejlesztett [könyvtárbot] (simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2TxW3dfMfxy 3%23%2F%3Fv%3D1-2% 26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gloncbqjek4gloncbqjek.</target> <target>Legutóbbi előzmények és továbbfejlesztett [könyvtárbot] (simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2TxW3dfMfxy 3%23%2F%3Fv%3D1-2% 26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gloncbqjek4gloncbqjek.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve"> <trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source> <source>Recipients see updates as you type them.</source>
<target>A címzettek a beírás közben látják a frissítéseket.</target> <target>A címzettek a beírás közben látják a frissítéseket.</target>
@@ -4786,19 +4726,11 @@ Hiba: %@</target>
<target>Üdvözlőszöveg mentése?</target> <target>Üdvözlőszöveg mentése?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve"> <trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source> <source>Saved WebRTC ICE servers will be removed</source>
<target>A mentett WebRTC ICE kiszolgálók eltávolításra kerülnek</target> <target>A mentett WebRTC ICE kiszolgálók eltávolításra kerülnek</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve"> <trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source> <source>Saved message</source>
<target>Mentett üzenet</target> <target>Mentett üzenet</target>
@@ -5220,14 +5152,6 @@ Hiba: %@</target>
<trans-unit id="SimpleX links" xml:space="preserve"> <trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source> <source>SimpleX links</source>
<target>SimpleX hivatkozások</target> <target>SimpleX hivatkozások</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve"> <trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -6057,10 +5981,6 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola
<target>A hangüzenetek küldése le van tiltva ebben a csoportban.</target> <target>A hangüzenetek küldése le van tiltva ebben a csoportban.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve"> <trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source> <source>Voice messages prohibited!</source>
<target>A hangüzenetek le vannak tilva!</target> <target>A hangüzenetek le vannak tilva!</target>
@@ -6139,14 +6059,6 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola
<target>Inkognitóprofil megosztása esetén a rendszer azt a profilt fogja használni azokhoz a csoportokhoz, amelyekbe meghívást kapott.</target> <target>Inkognitóprofil megosztása esetén a rendszer azt a profilt fogja használni azokhoz a csoportokhoz, amelyekbe meghívást kapott.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve"> <trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source> <source>With encrypted files and media.</source>
<target>Titkosított fájlokkal és médiatartalommal.</target> <target>Titkosított fájlokkal és médiatartalommal.</target>
@@ -6628,10 +6540,6 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
<target>admin</target> <target>admin</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve"> <trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source> <source>agreeing encryption for %@…</source>
<target>titkosítás jóváhagyása %@ számára…</target> <target>titkosítás jóváhagyása %@ számára…</target>
@@ -6642,10 +6550,6 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
<target>titkosítás elfogadása…</target> <target>titkosítás elfogadása…</target>
<note>chat item text</note> <note>chat item text</note>
</trans-unit> </trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve"> <trans-unit id="always" xml:space="preserve">
<source>always</source> <source>always</source>
<target>mindig</target> <target>mindig</target>
@@ -6976,10 +6880,6 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
<target>esemény történt</target> <target>esemény történt</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve"> <trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source> <source>group deleted</source>
<target>a csoport törölve</target> <target>a csoport törölve</target>
@@ -7187,10 +7087,6 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
<target>tulajdonos</target> <target>tulajdonos</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve"> <trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source> <source>peer-to-peer</source>
<target>ponttól-pontig</target> <target>ponttól-pontig</target>
@@ -7240,14 +7136,6 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
<target>eltávolítottak</target> <target>eltávolítottak</target>
<note>rcv group event chat item</note> <note>rcv group event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve"> <trans-unit id="sec" xml:space="preserve">
<source>sec</source> <source>sec</source>
<target>mp</target> <target>mp</target>
@@ -7387,10 +7275,6 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
<target>igen</target> <target>igen</target>
<note>pref value</note> <note>pref value</note>
</trans-unit> </trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve"> <trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source> <source>you are invited to group</source>
<target>meghívást kapott a csoportba</target> <target>meghívást kapott a csoportba</target>
@@ -743,10 +743,6 @@
<target>Permetti di eliminare irreversibilmente i messaggi inviati. (24 ore)</target> <target>Permetti di eliminare irreversibilmente i messaggi inviati. (24 ore)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve"> <trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source> <source>Allow to send files and media.</source>
<target>Consenti l'invio di file e contenuti multimediali.</target> <target>Consenti l'invio di file e contenuti multimediali.</target>
@@ -1087,10 +1083,6 @@
<target>Impossibile ricevere il file</target> <target>Impossibile ricevere il file</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve"> <trans-unit id="Change" xml:space="preserve">
<source>Change</source> <source>Change</source>
<target>Cambia</target> <target>Cambia</target>
@@ -2086,10 +2078,6 @@ Non è reversibile!</target>
<target>Esegui downgrade e apri chat</target> <target>Esegui downgrade e apri chat</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve"> <trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source> <source>Download failed</source>
<target>Scaricamento fallito</target> <target>Scaricamento fallito</target>
@@ -2200,10 +2188,6 @@ Non è reversibile!</target>
<target>Attiva il codice di autodistruzione</target> <target>Attiva il codice di autodistruzione</target>
<note>set passcode view</note> <note>set passcode view</note>
</trans-unit> </trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve"> <trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source> <source>Encrypt</source>
<target>Crittografare</target> <target>Crittografare</target>
@@ -2729,10 +2713,6 @@ Non è reversibile!</target>
<target>File e contenuti multimediali sono vietati in questo gruppo.</target> <target>File e contenuti multimediali sono vietati in questo gruppo.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve"> <trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source> <source>Files and media prohibited!</source>
<target>File e contenuti multimediali vietati!</target> <target>File e contenuti multimediali vietati!</target>
@@ -2798,18 +2778,6 @@ Non è reversibile!</target>
<target>Per console</target> <target>Per console</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve"> <trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source> <source>Found desktop</source>
<target>Desktop trovato</target> <target>Desktop trovato</target>
@@ -2920,10 +2888,6 @@ Non è reversibile!</target>
<target>I membri del gruppo possono eliminare irreversibilmente i messaggi inviati. (24 ore)</target> <target>I membri del gruppo possono eliminare irreversibilmente i messaggi inviati. (24 ore)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve"> <trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source> <source>Group members can send direct messages.</source>
<target>I membri del gruppo possono inviare messaggi diretti.</target> <target>I membri del gruppo possono inviare messaggi diretti.</target>
@@ -3789,10 +3753,6 @@ Questo è il tuo link per il gruppo %@!</target>
<target>Rete e server</target> <target>Rete e server</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve"> <trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source> <source>Network settings</source>
<target>Impostazioni di rete</target> <target>Impostazioni di rete</target>
@@ -3903,10 +3863,6 @@ Questo è il tuo link per il gruppo %@!</target>
<target>Nessuna cronologia</target> <target>Nessuna cronologia</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve"> <trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source> <source>No permission to record voice message</source>
<target>Nessuna autorizzazione per registrare messaggi vocali</target> <target>Nessuna autorizzazione per registrare messaggi vocali</target>
@@ -4121,10 +4077,6 @@ Questo è il tuo link per il gruppo %@!</target>
<target>O mostra questo codice</target> <target>O mostra questo codice</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve"> <trans-unit id="PING count" xml:space="preserve">
<source>PING count</source> <source>PING count</source>
<target>Conteggio PING</target> <target>Conteggio PING</target>
@@ -4387,10 +4339,6 @@ Errore: %@</target>
<target>Proibisci le reazioni ai messaggi.</target> <target>Proibisci le reazioni ai messaggi.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve"> <trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source> <source>Prohibit sending direct messages to members.</source>
<target>Proibisci l'invio di messaggi diretti ai membri.</target> <target>Proibisci l'invio di messaggi diretti ai membri.</target>
@@ -4521,10 +4469,6 @@ Errore: %@</target>
<target>L'indirizzo di ricezione verrà cambiato in un server diverso. La modifica dell'indirizzo verrà completata dopo che il mittente sarà in linea.</target> <target>L'indirizzo di ricezione verrà cambiato in un server diverso. La modifica dell'indirizzo verrà completata dopo che il mittente sarà in linea.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Receiving concurrency" xml:space="preserve">
<source>Receiving concurrency</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving file will be stopped." xml:space="preserve"> <trans-unit id="Receiving file will be stopped." xml:space="preserve">
<source>Receiving file will be stopped.</source> <source>Receiving file will be stopped.</source>
<target>La ricezione del file verrà interrotta.</target> <target>La ricezione del file verrà interrotta.</target>
@@ -4540,10 +4484,6 @@ Errore: %@</target>
<target>Cronologia recente e [bot della directory](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) migliorato.</target> <target>Cronologia recente e [bot della directory](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) migliorato.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve"> <trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source> <source>Recipients see updates as you type them.</source>
<target>I destinatari vedono gli aggiornamenti mentre li digiti.</target> <target>I destinatari vedono gli aggiornamenti mentre li digiti.</target>
@@ -4844,19 +4784,11 @@ Errore: %@</target>
<target>Salvare il messaggio di benvenuto?</target> <target>Salvare il messaggio di benvenuto?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve"> <trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source> <source>Saved WebRTC ICE servers will be removed</source>
<target>I server WebRTC ICE salvati verranno rimossi</target> <target>I server WebRTC ICE salvati verranno rimossi</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve"> <trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source> <source>Saved message</source>
<target>Messaggio salvato</target> <target>Messaggio salvato</target>
@@ -5280,14 +5212,6 @@ Errore: %@</target>
<trans-unit id="SimpleX links" xml:space="preserve"> <trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source> <source>SimpleX links</source>
<target>Link di SimpleX</target> <target>Link di SimpleX</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve"> <trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -6126,10 +6050,6 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
<target>I messaggi vocali sono vietati in questo gruppo.</target> <target>I messaggi vocali sono vietati in questo gruppo.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve"> <trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source> <source>Voice messages prohibited!</source>
<target>Messaggi vocali vietati!</target> <target>Messaggi vocali vietati!</target>
@@ -6210,14 +6130,6 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
<target>Quando condividi un profilo in incognito con qualcuno, questo profilo verrà utilizzato per i gruppi a cui ti invitano.</target> <target>Quando condividi un profilo in incognito con qualcuno, questo profilo verrà utilizzato per i gruppi a cui ti invitano.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve"> <trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source> <source>With encrypted files and media.</source>
<target>Con file e multimediali criptati.</target> <target>Con file e multimediali criptati.</target>
@@ -6701,10 +6613,6 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
<target>amministratore</target> <target>amministratore</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve"> <trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source> <source>agreeing encryption for %@…</source>
<target>concordando la crittografia per %@…</target> <target>concordando la crittografia per %@…</target>
@@ -6715,10 +6623,6 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
<target>concordando la crittografia…</target> <target>concordando la crittografia…</target>
<note>chat item text</note> <note>chat item text</note>
</trans-unit> </trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve"> <trans-unit id="always" xml:space="preserve">
<source>always</source> <source>always</source>
<target>sempre</target> <target>sempre</target>
@@ -7049,10 +6953,6 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
<target>evento accaduto</target> <target>evento accaduto</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve"> <trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source> <source>group deleted</source>
<target>gruppo eliminato</target> <target>gruppo eliminato</target>
@@ -7260,10 +7160,6 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
<target>proprietario</target> <target>proprietario</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve"> <trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source> <source>peer-to-peer</source>
<target>peer-to-peer</target> <target>peer-to-peer</target>
@@ -7314,14 +7210,6 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
<target>ti ha rimosso/a</target> <target>ti ha rimosso/a</target>
<note>rcv group event chat item</note> <note>rcv group event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve"> <trans-unit id="sec" xml:space="preserve">
<source>sec</source> <source>sec</source>
<target>sec</target> <target>sec</target>
@@ -7462,10 +7350,6 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
<target>sì</target> <target>sì</target>
<note>pref value</note> <note>pref value</note>
</trans-unit> </trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve"> <trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source> <source>you are invited to group</source>
<target>sei stato/a invitato/a al gruppo</target> <target>sei stato/a invitato/a al gruppo</target>
@@ -719,10 +719,6 @@
<target>送信済みメッセージの永久削除を許可する。</target> <target>送信済みメッセージの永久削除を許可する。</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve"> <trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source> <source>Allow to send files and media.</source>
<target>ファイルやメディアの送信を許可する。</target> <target>ファイルやメディアの送信を許可する。</target>
@@ -1046,10 +1042,6 @@
<target>ファイル受信ができません</target> <target>ファイル受信ができません</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve"> <trans-unit id="Change" xml:space="preserve">
<source>Change</source> <source>Change</source>
<target>変更</target> <target>変更</target>
@@ -2006,10 +1998,6 @@ This cannot be undone!</source>
<target>ダウングレードしてチャットを開く</target> <target>ダウングレードしてチャットを開く</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve"> <trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source> <source>Download failed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -2115,10 +2103,6 @@ This cannot be undone!</source>
<target>自己破壊パスコードを有効にする</target> <target>自己破壊パスコードを有効にする</target>
<note>set passcode view</note> <note>set passcode view</note>
</trans-unit> </trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve"> <trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source> <source>Encrypt</source>
<target>暗号化する</target> <target>暗号化する</target>
@@ -2624,10 +2608,6 @@ This cannot be undone!</source>
<target>このグループでは、ファイルとメディアは禁止されています。</target> <target>このグループでは、ファイルとメディアは禁止されています。</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve"> <trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source> <source>Files and media prohibited!</source>
<target>ファイルとメディアは禁止されています!</target> <target>ファイルとメディアは禁止されています!</target>
@@ -2691,18 +2671,6 @@ This cannot be undone!</source>
<target>コンソール</target> <target>コンソール</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve"> <trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source> <source>Found desktop</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -2809,10 +2777,6 @@ This cannot be undone!</source>
<target>グループのメンバーがメッセージを完全削除することができます。</target> <target>グループのメンバーがメッセージを完全削除することができます。</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve"> <trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source> <source>Group members can send direct messages.</source>
<target>グループのメンバーがダイレクトメッセージを送信できます。</target> <target>グループのメンバーがダイレクトメッセージを送信できます。</target>
@@ -3641,10 +3605,6 @@ This is your link for group %@!</source>
<target>ネットワークとサーバ</target> <target>ネットワークとサーバ</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve"> <trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source> <source>Network settings</source>
<target>ネットワーク設定</target> <target>ネットワーク設定</target>
@@ -3754,10 +3714,6 @@ This is your link for group %@!</source>
<target>履歴はありません</target> <target>履歴はありません</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve"> <trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source> <source>No permission to record voice message</source>
<target>音声メッセージを録音する権限がありません</target> <target>音声メッセージを録音する権限がありません</target>
@@ -3963,10 +3919,6 @@ This is your link for group %@!</source>
<source>Or show this code</source> <source>Or show this code</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve"> <trans-unit id="PING count" xml:space="preserve">
<source>PING count</source> <source>PING count</source>
<target>PING回数</target> <target>PING回数</target>
@@ -4217,10 +4169,6 @@ Error: %@</source>
<target>メッセージへのリアクションは禁止されています。</target> <target>メッセージへのリアクションは禁止されています。</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve"> <trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source> <source>Prohibit sending direct messages to members.</source>
<target>メンバー間のダイレクトメッセージを使用禁止にする。</target> <target>メンバー間のダイレクトメッセージを使用禁止にする。</target>
@@ -4347,10 +4295,6 @@ Error: %@</source>
<target>開発中の機能です!相手のクライアントが4.2でなければ機能しません。アドレス変更が完了すると、会話にメッセージが出ます。連絡相手 (またはグループのメンバー) からメッセージを受信できないかをご確認ください。</target> <target>開発中の機能です!相手のクライアントが4.2でなければ機能しません。アドレス変更が完了すると、会話にメッセージが出ます。連絡相手 (またはグループのメンバー) からメッセージを受信できないかをご確認ください。</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Receiving concurrency" xml:space="preserve">
<source>Receiving concurrency</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving file will be stopped." xml:space="preserve"> <trans-unit id="Receiving file will be stopped." xml:space="preserve">
<source>Receiving file will be stopped.</source> <source>Receiving file will be stopped.</source>
<target>ファイルの受信を停止します。</target> <target>ファイルの受信を停止します。</target>
@@ -4365,10 +4309,6 @@ Error: %@</source>
<source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source> <source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve"> <trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source> <source>Recipients see updates as you type them.</source>
<target>受信者には、入力時に更新内容が表示されます。</target> <target>受信者には、入力時に更新内容が表示されます。</target>
@@ -4662,19 +4602,11 @@ Error: %@</source>
<target>ウェルカムメッセージを保存しますか?</target> <target>ウェルカムメッセージを保存しますか?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve"> <trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source> <source>Saved WebRTC ICE servers will be removed</source>
<target>保存されたWebRTC ICEサーバは削除されます</target> <target>保存されたWebRTC ICEサーバは削除されます</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve"> <trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source> <source>Saved message</source>
<note>message info title</note> <note>message info title</note>
@@ -5081,14 +5013,6 @@ Error: %@</source>
<trans-unit id="SimpleX links" xml:space="preserve"> <trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source> <source>SimpleX links</source>
<target>SimpleXリンク</target> <target>SimpleXリンク</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve"> <trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -5889,10 +5813,6 @@ To connect, please ask your contact to create another connection link and check
<target>このグループでは音声メッセージが使用禁止です。</target> <target>このグループでは音声メッセージが使用禁止です。</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve"> <trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source> <source>Voice messages prohibited!</source>
<target>音声メッセージは使用禁止です!</target> <target>音声メッセージは使用禁止です!</target>
@@ -5970,14 +5890,6 @@ To connect, please ask your contact to create another connection link and check
<target>連絡相手にシークレットモードのプロフィールを共有すると、その連絡相手に招待されたグループでも同じプロフィールが使われます。</target> <target>連絡相手にシークレットモードのプロフィールを共有すると、その連絡相手に招待されたグループでも同じプロフィールが使われます。</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve"> <trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source> <source>With encrypted files and media.</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -6441,10 +6353,6 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>管理者</target> <target>管理者</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve"> <trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source> <source>agreeing encryption for %@…</source>
<target>%@の暗号化に同意しています…</target> <target>%@の暗号化に同意しています…</target>
@@ -6455,10 +6363,6 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>暗号化に同意しています…</target> <target>暗号化に同意しています…</target>
<note>chat item text</note> <note>chat item text</note>
</trans-unit> </trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve"> <trans-unit id="always" xml:space="preserve">
<source>always</source> <source>always</source>
<target>常に</target> <target>常に</target>
@@ -6781,10 +6685,6 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>イベント発生</target> <target>イベント発生</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve"> <trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source> <source>group deleted</source>
<target>グループ削除済み</target> <target>グループ削除済み</target>
@@ -6991,10 +6891,6 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>オーナー</target> <target>オーナー</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve"> <trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source> <source>peer-to-peer</source>
<target>P2P</target> <target>P2P</target>
@@ -7042,14 +6938,6 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>あなたを除名しました</target> <target>あなたを除名しました</target>
<note>rcv group event chat item</note> <note>rcv group event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve"> <trans-unit id="sec" xml:space="preserve">
<source>sec</source> <source>sec</source>
<target>秒</target> <target>秒</target>
@@ -7182,10 +7070,6 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>はい</target> <target>はい</target>
<note>pref value</note> <note>pref value</note>
</trans-unit> </trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve"> <trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source> <source>you are invited to group</source>
<target>グループ招待が届きました</target> <target>グループ招待が届きました</target>
@@ -743,10 +743,6 @@
<target>Sta toe om verzonden berichten onomkeerbaar te verwijderen. (24 uur)</target> <target>Sta toe om verzonden berichten onomkeerbaar te verwijderen. (24 uur)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve"> <trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source> <source>Allow to send files and media.</source>
<target>Sta toe om bestanden en media te verzenden.</target> <target>Sta toe om bestanden en media te verzenden.</target>
@@ -1087,10 +1083,6 @@
<target>Kan bestand niet ontvangen</target> <target>Kan bestand niet ontvangen</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve"> <trans-unit id="Change" xml:space="preserve">
<source>Change</source> <source>Change</source>
<target>Veranderen</target> <target>Veranderen</target>
@@ -2086,10 +2078,6 @@ Dit kan niet ongedaan gemaakt worden!</target>
<target>Downgraden en chat openen</target> <target>Downgraden en chat openen</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve"> <trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source> <source>Download failed</source>
<target>Download mislukt</target> <target>Download mislukt</target>
@@ -2200,10 +2188,6 @@ Dit kan niet ongedaan gemaakt worden!</target>
<target>Zelfvernietigings wachtwoord inschakelen</target> <target>Zelfvernietigings wachtwoord inschakelen</target>
<note>set passcode view</note> <note>set passcode view</note>
</trans-unit> </trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve"> <trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source> <source>Encrypt</source>
<target>Versleutelen</target> <target>Versleutelen</target>
@@ -2729,10 +2713,6 @@ Dit kan niet ongedaan gemaakt worden!</target>
<target>Bestanden en media zijn verboden in deze groep.</target> <target>Bestanden en media zijn verboden in deze groep.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve"> <trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source> <source>Files and media prohibited!</source>
<target>Bestanden en media verboden!</target> <target>Bestanden en media verboden!</target>
@@ -2798,18 +2778,6 @@ Dit kan niet ongedaan gemaakt worden!</target>
<target>Voor console</target> <target>Voor console</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve"> <trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source> <source>Found desktop</source>
<target>Desktop gevonden</target> <target>Desktop gevonden</target>
@@ -2920,10 +2888,6 @@ Dit kan niet ongedaan gemaakt worden!</target>
<target>Groepsleden kunnen verzonden berichten onherroepelijk verwijderen. (24 uur)</target> <target>Groepsleden kunnen verzonden berichten onherroepelijk verwijderen. (24 uur)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve"> <trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source> <source>Group members can send direct messages.</source>
<target>Groepsleden kunnen directe berichten sturen.</target> <target>Groepsleden kunnen directe berichten sturen.</target>
@@ -3789,10 +3753,6 @@ Dit is jouw link voor groep %@!</target>
<target>Netwerk &amp; servers</target> <target>Netwerk &amp; servers</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve"> <trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source> <source>Network settings</source>
<target>Netwerk instellingen</target> <target>Netwerk instellingen</target>
@@ -3903,10 +3863,6 @@ Dit is jouw link voor groep %@!</target>
<target>Geen geschiedenis</target> <target>Geen geschiedenis</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve"> <trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source> <source>No permission to record voice message</source>
<target>Geen toestemming om spraakbericht op te nemen</target> <target>Geen toestemming om spraakbericht op te nemen</target>
@@ -4121,10 +4077,6 @@ Dit is jouw link voor groep %@!</target>
<target>Of laat deze code zien</target> <target>Of laat deze code zien</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve"> <trans-unit id="PING count" xml:space="preserve">
<source>PING count</source> <source>PING count</source>
<target>PING count</target> <target>PING count</target>
@@ -4387,10 +4339,6 @@ Fout: %@</target>
<target>Berichten reacties verbieden.</target> <target>Berichten reacties verbieden.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve"> <trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source> <source>Prohibit sending direct messages to members.</source>
<target>Verbied het sturen van directe berichten naar leden.</target> <target>Verbied het sturen van directe berichten naar leden.</target>
@@ -4521,10 +4469,6 @@ Fout: %@</target>
<target>Het ontvangstadres wordt gewijzigd naar een andere server. Adres wijziging wordt voltooid nadat de afzender online is.</target> <target>Het ontvangstadres wordt gewijzigd naar een andere server. Adres wijziging wordt voltooid nadat de afzender online is.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Receiving concurrency" xml:space="preserve">
<source>Receiving concurrency</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving file will be stopped." xml:space="preserve"> <trans-unit id="Receiving file will be stopped." xml:space="preserve">
<source>Receiving file will be stopped.</source> <source>Receiving file will be stopped.</source>
<target>Het ontvangen van het bestand wordt gestopt.</target> <target>Het ontvangen van het bestand wordt gestopt.</target>
@@ -4540,10 +4484,6 @@ Fout: %@</target>
<target>Recente geschiedenis en verbeterde [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target> <target>Recente geschiedenis en verbeterde [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve"> <trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source> <source>Recipients see updates as you type them.</source>
<target>Ontvangers zien updates terwijl u ze typt.</target> <target>Ontvangers zien updates terwijl u ze typt.</target>
@@ -4844,19 +4784,11 @@ Fout: %@</target>
<target>Welkomst bericht opslaan?</target> <target>Welkomst bericht opslaan?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve"> <trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source> <source>Saved WebRTC ICE servers will be removed</source>
<target>Opgeslagen WebRTC ICE servers worden verwijderd</target> <target>Opgeslagen WebRTC ICE servers worden verwijderd</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve"> <trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source> <source>Saved message</source>
<target>Opgeslagen bericht</target> <target>Opgeslagen bericht</target>
@@ -5280,14 +5212,6 @@ Fout: %@</target>
<trans-unit id="SimpleX links" xml:space="preserve"> <trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source> <source>SimpleX links</source>
<target>SimpleX links</target> <target>SimpleX links</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve"> <trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -6126,10 +6050,6 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak
<target>Spraak berichten zijn verboden in deze groep.</target> <target>Spraak berichten zijn verboden in deze groep.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve"> <trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source> <source>Voice messages prohibited!</source>
<target>Spraak berichten verboden!</target> <target>Spraak berichten verboden!</target>
@@ -6210,14 +6130,6 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak
<target>Wanneer je een incognito profiel met iemand deelt, wordt dit profiel gebruikt voor de groepen waarvoor ze je uitnodigen.</target> <target>Wanneer je een incognito profiel met iemand deelt, wordt dit profiel gebruikt voor de groepen waarvoor ze je uitnodigen.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve"> <trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source> <source>With encrypted files and media.</source>
<target>Met versleutelde bestanden en media.</target> <target>Met versleutelde bestanden en media.</target>
@@ -6701,10 +6613,6 @@ SimpleX servers kunnen uw profiel niet zien.</target>
<target>Beheerder</target> <target>Beheerder</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve"> <trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source> <source>agreeing encryption for %@…</source>
<target>versleuteling overeenkomen voor %@…</target> <target>versleuteling overeenkomen voor %@…</target>
@@ -6715,10 +6623,6 @@ SimpleX servers kunnen uw profiel niet zien.</target>
<target>versleuteling overeenkomen…</target> <target>versleuteling overeenkomen…</target>
<note>chat item text</note> <note>chat item text</note>
</trans-unit> </trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve"> <trans-unit id="always" xml:space="preserve">
<source>always</source> <source>always</source>
<target>altijd</target> <target>altijd</target>
@@ -7049,10 +6953,6 @@ SimpleX servers kunnen uw profiel niet zien.</target>
<target>gebeurtenis gebeurd</target> <target>gebeurtenis gebeurd</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve"> <trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source> <source>group deleted</source>
<target>groep verwijderd</target> <target>groep verwijderd</target>
@@ -7260,10 +7160,6 @@ SimpleX servers kunnen uw profiel niet zien.</target>
<target>Eigenaar</target> <target>Eigenaar</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve"> <trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source> <source>peer-to-peer</source>
<target>peer-to-peer</target> <target>peer-to-peer</target>
@@ -7314,14 +7210,6 @@ SimpleX servers kunnen uw profiel niet zien.</target>
<target>heeft je verwijderd</target> <target>heeft je verwijderd</target>
<note>rcv group event chat item</note> <note>rcv group event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve"> <trans-unit id="sec" xml:space="preserve">
<source>sec</source> <source>sec</source>
<target>sec</target> <target>sec</target>
@@ -7462,10 +7350,6 @@ SimpleX servers kunnen uw profiel niet zien.</target>
<target>Ja</target> <target>Ja</target>
<note>pref value</note> <note>pref value</note>
</trans-unit> </trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve"> <trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source> <source>you are invited to group</source>
<target>je bent uitgenodigd voor de groep</target> <target>je bent uitgenodigd voor de groep</target>
@@ -743,10 +743,6 @@
<target>Zezwól na nieodwracalne usunięcie wysłanych wiadomości. (24 godziny)</target> <target>Zezwól na nieodwracalne usunięcie wysłanych wiadomości. (24 godziny)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve"> <trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source> <source>Allow to send files and media.</source>
<target>Pozwól na wysyłanie plików i mediów.</target> <target>Pozwól na wysyłanie plików i mediów.</target>
@@ -1087,10 +1083,6 @@
<target>Nie można odebrać pliku</target> <target>Nie można odebrać pliku</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve"> <trans-unit id="Change" xml:space="preserve">
<source>Change</source> <source>Change</source>
<target>Zmień</target> <target>Zmień</target>
@@ -2086,10 +2078,6 @@ To nie może być cofnięte!</target>
<target>Obniż wersję i otwórz czat</target> <target>Obniż wersję i otwórz czat</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve"> <trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source> <source>Download failed</source>
<target>Pobieranie nie udane</target> <target>Pobieranie nie udane</target>
@@ -2200,10 +2188,6 @@ To nie może być cofnięte!</target>
<target>Włącz pin samodestrukcji</target> <target>Włącz pin samodestrukcji</target>
<note>set passcode view</note> <note>set passcode view</note>
</trans-unit> </trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve"> <trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source> <source>Encrypt</source>
<target>Szyfruj</target> <target>Szyfruj</target>
@@ -2729,10 +2713,6 @@ To nie może być cofnięte!</target>
<target>Pliki i media są zabronione w tej grupie.</target> <target>Pliki i media są zabronione w tej grupie.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve"> <trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source> <source>Files and media prohibited!</source>
<target>Pliki i media zabronione!</target> <target>Pliki i media zabronione!</target>
@@ -2798,18 +2778,6 @@ To nie może być cofnięte!</target>
<target>Dla konsoli</target> <target>Dla konsoli</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve"> <trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source> <source>Found desktop</source>
<target>Znaleziono komputer</target> <target>Znaleziono komputer</target>
@@ -2920,10 +2888,6 @@ To nie może być cofnięte!</target>
<target>Członkowie grupy mogą nieodwracalnie usuwać wysłane wiadomości. (24 godziny)</target> <target>Członkowie grupy mogą nieodwracalnie usuwać wysłane wiadomości. (24 godziny)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve"> <trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source> <source>Group members can send direct messages.</source>
<target>Członkowie grupy mogą wysyłać bezpośrednie wiadomości.</target> <target>Członkowie grupy mogą wysyłać bezpośrednie wiadomości.</target>
@@ -3789,10 +3753,6 @@ To jest twój link do grupy %@!</target>
<target>Sieć i serwery</target> <target>Sieć i serwery</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve"> <trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source> <source>Network settings</source>
<target>Ustawienia sieci</target> <target>Ustawienia sieci</target>
@@ -3903,10 +3863,6 @@ To jest twój link do grupy %@!</target>
<target>Brak historii</target> <target>Brak historii</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve"> <trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source> <source>No permission to record voice message</source>
<target>Brak uprawnień do nagrywania wiadomości głosowej</target> <target>Brak uprawnień do nagrywania wiadomości głosowej</target>
@@ -4121,10 +4077,6 @@ To jest twój link do grupy %@!</target>
<target>Lub pokaż ten kod</target> <target>Lub pokaż ten kod</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve"> <trans-unit id="PING count" xml:space="preserve">
<source>PING count</source> <source>PING count</source>
<target>Liczba PINGÓW</target> <target>Liczba PINGÓW</target>
@@ -4387,10 +4339,6 @@ Błąd: %@</target>
<target>Zabroń reakcje wiadomości.</target> <target>Zabroń reakcje wiadomości.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve"> <trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source> <source>Prohibit sending direct messages to members.</source>
<target>Zabroń wysyłania bezpośrednich wiadomości do członków.</target> <target>Zabroń wysyłania bezpośrednich wiadomości do członków.</target>
@@ -4521,10 +4469,6 @@ Błąd: %@</target>
<target>Adres odbiorczy zostanie zmieniony na inny serwer. Zmiana adresu zostanie zakończona gdy nadawca będzie online.</target> <target>Adres odbiorczy zostanie zmieniony na inny serwer. Zmiana adresu zostanie zakończona gdy nadawca będzie online.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Receiving concurrency" xml:space="preserve">
<source>Receiving concurrency</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving file will be stopped." xml:space="preserve"> <trans-unit id="Receiving file will be stopped." xml:space="preserve">
<source>Receiving file will be stopped.</source> <source>Receiving file will be stopped.</source>
<target>Odbieranie pliku zostanie przerwane.</target> <target>Odbieranie pliku zostanie przerwane.</target>
@@ -4540,10 +4484,6 @@ Błąd: %@</target>
<target>Ostania historia i ulepszony [bot adresowy](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target> <target>Ostania historia i ulepszony [bot adresowy](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve"> <trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source> <source>Recipients see updates as you type them.</source>
<target>Odbiorcy widzą aktualizacje podczas ich wpisywania.</target> <target>Odbiorcy widzą aktualizacje podczas ich wpisywania.</target>
@@ -4844,19 +4784,11 @@ Błąd: %@</target>
<target>Zapisać wiadomość powitalną?</target> <target>Zapisać wiadomość powitalną?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve"> <trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source> <source>Saved WebRTC ICE servers will be removed</source>
<target>Zapisane serwery WebRTC ICE zostaną usunięte</target> <target>Zapisane serwery WebRTC ICE zostaną usunięte</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve"> <trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source> <source>Saved message</source>
<target>Zachowano wiadomość</target> <target>Zachowano wiadomość</target>
@@ -5280,14 +5212,6 @@ Błąd: %@</target>
<trans-unit id="SimpleX links" xml:space="preserve"> <trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source> <source>SimpleX links</source>
<target>Linki SimpleX</target> <target>Linki SimpleX</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve"> <trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -6126,10 +6050,6 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc
<target>Wiadomości głosowe są zabronione w tej grupie.</target> <target>Wiadomości głosowe są zabronione w tej grupie.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve"> <trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source> <source>Voice messages prohibited!</source>
<target>Wiadomości głosowe zabronione!</target> <target>Wiadomości głosowe zabronione!</target>
@@ -6210,14 +6130,6 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc
<target>Gdy udostępnisz komuś profil incognito, będzie on używany w grupach, do których Cię zaprosi.</target> <target>Gdy udostępnisz komuś profil incognito, będzie on używany w grupach, do których Cię zaprosi.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve"> <trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source> <source>With encrypted files and media.</source>
<target>Z zaszyfrowanymi plikami i multimediami.</target> <target>Z zaszyfrowanymi plikami i multimediami.</target>
@@ -6701,10 +6613,6 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
<target>administrator</target> <target>administrator</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve"> <trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source> <source>agreeing encryption for %@…</source>
<target>uzgadnianie szyfrowania dla %@…</target> <target>uzgadnianie szyfrowania dla %@…</target>
@@ -6715,10 +6623,6 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
<target>uzgadnianie szyfrowania…</target> <target>uzgadnianie szyfrowania…</target>
<note>chat item text</note> <note>chat item text</note>
</trans-unit> </trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve"> <trans-unit id="always" xml:space="preserve">
<source>always</source> <source>always</source>
<target>zawsze</target> <target>zawsze</target>
@@ -7049,10 +6953,6 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
<target>nowe wydarzenie</target> <target>nowe wydarzenie</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve"> <trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source> <source>group deleted</source>
<target>grupa usunięta</target> <target>grupa usunięta</target>
@@ -7260,10 +7160,6 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
<target>właściciel</target> <target>właściciel</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve"> <trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source> <source>peer-to-peer</source>
<target>peer-to-peer</target> <target>peer-to-peer</target>
@@ -7314,14 +7210,6 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
<target>usunął cię</target> <target>usunął cię</target>
<note>rcv group event chat item</note> <note>rcv group event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve"> <trans-unit id="sec" xml:space="preserve">
<source>sec</source> <source>sec</source>
<target>sek</target> <target>sek</target>
@@ -7462,10 +7350,6 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
<target>tak</target> <target>tak</target>
<note>pref value</note> <note>pref value</note>
</trans-unit> </trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve"> <trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source> <source>you are invited to group</source>
<target>jesteś zaproszony do grupy</target> <target>jesteś zaproszony do grupy</target>
@@ -743,10 +743,6 @@
<target>Разрешить необратимо удалять отправленные сообщения. (24 часа)</target> <target>Разрешить необратимо удалять отправленные сообщения. (24 часа)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve"> <trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source> <source>Allow to send files and media.</source>
<target>Разрешить посылать файлы и медиа.</target> <target>Разрешить посылать файлы и медиа.</target>
@@ -1087,10 +1083,6 @@
<target>Невозможно получить файл</target> <target>Невозможно получить файл</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve"> <trans-unit id="Change" xml:space="preserve">
<source>Change</source> <source>Change</source>
<target>Поменять</target> <target>Поменять</target>
@@ -2086,10 +2078,6 @@ This cannot be undone!</source>
<target>Откатить версию и открыть чат</target> <target>Откатить версию и открыть чат</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve"> <trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source> <source>Download failed</source>
<target>Ошибка загрузки</target> <target>Ошибка загрузки</target>
@@ -2200,10 +2188,6 @@ This cannot be undone!</source>
<target>Включить код самоуничтожения</target> <target>Включить код самоуничтожения</target>
<note>set passcode view</note> <note>set passcode view</note>
</trans-unit> </trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve"> <trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source> <source>Encrypt</source>
<target>Зашифровать</target> <target>Зашифровать</target>
@@ -2729,10 +2713,6 @@ This cannot be undone!</source>
<target>Файлы и медиа запрещены в этой группе.</target> <target>Файлы и медиа запрещены в этой группе.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve"> <trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source> <source>Files and media prohibited!</source>
<target>Файлы и медиа запрещены!</target> <target>Файлы и медиа запрещены!</target>
@@ -2798,18 +2778,6 @@ This cannot be undone!</source>
<target>Для консоли</target> <target>Для консоли</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve"> <trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source> <source>Found desktop</source>
<target>Компьютер найден</target> <target>Компьютер найден</target>
@@ -2920,10 +2888,6 @@ This cannot be undone!</source>
<target>Члены группы могут необратимо удалять отправленные сообщения. (24 часа)</target> <target>Члены группы могут необратимо удалять отправленные сообщения. (24 часа)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve"> <trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source> <source>Group members can send direct messages.</source>
<target>Члены группы могут посылать прямые сообщения.</target> <target>Члены группы могут посылать прямые сообщения.</target>
@@ -3789,10 +3753,6 @@ This is your link for group %@!</source>
<target>Сеть &amp; серверы</target> <target>Сеть &amp; серверы</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve"> <trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source> <source>Network settings</source>
<target>Настройки сети</target> <target>Настройки сети</target>
@@ -3903,10 +3863,6 @@ This is your link for group %@!</source>
<target>Нет истории</target> <target>Нет истории</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve"> <trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source> <source>No permission to record voice message</source>
<target>Нет разрешения для записи голосового сообщения</target> <target>Нет разрешения для записи голосового сообщения</target>
@@ -4121,10 +4077,6 @@ This is your link for group %@!</source>
<target>Или покажите этот код</target> <target>Или покажите этот код</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve"> <trans-unit id="PING count" xml:space="preserve">
<source>PING count</source> <source>PING count</source>
<target>Количество PING</target> <target>Количество PING</target>
@@ -4387,10 +4339,6 @@ Error: %@</source>
<target>Запретить реакции на сообщения.</target> <target>Запретить реакции на сообщения.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve"> <trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source> <source>Prohibit sending direct messages to members.</source>
<target>Запретить посылать прямые сообщения членам группы.</target> <target>Запретить посылать прямые сообщения членам группы.</target>
@@ -4521,10 +4469,6 @@ Error: %@</source>
<target>Адрес получения сообщений будет перемещён на другой сервер. Изменение адреса завершится после того как отправитель будет онлайн.</target> <target>Адрес получения сообщений будет перемещён на другой сервер. Изменение адреса завершится после того как отправитель будет онлайн.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Receiving concurrency" xml:space="preserve">
<source>Receiving concurrency</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving file will be stopped." xml:space="preserve"> <trans-unit id="Receiving file will be stopped." xml:space="preserve">
<source>Receiving file will be stopped.</source> <source>Receiving file will be stopped.</source>
<target>Приём файла будет прекращён.</target> <target>Приём файла будет прекращён.</target>
@@ -4540,10 +4484,6 @@ Error: %@</source>
<target>История сообщений и улучшенный [каталог групп](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target> <target>История сообщений и улучшенный [каталог групп](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve"> <trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source> <source>Recipients see updates as you type them.</source>
<target>Получатели видят их в то время как Вы их набираете.</target> <target>Получатели видят их в то время как Вы их набираете.</target>
@@ -4844,19 +4784,11 @@ Error: %@</source>
<target>Сохранить приветственное сообщение?</target> <target>Сохранить приветственное сообщение?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve"> <trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source> <source>Saved WebRTC ICE servers will be removed</source>
<target>Сохраненные WebRTC ICE серверы будут удалены</target> <target>Сохраненные WebRTC ICE серверы будут удалены</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve"> <trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source> <source>Saved message</source>
<target>Сохраненное сообщение</target> <target>Сохраненное сообщение</target>
@@ -5280,14 +5212,6 @@ Error: %@</source>
<trans-unit id="SimpleX links" xml:space="preserve"> <trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source> <source>SimpleX links</source>
<target>SimpleX ссылки</target> <target>SimpleX ссылки</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve"> <trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -6126,10 +6050,6 @@ To connect, please ask your contact to create another connection link and check
<target>Голосовые сообщения запрещены в этой группе.</target> <target>Голосовые сообщения запрещены в этой группе.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve"> <trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source> <source>Voice messages prohibited!</source>
<target>Голосовые сообщения запрещены!</target> <target>Голосовые сообщения запрещены!</target>
@@ -6210,14 +6130,6 @@ To connect, please ask your contact to create another connection link and check
<target>Когда Вы соединены с контактом инкогнито, тот же самый инкогнито профиль будет использоваться для групп с этим контактом.</target> <target>Когда Вы соединены с контактом инкогнито, тот же самый инкогнито профиль будет использоваться для групп с этим контактом.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve"> <trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source> <source>With encrypted files and media.</source>
<target>С зашифрованными файлами и медиа.</target> <target>С зашифрованными файлами и медиа.</target>
@@ -6701,10 +6613,6 @@ SimpleX серверы не могут получить доступ к Ваше
<target>админ</target> <target>админ</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve"> <trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source> <source>agreeing encryption for %@…</source>
<target>шифрование согласовывается для %@…</target> <target>шифрование согласовывается для %@…</target>
@@ -6715,10 +6623,6 @@ SimpleX серверы не могут получить доступ к Ваше
<target>шифрование согласовывается…</target> <target>шифрование согласовывается…</target>
<note>chat item text</note> <note>chat item text</note>
</trans-unit> </trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve"> <trans-unit id="always" xml:space="preserve">
<source>always</source> <source>always</source>
<target>всегда</target> <target>всегда</target>
@@ -7049,10 +6953,6 @@ SimpleX серверы не могут получить доступ к Ваше
<target>событие произошло</target> <target>событие произошло</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve"> <trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source> <source>group deleted</source>
<target>группа удалена</target> <target>группа удалена</target>
@@ -7260,10 +7160,6 @@ SimpleX серверы не могут получить доступ к Ваше
<target>владелец</target> <target>владелец</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve"> <trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source> <source>peer-to-peer</source>
<target>peer-to-peer</target> <target>peer-to-peer</target>
@@ -7314,14 +7210,6 @@ SimpleX серверы не могут получить доступ к Ваше
<target>удалил(а) Вас из группы</target> <target>удалил(а) Вас из группы</target>
<note>rcv group event chat item</note> <note>rcv group event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve"> <trans-unit id="sec" xml:space="preserve">
<source>sec</source> <source>sec</source>
<target>сек</target> <target>сек</target>
@@ -7462,10 +7350,6 @@ SimpleX серверы не могут получить доступ к Ваше
<target>да</target> <target>да</target>
<note>pref value</note> <note>pref value</note>
</trans-unit> </trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve"> <trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source> <source>you are invited to group</source>
<target>Вы приглашены в группу</target> <target>Вы приглашены в группу</target>
@@ -707,10 +707,6 @@
<target>อนุญาตให้ลบข้อความที่ส่งไปแล้วอย่างถาวร</target> <target>อนุญาตให้ลบข้อความที่ส่งไปแล้วอย่างถาวร</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve"> <trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source> <source>Allow to send files and media.</source>
<target>อนุญาตให้ส่งไฟล์และสื่อ</target> <target>อนุญาตให้ส่งไฟล์และสื่อ</target>
@@ -1032,10 +1028,6 @@
<target>ไม่สามารถรับไฟล์ได้</target> <target>ไม่สามารถรับไฟล์ได้</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve"> <trans-unit id="Change" xml:space="preserve">
<source>Change</source> <source>Change</source>
<target>เปลี่ยน</target> <target>เปลี่ยน</target>
@@ -1987,10 +1979,6 @@ This cannot be undone!</source>
<target>ปรับลดรุ่นและเปิดแชท</target> <target>ปรับลดรุ่นและเปิดแชท</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve"> <trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source> <source>Download failed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -2096,10 +2084,6 @@ This cannot be undone!</source>
<target>เปิดใช้งานรหัสผ่านแบบทําลายตัวเอง</target> <target>เปิดใช้งานรหัสผ่านแบบทําลายตัวเอง</target>
<note>set passcode view</note> <note>set passcode view</note>
</trans-unit> </trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve"> <trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source> <source>Encrypt</source>
<target>Encrypt</target> <target>Encrypt</target>
@@ -2602,10 +2586,6 @@ This cannot be undone!</source>
<target>ไฟล์และสื่อเป็นสิ่งต้องห้ามในกลุ่มนี้</target> <target>ไฟล์และสื่อเป็นสิ่งต้องห้ามในกลุ่มนี้</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve"> <trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source> <source>Files and media prohibited!</source>
<target>ไฟล์และสื่อต้องห้าม!</target> <target>ไฟล์และสื่อต้องห้าม!</target>
@@ -2669,18 +2649,6 @@ This cannot be undone!</source>
<target>สำหรับคอนโซล</target> <target>สำหรับคอนโซล</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve"> <trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source> <source>Found desktop</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -2787,10 +2755,6 @@ This cannot be undone!</source>
<target>สมาชิกกลุ่มสามารถลบข้อความที่ส่งแล้วอย่างถาวร</target> <target>สมาชิกกลุ่มสามารถลบข้อความที่ส่งแล้วอย่างถาวร</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve"> <trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source> <source>Group members can send direct messages.</source>
<target>สมาชิกกลุ่มสามารถส่งข้อความโดยตรงได้</target> <target>สมาชิกกลุ่มสามารถส่งข้อความโดยตรงได้</target>
@@ -3617,10 +3581,6 @@ This is your link for group %@!</source>
<target>เครือข่ายและเซิร์ฟเวอร์</target> <target>เครือข่ายและเซิร์ฟเวอร์</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve"> <trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source> <source>Network settings</source>
<target>การตั้งค่าเครือข่าย</target> <target>การตั้งค่าเครือข่าย</target>
@@ -3728,10 +3688,6 @@ This is your link for group %@!</source>
<target>ไม่มีประวัติ</target> <target>ไม่มีประวัติ</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve"> <trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source> <source>No permission to record voice message</source>
<target>ไม่อนุญาตให้บันทึกข้อความเสียง</target> <target>ไม่อนุญาตให้บันทึกข้อความเสียง</target>
@@ -3936,10 +3892,6 @@ This is your link for group %@!</source>
<source>Or show this code</source> <source>Or show this code</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve"> <trans-unit id="PING count" xml:space="preserve">
<source>PING count</source> <source>PING count</source>
<target>จํานวน PING</target> <target>จํานวน PING</target>
@@ -4190,10 +4142,6 @@ Error: %@</source>
<target>ห้ามแสดงปฏิกิริยาต่อข้อความ</target> <target>ห้ามแสดงปฏิกิริยาต่อข้อความ</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve"> <trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source> <source>Prohibit sending direct messages to members.</source>
<target>ห้ามส่งข้อความโดยตรงถึงสมาชิก</target> <target>ห้ามส่งข้อความโดยตรงถึงสมาชิก</target>
@@ -4320,10 +4268,6 @@ Error: %@</source>
<target>ที่อยู่ผู้รับจะถูกเปลี่ยนเป็นเซิร์ฟเวอร์อื่น การเปลี่ยนแปลงที่อยู่จะเสร็จสมบูรณ์หลังจากที่ผู้ส่งออนไลน์</target> <target>ที่อยู่ผู้รับจะถูกเปลี่ยนเป็นเซิร์ฟเวอร์อื่น การเปลี่ยนแปลงที่อยู่จะเสร็จสมบูรณ์หลังจากที่ผู้ส่งออนไลน์</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Receiving concurrency" xml:space="preserve">
<source>Receiving concurrency</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving file will be stopped." xml:space="preserve"> <trans-unit id="Receiving file will be stopped." xml:space="preserve">
<source>Receiving file will be stopped.</source> <source>Receiving file will be stopped.</source>
<target>การรับไฟล์จะหยุดลง</target> <target>การรับไฟล์จะหยุดลง</target>
@@ -4338,10 +4282,6 @@ Error: %@</source>
<source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source> <source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve"> <trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source> <source>Recipients see updates as you type them.</source>
<target>ผู้รับจะเห็นการอัปเดตเมื่อคุณพิมพ์</target> <target>ผู้รับจะเห็นการอัปเดตเมื่อคุณพิมพ์</target>
@@ -4634,19 +4574,11 @@ Error: %@</source>
<target>บันทึกข้อความต้อนรับ?</target> <target>บันทึกข้อความต้อนรับ?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve"> <trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source> <source>Saved WebRTC ICE servers will be removed</source>
<target>เซิร์ฟเวอร์ WebRTC ICE ที่บันทึกไว้จะถูกลบออก</target> <target>เซิร์ฟเวอร์ WebRTC ICE ที่บันทึกไว้จะถูกลบออก</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve"> <trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source> <source>Saved message</source>
<note>message info title</note> <note>message info title</note>
@@ -5057,14 +4989,6 @@ Error: %@</source>
<trans-unit id="SimpleX links" xml:space="preserve"> <trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source> <source>SimpleX links</source>
<target>ลิงก์ SimpleX</target> <target>ลิงก์ SimpleX</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve"> <trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -5861,10 +5785,6 @@ To connect, please ask your contact to create another connection link and check
<target>ข้อความเสียงเป็นสิ่งต้องห้ามในกลุ่มนี้</target> <target>ข้อความเสียงเป็นสิ่งต้องห้ามในกลุ่มนี้</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve"> <trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source> <source>Voice messages prohibited!</source>
<target>ห้ามข้อความเสียง!</target> <target>ห้ามข้อความเสียง!</target>
@@ -5942,14 +5862,6 @@ To connect, please ask your contact to create another connection link and check
<target>เมื่อคุณแชร์โปรไฟล์ที่ไม่ระบุตัวตนกับใครสักคน โปรไฟล์นี้จะใช้สำหรับกลุ่มที่พวกเขาเชิญคุณ</target> <target>เมื่อคุณแชร์โปรไฟล์ที่ไม่ระบุตัวตนกับใครสักคน โปรไฟล์นี้จะใช้สำหรับกลุ่มที่พวกเขาเชิญคุณ</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve"> <trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source> <source>With encrypted files and media.</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -6411,10 +6323,6 @@ SimpleX servers cannot see your profile.</source>
<target>ผู้ดูแลระบบ</target> <target>ผู้ดูแลระบบ</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve"> <trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source> <source>agreeing encryption for %@…</source>
<target>ยอมรับ encryption สำหรับ %@…</target> <target>ยอมรับ encryption สำหรับ %@…</target>
@@ -6425,10 +6333,6 @@ SimpleX servers cannot see your profile.</source>
<target>เห็นด้วยกับการ encryption…</target> <target>เห็นด้วยกับการ encryption…</target>
<note>chat item text</note> <note>chat item text</note>
</trans-unit> </trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve"> <trans-unit id="always" xml:space="preserve">
<source>always</source> <source>always</source>
<target>เสมอ</target> <target>เสมอ</target>
@@ -6749,10 +6653,6 @@ SimpleX servers cannot see your profile.</source>
<source>event happened</source> <source>event happened</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve"> <trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source> <source>group deleted</source>
<target>ลบกลุ่มแล้ว</target> <target>ลบกลุ่มแล้ว</target>
@@ -6959,10 +6859,6 @@ SimpleX servers cannot see your profile.</source>
<target>เจ้าของ</target> <target>เจ้าของ</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve"> <trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source> <source>peer-to-peer</source>
<target>เพื่อนต่อเพื่อน</target> <target>เพื่อนต่อเพื่อน</target>
@@ -7010,14 +6906,6 @@ SimpleX servers cannot see your profile.</source>
<target>ลบคุณออกแล้ว</target> <target>ลบคุณออกแล้ว</target>
<note>rcv group event chat item</note> <note>rcv group event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve"> <trans-unit id="sec" xml:space="preserve">
<source>sec</source> <source>sec</source>
<target>วินาที</target> <target>วินาที</target>
@@ -7150,10 +7038,6 @@ SimpleX servers cannot see your profile.</source>
<target>ใช่</target> <target>ใช่</target>
<note>pref value</note> <note>pref value</note>
</trans-unit> </trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve"> <trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source> <source>you are invited to group</source>
<target>คุณได้รับเชิญให้เข้าร่วมกลุ่ม</target> <target>คุณได้รับเชิญให้เข้าร่วมกลุ่ม</target>
@@ -743,10 +743,6 @@
<target>Gönderilen mesajların kalıcı olarak silinmesine izin ver. (24 saat içinde)</target> <target>Gönderilen mesajların kalıcı olarak silinmesine izin ver. (24 saat içinde)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve"> <trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source> <source>Allow to send files and media.</source>
<target>Dosya ve medya göndermeye izin ver.</target> <target>Dosya ve medya göndermeye izin ver.</target>
@@ -1087,10 +1083,6 @@
<target>Dosya alınamıyor</target> <target>Dosya alınamıyor</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve"> <trans-unit id="Change" xml:space="preserve">
<source>Change</source> <source>Change</source>
<target>Değiştir</target> <target>Değiştir</target>
@@ -2086,10 +2078,6 @@ Bu geri alınamaz!</target>
<target>Sürüm düşür ve sohbeti aç</target> <target>Sürüm düşür ve sohbeti aç</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve"> <trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source> <source>Download failed</source>
<target>Yükleme başarısız oldu</target> <target>Yükleme başarısız oldu</target>
@@ -2200,10 +2188,6 @@ Bu geri alınamaz!</target>
<target>Kendini imha şifresini etkinleştir</target> <target>Kendini imha şifresini etkinleştir</target>
<note>set passcode view</note> <note>set passcode view</note>
</trans-unit> </trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve"> <trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source> <source>Encrypt</source>
<target>Şifreleme</target> <target>Şifreleme</target>
@@ -2729,10 +2713,6 @@ Bu geri alınamaz!</target>
<target>Dosyalar ve medya bu grupta yasaklandı.</target> <target>Dosyalar ve medya bu grupta yasaklandı.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve"> <trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source> <source>Files and media prohibited!</source>
<target>Dosyalar ve medya yasaklandı!</target> <target>Dosyalar ve medya yasaklandı!</target>
@@ -2798,18 +2778,6 @@ Bu geri alınamaz!</target>
<target>Konsol için</target> <target>Konsol için</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve"> <trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source> <source>Found desktop</source>
<target>Bilgisayar bulundu</target> <target>Bilgisayar bulundu</target>
@@ -2920,10 +2888,6 @@ Bu geri alınamaz!</target>
<target>Grup üyeleri, gönderilen mesajları kalıcı olarak silebilir. (24 saat içinde)</target> <target>Grup üyeleri, gönderilen mesajları kalıcı olarak silebilir. (24 saat içinde)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve"> <trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source> <source>Group members can send direct messages.</source>
<target>Grup üyeleri doğrudan mesajlar gönderebilir.</target> <target>Grup üyeleri doğrudan mesajlar gönderebilir.</target>
@@ -3789,10 +3753,6 @@ Bu senin grup için bağlantın %@!</target>
<target>Ağ &amp; sunucular</target> <target>Ağ &amp; sunucular</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve"> <trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source> <source>Network settings</source>
<target>Ağ ayarları</target> <target>Ağ ayarları</target>
@@ -3903,10 +3863,6 @@ Bu senin grup için bağlantın %@!</target>
<target>Geçmiş yok</target> <target>Geçmiş yok</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve"> <trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source> <source>No permission to record voice message</source>
<target>Sesli mesaj kaydetmek için izin yok</target> <target>Sesli mesaj kaydetmek için izin yok</target>
@@ -4121,10 +4077,6 @@ Bu senin grup için bağlantın %@!</target>
<target>Veya bu kodu göster</target> <target>Veya bu kodu göster</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve"> <trans-unit id="PING count" xml:space="preserve">
<source>PING count</source> <source>PING count</source>
<target>PING sayısı</target> <target>PING sayısı</target>
@@ -4387,10 +4339,6 @@ Hata: %@</target>
<target>Mesajlarda tepkileri yasakla.</target> <target>Mesajlarda tepkileri yasakla.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve"> <trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source> <source>Prohibit sending direct messages to members.</source>
<target>Geri dönülmez mesaj silme işlemini yasakla.</target> <target>Geri dönülmez mesaj silme işlemini yasakla.</target>
@@ -4521,10 +4469,6 @@ Hata: %@</target>
<target>Alıcı adresi farklı bir sunucuya değiştirilecektir. Gönderici çevrimiçi olduktan sonra adres değişikliği tamamlanacaktır.</target> <target>Alıcı adresi farklı bir sunucuya değiştirilecektir. Gönderici çevrimiçi olduktan sonra adres değişikliği tamamlanacaktır.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Receiving concurrency" xml:space="preserve">
<source>Receiving concurrency</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving file will be stopped." xml:space="preserve"> <trans-unit id="Receiving file will be stopped." xml:space="preserve">
<source>Receiving file will be stopped.</source> <source>Receiving file will be stopped.</source>
<target>Dosya alımı durdurulacaktır.</target> <target>Dosya alımı durdurulacaktır.</target>
@@ -4540,10 +4484,6 @@ Hata: %@</target>
<target>Yakın geçmiş ve geliştirilmiş [dizin botu](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex. im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target> <target>Yakın geçmiş ve geliştirilmiş [dizin botu](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex. im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve"> <trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source> <source>Recipients see updates as you type them.</source>
<target>Alıcılar yazdığına göre güncellemeleri görecektir.</target> <target>Alıcılar yazdığına göre güncellemeleri görecektir.</target>
@@ -4844,19 +4784,11 @@ Hata: %@</target>
<target>Hoşgeldin mesajı kaydedilsin mi?</target> <target>Hoşgeldin mesajı kaydedilsin mi?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve"> <trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source> <source>Saved WebRTC ICE servers will be removed</source>
<target>Kaydedilmiş WebRTC ICE sunucuları silinecek</target> <target>Kaydedilmiş WebRTC ICE sunucuları silinecek</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve"> <trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source> <source>Saved message</source>
<target>Kaydedilmiş mesaj</target> <target>Kaydedilmiş mesaj</target>
@@ -5280,14 +5212,6 @@ Hata: %@</target>
<trans-unit id="SimpleX links" xml:space="preserve"> <trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source> <source>SimpleX links</source>
<target>SimpleX bağlantıları</target> <target>SimpleX bağlantıları</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve"> <trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -6126,10 +6050,6 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste
<target>Bu grupta sesli mesajlar yasaktır.</target> <target>Bu grupta sesli mesajlar yasaktır.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve"> <trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source> <source>Voice messages prohibited!</source>
<target>Sesli mesajlar yasaktır!</target> <target>Sesli mesajlar yasaktır!</target>
@@ -6210,14 +6130,6 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste
<target>Biriyle gizli bir profil paylaştığınızda, bu profil sizi davet ettikleri gruplar için kullanılacaktır.</target> <target>Biriyle gizli bir profil paylaştığınızda, bu profil sizi davet ettikleri gruplar için kullanılacaktır.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve"> <trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source> <source>With encrypted files and media.</source>
<target>Şifrelenmiş dosyalar ve medya ile birlikte.</target> <target>Şifrelenmiş dosyalar ve medya ile birlikte.</target>
@@ -6701,10 +6613,6 @@ SimpleX sunucuları profilinizi göremez.</target>
<target>yönetici</target> <target>yönetici</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve"> <trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source> <source>agreeing encryption for %@…</source>
<target>%@ için şifreleme kabul ediliyor…</target> <target>%@ için şifreleme kabul ediliyor…</target>
@@ -6715,10 +6623,6 @@ SimpleX sunucuları profilinizi göremez.</target>
<target>şifreleme kabul ediliyor…</target> <target>şifreleme kabul ediliyor…</target>
<note>chat item text</note> <note>chat item text</note>
</trans-unit> </trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve"> <trans-unit id="always" xml:space="preserve">
<source>always</source> <source>always</source>
<target>her zaman</target> <target>her zaman</target>
@@ -7049,10 +6953,6 @@ SimpleX sunucuları profilinizi göremez.</target>
<target>etkinlik yaşandı</target> <target>etkinlik yaşandı</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve"> <trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source> <source>group deleted</source>
<target>grup silindi</target> <target>grup silindi</target>
@@ -7260,10 +7160,6 @@ SimpleX sunucuları profilinizi göremez.</target>
<target>sahip</target> <target>sahip</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve"> <trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source> <source>peer-to-peer</source>
<target>eşler arası</target> <target>eşler arası</target>
@@ -7314,14 +7210,6 @@ SimpleX sunucuları profilinizi göremez.</target>
<target>sen kaldırıldın</target> <target>sen kaldırıldın</target>
<note>rcv group event chat item</note> <note>rcv group event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve"> <trans-unit id="sec" xml:space="preserve">
<source>sec</source> <source>sec</source>
<target>sn</target> <target>sn</target>
@@ -7462,10 +7350,6 @@ SimpleX sunucuları profilinizi göremez.</target>
<target>evet</target> <target>evet</target>
<note>pref value</note> <note>pref value</note>
</trans-unit> </trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve"> <trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source> <source>you are invited to group</source>
<target>gruba davet edildiniz</target> <target>gruba davet edildiniz</target>
@@ -743,10 +743,6 @@
<target>Дозволяє безповоротно видаляти надіслані повідомлення. (24 години)</target> <target>Дозволяє безповоротно видаляти надіслані повідомлення. (24 години)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve"> <trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source> <source>Allow to send files and media.</source>
<target>Дозволяє надсилати файли та медіа.</target> <target>Дозволяє надсилати файли та медіа.</target>
@@ -1087,10 +1083,6 @@
<target>Не вдається отримати файл</target> <target>Не вдається отримати файл</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve"> <trans-unit id="Change" xml:space="preserve">
<source>Change</source> <source>Change</source>
<target>Зміна</target> <target>Зміна</target>
@@ -2086,10 +2078,6 @@ This cannot be undone!</source>
<target>Пониження та відкритий чат</target> <target>Пониження та відкритий чат</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve"> <trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source> <source>Download failed</source>
<target>Не вдалося завантажити</target> <target>Не вдалося завантажити</target>
@@ -2200,10 +2188,6 @@ This cannot be undone!</source>
<target>Увімкнути пароль самознищення</target> <target>Увімкнути пароль самознищення</target>
<note>set passcode view</note> <note>set passcode view</note>
</trans-unit> </trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve"> <trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source> <source>Encrypt</source>
<target>Зашифрувати</target> <target>Зашифрувати</target>
@@ -2729,10 +2713,6 @@ This cannot be undone!</source>
<target>Файли та медіа в цій групі заборонені.</target> <target>Файли та медіа в цій групі заборонені.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve"> <trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source> <source>Files and media prohibited!</source>
<target>Файли та медіа заборонені!</target> <target>Файли та медіа заборонені!</target>
@@ -2798,18 +2778,6 @@ This cannot be undone!</source>
<target>Для консолі</target> <target>Для консолі</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve"> <trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source> <source>Found desktop</source>
<target>Знайдено робочий стіл</target> <target>Знайдено робочий стіл</target>
@@ -2920,10 +2888,6 @@ This cannot be undone!</source>
<target>Учасники групи можуть безповоротно видаляти надіслані повідомлення. (24 години)</target> <target>Учасники групи можуть безповоротно видаляти надіслані повідомлення. (24 години)</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve"> <trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source> <source>Group members can send direct messages.</source>
<target>Учасники групи можуть надсилати прямі повідомлення.</target> <target>Учасники групи можуть надсилати прямі повідомлення.</target>
@@ -3789,10 +3753,6 @@ This is your link for group %@!</source>
<target>Мережа та сервери</target> <target>Мережа та сервери</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve"> <trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source> <source>Network settings</source>
<target>Налаштування мережі</target> <target>Налаштування мережі</target>
@@ -3903,10 +3863,6 @@ This is your link for group %@!</source>
<target>Немає історії</target> <target>Немає історії</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve"> <trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source> <source>No permission to record voice message</source>
<target>Немає дозволу на запис голосового повідомлення</target> <target>Немає дозволу на запис голосового повідомлення</target>
@@ -4121,10 +4077,6 @@ This is your link for group %@!</source>
<target>Або покажіть цей код</target> <target>Або покажіть цей код</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve"> <trans-unit id="PING count" xml:space="preserve">
<source>PING count</source> <source>PING count</source>
<target>Кількість PING</target> <target>Кількість PING</target>
@@ -4387,10 +4339,6 @@ Error: %@</source>
<target>Заборонити реакції на повідомлення.</target> <target>Заборонити реакції на повідомлення.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve"> <trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source> <source>Prohibit sending direct messages to members.</source>
<target>Заборонити надсилати прямі повідомлення учасникам.</target> <target>Заборонити надсилати прямі повідомлення учасникам.</target>
@@ -4521,10 +4469,6 @@ Error: %@</source>
<target>Адреса отримувача буде змінена на інший сервер. Зміна адреси завершиться після того, як відправник з'явиться в мережі.</target> <target>Адреса отримувача буде змінена на інший сервер. Зміна адреси завершиться після того, як відправник з'явиться в мережі.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Receiving concurrency" xml:space="preserve">
<source>Receiving concurrency</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving file will be stopped." xml:space="preserve"> <trans-unit id="Receiving file will be stopped." xml:space="preserve">
<source>Receiving file will be stopped.</source> <source>Receiving file will be stopped.</source>
<target>Отримання файлу буде зупинено.</target> <target>Отримання файлу буде зупинено.</target>
@@ -4540,10 +4484,6 @@ Error: %@</source>
<target>Нещодавня історія та покращення [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target> <target>Нещодавня історія та покращення [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve"> <trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source> <source>Recipients see updates as you type them.</source>
<target>Одержувачі бачать оновлення, коли ви їх вводите.</target> <target>Одержувачі бачать оновлення, коли ви їх вводите.</target>
@@ -4844,19 +4784,11 @@ Error: %@</source>
<target>Зберегти вітальне повідомлення?</target> <target>Зберегти вітальне повідомлення?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve"> <trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source> <source>Saved WebRTC ICE servers will be removed</source>
<target>Збережені сервери WebRTC ICE буде видалено</target> <target>Збережені сервери WebRTC ICE буде видалено</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve"> <trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source> <source>Saved message</source>
<target>Збережене повідомлення</target> <target>Збережене повідомлення</target>
@@ -5280,14 +5212,6 @@ Error: %@</source>
<trans-unit id="SimpleX links" xml:space="preserve"> <trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source> <source>SimpleX links</source>
<target>Посилання SimpleX</target> <target>Посилання SimpleX</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve"> <trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -6126,10 +6050,6 @@ To connect, please ask your contact to create another connection link and check
<target>Голосові повідомлення в цій групі заборонені.</target> <target>Голосові повідомлення в цій групі заборонені.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve"> <trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source> <source>Voice messages prohibited!</source>
<target>Голосові повідомлення заборонені!</target> <target>Голосові повідомлення заборонені!</target>
@@ -6210,14 +6130,6 @@ To connect, please ask your contact to create another connection link and check
<target>Коли ви ділитеся з кимось своїм профілем інкогніто, цей профіль буде використовуватися для груп, до яких вас запрошують.</target> <target>Коли ви ділитеся з кимось своїм профілем інкогніто, цей профіль буде використовуватися для груп, до яких вас запрошують.</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve"> <trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source> <source>With encrypted files and media.</source>
<target>З зашифрованими файлами та медіа.</target> <target>З зашифрованими файлами та медіа.</target>
@@ -6701,10 +6613,6 @@ SimpleX servers cannot see your profile.</source>
<target>адмін</target> <target>адмін</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve"> <trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source> <source>agreeing encryption for %@…</source>
<target>узгодження шифрування для %@…</target> <target>узгодження шифрування для %@…</target>
@@ -6715,10 +6623,6 @@ SimpleX servers cannot see your profile.</source>
<target>узгодження шифрування…</target> <target>узгодження шифрування…</target>
<note>chat item text</note> <note>chat item text</note>
</trans-unit> </trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve"> <trans-unit id="always" xml:space="preserve">
<source>always</source> <source>always</source>
<target>завжди</target> <target>завжди</target>
@@ -7049,10 +6953,6 @@ SimpleX servers cannot see your profile.</source>
<target>відбулася подія</target> <target>відбулася подія</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve"> <trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source> <source>group deleted</source>
<target>групу видалено</target> <target>групу видалено</target>
@@ -7260,10 +7160,6 @@ SimpleX servers cannot see your profile.</source>
<target>власник</target> <target>власник</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve"> <trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source> <source>peer-to-peer</source>
<target>одноранговий</target> <target>одноранговий</target>
@@ -7314,14 +7210,6 @@ SimpleX servers cannot see your profile.</source>
<target>прибрали вас</target> <target>прибрали вас</target>
<note>rcv group event chat item</note> <note>rcv group event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve"> <trans-unit id="sec" xml:space="preserve">
<source>sec</source> <source>sec</source>
<target>сек</target> <target>сек</target>
@@ -7462,10 +7350,6 @@ SimpleX servers cannot see your profile.</source>
<target>так</target> <target>так</target>
<note>pref value</note> <note>pref value</note>
</trans-unit> </trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve"> <trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source> <source>you are invited to group</source>
<target>вас запрошують до групи</target> <target>вас запрошують до групи</target>
@@ -728,10 +728,6 @@
<target>允许不可撤回地删除已发送消息。</target> <target>允许不可撤回地删除已发送消息。</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
<source>Allow to send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Allow to send files and media." xml:space="preserve"> <trans-unit id="Allow to send files and media." xml:space="preserve">
<source>Allow to send files and media.</source> <source>Allow to send files and media.</source>
<target>允许发送文件和媒体。</target> <target>允许发送文件和媒体。</target>
@@ -1067,10 +1063,6 @@
<target>无法接收文件</target> <target>无法接收文件</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Cellular" xml:space="preserve">
<source>Cellular</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve"> <trans-unit id="Change" xml:space="preserve">
<source>Change</source> <source>Change</source>
<target>更改</target> <target>更改</target>
@@ -2047,10 +2039,6 @@ This cannot be undone!</source>
<target>降级并打开聊天</target> <target>降级并打开聊天</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Download" xml:space="preserve">
<source>Download</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve"> <trans-unit id="Download failed" xml:space="preserve">
<source>Download failed</source> <source>Download failed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
@@ -2157,10 +2145,6 @@ This cannot be undone!</source>
<target>启用自毁密码</target> <target>启用自毁密码</target>
<note>set passcode view</note> <note>set passcode view</note>
</trans-unit> </trans-unit>
<trans-unit id="Enabled for" xml:space="preserve">
<source>Enabled for</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Encrypt" xml:space="preserve"> <trans-unit id="Encrypt" xml:space="preserve">
<source>Encrypt</source> <source>Encrypt</source>
<target>加密</target> <target>加密</target>
@@ -2674,10 +2658,6 @@ This cannot be undone!</source>
<target>此群组中禁止文件和媒体。</target> <target>此群组中禁止文件和媒体。</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Files and media not allowed" xml:space="preserve">
<source>Files and media not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Files and media prohibited!" xml:space="preserve"> <trans-unit id="Files and media prohibited!" xml:space="preserve">
<source>Files and media prohibited!</source> <source>Files and media prohibited!</source>
<target>禁止文件和媒体!</target> <target>禁止文件和媒体!</target>
@@ -2741,18 +2721,6 @@ This cannot be undone!</source>
<target>用于控制台</target> <target>用于控制台</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Forward" xml:space="preserve">
<source>Forward</source>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Forwarded" xml:space="preserve">
<source>Forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Forwarded from" xml:space="preserve">
<source>Forwarded from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Found desktop" xml:space="preserve"> <trans-unit id="Found desktop" xml:space="preserve">
<source>Found desktop</source> <source>Found desktop</source>
<target>找到了桌面</target> <target>找到了桌面</target>
@@ -2862,10 +2830,6 @@ This cannot be undone!</source>
<target>群组成员可以不可撤回地删除已发送的消息。</target> <target>群组成员可以不可撤回地删除已发送的消息。</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
<source>Group members can send SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group members can send direct messages." xml:space="preserve"> <trans-unit id="Group members can send direct messages." xml:space="preserve">
<source>Group members can send direct messages.</source> <source>Group members can send direct messages.</source>
<target>群组成员可以私信。</target> <target>群组成员可以私信。</target>
@@ -3709,10 +3673,6 @@ This is your link for group %@!</source>
<target>网络和服务器</target> <target>网络和服务器</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Network connection" xml:space="preserve">
<source>Network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network settings" xml:space="preserve"> <trans-unit id="Network settings" xml:space="preserve">
<source>Network settings</source> <source>Network settings</source>
<target>网络设置</target> <target>网络设置</target>
@@ -3823,10 +3783,6 @@ This is your link for group %@!</source>
<target>无历史记录</target> <target>无历史记录</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
<source>No network connection</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No permission to record voice message" xml:space="preserve"> <trans-unit id="No permission to record voice message" xml:space="preserve">
<source>No permission to record voice message</source> <source>No permission to record voice message</source>
<target>没有录制语音消息的权限</target> <target>没有录制语音消息的权限</target>
@@ -4037,10 +3993,6 @@ This is your link for group %@!</source>
<target>或者显示此码</target> <target>或者显示此码</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Other" xml:space="preserve">
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="PING count" xml:space="preserve"> <trans-unit id="PING count" xml:space="preserve">
<source>PING count</source> <source>PING count</source>
<target>PING 次数</target> <target>PING 次数</target>
@@ -4296,10 +4248,6 @@ Error: %@</source>
<target>禁止消息回应。</target> <target>禁止消息回应。</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
<source>Prohibit sending SimpleX links.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve"> <trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source> <source>Prohibit sending direct messages to members.</source>
<target>禁止向成员发送私信。</target> <target>禁止向成员发送私信。</target>
@@ -4428,10 +4376,6 @@ Error: %@</source>
<target>接收地址将变更到不同的服务器。地址更改将在发件人上线后完成。</target> <target>接收地址将变更到不同的服务器。地址更改将在发件人上线后完成。</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Receiving concurrency" xml:space="preserve">
<source>Receiving concurrency</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving file will be stopped." xml:space="preserve"> <trans-unit id="Receiving file will be stopped." xml:space="preserve">
<source>Receiving file will be stopped.</source> <source>Receiving file will be stopped.</source>
<target>即将停止接收文件。</target> <target>即将停止接收文件。</target>
@@ -4446,10 +4390,6 @@ Error: %@</source>
<source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source> <source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&amp;smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
<source>Recipient(s) can't see who this message is from.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Recipients see updates as you type them." xml:space="preserve"> <trans-unit id="Recipients see updates as you type them." xml:space="preserve">
<source>Recipients see updates as you type them.</source> <source>Recipients see updates as you type them.</source>
<target>对方会在您键入时看到更新。</target> <target>对方会在您键入时看到更新。</target>
@@ -4746,19 +4686,11 @@ Error: %@</source>
<target>保存欢迎信息?</target> <target>保存欢迎信息?</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved" xml:space="preserve">
<source>Saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve"> <trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
<source>Saved WebRTC ICE servers will be removed</source> <source>Saved WebRTC ICE servers will be removed</source>
<target>已保存的WebRTC ICE服务器将被删除</target> <target>已保存的WebRTC ICE服务器将被删除</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Saved from" xml:space="preserve">
<source>Saved from</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Saved message" xml:space="preserve"> <trans-unit id="Saved message" xml:space="preserve">
<source>Saved message</source> <source>Saved message</source>
<target>已保存的消息</target> <target>已保存的消息</target>
@@ -5180,14 +5112,6 @@ Error: %@</source>
<trans-unit id="SimpleX links" xml:space="preserve"> <trans-unit id="SimpleX links" xml:space="preserve">
<source>SimpleX links</source> <source>SimpleX links</source>
<target>SimpleX 链接</target> <target>SimpleX 链接</target>
<note>chat feature</note>
</trans-unit>
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
<source>SimpleX links are prohibited in this group.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
<source>SimpleX links not allowed</source>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="SimpleX one-time invitation" xml:space="preserve"> <trans-unit id="SimpleX one-time invitation" xml:space="preserve">
@@ -6015,10 +5939,6 @@ To connect, please ask your contact to create another connection link and check
<target>语音信息在该群组中被禁用。</target> <target>语音信息在该群组中被禁用。</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="Voice messages not allowed" xml:space="preserve">
<source>Voice messages not allowed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Voice messages prohibited!" xml:space="preserve"> <trans-unit id="Voice messages prohibited!" xml:space="preserve">
<source>Voice messages prohibited!</source> <source>Voice messages prohibited!</source>
<target>语音消息禁止发送!</target> <target>语音消息禁止发送!</target>
@@ -6096,14 +6016,6 @@ To connect, please ask your contact to create another connection link and check
<target>当您与某人共享隐身聊天资料时,该资料将用于他们邀请您加入的群组。</target> <target>当您与某人共享隐身聊天资料时,该资料将用于他们邀请您加入的群组。</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="WiFi" xml:space="preserve">
<source>WiFi</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wired ethernet" xml:space="preserve">
<source>Wired ethernet</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="With encrypted files and media." xml:space="preserve"> <trans-unit id="With encrypted files and media." xml:space="preserve">
<source>With encrypted files and media.</source> <source>With encrypted files and media.</source>
<target>加密的文件和媒体。</target> <target>加密的文件和媒体。</target>
@@ -6575,10 +6487,6 @@ SimpleX 服务器无法看到您的资料。</target>
<target>管理员</target> <target>管理员</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="admins" xml:space="preserve">
<source>admins</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="agreeing encryption for %@…" xml:space="preserve"> <trans-unit id="agreeing encryption for %@…" xml:space="preserve">
<source>agreeing encryption for %@…</source> <source>agreeing encryption for %@…</source>
<target>正在协商将加密应用于 %@…</target> <target>正在协商将加密应用于 %@…</target>
@@ -6589,10 +6497,6 @@ SimpleX 服务器无法看到您的资料。</target>
<target>同意加密…</target> <target>同意加密…</target>
<note>chat item text</note> <note>chat item text</note>
</trans-unit> </trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve"> <trans-unit id="always" xml:space="preserve">
<source>always</source> <source>always</source>
<target>始终</target> <target>始终</target>
@@ -6921,10 +6825,6 @@ SimpleX 服务器无法看到您的资料。</target>
<target>发生的事</target> <target>发生的事</target>
<note>No comment provided by engineer.</note> <note>No comment provided by engineer.</note>
</trans-unit> </trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
<source>forwarded</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group deleted" xml:space="preserve"> <trans-unit id="group deleted" xml:space="preserve">
<source>group deleted</source> <source>group deleted</source>
<target>群组已删除</target> <target>群组已删除</target>
@@ -7131,10 +7031,6 @@ SimpleX 服务器无法看到您的资料。</target>
<target>群主</target> <target>群主</target>
<note>member role</note> <note>member role</note>
</trans-unit> </trans-unit>
<trans-unit id="owners" xml:space="preserve">
<source>owners</source>
<note>feature role</note>
</trans-unit>
<trans-unit id="peer-to-peer" xml:space="preserve"> <trans-unit id="peer-to-peer" xml:space="preserve">
<source>peer-to-peer</source> <source>peer-to-peer</source>
<target>点对点</target> <target>点对点</target>
@@ -7184,14 +7080,6 @@ SimpleX 服务器无法看到您的资料。</target>
<target>已将您移除</target> <target>已将您移除</target>
<note>rcv group event chat item</note> <note>rcv group event chat item</note>
</trans-unit> </trans-unit>
<trans-unit id="saved" xml:space="preserve">
<source>saved</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved from %@" xml:space="preserve">
<source>saved from %@</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="sec" xml:space="preserve"> <trans-unit id="sec" xml:space="preserve">
<source>sec</source> <source>sec</source>
<target>秒</target> <target>秒</target>
@@ -7329,10 +7217,6 @@ SimpleX 服务器无法看到您的资料。</target>
<target>是</target> <target>是</target>
<note>pref value</note> <note>pref value</note>
</trans-unit> </trans-unit>
<trans-unit id="you" xml:space="preserve">
<source>you</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve"> <trans-unit id="you are invited to group" xml:space="preserve">
<source>you are invited to group</source> <source>you are invited to group</source>
<target>您被邀请加入群组</target> <target>您被邀请加入群组</target>
@@ -586,6 +586,9 @@ func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? {
cleanupDirectFile(aChatItem) cleanupDirectFile(aChatItem)
} }
return nil return nil
case let .sndFileCompleteXFTP(_, aChatItem, _):
cleanupFile(aChatItem)
return nil
case let .callInvitation(invitation): case let .callInvitation(invitation):
// Do not post it without CallKit support, iOS will stop launching the app without showing CallKit // Do not post it without CallKit support, iOS will stop launching the app without showing CallKit
return ( return (
+42 -40
View File
@@ -29,11 +29,6 @@
5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; }; 5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; };
5C13730B28156D2700F43030 /* ContactConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C13730A28156D2700F43030 /* ContactConnectionView.swift */; }; 5C13730B28156D2700F43030 /* ContactConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C13730A28156D2700F43030 /* ContactConnectionView.swift */; };
5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; }; 5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; };
5C2217842BD4465500A8B0E7 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C22177F2BD4465500A8B0E7 /* libgmpxx.a */; };
5C2217852BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217802BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy-ghc9.6.3.a */; };
5C2217862BD4465500A8B0E7 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217812BD4465500A8B0E7 /* libgmp.a */; };
5C2217872BD4465500A8B0E7 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217822BD4465500A8B0E7 /* libffi.a */; };
5C2217882BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217832BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy.a */; };
5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; }; 5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; };
5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260A27A30CFA00F70299 /* ChatListView.swift */; }; 5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260A27A30CFA00F70299 /* ChatListView.swift */; };
5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260E27A30FDC00F70299 /* ChatView.swift */; }; 5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260E27A30FDC00F70299 /* ChatView.swift */; };
@@ -144,6 +139,11 @@
5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; }; 5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; };
5CEBD7462A5C0A8F00665FE2 /* KeyboardPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */; }; 5CEBD7462A5C0A8F00665FE2 /* KeyboardPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */; };
5CEBD7482A5F115D00665FE2 /* SetDeliveryReceiptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */; }; 5CEBD7482A5F115D00665FE2 /* SetDeliveryReceiptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */; };
5CF898622BB984E400EE33B6 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF8985D2BB984E400EE33B6 /* libgmpxx.a */; };
5CF898632BB984E400EE33B6 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF8985E2BB984E400EE33B6 /* libffi.a */; };
5CF898642BB984E400EE33B6 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF8985F2BB984E400EE33B6 /* libgmp.a */; };
5CF898652BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF898602BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv.a */; };
5CF898662BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF898612BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv-ghc9.6.3.a */; };
5CF937202B24DE8C00E1D781 /* SharedFileSubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */; }; 5CF937202B24DE8C00E1D781 /* SharedFileSubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */; };
5CF937232B2503D000E1D781 /* NSESubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF937212B25034A00E1D781 /* NSESubscriber.swift */; }; 5CF937232B2503D000E1D781 /* NSESubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF937212B25034A00E1D781 /* NSESubscriber.swift */; };
5CFA59C42860BC6200863A68 /* MigrateToAppGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */; }; 5CFA59C42860BC6200863A68 /* MigrateToAppGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */; };
@@ -163,6 +163,11 @@
64466DC829FC2B3B00E3D48D /* CreateSimpleXAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64466DC729FC2B3B00E3D48D /* CreateSimpleXAddress.swift */; }; 64466DC829FC2B3B00E3D48D /* CreateSimpleXAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64466DC729FC2B3B00E3D48D /* CreateSimpleXAddress.swift */; };
64466DCC29FFE3E800E3D48D /* MailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64466DCB29FFE3E800E3D48D /* MailView.swift */; }; 64466DCC29FFE3E800E3D48D /* MailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64466DCB29FFE3E800E3D48D /* MailView.swift */; };
6448BBB628FA9D56000D2AB9 /* GroupLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6448BBB528FA9D56000D2AB9 /* GroupLinkView.swift */; }; 6448BBB628FA9D56000D2AB9 /* GroupLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6448BBB528FA9D56000D2AB9 /* GroupLinkView.swift */; };
6449333A2AF8E51000AC506E /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933352AF8E51000AC506E /* libgmpxx.a */; };
6449333B2AF8E51000AC506E /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933362AF8E51000AC506E /* libgmp.a */; };
6449333C2AF8E51000AC506E /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933372AF8E51000AC506E /* libffi.a */; };
6449333D2AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933382AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8-ghc9.6.3.a */; };
6449333E2AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 644933392AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8.a */; };
644EFFDE292BCD9D00525D5B /* ComposeVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFDD292BCD9D00525D5B /* ComposeVoiceView.swift */; }; 644EFFDE292BCD9D00525D5B /* ComposeVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFDD292BCD9D00525D5B /* ComposeVoiceView.swift */; };
644EFFE0292CFD7F00525D5B /* CIVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFDF292CFD7F00525D5B /* CIVoiceView.swift */; }; 644EFFE0292CFD7F00525D5B /* CIVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFDF292CFD7F00525D5B /* CIVoiceView.swift */; };
644EFFE2292D089800525D5B /* FramedCIVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFE1292D089800525D5B /* FramedCIVoiceView.swift */; }; 644EFFE2292D089800525D5B /* FramedCIVoiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 644EFFE1292D089800525D5B /* FramedCIVoiceView.swift */; };
@@ -172,7 +177,6 @@
646BB38E283FDB6D001CE359 /* LocalAuthenticationUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 646BB38D283FDB6D001CE359 /* LocalAuthenticationUtils.swift */; }; 646BB38E283FDB6D001CE359 /* LocalAuthenticationUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 646BB38D283FDB6D001CE359 /* LocalAuthenticationUtils.swift */; };
647F090E288EA27B00644C40 /* GroupMemberInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647F090D288EA27B00644C40 /* GroupMemberInfoView.swift */; }; 647F090E288EA27B00644C40 /* GroupMemberInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647F090D288EA27B00644C40 /* GroupMemberInfoView.swift */; };
648010AB281ADD15009009B9 /* CIFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648010AA281ADD15009009B9 /* CIFileView.swift */; }; 648010AB281ADD15009009B9 /* CIFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648010AA281ADD15009009B9 /* CIFileView.swift */; };
648679AB2BC96A74006456E7 /* ChatItemForwardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */; };
649BCDA0280460FD00C3A862 /* ComposeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCD9F280460FD00C3A862 /* ComposeImageView.swift */; }; 649BCDA0280460FD00C3A862 /* ComposeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCD9F280460FD00C3A862 /* ComposeImageView.swift */; };
649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCDA12805D6EF00C3A862 /* CIImageView.swift */; }; 649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCDA12805D6EF00C3A862 /* CIImageView.swift */; };
64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; }; 64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; };
@@ -188,7 +192,6 @@
8C69FE7D2B8C7D2700267E38 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */; }; 8C69FE7D2B8C7D2700267E38 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */; };
8C7D949A2B88952700B7B9E1 /* MigrateToDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */; }; 8C7D949A2B88952700B7B9E1 /* MigrateToDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */; };
8C7DF3202B7CDB0A00C886D0 /* MigrateFromDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */; }; 8C7DF3202B7CDB0A00C886D0 /* MigrateFromDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */; };
8CC956EE2BC0041000412A11 /* NetworkObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC956ED2BC0041000412A11 /* NetworkObserver.swift */; };
D7197A1829AE89660055C05A /* WebRTC in Frameworks */ = {isa = PBXBuildFile; productRef = D7197A1729AE89660055C05A /* WebRTC */; }; D7197A1829AE89660055C05A /* WebRTC in Frameworks */ = {isa = PBXBuildFile; productRef = D7197A1729AE89660055C05A /* WebRTC */; };
D72A9088294BD7A70047C86D /* NativeTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72A9087294BD7A70047C86D /* NativeTextEditor.swift */; }; D72A9088294BD7A70047C86D /* NativeTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72A9087294BD7A70047C86D /* NativeTextEditor.swift */; };
D741547829AF89AF0022400A /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D741547729AF89AF0022400A /* StoreKit.framework */; }; D741547829AF89AF0022400A /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D741547729AF89AF0022400A /* StoreKit.framework */; };
@@ -279,11 +282,6 @@
5C13730A28156D2700F43030 /* ContactConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactConnectionView.swift; sourceTree = "<group>"; }; 5C13730A28156D2700F43030 /* ContactConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactConnectionView.swift; sourceTree = "<group>"; };
5C13730C2815740A00F43030 /* DebugJSON.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = DebugJSON.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 5C13730C2815740A00F43030 /* DebugJSON.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = DebugJSON.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemView.swift; sourceTree = "<group>"; }; 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemView.swift; sourceTree = "<group>"; };
5C22177F2BD4465500A8B0E7 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5C2217802BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy-ghc9.6.3.a"; sourceTree = "<group>"; };
5C2217812BD4465500A8B0E7 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5C2217822BD4465500A8B0E7 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5C2217832BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy.a"; sourceTree = "<group>"; };
5C245F3C2B501E98001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; }; 5C245F3C2B501E98001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
5C245F3D2B501F13001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = "tr.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; }; 5C245F3D2B501F13001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = "tr.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
5C245F3E2B501F13001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; }; 5C245F3E2B501F13001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@@ -438,6 +436,11 @@
5CEACCEC27DEA495000BD591 /* MsgContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MsgContentView.swift; sourceTree = "<group>"; }; 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MsgContentView.swift; sourceTree = "<group>"; };
5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardPadding.swift; sourceTree = "<group>"; }; 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardPadding.swift; sourceTree = "<group>"; };
5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDeliveryReceiptsView.swift; sourceTree = "<group>"; }; 5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDeliveryReceiptsView.swift; sourceTree = "<group>"; };
5CF8985D2BB984E400EE33B6 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5CF8985E2BB984E400EE33B6 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5CF8985F2BB984E400EE33B6 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5CF898602BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv.a"; sourceTree = "<group>"; };
5CF898612BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv-ghc9.6.3.a"; sourceTree = "<group>"; };
5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedFileSubscriber.swift; sourceTree = "<group>"; }; 5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedFileSubscriber.swift; sourceTree = "<group>"; };
5CF937212B25034A00E1D781 /* NSESubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSESubscriber.swift; sourceTree = "<group>"; }; 5CF937212B25034A00E1D781 /* NSESubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSESubscriber.swift; sourceTree = "<group>"; };
5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToAppGroupView.swift; sourceTree = "<group>"; }; 5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToAppGroupView.swift; sourceTree = "<group>"; };
@@ -456,6 +459,11 @@
64466DC729FC2B3B00E3D48D /* CreateSimpleXAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateSimpleXAddress.swift; sourceTree = "<group>"; }; 64466DC729FC2B3B00E3D48D /* CreateSimpleXAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateSimpleXAddress.swift; sourceTree = "<group>"; };
64466DCB29FFE3E800E3D48D /* MailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailView.swift; sourceTree = "<group>"; }; 64466DCB29FFE3E800E3D48D /* MailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailView.swift; sourceTree = "<group>"; };
6448BBB528FA9D56000D2AB9 /* GroupLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupLinkView.swift; sourceTree = "<group>"; }; 6448BBB528FA9D56000D2AB9 /* GroupLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupLinkView.swift; sourceTree = "<group>"; };
644933352AF8E51000AC506E /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
644933362AF8E51000AC506E /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
644933372AF8E51000AC506E /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
644933382AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8-ghc9.6.3.a"; sourceTree = "<group>"; };
644933392AF8E51000AC506E /* libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.0.3-EnhmkSQK6HvJ11g1uZERg8.a"; sourceTree = "<group>"; };
644EFFDD292BCD9D00525D5B /* ComposeVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeVoiceView.swift; sourceTree = "<group>"; }; 644EFFDD292BCD9D00525D5B /* ComposeVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeVoiceView.swift; sourceTree = "<group>"; };
644EFFDF292CFD7F00525D5B /* CIVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIVoiceView.swift; sourceTree = "<group>"; }; 644EFFDF292CFD7F00525D5B /* CIVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIVoiceView.swift; sourceTree = "<group>"; };
644EFFE1292D089800525D5B /* FramedCIVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FramedCIVoiceView.swift; sourceTree = "<group>"; }; 644EFFE1292D089800525D5B /* FramedCIVoiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FramedCIVoiceView.swift; sourceTree = "<group>"; };
@@ -465,7 +473,6 @@
646BB38D283FDB6D001CE359 /* LocalAuthenticationUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAuthenticationUtils.swift; sourceTree = "<group>"; }; 646BB38D283FDB6D001CE359 /* LocalAuthenticationUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAuthenticationUtils.swift; sourceTree = "<group>"; };
647F090D288EA27B00644C40 /* GroupMemberInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupMemberInfoView.swift; sourceTree = "<group>"; }; 647F090D288EA27B00644C40 /* GroupMemberInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupMemberInfoView.swift; sourceTree = "<group>"; };
648010AA281ADD15009009B9 /* CIFileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIFileView.swift; sourceTree = "<group>"; }; 648010AA281ADD15009009B9 /* CIFileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIFileView.swift; sourceTree = "<group>"; };
648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemForwardingView.swift; sourceTree = "<group>"; };
6493D667280ED77F007A76FB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; }; 6493D667280ED77F007A76FB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
649BCD9F280460FD00C3A862 /* ComposeImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeImageView.swift; sourceTree = "<group>"; }; 649BCD9F280460FD00C3A862 /* ComposeImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeImageView.swift; sourceTree = "<group>"; };
649BCDA12805D6EF00C3A862 /* CIImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIImageView.swift; sourceTree = "<group>"; }; 649BCDA12805D6EF00C3A862 /* CIImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIImageView.swift; sourceTree = "<group>"; };
@@ -483,7 +490,6 @@
8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; }; 8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToDevice.swift; sourceTree = "<group>"; }; 8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToDevice.swift; sourceTree = "<group>"; };
8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateFromDevice.swift; sourceTree = "<group>"; }; 8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateFromDevice.swift; sourceTree = "<group>"; };
8CC956ED2BC0041000412A11 /* NetworkObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkObserver.swift; sourceTree = "<group>"; };
D72A9087294BD7A70047C86D /* NativeTextEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeTextEditor.swift; sourceTree = "<group>"; }; D72A9087294BD7A70047C86D /* NativeTextEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeTextEditor.swift; sourceTree = "<group>"; };
D741547729AF89AF0022400A /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/StoreKit.framework; sourceTree = DEVELOPER_DIR; }; D741547729AF89AF0022400A /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/StoreKit.framework; sourceTree = DEVELOPER_DIR; };
D741547929AF90B00022400A /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/PushKit.framework; sourceTree = DEVELOPER_DIR; }; D741547929AF90B00022400A /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/PushKit.framework; sourceTree = DEVELOPER_DIR; };
@@ -525,13 +531,13 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
5C2217842BD4465500A8B0E7 /* libgmpxx.a in Frameworks */, 5CF898652BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv.a in Frameworks */,
5C2217872BD4465500A8B0E7 /* libffi.a in Frameworks */, 5CF898622BB984E400EE33B6 /* libgmpxx.a in Frameworks */,
5C2217862BD4465500A8B0E7 /* libgmp.a in Frameworks */, 5CF898642BB984E400EE33B6 /* libgmp.a in Frameworks */,
5C2217882BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy.a in Frameworks */,
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */, 5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */, 5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
5C2217852BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy-ghc9.6.3.a in Frameworks */, 5CF898632BB984E400EE33B6 /* libffi.a in Frameworks */,
5CF898662BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv-ghc9.6.3.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -587,7 +593,6 @@
5CBE6C11294487F7002D9531 /* VerifyCodeView.swift */, 5CBE6C11294487F7002D9531 /* VerifyCodeView.swift */,
5CBE6C132944CC12002D9531 /* ScanCodeView.swift */, 5CBE6C132944CC12002D9531 /* ScanCodeView.swift */,
64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */, 64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */,
648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */,
); );
path = Chat; path = Chat;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -595,11 +600,11 @@
5C764E5C279C70B7000C6508 /* Libraries */ = { 5C764E5C279C70B7000C6508 /* Libraries */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
5C2217822BD4465500A8B0E7 /* libffi.a */, 5CF8985E2BB984E400EE33B6 /* libffi.a */,
5C2217812BD4465500A8B0E7 /* libgmp.a */, 5CF8985F2BB984E400EE33B6 /* libgmp.a */,
5C22177F2BD4465500A8B0E7 /* libgmpxx.a */, 5CF8985D2BB984E400EE33B6 /* libgmpxx.a */,
5C2217802BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy-ghc9.6.3.a */, 5CF898612BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv-ghc9.6.3.a */,
5C2217832BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy.a */, 5CF898602BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv.a */,
); );
path = Libraries; path = Libraries;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -628,7 +633,6 @@
5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */, 5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */,
5C93293E2928E0FD0090FFF9 /* AudioRecPlay.swift */, 5C93293E2928E0FD0090FFF9 /* AudioRecPlay.swift */,
5CBD2859295711D700EC2CF4 /* ImageUtils.swift */, 5CBD2859295711D700EC2CF4 /* ImageUtils.swift */,
8CC956ED2BC0041000412A11 /* NetworkObserver.swift */,
); );
path = Model; path = Model;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1164,7 +1168,6 @@
5C10D88828EED12E00E58BF0 /* ContactConnectionInfo.swift in Sources */, 5C10D88828EED12E00E58BF0 /* ContactConnectionInfo.swift in Sources */,
5CBE6C12294487F7002D9531 /* VerifyCodeView.swift in Sources */, 5CBE6C12294487F7002D9531 /* VerifyCodeView.swift in Sources */,
3CDBCF4227FAE51000354CDD /* ComposeLinkView.swift in Sources */, 3CDBCF4227FAE51000354CDD /* ComposeLinkView.swift in Sources */,
648679AB2BC96A74006456E7 /* ChatItemForwardingView.swift in Sources */,
64466DC829FC2B3B00E3D48D /* CreateSimpleXAddress.swift in Sources */, 64466DC829FC2B3B00E3D48D /* CreateSimpleXAddress.swift in Sources */,
3CDBCF4827FF621E00354CDD /* CILinkView.swift in Sources */, 3CDBCF4827FF621E00354CDD /* CILinkView.swift in Sources */,
5C7505A827B6D34800BE3227 /* ChatInfoToolbar.swift in Sources */, 5C7505A827B6D34800BE3227 /* ChatInfoToolbar.swift in Sources */,
@@ -1183,7 +1186,6 @@
5C9A5BDB2871E05400A5B906 /* SetNotificationsMode.swift in Sources */, 5C9A5BDB2871E05400A5B906 /* SetNotificationsMode.swift in Sources */,
5CB0BA8E2827126500B3292C /* OnboardingView.swift in Sources */, 5CB0BA8E2827126500B3292C /* OnboardingView.swift in Sources */,
6442E0BE2880182D00CEC0F9 /* GroupChatInfoView.swift in Sources */, 6442E0BE2880182D00CEC0F9 /* GroupChatInfoView.swift in Sources */,
8CC956EE2BC0041000412A11 /* NetworkObserver.swift in Sources */,
5C2E261227A30FEA00F70299 /* TerminalView.swift in Sources */, 5C2E261227A30FEA00F70299 /* TerminalView.swift in Sources */,
5C9FD96E27A5D6ED0075386C /* SendMessageView.swift in Sources */, 5C9FD96E27A5D6ED0075386C /* SendMessageView.swift in Sources */,
5CA7DFC329302AF000F7FDDE /* AppSheet.swift in Sources */, 5CA7DFC329302AF000F7FDDE /* AppSheet.swift in Sources */,
@@ -1544,7 +1546,7 @@
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 208; CURRENT_PROJECT_VERSION = 204;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T; DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
@@ -1569,7 +1571,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
LLVM_LTO = YES_THIN; LLVM_LTO = YES_THIN;
MARKETING_VERSION = 5.7; MARKETING_VERSION = 5.6;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX; PRODUCT_NAME = SimpleX;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@@ -1593,7 +1595,7 @@
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 208; CURRENT_PROJECT_VERSION = 204;
DEAD_CODE_STRIPPING = YES; DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T; DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
@@ -1618,7 +1620,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
LLVM_LTO = YES; LLVM_LTO = YES;
MARKETING_VERSION = 5.7; MARKETING_VERSION = 5.6;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX; PRODUCT_NAME = SimpleX;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@@ -1679,7 +1681,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 208; CURRENT_PROJECT_VERSION = 204;
DEVELOPMENT_TEAM = 5NN7GUYB6T; DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
GCC_OPTIMIZATION_LEVEL = s; GCC_OPTIMIZATION_LEVEL = s;
@@ -1694,7 +1696,7 @@
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
LLVM_LTO = YES; LLVM_LTO = YES;
MARKETING_VERSION = 5.7; MARKETING_VERSION = 5.6;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1716,7 +1718,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 208; CURRENT_PROJECT_VERSION = 204;
DEVELOPMENT_TEAM = 5NN7GUYB6T; DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
ENABLE_CODE_COVERAGE = NO; ENABLE_CODE_COVERAGE = NO;
@@ -1731,7 +1733,7 @@
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
LLVM_LTO = YES; LLVM_LTO = YES;
MARKETING_VERSION = 5.7; MARKETING_VERSION = 5.6;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1753,7 +1755,7 @@
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 208; CURRENT_PROJECT_VERSION = 204;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T; DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1779,7 +1781,7 @@
"$(PROJECT_DIR)/Libraries/sim", "$(PROJECT_DIR)/Libraries/sim",
); );
LLVM_LTO = YES; LLVM_LTO = YES;
MARKETING_VERSION = 5.7; MARKETING_VERSION = 5.6;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos; SDKROOT = iphoneos;
@@ -1804,7 +1806,7 @@
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES; CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES; CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 208; CURRENT_PROJECT_VERSION = 204;
DEFINES_MODULE = YES; DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T; DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1830,7 +1832,7 @@
"$(PROJECT_DIR)/Libraries/sim", "$(PROJECT_DIR)/Libraries/sim",
); );
LLVM_LTO = YES; LLVM_LTO = YES;
MARKETING_VERSION = 5.7; MARKETING_VERSION = 5.6;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos; SDKROOT = iphoneos;
-5
View File
@@ -87,11 +87,6 @@ public func chatInitControllerRemovingDatabases() {
public func chatCloseStore() { public func chatCloseStore() {
// Prevent crash when exiting the app with already closed store (for example, after changing a database passpharase)
guard hasChatCtrl() else {
logger.error("chatCloseStore: already closed, chatCtrl is nil")
return
}
let err = fromCString(chat_close_store(getChatCtrl())) let err = fromCString(chat_close_store(getChatCtrl()))
if err != "" { if err != "" {
logger.error("chatCloseStore error: \(err)") logger.error("chatCloseStore error: \(err)")
+4 -43
View File
@@ -50,7 +50,6 @@ public enum ChatCommand {
case apiDeleteChatItem(type: ChatType, id: Int64, itemId: Int64, mode: CIDeleteMode) case apiDeleteChatItem(type: ChatType, id: Int64, itemId: Int64, mode: CIDeleteMode)
case apiDeleteMemberChatItem(groupId: Int64, groupMemberId: Int64, itemId: Int64) case apiDeleteMemberChatItem(groupId: Int64, groupMemberId: Int64, itemId: Int64)
case apiChatItemReaction(type: ChatType, id: Int64, itemId: Int64, add: Bool, reaction: MsgReaction) case apiChatItemReaction(type: ChatType, id: Int64, itemId: Int64, add: Bool, reaction: MsgReaction)
case apiForwardChatItem(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemId: Int64)
case apiGetNtfToken case apiGetNtfToken
case apiRegisterToken(token: DeviceToken, notificationMode: NotificationsMode) case apiRegisterToken(token: DeviceToken, notificationMode: NotificationsMode)
case apiVerifyToken(token: DeviceToken, nonce: String, code: String) case apiVerifyToken(token: DeviceToken, nonce: String, code: String)
@@ -78,7 +77,6 @@ public enum ChatCommand {
case apiGetChatItemTTL(userId: Int64) case apiGetChatItemTTL(userId: Int64)
case apiSetNetworkConfig(networkConfig: NetCfg) case apiSetNetworkConfig(networkConfig: NetCfg)
case apiGetNetworkConfig case apiGetNetworkConfig
case apiSetNetworkInfo(networkInfo: UserNetworkInfo)
case reconnectAllServers case reconnectAllServers
case apiSetChatSettings(type: ChatType, id: Int64, chatSettings: ChatSettings) case apiSetChatSettings(type: ChatType, id: Int64, chatSettings: ChatSettings)
case apiSetMemberSettings(groupId: Int64, groupMemberId: Int64, memberSettings: GroupMemberSettings) case apiSetMemberSettings(groupId: Int64, groupMemberId: Int64, memberSettings: GroupMemberSettings)
@@ -196,7 +194,6 @@ public enum ChatCommand {
case let .apiDeleteChatItem(type, id, itemId, mode): return "/_delete item \(ref(type, id)) \(itemId) \(mode.rawValue)" case let .apiDeleteChatItem(type, id, itemId, mode): return "/_delete item \(ref(type, id)) \(itemId) \(mode.rawValue)"
case let .apiDeleteMemberChatItem(groupId, groupMemberId, itemId): return "/_delete member item #\(groupId) \(groupMemberId) \(itemId)" case let .apiDeleteMemberChatItem(groupId, groupMemberId, itemId): return "/_delete member item #\(groupId) \(groupMemberId) \(itemId)"
case let .apiChatItemReaction(type, id, itemId, add, reaction): return "/_reaction \(ref(type, id)) \(itemId) \(onOff(add)) \(encodeJSON(reaction))" case let .apiChatItemReaction(type, id, itemId, add, reaction): return "/_reaction \(ref(type, id)) \(itemId) \(onOff(add)) \(encodeJSON(reaction))"
case let .apiForwardChatItem(toChatType, toChatId, fromChatType, fromChatId, itemId): return "/_forward \(ref(toChatType, toChatId)) \(ref(fromChatType, fromChatId)) \(itemId)"
case .apiGetNtfToken: return "/_ntf get " case .apiGetNtfToken: return "/_ntf get "
case let .apiRegisterToken(token, notificationMode): return "/_ntf register \(token.cmdString) \(notificationMode.rawValue)" case let .apiRegisterToken(token, notificationMode): return "/_ntf register \(token.cmdString) \(notificationMode.rawValue)"
case let .apiVerifyToken(token, nonce, code): return "/_ntf verify \(token.cmdString) \(nonce) \(code)" case let .apiVerifyToken(token, nonce, code): return "/_ntf verify \(token.cmdString) \(nonce) \(code)"
@@ -224,7 +221,6 @@ public enum ChatCommand {
case let .apiGetChatItemTTL(userId): return "/_ttl \(userId)" case let .apiGetChatItemTTL(userId): return "/_ttl \(userId)"
case let .apiSetNetworkConfig(networkConfig): return "/_network \(encodeJSON(networkConfig))" case let .apiSetNetworkConfig(networkConfig): return "/_network \(encodeJSON(networkConfig))"
case .apiGetNetworkConfig: return "/network" case .apiGetNetworkConfig: return "/network"
case let .apiSetNetworkInfo(networkInfo): return "/_network info \(encodeJSON(networkInfo))"
case .reconnectAllServers: return "/reconnect" case .reconnectAllServers: return "/reconnect"
case let .apiSetChatSettings(type, id, chatSettings): return "/_settings \(ref(type, id)) \(encodeJSON(chatSettings))" case let .apiSetChatSettings(type, id, chatSettings): return "/_settings \(ref(type, id)) \(encodeJSON(chatSettings))"
case let .apiSetMemberSettings(groupId, groupMemberId, memberSettings): return "/_member settings #\(groupId) \(groupMemberId) \(encodeJSON(memberSettings))" case let .apiSetMemberSettings(groupId, groupMemberId, memberSettings): return "/_member settings #\(groupId) \(groupMemberId) \(encodeJSON(memberSettings))"
@@ -345,7 +341,6 @@ public enum ChatCommand {
case .apiConnectContactViaAddress: return "apiConnectContactViaAddress" case .apiConnectContactViaAddress: return "apiConnectContactViaAddress"
case .apiDeleteMemberChatItem: return "apiDeleteMemberChatItem" case .apiDeleteMemberChatItem: return "apiDeleteMemberChatItem"
case .apiChatItemReaction: return "apiChatItemReaction" case .apiChatItemReaction: return "apiChatItemReaction"
case .apiForwardChatItem: return "apiForwardChatItem"
case .apiGetNtfToken: return "apiGetNtfToken" case .apiGetNtfToken: return "apiGetNtfToken"
case .apiRegisterToken: return "apiRegisterToken" case .apiRegisterToken: return "apiRegisterToken"
case .apiVerifyToken: return "apiVerifyToken" case .apiVerifyToken: return "apiVerifyToken"
@@ -373,7 +368,6 @@ public enum ChatCommand {
case .apiGetChatItemTTL: return "apiGetChatItemTTL" case .apiGetChatItemTTL: return "apiGetChatItemTTL"
case .apiSetNetworkConfig: return "apiSetNetworkConfig" case .apiSetNetworkConfig: return "apiSetNetworkConfig"
case .apiGetNetworkConfig: return "apiGetNetworkConfig" case .apiGetNetworkConfig: return "apiGetNetworkConfig"
case .apiSetNetworkInfo: return "apiSetNetworkInfo"
case .reconnectAllServers: return "reconnectAllServers" case .reconnectAllServers: return "reconnectAllServers"
case .apiSetChatSettings: return "apiSetChatSettings" case .apiSetChatSettings: return "apiSetChatSettings"
case .apiSetMemberSettings: return "apiSetMemberSettings" case .apiSetMemberSettings: return "apiSetMemberSettings"
@@ -1257,7 +1251,6 @@ public struct NetCfg: Codable, Equatable {
public var tcpConnectTimeout: Int // microseconds public var tcpConnectTimeout: Int // microseconds
public var tcpTimeout: Int // microseconds public var tcpTimeout: Int // microseconds
public var tcpTimeoutPerKb: Int // microseconds public var tcpTimeoutPerKb: Int // microseconds
public var rcvConcurrency: Int // pool size
public var tcpKeepAlive: KeepAliveOpts? public var tcpKeepAlive: KeepAliveOpts?
public var smpPingInterval: Int // microseconds public var smpPingInterval: Int // microseconds
public var smpPingCount: Int // times public var smpPingCount: Int // times
@@ -1266,10 +1259,9 @@ public struct NetCfg: Codable, Equatable {
public static let defaults: NetCfg = NetCfg( public static let defaults: NetCfg = NetCfg(
socksProxy: nil, socksProxy: nil,
sessionMode: TransportSessionMode.user, sessionMode: TransportSessionMode.user,
tcpConnectTimeout: 10_000_000, tcpConnectTimeout: 20_000_000,
tcpTimeout: 15_000_000, tcpTimeout: 15_000_000,
tcpTimeoutPerKb: 10_000, tcpTimeoutPerKb: 45_000,
rcvConcurrency: 12,
tcpKeepAlive: KeepAliveOpts.defaults, tcpKeepAlive: KeepAliveOpts.defaults,
smpPingInterval: 1200_000_000, smpPingInterval: 1200_000_000,
smpPingCount: 3, smpPingCount: 3,
@@ -1279,10 +1271,9 @@ public struct NetCfg: Codable, Equatable {
public static let proxyDefaults: NetCfg = NetCfg( public static let proxyDefaults: NetCfg = NetCfg(
socksProxy: nil, socksProxy: nil,
sessionMode: TransportSessionMode.user, sessionMode: TransportSessionMode.user,
tcpConnectTimeout: 20_000_000, tcpConnectTimeout: 30_000_000,
tcpTimeout: 20_000_000, tcpTimeout: 20_000_000,
tcpTimeoutPerKb: 15_000, tcpTimeoutPerKb: 60_000,
rcvConcurrency: 8,
tcpKeepAlive: KeepAliveOpts.defaults, tcpKeepAlive: KeepAliveOpts.defaults,
smpPingInterval: 1200_000_000, smpPingInterval: 1200_000_000,
smpPingCount: 3, smpPingCount: 3,
@@ -1723,8 +1714,6 @@ public enum ChatErrorType: Decodable {
case fallbackToSMPProhibited(fileId: Int64) case fallbackToSMPProhibited(fileId: Int64)
case inlineFileProhibited(fileId: Int64) case inlineFileProhibited(fileId: Int64)
case invalidQuote case invalidQuote
case invalidForward
case forwardNoFile
case invalidChatItemUpdate case invalidChatItemUpdate
case invalidChatItemDelete case invalidChatItemDelete
case hasCurrentCall case hasCurrentCall
@@ -2097,31 +2086,3 @@ public enum AppSettingsLockScreenCalls: String, Codable {
case show case show
case accept case accept
} }
public struct UserNetworkInfo: Codable, Equatable {
public let networkType: UserNetworkType
public let online: Bool
public init(networkType: UserNetworkType, online: Bool) {
self.networkType = networkType
self.online = online
}
}
public enum UserNetworkType: String, Codable {
case none
case cellular
case wifi
case ethernet
case other
public var text: LocalizedStringKey {
switch self {
case .none: "No network connection"
case .cellular: "Cellular"
case .wifi: "WiFi"
case .ethernet: "Wired ethernet"
case .other: "Other"
}
}
}
-5
View File
@@ -29,7 +29,6 @@ let GROUP_DEFAULT_NETWORK_SESSION_MODE = "networkSessionMode"
let GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT = "networkTCPConnectTimeout" let GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT = "networkTCPConnectTimeout"
let GROUP_DEFAULT_NETWORK_TCP_TIMEOUT = "networkTCPTimeout" let GROUP_DEFAULT_NETWORK_TCP_TIMEOUT = "networkTCPTimeout"
let GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB = "networkTCPTimeoutPerKb" let GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB = "networkTCPTimeoutPerKb"
let GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY = "networkRcvConcurrency"
let GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL = "networkSMPPingInterval" let GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL = "networkSMPPingInterval"
let GROUP_DEFAULT_NETWORK_SMP_PING_COUNT = "networkSMPPingCount" let GROUP_DEFAULT_NETWORK_SMP_PING_COUNT = "networkSMPPingCount"
let GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE = "networkEnableKeepAlive" let GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE = "networkEnableKeepAlive"
@@ -56,7 +55,6 @@ public func registerGroupDefaults() {
GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT: NetCfg.defaults.tcpConnectTimeout, GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT: NetCfg.defaults.tcpConnectTimeout,
GROUP_DEFAULT_NETWORK_TCP_TIMEOUT: NetCfg.defaults.tcpTimeout, GROUP_DEFAULT_NETWORK_TCP_TIMEOUT: NetCfg.defaults.tcpTimeout,
GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB: NetCfg.defaults.tcpTimeoutPerKb, GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB: NetCfg.defaults.tcpTimeoutPerKb,
GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY: NetCfg.defaults.rcvConcurrency,
GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL: NetCfg.defaults.smpPingInterval, GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL: NetCfg.defaults.smpPingInterval,
GROUP_DEFAULT_NETWORK_SMP_PING_COUNT: NetCfg.defaults.smpPingCount, GROUP_DEFAULT_NETWORK_SMP_PING_COUNT: NetCfg.defaults.smpPingCount,
GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE: NetCfg.defaults.enableKeepAlive, GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE: NetCfg.defaults.enableKeepAlive,
@@ -280,7 +278,6 @@ public func getNetCfg() -> NetCfg {
let tcpConnectTimeout = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT) let tcpConnectTimeout = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT)
let tcpTimeout = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT) let tcpTimeout = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT)
let tcpTimeoutPerKb = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB) let tcpTimeoutPerKb = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB)
let rcvConcurrency = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY)
let smpPingInterval = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL) let smpPingInterval = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL)
let smpPingCount = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_SMP_PING_COUNT) let smpPingCount = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_SMP_PING_COUNT)
let enableKeepAlive = groupDefaults.bool(forKey: GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE) let enableKeepAlive = groupDefaults.bool(forKey: GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE)
@@ -300,7 +297,6 @@ public func getNetCfg() -> NetCfg {
tcpConnectTimeout: tcpConnectTimeout, tcpConnectTimeout: tcpConnectTimeout,
tcpTimeout: tcpTimeout, tcpTimeout: tcpTimeout,
tcpTimeoutPerKb: tcpTimeoutPerKb, tcpTimeoutPerKb: tcpTimeoutPerKb,
rcvConcurrency: rcvConcurrency,
tcpKeepAlive: tcpKeepAlive, tcpKeepAlive: tcpKeepAlive,
smpPingInterval: smpPingInterval, smpPingInterval: smpPingInterval,
smpPingCount: smpPingCount, smpPingCount: smpPingCount,
@@ -314,7 +310,6 @@ public func setNetCfg(_ cfg: NetCfg) {
groupDefaults.set(cfg.tcpConnectTimeout, forKey: GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT) groupDefaults.set(cfg.tcpConnectTimeout, forKey: GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT)
groupDefaults.set(cfg.tcpTimeout, forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT) groupDefaults.set(cfg.tcpTimeout, forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT)
groupDefaults.set(cfg.tcpTimeoutPerKb, forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB) groupDefaults.set(cfg.tcpTimeoutPerKb, forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB)
groupDefaults.set(cfg.rcvConcurrency, forKey: GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY)
groupDefaults.set(cfg.smpPingInterval, forKey: GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL) groupDefaults.set(cfg.smpPingInterval, forKey: GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL)
groupDefaults.set(cfg.smpPingCount, forKey: GROUP_DEFAULT_NETWORK_SMP_PING_COUNT) groupDefaults.set(cfg.smpPingCount, forKey: GROUP_DEFAULT_NETWORK_SMP_PING_COUNT)
if let tcpKeepAlive = cfg.tcpKeepAlive { if let tcpKeepAlive = cfg.tcpKeepAlive {
+32 -144
View File
@@ -543,7 +543,6 @@ public protocol Feature {
var iconFilled: String { get } var iconFilled: String { get }
var iconScale: CGFloat { get } var iconScale: CGFloat { get }
var hasParam: Bool { get } var hasParam: Bool { get }
var hasRole: Bool { get }
var text: String { get } var text: String { get }
} }
@@ -570,8 +569,6 @@ public enum ChatFeature: String, Decodable, Feature {
} }
} }
public var hasRole: Bool { false }
public var text: String { public var text: String {
switch self { switch self {
case .timedMessages: return NSLocalizedString("Disappearing messages", comment: "chat feature") case .timedMessages: return NSLocalizedString("Disappearing messages", comment: "chat feature")
@@ -697,7 +694,6 @@ public enum GroupFeature: String, Decodable, Feature {
case reactions case reactions
case voice case voice
case files case files
case simplexLinks
case history case history
public var id: Self { self } public var id: Self { self }
@@ -709,19 +705,6 @@ public enum GroupFeature: String, Decodable, Feature {
} }
} }
public var hasRole: Bool {
switch self {
case .timedMessages: false
case .directMessages: true
case .fullDelete: false
case .reactions: false
case .voice: true
case .files: true
case .simplexLinks: true
case .history: false
}
}
public var text: String { public var text: String {
switch self { switch self {
case .timedMessages: return NSLocalizedString("Disappearing messages", comment: "chat feature") case .timedMessages: return NSLocalizedString("Disappearing messages", comment: "chat feature")
@@ -730,7 +713,6 @@ public enum GroupFeature: String, Decodable, Feature {
case .reactions: return NSLocalizedString("Message reactions", comment: "chat feature") case .reactions: return NSLocalizedString("Message reactions", comment: "chat feature")
case .voice: return NSLocalizedString("Voice messages", comment: "chat feature") case .voice: return NSLocalizedString("Voice messages", comment: "chat feature")
case .files: return NSLocalizedString("Files and media", comment: "chat feature") case .files: return NSLocalizedString("Files and media", comment: "chat feature")
case .simplexLinks: return NSLocalizedString("SimpleX links", comment: "chat feature")
case .history: return NSLocalizedString("Visible history", comment: "chat feature") case .history: return NSLocalizedString("Visible history", comment: "chat feature")
} }
} }
@@ -743,7 +725,6 @@ public enum GroupFeature: String, Decodable, Feature {
case .reactions: return "face.smiling" case .reactions: return "face.smiling"
case .voice: return "mic" case .voice: return "mic"
case .files: return "doc" case .files: return "doc"
case .simplexLinks: return "link.circle"
case .history: return "clock" case .history: return "clock"
} }
} }
@@ -756,7 +737,6 @@ public enum GroupFeature: String, Decodable, Feature {
case .reactions: return "face.smiling.fill" case .reactions: return "face.smiling.fill"
case .voice: return "mic.fill" case .voice: return "mic.fill"
case .files: return "doc.fill" case .files: return "doc.fill"
case .simplexLinks: return "link.circle.fill"
case .history: return "clock.fill" case .history: return "clock.fill"
} }
} }
@@ -801,11 +781,6 @@ public enum GroupFeature: String, Decodable, Feature {
case .on: return "Allow to send files and media." case .on: return "Allow to send files and media."
case .off: return "Prohibit sending files and media." case .off: return "Prohibit sending files and media."
} }
case .simplexLinks:
switch enabled {
case .on: return "Allow to send SimpleX links."
case .off: return "Prohibit sending SimpleX links."
}
case .history: case .history:
switch enabled { switch enabled {
case .on: return "Send up to 100 last messages to new members." case .on: return "Send up to 100 last messages to new members."
@@ -844,11 +819,6 @@ public enum GroupFeature: String, Decodable, Feature {
case .on: return "Group members can send files and media." case .on: return "Group members can send files and media."
case .off: return "Files and media are prohibited in this group." case .off: return "Files and media are prohibited in this group."
} }
case .simplexLinks:
switch enabled {
case .on: return "Group members can send SimpleX links."
case .off: return "SimpleX links are prohibited in this group."
}
case .history: case .history:
switch enabled { switch enabled {
case .on: return "Up to 100 last messages are sent to new members." case .on: return "Up to 100 last messages are sent to new members."
@@ -988,22 +958,20 @@ public enum FeatureAllowed: String, Codable, Identifiable {
public struct FullGroupPreferences: Decodable, Equatable { public struct FullGroupPreferences: Decodable, Equatable {
public var timedMessages: TimedMessagesGroupPreference public var timedMessages: TimedMessagesGroupPreference
public var directMessages: RoleGroupPreference public var directMessages: GroupPreference
public var fullDelete: GroupPreference public var fullDelete: GroupPreference
public var reactions: GroupPreference public var reactions: GroupPreference
public var voice: RoleGroupPreference public var voice: GroupPreference
public var files: RoleGroupPreference public var files: GroupPreference
public var simplexLinks: RoleGroupPreference
public var history: GroupPreference public var history: GroupPreference
public init( public init(
timedMessages: TimedMessagesGroupPreference, timedMessages: TimedMessagesGroupPreference,
directMessages: RoleGroupPreference, directMessages: GroupPreference,
fullDelete: GroupPreference, fullDelete: GroupPreference,
reactions: GroupPreference, reactions: GroupPreference,
voice: RoleGroupPreference, voice: GroupPreference,
files: RoleGroupPreference, files: GroupPreference,
simplexLinks: RoleGroupPreference,
history: GroupPreference history: GroupPreference
) { ) {
self.timedMessages = timedMessages self.timedMessages = timedMessages
@@ -1012,40 +980,36 @@ public struct FullGroupPreferences: Decodable, Equatable {
self.reactions = reactions self.reactions = reactions
self.voice = voice self.voice = voice
self.files = files self.files = files
self.simplexLinks = simplexLinks
self.history = history self.history = history
} }
public static let sampleData = FullGroupPreferences( public static let sampleData = FullGroupPreferences(
timedMessages: TimedMessagesGroupPreference(enable: .off), timedMessages: TimedMessagesGroupPreference(enable: .off),
directMessages: RoleGroupPreference(enable: .off, role: nil), directMessages: GroupPreference(enable: .off),
fullDelete: GroupPreference(enable: .off), fullDelete: GroupPreference(enable: .off),
reactions: GroupPreference(enable: .on), reactions: GroupPreference(enable: .on),
voice: RoleGroupPreference(enable: .on, role: nil), voice: GroupPreference(enable: .on),
files: RoleGroupPreference(enable: .on, role: nil), files: GroupPreference(enable: .on),
simplexLinks: RoleGroupPreference(enable: .on, role: nil),
history: GroupPreference(enable: .on) history: GroupPreference(enable: .on)
) )
} }
public struct GroupPreferences: Codable { public struct GroupPreferences: Codable {
public var timedMessages: TimedMessagesGroupPreference? public var timedMessages: TimedMessagesGroupPreference?
public var directMessages: RoleGroupPreference? public var directMessages: GroupPreference?
public var fullDelete: GroupPreference? public var fullDelete: GroupPreference?
public var reactions: GroupPreference? public var reactions: GroupPreference?
public var voice: RoleGroupPreference? public var voice: GroupPreference?
public var files: RoleGroupPreference? public var files: GroupPreference?
public var simplexLinks: RoleGroupPreference?
public var history: GroupPreference? public var history: GroupPreference?
public init( public init(
timedMessages: TimedMessagesGroupPreference? = nil, timedMessages: TimedMessagesGroupPreference? = nil,
directMessages: RoleGroupPreference? = nil, directMessages: GroupPreference? = nil,
fullDelete: GroupPreference? = nil, fullDelete: GroupPreference? = nil,
reactions: GroupPreference? = nil, reactions: GroupPreference? = nil,
voice: RoleGroupPreference? = nil, voice: GroupPreference? = nil,
files: RoleGroupPreference? = nil, files: GroupPreference? = nil,
simplexLinks: RoleGroupPreference? = nil,
history: GroupPreference? = nil history: GroupPreference? = nil
) { ) {
self.timedMessages = timedMessages self.timedMessages = timedMessages
@@ -1054,18 +1018,16 @@ public struct GroupPreferences: Codable {
self.reactions = reactions self.reactions = reactions
self.voice = voice self.voice = voice
self.files = files self.files = files
self.simplexLinks = simplexLinks
self.history = history self.history = history
} }
public static let sampleData = GroupPreferences( public static let sampleData = GroupPreferences(
timedMessages: TimedMessagesGroupPreference(enable: .off), timedMessages: TimedMessagesGroupPreference(enable: .off),
directMessages: RoleGroupPreference(enable: .off, role: nil), directMessages: GroupPreference(enable: .off),
fullDelete: GroupPreference(enable: .off), fullDelete: GroupPreference(enable: .off),
reactions: GroupPreference(enable: .on), reactions: GroupPreference(enable: .on),
voice: RoleGroupPreference(enable: .on, role: nil), voice: GroupPreference(enable: .on),
files: RoleGroupPreference(enable: .on, role: nil), files: GroupPreference(enable: .on),
simplexLinks: RoleGroupPreference(enable: .on, role: nil),
history: GroupPreference(enable: .on) history: GroupPreference(enable: .on)
) )
} }
@@ -1078,7 +1040,6 @@ public func toGroupPreferences(_ fullPreferences: FullGroupPreferences) -> Group
reactions: fullPreferences.reactions, reactions: fullPreferences.reactions,
voice: fullPreferences.voice, voice: fullPreferences.voice,
files: fullPreferences.files, files: fullPreferences.files,
simplexLinks: fullPreferences.simplexLinks,
history: fullPreferences.history history: fullPreferences.history
) )
} }
@@ -1090,37 +1051,11 @@ public struct GroupPreference: Codable, Equatable {
enable == .on enable == .on
} }
public func enabled(_ role: GroupMemberRole?, for m: GroupMember?) -> GroupFeatureEnabled {
switch enable {
case .off: .off
case .on:
if let role, let m {
m.memberRole >= role ? .on : .off
} else {
.on
}
}
}
public init(enable: GroupFeatureEnabled) { public init(enable: GroupFeatureEnabled) {
self.enable = enable self.enable = enable
} }
} }
public struct RoleGroupPreference: Codable, Equatable {
public var enable: GroupFeatureEnabled
public var role: GroupMemberRole?
public func on(for m: GroupMember) -> Bool {
enable == .on && m.memberRole >= (role ?? .observer)
}
public init(enable: GroupFeatureEnabled, role: GroupMemberRole?) {
self.enable = enable
self.role = role
}
}
public struct TimedMessagesGroupPreference: Codable, Equatable { public struct TimedMessagesGroupPreference: Codable, Equatable {
public var enable: GroupFeatureEnabled public var enable: GroupFeatureEnabled
public var ttl: Int? public var ttl: Int?
@@ -1345,7 +1280,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat {
case .timedMessages: return prefs.timedMessages.on case .timedMessages: return prefs.timedMessages.on
case .fullDelete: return prefs.fullDelete.on case .fullDelete: return prefs.fullDelete.on
case .reactions: return prefs.reactions.on case .reactions: return prefs.reactions.on
case .voice: return prefs.voice.on(for: groupInfo.membership) case .voice: return prefs.voice.on
case .calls: return false case .calls: return false
} }
case .local: case .local:
@@ -1388,7 +1323,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat {
return .other return .other
} }
case let .group(groupInfo): case let .group(groupInfo):
if !groupInfo.fullGroupPreferences.voice.on(for: groupInfo.membership) { if !groupInfo.fullGroupPreferences.voice.on {
return .groupOwnerCan return .groupOwnerCan
} else { } else {
return .other return .other
@@ -2040,7 +1975,7 @@ public struct GroupMemberIds: Decodable {
var groupId: Int64 var groupId: Int64
} }
public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Codable { public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Decodable {
case observer = "observer" case observer = "observer"
case author = "author" case author = "author"
case member = "member" case member = "member"
@@ -2437,10 +2372,10 @@ public struct ChatItem: Identifiable, Decodable {
} }
} }
public static func getSample (_ id: Int64, _ dir: CIDirection, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, quotedItem: CIQuote? = nil, file: CIFile? = nil, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, deletable: Bool = true, editable: Bool = true) -> ChatItem { public static func getSample (_ id: Int64, _ dir: CIDirection, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, quotedItem: CIQuote? = nil, file: CIFile? = nil, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, editable: Bool = true) -> ChatItem {
ChatItem( ChatItem(
chatDir: dir, chatDir: dir,
meta: CIMeta.getSample(id, ts, text, status, itemDeleted: itemDeleted, itemEdited: itemEdited, itemLive: itemLive, deletable: deletable, editable: editable), meta: CIMeta.getSample(id, ts, text, status, itemDeleted: itemDeleted, itemEdited: itemEdited, itemLive: itemLive, editable: editable),
content: .sndMsgContent(msgContent: .text(text)), content: .sndMsgContent(msgContent: .text(text)),
quotedItem: quotedItem, quotedItem: quotedItem,
file: file file: file
@@ -2531,7 +2466,6 @@ public struct ChatItem: Identifiable, Decodable {
itemDeleted: nil, itemDeleted: nil,
itemEdited: false, itemEdited: false,
itemLive: false, itemLive: false,
deletable: false,
editable: false editable: false
), ),
content: .rcvDeleted(deleteMode: .cidmBroadcast), content: .rcvDeleted(deleteMode: .cidmBroadcast),
@@ -2553,7 +2487,6 @@ public struct ChatItem: Identifiable, Decodable {
itemDeleted: nil, itemDeleted: nil,
itemEdited: false, itemEdited: false,
itemLive: true, itemLive: true,
deletable: false,
editable: false editable: false
), ),
content: .sndMsgContent(msgContent: .text("")), content: .sndMsgContent(msgContent: .text("")),
@@ -2613,12 +2546,10 @@ public struct CIMeta: Decodable {
public var itemStatus: CIStatus public var itemStatus: CIStatus
public var createdAt: Date public var createdAt: Date
public var updatedAt: Date public var updatedAt: Date
public var itemForwarded: CIForwardedFrom?
public var itemDeleted: CIDeleted? public var itemDeleted: CIDeleted?
public var itemEdited: Bool public var itemEdited: Bool
public var itemTimed: CITimed? public var itemTimed: CITimed?
public var itemLive: Bool? public var itemLive: Bool?
public var deletable: Bool
public var editable: Bool public var editable: Bool
public var timestampText: Text { get { formatTimestampText(itemTs) } } public var timestampText: Text { get { formatTimestampText(itemTs) } }
@@ -2635,7 +2566,7 @@ public struct CIMeta: Decodable {
itemStatus.statusIcon(metaColor) itemStatus.statusIcon(metaColor)
} }
public static func getSample(_ id: Int64, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, deletable: Bool = true, editable: Bool = true) -> CIMeta { public static func getSample(_ id: Int64, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, editable: Bool = true) -> CIMeta {
CIMeta( CIMeta(
itemId: id, itemId: id,
itemTs: ts, itemTs: ts,
@@ -2646,7 +2577,6 @@ public struct CIMeta: Decodable {
itemDeleted: itemDeleted, itemDeleted: itemDeleted,
itemEdited: itemEdited, itemEdited: itemEdited,
itemLive: itemLive, itemLive: itemLive,
deletable: deletable,
editable: editable editable: editable
) )
} }
@@ -2662,7 +2592,6 @@ public struct CIMeta: Decodable {
itemDeleted: nil, itemDeleted: nil,
itemEdited: false, itemEdited: false,
itemLive: false, itemLive: false,
deletable: false,
editable: false editable: false
) )
} }
@@ -2784,31 +2713,6 @@ public enum CIDeleted: Decodable {
} }
} }
public enum MsgDirection: String, Decodable {
case rcv = "rcv"
case snd = "snd"
}
public enum CIForwardedFrom: Decodable {
case unknown
case contact(chatName: String, msgDir: MsgDirection, contactId: Int64?, chatItemId: Int64?)
case group(chatName: String, msgDir: MsgDirection, groupId: Int64?, chatItemId: Int64?)
var chatName: String {
switch self {
case .unknown: ""
case let .contact(chatName, _, _, _): chatName
case let .group(chatName, _, _, _): chatName
}
}
public func text(_ chatType: ChatType) -> LocalizedStringKey {
chatType == .local
? (chatName == "" ? "saved" : "saved from \(chatName)")
: "forwarded"
}
}
public enum CIDeleteMode: String, Decodable { public enum CIDeleteMode: String, Decodable {
case cidmBroadcast = "broadcast" case cidmBroadcast = "broadcast"
case cidmInternal = "internal" case cidmInternal = "internal"
@@ -2838,8 +2742,8 @@ public enum CIContent: Decodable, ItemContent {
case sndChatFeature(feature: ChatFeature, enabled: FeatureEnabled, param: Int?) case sndChatFeature(feature: ChatFeature, enabled: FeatureEnabled, param: Int?)
case rcvChatPreference(feature: ChatFeature, allowed: FeatureAllowed, param: Int?) case rcvChatPreference(feature: ChatFeature, allowed: FeatureAllowed, param: Int?)
case sndChatPreference(feature: ChatFeature, allowed: FeatureAllowed, param: Int?) case sndChatPreference(feature: ChatFeature, allowed: FeatureAllowed, param: Int?)
case rcvGroupFeature(groupFeature: GroupFeature, preference: GroupPreference, param: Int?, memberRole_: GroupMemberRole?) case rcvGroupFeature(groupFeature: GroupFeature, preference: GroupPreference, param: Int?)
case sndGroupFeature(groupFeature: GroupFeature, preference: GroupPreference, param: Int?, memberRole_: GroupMemberRole?) case sndGroupFeature(groupFeature: GroupFeature, preference: GroupPreference, param: Int?)
case rcvChatFeatureRejected(feature: ChatFeature) case rcvChatFeatureRejected(feature: ChatFeature)
case rcvGroupFeatureRejected(groupFeature: GroupFeature) case rcvGroupFeatureRejected(groupFeature: GroupFeature)
case sndModerated case sndModerated
@@ -2873,8 +2777,8 @@ public enum CIContent: Decodable, ItemContent {
case let .sndChatFeature(feature, enabled, param): return CIContent.featureText(feature, enabled.text, param) case let .sndChatFeature(feature, enabled, param): return CIContent.featureText(feature, enabled.text, param)
case let .rcvChatPreference(feature, allowed, param): return CIContent.preferenceText(feature, allowed, param) case let .rcvChatPreference(feature, allowed, param): return CIContent.preferenceText(feature, allowed, param)
case let .sndChatPreference(feature, allowed, param): return CIContent.preferenceText(feature, allowed, param) case let .sndChatPreference(feature, allowed, param): return CIContent.preferenceText(feature, allowed, param)
case let .rcvGroupFeature(feature, preference, param, role): return CIContent.featureText(feature, preference.enable.text, param, role) case let .rcvGroupFeature(feature, preference, param): return CIContent.featureText(feature, preference.enable.text, param)
case let .sndGroupFeature(feature, preference, param, role): return CIContent.featureText(feature, preference.enable.text, param, role) case let .sndGroupFeature(feature, preference, param): return CIContent.featureText(feature, preference.enable.text, param)
case let .rcvChatFeatureRejected(feature): return String.localizedStringWithFormat("%@: received, prohibited", feature.text) case let .rcvChatFeatureRejected(feature): return String.localizedStringWithFormat("%@: received, prohibited", feature.text)
case let .rcvGroupFeatureRejected(groupFeature): return String.localizedStringWithFormat("%@: received, prohibited", groupFeature.text) case let .rcvGroupFeatureRejected(groupFeature): return String.localizedStringWithFormat("%@: received, prohibited", groupFeature.text)
case .sndModerated: return NSLocalizedString("moderated", comment: "moderated chat item") case .sndModerated: return NSLocalizedString("moderated", comment: "moderated chat item")
@@ -2899,25 +2803,10 @@ public enum CIContent: Decodable, ItemContent {
NSLocalizedString("This chat is protected by end-to-end encryption.", comment: "E2EE info chat item") NSLocalizedString("This chat is protected by end-to-end encryption.", comment: "E2EE info chat item")
} }
static func featureText(_ feature: Feature, _ enabled: String, _ param: Int?, _ role: GroupMemberRole? = nil) -> String { static func featureText(_ feature: Feature, _ enabled: String, _ param: Int?) -> String {
( feature.hasParam
feature.hasParam ? "\(feature.text): \(timeText(param))"
? "\(feature.text): \(timeText(param))" : "\(feature.text): \(enabled)"
: "\(feature.text): \(enabled)"
) +
(
feature.hasRole && role != nil
? " (\(roleText(role)))"
: ""
)
}
private static func roleText(_ role: GroupMemberRole?) -> String {
switch role {
case .owner: NSLocalizedString("owners", comment: "feature role")
case .admin: NSLocalizedString("admins", comment: "feature role")
default: NSLocalizedString("all members", comment: "feature role")
}
} }
public static func preferenceText(_ feature: Feature, _ allowed: FeatureAllowed, _ param: Int?) -> String { public static func preferenceText(_ feature: Feature, _ allowed: FeatureAllowed, _ param: Int?) -> String {
@@ -3862,7 +3751,6 @@ public enum ChatItemTTL: Hashable, Identifiable, Comparable {
public struct ChatItemInfo: Decodable { public struct ChatItemInfo: Decodable {
public var itemVersions: [ChatItemVersion] public var itemVersions: [ChatItemVersion]
public var memberDeliveryStatuses: [MemberDeliveryStatus]? public var memberDeliveryStatuses: [MemberDeliveryStatus]?
public var forwardedFromChatItem: AChatItem?
} }
public struct ChatItemVersion: Decodable { public struct ChatItemVersion: Decodable {
Binary file not shown.
Binary file not shown.
@@ -48,7 +48,12 @@ android {
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
} }
} }
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions { kotlinOptions {
jvmTarget = "11"
freeCompilerArgs += "-opt-in=kotlinx.coroutines.DelicateCoroutinesApi" freeCompilerArgs += "-opt-in=kotlinx.coroutines.DelicateCoroutinesApi"
freeCompilerArgs += "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi" freeCompilerArgs += "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi"
freeCompilerArgs += "-opt-in=androidx.compose.ui.text.ExperimentalTextApi" freeCompilerArgs += "-opt-in=androidx.compose.ui.text.ExperimentalTextApi"
@@ -89,12 +89,11 @@ class MainActivity: FragmentActivity() {
} }
override fun onBackPressed() { override fun onBackPressed() {
val canFinishActivity = ( if (
onBackPressedDispatcher.hasEnabledCallbacks() // Has something to do in a backstack onBackPressedDispatcher.hasEnabledCallbacks() // Has something to do in a backstack
|| Build.VERSION.SDK_INT >= Build.VERSION_CODES.R // Android 11 or above || Build.VERSION.SDK_INT >= Build.VERSION_CODES.R // Android 11 or above
|| isTaskRoot // there are still other tasks after we reach the main (home) activity || isTaskRoot // there are still other tasks after we reach the main (home) activity
) && SimplexApp.context.chatModel.sharedContent.value !is SharedContent.Forward ) {
if (canFinishActivity) {
// https://medium.com/mobile-app-development-publication/the-risk-of-android-strandhogg-security-issue-and-how-it-can-be-mitigated-80d2ddb4af06 // https://medium.com/mobile-app-development-publication/the-risk-of-android-strandhogg-security-issue-and-how-it-can-be-mitigated-80d2ddb4af06
super.onBackPressed() super.onBackPressed()
} }
@@ -105,15 +104,9 @@ class MainActivity: FragmentActivity() {
AppLock.laFailed.value = true AppLock.laFailed.value = true
} }
if (!onBackPressedDispatcher.hasEnabledCallbacks()) { if (!onBackPressedDispatcher.hasEnabledCallbacks()) {
val sharedContent = chatModel.sharedContent.value
// Drop shared content // Drop shared content
chatModel.sharedContent.value = null SimplexApp.context.chatModel.sharedContent.value = null
if (sharedContent is SharedContent.Forward) { finish()
chatModel.chatId.value = sharedContent.fromChatInfo.id
}
if (canFinishActivity) {
finish()
}
} }
} }
} }
@@ -16,7 +16,8 @@ import androidx.work.*
import chat.simplex.app.model.NtfManager import chat.simplex.app.model.NtfManager
import chat.simplex.app.model.NtfManager.AcceptCallAction import chat.simplex.app.model.NtfManager.AcceptCallAction
import chat.simplex.app.views.call.CallActivity import chat.simplex.app.views.call.CallActivity
import chat.simplex.common.helpers.* import chat.simplex.common.helpers.APPLICATION_ID
import chat.simplex.common.helpers.requiresIgnoringBattery
import chat.simplex.common.model.* import chat.simplex.common.model.*
import chat.simplex.common.model.ChatController.appPrefs import chat.simplex.common.model.ChatController.appPrefs
import chat.simplex.common.model.ChatModel.updatingChatsMutex import chat.simplex.common.model.ChatModel.updatingChatsMutex
@@ -290,10 +291,6 @@ class SimplexApp: Application(), LifecycleEventObserver {
activeCallDestroyWebView() activeCallDestroyWebView()
} }
override fun androidRestartNetworkObserver() {
NetworkObserver.shared.restartNetworkObserver()
}
@SuppressLint("SourceLockedOrientationActivity") @SuppressLint("SourceLockedOrientationActivity")
@Composable @Composable
override fun androidLockPortraitOrientation() { override fun androidLockPortraitOrientation() {
-24
View File
@@ -83,30 +83,6 @@ plugins {
id("org.jetbrains.kotlin.plugin.serialization") apply false id("org.jetbrains.kotlin.plugin.serialization") apply false
} }
// https://raymondctc.medium.com/configuring-your-sourcecompatibility-targetcompatibility-and-kotlinoptions-jvmtarget-all-at-once-66bf2198145f
val jvmVersion: Provider<String> = providers.gradleProperty("kotlin.jvm.target")
configure(subprojects) {
// Apply compileOptions to subprojects
plugins.withType<com.android.build.gradle.BasePlugin>().configureEach {
extensions.findByType<com.android.build.gradle.BaseExtension>()?.apply {
jvmVersion.map { JavaVersion.toVersion(it) }.orNull?.let {
compileOptions {
sourceCompatibility = it
targetCompatibility = it
}
}
}
}
// Apply kotlinOptions.jvmTarget to subprojects
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
kotlinOptions {
if (jvmVersion.isPresent) jvmTarget = jvmVersion.get()
}
}
}
tasks.register("clean", Delete::class) { tasks.register("clean", Delete::class) {
delete(rootProject.buildDir) delete(rootProject.buildDir)
} }
+7 -1
View File
@@ -12,7 +12,9 @@ version = extra["android.version_name"] as String
kotlin { kotlin {
androidTarget() androidTarget()
jvm("desktop") jvm("desktop") {
jvmToolchain(11)
}
applyDefaultHierarchyTemplate() applyDefaultHierarchyTemplate()
sourceSets { sourceSets {
all { all {
@@ -117,6 +119,10 @@ android {
} }
testOptions.targetSdk = 33 testOptions.targetSdk = 33
lint.targetSdk = 33 lint.targetSdk = 33
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
val isAndroid = gradle.startParameter.taskNames.find { val isAndroid = gradle.startParameter.taskNames.find {
val lower = it.lowercase() val lower = it.lowercase()
lower.contains("release") || lower.startsWith("assemble") || lower.startsWith("install") lower.contains("release") || lower.startsWith("assemble") || lower.startsWith("install")
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="chat.simplex.common"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="chat.simplex.common">
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest> </manifest>
@@ -1,104 +0,0 @@
package chat.simplex.common.helpers
import android.net.*
import android.util.Log
import androidx.core.content.getSystemService
import chat.simplex.common.model.ChatModel.controller
import chat.simplex.common.model.UserNetworkInfo
import chat.simplex.common.model.UserNetworkType
import chat.simplex.common.platform.*
import chat.simplex.common.views.helpers.withBGApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
class NetworkObserver {
private var prevInfo: UserNetworkInfo? = null
// When having both mobile and Wi-Fi networks enabled with Wi-Fi being active, then disabling Wi-Fi, network reports its offline (which is true)
// but since it will be online after switching to mobile, there is no need to inform backend about such temporary change.
// But if it will not be online after some seconds, report it and apply required measures
private var noNetworkJob = Job() as Job
private val networkCallback = object: ConnectivityManager.NetworkCallback() {
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) = networkCapabilitiesChanged(networkCapabilities)
override fun onLost(network: Network) = networkLost()
}
private val connectivityManager: ConnectivityManager? = androidAppContext.getSystemService()
fun restartNetworkObserver() {
if (connectivityManager == null) {
Log.e(TAG, "Connectivity manager is unavailable, network observer is disabled")
val info = UserNetworkInfo(
networkType = UserNetworkType.OTHER,
online = true,
)
prevInfo = info
setNetworkInfo(info)
return
}
try {
connectivityManager.unregisterNetworkCallback(networkCallback)
} catch (e: Exception) {
// do nothing
}
val initialCapabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
if (initialCapabilities != null) {
networkCapabilitiesChanged(initialCapabilities)
} else {
networkLost()
}
try {
connectivityManager.registerDefaultNetworkCallback(networkCallback)
} catch (e: Exception) {
Log.e(TAG, "Error registering network callback: ${e.stackTraceToString()}")
}
}
private fun networkCapabilitiesChanged(capabilities: NetworkCapabilities) {
connectivityManager ?: return
val info = UserNetworkInfo(
networkType = networkTypeFromCapabilities(capabilities),
online = capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED),
)
if (prevInfo != info) {
prevInfo = info
setNetworkInfo(info)
}
}
private fun networkLost() {
Log.d(TAG, "Network changed: lost")
val none = UserNetworkInfo(networkType = UserNetworkType.NONE, false)
prevInfo = none
setNetworkInfo(none)
}
private fun setNetworkInfo(info: UserNetworkInfo) {
Log.d(TAG, "Network changed: $info")
noNetworkJob.cancel()
if (info.online) {
withBGApi {
if (controller.hasChatCtrl() && controller.apiSetNetworkInfo(info)) {
chatModel.networkInfo.value = info
}
}
} else {
noNetworkJob = withBGApi {
delay(3000)
if (controller.hasChatCtrl() && controller.apiSetNetworkInfo(info)) {
chatModel.networkInfo.value = info
}
}
}
}
private fun networkTypeFromCapabilities(capabilities: NetworkCapabilities): UserNetworkType = when {
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> UserNetworkType.ETHERNET
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> UserNetworkType.WIFI
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> UserNetworkType.CELLULAR
else -> UserNetworkType.OTHER
}
companion object {
val shared = NetworkObserver()
}
}
@@ -2,16 +2,17 @@ package chat.simplex.common.helpers
import android.media.* import android.media.*
import android.net.Uri import android.net.Uri
import android.os.* import android.os.VibrationEffect
import android.os.Vibrator
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import chat.simplex.common.R import chat.simplex.common.R
import chat.simplex.common.platform.* import chat.simplex.common.platform.SoundPlayerInterface
import chat.simplex.common.views.helpers.withApi import chat.simplex.common.platform.androidAppContext
import kotlinx.coroutines.* import kotlinx.coroutines.*
object SoundPlayer: SoundPlayerInterface { object SoundPlayer: SoundPlayerInterface {
private var player: MediaPlayer? = null private var player: MediaPlayer? = null
private var playing = false var playing = false
override fun start(scope: CoroutineScope, sound: Boolean) { override fun start(scope: CoroutineScope, sound: Boolean) {
player?.reset() player?.reset()
@@ -31,7 +32,7 @@ object SoundPlayer: SoundPlayerInterface {
scope.launch { scope.launch {
while (playing) { while (playing) {
if (sound) player?.start() if (sound) player?.start()
vibrator?.vibrateApiVersionAware(effect) vibrator?.vibrate(effect)
delay(3500) delay(3500)
} }
} }
@@ -42,82 +43,3 @@ object SoundPlayer: SoundPlayerInterface {
player?.stop() player?.stop()
} }
} }
object CallSoundsPlayer: CallSoundsPlayerInterface {
private var player: MediaPlayer? = null
private var playingJob: Job? = null
private fun start(soundPath: String, delay: Long, scope: CoroutineScope) {
playingJob?.cancel()
player?.reset()
player = MediaPlayer().apply {
setAudioAttributes(
AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
.build()
)
setDataSource(androidAppContext, Uri.parse(soundPath))
prepare()
}
if (delay < 1000) {
player?.isLooping = true
player?.start()
return
}
playingJob = scope.launch {
while (isActive) {
player?.start()
delay(delay)
}
}
}
override fun startConnectingCallSound(scope: CoroutineScope) {
// Taken from https://github.com/TelegramOrg/Telegram-Android
// https://github.com/TelegramOrg/Telegram-Android/blob/master/LICENSE
start("android.resource://" + androidAppContext.packageName + "/" + R.raw.connecting_call, 0, scope)
}
override fun startInCallSound(scope: CoroutineScope) {
start("android.resource://" + androidAppContext.packageName + "/" + R.raw.in_call, 2000, scope)
}
override fun vibrate(times: Int) {
val vibrator = ContextCompat.getSystemService(androidAppContext, Vibrator::class.java)
val effect = VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE)
vibrator?.vibrateApiVersionAware(effect)
repeat(times - 1) {
withApi {
delay(50)
vibrator?.vibrateApiVersionAware(effect)
}
}
}
override fun stop() {
playingJob?.cancel()
player?.stop()
}
}
private fun Vibrator.vibrateApiVersionAware(effect: VibrationEffect) {
if (Build.VERSION.SDK_INT >= 33) {
vibrate(
effect,
VibrationAttributes.Builder()
.setUsage(VibrationAttributes.USAGE_ALARM)
.build()
)
} else if (Build.VERSION.SDK_INT >= 29) {
vibrate(
effect,
AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ALARM)
.build()
)
} else {
vibrate(effect)
}
}
@@ -296,7 +296,6 @@ actual object AudioPlayer: AudioPlayerInterface {
} }
actual typealias SoundPlayer = chat.simplex.common.helpers.SoundPlayer actual typealias SoundPlayer = chat.simplex.common.helpers.SoundPlayer
actual typealias CallSoundsPlayer = chat.simplex.common.helpers.CallSoundsPlayer
class CryptoMediaSource(val data: ByteArray) : MediaDataSource() { class CryptoMediaSource(val data: ByteArray) : MediaDataSource() {
override fun readAt(position: Long, buffer: ByteArray, offset: Int, size: Int): Int { override fun readAt(position: Long, buffer: ByteArray, offset: Int, size: Int): Int {
@@ -1,235 +0,0 @@
package chat.simplex.common.views.call
import android.content.Context
import android.media.*
import android.media.AudioManager.OnCommunicationDeviceChangedListener
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.compose.runtime.*
import chat.simplex.common.platform.*
import dev.icerock.moko.resources.ImageResource
import chat.simplex.res.MR
import dev.icerock.moko.resources.StringResource
import java.util.concurrent.Executors
interface CallAudioDeviceManagerInterface {
val devices: State<List<AudioDeviceInfo>>
val currentDevice: MutableState<AudioDeviceInfo?>
fun start()
fun stop()
// AudioDeviceInfo.AudioDeviceType
fun selectLastExternalDeviceOrDefault(speaker: Boolean, keepAnyNonEarpiece: Boolean)
// AudioDeviceInfo.AudioDeviceType
fun selectDevice(id: Int)
companion object {
fun new(): CallAudioDeviceManagerInterface =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PostSCallAudioDeviceManager()
} else {
PreSCallAudioDeviceManager()
}
}
}
@RequiresApi(Build.VERSION_CODES.S)
class PostSCallAudioDeviceManager: CallAudioDeviceManagerInterface {
private val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
override val devices: MutableState<List<AudioDeviceInfo>> = mutableStateOf(emptyList())
override val currentDevice: MutableState<AudioDeviceInfo?> = mutableStateOf(null)
private val audioCallback = object: AudioDeviceCallback() {
override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>) {
Log.d(TAG, "Added audio devices: ${addedDevices.map { it.type }}")
super.onAudioDevicesAdded(addedDevices)
val oldDevices = devices.value
devices.value = am.availableCommunicationDevices
Log.d(TAG, "Added audio devices2: ${devices.value.map { it.type }}")
if (devices.value.size - oldDevices.size > 0) {
selectLastExternalDeviceOrDefault(chatModel.activeCall.value?.supportsVideo() == true, false)
}
}
override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>) {
Log.d(TAG, "Removed audio devices: ${removedDevices.map { it.type }}")
super.onAudioDevicesRemoved(removedDevices)
devices.value = am.availableCommunicationDevices
}
}
private val listener: OnCommunicationDeviceChangedListener = OnCommunicationDeviceChangedListener { device ->
devices.value = am.availableCommunicationDevices
currentDevice.value = device
}
override fun start() {
am.registerAudioDeviceCallback(audioCallback, null)
am.addOnCommunicationDeviceChangedListener(Executors.newSingleThreadExecutor(), listener)
}
override fun stop() {
am.unregisterAudioDeviceCallback(audioCallback)
am.removeOnCommunicationDeviceChangedListener(listener)
}
override fun selectLastExternalDeviceOrDefault(speaker: Boolean, keepAnyNonEarpiece: Boolean) {
Log.d(TAG, "selectLastExternalDeviceOrDefault: set audio mode, speaker enabled: $speaker")
am.mode = AudioManager.MODE_IN_COMMUNICATION
val commDevice = am.communicationDevice
if (keepAnyNonEarpiece && commDevice != null && commDevice.type != AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) {
// some external device or speaker selected already, no need to change it
return
}
val preferredSecondaryDevice = if (speaker) AudioDeviceInfo.TYPE_BUILTIN_SPEAKER else AudioDeviceInfo.TYPE_BUILTIN_EARPIECE
val externalDevice = devices.value.lastOrNull { it.type != AudioDeviceInfo.TYPE_BUILTIN_SPEAKER && it.type != AudioDeviceInfo.TYPE_BUILTIN_EARPIECE }
// External device already selected
if (externalDevice != null && externalDevice.type == am.communicationDevice?.type) {
return
}
if (externalDevice != null) {
am.setCommunicationDevice(externalDevice)
} else if (am.communicationDevice?.type != preferredSecondaryDevice) {
am.availableCommunicationDevices.firstOrNull { it.type == preferredSecondaryDevice }?.let {
am.setCommunicationDevice(it)
}
}
}
override fun selectDevice(id: Int) {
am.mode = AudioManager.MODE_IN_COMMUNICATION
val device = devices.value.lastOrNull { it.id == id }
if (device != null && am.communicationDevice?.id != id ) {
am.setCommunicationDevice(device)
}
}
}
class PreSCallAudioDeviceManager: CallAudioDeviceManagerInterface {
private val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
override val devices: MutableState<List<AudioDeviceInfo>> = mutableStateOf(emptyList())
override val currentDevice: MutableState<AudioDeviceInfo?> = mutableStateOf(null)
private val audioCallback = object: AudioDeviceCallback() {
override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>) {
Log.d(TAG, "Added audio devices: ${addedDevices.map { it.type }}")
super.onAudioDevicesAdded(addedDevices)
val wasSize = devices.value.size
devices.value += addedDevices.filter { it.hasSupportedType() }
val addedCount = devices.value.size - wasSize
//if (addedCount > 0 && chatModel.activeCall.value?.callState == CallState.Connected) {
// Setting params in Connected state makes sure that Bluetooth will NOT be broken on Android < 12
selectLastExternalDeviceOrDefault(chatModel.activeCall.value?.supportsVideo() == true, false)
//}
}
override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>) {
Log.d(TAG, "Removed audio devices: ${removedDevices.map { it.type }}")
super.onAudioDevicesRemoved(removedDevices)
val wasSize = devices.value.size
devices.value = devices.value.filterNot { removedDevices.any { rm -> rm.id == it.id } }
//val removedCount = wasSize - devices.value.size
//if (devices.value.count { it.hasSupportedType() } == 2 && chatModel.activeCall.value?.callState == CallState.Connected) {
// Setting params in Connected state makes sure that Bluetooth will NOT be broken on Android < 12
selectLastExternalDeviceOrDefault(chatModel.activeCall.value?.supportsVideo() == true, true)
//}
}
}
override fun start() {
am.registerAudioDeviceCallback(audioCallback, null)
}
override fun stop() {
am.unregisterAudioDeviceCallback(audioCallback)
}
override fun selectLastExternalDeviceOrDefault(speaker: Boolean, keepAnyNonEarpiece: Boolean) {
Log.d(TAG, "selectLastExternalDeviceOrDefault: set audio mode, speaker enabled: $speaker")
val preferredSecondaryDevice = if (speaker) AudioDeviceInfo.TYPE_BUILTIN_SPEAKER else AudioDeviceInfo.TYPE_BUILTIN_EARPIECE
val externalDevice = devices.value.lastOrNull { it.hasSupportedType() && it.isSource && it.isSink && it.type != AudioDeviceInfo.TYPE_BUILTIN_SPEAKER && it.type != AudioDeviceInfo.TYPE_BUILTIN_EARPIECE }
if (externalDevice != null) {
selectDevice(externalDevice.id)
} else {
am.stopBluetoothSco()
am.isWiredHeadsetOn = false
am.isSpeakerphoneOn = preferredSecondaryDevice == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER
am.isBluetoothScoOn = false
val newCurrentDevice = devices.value.firstOrNull { it.type == preferredSecondaryDevice }
adaptToCurrentlyActiveDevice(newCurrentDevice)
}
}
override fun selectDevice(id: Int) {
val device = devices.value.lastOrNull { it.id == id }
val isExternalDevice = device != null && device.type != AudioDeviceInfo.TYPE_BUILTIN_SPEAKER && device.type != AudioDeviceInfo.TYPE_BUILTIN_EARPIECE
if (isExternalDevice) {
am.isSpeakerphoneOn = false
if (device?.type == AudioDeviceInfo.TYPE_WIRED_HEADPHONES) {
am.isWiredHeadsetOn = true
am.stopBluetoothSco()
am.isBluetoothScoOn = false
} else {
am.isWiredHeadsetOn = false
am.startBluetoothSco()
am.isBluetoothScoOn = true
}
adaptToCurrentlyActiveDevice(device)
} else {
am.stopBluetoothSco()
am.isWiredHeadsetOn = false
am.isSpeakerphoneOn = device?.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER
am.isBluetoothScoOn = false
adaptToCurrentlyActiveDevice(device)
}
}
private fun adaptToCurrentlyActiveDevice(newCurrentDevice: AudioDeviceInfo?) {
currentDevice.value = newCurrentDevice
}
private fun AudioDeviceInfo.hasSupportedType(): Boolean = when (type) {
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> true
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> true
AudioDeviceInfo.TYPE_BLUETOOTH_SCO -> true
AudioDeviceInfo.TYPE_BLE_HEADSET -> true
AudioDeviceInfo.TYPE_BLE_SPEAKER -> true
AudioDeviceInfo.TYPE_WIRED_HEADPHONES -> true
else -> false
}
}
val AudioDeviceInfo.icon: ImageResource
get() = when (this.type) {
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> MR.images.ic_volume_down
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> MR.images.ic_volume_up
AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
AudioDeviceInfo.TYPE_BLE_HEADSET,
AudioDeviceInfo.TYPE_BLE_SPEAKER -> MR.images.ic_bluetooth
AudioDeviceInfo.TYPE_WIRED_HEADPHONES -> MR.images.ic_headphones
AudioDeviceInfo.TYPE_USB_HEADSET, AudioDeviceInfo.TYPE_USB_DEVICE -> MR.images.ic_usb
else -> MR.images.ic_brand_awareness_filled
}
val AudioDeviceInfo.name: StringResource?
get() = when (this.type) {
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> MR.strings.audio_device_earpiece
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> MR.strings.audio_device_speaker
AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
AudioDeviceInfo.TYPE_BLE_HEADSET,
AudioDeviceInfo.TYPE_BLE_SPEAKER -> null // Use product name instead
AudioDeviceInfo.TYPE_WIRED_HEADPHONES -> MR.strings.audio_device_wired_headphones
AudioDeviceInfo.TYPE_USB_HEADSET, AudioDeviceInfo.TYPE_USB_DEVICE -> null // Use product name instead
else -> null // Use product name instead
}
@@ -26,7 +26,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
@@ -38,14 +39,14 @@ import androidx.webkit.WebViewAssetLoader
import androidx.webkit.WebViewClientCompat import androidx.webkit.WebViewClientCompat
import chat.simplex.common.helpers.showAllowPermissionInSettingsAlert import chat.simplex.common.helpers.showAllowPermissionInSettingsAlert
import chat.simplex.common.model.* import chat.simplex.common.model.*
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.* import chat.simplex.common.ui.theme.*
import chat.simplex.common.model.ChatModel
import chat.simplex.common.model.Contact
import chat.simplex.common.platform.*
import chat.simplex.common.views.helpers.* import chat.simplex.common.views.helpers.*
import chat.simplex.res.MR import chat.simplex.res.MR
import com.google.accompanist.permissions.* import com.google.accompanist.permissions.*
import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.StringResource
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
@@ -68,8 +69,7 @@ fun activeCallDestroyWebView() = withApi {
@SuppressLint("SourceLockedOrientationActivity") @SuppressLint("SourceLockedOrientationActivity")
@Composable @Composable
actual fun ActiveCallView() { actual fun ActiveCallView() {
val call = remember { chatModel.activeCall }.value val audioViaBluetooth = rememberSaveable { mutableStateOf(false) }
val scope = rememberCoroutineScope()
val proximityLock = remember { val proximityLock = remember {
val pm = (androidAppContext.getSystemService(Context.POWER_SERVICE) as PowerManager) val pm = (androidAppContext.getSystemService(Context.POWER_SERVICE) as PowerManager)
if (pm.isWakeLockLevelSupported(PROXIMITY_SCREEN_OFF_WAKE_LOCK)) { if (pm.isWakeLockLevelSupported(PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
@@ -78,23 +78,37 @@ actual fun ActiveCallView() {
null null
} }
} }
val wasConnected = rememberSaveable { mutableStateOf(false) }
LaunchedEffect(call) {
if (call?.callState == CallState.Connected && !wasConnected.value) {
CallSoundsPlayer.vibrate(2)
wasConnected.value = true
}
}
val callAudioDeviceManager = remember { CallAudioDeviceManagerInterface.new() }
DisposableEffect(Unit) { DisposableEffect(Unit) {
callAudioDeviceManager.start() val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
onDispose { var btDeviceCount = 0
CallSoundsPlayer.stop() val audioCallback = object: AudioDeviceCallback() {
if (wasConnected.value) { override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>) {
CallSoundsPlayer.vibrate() Log.d(TAG, "Added audio devices: ${addedDevices.map { it.type }}")
super.onAudioDevicesAdded(addedDevices)
val addedCount = addedDevices.count { it.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO }
btDeviceCount += addedCount
audioViaBluetooth.value = btDeviceCount > 0
if (addedCount > 0 && chatModel.activeCall.value?.callState == CallState.Connected) {
// Setting params in Connected state makes sure that Bluetooth will NOT be broken on Android < 12
setCallSound(chatModel.activeCall.value?.soundSpeaker ?: return, audioViaBluetooth)
}
} }
override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>) {
Log.d(TAG, "Removed audio devices: ${removedDevices.map { it.type }}")
super.onAudioDevicesRemoved(removedDevices)
val removedCount = removedDevices.count { it.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO }
btDeviceCount -= removedCount
audioViaBluetooth.value = btDeviceCount > 0
if (btDeviceCount == 0 && chatModel.activeCall.value?.callState == CallState.Connected) {
// Setting params in Connected state makes sure that Bluetooth will NOT be broken on Android < 12
setCallSound(chatModel.activeCall.value?.soundSpeaker ?: return, audioViaBluetooth)
}
}
}
am.registerAudioDeviceCallback(audioCallback, null)
onDispose {
dropAudioManagerOverrides() dropAudioManagerOverrides()
callAudioDeviceManager.stop() am.unregisterAudioDeviceCallback(audioCallback)
if (proximityLock?.isHeld == true) { if (proximityLock?.isHeld == true) {
proximityLock.release() proximityLock.release()
} }
@@ -108,6 +122,8 @@ actual fun ActiveCallView() {
if (proximityLock?.isHeld == false) proximityLock.acquire() if (proximityLock?.isHeld == false) proximityLock.acquire()
} }
} }
val scope = rememberCoroutineScope()
val call = chatModel.activeCall.value
Box(Modifier.fillMaxSize()) { Box(Modifier.fillMaxSize()) {
WebRTCView(chatModel.callCommand) { apiMsg -> WebRTCView(chatModel.callCommand) { apiMsg ->
Log.d(TAG, "received from WebRTCView: $apiMsg") Log.d(TAG, "received from WebRTCView: $apiMsg")
@@ -120,9 +136,6 @@ actual fun ActiveCallView() {
val callType = CallType(call.localMedia, r.capabilities) val callType = CallType(call.localMedia, r.capabilities)
chatModel.controller.apiSendCallInvitation(callRh, call.contact, callType) chatModel.controller.apiSendCallInvitation(callRh, call.contact, callType)
updateActiveCall(call) { it.copy(callState = CallState.InvitationSent, localCapabilities = r.capabilities) } updateActiveCall(call) { it.copy(callState = CallState.InvitationSent, localCapabilities = r.capabilities) }
callAudioDeviceManager.selectLastExternalDeviceOrDefault(call.soundSpeaker, true)
CallSoundsPlayer.startConnectingCallSound(scope)
activeCallWaitDeliveryReceipt(scope)
} }
is WCallResponse.Offer -> withBGApi { is WCallResponse.Offer -> withBGApi {
chatModel.controller.apiSendCallOffer(callRh, call.contact, r.offer, r.iceCandidates, call.localMedia, r.capabilities) chatModel.controller.apiSendCallOffer(callRh, call.contact, r.offer, r.iceCandidates, call.localMedia, r.capabilities)
@@ -131,7 +144,6 @@ actual fun ActiveCallView() {
is WCallResponse.Answer -> withBGApi { is WCallResponse.Answer -> withBGApi {
chatModel.controller.apiSendCallAnswer(callRh, call.contact, r.answer, r.iceCandidates) chatModel.controller.apiSendCallAnswer(callRh, call.contact, r.answer, r.iceCandidates)
updateActiveCall(call) { it.copy(callState = CallState.Negotiated) } updateActiveCall(call) { it.copy(callState = CallState.Negotiated) }
CallSoundsPlayer.stop()
} }
is WCallResponse.Ice -> withBGApi { is WCallResponse.Ice -> withBGApi {
chatModel.controller.apiSendCallExtraInfo(callRh, call.contact, r.iceCandidates) chatModel.controller.apiSendCallExtraInfo(callRh, call.contact, r.iceCandidates)
@@ -141,7 +153,7 @@ actual fun ActiveCallView() {
val callStatus = json.decodeFromString<WebRTCCallStatus>("\"${r.state.connectionState}\"") val callStatus = json.decodeFromString<WebRTCCallStatus>("\"${r.state.connectionState}\"")
if (callStatus == WebRTCCallStatus.Connected) { if (callStatus == WebRTCCallStatus.Connected) {
updateActiveCall(call) { it.copy(callState = CallState.Connected, connectedAt = Clock.System.now()) } updateActiveCall(call) { it.copy(callState = CallState.Connected, connectedAt = Clock.System.now()) }
callAudioDeviceManager.selectLastExternalDeviceOrDefault(call.soundSpeaker, true) setCallSound(call.soundSpeaker, audioViaBluetooth)
} }
withBGApi { chatModel.controller.apiCallStatus(callRh, call.contact, callStatus) } withBGApi { chatModel.controller.apiCallStatus(callRh, call.contact, callStatus) }
} catch (e: Throwable) { } catch (e: Throwable) {
@@ -150,7 +162,7 @@ actual fun ActiveCallView() {
is WCallResponse.Connected -> { is WCallResponse.Connected -> {
updateActiveCall(call) { it.copy(callState = CallState.Connected, connectionInfo = r.connectionInfo) } updateActiveCall(call) { it.copy(callState = CallState.Connected, connectionInfo = r.connectionInfo) }
scope.launch { scope.launch {
callAudioDeviceManager.selectLastExternalDeviceOrDefault(call.soundSpeaker, true) setCallSound(call.soundSpeaker, audioViaBluetooth)
} }
} }
is WCallResponse.End -> { is WCallResponse.End -> {
@@ -196,7 +208,7 @@ actual fun ActiveCallView() {
else -> false else -> false
} }
if (call != null && showOverlay) { if (call != null && showOverlay) {
ActiveCallOverlay(call, chatModel, callAudioDeviceManager) ActiveCallOverlay(call, chatModel, audioViaBluetooth)
} }
} }
val context = LocalContext.current val context = LocalContext.current
@@ -222,21 +234,19 @@ actual fun ActiveCallView() {
} }
@Composable @Composable
private fun ActiveCallOverlay(call: Call, chatModel: ChatModel, callAudioDeviceManager: CallAudioDeviceManagerInterface) { private fun ActiveCallOverlay(call: Call, chatModel: ChatModel, audioViaBluetooth: MutableState<Boolean>) {
ActiveCallOverlayLayout( ActiveCallOverlayLayout(
call = call, call = call,
devices = remember { callAudioDeviceManager.devices }.value, speakerCanBeEnabled = !audioViaBluetooth.value,
currentDevice = remember { callAudioDeviceManager.currentDevice },
dismiss = { withBGApi { chatModel.callManager.endCall(call) } }, dismiss = { withBGApi { chatModel.callManager.endCall(call) } },
toggleAudio = { chatModel.callCommand.add(WCallCommand.Media(CallMediaType.Audio, enable = !call.audioEnabled)) }, toggleAudio = { chatModel.callCommand.add(WCallCommand.Media(CallMediaType.Audio, enable = !call.audioEnabled)) },
selectDevice = { callAudioDeviceManager.selectDevice(it.id) },
toggleVideo = { chatModel.callCommand.add(WCallCommand.Media(CallMediaType.Video, enable = !call.videoEnabled)) }, toggleVideo = { chatModel.callCommand.add(WCallCommand.Media(CallMediaType.Video, enable = !call.videoEnabled)) },
toggleSound = { toggleSound = {
var call = chatModel.activeCall.value var call = chatModel.activeCall.value
if (call != null) { if (call != null) {
call = call.copy(soundSpeaker = !call.soundSpeaker) call = call.copy(soundSpeaker = !call.soundSpeaker)
chatModel.activeCall.value = call chatModel.activeCall.value = call
callAudioDeviceManager.selectLastExternalDeviceOrDefault(call.soundSpeaker, true) setCallSound(call.soundSpeaker, audioViaBluetooth)
} }
}, },
flipCamera = { chatModel.callCommand.add(WCallCommand.Camera(call.localCamera.flipped)) } flipCamera = { chatModel.callCommand.add(WCallCommand.Camera(call.localCamera.flipped)) }
@@ -247,18 +257,42 @@ private fun ActiveCallOverlay(call: Call, chatModel: ChatModel, callAudioDeviceM
fun ActiveCallOverlayDisabled(call: Call) { fun ActiveCallOverlayDisabled(call: Call) {
ActiveCallOverlayLayout( ActiveCallOverlayLayout(
call = call, call = call,
devices = emptyList(), speakerCanBeEnabled = false,
currentDevice = remember { mutableStateOf(null) },
enabled = false, enabled = false,
dismiss = {}, dismiss = {},
toggleAudio = {}, toggleAudio = {},
selectDevice = {},
toggleVideo = {}, toggleVideo = {},
toggleSound = {}, toggleSound = {},
flipCamera = {} flipCamera = {}
) )
} }
private fun setCallSound(speaker: Boolean, audioViaBluetooth: MutableState<Boolean>) {
val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
Log.d(TAG, "setCallSound: set audio mode, speaker enabled: $speaker")
am.mode = AudioManager.MODE_IN_COMMUNICATION
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val btDevice = am.availableCommunicationDevices.lastOrNull { it.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO }
val preferredSecondaryDevice = if (speaker) AudioDeviceInfo.TYPE_BUILTIN_SPEAKER else AudioDeviceInfo.TYPE_BUILTIN_EARPIECE
if (btDevice != null) {
am.setCommunicationDevice(btDevice)
} else if (am.communicationDevice?.type != preferredSecondaryDevice) {
am.availableCommunicationDevices.firstOrNull { it.type == preferredSecondaryDevice }?.let {
am.setCommunicationDevice(it)
}
}
} else {
if (audioViaBluetooth.value) {
am.isSpeakerphoneOn = false
am.startBluetoothSco()
} else {
am.stopBluetoothSco()
am.isSpeakerphoneOn = speaker
}
am.isBluetoothScoOn = am.isBluetoothScoAvailableOffCall && audioViaBluetooth.value
}
}
private fun dropAudioManagerOverrides() { private fun dropAudioManagerOverrides() {
val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
am.mode = AudioManager.MODE_NORMAL am.mode = AudioManager.MODE_NORMAL
@@ -274,12 +308,10 @@ private fun dropAudioManagerOverrides() {
@Composable @Composable
private fun ActiveCallOverlayLayout( private fun ActiveCallOverlayLayout(
call: Call, call: Call,
devices: List<AudioDeviceInfo>, speakerCanBeEnabled: Boolean,
currentDevice: State<AudioDeviceInfo?>,
enabled: Boolean = true, enabled: Boolean = true,
dismiss: () -> Unit, dismiss: () -> Unit,
toggleAudio: () -> Unit, toggleAudio: () -> Unit,
selectDevice: (AudioDeviceInfo) -> Unit,
toggleVideo: () -> Unit, toggleVideo: () -> Unit,
toggleSound: () -> Unit, toggleSound: () -> Unit,
flipCamera: () -> Unit flipCamera: () -> Unit
@@ -292,32 +324,6 @@ private fun ActiveCallOverlayLayout(
} }
} }
Column(Modifier.padding(horizontal = DEFAULT_PADDING)) { Column(Modifier.padding(horizontal = DEFAULT_PADDING)) {
@Composable
fun SelectSoundDevice() {
if (devices.size == 2 &&
devices.all { it.type == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE || it.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER } ||
currentDevice.value == null ||
devices.none { it.id == currentDevice.value?.id }
) {
ToggleSoundButton(call, enabled, toggleSound)
} else {
ExposedDropDownSettingWithIcon(
devices.map { Triple(it, it.icon, if (it.name != null) generalGetString(it.name!!) else it.productName.toString()) },
currentDevice,
fontSize = 18.sp,
iconSize = 40.dp,
listIconSize = 30.dp,
iconColor = Color(0xFFFFFFD8),
minWidth = 300.dp,
onSelected = {
if (it != null) {
selectDevice(it)
}
}
)
}
}
when (media) { when (media) {
CallMediaType.Video -> { CallMediaType.Video -> {
VideoCallInfoView(call) VideoCallInfoView(call)
@@ -326,7 +332,7 @@ private fun ActiveCallOverlayLayout(
} }
Row(Modifier.fillMaxWidth().padding(horizontal = 6.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) { Row(Modifier.fillMaxWidth().padding(horizontal = 6.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) {
ToggleAudioButton(call, enabled, toggleAudio) ToggleAudioButton(call, enabled, toggleAudio)
SelectSoundDevice() Spacer(Modifier.size(40.dp))
IconButton(onClick = dismiss, enabled = enabled) { IconButton(onClick = dismiss, enabled = enabled) {
Icon(painterResource(MR.images.ic_call_end_filled), stringResource(MR.strings.icon_descr_hang_up), tint = if (enabled) Color.Red else MaterialTheme.colors.secondary, modifier = Modifier.size(64.dp)) Icon(painterResource(MR.images.ic_call_end_filled), stringResource(MR.strings.icon_descr_hang_up), tint = if (enabled) Color.Red else MaterialTheme.colors.secondary, modifier = Modifier.size(64.dp))
} }
@@ -364,7 +370,7 @@ private fun ActiveCallOverlayLayout(
} }
Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.CenterEnd) { Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.CenterEnd) {
Box(Modifier.padding(end = 32.dp)) { Box(Modifier.padding(end = 32.dp)) {
SelectSoundDevice() ToggleSoundButton(call, speakerCanBeEnabled && enabled, toggleSound)
} }
} }
} }
@@ -759,11 +765,9 @@ fun PreviewActiveCallOverlayVideo() {
RTCIceCandidate(RTCIceCandidateType.Host, "tcp") RTCIceCandidate(RTCIceCandidateType.Host, "tcp")
) )
), ),
devices = emptyList(), speakerCanBeEnabled = true,
currentDevice = remember { mutableStateOf(null) },
dismiss = {}, dismiss = {},
toggleAudio = {}, toggleAudio = {},
selectDevice = {},
toggleVideo = {}, toggleVideo = {},
toggleSound = {}, toggleSound = {},
flipCamera = {} flipCamera = {}
@@ -788,11 +792,9 @@ fun PreviewActiveCallOverlayAudio() {
RTCIceCandidate(RTCIceCandidateType.Host, "udp") RTCIceCandidate(RTCIceCandidateType.Host, "udp")
) )
), ),
devices = emptyList(), speakerCanBeEnabled = true,
currentDevice = remember { mutableStateOf(null) },
dismiss = {}, dismiss = {},
toggleAudio = {}, toggleAudio = {},
selectDevice = {},
toggleVideo = {}, toggleVideo = {},
toggleSound = {}, toggleSound = {},
flipCamera = {} flipCamera = {}
@@ -6,6 +6,7 @@ import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import chat.simplex.common.helpers.toUri
import chat.simplex.common.model.CIFile import chat.simplex.common.model.CIFile
import chat.simplex.common.platform.* import chat.simplex.common.platform.*
import chat.simplex.common.views.helpers.ModalManager import chat.simplex.common.views.helpers.ModalManager
@@ -14,6 +15,7 @@ import coil.compose.rememberAsyncImagePainter
import coil.decode.GifDecoder import coil.decode.GifDecoder
import coil.decode.ImageDecoderDecoder import coil.decode.ImageDecoderDecoder
import coil.request.ImageRequest import coil.request.ImageRequest
import java.net.URI
@Composable @Composable
actual fun SimpleAndAnimatedImageView( actual fun SimpleAndAnimatedImageView(
@@ -41,7 +43,6 @@ actual fun SimpleAndAnimatedImageView(
} }
private val imageLoader = ImageLoader.Builder(androidAppContext) private val imageLoader = ImageLoader.Builder(androidAppContext)
.networkObserverEnabled(false)
.components { .components {
if (SDK_INT >= 28) { if (SDK_INT >= 28) {
add(ImageDecoderDecoder.Factory()) add(ImageDecoderDecoder.Factory())
@@ -3,7 +3,7 @@ package chat.simplex.common.views.chat.item
import android.os.Build import android.os.Build
import android.view.View import android.view.View
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.runtime.* import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.* import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.painter.BitmapPainter import androidx.compose.ui.graphics.painter.BitmapPainter
@@ -11,8 +11,8 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import chat.simplex.common.helpers.toUri
import chat.simplex.common.platform.VideoPlayer import chat.simplex.common.platform.VideoPlayer
import chat.simplex.common.platform.androidAppContext
import chat.simplex.res.MR import chat.simplex.res.MR
import coil.ImageLoader import coil.ImageLoader
import coil.compose.rememberAsyncImagePainter import coil.compose.rememberAsyncImagePainter
@@ -23,11 +23,21 @@ import coil.size.Size
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout import com.google.android.exoplayer2.ui.AspectRatioFrameLayout
import com.google.android.exoplayer2.ui.StyledPlayerView import com.google.android.exoplayer2.ui.StyledPlayerView
import dev.icerock.moko.resources.compose.stringResource import dev.icerock.moko.resources.compose.stringResource
import java.net.URI
@Composable @Composable
actual fun FullScreenImageView(modifier: Modifier, data: ByteArray, imageBitmap: ImageBitmap) { actual fun FullScreenImageView(modifier: Modifier, data: ByteArray, imageBitmap: ImageBitmap) {
// I'm using a new private instance of imageLoader here because if I use one instance in multiple places // I'm making a new instance of imageLoader here because if I use one instance in multiple places
// after end of composition here a GIF from the first instance will be paused automatically which isn't what I want // after end of composition here a GIF from the first instance will be paused automatically which isn't what I want
val imageLoader = ImageLoader.Builder(LocalContext.current)
.components {
if (Build.VERSION.SDK_INT >= 28) {
add(ImageDecoderDecoder.Factory())
} else {
add(GifDecoder.Factory())
}
}
.build()
Image( Image(
rememberAsyncImagePainter( rememberAsyncImagePainter(
ImageRequest.Builder(LocalContext.current).data(data = data).size(Size.ORIGINAL).build(), ImageRequest.Builder(LocalContext.current).data(data = data).size(Size.ORIGINAL).build(),
@@ -63,14 +73,3 @@ actual fun FullScreenVideoView(player: VideoPlayer, modifier: Modifier, close: (
modifier modifier
) )
} }
private val imageLoader = ImageLoader.Builder(androidAppContext)
.networkObserverEnabled(false)
.components {
if (Build.VERSION.SDK_INT >= 28) {
add(ImageDecoderDecoder.Factory())
} else {
add(GifDecoder.Factory())
}
}
.build()
@@ -71,7 +71,7 @@ if(NOT APPLE)
else() else()
# Without direct linking it can't find hs_init in linking step # Without direct linking it can't find hs_init in linking step
add_library( rts SHARED IMPORTED ) add_library( rts SHARED IMPORTED )
FILE(GLOB RTSLIB ${CMAKE_SOURCE_DIR}/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/libHSrts*_thr-*.${OS_LIB_EXT}) FILE(GLOB RTSLIB ${CMAKE_SOURCE_DIR}/libs/${OS_LIB_PATH}-${OS_LIB_ARCH}/deps/libHSrts*_thr-*.${OS_LIB_EXT})
set_target_properties( rts PROPERTIES IMPORTED_LOCATION ${RTSLIB}) set_target_properties( rts PROPERTIES IMPORTED_LOCATION ${RTSLIB})
target_link_libraries(app-lib rts simplex) target_link_libraries(app-lib rts simplex)
@@ -111,14 +111,13 @@ object ChatModel {
var draft = mutableStateOf(null as ComposeState?) var draft = mutableStateOf(null as ComposeState?)
var draftChatId = mutableStateOf(null as String?) var draftChatId = mutableStateOf(null as String?)
// working with external intents or internal forwarding of chat items // working with external intents
val sharedContent = mutableStateOf(null as SharedContent?) val sharedContent = mutableStateOf(null as SharedContent?)
val filesToDelete = mutableSetOf<File>() val filesToDelete = mutableSetOf<File>()
val simplexLinkMode by lazy { mutableStateOf(ChatController.appPrefs.simplexLinkMode.get()) } val simplexLinkMode by lazy { mutableStateOf(ChatController.appPrefs.simplexLinkMode.get()) }
val clipboardHasText = mutableStateOf(false) val clipboardHasText = mutableStateOf(false)
val networkInfo = mutableStateOf(UserNetworkInfo(networkType = UserNetworkType.OTHER, online = true))
val updatingChatsMutex: Mutex = Mutex() val updatingChatsMutex: Mutex = Mutex()
val changingActiveUserMutex: Mutex = Mutex() val changingActiveUserMutex: Mutex = Mutex()
@@ -805,24 +804,6 @@ data class Chat(
val id: String get() = chatInfo.id val id: String get() = chatInfo.id
fun groupFeatureEnabled(feature: GroupFeature): Boolean =
if (chatInfo is ChatInfo.Group) {
val groupInfo = chatInfo.groupInfo
val p = groupInfo.fullGroupPreferences
when (feature) {
GroupFeature.TimedMessages -> p.timedMessages.on
GroupFeature.DirectMessages -> p.directMessages.on(groupInfo.membership)
GroupFeature.FullDelete -> p.fullDelete.on
GroupFeature.Reactions -> p.reactions.on
GroupFeature.Voice -> p.voice.on(groupInfo.membership)
GroupFeature.Files -> p.files.on(groupInfo.membership)
GroupFeature.SimplexLinks -> p.simplexLinks.on(groupInfo.membership)
GroupFeature.History -> p.history.on
}
} else {
true
}
@Serializable @Serializable
data class ChatStats(val unreadCount: Int = 0, val minUnreadItemId: Long = 0, val unreadChat: Boolean = false) data class ChatStats(val unreadCount: Int = 0, val minUnreadItemId: Long = 0, val unreadChat: Boolean = false)
@@ -1258,7 +1239,7 @@ data class GroupInfo (
ChatFeature.TimedMessages -> fullGroupPreferences.timedMessages.on ChatFeature.TimedMessages -> fullGroupPreferences.timedMessages.on
ChatFeature.FullDelete -> fullGroupPreferences.fullDelete.on ChatFeature.FullDelete -> fullGroupPreferences.fullDelete.on
ChatFeature.Reactions -> fullGroupPreferences.reactions.on ChatFeature.Reactions -> fullGroupPreferences.reactions.on
ChatFeature.Voice -> fullGroupPreferences.voice.on(membership) ChatFeature.Voice -> fullGroupPreferences.voice.on
ChatFeature.Calls -> false ChatFeature.Calls -> false
} }
override val timedMessagesTTL: Int? get() = with(fullGroupPreferences.timedMessages) { if (on) ttl else null } override val timedMessagesTTL: Int? get() = with(fullGroupPreferences.timedMessages) { if (on) ttl else null }
@@ -1753,7 +1734,7 @@ data class ChatItem (
val allowAddReaction: Boolean get() = val allowAddReaction: Boolean get() =
meta.itemDeleted == null && !isLiveDummy && (reactions.count { it.userReacted } < 3) meta.itemDeleted == null && !isLiveDummy && (reactions.count { it.userReacted } < 3)
val isLiveDummy: Boolean get() = meta.itemId == TEMP_LIVE_CHAT_ITEM_ID private val isLiveDummy: Boolean get() = meta.itemId == TEMP_LIVE_CHAT_ITEM_ID
val encryptedFile: Boolean? = if (file?.fileSource == null) null else file.fileSource.cryptoArgs != null val encryptedFile: Boolean? = if (file?.fileSource == null) null else file.fileSource.cryptoArgs != null
@@ -1902,16 +1883,14 @@ data class ChatItem (
status: CIStatus = CIStatus.SndNew(), status: CIStatus = CIStatus.SndNew(),
quotedItem: CIQuote? = null, quotedItem: CIQuote? = null,
file: CIFile? = null, file: CIFile? = null,
itemForwarded: CIForwardedFrom? = null,
itemDeleted: CIDeleted? = null, itemDeleted: CIDeleted? = null,
itemEdited: Boolean = false, itemEdited: Boolean = false,
itemTimed: CITimed? = null, itemTimed: CITimed? = null,
deletable: Boolean = true,
editable: Boolean = true editable: Boolean = true
) = ) =
ChatItem( ChatItem(
chatDir = dir, chatDir = dir,
meta = CIMeta.getSample(id, ts, text, status, itemForwarded, itemDeleted, itemEdited, itemTimed, deletable, editable), meta = CIMeta.getSample(id, ts, text, status, itemDeleted, itemEdited, itemTimed, editable),
content = CIContent.SndMsgContent(msgContent = MsgContent.MCText(text)), content = CIContent.SndMsgContent(msgContent = MsgContent.MCText(text)),
quotedItem = quotedItem, quotedItem = quotedItem,
reactions = listOf(), reactions = listOf(),
@@ -1995,12 +1974,10 @@ data class ChatItem (
itemStatus = CIStatus.RcvRead(), itemStatus = CIStatus.RcvRead(),
createdAt = Clock.System.now(), createdAt = Clock.System.now(),
updatedAt = Clock.System.now(), updatedAt = Clock.System.now(),
itemForwarded = null,
itemDeleted = null, itemDeleted = null,
itemEdited = false, itemEdited = false,
itemTimed = null, itemTimed = null,
itemLive = false, itemLive = false,
deletable = false,
editable = false editable = false
), ),
content = CIContent.RcvDeleted(deleteMode = CIDeleteMode.cidmBroadcast), content = CIContent.RcvDeleted(deleteMode = CIDeleteMode.cidmBroadcast),
@@ -2018,12 +1995,10 @@ data class ChatItem (
itemStatus = CIStatus.RcvRead(), itemStatus = CIStatus.RcvRead(),
createdAt = Clock.System.now(), createdAt = Clock.System.now(),
updatedAt = Clock.System.now(), updatedAt = Clock.System.now(),
itemForwarded = null,
itemDeleted = null, itemDeleted = null,
itemEdited = false, itemEdited = false,
itemTimed = null, itemTimed = null,
itemLive = true, itemLive = true,
deletable = false,
editable = false editable = false
), ),
content = CIContent.SndMsgContent(MsgContent.MCText("")), content = CIContent.SndMsgContent(MsgContent.MCText("")),
@@ -2120,12 +2095,10 @@ data class CIMeta (
val itemStatus: CIStatus, val itemStatus: CIStatus,
val createdAt: Instant, val createdAt: Instant,
val updatedAt: Instant, val updatedAt: Instant,
val itemForwarded: CIForwardedFrom?,
val itemDeleted: CIDeleted?, val itemDeleted: CIDeleted?,
val itemEdited: Boolean, val itemEdited: Boolean,
val itemTimed: CITimed?, val itemTimed: CITimed?,
val itemLive: Boolean?, val itemLive: Boolean?,
val deletable: Boolean,
val editable: Boolean val editable: Boolean
) { ) {
val timestampText: String get() = getTimestampText(itemTs) val timestampText: String get() = getTimestampText(itemTs)
@@ -2145,8 +2118,7 @@ data class CIMeta (
companion object { companion object {
fun getSample( fun getSample(
id: Long, ts: Instant, text: String, status: CIStatus = CIStatus.SndNew(), id: Long, ts: Instant, text: String, status: CIStatus = CIStatus.SndNew(),
itemForwarded: CIForwardedFrom? = null, itemDeleted: CIDeleted? = null, itemEdited: Boolean = false, itemDeleted: CIDeleted? = null, itemEdited: Boolean = false, itemTimed: CITimed? = null, itemLive: Boolean = false, editable: Boolean = true
itemTimed: CITimed? = null, itemLive: Boolean = false, deletable: Boolean = true, editable: Boolean = true
): CIMeta = ): CIMeta =
CIMeta( CIMeta(
itemId = id, itemId = id,
@@ -2155,12 +2127,10 @@ data class CIMeta (
itemStatus = status, itemStatus = status,
createdAt = ts, createdAt = ts,
updatedAt = ts, updatedAt = ts,
itemForwarded = itemForwarded,
itemDeleted = itemDeleted, itemDeleted = itemDeleted,
itemEdited = itemEdited, itemEdited = itemEdited,
itemTimed = itemTimed, itemTimed = itemTimed,
itemLive = itemLive, itemLive = itemLive,
deletable = deletable,
editable = editable editable = editable
) )
@@ -2173,12 +2143,10 @@ data class CIMeta (
itemStatus = CIStatus.SndNew(), itemStatus = CIStatus.SndNew(),
createdAt = Clock.System.now(), createdAt = Clock.System.now(),
updatedAt = Clock.System.now(), updatedAt = Clock.System.now(),
itemForwarded = null,
itemDeleted = null, itemDeleted = null,
itemEdited = false, itemEdited = false,
itemTimed = null, itemTimed = null,
itemLive = false, itemLive = false,
deletable = false,
editable = false editable = false
) )
} }
@@ -2289,37 +2257,6 @@ sealed class CIDeleted {
@Serializable @SerialName("moderated") class Moderated(val deletedTs: Instant?, val byGroupMember: GroupMember): CIDeleted() @Serializable @SerialName("moderated") class Moderated(val deletedTs: Instant?, val byGroupMember: GroupMember): CIDeleted()
} }
@Serializable
enum class MsgDirection {
@SerialName("rcv") Rcv,
@SerialName("snd") Snd;
}
@Serializable
sealed class CIForwardedFrom {
@Serializable @SerialName("unknown") object Unknown: CIForwardedFrom()
@Serializable @SerialName("contact") class Contact(override val chatName: String, val msgDir: MsgDirection, val contactId: Long? = null, val chatItemId: Long? = null): CIForwardedFrom()
@Serializable @SerialName("group") class Group(override val chatName: String, val msgDir: MsgDirection, val groupId: Long? = null, val chatItemId: Long? = null): CIForwardedFrom()
open val chatName: String
get() = when (this) {
Unknown -> ""
is Contact -> chatName
is Group -> chatName
}
fun text(chatType: ChatType): String =
if (chatType == ChatType.Local) {
if (chatName.isEmpty()) {
generalGetString(MR.strings.saved_description)
} else {
generalGetString(MR.strings.saved_from_description).format(chatName)
}
} else {
generalGetString(MR.strings.forwarded_description)
}
}
@Serializable @Serializable
enum class CIDeleteMode(val deleteMode: String) { enum class CIDeleteMode(val deleteMode: String) {
@SerialName("internal") cidmInternal("internal"), @SerialName("internal") cidmInternal("internal"),
@@ -2355,8 +2292,8 @@ sealed class CIContent: ItemContent {
@Serializable @SerialName("sndChatFeature") class SndChatFeature(val feature: ChatFeature, val enabled: FeatureEnabled, val param: Int? = null): CIContent() { override val msgContent: MsgContent? get() = null } @Serializable @SerialName("sndChatFeature") class SndChatFeature(val feature: ChatFeature, val enabled: FeatureEnabled, val param: Int? = null): CIContent() { override val msgContent: MsgContent? get() = null }
@Serializable @SerialName("rcvChatPreference") class RcvChatPreference(val feature: ChatFeature, val allowed: FeatureAllowed, val param: Int? = null): CIContent() { override val msgContent: MsgContent? get() = null } @Serializable @SerialName("rcvChatPreference") class RcvChatPreference(val feature: ChatFeature, val allowed: FeatureAllowed, val param: Int? = null): CIContent() { override val msgContent: MsgContent? get() = null }
@Serializable @SerialName("sndChatPreference") class SndChatPreference(val feature: ChatFeature, val allowed: FeatureAllowed, val param: Int? = null): CIContent() { override val msgContent: MsgContent? get() = null } @Serializable @SerialName("sndChatPreference") class SndChatPreference(val feature: ChatFeature, val allowed: FeatureAllowed, val param: Int? = null): CIContent() { override val msgContent: MsgContent? get() = null }
@Serializable @SerialName("rcvGroupFeature") class RcvGroupFeature(val groupFeature: GroupFeature, val preference: GroupPreference, val param: Int? = null, val memberRole_: GroupMemberRole?): CIContent() { override val msgContent: MsgContent? get() = null } @Serializable @SerialName("rcvGroupFeature") class RcvGroupFeature(val groupFeature: GroupFeature, val preference: GroupPreference, val param: Int? = null): CIContent() { override val msgContent: MsgContent? get() = null }
@Serializable @SerialName("sndGroupFeature") class SndGroupFeature(val groupFeature: GroupFeature, val preference: GroupPreference, val param: Int? = null, val memberRole_: GroupMemberRole?): CIContent() { override val msgContent: MsgContent? get() = null } @Serializable @SerialName("sndGroupFeature") class SndGroupFeature(val groupFeature: GroupFeature, val preference: GroupPreference, val param: Int? = null): CIContent() { override val msgContent: MsgContent? get() = null }
@Serializable @SerialName("rcvChatFeatureRejected") class RcvChatFeatureRejected(val feature: ChatFeature): CIContent() { override val msgContent: MsgContent? get() = null } @Serializable @SerialName("rcvChatFeatureRejected") class RcvChatFeatureRejected(val feature: ChatFeature): CIContent() { override val msgContent: MsgContent? get() = null }
@Serializable @SerialName("rcvGroupFeatureRejected") class RcvGroupFeatureRejected(val groupFeature: GroupFeature): CIContent() { override val msgContent: MsgContent? get() = null } @Serializable @SerialName("rcvGroupFeatureRejected") class RcvGroupFeatureRejected(val groupFeature: GroupFeature): CIContent() { override val msgContent: MsgContent? get() = null }
@Serializable @SerialName("sndModerated") object SndModerated: CIContent() { override val msgContent: MsgContent? get() = null } @Serializable @SerialName("sndModerated") object SndModerated: CIContent() { override val msgContent: MsgContent? get() = null }
@@ -2388,8 +2325,8 @@ sealed class CIContent: ItemContent {
is SndChatFeature -> featureText(feature, enabled.text, param) is SndChatFeature -> featureText(feature, enabled.text, param)
is RcvChatPreference -> preferenceText(feature, allowed, param) is RcvChatPreference -> preferenceText(feature, allowed, param)
is SndChatPreference -> preferenceText(feature, allowed, param) is SndChatPreference -> preferenceText(feature, allowed, param)
is RcvGroupFeature -> featureText(groupFeature, preference.enable.text, param, memberRole_) is RcvGroupFeature -> featureText(groupFeature, preference.enable.text, param)
is SndGroupFeature -> featureText(groupFeature, preference.enable.text, param, memberRole_) is SndGroupFeature -> featureText(groupFeature, preference.enable.text, param)
is RcvChatFeatureRejected -> "${feature.text}: ${generalGetString(MR.strings.feature_received_prohibited)}" is RcvChatFeatureRejected -> "${feature.text}: ${generalGetString(MR.strings.feature_received_prohibited)}"
is RcvGroupFeatureRejected -> "${groupFeature.text}: ${generalGetString(MR.strings.feature_received_prohibited)}" is RcvGroupFeatureRejected -> "${groupFeature.text}: ${generalGetString(MR.strings.feature_received_prohibited)}"
is SndModerated -> generalGetString(MR.strings.moderated_description) is SndModerated -> generalGetString(MR.strings.moderated_description)
@@ -2426,23 +2363,11 @@ sealed class CIContent: ItemContent {
private val e2eeInfoNoPQStr: String = generalGetString(MR.strings.e2ee_info_no_pq_short) private val e2eeInfoNoPQStr: String = generalGetString(MR.strings.e2ee_info_no_pq_short)
fun featureText(feature: Feature, enabled: String, param: Int?, role: GroupMemberRole? = null): String = fun featureText(feature: Feature, enabled: String, param: Int?): String =
(if (feature.hasParam) { if (feature.hasParam) {
"${feature.text}: ${timeText(param)}" "${feature.text}: ${timeText(param)}"
} else { } else {
"${feature.text}: $enabled" "${feature.text}: $enabled"
}) + (
if (feature.hasRole && role != null)
" (${roleText(role)})"
else
""
)
private fun roleText(role: GroupMemberRole?): String =
when (role) {
GroupMemberRole.Owner -> generalGetString(MR.strings.feature_roles_owners)
GroupMemberRole.Admin -> generalGetString(MR.strings.feature_roles_admins)
else -> generalGetString(MR.strings.feature_roles_all_members)
} }
fun preferenceText(feature: Feature, allowed: FeatureAllowed, param: Int?): String = when { fun preferenceText(feature: Feature, allowed: FeatureAllowed, param: Int?): String = when {
@@ -3326,8 +3251,7 @@ sealed class ChatItemTTL: Comparable<ChatItemTTL?> {
@Serializable @Serializable
class ChatItemInfo( class ChatItemInfo(
val itemVersions: List<ChatItemVersion>, val itemVersions: List<ChatItemVersion>,
val memberDeliveryStatuses: List<MemberDeliveryStatus>?, val memberDeliveryStatuses: List<MemberDeliveryStatus>?
val forwardedFromChatItem: AChatItem?
) )
@Serializable @Serializable
@@ -20,7 +20,6 @@ import com.charleskorn.kaml.YamlConfiguration
import chat.simplex.res.MR import chat.simplex.res.MR
import com.russhwolf.settings.Settings import com.russhwolf.settings.Settings
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
@@ -135,7 +134,6 @@ class AppPreferences {
val networkTCPConnectTimeout = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_CONNECT_TIMEOUT, NetCfg.defaults.tcpConnectTimeout, NetCfg.proxyDefaults.tcpConnectTimeout) val networkTCPConnectTimeout = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_CONNECT_TIMEOUT, NetCfg.defaults.tcpConnectTimeout, NetCfg.proxyDefaults.tcpConnectTimeout)
val networkTCPTimeout = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_TIMEOUT, NetCfg.defaults.tcpTimeout, NetCfg.proxyDefaults.tcpTimeout) val networkTCPTimeout = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_TIMEOUT, NetCfg.defaults.tcpTimeout, NetCfg.proxyDefaults.tcpTimeout)
val networkTCPTimeoutPerKb = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_TIMEOUT_PER_KB, NetCfg.defaults.tcpTimeoutPerKb, NetCfg.proxyDefaults.tcpTimeoutPerKb) val networkTCPTimeoutPerKb = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_TIMEOUT_PER_KB, NetCfg.defaults.tcpTimeoutPerKb, NetCfg.proxyDefaults.tcpTimeoutPerKb)
val networkRcvConcurrency = mkIntPreference(SHARED_PREFS_NETWORK_RCV_CONCURRENCY, NetCfg.defaults.rcvConcurrency)
val networkSMPPingInterval = mkLongPreference(SHARED_PREFS_NETWORK_SMP_PING_INTERVAL, NetCfg.defaults.smpPingInterval) val networkSMPPingInterval = mkLongPreference(SHARED_PREFS_NETWORK_SMP_PING_INTERVAL, NetCfg.defaults.smpPingInterval)
val networkSMPPingCount = mkIntPreference(SHARED_PREFS_NETWORK_SMP_PING_COUNT, NetCfg.defaults.smpPingCount) val networkSMPPingCount = mkIntPreference(SHARED_PREFS_NETWORK_SMP_PING_COUNT, NetCfg.defaults.smpPingCount)
val networkEnableKeepAlive = mkBoolPreference(SHARED_PREFS_NETWORK_ENABLE_KEEP_ALIVE, NetCfg.defaults.enableKeepAlive) val networkEnableKeepAlive = mkBoolPreference(SHARED_PREFS_NETWORK_ENABLE_KEEP_ALIVE, NetCfg.defaults.enableKeepAlive)
@@ -305,7 +303,6 @@ class AppPreferences {
private const val SHARED_PREFS_NETWORK_TCP_CONNECT_TIMEOUT = "NetworkTCPConnectTimeout" private const val SHARED_PREFS_NETWORK_TCP_CONNECT_TIMEOUT = "NetworkTCPConnectTimeout"
private const val SHARED_PREFS_NETWORK_TCP_TIMEOUT = "NetworkTCPTimeout" private const val SHARED_PREFS_NETWORK_TCP_TIMEOUT = "NetworkTCPTimeout"
private const val SHARED_PREFS_NETWORK_TCP_TIMEOUT_PER_KB = "networkTCPTimeoutPerKb" private const val SHARED_PREFS_NETWORK_TCP_TIMEOUT_PER_KB = "networkTCPTimeoutPerKb"
private const val SHARED_PREFS_NETWORK_RCV_CONCURRENCY = "networkRcvConcurrency"
private const val SHARED_PREFS_NETWORK_SMP_PING_INTERVAL = "NetworkSMPPingInterval" private const val SHARED_PREFS_NETWORK_SMP_PING_INTERVAL = "NetworkSMPPingInterval"
private const val SHARED_PREFS_NETWORK_SMP_PING_COUNT = "NetworkSMPPingCount" private const val SHARED_PREFS_NETWORK_SMP_PING_COUNT = "NetworkSMPPingCount"
private const val SHARED_PREFS_NETWORK_ENABLE_KEEP_ALIVE = "NetworkEnableKeepAlive" private const val SHARED_PREFS_NETWORK_ENABLE_KEEP_ALIVE = "NetworkEnableKeepAlive"
@@ -353,15 +350,11 @@ object ChatController {
var ctrl: ChatCtrl? = -1 var ctrl: ChatCtrl? = -1
val appPrefs: AppPreferences by lazy { AppPreferences() } val appPrefs: AppPreferences by lazy { AppPreferences() }
val messagesChannel: Channel<APIResponse> = Channel()
val chatModel = ChatModel val chatModel = ChatModel
private var receiverStarted = false private var receiverStarted = false
var lastMsgReceivedTimestamp: Long = System.currentTimeMillis() var lastMsgReceivedTimestamp: Long = System.currentTimeMillis()
private set private set
fun hasChatCtrl() = ctrl != -1L && ctrl != null
private suspend fun currentUserId(funcName: String): Long = changingActiveUserMutex.withLock { private suspend fun currentUserId(funcName: String): Long = changingActiveUserMutex.withLock {
val userId = chatModel.currentUser.value?.userId val userId = chatModel.currentUser.value?.userId
if (userId == null) { if (userId == null) {
@@ -488,7 +481,6 @@ object ChatController {
if (msg != null) { if (msg != null) {
val finishedWithoutTimeout = withTimeoutOrNull(60_000L) { val finishedWithoutTimeout = withTimeoutOrNull(60_000L) {
processReceivedMsg(msg) processReceivedMsg(msg)
messagesChannel.trySend(msg)
} }
if (finishedWithoutTimeout == null) { if (finishedWithoutTimeout == null) {
Log.e(TAG, "Timeout reached while processing received message: " + msg.resp.responseType) Log.e(TAG, "Timeout reached while processing received message: " + msg.resp.responseType)
@@ -740,16 +732,12 @@ object ChatController {
suspend fun apiSendMessage(rh: Long?, type: ChatType, id: Long, file: CryptoFile? = null, quotedItemId: Long? = null, mc: MsgContent, live: Boolean = false, ttl: Int? = null): AChatItem? { suspend fun apiSendMessage(rh: Long?, type: ChatType, id: Long, file: CryptoFile? = null, quotedItemId: Long? = null, mc: MsgContent, live: Boolean = false, ttl: Int? = null): AChatItem? {
val cmd = CC.ApiSendMessage(type, id, file, quotedItemId, mc, live, ttl) val cmd = CC.ApiSendMessage(type, id, file, quotedItemId, mc, live, ttl)
return processSendMessageCmd(rh, cmd)
}
private suspend fun processSendMessageCmd(rh: Long?, cmd: CC): AChatItem? {
val r = sendCmd(rh, cmd) val r = sendCmd(rh, cmd)
return when (r) { return when (r) {
is CR.NewChatItem -> r.chatItem is CR.NewChatItem -> r.chatItem
else -> { else -> {
if (!(networkErrorAlert(r))) { if (!(networkErrorAlert(r))) {
apiErrorAlert("processSendMessageCmd", generalGetString(MR.strings.error_sending_message), r) apiErrorAlert("apiSendMessage", generalGetString(MR.strings.error_sending_message), r)
} }
null null
} }
@@ -777,13 +765,6 @@ object ChatController {
} }
} }
suspend fun apiForwardChatItem(rh: Long?, toChatType: ChatType, toChatId: Long, fromChatType: ChatType, fromChatId: Long, itemId: Long): ChatItem? {
val cmd = CC.ApiForwardChatItem(toChatType, toChatId, fromChatType, fromChatId, itemId)
return processSendMessageCmd(rh, cmd)?.chatItem
}
suspend fun apiUpdateChatItem(rh: Long?, type: ChatType, id: Long, itemId: Long, mc: MsgContent, live: Boolean = false): AChatItem? { suspend fun apiUpdateChatItem(rh: Long?, type: ChatType, id: Long, itemId: Long, mc: MsgContent, live: Boolean = false): AChatItem? {
val r = sendCmd(rh, CC.ApiUpdateChatItem(type, id, itemId, mc, live)) val r = sendCmd(rh, CC.ApiUpdateChatItem(type, id, itemId, mc, live))
if (r is CR.ChatItemUpdated) return r.chatItem if (r is CR.ChatItemUpdated) return r.chatItem
@@ -894,9 +875,6 @@ object ChatController {
} }
} }
suspend fun apiSetNetworkInfo(networkInfo: UserNetworkInfo): Boolean =
sendCommandOkResp(null, CC.APISetNetworkInfo(networkInfo))
suspend fun apiSetMemberSettings(rh: Long?, groupId: Long, groupMemberId: Long, memberSettings: GroupMemberSettings): Boolean = suspend fun apiSetMemberSettings(rh: Long?, groupId: Long, groupMemberId: Long, memberSettings: GroupMemberSettings): Boolean =
sendCommandOkResp(rh, CC.ApiSetMemberSettings(groupId, groupMemberId, memberSettings)) sendCommandOkResp(rh, CC.ApiSetMemberSettings(groupId, groupMemberId, memberSettings))
@@ -1984,6 +1962,7 @@ object ChatController {
} }
is CR.SndFileCompleteXFTP -> { is CR.SndFileCompleteXFTP -> {
chatItemSimpleUpdate(rhId, r.user, r.chatItem) chatItemSimpleUpdate(rhId, r.user, r.chatItem)
cleanupFile(r.chatItem)
} }
is CR.SndFileError -> { is CR.SndFileError -> {
if (r.chatItem_ != null) { if (r.chatItem_ != null) {
@@ -2140,7 +2119,7 @@ object ChatController {
if (active(r.user)) { if (active(r.user)) {
chatModel.updateContact(rhId, r.contact) chatModel.updateContact(rhId, r.contact)
} }
is CR.ChatRespError -> when { is CR.ChatCmdError -> when {
r.chatError is ChatError.ChatErrorAgent && r.chatError.agentError is AgentErrorType.CRITICAL -> { r.chatError is ChatError.ChatErrorAgent && r.chatError.agentError is AgentErrorType.CRITICAL -> {
chatModel.processedCriticalError.newError(r.chatError.agentError, r.chatError.agentError.offerRestart) chatModel.processedCriticalError.newError(r.chatError.agentError, r.chatError.agentError.offerRestart)
} }
@@ -2307,7 +2286,6 @@ object ChatController {
val tcpConnectTimeout = appPrefs.networkTCPConnectTimeout.get() val tcpConnectTimeout = appPrefs.networkTCPConnectTimeout.get()
val tcpTimeout = appPrefs.networkTCPTimeout.get() val tcpTimeout = appPrefs.networkTCPTimeout.get()
val tcpTimeoutPerKb = appPrefs.networkTCPTimeoutPerKb.get() val tcpTimeoutPerKb = appPrefs.networkTCPTimeoutPerKb.get()
val rcvConcurrency = appPrefs.networkRcvConcurrency.get()
val smpPingInterval = appPrefs.networkSMPPingInterval.get() val smpPingInterval = appPrefs.networkSMPPingInterval.get()
val smpPingCount = appPrefs.networkSMPPingCount.get() val smpPingCount = appPrefs.networkSMPPingCount.get()
val enableKeepAlive = appPrefs.networkEnableKeepAlive.get() val enableKeepAlive = appPrefs.networkEnableKeepAlive.get()
@@ -2327,7 +2305,6 @@ object ChatController {
tcpConnectTimeout = tcpConnectTimeout, tcpConnectTimeout = tcpConnectTimeout,
tcpTimeout = tcpTimeout, tcpTimeout = tcpTimeout,
tcpTimeoutPerKb = tcpTimeoutPerKb, tcpTimeoutPerKb = tcpTimeoutPerKb,
rcvConcurrency = rcvConcurrency,
tcpKeepAlive = tcpKeepAlive, tcpKeepAlive = tcpKeepAlive,
smpPingInterval = smpPingInterval, smpPingInterval = smpPingInterval,
smpPingCount = smpPingCount smpPingCount = smpPingCount
@@ -2345,7 +2322,6 @@ object ChatController {
appPrefs.networkTCPConnectTimeout.set(cfg.tcpConnectTimeout) appPrefs.networkTCPConnectTimeout.set(cfg.tcpConnectTimeout)
appPrefs.networkTCPTimeout.set(cfg.tcpTimeout) appPrefs.networkTCPTimeout.set(cfg.tcpTimeout)
appPrefs.networkTCPTimeoutPerKb.set(cfg.tcpTimeoutPerKb) appPrefs.networkTCPTimeoutPerKb.set(cfg.tcpTimeoutPerKb)
appPrefs.networkRcvConcurrency.set(cfg.rcvConcurrency)
appPrefs.networkSMPPingInterval.set(cfg.smpPingInterval) appPrefs.networkSMPPingInterval.set(cfg.smpPingInterval)
appPrefs.networkSMPPingCount.set(cfg.smpPingCount) appPrefs.networkSMPPingCount.set(cfg.smpPingCount)
if (cfg.tcpKeepAlive != null) { if (cfg.tcpKeepAlive != null) {
@@ -2411,7 +2387,6 @@ sealed class CC {
class ApiDeleteChatItem(val type: ChatType, val id: Long, val itemId: Long, val mode: CIDeleteMode): CC() class ApiDeleteChatItem(val type: ChatType, val id: Long, val itemId: Long, val mode: CIDeleteMode): CC()
class ApiDeleteMemberChatItem(val groupId: Long, val groupMemberId: Long, val itemId: Long): CC() class ApiDeleteMemberChatItem(val groupId: Long, val groupMemberId: Long, val itemId: Long): CC()
class ApiChatItemReaction(val type: ChatType, val id: Long, val itemId: Long, val add: Boolean, val reaction: MsgReaction): CC() class ApiChatItemReaction(val type: ChatType, val id: Long, val itemId: Long, val add: Boolean, val reaction: MsgReaction): CC()
class ApiForwardChatItem(val toChatType: ChatType, val toChatId: Long, val fromChatType: ChatType, val fromChatId: Long, val itemId: Long): CC()
class ApiNewGroup(val userId: Long, val incognito: Boolean, val groupProfile: GroupProfile): CC() class ApiNewGroup(val userId: Long, val incognito: Boolean, val groupProfile: GroupProfile): CC()
class ApiAddMember(val groupId: Long, val contactId: Long, val memberRole: GroupMemberRole): CC() class ApiAddMember(val groupId: Long, val contactId: Long, val memberRole: GroupMemberRole): CC()
class ApiJoinGroup(val groupId: Long): CC() class ApiJoinGroup(val groupId: Long): CC()
@@ -2434,7 +2409,6 @@ sealed class CC {
class APIGetChatItemTTL(val userId: Long): CC() class APIGetChatItemTTL(val userId: Long): CC()
class APISetNetworkConfig(val networkConfig: NetCfg): CC() class APISetNetworkConfig(val networkConfig: NetCfg): CC()
class APIGetNetworkConfig: CC() class APIGetNetworkConfig: CC()
class APISetNetworkInfo(val networkInfo: UserNetworkInfo): CC()
class APISetChatSettings(val type: ChatType, val id: Long, val chatSettings: ChatSettings): CC() class APISetChatSettings(val type: ChatType, val id: Long, val chatSettings: ChatSettings): CC()
class ApiSetMemberSettings(val groupId: Long, val groupMemberId: Long, val memberSettings: GroupMemberSettings): CC() class ApiSetMemberSettings(val groupId: Long, val groupMemberId: Long, val memberSettings: GroupMemberSettings): CC()
class APIContactInfo(val contactId: Long): CC() class APIContactInfo(val contactId: Long): CC()
@@ -2555,7 +2529,6 @@ sealed class CC {
is ApiDeleteChatItem -> "/_delete item ${chatRef(type, id)} $itemId ${mode.deleteMode}" is ApiDeleteChatItem -> "/_delete item ${chatRef(type, id)} $itemId ${mode.deleteMode}"
is ApiDeleteMemberChatItem -> "/_delete member item #$groupId $groupMemberId $itemId" is ApiDeleteMemberChatItem -> "/_delete member item #$groupId $groupMemberId $itemId"
is ApiChatItemReaction -> "/_reaction ${chatRef(type, id)} $itemId ${onOff(add)} ${json.encodeToString(reaction)}" is ApiChatItemReaction -> "/_reaction ${chatRef(type, id)} $itemId ${onOff(add)} ${json.encodeToString(reaction)}"
is ApiForwardChatItem -> "/_forward ${chatRef(toChatType, toChatId)} ${chatRef(fromChatType, fromChatId)} $itemId"
is ApiNewGroup -> "/_group $userId incognito=${onOff(incognito)} ${json.encodeToString(groupProfile)}" is ApiNewGroup -> "/_group $userId incognito=${onOff(incognito)} ${json.encodeToString(groupProfile)}"
is ApiAddMember -> "/_add #$groupId $contactId ${memberRole.memberRole}" is ApiAddMember -> "/_add #$groupId $contactId ${memberRole.memberRole}"
is ApiJoinGroup -> "/_join #$groupId" is ApiJoinGroup -> "/_join #$groupId"
@@ -2578,7 +2551,6 @@ sealed class CC {
is APIGetChatItemTTL -> "/_ttl $userId" is APIGetChatItemTTL -> "/_ttl $userId"
is APISetNetworkConfig -> "/_network ${json.encodeToString(networkConfig)}" is APISetNetworkConfig -> "/_network ${json.encodeToString(networkConfig)}"
is APIGetNetworkConfig -> "/network" is APIGetNetworkConfig -> "/network"
is APISetNetworkInfo -> "/_network info ${json.encodeToString(networkInfo)}"
is APISetChatSettings -> "/_settings ${chatRef(type, id)} ${json.encodeToString(chatSettings)}" is APISetChatSettings -> "/_settings ${chatRef(type, id)} ${json.encodeToString(chatSettings)}"
is ApiSetMemberSettings -> "/_member settings #$groupId $groupMemberId ${json.encodeToString(memberSettings)}" is ApiSetMemberSettings -> "/_member settings #$groupId $groupMemberId ${json.encodeToString(memberSettings)}"
is APIContactInfo -> "/_info @$contactId" is APIContactInfo -> "/_info @$contactId"
@@ -2694,7 +2666,6 @@ sealed class CC {
is ApiDeleteChatItem -> "apiDeleteChatItem" is ApiDeleteChatItem -> "apiDeleteChatItem"
is ApiDeleteMemberChatItem -> "apiDeleteMemberChatItem" is ApiDeleteMemberChatItem -> "apiDeleteMemberChatItem"
is ApiChatItemReaction -> "apiChatItemReaction" is ApiChatItemReaction -> "apiChatItemReaction"
is ApiForwardChatItem -> "apiForwardChatItem"
is ApiNewGroup -> "apiNewGroup" is ApiNewGroup -> "apiNewGroup"
is ApiAddMember -> "apiAddMember" is ApiAddMember -> "apiAddMember"
is ApiJoinGroup -> "apiJoinGroup" is ApiJoinGroup -> "apiJoinGroup"
@@ -2717,7 +2688,6 @@ sealed class CC {
is APIGetChatItemTTL -> "apiGetChatItemTTL" is APIGetChatItemTTL -> "apiGetChatItemTTL"
is APISetNetworkConfig -> "apiSetNetworkConfig" is APISetNetworkConfig -> "apiSetNetworkConfig"
is APIGetNetworkConfig -> "apiGetNetworkConfig" is APIGetNetworkConfig -> "apiGetNetworkConfig"
is APISetNetworkInfo -> "apiSetNetworkInfo"
is APISetChatSettings -> "apiSetChatSettings" is APISetChatSettings -> "apiSetChatSettings"
is ApiSetMemberSettings -> "apiSetMemberSettings" is ApiSetMemberSettings -> "apiSetMemberSettings"
is APIContactInfo -> "apiContactInfo" is APIContactInfo -> "apiContactInfo"
@@ -3039,7 +3009,6 @@ data class NetCfg(
val tcpConnectTimeout: Long, // microseconds val tcpConnectTimeout: Long, // microseconds
val tcpTimeout: Long, // microseconds val tcpTimeout: Long, // microseconds
val tcpTimeoutPerKb: Long, // microseconds val tcpTimeoutPerKb: Long, // microseconds
val rcvConcurrency: Int, // pool size
val tcpKeepAlive: KeepAliveOpts?, val tcpKeepAlive: KeepAliveOpts?,
val smpPingInterval: Long, // microseconds val smpPingInterval: Long, // microseconds
val smpPingCount: Int, val smpPingCount: Int,
@@ -3064,10 +3033,9 @@ data class NetCfg(
hostMode = HostMode.OnionViaSocks, hostMode = HostMode.OnionViaSocks,
requiredHostMode = false, requiredHostMode = false,
sessionMode = TransportSessionMode.User, sessionMode = TransportSessionMode.User,
tcpConnectTimeout = 10_000_000, tcpConnectTimeout = 20_000_000,
tcpTimeout = 15_000_000, tcpTimeout = 15_000_000,
tcpTimeoutPerKb = 10_000, tcpTimeoutPerKb = 45_000,
rcvConcurrency = 12,
tcpKeepAlive = KeepAliveOpts.defaults, tcpKeepAlive = KeepAliveOpts.defaults,
smpPingInterval = 1200_000_000, smpPingInterval = 1200_000_000,
smpPingCount = 3 smpPingCount = 3
@@ -3079,10 +3047,9 @@ data class NetCfg(
hostMode = HostMode.OnionViaSocks, hostMode = HostMode.OnionViaSocks,
requiredHostMode = false, requiredHostMode = false,
sessionMode = TransportSessionMode.User, sessionMode = TransportSessionMode.User,
tcpConnectTimeout = 20_000_000, tcpConnectTimeout = 30_000_000,
tcpTimeout = 20_000_000, tcpTimeout = 20_000_000,
tcpTimeoutPerKb = 15_000, tcpTimeoutPerKb = 60_000,
rcvConcurrency = 8,
tcpKeepAlive = KeepAliveOpts.defaults, tcpKeepAlive = KeepAliveOpts.defaults,
smpPingInterval = 1200_000_000, smpPingInterval = 1200_000_000,
smpPingCount = 3 smpPingCount = 3
@@ -3451,7 +3418,6 @@ interface Feature {
@Composable @Composable
fun iconFilled(): Painter fun iconFilled(): Painter
val hasParam: Boolean val hasParam: Boolean
val hasRole: Boolean
} }
@Serializable @Serializable
@@ -3471,7 +3437,6 @@ enum class ChatFeature: Feature {
TimedMessages -> true TimedMessages -> true
else -> false else -> false
} }
override val hasRole: Boolean = false
override val text: String override val text: String
get() = when(this) { get() = when(this) {
@@ -3572,7 +3537,6 @@ enum class GroupFeature: Feature {
@SerialName("reactions") Reactions, @SerialName("reactions") Reactions,
@SerialName("voice") Voice, @SerialName("voice") Voice,
@SerialName("files") Files, @SerialName("files") Files,
@SerialName("simplexLinks") SimplexLinks,
@SerialName("history") History; @SerialName("history") History;
override val hasParam: Boolean get() = when(this) { override val hasParam: Boolean get() = when(this) {
@@ -3580,18 +3544,6 @@ enum class GroupFeature: Feature {
else -> false else -> false
} }
override val hasRole: Boolean
get() = when (this) {
TimedMessages -> false
DirectMessages -> true
FullDelete -> false
Reactions -> false
Voice -> true
Files -> true
SimplexLinks -> true
History -> false
}
override val text: String override val text: String
get() = when(this) { get() = when(this) {
TimedMessages -> generalGetString(MR.strings.timed_messages) TimedMessages -> generalGetString(MR.strings.timed_messages)
@@ -3600,7 +3552,6 @@ enum class GroupFeature: Feature {
Reactions -> generalGetString(MR.strings.message_reactions) Reactions -> generalGetString(MR.strings.message_reactions)
Voice -> generalGetString(MR.strings.voice_messages) Voice -> generalGetString(MR.strings.voice_messages)
Files -> generalGetString(MR.strings.files_and_media) Files -> generalGetString(MR.strings.files_and_media)
SimplexLinks -> generalGetString(MR.strings.simplex_links)
History -> generalGetString(MR.strings.recent_history) History -> generalGetString(MR.strings.recent_history)
} }
@@ -3612,7 +3563,6 @@ enum class GroupFeature: Feature {
Reactions -> painterResource(MR.images.ic_add_reaction) Reactions -> painterResource(MR.images.ic_add_reaction)
Voice -> painterResource(MR.images.ic_keyboard_voice) Voice -> painterResource(MR.images.ic_keyboard_voice)
Files -> painterResource(MR.images.ic_draft) Files -> painterResource(MR.images.ic_draft)
SimplexLinks -> painterResource(MR.images.ic_link)
History -> painterResource(MR.images.ic_schedule) History -> painterResource(MR.images.ic_schedule)
} }
@@ -3624,7 +3574,6 @@ enum class GroupFeature: Feature {
Reactions -> painterResource(MR.images.ic_add_reaction_filled) Reactions -> painterResource(MR.images.ic_add_reaction_filled)
Voice -> painterResource(MR.images.ic_keyboard_voice_filled) Voice -> painterResource(MR.images.ic_keyboard_voice_filled)
Files -> painterResource(MR.images.ic_draft_filled) Files -> painterResource(MR.images.ic_draft_filled)
SimplexLinks -> painterResource(MR.images.ic_link)
History -> painterResource(MR.images.ic_schedule_filled) History -> painterResource(MR.images.ic_schedule_filled)
} }
@@ -3655,10 +3604,6 @@ enum class GroupFeature: Feature {
GroupFeatureEnabled.ON -> generalGetString(MR.strings.allow_to_send_files) GroupFeatureEnabled.ON -> generalGetString(MR.strings.allow_to_send_files)
GroupFeatureEnabled.OFF -> generalGetString(MR.strings.prohibit_sending_files) GroupFeatureEnabled.OFF -> generalGetString(MR.strings.prohibit_sending_files)
} }
SimplexLinks -> when(enabled) {
GroupFeatureEnabled.ON -> generalGetString(MR.strings.allow_to_send_simplex_links)
GroupFeatureEnabled.OFF -> generalGetString(MR.strings.prohibit_sending_simplex_links)
}
History -> when(enabled) { History -> when(enabled) {
GroupFeatureEnabled.ON -> generalGetString(MR.strings.enable_sending_recent_history) GroupFeatureEnabled.ON -> generalGetString(MR.strings.enable_sending_recent_history)
GroupFeatureEnabled.OFF -> generalGetString(MR.strings.disable_sending_recent_history) GroupFeatureEnabled.OFF -> generalGetString(MR.strings.disable_sending_recent_history)
@@ -3690,10 +3635,6 @@ enum class GroupFeature: Feature {
GroupFeatureEnabled.ON -> generalGetString(MR.strings.group_members_can_send_files) GroupFeatureEnabled.ON -> generalGetString(MR.strings.group_members_can_send_files)
GroupFeatureEnabled.OFF -> generalGetString(MR.strings.files_are_prohibited_in_group) GroupFeatureEnabled.OFF -> generalGetString(MR.strings.files_are_prohibited_in_group)
} }
SimplexLinks -> when(enabled) {
GroupFeatureEnabled.ON -> generalGetString(MR.strings.group_members_can_send_simplex_links)
GroupFeatureEnabled.OFF -> generalGetString(MR.strings.simplex_links_are_prohibited_in_group)
}
History -> when(enabled) { History -> when(enabled) {
GroupFeatureEnabled.ON -> generalGetString(MR.strings.recent_history_is_sent_to_new_members) GroupFeatureEnabled.ON -> generalGetString(MR.strings.recent_history_is_sent_to_new_members)
GroupFeatureEnabled.OFF -> generalGetString(MR.strings.recent_history_is_not_sent_to_new_members) GroupFeatureEnabled.OFF -> generalGetString(MR.strings.recent_history_is_not_sent_to_new_members)
@@ -3807,12 +3748,11 @@ enum class FeatureAllowed {
@Serializable @Serializable
data class FullGroupPreferences( data class FullGroupPreferences(
val timedMessages: TimedMessagesGroupPreference, val timedMessages: TimedMessagesGroupPreference,
val directMessages: RoleGroupPreference, val directMessages: GroupPreference,
val fullDelete: GroupPreference, val fullDelete: GroupPreference,
val reactions: GroupPreference, val reactions: GroupPreference,
val voice: RoleGroupPreference, val voice: GroupPreference,
val files: RoleGroupPreference, val files: GroupPreference,
val simplexLinks: RoleGroupPreference,
val history: GroupPreference, val history: GroupPreference,
) { ) {
fun toGroupPreferences(): GroupPreferences = fun toGroupPreferences(): GroupPreferences =
@@ -3823,19 +3763,17 @@ data class FullGroupPreferences(
reactions = reactions, reactions = reactions,
voice = voice, voice = voice,
files = files, files = files,
simplexLinks = simplexLinks,
history = history history = history
) )
companion object { companion object {
val sampleData = FullGroupPreferences( val sampleData = FullGroupPreferences(
timedMessages = TimedMessagesGroupPreference(GroupFeatureEnabled.OFF), timedMessages = TimedMessagesGroupPreference(GroupFeatureEnabled.OFF),
directMessages = RoleGroupPreference(GroupFeatureEnabled.OFF, role = null), directMessages = GroupPreference(GroupFeatureEnabled.OFF),
fullDelete = GroupPreference(GroupFeatureEnabled.OFF), fullDelete = GroupPreference(GroupFeatureEnabled.OFF),
reactions = GroupPreference(GroupFeatureEnabled.ON), reactions = GroupPreference(GroupFeatureEnabled.ON),
voice = RoleGroupPreference(GroupFeatureEnabled.ON, role = null), voice = GroupPreference(GroupFeatureEnabled.ON),
files = RoleGroupPreference(GroupFeatureEnabled.ON, role = null), files = GroupPreference(GroupFeatureEnabled.ON),
simplexLinks = RoleGroupPreference(GroupFeatureEnabled.ON, role = null),
history = GroupPreference(GroupFeatureEnabled.ON), history = GroupPreference(GroupFeatureEnabled.ON),
) )
} }
@@ -3844,23 +3782,21 @@ data class FullGroupPreferences(
@Serializable @Serializable
data class GroupPreferences( data class GroupPreferences(
val timedMessages: TimedMessagesGroupPreference? = null, val timedMessages: TimedMessagesGroupPreference? = null,
val directMessages: RoleGroupPreference? = null, val directMessages: GroupPreference? = null,
val fullDelete: GroupPreference? = null, val fullDelete: GroupPreference? = null,
val reactions: GroupPreference? = null, val reactions: GroupPreference? = null,
val voice: RoleGroupPreference? = null, val voice: GroupPreference? = null,
val files: RoleGroupPreference? = null, val files: GroupPreference? = null,
val simplexLinks: RoleGroupPreference? = null,
val history: GroupPreference? = null, val history: GroupPreference? = null,
) { ) {
companion object { companion object {
val sampleData = GroupPreferences( val sampleData = GroupPreferences(
timedMessages = TimedMessagesGroupPreference(GroupFeatureEnabled.OFF), timedMessages = TimedMessagesGroupPreference(GroupFeatureEnabled.OFF),
directMessages = RoleGroupPreference(GroupFeatureEnabled.OFF, role = null), directMessages = GroupPreference(GroupFeatureEnabled.OFF),
fullDelete = GroupPreference(GroupFeatureEnabled.OFF), fullDelete = GroupPreference(GroupFeatureEnabled.OFF),
reactions = GroupPreference(GroupFeatureEnabled.ON), reactions = GroupPreference(GroupFeatureEnabled.ON),
voice = RoleGroupPreference(GroupFeatureEnabled.ON, role = null), voice = GroupPreference(GroupFeatureEnabled.ON),
files = RoleGroupPreference(GroupFeatureEnabled.ON, role = null), files = GroupPreference(GroupFeatureEnabled.ON),
simplexLinks = RoleGroupPreference(GroupFeatureEnabled.ON, role = null),
history = GroupPreference(GroupFeatureEnabled.ON), history = GroupPreference(GroupFeatureEnabled.ON),
) )
} }
@@ -3871,26 +3807,6 @@ data class GroupPreference(
val enable: GroupFeatureEnabled val enable: GroupFeatureEnabled
) { ) {
val on: Boolean get() = enable == GroupFeatureEnabled.ON val on: Boolean get() = enable == GroupFeatureEnabled.ON
fun enabled(role: GroupMemberRole?, m: GroupMember?): GroupFeatureEnabled =
when (enable) {
GroupFeatureEnabled.OFF -> GroupFeatureEnabled.OFF
GroupFeatureEnabled.ON ->
if (role != null && m != null) {
if (m.memberRole >= role) GroupFeatureEnabled.ON else GroupFeatureEnabled.OFF
} else {
GroupFeatureEnabled.ON
}
}
}
@Serializable
data class RoleGroupPreference(
val enable: GroupFeatureEnabled,
val role: GroupMemberRole? = null,
) {
fun on(m: GroupMember): Boolean =
enable == GroupFeatureEnabled.ON && m.memberRole >= (role ?: GroupMemberRole.Observer)
} }
@Serializable @Serializable
@@ -4844,7 +4760,6 @@ sealed class ChatErrorType {
is FallbackToSMPProhibited -> "fallbackToSMPProhibited" is FallbackToSMPProhibited -> "fallbackToSMPProhibited"
is InlineFileProhibited -> "inlineFileProhibited" is InlineFileProhibited -> "inlineFileProhibited"
is InvalidQuote -> "invalidQuote" is InvalidQuote -> "invalidQuote"
is InvalidForward -> "invalidForward"
is InvalidChatItemUpdate -> "invalidChatItemUpdate" is InvalidChatItemUpdate -> "invalidChatItemUpdate"
is InvalidChatItemDelete -> "invalidChatItemDelete" is InvalidChatItemDelete -> "invalidChatItemDelete"
is HasCurrentCall -> "hasCurrentCall" is HasCurrentCall -> "hasCurrentCall"
@@ -4923,7 +4838,6 @@ sealed class ChatErrorType {
@Serializable @SerialName("fallbackToSMPProhibited") class FallbackToSMPProhibited(val fileId: Long): ChatErrorType() @Serializable @SerialName("fallbackToSMPProhibited") class FallbackToSMPProhibited(val fileId: Long): ChatErrorType()
@Serializable @SerialName("inlineFileProhibited") class InlineFileProhibited(val fileId: Long): ChatErrorType() @Serializable @SerialName("inlineFileProhibited") class InlineFileProhibited(val fileId: Long): ChatErrorType()
@Serializable @SerialName("invalidQuote") object InvalidQuote: ChatErrorType() @Serializable @SerialName("invalidQuote") object InvalidQuote: ChatErrorType()
@Serializable @SerialName("invalidForward") object InvalidForward: ChatErrorType()
@Serializable @SerialName("invalidChatItemUpdate") object InvalidChatItemUpdate: ChatErrorType() @Serializable @SerialName("invalidChatItemUpdate") object InvalidChatItemUpdate: ChatErrorType()
@Serializable @SerialName("invalidChatItemDelete") object InvalidChatItemDelete: ChatErrorType() @Serializable @SerialName("invalidChatItemDelete") object InvalidChatItemDelete: ChatErrorType()
@Serializable @SerialName("hasCurrentCall") object HasCurrentCall: ChatErrorType() @Serializable @SerialName("hasCurrentCall") object HasCurrentCall: ChatErrorType()
@@ -5613,26 +5527,3 @@ enum class AppSettingsLockScreenCalls {
} }
} }
} }
@Serializable
data class UserNetworkInfo(
val networkType: UserNetworkType,
val online: Boolean,
)
enum class UserNetworkType {
@SerialName("none") NONE,
@SerialName("cellular") CELLULAR,
@SerialName("wifi") WIFI,
@SerialName("ethernet") ETHERNET,
@SerialName("other") OTHER;
val text: String
get() = when (this) {
NONE -> generalGetString(MR.strings.network_type_no_network_connection)
CELLULAR -> generalGetString(MR.strings.network_type_cellular)
WIFI -> generalGetString(MR.strings.network_type_network_wifi)
ETHERNET -> generalGetString(MR.strings.network_type_ethernet)
OTHER -> generalGetString(MR.strings.network_type_other)
}
}
@@ -9,6 +9,7 @@ import chat.simplex.common.views.helpers.DatabaseUtils.randomDatabasePassword
import chat.simplex.common.views.onboarding.OnboardingStage import chat.simplex.common.views.onboarding.OnboardingStage
import chat.simplex.res.MR import chat.simplex.res.MR
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.serialization.decodeFromString
import java.io.File import java.io.File
import java.nio.ByteBuffer import java.nio.ByteBuffer
@@ -87,7 +88,6 @@ suspend fun initChatController(useKey: String? = null, confirmMigrations: Migrat
Log.d(TAG, "Unable to migrate successfully: $res") Log.d(TAG, "Unable to migrate successfully: $res")
return return
} }
platform.androidRestartNetworkObserver()
controller.apiSetTempFolder(coreTmpDir.absolutePath) controller.apiSetTempFolder(coreTmpDir.absolutePath)
controller.apiSetFilesFolder(appFilesDir.absolutePath) controller.apiSetFilesFolder(appFilesDir.absolutePath)
if (appPlatform.isDesktop) { if (appPlatform.isDesktop) {
@@ -23,7 +23,6 @@ interface PlatformInterface {
fun androidStartCallActivity(acceptCall: Boolean, remoteHostId: Long? = null, chatId: ChatId? = null) {} fun androidStartCallActivity(acceptCall: Boolean, remoteHostId: Long? = null, chatId: ChatId? = null) {}
fun androidPictureInPictureAllowed(): Boolean = true fun androidPictureInPictureAllowed(): Boolean = true
fun androidCallEnded() {} fun androidCallEnded() {}
fun androidRestartNetworkObserver() {}
@Composable fun androidLockPortraitOrientation() {} @Composable fun androidLockPortraitOrientation() {}
suspend fun androidAskToAllowBackgroundCalls(): Boolean = true suspend fun androidAskToAllowBackgroundCalls(): Boolean = true
@Composable fun desktopScrollBarComponents(): Triple<Animatable<Float, AnimationVector1D>, Modifier, MutableState<Job>> = remember { Triple(Animatable(0f), Modifier, mutableStateOf(Job())) } @Composable fun desktopScrollBarComponents(): Triple<Animatable<Float, AnimationVector1D>, Modifier, MutableState<Job>> = remember { Triple(Animatable(0f), Modifier, mutableStateOf(Job())) }
@@ -39,13 +39,4 @@ interface SoundPlayerInterface {
fun stop() fun stop()
} }
interface CallSoundsPlayerInterface {
fun startConnectingCallSound(scope: CoroutineScope)
fun startInCallSound(scope: CoroutineScope)
fun stop()
fun vibrate(times: Int = 1)
}
expect object SoundPlayer: SoundPlayerInterface expect object SoundPlayer: SoundPlayerInterface
expect object CallSoundsPlayer: CallSoundsPlayerInterface
@@ -85,7 +85,6 @@ fun TerminalLayout(
isDirectChat = false, isDirectChat = false,
liveMessageAlertShown = SharedPreference(get = { false }, set = {}), liveMessageAlertShown = SharedPreference(get = { false }, set = {}),
sendMsgEnabled = true, sendMsgEnabled = true,
sendButtonEnabled = true,
nextSendGrpInv = false, nextSendGrpInv = false,
needToAllowVoiceToContact = false, needToAllowVoiceToContact = false,
allowedVoiceByPrefs = false, allowedVoiceByPrefs = false,
@@ -1,26 +1,6 @@
package chat.simplex.common.views.call package chat.simplex.common.views.call
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import chat.simplex.common.model.*
import chat.simplex.common.model.ChatModel.controller
import chat.simplex.common.platform.*
import kotlinx.coroutines.*
@Composable @Composable
expect fun ActiveCallView() expect fun ActiveCallView()
fun activeCallWaitDeliveryReceipt(scope: CoroutineScope) = scope.launch(Dispatchers.Default) {
for (apiResp in controller.messagesChannel) {
val call = chatModel.activeCall.value
if (call == null || call.callState > CallState.InvitationSent) break
val msg = apiResp.resp
if (apiResp.remoteHostId == call.remoteHostId &&
msg is CR.ChatItemStatusUpdated &&
msg.chatItem.chatInfo.id == call.contact.id &&
msg.chatItem.chatItem.content is CIContent.SndCall &&
msg.chatItem.chatItem.meta.itemStatus is CIStatus.SndRcvd) {
CallSoundsPlayer.startInCallSound(scope)
break
}
}
}
@@ -187,11 +187,10 @@ data class ConnectionState(
) )
// the servers are expected in this format: // the servers are expected in this format:
// stuns:stun.simplex.im:443?transport=tcp // stun:stun.simplex.im:443?transport=tcp
// turns:private2:Hxuq2QxUjnhj96Zq2r4HjqHRj@turn.simplex.im:443?transport=tcp // turn:private:yleob6AVkiNI87hpR94Z@turn.simplex.im:443?transport=tcp
fun parseRTCIceServer(str: String): RTCIceServer? { fun parseRTCIceServer(str: String): RTCIceServer? {
var s = replaceScheme(str, "stun:") var s = replaceScheme(str, "stun:")
s = replaceScheme(s, "stuns:")
s = replaceScheme(s, "turn:") s = replaceScheme(s, "turn:")
s = replaceScheme(s, "turns:") s = replaceScheme(s, "turns:")
val u = runCatching { URI(s) }.getOrNull() val u = runCatching { URI(s) }.getOrNull()
@@ -199,7 +198,7 @@ fun parseRTCIceServer(str: String): RTCIceServer? {
val scheme = u.scheme val scheme = u.scheme
val host = u.host val host = u.host
val port = u.port val port = u.port
if (u.path == "" && (scheme == "stun" || scheme == "stuns" || scheme == "turn" || scheme == "turns")) { if (u.path == "" && (scheme == "stun" || scheme == "turn" || scheme == "turns")) {
val userInfo = u.userInfo?.split(":") val userInfo = u.userInfo?.split(":")
val query = if (u.query == null || u.query == "") "" else "?${u.query}" val query = if (u.query == null || u.query == "") "" else "?${u.query}"
return RTCIceServer( return RTCIceServer(
@@ -13,8 +13,9 @@ import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.* import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource import dev.icerock.moko.resources.compose.stringResource
@@ -28,7 +29,6 @@ import chat.simplex.common.views.chat.item.ItemAction
import chat.simplex.common.views.chat.item.MarkdownText import chat.simplex.common.views.chat.item.MarkdownText
import chat.simplex.common.views.helpers.* import chat.simplex.common.views.helpers.*
import chat.simplex.common.ui.theme.* import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.chatlist.*
import chat.simplex.res.MR import chat.simplex.res.MR
import dev.icerock.moko.resources.ImageResource import dev.icerock.moko.resources.ImageResource
@@ -36,11 +36,10 @@ sealed class CIInfoTab {
class Delivery(val memberDeliveryStatuses: List<MemberDeliveryStatus>): CIInfoTab() class Delivery(val memberDeliveryStatuses: List<MemberDeliveryStatus>): CIInfoTab()
object History: CIInfoTab() object History: CIInfoTab()
class Quote(val quotedItem: CIQuote): CIInfoTab() class Quote(val quotedItem: CIQuote): CIInfoTab()
class Forwarded(val forwardedFromChatItem: AChatItem): CIInfoTab()
} }
@Composable @Composable
fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools: Boolean) { fun ChatItemInfoView(chatModel: ChatModel, ci: ChatItem, ciInfo: ChatItemInfo, devTools: Boolean) {
val sent = ci.chatDir.sent val sent = ci.chatDir.sent
val appColors = CurrentColors.collectAsState().value.appColors val appColors = CurrentColors.collectAsState().value.appColors
val uriHandler = LocalUriHandler.current val uriHandler = LocalUriHandler.current
@@ -152,70 +151,6 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
} }
} }
val local = when (ci.chatDir) {
is CIDirection.LocalSnd -> true
is CIDirection.LocalRcv -> true
else -> false
}
@Composable
fun ForwardedFromSender(forwardedFromItem: AChatItem) {
@Composable
fun ItemText(text: String, fontStyle: FontStyle = FontStyle.Normal, color: Color = MaterialTheme.colors.onBackground) {
Text(
text,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.body1,
fontStyle = fontStyle,
color = color,
)
}
Row(verticalAlignment = Alignment.CenterVertically) {
ChatInfoImage(forwardedFromItem.chatInfo, size = 57.dp)
Column(
modifier = Modifier
.padding(start = 15.dp)
.weight(1F)
) {
if (forwardedFromItem.chatItem.chatDir.sent) {
ItemText(text = stringResource(MR.strings.sender_you_pronoun), fontStyle = FontStyle.Italic)
Spacer(Modifier.height(7.dp))
ItemText(forwardedFromItem.chatInfo.chatViewName, color = MaterialTheme.colors.secondary)
} else if (forwardedFromItem.chatItem.chatDir is CIDirection.GroupRcv) {
ItemText(text = forwardedFromItem.chatItem.chatDir.groupMember.chatViewName)
Spacer(Modifier.height(7.dp))
ItemText(forwardedFromItem.chatInfo.chatViewName, color = MaterialTheme.colors.secondary)
} else {
ItemText(forwardedFromItem.chatInfo.chatViewName, color = MaterialTheme.colors.onBackground)
}
}
}
}
@Composable
fun ForwardedFromView(forwardedFromItem: AChatItem) {
Column {
SectionItemView(
click = {
withBGApi {
openChat(chatRh, forwardedFromItem.chatInfo, chatModel)
ModalManager.end.closeModals()
}
},
padding = PaddingValues(start = 17.dp, end = DEFAULT_PADDING)
) {
ForwardedFromSender(forwardedFromItem)
}
if (!local) {
Divider(Modifier.padding(start = DEFAULT_PADDING_HALF, top = 41.dp, end = DEFAULT_PADDING_HALF, bottom = DEFAULT_PADDING_HALF))
Text(stringResource(MR.strings.recipients_can_not_see_who_message_from), Modifier.padding(horizontal = DEFAULT_PADDING), fontSize = 12.sp, color = MaterialTheme.colors.secondary)
}
}
}
@Composable @Composable
fun Details() { fun Details() {
AppBarTitle(stringResource(if (ci.localNote) MR.strings.saved_message_title else if (sent) MR.strings.sent_message else MR.strings.received_message)) AppBarTitle(stringResource(if (ci.localNote) MR.strings.saved_message_title else if (sent) MR.strings.sent_message else MR.strings.received_message))
@@ -253,7 +188,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
// LALAL SCROLLBAR DOESN'T WORK // LALAL SCROLLBAR DOESN'T WORK
ColumnWithScrollBar(Modifier.fillMaxWidth()) { ColumnWithScrollBar(Modifier.fillMaxWidth()) {
Details() Details()
SectionDividerSpaced(maxTopPadding = false, maxBottomPadding = true) SectionDividerSpaced(maxTopPadding = false, maxBottomPadding = false)
val versions = ciInfo.itemVersions val versions = ciInfo.itemVersions
if (versions.isNotEmpty()) { if (versions.isNotEmpty()) {
SectionView(padding = PaddingValues(horizontal = DEFAULT_PADDING)) { SectionView(padding = PaddingValues(horizontal = DEFAULT_PADDING)) {
@@ -278,7 +213,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
// LALAL SCROLLBAR DOESN'T WORK // LALAL SCROLLBAR DOESN'T WORK
ColumnWithScrollBar(Modifier.fillMaxWidth()) { ColumnWithScrollBar(Modifier.fillMaxWidth()) {
Details() Details()
SectionDividerSpaced(maxTopPadding = false, maxBottomPadding = true) SectionDividerSpaced(maxTopPadding = false, maxBottomPadding = false)
SectionView(padding = PaddingValues(horizontal = DEFAULT_PADDING)) { SectionView(padding = PaddingValues(horizontal = DEFAULT_PADDING)) {
Text(stringResource(MR.strings.in_reply_to), style = MaterialTheme.typography.h2, modifier = Modifier.padding(bottom = DEFAULT_PADDING)) Text(stringResource(MR.strings.in_reply_to), style = MaterialTheme.typography.h2, modifier = Modifier.padding(bottom = DEFAULT_PADDING))
QuotedMsgView(qi) QuotedMsgView(qi)
@@ -287,22 +222,6 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
} }
} }
@Composable
fun ForwardedFromTab(forwardedFromItem: AChatItem) {
// LALAL SCROLLBAR DOESN'T WORK
ColumnWithScrollBar(Modifier.fillMaxWidth()) {
Details()
SectionDividerSpaced(maxTopPadding = false, maxBottomPadding = true)
SectionView {
Text(stringResource(if (local) MR.strings.saved_from_chat_item_info_title else MR.strings.forwarded_from_chat_item_info_title),
style = MaterialTheme.typography.h2,
modifier = Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING))
ForwardedFromView(forwardedFromItem)
}
SectionBottomSpacer()
}
}
@Composable @Composable
fun MemberDeliveryStatusView(member: GroupMember, status: CIStatus) { fun MemberDeliveryStatusView(member: GroupMember, status: CIStatus) {
SectionItemView( SectionItemView(
@@ -352,7 +271,7 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
// LALAL SCROLLBAR DOESN'T WORK // LALAL SCROLLBAR DOESN'T WORK
ColumnWithScrollBar(Modifier.fillMaxWidth()) { ColumnWithScrollBar(Modifier.fillMaxWidth()) {
Details() Details()
SectionDividerSpaced(maxTopPadding = false, maxBottomPadding = true) SectionDividerSpaced(maxTopPadding = false, maxBottomPadding = false)
val mss = membersStatuses(chatModel, memberDeliveryStatuses) val mss = membersStatuses(chatModel, memberDeliveryStatuses)
if (mss.isNotEmpty()) { if (mss.isNotEmpty()) {
SectionView(padding = PaddingValues(horizontal = DEFAULT_PADDING)) { SectionView(padding = PaddingValues(horizontal = DEFAULT_PADDING)) {
@@ -378,7 +297,6 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
is CIInfoTab.Delivery -> stringResource(MR.strings.delivery) is CIInfoTab.Delivery -> stringResource(MR.strings.delivery)
is CIInfoTab.History -> stringResource(MR.strings.edit_history) is CIInfoTab.History -> stringResource(MR.strings.edit_history)
is CIInfoTab.Quote -> stringResource(MR.strings.in_reply_to) is CIInfoTab.Quote -> stringResource(MR.strings.in_reply_to)
is CIInfoTab.Forwarded -> stringResource(if (local) MR.strings.saved_chat_item_info_tab else MR.strings.forwarded_chat_item_info_tab)
} }
} }
@@ -387,7 +305,6 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
is CIInfoTab.Delivery -> MR.images.ic_double_check is CIInfoTab.Delivery -> MR.images.ic_double_check
is CIInfoTab.History -> MR.images.ic_history is CIInfoTab.History -> MR.images.ic_history
is CIInfoTab.Quote -> MR.images.ic_reply is CIInfoTab.Quote -> MR.images.ic_reply
is CIInfoTab.Forwarded -> MR.images.ic_forward
} }
} }
@@ -399,9 +316,6 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
if (ci.quotedItem != null) { if (ci.quotedItem != null) {
numTabs += 1 numTabs += 1
} }
if (ciInfo.forwardedFromChatItem != null) {
numTabs += 1
}
return numTabs return numTabs
} }
@@ -412,6 +326,11 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
.fillMaxHeight(), .fillMaxHeight(),
verticalArrangement = Arrangement.SpaceBetween verticalArrangement = Arrangement.SpaceBetween
) { ) {
LaunchedEffect(ciInfo) {
if (ciInfo.memberDeliveryStatuses != null) {
selection.value = CIInfoTab.Delivery(ciInfo.memberDeliveryStatuses)
}
}
Column(Modifier.weight(1f)) { Column(Modifier.weight(1f)) {
when (val sel = selection.value) { when (val sel = selection.value) {
is CIInfoTab.Delivery -> { is CIInfoTab.Delivery -> {
@@ -425,10 +344,6 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
is CIInfoTab.Quote -> { is CIInfoTab.Quote -> {
QuoteTab(sel.quotedItem) QuoteTab(sel.quotedItem)
} }
is CIInfoTab.Forwarded -> {
ForwardedFromTab(sel.forwardedFromChatItem)
}
} }
} }
val availableTabs = mutableListOf<CIInfoTab>() val availableTabs = mutableListOf<CIInfoTab>()
@@ -439,19 +354,6 @@ fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools
if (ci.quotedItem != null) { if (ci.quotedItem != null) {
availableTabs.add(CIInfoTab.Quote(ci.quotedItem)) availableTabs.add(CIInfoTab.Quote(ci.quotedItem))
} }
if (ciInfo.forwardedFromChatItem != null) {
availableTabs.add(CIInfoTab.Forwarded(ciInfo.forwardedFromChatItem))
}
if (availableTabs.none { it.javaClass == selection.value.javaClass }) {
selection.value = availableTabs.first()
}
LaunchedEffect(ciInfo) {
if (ciInfo.forwardedFromChatItem != null && selection.value is CIInfoTab.Forwarded) {
selection.value = CIInfoTab.Forwarded(ciInfo.forwardedFromChatItem)
} else if (ciInfo.memberDeliveryStatuses != null) {
selection.value = CIInfoTab.Delivery(ciInfo.memberDeliveryStatuses)
}
}
TabRow( TabRow(
selectedTabIndex = availableTabs.indexOfFirst { it::class == selection.value::class }, selectedTabIndex = availableTabs.indexOfFirst { it::class == selection.value::class },
backgroundColor = Color.Transparent, backgroundColor = Color.Transparent,
@@ -52,11 +52,9 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: suspend (chatId:
val user = chatModel.currentUser.value val user = chatModel.currentUser.value
val useLinkPreviews = chatModel.controller.appPrefs.privacyLinkPreviews.get() val useLinkPreviews = chatModel.controller.appPrefs.privacyLinkPreviews.get()
val composeState = rememberSaveable(saver = ComposeState.saver()) { val composeState = rememberSaveable(saver = ComposeState.saver()) {
val draft = chatModel.draft.value
val sharedContent = chatModel.sharedContent.value
mutableStateOf( mutableStateOf(
if (chatModel.draftChatId.value == chatId && draft != null && (sharedContent !is SharedContent.Forward || sharedContent.fromChatInfo.id == chatId)) { if (chatModel.draftChatId.value == chatId && chatModel.draft.value != null) {
draft chatModel.draft.value ?: ComposeState(useLinkPreviews = useLinkPreviews)
} else { } else {
ComposeState(useLinkPreviews = useLinkPreviews) ComposeState(useLinkPreviews = useLinkPreviews)
} }
@@ -410,7 +408,7 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: suspend (chatId:
clipboard.shareText(itemInfoShareText(chatModel, cItem, ciInfo, chatModel.controller.appPrefs.developerTools.get())) clipboard.shareText(itemInfoShareText(chatModel, cItem, ciInfo, chatModel.controller.appPrefs.developerTools.get()))
} }
}) { close -> }) { close ->
ChatItemInfoView(chatRh, cItem, ciInfo, devTools = chatModel.controller.appPrefs.developerTools.get()) ChatItemInfoView(chatModel, cItem, ciInfo, devTools = chatModel.controller.appPrefs.developerTools.get())
KeyChangeEffect(chatModel.chatId.value) { KeyChangeEffect(chatModel.chatId.value) {
close() close()
} }
@@ -958,13 +956,13 @@ fun BoxWithConstraintsScope.ChatItemsList(
tryOrShowError("${cItem.id}ChatItem", error = { tryOrShowError("${cItem.id}ChatItem", error = {
CIBrokenComposableView(if (cItem.chatDir.sent) Alignment.CenterEnd else Alignment.CenterStart) CIBrokenComposableView(if (cItem.chatDir.sent) Alignment.CenterEnd else Alignment.CenterStart)
}) { }) {
ChatItemView(chat.remoteHostId, chat.chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, range = range, deleteMessage = deleteMessage, deleteMessages = deleteMessages, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, setReaction = setReaction, showItemDetails = showItemDetails, developerTools = developerTools) ChatItemView(chat.chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, range = range, deleteMessage = deleteMessage, deleteMessages = deleteMessages, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, setReaction = setReaction, showItemDetails = showItemDetails, developerTools = developerTools)
} }
} }
@Composable @Composable
fun ChatItemView(cItem: ChatItem, range: IntRange?, prevItem: ChatItem?) { fun ChatItemView(cItem: ChatItem, range: IntRange?, prevItem: ChatItem?) {
val voiceWithTransparentBack = cItem.content.msgContent is MsgContent.MCVoice && cItem.content.text.isEmpty() && cItem.quotedItem == null && cItem.meta.itemForwarded == null val voiceWithTransparentBack = cItem.content.msgContent is MsgContent.MCVoice && cItem.content.text.isEmpty() && cItem.quotedItem == null
if (chat.chatInfo is ChatInfo.Group) { if (chat.chatInfo is ChatInfo.Group) {
if (cItem.chatDir is CIDirection.GroupRcv) { if (cItem.chatDir is CIDirection.GroupRcv) {
val member = cItem.chatDir.groupMember val member = cItem.chatDir.groupMember
@@ -1092,9 +1090,9 @@ private fun ScrollToBottom(chatId: ChatId, listState: LazyListState, chatItems:
.collect { .collect {
try { try {
if (listState.firstVisibleItemIndex == 0 || (listState.firstVisibleItemIndex == 1 && listState.layoutInfo.totalItemsCount == chatItems.size)) { if (listState.firstVisibleItemIndex == 0 || (listState.firstVisibleItemIndex == 1 && listState.layoutInfo.totalItemsCount == chatItems.size)) {
if (appPlatform.isAndroid) listState.animateScrollToItem(0) else listState.scrollToItem(0) listState.animateScrollToItem(0)
} else { } else {
if (appPlatform.isAndroid) listState.animateScrollBy(scrollDistance) else listState.scrollBy(scrollDistance) listState.animateScrollBy(scrollDistance)
} }
} catch (e: CancellationException) { } catch (e: CancellationException) {
/** /**
@@ -1394,12 +1392,11 @@ private fun providerForGallery(
return null return null
} }
// Pager has a bug with overflowing when total pages is around Int.MAX_VALUE. Using smaller value var initialIndex = Int.MAX_VALUE / 2
var initialIndex = 10000 / 2
var initialChatId = cItemId var initialChatId = cItemId
return object: ImageGalleryProvider { return object: ImageGalleryProvider {
override val initialIndex: Int = initialIndex override val initialIndex: Int = initialIndex
override val totalMediaSize = mutableStateOf(10000) override val totalMediaSize = mutableStateOf(Int.MAX_VALUE)
override fun getMedia(index: Int): ProviderMedia? { override fun getMedia(index: Int): ProviderMedia? {
val internalIndex = initialIndex - index val internalIndex = initialIndex - index
val item = item(internalIndex, initialChatId)?.second ?: return null val item = item(internalIndex, initialChatId)?.second ?: return null
@@ -1,7 +1,6 @@
@file:UseSerializers(UriSerializer::class) @file:UseSerializers(UriSerializer::class)
package chat.simplex.common.views.chat package chat.simplex.common.views.chat
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.* import androidx.compose.material.*
@@ -12,8 +11,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.text.font.FontStyle
import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@@ -21,7 +18,8 @@ import chat.simplex.common.model.*
import chat.simplex.common.model.ChatModel.controller import chat.simplex.common.model.ChatModel.controller
import chat.simplex.common.model.ChatModel.filesToDelete import chat.simplex.common.model.ChatModel.filesToDelete
import chat.simplex.common.platform.* import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.* import chat.simplex.common.ui.theme.Indigo
import chat.simplex.common.ui.theme.isSystemInDarkTheme
import chat.simplex.common.views.chat.item.* import chat.simplex.common.views.chat.item.*
import chat.simplex.common.views.helpers.* import chat.simplex.common.views.helpers.*
import chat.simplex.res.MR import chat.simplex.res.MR
@@ -46,7 +44,6 @@ sealed class ComposeContextItem {
@Serializable object NoContextItem: ComposeContextItem() @Serializable object NoContextItem: ComposeContextItem()
@Serializable class QuotedItem(val chatItem: ChatItem): ComposeContextItem() @Serializable class QuotedItem(val chatItem: ChatItem): ComposeContextItem()
@Serializable class EditingItem(val chatItem: ChatItem): ComposeContextItem() @Serializable class EditingItem(val chatItem: ChatItem): ComposeContextItem()
@Serializable class ForwardingItem(val chatItem: ChatItem, val fromChatInfo: ChatInfo): ComposeContextItem()
} }
@Serializable @Serializable
@@ -80,18 +77,13 @@ data class ComposeState(
is ComposeContextItem.EditingItem -> true is ComposeContextItem.EditingItem -> true
else -> false else -> false
} }
val forwarding: Boolean
get() = when (contextItem) {
is ComposeContextItem.ForwardingItem -> true
else -> false
}
val sendEnabled: () -> Boolean val sendEnabled: () -> Boolean
get() = { get() = {
val hasContent = when (preview) { val hasContent = when (preview) {
is ComposePreview.MediaPreview -> true is ComposePreview.MediaPreview -> true
is ComposePreview.VoicePreview -> true is ComposePreview.VoicePreview -> true
is ComposePreview.FilePreview -> true is ComposePreview.FilePreview -> true
else -> message.isNotEmpty() || forwarding || liveMessage != null else -> message.isNotEmpty() || liveMessage != null
} }
hasContent && !inProgress hasContent && !inProgress
} }
@@ -115,7 +107,7 @@ data class ComposeState(
val attachmentDisabled: Boolean val attachmentDisabled: Boolean
get() { get() {
if (editing || forwarding || liveMessage != null || inProgress) return true if (editing || liveMessage != null || inProgress) return true
return when (preview) { return when (preview) {
ComposePreview.NoPreview -> false ComposePreview.NoPreview -> false
is ComposePreview.CLinkPreview -> false is ComposePreview.CLinkPreview -> false
@@ -123,15 +115,6 @@ data class ComposeState(
} }
} }
val attachmentPreview: Boolean
get() = when (preview) {
ComposePreview.NoPreview -> false
is ComposePreview.CLinkPreview -> false
is ComposePreview.MediaPreview -> preview.content.isNotEmpty()
is ComposePreview.VoicePreview -> false
is ComposePreview.FilePreview -> true
}
val empty: Boolean val empty: Boolean
get() = message.isEmpty() && preview is ComposePreview.NoPreview && contextItem is ComposeContextItem.NoContextItem get() = message.isEmpty() && preview is ComposePreview.NoPreview && contextItem is ComposeContextItem.NoContextItem
@@ -260,23 +243,10 @@ fun ComposeView(
attachmentOption: MutableState<AttachmentOption?>, attachmentOption: MutableState<AttachmentOption?>,
showChooseAttachment: () -> Unit showChooseAttachment: () -> Unit
) { ) {
val cancelledLinks = rememberSaveable { mutableSetOf<String>() }
fun isSimplexLink(link: String): Boolean =
link.startsWith("https://simplex.chat", true) || link.startsWith("http://simplex.chat", true)
fun parseMessage(msg: String): Pair<String?, Boolean> {
if (msg.isBlank()) return null to false
val parsedMsg = parseToMarkdown(msg) ?: return null to false
val link = parsedMsg.firstOrNull { ft -> ft.format is Format.Uri && !cancelledLinks.contains(ft.text) && !isSimplexLink(ft.text) }
val simplexLink = parsedMsg.any { ft -> ft.format is Format.SimplexLink }
return link?.text to simplexLink
}
val linkUrl = rememberSaveable { mutableStateOf<String?>(null) } val linkUrl = rememberSaveable { mutableStateOf<String?>(null) }
// default value parsed because of draft
val hasSimplexLink = rememberSaveable { mutableStateOf(parseMessage(composeState.value.message).second) }
val prevLinkUrl = rememberSaveable { mutableStateOf<String?>(null) } val prevLinkUrl = rememberSaveable { mutableStateOf<String?>(null) }
val pendingLinkUrl = rememberSaveable { mutableStateOf<String?>(null) } val pendingLinkUrl = rememberSaveable { mutableStateOf<String?>(null) }
val cancelledLinks = rememberSaveable { mutableSetOf<String>() }
val useLinkPreviews = chatModel.controller.appPrefs.privacyLinkPreviews.get() val useLinkPreviews = chatModel.controller.appPrefs.privacyLinkPreviews.get()
val saveLastDraft = chatModel.controller.appPrefs.privacySaveLastDraft.get() val saveLastDraft = chatModel.controller.appPrefs.privacySaveLastDraft.get()
val smallFont = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onBackground) val smallFont = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onBackground)
@@ -285,6 +255,15 @@ fun ComposeView(
AttachmentSelection(composeState, attachmentOption, composeState::processPickedFile) { uris, text -> CoroutineScope(Dispatchers.IO).launch { composeState.processPickedMedia(uris, text) } } AttachmentSelection(composeState, attachmentOption, composeState::processPickedFile) { uris, text -> CoroutineScope(Dispatchers.IO).launch { composeState.processPickedMedia(uris, text) } }
fun isSimplexLink(link: String): Boolean =
link.startsWith("https://simplex.chat", true) || link.startsWith("http://simplex.chat", true)
fun parseMessage(msg: String): String? {
val parsedMsg = parseToMarkdown(msg)
val link = parsedMsg?.firstOrNull { ft -> ft.format is Format.Uri && !cancelledLinks.contains(ft.text) && !isSimplexLink(ft.text) }
return link?.text
}
fun loadLinkPreview(url: String, wait: Long? = null) { fun loadLinkPreview(url: String, wait: Long? = null) {
if (pendingLinkUrl.value == url) { if (pendingLinkUrl.value == url) {
composeState.value = composeState.value.copy(preview = ComposePreview.CLinkPreview(null)) composeState.value = composeState.value.copy(preview = ComposePreview.CLinkPreview(null))
@@ -304,9 +283,7 @@ fun ComposeView(
fun showLinkPreview(s: String) { fun showLinkPreview(s: String) {
prevLinkUrl.value = linkUrl.value prevLinkUrl.value = linkUrl.value
val parsed = parseMessage(s) linkUrl.value = parseMessage(s)
linkUrl.value = parsed.first
hasSimplexLink.value = parsed.second
val url = linkUrl.value val url = linkUrl.value
if (url != null) { if (url != null) {
if (url != composeState.value.linkPreview?.uri && url != pendingLinkUrl.value) { if (url != composeState.value.linkPreview?.uri && url != pendingLinkUrl.value) {
@@ -361,7 +338,6 @@ fun ComposeView(
is SharedContent.Media -> shared.uris.map { it.toString() } is SharedContent.Media -> shared.uris.map { it.toString() }
is SharedContent.File -> listOf(shared.uri.toString()) is SharedContent.File -> listOf(shared.uri.toString())
is SharedContent.Text -> emptyList() is SharedContent.Text -> emptyList()
is SharedContent.Forward -> emptyList()
} }
// When sharing a file and pasting it in SimpleX itself, the file shouldn't be deleted before sending or before leaving the chat after sharing // When sharing a file and pasting it in SimpleX itself, the file shouldn't be deleted before sending or before leaving the chat after sharing
chatModel.filesToDelete.removeAll { file -> chatModel.filesToDelete.removeAll { file ->
@@ -408,25 +384,10 @@ fun ComposeView(
composeState.value = composeState.value.copy(inProgress = true) composeState.value = composeState.value.copy(inProgress = true)
} }
suspend fun forwardItem(rhId: Long?, forwardedItem: ChatItem, fromChatInfo: ChatInfo): ChatItem? {
val chatItem = controller.apiForwardChatItem(
rh = rhId,
toChatType = chat.chatInfo.chatType,
toChatId = chat.chatInfo.apiId,
fromChatType = fromChatInfo.chatType,
fromChatId = fromChatInfo.apiId,
itemId = forwardedItem.id
)
if (chatItem != null) {
chatModel.addChatItem(rhId, chat.chatInfo, chatItem)
}
return chatItem
}
fun checkLinkPreview(): MsgContent { fun checkLinkPreview(): MsgContent {
return when (val composePreview = cs.preview) { return when (val composePreview = cs.preview) {
is ComposePreview.CLinkPreview -> { is ComposePreview.CLinkPreview -> {
val url = parseMessage(msgText).first val url = parseMessage(msgText)
val lp = composePreview.linkPreview val lp = composePreview.linkPreview
if (lp != null && url == lp.uri) { if (lp != null && url == lp.uri) {
MsgContent.MCLink(msgText, preview = lp) MsgContent.MCLink(msgText, preview = lp)
@@ -482,18 +443,11 @@ fun ComposeView(
if (liveMessage != null) composeState.value = cs.copy(liveMessage = null) if (liveMessage != null) composeState.value = cs.copy(liveMessage = null)
sending() sending()
} }
if (!cs.forwarding || chatModel.draft.value?.forwarding == true) { clearCurrentDraft()
clearCurrentDraft()
}
if (chat.nextSendGrpInv) { if (chat.nextSendGrpInv) {
sendMemberContactInvitation() sendMemberContactInvitation()
sent = null sent = null
} else if (cs.contextItem is ComposeContextItem.ForwardingItem) {
sent = forwardItem(chat.remoteHostId, cs.contextItem.chatItem, cs.contextItem.fromChatInfo)
if (cs.message.isNotEmpty()) {
sent = send(chat, checkLinkPreview(), quoted = sent?.id, live = false, ttl = null)
}
} else if (cs.contextItem is ComposeContextItem.EditingItem) { } else if (cs.contextItem is ComposeContextItem.EditingItem) {
val ei = cs.contextItem.chatItem val ei = cs.contextItem.chatItem
sent = updateMessage(ei, chat, live) sent = updateMessage(ei, chat, live)
@@ -592,15 +546,7 @@ fun ComposeView(
sent = send(chat, MsgContent.MCText(msgText), quotedItemId, null, live, ttl) sent = send(chat, MsgContent.MCText(msgText), quotedItemId, null, live, ttl)
} }
} }
val wasForwarding = cs.forwarding
val forwardingFromChatId = (cs.contextItem as? ComposeContextItem.ForwardingItem)?.fromChatInfo?.id
clearState(live) clearState(live)
val draft = chatModel.draft.value
if (wasForwarding && chatModel.draftChatId.value == chat.chatInfo.id && forwardingFromChatId != chat.chatInfo.id && draft != null) {
composeState.value = draft
} else {
clearCurrentDraft()
}
return sent return sent
} }
@@ -617,16 +563,8 @@ fun ComposeView(
} else { } else {
textStyle.value = smallFont textStyle.value = smallFont
if (composeState.value.linkPreviewAllowed) { if (composeState.value.linkPreviewAllowed) {
if (s.isNotEmpty()) { if (s.isNotEmpty()) showLinkPreview(s)
showLinkPreview(s) else resetLinkPreview()
} else {
resetLinkPreview()
hasSimplexLink.value = false
}
} else if (s.isNotEmpty() && !chat.groupFeatureEnabled(GroupFeature.SimplexLinks)) {
hasSimplexLink.value = parseMessage(s).second
} else {
hasSimplexLink.value = false
} }
} }
} }
@@ -762,16 +700,6 @@ fun ComposeView(
} }
} }
@Composable
fun MsgNotAllowedView(reason: String, icon: Painter) {
val color = CurrentColors.collectAsState().value.appColors.receivedMessage
Row(Modifier.padding(top = 5.dp).fillMaxWidth().background(color).padding(horizontal = DEFAULT_PADDING_HALF, vertical = DEFAULT_PADDING_HALF * 1.5f), verticalAlignment = Alignment.CenterVertically) {
Icon(icon, null, tint = MaterialTheme.colors.secondary)
Spacer(Modifier.width(DEFAULT_PADDING_HALF))
Text(reason, fontStyle = FontStyle.Italic)
}
}
@Composable @Composable
fun contextItemView() { fun contextItemView() {
when (val contextItem = composeState.value.contextItem) { when (val contextItem = composeState.value.contextItem) {
@@ -782,9 +710,6 @@ fun ComposeView(
is ComposeContextItem.EditingItem -> ContextItemView(contextItem.chatItem, painterResource(MR.images.ic_edit_filled)) { is ComposeContextItem.EditingItem -> ContextItemView(contextItem.chatItem, painterResource(MR.images.ic_edit_filled)) {
clearState() clearState()
} }
is ComposeContextItem.ForwardingItem -> ContextItemView(contextItem.chatItem, painterResource(MR.images.ic_forward), showSender = false) {
composeState.value = composeState.value.copy(contextItem = ComposeContextItem.NoContextItem)
}
} }
} }
@@ -804,10 +729,6 @@ fun ComposeView(
is SharedContent.Text -> onMessageChange(shared.text) is SharedContent.Text -> onMessageChange(shared.text)
is SharedContent.Media -> composeState.processPickedMedia(shared.uris, shared.text) is SharedContent.Media -> composeState.processPickedMedia(shared.uris, shared.text)
is SharedContent.File -> composeState.processPickedFile(shared.uri, shared.text) is SharedContent.File -> composeState.processPickedFile(shared.uri, shared.text)
is SharedContent.Forward -> composeState.value = composeState.value.copy(
contextItem = ComposeContextItem.ForwardingItem(shared.chatItem, shared.fromChatInfo),
preview = if (composeState.value.preview is ComposePreview.CLinkPreview) composeState.value.preview else ComposePreview.NoPreview
)
null -> {} null -> {}
} }
chatModel.sharedContent.value = null chatModel.sharedContent.value = null
@@ -822,17 +743,7 @@ fun ComposeView(
if (nextSendGrpInv.value) { if (nextSendGrpInv.value) {
ComposeContextInvitingContactMemberView() ComposeContextInvitingContactMemberView()
} }
val simplexLinkProhibited = hasSimplexLink.value && !chat.groupFeatureEnabled(GroupFeature.SimplexLinks)
val fileProhibited = composeState.value.attachmentPreview && !chat.groupFeatureEnabled(GroupFeature.Files)
val voiceProhibited = composeState.value.preview is ComposePreview.VoicePreview && !chat.chatInfo.featureEnabled(ChatFeature.Voice)
if (composeState.value.preview !is ComposePreview.VoicePreview || composeState.value.editing) { if (composeState.value.preview !is ComposePreview.VoicePreview || composeState.value.editing) {
if (simplexLinkProhibited) {
MsgNotAllowedView(generalGetString(MR.strings.simplex_links_not_allowed), icon = painterResource(MR.images.ic_link))
} else if (fileProhibited) {
MsgNotAllowedView(generalGetString(MR.strings.files_and_media_not_allowed), icon = painterResource(MR.images.ic_draft))
} else if (voiceProhibited) {
MsgNotAllowedView(generalGetString(MR.strings.voice_messages_not_allowed), icon = painterResource(MR.images.ic_mic))
}
contextItemView() contextItemView()
when { when {
composeState.value.editing && composeState.value.preview is ComposePreview.VoicePreview -> {} composeState.value.editing && composeState.value.preview is ComposePreview.VoicePreview -> {}
@@ -853,7 +764,7 @@ fun ComposeView(
modifier = Modifier.padding(end = 8.dp), modifier = Modifier.padding(end = 8.dp),
verticalAlignment = Alignment.Bottom, verticalAlignment = Alignment.Bottom,
) { ) {
val isGroupAndProhibitedFiles = chat.chatInfo is ChatInfo.Group && !chat.chatInfo.groupInfo.fullGroupPreferences.files.on(chat.chatInfo.groupInfo.membership) val isGroupAndProhibitedFiles = chat.chatInfo is ChatInfo.Group && !chat.chatInfo.groupInfo.fullGroupPreferences.files.on
val attachmentClicked = if (isGroupAndProhibitedFiles) { val attachmentClicked = if (isGroupAndProhibitedFiles) {
{ {
AlertManager.shared.showAlertMsg( AlertManager.shared.showAlertMsg(
@@ -947,17 +858,6 @@ fun ComposeView(
chatModel.removeLiveDummy() chatModel.removeLiveDummy()
CIFile.cachedRemoteFileRequests.clear() CIFile.cachedRemoteFileRequests.clear()
} }
if (appPlatform.isDesktop) {
// Don't enable this on Android, it breaks it, This method only works on desktop. For Android there is a `KeyChangeEffect(chatModel.chatId.value)`
DisposableEffect(Unit) {
onDispose {
if (chatModel.sharedContent.value is SharedContent.Forward && saveLastDraft && !composeState.value.empty) {
chatModel.draft.value = composeState.value
chatModel.draftChatId.value = chat.id
}
}
}
}
val timedMessageAllowed = remember(chat.chatInfo) { chat.chatInfo.featureEnabled(ChatFeature.TimedMessages) } val timedMessageAllowed = remember(chat.chatInfo) { chat.chatInfo.featureEnabled(ChatFeature.TimedMessages) }
val sendButtonColor = val sendButtonColor =
@@ -971,7 +871,6 @@ fun ComposeView(
chat.chatInfo is ChatInfo.Direct, chat.chatInfo is ChatInfo.Direct,
liveMessageAlertShown = chatModel.controller.appPrefs.liveMessageAlertShown, liveMessageAlertShown = chatModel.controller.appPrefs.liveMessageAlertShown,
sendMsgEnabled = sendMsgEnabled.value, sendMsgEnabled = sendMsgEnabled.value,
sendButtonEnabled = sendMsgEnabled.value && !(simplexLinkProhibited || fileProhibited || voiceProhibited),
nextSendGrpInv = nextSendGrpInv.value, nextSendGrpInv = nextSendGrpInv.value,
needToAllowVoiceToContact, needToAllowVoiceToContact,
allowedVoiceByPrefs, allowedVoiceByPrefs,
@@ -4,29 +4,26 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material.* import androidx.compose.material.*
import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.desktop.ui.tooling.preview.Preview import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.text.InlineTextContent import androidx.compose.ui.text.TextStyle
import androidx.compose.foundation.text.appendInlineContent
import androidx.compose.runtime.*
import androidx.compose.ui.text.*
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import chat.simplex.common.ui.theme.* import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.chat.item.* import chat.simplex.common.views.chat.item.*
import chat.simplex.common.model.* import chat.simplex.common.model.*
import chat.simplex.res.MR import chat.simplex.res.MR
import dev.icerock.moko.resources.ImageResource
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
@Composable @Composable
fun ContextItemView( fun ContextItemView(
contextItem: ChatItem, contextItem: ChatItem,
contextIcon: Painter, contextIcon: Painter,
showSender: Boolean = true,
cancelContextItem: () -> Unit cancelContextItem: () -> Unit
) { ) {
val sent = contextItem.chatDir.sent val sent = contextItem.chatDir.sent
@@ -34,47 +31,16 @@ fun ContextItemView(
val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage
@Composable @Composable
fun MessageText(attachment: ImageResource?, lines: Int) { fun msgContentView(lines: Int) {
val inlineContent: Pair<AnnotatedString.Builder.() -> Unit, Map<String, InlineTextContent>>? = if (attachment != null) {
remember(contextItem.id) {
val inlineContentBuilder: AnnotatedString.Builder.() -> Unit = {
appendInlineContent(id = "attachmentIcon")
append(" ")
}
val inlineContent = mapOf(
"attachmentIcon" to InlineTextContent(
Placeholder(20.sp, 20.sp, PlaceholderVerticalAlign.TextCenter)
) {
Icon(painterResource(attachment), null, tint = MaterialTheme.colors.secondary)
}
)
inlineContentBuilder to inlineContent
}
} else null
MarkdownText( MarkdownText(
contextItem.text, contextItem.formattedText, contextItem.text, contextItem.formattedText,
sender = null,
toggleSecrets = false, toggleSecrets = false,
maxLines = lines, maxLines = lines,
inlineContent = inlineContent,
linkMode = SimplexLinkMode.DESCRIPTION, linkMode = SimplexLinkMode.DESCRIPTION,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) )
} }
fun attachment(): ImageResource? =
when (contextItem.content.msgContent) {
is MsgContent.MCFile -> MR.images.ic_draft_filled
is MsgContent.MCImage -> MR.images.ic_image
is MsgContent.MCVoice -> MR.images.ic_play_arrow_filled
else -> null
}
@Composable
fun ContextMsgPreview(lines: Int) {
MessageText(remember(contextItem.id) { attachment() }, lines)
}
Row( Row(
Modifier Modifier
.padding(top = 8.dp) .padding(top = 8.dp)
@@ -98,7 +64,7 @@ fun ContextItemView(
tint = MaterialTheme.colors.secondary, tint = MaterialTheme.colors.secondary,
) )
val sender = contextItem.memberDisplayName val sender = contextItem.memberDisplayName
if (showSender && sender != null) { if (sender != null) {
Column( Column(
horizontalAlignment = Alignment.Start, horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.spacedBy(4.dp), verticalArrangement = Arrangement.spacedBy(4.dp),
@@ -107,10 +73,10 @@ fun ContextItemView(
sender, sender,
style = TextStyle(fontSize = 13.5.sp, color = CurrentColors.value.colors.secondary) style = TextStyle(fontSize = 13.5.sp, color = CurrentColors.value.colors.secondary)
) )
ContextMsgPreview(lines = 2) msgContentView(lines = 2)
} }
} else { } else {
ContextMsgPreview(lines = 3) msgContentView(lines = 3)
} }
} }
IconButton(onClick = cancelContextItem) { IconButton(onClick = cancelContextItem) {
@@ -39,7 +39,6 @@ fun SendMsgView(
isDirectChat: Boolean, isDirectChat: Boolean,
liveMessageAlertShown: SharedPreference<Boolean>, liveMessageAlertShown: SharedPreference<Boolean>,
sendMsgEnabled: Boolean, sendMsgEnabled: Boolean,
sendButtonEnabled: Boolean,
nextSendGrpInv: Boolean, nextSendGrpInv: Boolean,
needToAllowVoiceToContact: Boolean, needToAllowVoiceToContact: Boolean,
allowedVoiceByPrefs: Boolean, allowedVoiceByPrefs: Boolean,
@@ -72,12 +71,11 @@ fun SendMsgView(
} }
} }
val showVoiceButton = !nextSendGrpInv && cs.message.isEmpty() && showVoiceRecordIcon && !composeState.value.editing && val showVoiceButton = !nextSendGrpInv && cs.message.isEmpty() && showVoiceRecordIcon && !composeState.value.editing &&
!composeState.value.forwarding && cs.liveMessage == null && (cs.preview is ComposePreview.NoPreview || recState.value is RecordingState.Started) cs.liveMessage == null && (cs.preview is ComposePreview.NoPreview || recState.value is RecordingState.Started)
val showDeleteTextButton = rememberSaveable { mutableStateOf(false) } val showDeleteTextButton = rememberSaveable { mutableStateOf(false) }
val sendMsgButtonDisabled = !sendMsgEnabled || !cs.sendEnabled() || val sendMsgButtonDisabled = !sendMsgEnabled || !cs.sendEnabled() ||
(!allowedVoiceByPrefs && cs.preview is ComposePreview.VoicePreview) || (!allowedVoiceByPrefs && cs.preview is ComposePreview.VoicePreview) ||
cs.endLiveDisabled || cs.endLiveDisabled
!sendButtonEnabled
PlatformTextField(composeState, sendMsgEnabled, sendMsgButtonDisabled, textStyle, showDeleteTextButton, userIsObserver, onMessageChange, editPrevMessage, onFilesPasted) { PlatformTextField(composeState, sendMsgEnabled, sendMsgButtonDisabled, textStyle, showDeleteTextButton, userIsObserver, onMessageChange, editPrevMessage, onFilesPasted) {
if (!cs.inProgress) { if (!cs.inProgress) {
sendMessage(null) sendMessage(null)
@@ -157,7 +155,7 @@ fun SendMsgView(
fun MenuItems(): List<@Composable () -> Unit> { fun MenuItems(): List<@Composable () -> Unit> {
val menuItems = mutableListOf<@Composable () -> Unit>() val menuItems = mutableListOf<@Composable () -> Unit>()
if (cs.liveMessage == null && !cs.editing && !cs.forwarding && !nextSendGrpInv || sendMsgEnabled) { if (cs.liveMessage == null && !cs.editing && !nextSendGrpInv || sendMsgEnabled) {
if ( if (
cs.preview !is ComposePreview.VoicePreview && cs.preview !is ComposePreview.VoicePreview &&
cs.contextItem is ComposeContextItem.NoContextItem && cs.contextItem is ComposeContextItem.NoContextItem &&
@@ -432,7 +430,7 @@ private fun SendMsgButton(
.padding(4.dp) .padding(4.dp)
.alpha(alpha.value) .alpha(alpha.value)
.clip(CircleShape) .clip(CircleShape)
.background(if (enabled) sendButtonColor else MaterialTheme.colors.secondary.copy(alpha = 0.75f)) .background(if (enabled) sendButtonColor else MaterialTheme.colors.secondary)
.padding(3.dp) .padding(3.dp)
) )
} }
@@ -554,7 +552,6 @@ fun PreviewSendMsgView() {
isDirectChat = true, isDirectChat = true,
liveMessageAlertShown = SharedPreference(get = { true }, set = { }), liveMessageAlertShown = SharedPreference(get = { true }, set = { }),
sendMsgEnabled = true, sendMsgEnabled = true,
sendButtonEnabled = true,
nextSendGrpInv = false, nextSendGrpInv = false,
needToAllowVoiceToContact = false, needToAllowVoiceToContact = false,
allowedVoiceByPrefs = true, allowedVoiceByPrefs = true,
@@ -589,7 +586,6 @@ fun PreviewSendMsgViewEditing() {
isDirectChat = true, isDirectChat = true,
liveMessageAlertShown = SharedPreference(get = { true }, set = { }), liveMessageAlertShown = SharedPreference(get = { true }, set = { }),
sendMsgEnabled = true, sendMsgEnabled = true,
sendButtonEnabled = true,
nextSendGrpInv = false, nextSendGrpInv = false,
needToAllowVoiceToContact = false, needToAllowVoiceToContact = false,
allowedVoiceByPrefs = true, allowedVoiceByPrefs = true,
@@ -624,7 +620,6 @@ fun PreviewSendMsgViewInProgress() {
isDirectChat = true, isDirectChat = true,
liveMessageAlertShown = SharedPreference(get = { true }, set = { }), liveMessageAlertShown = SharedPreference(get = { true }, set = { }),
sendMsgEnabled = true, sendMsgEnabled = true,
sendButtonEnabled = true,
nextSendGrpInv = false, nextSendGrpInv = false,
needToAllowVoiceToContact = false, needToAllowVoiceToContact = false,
allowedVoiceByPrefs = true, allowedVoiceByPrefs = true,
@@ -62,7 +62,7 @@ fun GroupChatInfoView(chatModel: ChatModel, rhId: Long?, chatId: String, groupLi
}, },
members = chatModel.groupMembers members = chatModel.groupMembers
.filter { it.memberStatus != GroupMemberStatus.MemLeft && it.memberStatus != GroupMemberStatus.MemRemoved } .filter { it.memberStatus != GroupMemberStatus.MemLeft && it.memberStatus != GroupMemberStatus.MemRemoved }
.sortedByDescending { it.memberRole }, .sortedBy { it.displayName.lowercase() },
developerTools, developerTools,
groupLink, groupLink,
addMembers = { addMembers = {
@@ -309,7 +309,7 @@ fun GroupMemberInfoLayout(
SectionView { SectionView {
if (contactId != null && knownDirectChat(contactId) != null) { if (contactId != null && knownDirectChat(contactId) != null) {
OpenChatButton(onClick = { openDirectChat(contactId) }) OpenChatButton(onClick = { openDirectChat(contactId) })
} else if (groupInfo.fullGroupPreferences.directMessages.on(groupInfo.membership)) { } else if (groupInfo.fullGroupPreferences.directMessages.on) {
if (contactId != null) { if (contactId != null) {
OpenChatButton(onClick = { openDirectChat(contactId) }) OpenChatButton(onClick = { openDirectChat(contactId) })
} else if (member.activeConn?.peerChatVRange?.isCompatibleRange(CREATE_MEMBER_CONTACT_VRANGE) == true) { } else if (member.activeConn?.peerChatVRange?.isCompatibleRange(CREATE_MEMBER_CONTACT_VRANGE) == true) {
@@ -335,7 +335,7 @@ fun GroupMemberInfoLayout(
val clipboard = LocalClipboardManager.current val clipboard = LocalClipboardManager.current
ShareAddressButton { clipboard.shareText(simplexChatLink(member.contactLink)) } ShareAddressButton { clipboard.shareText(simplexChatLink(member.contactLink)) }
if (contactId != null) { if (contactId != null) {
if (knownDirectChat(contactId) == null && !groupInfo.fullGroupPreferences.directMessages.on(groupInfo.membership)) { if (knownDirectChat(contactId) == null && !groupInfo.fullGroupPreferences.directMessages.on) {
ConnectViaAddressButton(onClick = { connectViaAddress(member.contactLink) }) ConnectViaAddressButton(onClick = { connectViaAddress(member.contactLink) })
} }
} else { } else {
@@ -6,6 +6,7 @@ import SectionDividerSpaced
import SectionItemView import SectionItemView
import SectionTextFooter import SectionTextFooter
import SectionView import SectionView
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material.MaterialTheme import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text import androidx.compose.material.Text
@@ -19,20 +20,13 @@ import chat.simplex.common.views.usersettings.PreferenceToggleWithIcon
import chat.simplex.common.model.* import chat.simplex.common.model.*
import chat.simplex.common.platform.ColumnWithScrollBar import chat.simplex.common.platform.ColumnWithScrollBar
import chat.simplex.res.MR import chat.simplex.res.MR
import dev.icerock.moko.resources.compose.painterResource
private val featureRoles: List<Pair<GroupMemberRole?, String>> = listOf(
null to generalGetString(MR.strings.feature_roles_all_members),
GroupMemberRole.Admin to generalGetString(MR.strings.feature_roles_admins),
GroupMemberRole.Owner to generalGetString(MR.strings.feature_roles_owners)
)
@Composable @Composable
fun GroupPreferencesView(m: ChatModel, rhId: Long?, chatId: String, close: () -> Unit) { fun GroupPreferencesView(m: ChatModel, rhId: Long?, chatId: String, close: () -> Unit,) {
val groupInfo = remember { derivedStateOf { val groupInfo = remember { derivedStateOf {
val ch = m.getChat(chatId) val ch = m.getChat(chatId)
val g = (ch?.chatInfo as? ChatInfo.Group)?.groupInfo val g = (ch?.chatInfo as? ChatInfo.Group)?.groupInfo
if (g == null || ch.remoteHostId != rhId) null else g if (g == null || ch?.remoteHostId != rhId) null else g
}} }}
val gInfo = groupInfo.value ?: return val gInfo = groupInfo.value ?: return
var preferences by rememberSaveable(gInfo, stateSaver = serializableSaver()) { mutableStateOf(gInfo.fullGroupPreferences) } var preferences by rememberSaveable(gInfo, stateSaver = serializableSaver()) { mutableStateOf(gInfo.fullGroupPreferences) }
@@ -87,7 +81,7 @@ private fun GroupPreferencesLayout(
val onTTLUpdated = { ttl: Int? -> val onTTLUpdated = { ttl: Int? ->
applyPrefs(preferences.copy(timedMessages = preferences.timedMessages.copy(ttl = ttl))) applyPrefs(preferences.copy(timedMessages = preferences.timedMessages.copy(ttl = ttl)))
} }
FeatureSection(GroupFeature.TimedMessages, timedMessages, null, groupInfo, preferences, onTTLUpdated) { enable, _ -> FeatureSection(GroupFeature.TimedMessages, timedMessages, groupInfo, preferences, onTTLUpdated) { enable ->
if (enable == GroupFeatureEnabled.ON) { if (enable == GroupFeatureEnabled.ON) {
applyPrefs(preferences.copy(timedMessages = TimedMessagesGroupPreference(enable = enable, ttl = preferences.timedMessages.ttl ?: 86400))) applyPrefs(preferences.copy(timedMessages = TimedMessagesGroupPreference(enable = enable, ttl = preferences.timedMessages.ttl ?: 86400)))
} else { } else {
@@ -96,45 +90,33 @@ private fun GroupPreferencesLayout(
} }
SectionDividerSpaced(true, maxBottomPadding = false) SectionDividerSpaced(true, maxBottomPadding = false)
val allowDirectMessages = remember(preferences) { mutableStateOf(preferences.directMessages.enable) } val allowDirectMessages = remember(preferences) { mutableStateOf(preferences.directMessages.enable) }
val directMessagesRole = remember(preferences) { mutableStateOf(preferences.directMessages.role) } FeatureSection(GroupFeature.DirectMessages, allowDirectMessages, groupInfo, preferences, onTTLUpdated) {
FeatureSection(GroupFeature.DirectMessages, allowDirectMessages, directMessagesRole, groupInfo, preferences, onTTLUpdated) { enable, role -> applyPrefs(preferences.copy(directMessages = GroupPreference(enable = it)))
applyPrefs(preferences.copy(directMessages = RoleGroupPreference(enable = enable, role)))
} }
SectionDividerSpaced(true, maxBottomPadding = false) SectionDividerSpaced(true, maxBottomPadding = false)
val allowFullDeletion = remember(preferences) { mutableStateOf(preferences.fullDelete.enable) } val allowFullDeletion = remember(preferences) { mutableStateOf(preferences.fullDelete.enable) }
FeatureSection(GroupFeature.FullDelete, allowFullDeletion, null, groupInfo, preferences, onTTLUpdated) { enable, _ -> FeatureSection(GroupFeature.FullDelete, allowFullDeletion, groupInfo, preferences, onTTLUpdated) {
applyPrefs(preferences.copy(fullDelete = GroupPreference(enable = enable))) applyPrefs(preferences.copy(fullDelete = GroupPreference(enable = it)))
} }
SectionDividerSpaced(true, maxBottomPadding = false) SectionDividerSpaced(true, maxBottomPadding = false)
val allowReactions = remember(preferences) { mutableStateOf(preferences.reactions.enable) } val allowReactions = remember(preferences) { mutableStateOf(preferences.reactions.enable) }
FeatureSection(GroupFeature.Reactions, allowReactions, null, groupInfo, preferences, onTTLUpdated) { enable, _ -> FeatureSection(GroupFeature.Reactions, allowReactions, groupInfo, preferences, onTTLUpdated) {
applyPrefs(preferences.copy(reactions = GroupPreference(enable = enable))) applyPrefs(preferences.copy(reactions = GroupPreference(enable = it)))
} }
SectionDividerSpaced(true, maxBottomPadding = false) SectionDividerSpaced(true, maxBottomPadding = false)
val allowVoice = remember(preferences) { mutableStateOf(preferences.voice.enable) } val allowVoice = remember(preferences) { mutableStateOf(preferences.voice.enable) }
val voiceRole = remember(preferences) { mutableStateOf(preferences.voice.role) } FeatureSection(GroupFeature.Voice, allowVoice, groupInfo, preferences, onTTLUpdated) {
FeatureSection(GroupFeature.Voice, allowVoice, voiceRole, groupInfo, preferences, onTTLUpdated) { enable, role -> applyPrefs(preferences.copy(voice = GroupPreference(enable = it)))
applyPrefs(preferences.copy(voice = RoleGroupPreference(enable = enable, role)))
} }
SectionDividerSpaced(true, maxBottomPadding = false) SectionDividerSpaced(true, maxBottomPadding = false)
val allowFiles = remember(preferences) { mutableStateOf(preferences.files.enable) } val allowFiles = remember(preferences) { mutableStateOf(preferences.files.enable) }
val filesRole = remember(preferences) { mutableStateOf(preferences.files.role) } FeatureSection(GroupFeature.Files, allowFiles, groupInfo, preferences, onTTLUpdated) {
FeatureSection(GroupFeature.Files, allowFiles, filesRole, groupInfo, preferences, onTTLUpdated) { enable, role -> applyPrefs(preferences.copy(files = GroupPreference(enable = it)))
applyPrefs(preferences.copy(files = RoleGroupPreference(enable = enable, role)))
} }
// TODO enable simplexLinks preference in 5.8
// SectionDividerSpaced(true, maxBottomPadding = false)
// val allowSimplexLinks = remember(preferences) { mutableStateOf(preferences.simplexLinks.enable) }
// val simplexLinksRole = remember(preferences) { mutableStateOf(preferences.simplexLinks.role) }
// FeatureSection(GroupFeature.SimplexLinks, allowSimplexLinks, simplexLinksRole, groupInfo, preferences, onTTLUpdated) { enable, role ->
// applyPrefs(preferences.copy(simplexLinks = RoleGroupPreference(enable = enable, role)))
// }
SectionDividerSpaced(true, maxBottomPadding = false) SectionDividerSpaced(true, maxBottomPadding = false)
val enableHistory = remember(preferences) { mutableStateOf(preferences.history.enable) } val enableHistory = remember(preferences) { mutableStateOf(preferences.history.enable) }
FeatureSection(GroupFeature.History, enableHistory, null, groupInfo, preferences, onTTLUpdated) { enable, _ -> FeatureSection(GroupFeature.History, enableHistory, groupInfo, preferences, onTTLUpdated) {
applyPrefs(preferences.copy(history = GroupPreference(enable = enable))) applyPrefs(preferences.copy(history = GroupPreference(enable = it)))
} }
if (groupInfo.canEdit) { if (groupInfo.canEdit) {
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false) SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
@@ -152,11 +134,10 @@ private fun GroupPreferencesLayout(
private fun FeatureSection( private fun FeatureSection(
feature: GroupFeature, feature: GroupFeature,
enableFeature: State<GroupFeatureEnabled>, enableFeature: State<GroupFeatureEnabled>,
enableForRole: State<GroupMemberRole?>? = null,
groupInfo: GroupInfo, groupInfo: GroupInfo,
preferences: FullGroupPreferences, preferences: FullGroupPreferences,
onTTLUpdated: (Int?) -> Unit, onTTLUpdated: (Int?) -> Unit,
onSelected: (GroupFeatureEnabled, GroupMemberRole?) -> Unit onSelected: (GroupFeatureEnabled) -> Unit
) { ) {
SectionView { SectionView {
val on = enableFeature.value == GroupFeatureEnabled.ON val on = enableFeature.value == GroupFeatureEnabled.ON
@@ -170,7 +151,7 @@ private fun FeatureSection(
iconTint, iconTint,
enableFeature.value == GroupFeatureEnabled.ON, enableFeature.value == GroupFeatureEnabled.ON,
) { checked -> ) { checked ->
onSelected(if (checked) GroupFeatureEnabled.ON else GroupFeatureEnabled.OFF, enableForRole?.value) onSelected(if (checked) GroupFeatureEnabled.ON else GroupFeatureEnabled.OFF)
} }
if (timedOn) { if (timedOn) {
val ttl = rememberSaveable(preferences.timedMessages) { mutableStateOf(preferences.timedMessages.ttl) } val ttl = rememberSaveable(preferences.timedMessages) { mutableStateOf(preferences.timedMessages.ttl) }
@@ -184,18 +165,6 @@ private fun FeatureSection(
onSelected = onTTLUpdated onSelected = onTTLUpdated
) )
} }
if (enableFeature.value == GroupFeatureEnabled.ON && enableForRole != null) {
ExposedDropDownSettingRow(
generalGetString(MR.strings.feature_enabled_for),
featureRoles,
enableForRole,
// remove in v5.8
enabled = remember { mutableStateOf(false) },
onSelected = { value ->
onSelected(enableFeature.value, value)
}
)
}
} else { } else {
InfoRow( InfoRow(
feature.text, feature.text,
@@ -206,14 +175,6 @@ private fun FeatureSection(
if (timedOn) { if (timedOn) {
InfoRow(generalGetString(MR.strings.delete_after), timeText(preferences.timedMessages.ttl)) InfoRow(generalGetString(MR.strings.delete_after), timeText(preferences.timedMessages.ttl))
} }
if (enableFeature.value == GroupFeatureEnabled.ON && enableForRole != null) {
InfoRow(generalGetString(MR.strings.feature_enabled_for), featureRoles.firstOrNull { it.first == enableForRole.value }?.second ?: generalGetString(MR.strings.feature_roles_all_members), textColor = MaterialTheme.colors.secondary)
}
}
}
KeyChangeEffect(enableFeature.value) {
if (enableFeature.value == GroupFeatureEnabled.OFF) {
onSelected(enableFeature.value, null)
} }
} }
SectionTextFooter(feature.enableDescription(enableFeature.value, groupInfo.canEdit)) SectionTextFooter(feature.enableDescription(enableFeature.value, groupInfo.canEdit))
@@ -16,7 +16,6 @@ import chat.simplex.common.platform.onRightClick
@Composable @Composable
fun CIChatFeatureView( fun CIChatFeatureView(
chatInfo: ChatInfo,
chatItem: ChatItem, chatItem: ChatItem,
feature: Feature, feature: Feature,
iconColor: Color, iconColor: Color,
@@ -24,7 +23,7 @@ fun CIChatFeatureView(
revealed: MutableState<Boolean>, revealed: MutableState<Boolean>,
showMenu: MutableState<Boolean>, showMenu: MutableState<Boolean>,
) { ) {
val merged = if (!revealed.value) mergedFeatures(chatItem, chatInfo) else emptyList() val merged = if (!revealed.value) mergedFeatures(chatItem) else emptyList()
Box( Box(
Modifier Modifier
.combinedClickable( .combinedClickable(
@@ -71,7 +70,7 @@ private fun Feature.toFeatureInfo(color: Color, param: Int?, type: String): Feat
) )
@Composable @Composable
private fun mergedFeatures(chatItem: ChatItem, chatInfo: ChatInfo): List<FeatureInfo>? { private fun mergedFeatures(chatItem: ChatItem): List<FeatureInfo>? {
val m = ChatModel val m = ChatModel
val fs: ArrayList<FeatureInfo> = arrayListOf() val fs: ArrayList<FeatureInfo> = arrayListOf()
val icons: MutableSet<PainterBox> = mutableSetOf() val icons: MutableSet<PainterBox> = mutableSetOf()
@@ -79,7 +78,7 @@ private fun mergedFeatures(chatItem: ChatItem, chatInfo: ChatInfo): List<Feature
if (i != null) { if (i != null) {
val reversedChatItems = m.chatItems.asReversed() val reversedChatItems = m.chatItems.asReversed()
while (i < reversedChatItems.size) { while (i < reversedChatItems.size) {
val f = featureInfo(reversedChatItems[i], chatInfo) ?: break val f = featureInfo(reversedChatItems[i]) ?: break
if (!icons.contains(f.icon)) { if (!icons.contains(f.icon)) {
fs.add(0, f) fs.add(0, f)
icons.add(f.icon) icons.add(f.icon)
@@ -91,12 +90,12 @@ private fun mergedFeatures(chatItem: ChatItem, chatInfo: ChatInfo): List<Feature
} }
@Composable @Composable
private fun featureInfo(ci: ChatItem, chatInfo: ChatInfo): FeatureInfo? = private fun featureInfo(ci: ChatItem): FeatureInfo? =
when (ci.content) { when (ci.content) {
is CIContent.RcvChatFeature -> ci.content.feature.toFeatureInfo(ci.content.enabled.iconColor, ci.content.param, ci.content.feature.name) is CIContent.RcvChatFeature -> ci.content.feature.toFeatureInfo(ci.content.enabled.iconColor, ci.content.param, ci.content.feature.name)
is CIContent.SndChatFeature -> ci.content.feature.toFeatureInfo(ci.content.enabled.iconColor, ci.content.param, ci.content.feature.name) is CIContent.SndChatFeature -> ci.content.feature.toFeatureInfo(ci.content.enabled.iconColor, ci.content.param, ci.content.feature.name)
is CIContent.RcvGroupFeature -> ci.content.groupFeature.toFeatureInfo(ci.content.preference.enabled(ci.content.memberRole_, (chatInfo as? ChatInfo.Group)?.groupInfo?.membership).iconColor, ci.content.param, ci.content.groupFeature.name) is CIContent.RcvGroupFeature -> ci.content.groupFeature.toFeatureInfo(ci.content.preference.enable.iconColor, ci.content.param, ci.content.groupFeature.name)
is CIContent.SndGroupFeature -> ci.content.groupFeature.toFeatureInfo(ci.content.preference.enabled(ci.content.memberRole_, (chatInfo as? ChatInfo.Group)?.groupInfo?.membership).iconColor, ci.content.param, ci.content.groupFeature.name) is CIContent.SndGroupFeature -> ci.content.groupFeature.toFeatureInfo(ci.content.preference.enable.iconColor, ci.content.param, ci.content.groupFeature.name)
else -> null else -> null
} }
@@ -12,7 +12,7 @@ import chat.simplex.common.ui.theme.*
@Composable @Composable
fun CIEventView(text: AnnotatedString) { fun CIEventView(text: AnnotatedString) {
Text(text, Modifier.padding(horizontal = 6.dp, vertical = 6.dp), style = MaterialTheme.typography.body1.copy(lineHeight = 22.sp), maxLines = 3) Text(text, Modifier.padding(horizontal = 6.dp, vertical = 6.dp), style = MaterialTheme.typography.body1.copy(lineHeight = 22.sp))
} }
@Preview/*( @Preview/*(
uiMode = Configuration.UI_MODE_NIGHT_YES, uiMode = Configuration.UI_MODE_NIGHT_YES,
@@ -59,11 +59,18 @@ fun CIFileView(
} }
} }
fun fileSizeValid(): Boolean {
if (file != null) {
return file.fileSize <= getMaxFileSize(file.fileProtocol)
}
return false
}
fun fileAction() { fun fileAction() {
if (file != null) { if (file != null) {
when { when {
file.fileStatus is CIFileStatus.RcvInvitation -> { file.fileStatus is CIFileStatus.RcvInvitation -> {
if (fileSizeValid(file)) { if (fileSizeValid()) {
receiveFile(file.fileId) receiveFile(file.fileId)
} else { } else {
AlertManager.shared.showAlertMsg( AlertManager.shared.showAlertMsg(
@@ -158,7 +165,7 @@ fun CIFileView(
is CIFileStatus.SndCancelled -> fileIcon(innerIcon = painterResource(MR.images.ic_close)) is CIFileStatus.SndCancelled -> fileIcon(innerIcon = painterResource(MR.images.ic_close))
is CIFileStatus.SndError -> fileIcon(innerIcon = painterResource(MR.images.ic_close)) is CIFileStatus.SndError -> fileIcon(innerIcon = painterResource(MR.images.ic_close))
is CIFileStatus.RcvInvitation -> is CIFileStatus.RcvInvitation ->
if (fileSizeValid(file)) if (fileSizeValid())
fileIcon(innerIcon = painterResource(MR.images.ic_arrow_downward), color = MaterialTheme.colors.primary) fileIcon(innerIcon = painterResource(MR.images.ic_arrow_downward), color = MaterialTheme.colors.primary)
else else
fileIcon(innerIcon = painterResource(MR.images.ic_priority_high), color = WarningOrange) fileIcon(innerIcon = painterResource(MR.images.ic_priority_high), color = WarningOrange)
@@ -209,8 +216,6 @@ fun CIFileView(
} }
} }
fun fileSizeValid(file: CIFile): Boolean = file.fileSize <= getMaxFileSize(file.fileProtocol)
@Composable @Composable
fun rememberSaveFileLauncher(ciFile: CIFile?): FileChooserLauncher = fun rememberSaveFileLauncher(ciFile: CIFile?): FileChooserLauncher =
rememberFileChooserLauncher(false, ciFile) { to: URI? -> rememberFileChooserLauncher(false, ciFile) { to: URI? ->
@@ -19,7 +19,6 @@ import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.* import androidx.compose.ui.unit.*
import chat.simplex.common.model.* import chat.simplex.common.model.*
import chat.simplex.common.model.ChatModel.controller
import chat.simplex.common.platform.* import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.* import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.chat.* import chat.simplex.common.views.chat.*
@@ -41,7 +40,6 @@ fun chatEventText(eventText: String, ts: String): AnnotatedString =
@Composable @Composable
fun ChatItemView( fun ChatItemView(
rhId: Long?,
cInfo: ChatInfo, cInfo: ChatInfo,
cItem: ChatItem, cItem: ChatItem,
composeState: MutableState<ComposeState>, composeState: MutableState<ComposeState>,
@@ -197,14 +195,10 @@ fun ChatItemView(
} }
val clipboard = LocalClipboardManager.current val clipboard = LocalClipboardManager.current
val cachedRemoteReqs = remember { CIFile.cachedRemoteFileRequests } val cachedRemoteReqs = remember { CIFile.cachedRemoteFileRequests }
fun fileForwardingAllowed() = when {
cItem.file != null && chatModel.connectedToRemote() && cachedRemoteReqs[cItem.file.fileSource] != false && cItem.file.loaded -> true
getLoadedFilePath(cItem.file) != null -> true
else -> false
}
val copyAndShareAllowed = when { val copyAndShareAllowed = when {
cItem.content.text.isNotEmpty() -> true cItem.content.text.isNotEmpty() -> true
fileForwardingAllowed() -> true cItem.file != null && chatModel.connectedToRemote() && cachedRemoteReqs[cItem.file.fileSource] != false && cItem.file.loaded -> true
getLoadedFilePath(cItem.file) != null -> true
else -> false else -> false
} }
@@ -233,19 +227,8 @@ fun ChatItemView(
showMenu.value = false showMenu.value = false
}) })
} }
if (cItem.file != null && (getLoadedFilePath(cItem.file) != null || (chatModel.connectedToRemote() && cachedRemoteReqs[cItem.file.fileSource] != false && cItem.file.loaded))) { if ((cItem.content.msgContent is MsgContent.MCImage || cItem.content.msgContent is MsgContent.MCVideo || cItem.content.msgContent is MsgContent.MCFile || cItem.content.msgContent is MsgContent.MCVoice) && (getLoadedFilePath(cItem.file) != null || (chatModel.connectedToRemote() && cachedRemoteReqs[cItem.file?.fileSource] != false && cItem.file?.loaded == true))) {
SaveContentItemAction(cItem, saveFileLauncher, showMenu) SaveContentItemAction(cItem, saveFileLauncher, showMenu)
} else if (cItem.file != null && cItem.file.fileStatus is CIFileStatus.RcvInvitation && fileSizeValid(cItem.file)) {
ItemAction(stringResource(MR.strings.download_file), painterResource(MR.images.ic_arrow_downward), onClick = {
withBGApi {
Log.d(TAG, "ChatItemView downloadFileAction")
val user = chatModel.currentUser.value
if (user != null) {
controller.receiveFile(rhId, user, cItem.file.fileId)
}
}
showMenu.value = false
})
} }
if (cItem.meta.editable && cItem.content.msgContent !is MsgContent.MCVoice && !live) { if (cItem.meta.editable && cItem.content.msgContent !is MsgContent.MCVoice && !live) {
ItemAction(stringResource(MR.strings.edit_verb), painterResource(MR.images.ic_edit_filled), onClick = { ItemAction(stringResource(MR.strings.edit_verb), painterResource(MR.images.ic_edit_filled), onClick = {
@@ -253,16 +236,6 @@ fun ChatItemView(
showMenu.value = false showMenu.value = false
}) })
} }
if (cItem.meta.itemDeleted == null &&
(cItem.file == null || fileForwardingAllowed()) &&
!cItem.isLiveDummy && !live
) {
ItemAction(stringResource(MR.strings.forward_chat_item), painterResource(MR.images.ic_forward), onClick = {
chatModel.chatId.value = null
chatModel.sharedContent.value = SharedContent.Forward(cItem, cInfo)
showMenu.value = false
})
}
ItemInfoAction(cInfo, cItem, showItemDetails, showMenu) ItemInfoAction(cInfo, cItem, showItemDetails, showMenu)
if (revealed.value) { if (revealed.value) {
HideItemAction(revealed, showMenu) HideItemAction(revealed, showMenu)
@@ -331,7 +304,7 @@ fun ChatItemView(
MarkedDeletedItemView(cItem, cInfo.timedMessagesTTL, revealed) MarkedDeletedItemView(cItem, cInfo.timedMessagesTTL, revealed)
MarkedDeletedItemDropdownMenu() MarkedDeletedItemDropdownMenu()
} else { } else {
if (cItem.quotedItem == null && cItem.meta.itemForwarded == null && cItem.meta.itemDeleted == null && !cItem.meta.isLive) { if (cItem.quotedItem == null && cItem.meta.itemDeleted == null && !cItem.meta.isLive) {
if (mc is MsgContent.MCText && isShortEmoji(cItem.content.text)) { if (mc is MsgContent.MCText && isShortEmoji(cItem.content.text)) {
EmojiItemView(cItem, cInfo.timedMessagesTTL) EmojiItemView(cItem, cInfo.timedMessagesTTL)
} else if (mc is MsgContent.MCVoice && cItem.content.text.isEmpty()) { } else if (mc is MsgContent.MCVoice && cItem.content.text.isEmpty()) {
@@ -469,11 +442,11 @@ fun ChatItemView(
MsgContentItemDropdownMenu() MsgContentItemDropdownMenu()
} }
is CIContent.RcvChatFeature -> { is CIContent.RcvChatFeature -> {
CIChatFeatureView(cInfo, cItem, c.feature, c.enabled.iconColor, revealed = revealed, showMenu = showMenu) CIChatFeatureView(cItem, c.feature, c.enabled.iconColor, revealed = revealed, showMenu = showMenu)
MsgContentItemDropdownMenu() MsgContentItemDropdownMenu()
} }
is CIContent.SndChatFeature -> { is CIContent.SndChatFeature -> {
CIChatFeatureView(cInfo, cItem, c.feature, c.enabled.iconColor, revealed = revealed, showMenu = showMenu) CIChatFeatureView(cItem, c.feature, c.enabled.iconColor, revealed = revealed, showMenu = showMenu)
MsgContentItemDropdownMenu() MsgContentItemDropdownMenu()
} }
is CIContent.RcvChatPreference -> { is CIContent.RcvChatPreference -> {
@@ -481,23 +454,23 @@ fun ChatItemView(
CIFeaturePreferenceView(cItem, ct, c.feature, c.allowed, acceptFeature) CIFeaturePreferenceView(cItem, ct, c.feature, c.allowed, acceptFeature)
} }
is CIContent.SndChatPreference -> { is CIContent.SndChatPreference -> {
CIChatFeatureView(cInfo, cItem, c.feature, MaterialTheme.colors.secondary, icon = c.feature.icon, revealed, showMenu = showMenu) CIChatFeatureView(cItem, c.feature, MaterialTheme.colors.secondary, icon = c.feature.icon, revealed, showMenu = showMenu)
MsgContentItemDropdownMenu() MsgContentItemDropdownMenu()
} }
is CIContent.RcvGroupFeature -> { is CIContent.RcvGroupFeature -> {
CIChatFeatureView(cInfo, cItem, c.groupFeature, c.preference.enabled(c.memberRole_, (cInfo as? ChatInfo.Group)?.groupInfo?.membership).iconColor, revealed = revealed, showMenu = showMenu) CIChatFeatureView(cItem, c.groupFeature, c.preference.enable.iconColor, revealed = revealed, showMenu = showMenu)
MsgContentItemDropdownMenu() MsgContentItemDropdownMenu()
} }
is CIContent.SndGroupFeature -> { is CIContent.SndGroupFeature -> {
CIChatFeatureView(cInfo, cItem, c.groupFeature, c.preference.enabled(c.memberRole_, (cInfo as? ChatInfo.Group)?.groupInfo?.membership).iconColor, revealed = revealed, showMenu = showMenu) CIChatFeatureView(cItem, c.groupFeature, c.preference.enable.iconColor, revealed = revealed, showMenu = showMenu)
MsgContentItemDropdownMenu() MsgContentItemDropdownMenu()
} }
is CIContent.RcvChatFeatureRejected -> { is CIContent.RcvChatFeatureRejected -> {
CIChatFeatureView(cInfo, cItem, c.feature, Color.Red, revealed = revealed, showMenu = showMenu) CIChatFeatureView(cItem, c.feature, Color.Red, revealed = revealed, showMenu = showMenu)
MsgContentItemDropdownMenu() MsgContentItemDropdownMenu()
} }
is CIContent.RcvGroupFeatureRejected -> { is CIContent.RcvGroupFeatureRejected -> {
CIChatFeatureView(cInfo, cItem, c.groupFeature, Color.Red, revealed = revealed, showMenu = showMenu) CIChatFeatureView(cItem, c.groupFeature, Color.Red, revealed = revealed, showMenu = showMenu)
MsgContentItemDropdownMenu() MsgContentItemDropdownMenu()
} }
is CIContent.SndModerated -> DeletedItem() is CIContent.SndModerated -> DeletedItem()
@@ -751,7 +724,7 @@ fun deleteMessageAlertDialog(chatItem: ChatItem, questionText: String, deleteMes
deleteMessage(chatItem.id, CIDeleteMode.cidmInternal) deleteMessage(chatItem.id, CIDeleteMode.cidmInternal)
AlertManager.shared.hideAlert() AlertManager.shared.hideAlert()
}) { Text(stringResource(MR.strings.for_me_only), color = MaterialTheme.colors.error) } }) { Text(stringResource(MR.strings.for_me_only), color = MaterialTheme.colors.error) }
if (chatItem.meta.deletable && !chatItem.localNote) { if (chatItem.meta.editable && !chatItem.localNote) {
Spacer(Modifier.padding(horizontal = 4.dp)) Spacer(Modifier.padding(horizontal = 4.dp))
TextButton(onClick = { TextButton(onClick = {
deleteMessage(chatItem.id, CIDeleteMode.cidmBroadcast) deleteMessage(chatItem.id, CIDeleteMode.cidmBroadcast)
@@ -809,7 +782,6 @@ expect fun copyItemToClipboard(cItem: ChatItem, clipboard: ClipboardManager)
fun PreviewChatItemView() { fun PreviewChatItemView() {
SimpleXTheme { SimpleXTheme {
ChatItemView( ChatItemView(
rhId = null,
ChatInfo.Direct.sampleData, ChatInfo.Direct.sampleData,
ChatItem.getSampleData( ChatItem.getSampleData(
1, CIDirection.DirectSnd(), Clock.System.now(), "hello" 1, CIDirection.DirectSnd(), Clock.System.now(), "hello"
@@ -846,7 +818,6 @@ fun PreviewChatItemView() {
fun PreviewChatItemViewDeletedContent() { fun PreviewChatItemViewDeletedContent() {
SimpleXTheme { SimpleXTheme {
ChatItemView( ChatItemView(
rhId = null,
ChatInfo.Direct.sampleData, ChatInfo.Direct.sampleData,
ChatItem.getDeletedContentSampleData(), ChatItem.getDeletedContentSampleData(),
useLinkPreviews = true, useLinkPreviews = true,
@@ -87,16 +87,15 @@ fun FramedItemView(
} }
@Composable @Composable
fun FramedItemHeader(caption: String, italic: Boolean, icon: Painter? = null, pad: Boolean = false) { fun FramedItemHeader(caption: String, italic: Boolean, icon: Painter? = null) {
val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage
val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage
Row( Row(
Modifier Modifier
.background(if (sent) sentColor.toQuote() else receivedColor.toQuote()) .background(if (sent) sentColor.toQuote() else receivedColor.toQuote())
.fillMaxWidth() .fillMaxWidth()
.padding(start = 8.dp, top = 6.dp, end = 12.dp, bottom = if (pad || (ci.quotedItem == null && ci.meta.itemForwarded == null)) 6.dp else 0.dp), .padding(start = 8.dp, top = 6.dp, end = 12.dp, bottom = if (ci.quotedItem == null) 6.dp else 0.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp), horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically
) { ) {
if (icon != null) { if (icon != null) {
Icon( Icon(
@@ -185,7 +184,7 @@ fun FramedItemView(
} }
val transparentBackground = (ci.content.msgContent is MsgContent.MCImage || ci.content.msgContent is MsgContent.MCVideo) && val transparentBackground = (ci.content.msgContent is MsgContent.MCImage || ci.content.msgContent is MsgContent.MCVideo) &&
!ci.meta.isLive && ci.content.text.isEmpty() && ci.quotedItem == null && ci.meta.itemForwarded == null !ci.meta.isLive && ci.content.text.isEmpty() && ci.quotedItem == null
val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage
val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage
@@ -220,11 +219,7 @@ fun FramedItemView(
} else if (ci.meta.isLive) { } else if (ci.meta.isLive) {
FramedItemHeader(stringResource(MR.strings.live), false) FramedItemHeader(stringResource(MR.strings.live), false)
} }
if (ci.quotedItem != null) { ci.quotedItem?.let { ciQuoteView(it) }
ciQuoteView(ci.quotedItem)
} else if (ci.meta.itemForwarded != null) {
FramedItemHeader(ci.meta.itemForwarded.text(chatInfo.chatType), true, painterResource(MR.images.ic_forward), pad = true)
}
if (ci.file == null && ci.formattedText == null && !ci.meta.isLive && isShortEmoji(ci.content.text)) { if (ci.file == null && ci.formattedText == null && !ci.meta.isLive && isShortEmoji(ci.content.text)) {
Box(Modifier.padding(vertical = 6.dp, horizontal = 12.dp)) { Box(Modifier.padding(vertical = 6.dp, horizontal = 12.dp)) {
Column( Column(
@@ -68,7 +68,7 @@ fun MarkdownText (
senderBold: Boolean = false, senderBold: Boolean = false,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
linkMode: SimplexLinkMode, linkMode: SimplexLinkMode,
inlineContent: Pair<AnnotatedString.Builder.() -> Unit, Map<String, InlineTextContent>>? = null, inlineContent: Map<String, InlineTextContent>? = null,
onLinkLongClick: (link: String) -> Unit = {} onLinkLongClick: (link: String) -> Unit = {}
) { ) {
val textLayoutDirection = remember (text) { val textLayoutDirection = remember (text) {
@@ -119,7 +119,6 @@ fun MarkdownText (
} }
if (formattedText == null) { if (formattedText == null) {
val annotatedText = buildAnnotatedString { val annotatedText = buildAnnotatedString {
inlineContent?.first?.invoke(this)
appendSender(this, sender, senderBold) appendSender(this, sender, senderBold)
if (text is String) append(text) if (text is String) append(text)
else if (text is AnnotatedString) append(text) else if (text is AnnotatedString) append(text)
@@ -128,11 +127,10 @@ fun MarkdownText (
} }
if (meta != null) withStyle(reserveTimestampStyle) { append(reserve) } if (meta != null) withStyle(reserveTimestampStyle) { append(reserve) }
} }
Text(annotatedText, style = style, modifier = modifier, maxLines = maxLines, overflow = overflow, inlineContent = inlineContent?.second ?: mapOf()) Text(annotatedText, style = style, modifier = modifier, maxLines = maxLines, overflow = overflow, inlineContent = inlineContent ?: mapOf())
} else { } else {
var hasAnnotations = false var hasAnnotations = false
val annotatedText = buildAnnotatedString { val annotatedText = buildAnnotatedString {
inlineContent?.first?.invoke(this)
appendSender(this, sender, senderBold) appendSender(this, sender, senderBold)
for ((i, ft) in formattedText.withIndex()) { for ((i, ft) in formattedText.withIndex()) {
if (ft.format == null) append(ft.text) if (ft.format == null) append(ft.text)
@@ -212,7 +210,7 @@ fun MarkdownText (
} }
) )
} else { } else {
Text(annotatedText, style = style, modifier = modifier, maxLines = maxLines, overflow = overflow, inlineContent = inlineContent?.second ?: mapOf()) Text(annotatedText, style = style, modifier = modifier, maxLines = maxLines, overflow = overflow)
} }
} }
} }
@@ -90,7 +90,7 @@ fun ChatPreviewView(
Icon(painterResource(MR.images.ic_verified_user), null, Modifier.size(19.dp).padding(end = 3.dp, top = 1.dp), tint = MaterialTheme.colors.secondary) Icon(painterResource(MR.images.ic_verified_user), null, Modifier.size(19.dp).padding(end = 3.dp, top = 1.dp), tint = MaterialTheme.colors.secondary)
} }
fun messageDraft(draft: ComposeState): Pair<AnnotatedString.Builder.() -> Unit, Map<String, InlineTextContent>> { fun messageDraft(draft: ComposeState): Pair<AnnotatedString, Map<String, InlineTextContent>> {
fun attachment(): Pair<ImageResource, String?>? = fun attachment(): Pair<ImageResource, String?>? =
when (draft.preview) { when (draft.preview) {
is ComposePreview.FilePreview -> MR.images.ic_draft_filled to draft.preview.fileName is ComposePreview.FilePreview -> MR.images.ic_draft_filled to draft.preview.fileName
@@ -100,7 +100,7 @@ fun ChatPreviewView(
} }
val attachment = attachment() val attachment = attachment()
val inlineContentBuilder: AnnotatedString.Builder.() -> Unit = { val text = buildAnnotatedString {
appendInlineContent(id = "editIcon") appendInlineContent(id = "editIcon")
append(" ") append(" ")
if (attachment != null) { if (attachment != null) {
@@ -110,6 +110,7 @@ fun ChatPreviewView(
} }
append(" ") append(" ")
} }
append(draft.message)
} }
val inlineContent: Map<String, InlineTextContent> = mapOf( val inlineContent: Map<String, InlineTextContent> = mapOf(
"editIcon" to InlineTextContent( "editIcon" to InlineTextContent(
@@ -123,7 +124,7 @@ fun ChatPreviewView(
Icon(if (attachment?.first != null) painterResource(attachment.first) else painterResource(MR.images.ic_edit_note), null, tint = MaterialTheme.colors.secondary) Icon(if (attachment?.first != null) painterResource(attachment.first) else painterResource(MR.images.ic_edit_note), null, tint = MaterialTheme.colors.secondary)
} }
) )
return inlineContentBuilder to inlineContent return text to inlineContent
} }
@Composable @Composable
@@ -168,7 +169,7 @@ fun ChatPreviewView(
if (ci != null) { if (ci != null) {
if (showChatPreviews || (chatModelDraftChatId == chat.id && chatModelDraft != null)) { if (showChatPreviews || (chatModelDraftChatId == chat.id && chatModelDraft != null)) {
val (text: CharSequence, inlineTextContent) = when { val (text: CharSequence, inlineTextContent) = when {
chatModelDraftChatId == chat.id && chatModelDraft != null -> remember(chatModelDraft) { chatModelDraft.message to messageDraft(chatModelDraft) } chatModelDraftChatId == chat.id && chatModelDraft != null -> remember(chatModelDraft) { messageDraft(chatModelDraft) }
ci.meta.itemDeleted == null -> ci.text to null ci.meta.itemDeleted == null -> ci.text to null
else -> markedDeletedText(ci.meta) to null else -> markedDeletedText(ci.meta) to null
} }
@@ -13,8 +13,9 @@ import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import chat.simplex.common.SettingsViewState import chat.simplex.common.SettingsViewState
import chat.simplex.common.model.*
import chat.simplex.common.views.helpers.* import chat.simplex.common.views.helpers.*
import chat.simplex.common.model.Chat
import chat.simplex.common.model.ChatModel
import chat.simplex.common.platform.* import chat.simplex.common.platform.*
import chat.simplex.res.MR import chat.simplex.res.MR
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@@ -73,7 +74,7 @@ private fun ShareListToolbar(chatModel: ChatModel, userPickerState: MutableState
val navButton: @Composable RowScope.() -> Unit = { val navButton: @Composable RowScope.() -> Unit = {
when { when {
showSearch -> NavigationButtonBack(hideSearchOnBack) showSearch -> NavigationButtonBack(hideSearchOnBack)
(users.size > 1 || chatModel.remoteHosts.isNotEmpty()) && remember { chatModel.sharedContent }.value !is SharedContent.Forward -> { users.size > 1 || chatModel.remoteHosts.isNotEmpty() -> {
val allRead = users val allRead = users
.filter { u -> !u.user.activeUser && !u.user.hidden } .filter { u -> !u.user.activeUser && !u.user.hidden }
.all { u -> u.unreadCount == 0 } .all { u -> u.unreadCount == 0 }
@@ -81,14 +82,7 @@ private fun ShareListToolbar(chatModel: ChatModel, userPickerState: MutableState
userPickerState.value = AnimatedViewState.VISIBLE userPickerState.value = AnimatedViewState.VISIBLE
} }
} }
else -> NavigationButtonBack(onButtonClicked = { else -> NavigationButtonBack(onButtonClicked = { chatModel.sharedContent.value = null })
val sharedContent = chatModel.sharedContent.value
// Drop shared content
chatModel.sharedContent.value = null
if (sharedContent is SharedContent.Forward) {
chatModel.chatId.value = sharedContent.fromChatInfo.id
}
})
} }
} }
if (chatModel.chats.size >= 8) { if (chatModel.chats.size >= 8) {
@@ -124,8 +118,7 @@ private fun ShareListToolbar(chatModel: ChatModel, userPickerState: MutableState
is SharedContent.Text -> stringResource(MR.strings.share_message) is SharedContent.Text -> stringResource(MR.strings.share_message)
is SharedContent.Media -> stringResource(MR.strings.share_image) is SharedContent.Media -> stringResource(MR.strings.share_image)
is SharedContent.File -> stringResource(MR.strings.share_file) is SharedContent.File -> stringResource(MR.strings.share_file)
is SharedContent.Forward -> stringResource(MR.strings.forward_message) else -> stringResource(MR.strings.share_message)
null -> stringResource(MR.strings.share_message)
}, },
color = MaterialTheme.colors.onBackground, color = MaterialTheme.colors.onBackground,
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.SemiBold,
@@ -142,14 +135,12 @@ private fun ShareListToolbar(chatModel: ChatModel, userPickerState: MutableState
@Composable @Composable
private fun ShareList(chatModel: ChatModel, search: String) { private fun ShareList(chatModel: ChatModel, search: String) {
val filter: (Chat) -> Boolean = { chat: Chat ->
chat.chatInfo.chatViewName.lowercase().contains(search.lowercase())
}
val chats by remember(search) { val chats by remember(search) {
derivedStateOf { derivedStateOf {
val sorted = chatModel.chats.toList().sortedByDescending { it.chatInfo is ChatInfo.Local } if (search.isEmpty()) chatModel.chats.toList().filter { it.chatInfo.ready } else chatModel.chats.toList().filter { it.chatInfo.ready }.filter(filter)
if (search.isEmpty()) {
sorted.filter { it.chatInfo.ready }
} else {
sorted.filter { it.chatInfo.ready && it.chatInfo.chatViewName.lowercase().contains(search.lowercase()) }
}
} }
} }
LazyColumnWithScrollBar( LazyColumnWithScrollBar(
@@ -2,8 +2,6 @@
package chat.simplex.common.views.helpers package chat.simplex.common.views.helpers
import androidx.compose.runtime.saveable.Saver import androidx.compose.runtime.saveable.Saver
import chat.simplex.common.model.ChatInfo
import chat.simplex.common.model.ChatItem
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.serialization.* import kotlinx.serialization.*
import kotlinx.serialization.descriptors.* import kotlinx.serialization.descriptors.*
@@ -15,7 +13,6 @@ sealed class SharedContent {
data class Text(val text: String): SharedContent() data class Text(val text: String): SharedContent()
data class Media(val text: String, val uris: List<URI>): SharedContent() data class Media(val text: String, val uris: List<URI>): SharedContent()
data class File(val text: String, val uri: URI): SharedContent() data class File(val text: String, val uri: URI): SharedContent()
data class Forward(val chatItem: ChatItem, val fromChatInfo: ChatInfo): SharedContent()
} }
enum class AnimatedViewState { enum class AnimatedViewState {
@@ -14,7 +14,6 @@ import androidx.compose.ui.unit.*
import chat.simplex.res.MR import chat.simplex.res.MR
import chat.simplex.common.ui.theme.* import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.usersettings.SettingsActionItemWithContent import chat.simplex.common.views.usersettings.SettingsActionItemWithContent
import dev.icerock.moko.resources.ImageResource
@Composable @Composable
fun <T> ExposedDropDownSetting( fun <T> ExposedDropDownSetting(
@@ -80,60 +79,6 @@ fun <T> ExposedDropDownSetting(
} }
} }
@Composable
fun <T> ExposedDropDownSettingWithIcon(
values: List<Triple<T, ImageResource, String>>,
selection: State<T>,
fontSize: TextUnit = 16.sp,
iconSize: Dp = 40.dp,
listIconSize: Dp = 30.dp,
iconColor: Color = MenuTextColor,
enabled: State<Boolean> = mutableStateOf(true),
minWidth: Dp = 200.dp,
onSelected: (T) -> Unit
) {
val expanded = remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
expanded = expanded.value,
onExpandedChange = {
expanded.value = !expanded.value && enabled.value
}
) {
Row(
Modifier.padding(start = 10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
val choice = values.first { it.first == selection.value }
Icon(painterResource(choice.second), choice.third, Modifier.size(iconSize), tint = iconColor)
}
DefaultExposedDropdownMenu(
modifier = Modifier.widthIn(min = minWidth),
expanded = expanded,
) {
values.forEach { selectionOption ->
DropdownMenuItem(
onClick = {
onSelected(selectionOption.first)
expanded.value = false
},
contentPadding = PaddingValues(horizontal = DEFAULT_PADDING * 1.5f)
) {
Icon(painterResource(selectionOption.second), selectionOption.third, Modifier.size(listIconSize))
Spacer(Modifier.width(15.dp))
Text(
selectionOption.third,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = MenuTextColor,
fontSize = fontSize,
)
}
}
}
}
}
@Composable @Composable
fun <T> ExposedDropDownSettingRow( fun <T> ExposedDropDownSettingRow(
title: String, title: String,
@@ -254,12 +254,12 @@ fun TextIconSpaced(extraPadding: Boolean = false) {
} }
@Composable @Composable
fun InfoRow(title: String, value: String, icon: Painter? = null, iconTint: Color? = null, textColor: Color = MaterialTheme.colors.onBackground) { fun InfoRow(title: String, value: String, icon: Painter? = null, iconTint: Color? = null) {
SectionItemViewSpaceBetween { SectionItemViewSpaceBetween {
Row { Row {
val iconSize = with(LocalDensity.current) { 21.sp.toDp() } val iconSize = with(LocalDensity.current) { 21.sp.toDp() }
if (icon != null) Icon(icon, title, Modifier.padding(end = 8.dp).size(iconSize), tint = iconTint ?: MaterialTheme.colors.secondary) if (icon != null) Icon(icon, title, Modifier.padding(end = 8.dp).size(iconSize), tint = iconTint ?: MaterialTheme.colors.secondary)
Text(title, color = textColor) Text(title)
} }
Text(value, color = MaterialTheme.colors.secondary) Text(value, color = MaterialTheme.colors.secondary)
} }
@@ -15,6 +15,7 @@ import chat.simplex.res.MR
import com.charleskorn.kaml.decodeFromStream import com.charleskorn.kaml.decodeFromStream
import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.StringResource
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import java.io.* import java.io.*
import java.net.URI import java.net.URI
@@ -38,7 +38,9 @@ import chat.simplex.common.views.usersettings.*
import chat.simplex.res.MR import chat.simplex.res.MR
import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource import dev.icerock.moko.resources.compose.stringResource
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.runBlocking
@Composable @Composable
fun ConnectMobileView() { fun ConnectMobileView() {
@@ -267,20 +269,12 @@ fun AddingMobileDevice(showTitle: Boolean, staleQrCode: MutableState<Boolean>, c
var cachedR by remember { mutableStateOf<CR.RemoteHostStarted?>(null) } var cachedR by remember { mutableStateOf<CR.RemoteHostStarted?>(null) }
val customAddress = rememberSaveable { mutableStateOf<RemoteCtrlAddress?>(null) } val customAddress = rememberSaveable { mutableStateOf<RemoteCtrlAddress?>(null) }
val customPort = rememberSaveable { mutableStateOf<Int?>(null) } val customPort = rememberSaveable { mutableStateOf<Int?>(null) }
var userChangedAddress by rememberSaveable { mutableStateOf(false) }
var userChangedPort by rememberSaveable { mutableStateOf(false) }
val startRemoteHost = suspend { val startRemoteHost = suspend {
if (customAddress.value != cachedR.address && cachedR != null) {
userChangedAddress = true
}
if (customPort.value != cachedR.port && cachedR != null) {
userChangedPort = true
}
val r = chatModel.controller.startRemoteHost( val r = chatModel.controller.startRemoteHost(
rhId = null, rhId = null,
multicast = controller.appPrefs.offerRemoteMulticast.get(), multicast = controller.appPrefs.offerRemoteMulticast.get(),
address = if (customAddress.value != null && userChangedAddress) customAddress.value else cachedR.rh?.bindAddress_, address = if (customAddress.value?.address != cachedR.address?.address) customAddress.value else cachedR.rh?.bindAddress_,
port = if (customPort.value != null && userChangedPort) customPort.value else cachedR.rh?.bindPort_ port = if (customPort.value != cachedR.port) customPort.value else cachedR.rh?.bindPort_
) )
if (r != null) { if (r != null) {
cachedR = r cachedR = r
@@ -349,20 +343,12 @@ private fun showConnectMobileDevice(rh: RemoteHostInfo, connecting: MutableState
var cachedR by remember { mutableStateOf<CR.RemoteHostStarted?>(null) } var cachedR by remember { mutableStateOf<CR.RemoteHostStarted?>(null) }
val customAddress = rememberSaveable { mutableStateOf<RemoteCtrlAddress?>(null) } val customAddress = rememberSaveable { mutableStateOf<RemoteCtrlAddress?>(null) }
val customPort = rememberSaveable { mutableStateOf<Int?>(null) } val customPort = rememberSaveable { mutableStateOf<Int?>(null) }
var userChangedAddress by rememberSaveable { mutableStateOf(false) }
var userChangedPort by rememberSaveable { mutableStateOf(false) }
val startRemoteHost = suspend { val startRemoteHost = suspend {
if (customAddress.value != cachedR.address && cachedR != null) {
userChangedAddress = true
}
if (customPort.value != cachedR.port && cachedR != null) {
userChangedPort = true
}
val r = chatModel.controller.startRemoteHost( val r = chatModel.controller.startRemoteHost(
rhId = rh.remoteHostId, rhId = rh.remoteHostId,
multicast = controller.appPrefs.offerRemoteMulticast.get(), multicast = controller.appPrefs.offerRemoteMulticast.get(),
address = if (customAddress.value != null && userChangedAddress) customAddress.value else cachedR.rh?.bindAddress_ ?: rh.bindAddress_, address = if (customAddress.value?.address != cachedR.address?.address) customAddress.value else cachedR.rh?.bindAddress_ ?: rh.bindAddress_,
port = if (customPort.value != null && userChangedPort) customPort.value else cachedR.rh?.bindPort_ ?: rh.bindPort_ port = if (customPort.value != cachedR.port) customPort.value else cachedR.rh?.bindPort_ ?: rh.bindPort_
) )
if (r != null) { if (r != null) {
cachedR = r cachedR = r
@@ -34,7 +34,6 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
val networkTCPConnectTimeout = remember { mutableStateOf(currentCfgVal.tcpConnectTimeout) } val networkTCPConnectTimeout = remember { mutableStateOf(currentCfgVal.tcpConnectTimeout) }
val networkTCPTimeout = remember { mutableStateOf(currentCfgVal.tcpTimeout) } val networkTCPTimeout = remember { mutableStateOf(currentCfgVal.tcpTimeout) }
val networkTCPTimeoutPerKb = remember { mutableStateOf(currentCfgVal.tcpTimeoutPerKb) } val networkTCPTimeoutPerKb = remember { mutableStateOf(currentCfgVal.tcpTimeoutPerKb) }
var networkRcvConcurrency = remember { mutableStateOf(currentCfgVal.rcvConcurrency) }
val networkSMPPingInterval = remember { mutableStateOf(currentCfgVal.smpPingInterval) } val networkSMPPingInterval = remember { mutableStateOf(currentCfgVal.smpPingInterval) }
val networkSMPPingCount = remember { mutableStateOf(currentCfgVal.smpPingCount) } val networkSMPPingCount = remember { mutableStateOf(currentCfgVal.smpPingCount) }
val networkEnableKeepAlive = remember { mutableStateOf(currentCfgVal.enableKeepAlive) } val networkEnableKeepAlive = remember { mutableStateOf(currentCfgVal.enableKeepAlive) }
@@ -69,7 +68,6 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
tcpConnectTimeout = networkTCPConnectTimeout.value, tcpConnectTimeout = networkTCPConnectTimeout.value,
tcpTimeout = networkTCPTimeout.value, tcpTimeout = networkTCPTimeout.value,
tcpTimeoutPerKb = networkTCPTimeoutPerKb.value, tcpTimeoutPerKb = networkTCPTimeoutPerKb.value,
rcvConcurrency = networkRcvConcurrency.value,
tcpKeepAlive = tcpKeepAlive, tcpKeepAlive = tcpKeepAlive,
smpPingInterval = networkSMPPingInterval.value, smpPingInterval = networkSMPPingInterval.value,
smpPingCount = networkSMPPingCount.value smpPingCount = networkSMPPingCount.value
@@ -80,7 +78,6 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
networkTCPConnectTimeout.value = cfg.tcpConnectTimeout networkTCPConnectTimeout.value = cfg.tcpConnectTimeout
networkTCPTimeout.value = cfg.tcpTimeout networkTCPTimeout.value = cfg.tcpTimeout
networkTCPTimeoutPerKb.value = cfg.tcpTimeoutPerKb networkTCPTimeoutPerKb.value = cfg.tcpTimeoutPerKb
networkRcvConcurrency.value = cfg.rcvConcurrency
networkSMPPingInterval.value = cfg.smpPingInterval networkSMPPingInterval.value = cfg.smpPingInterval
networkSMPPingCount.value = cfg.smpPingCount networkSMPPingCount.value = cfg.smpPingCount
networkEnableKeepAlive.value = cfg.enableKeepAlive networkEnableKeepAlive.value = cfg.enableKeepAlive
@@ -113,7 +110,6 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
networkTCPConnectTimeout, networkTCPConnectTimeout,
networkTCPTimeout, networkTCPTimeout,
networkTCPTimeoutPerKb, networkTCPTimeoutPerKb,
networkRcvConcurrency,
networkSMPPingInterval, networkSMPPingInterval,
networkSMPPingCount, networkSMPPingCount,
networkEnableKeepAlive, networkEnableKeepAlive,
@@ -132,7 +128,6 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
networkTCPConnectTimeout: MutableState<Long>, networkTCPConnectTimeout: MutableState<Long>,
networkTCPTimeout: MutableState<Long>, networkTCPTimeout: MutableState<Long>,
networkTCPTimeoutPerKb: MutableState<Long>, networkTCPTimeoutPerKb: MutableState<Long>,
networkRcvConcurrency: MutableState<Int>,
networkSMPPingInterval: MutableState<Long>, networkSMPPingInterval: MutableState<Long>,
networkSMPPingCount: MutableState<Int>, networkSMPPingCount: MutableState<Int>,
networkEnableKeepAlive: MutableState<Boolean>, networkEnableKeepAlive: MutableState<Boolean>,
@@ -159,7 +154,7 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
SectionItemView { SectionItemView {
TimeoutSettingRow( TimeoutSettingRow(
stringResource(MR.strings.network_option_tcp_connection_timeout), networkTCPConnectTimeout, stringResource(MR.strings.network_option_tcp_connection_timeout), networkTCPConnectTimeout,
listOf(5_000000, 7_500000, 10_000000, 15_000000, 20_000000, 30_000000, 40_000000), secondsLabel listOf(7_500000, 10_000000, 15_000000, 20_000000, 30_000_000, 45_000_000), secondsLabel
) )
} }
SectionItemView { SectionItemView {
@@ -172,13 +167,7 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
// can't be higher than 130ms to avoid overflow on 32bit systems // can't be higher than 130ms to avoid overflow on 32bit systems
TimeoutSettingRow( TimeoutSettingRow(
stringResource(MR.strings.network_option_protocol_timeout_per_kb), networkTCPTimeoutPerKb, stringResource(MR.strings.network_option_protocol_timeout_per_kb), networkTCPTimeoutPerKb,
listOf(2_500, 5_000, 10_000, 15_000, 20_000, 30_000), secondsLabel listOf(15_000, 30_000, 45_000, 60_000, 90_000, 120_000), secondsLabel
)
}
SectionItemView {
IntSettingRow(
stringResource(MR.strings.network_option_rcv_concurrency), networkRcvConcurrency,
listOf(1, 2, 4, 8, 12, 16, 24), ""
) )
} }
SectionItemView { SectionItemView {
@@ -429,7 +418,6 @@ fun PreviewAdvancedNetworkSettingsLayout() {
networkTCPConnectTimeout = remember { mutableStateOf(10_000000) }, networkTCPConnectTimeout = remember { mutableStateOf(10_000000) },
networkTCPTimeout = remember { mutableStateOf(10_000000) }, networkTCPTimeout = remember { mutableStateOf(10_000000) },
networkTCPTimeoutPerKb = remember { mutableStateOf(10_000) }, networkTCPTimeoutPerKb = remember { mutableStateOf(10_000) },
networkRcvConcurrency = remember { mutableStateOf(8) },
networkSMPPingInterval = remember { mutableStateOf(10_000000) }, networkSMPPingInterval = remember { mutableStateOf(10_000000) },
networkSMPPingCount = remember { mutableStateOf(3) }, networkSMPPingCount = remember { mutableStateOf(3) },
networkEnableKeepAlive = remember { mutableStateOf(true) }, networkEnableKeepAlive = remember { mutableStateOf(true) },

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